你让模型完成一个需要多轮转换的技术方案一次生成往往在第三步就给出了结论——而且语气里没有任何犹豫。更麻烦的是如果第三步推导错了后续文本通常不会自动纠正。最近我在整理多迭代推理相关的设计资料时反复看到两个概念并排出现Chained Recursive Language Models 和 Multi-Iteration Reasoning。乍看像论文名词但它并不只是学术概念对任何想把手动工作流自动化的人都有直接的工程意义。我的核心判断是这种方案真正的价值不只是让模型“多想几遍”而是把推理从一次不可回头的生成过程变成一条可检查、可反馈、可终止的状态更新链。一旦接受这个视角你面对的不再是“模型答得对不对”而是“我有没有把推理过程定义成一个能收敛的循环”。这个转变听起来不大但它会连带改变评估方式、成本模型和排错方式。1. 为什么一次推理经常直接失控1.1 自回归生成自带“将错就错”的倾向标准语言模型的生成是自回归的每生成一个 token系统把它追加到已有序列再去生成下一个。这种机制非常擅长局部连贯却缺少全局审查能力。原因要从 token 的预测方式来看模型只负责估计当前 token 条件下出现的概率并不负责保证整段文本与外部约束一致。这意味着当第一步推理出现偏差时后续的生成不会感觉到错误。它只会觉得“下一步应该续上这个已有的句子”。更隐蔽的是输出越是流畅读者越容易忽略中间的逻辑断裂。处理长任务时这种“将错就错”会被放大。模型能在局部保持自洽但很难随时回到最开始重新调整所有约束。举个例子当你要求模型完成“先分析需求再比较三个方案最后给出排期”这种任务时单次生成往往会把第一段提到的约束在后面逐渐淡忘。它不是没有能力处理这些条件而是自回归过程没有提供“回头看”的机制。每一步都在生成下一个 token而不是在全局图上做约束传播。1.2 思维链只是给推理“开了一个可见的窗口”思维链是常见的补救方案。它让模型先输出推理过程再给出答案。实践里它确实有效因为中间步骤会迫使模型多生成一些 token让后续的预测有了更明确的逻辑路径。但它仍是一次性生成不是可控的多阶段系统。单次生成的问题在于你无法在中间点改写任务无法要求模型回退到某一步重新推演也无法在每一步之间做程序化校验。你可以事后看到哪里错了但你需要重启整段生成来修正。这在工程上不是可回滚状态而是一次性事务。思维链还有一个容易被忽略的代价中间的推理步骤没有被结构化为状态。它只是一堆自然语言 token。你如果想把“模型已确认的结论”和“尚未解除的疑问”分离开就必须额外解析这段文本而这种解析非常脆弱。链式递归的本意就是想用更工程化的方式解决这个问题。1.3 从单次采样到多轮收敛的需求因此长链推理任务自然会产生一个新的需求能不能让模型把“初稿”和“校对”分离开能不能用一个程序循环去驱动模型而不是把整个流程挂在一次内部生成上这是多轮迭代推理得以成立的出发点。多轮迭代不等同于“重试”。重试是要求模型重新回答同一个问题不共享中间状态。多轮迭代则是把上一轮结果当作下一轮输入状态让模型有机会专门处理上一轮答错的部分。这样更接近人类工作流程先写草稿再根据反馈修改而不是每次从头开始再写一遍。所以链式递归语言模型的表面特征是“多调几次模型”深层特征却是“每一轮都在更新一个显式的推理状态”。你看到的循环本质上是一段可审计、可干预的推理过程。2. 链式递归语言模型把一次思维变成可检查的循环2.1 三个关键词决定设计方向Chained Recursive Language Models 涉及三个关键字每一个都在约束实现方式。Chained 要求前一轮输出进入下一轮输入形成链式依赖。这不是简单的多轮对话而是有方向、有顺序的信息传递。Recursive 指同一套更新函数反复作用在新状态上。模型在这里不是偶尔被调用一次而是稳定扮演“状态转换器”的角色。Language Models 则说明承担状态更新的不是额外规则引擎而是语言模型本身。链条并不是简单的上下文拼接。如果你只是把上一轮答案粘贴到下一轮 prompt并说“看看有没有错”那也是链式调用但过于懒惰。真正的链式递归强调状态更新函数模型需要从一个旧状态出发生成一个新状态。新状态可能修改了结论新增了证据调整了优先级而不是把同样的内容复述一遍。2.2 最小的循环结构状态、更新函数、终止条件任何链式递归实现都要有明确的“状态”。让模型每轮只产生一段自由文本理论上也能跑但很难做验证、回滚和收敛判断。所以建议先使用结构化状态。举例你可以用一个包含几个字段的 JSON 结构{ question: 原始问题, stable_conclusion: 已经稳定下来的结论, hypothesis: 本轮需要验证的新假设, remaining_doubts: [尚未消除的疑点], is_finished: false, finished_reason: }用语言模型作为更新函数输入上一个状态输出下一个状态。程序循环每一步都会调用这个更新函数直到满足终止条件。这里没有复杂的分支搜索只有一条明确的链。缺点是只能沿着一个方向推进优点是每一步状态都能被程序解析中途可以插入额外规则和人工检查。这种结构很像把一段长文本生成改造成了“状态机驱动”。模型不再被要求一次性给出完整答案而是在每次循环里只处理一个更小的任务在旧状态基础上做局部更新。局部任务变简单错误率自然会下降也更便于工程控制。2.3 与 Agent 循环、ReAct、树搜索之间的关系这一类方案容易和近年的其他多步框架混淆。Agent 循环通常会让模型调用工具观察工具返回结果再决定下一步。链式递归可以看成一个更小的 Agent 循环子集模型唯一的“工具”是它自己它观察的对象是上一轮的内部状态。ReAct 将推理和行动交替串联。如果行动本身是文本推理效果上和链式递归很接近但 ReAct 更强调外部反馈链式递归更强调内部状态的演化和终止判断。下面是几个概念的粗略对照方案核心动作反馈来源是否维护显式状态复杂度单次思维链在一轮生成内逐步推理无否低链式递归多轮调用模型更新状态上一轮状态是中Agent 循环多轮调用工具并行动工具返回结果是中高树搜索展开多条推理分支再筛选分支评估是高树搜索会增加分支模型会探索多条路径。链式递归没有分支只有一条链。这让它损失了一些探索能力但极大降低工程复杂度。对很多业务问题来说并不需要搜索一棵树只需要允许模型“迭代到质量稳定”。3. 递归迭代如何悄悄改变效率模型3.1 迭代成本不是加法是乘法每次递归迭代模型都要重新处理 prompt。如果历史状态不断增长成本会随步骤数近似线性增长甚至可能超过线性。因此多迭代从 token 消耗上看并不便宜。它本质上是拿计算资源换输出质量。但真正的问题不是贵不贵而是收益是否可量化。如果任务原本单轮 2000 token 就能完成改成 5 轮各 1000 token成本可能翻倍但错误率可能下降不少。工程上需要画一条“轮数与输出质量”的曲线决定这个任务应该用几轮。这里给出一个粗粒度的成本观察方式轮数每轮 prompt 规模总 token 消耗质量变化建议1较短低可能有明显漏项先跑一轮看基线3中中明显改善多数任务建议从 3 轮起步5较长高改善变缓或开始发散需要观察日志8很长很高未必更好先压缩历史再考虑加轮3.2 用小模型加迭代不等于能替代大模型有一种理想化说法与其用更大模型不如用小模型多迭代几次。但这个结论有条件。如果小模型在每一步都无法检索到关键信息或无法产生新的推导那么迭代只是增加不同的错误而不是收敛到正确结果。从实际操作经验看小模型加迭代更适合“已经知道怎么做但容易粗心”的任务。它不擅长解决“完全没有能力完成的推理”。所以评估时先拿单轮做一个下限测试确认模型能走出“第一步对的方向”再考虑用迭代补足细节。另一个需要注意的点是质量分布。多轮迭代可以减少随机错误但无法补偿模型本身的系统性盲区。如果一个模型对所有涉及日期计算的问题都容易错那么最后几轮它可能只是在错误版本之间来回切换。3.3 从黑盒生成走向可观测生产很多人让模型进入生产前会在“不确定”和“不可控”之间纠结。链式递归的优势在于把一次生成切成若干状态切片这让复杂的输出有了工程抓手。生产上可以记录每轮的 stable_conclusion 和 remaining_doubts从而审计推理路径也可以针对某一步设定规则比如当 doubt 数量为 0 时才允许终止还可以在发现某轮状态异常时回滚到上一轮用更温和的提示词重新生成。这在一个长生成里几乎无法做到。这也是我强调“它改变的是推演过程而不仅仅是答案”的原因。当你把状态字段保存下来你就拥有了一个推理过程的时间线。这个时间线对分析模型失误、迭代提示词和与业务方沟通都有直接价值。4. 实践笔记把链式递归跑起来的最小流程4.1 前置条件与问题选择需要的工程支持很少能调用的语言模型 API、能跑 Python 脚本的运行环境、一个保存状态的存储位置就够了。不需要 GPU也不需要额外框架。真正需要准备的是任务选择和提示模板。我会先选择“中间结论可被人工验证”的任务。例如写一份带预算约束的技术选型方案第一轮列方案第二轮查成本第三轮查风险。这样当你看到状态日志时能手动确认每一步是否合理。先不要拿需要大量领域知识、连人都很难评估的问题开刀那样你很难判断循环到底是在收敛还是在发散。4.2 一个最小状态循环的实现骨架以 Python 为例结构可以这样设计# 通用实现思路用循环驱动一个语言模型更新状态 class RecursiveReasoner: def __init__(self, llm_call, max_steps5): self.llm_call llm_call self.max_steps max_steps def _initial_state(self, question): return { question: question, stable_conclusion: , hypothesis: , remaining_doubts: [], is_finished: False, finished_reason: } def run(self, question): state self._initial_state(question) for step in range(1, self.max_steps 1): print(f--- Step {step} ---) state self.llm_call(state) if state.get(is_finished): break return state在真实工程中llm_call 函数需要负责拼提示词、调用模型接口、解析输出。解析要写容错逻辑比如当模型输出不是完整 JSON 时能否提取其中的部分字段当模型说“完成”但没给理由时是否仍然终止。这些都是隐藏的坑比循环本身更容易出问题。4.3 提示词模板设计下面是一份常见状态更新提示词框架结构可以按需调整你正在执行一个递归推理状态更新。 问题{question} 当前已知结论{stable_conclusion} 还没确定的疑点{remaining_doubts} 本轮假设{hypothesis} 请你完成下面任务 1. 检查“未确定疑点”和“本轮假设”。 2. 根据推理结果更新已知结论。 3. 如果某些疑点已被消除请从 remaining_doubts 列表中移除。 4. 如果需要新的验证请把新假设写入 hypothesis。 5. 如果所有疑点已消除把 is_finished 设置为 true。 输出结构必须是 JSON { stable_conclusion: ..., hypothesis: ..., remaining_doubts: [...], is_finished: true/false, finished_reason: ... }这个模板的关键在于它要求模型把自己当成一个状态编辑器而不是生成完整答案。这样下一轮才有机会只针对疑点做深入检查而不是从头再写一遍。如果你担心模型对“已经稳定的结论”反复改动可以在 prompt 里加一句除非有非常强的矛盾证据否则 stable_conclusion 不应被回退。这一句看似简单实际上能显著减少振荡。4.4 参数与调试细节关于 max_steps第一次跑时设成 2 或 3。如果 3 轮后状态还在剧烈变化说明状态设计或提示词有问题先把问题找出来不要直接拉到 10 轮。关于温度建议先设 0.2 到 0.3避免每轮随机波动被递推放大。关于输出长度按模型能力控制长度不要让模型每轮输出长篇小作文。关于日志每一轮都打印或落盘这样你才能判断循环是否真在收敛。另一个容易被忽略的细节是“状态解析失败”的兜底逻辑。模型输出一旦无法解析循环就卡住了。比较稳妥的做法是如果这一轮解析失败保留旧状态并让模型重新生成一次如果连续两次失败再终止循环并抛出日志。这样不至于因为一次临时输出格式问题让整个流程崩溃。4.5 先跑通、再优化的推进路径我建议按这个顺序推进先跑 1 轮。确认输入 prompt、状态解析、输出展示都正常。再跑 3 轮。观察状态有没有出现信息增量而不是同一句话的重复。再根据日志调整提示模板比如把“已知结论”字段描述得更具体。最后再加自动终止条件、错误重试、历史压缩。这个顺序看起来慢但能避免把提示词调试和循环逻辑调试混在一起。等你对状态变化有了手感再往生产环境扩展会顺利很多。5. 递归推理不收敛时的排查链路5.1 问题现象分类链式递归最典型的问题不是“结果错”而是“永远不收敛”。常见现象可以分成四类现象典型特征优先怀疑方向振荡结论在 A/B 两个状态之间反复横跳状态没有记录“上一轮已排除的方向”空转每轮输出都在变化但核心结论没有推进终止条件太松或提示模板缺少增量要求发散几轮后输出和原始问题关系越来越远历史压缩失效上下文被无关内容覆盖过度修改原本正确的结论被后续轮次改坏没有把稳定字段和可修改字段分离开振荡的根源通常是状态没有积累“已排除方向”。模型