Search-o1:让长推理模型边推理边检索的Reason-in-Documents机制 长推理模型有个很尴尬的处境它能把一道数学题拆成十几步推导但每一步用到的中间事实全靠参数里那点记忆。一旦题目涉及最新事件、冷门论文、具体数值模型就开始一本正经地编。Search-o1 这个工作就是冲着这个痛点来的——它让模型在推理链的中间环节自己判断我这里缺信息了然后主动发起检索把搜回来的文档消化成推理能直接用的形式再接着往下推。这套机制叫 Reason-in-Documents是整篇论文最值得琢磨的部分。我把它当成一个给长推理装外挂的案例来拆。适合已经跑过基础 RAG、想搞清楚检索怎么嵌进推理循环的人也适合刚接触 agentic search、想知道工程上到底难在哪的人。下面按我自己的理解顺序讲不按论文目录走。1. 长推理为什么会断粮1.1 参数记忆的边界在哪里先想清楚一个基本事实大模型的参数是训练数据的压缩不是数据库。压缩意味着有损有损意味着细节会丢、会串、会过时。你问它某篇 2024 年 3 月发的论文里那个 benchmark 的准确率是多少它要么答不出要么给你一个看起来很像的数字。这不是模型不聪明是信息根本不在它手里。长推理把这个问题放大了。短问答里模型编一个数字你一眼能看出来。但长推理是链式的第 3 步编了一个错的前提第 4 到第 15 步全在这个错前提上继续推最后得出一个逻辑自洽但事实错误的结论。这种错误特别隐蔽因为推理过程看起来严丝合缝。我见过不少 case模型洋洋洒洒推了 20 步每一步都合理但源头那个事实是幻觉整条链就废了。所以长推理对事实供给的要求比短问答高得多。它需要的是在正确的时机、拿到正确的信息、以正确的形式喂进去。1.2 传统 RAG 为什么接不住长推理传统 RAG 的流程是先检索、后生成用户提问 → 检索 top-k 文档 → 拼进 prompt → 模型一次性生成答案。这个流程对单跳问答够用对长推理就不行了。问题出在一次性上。长推理的信息需求是动态的推到第 5 步才发现需要某个定义推到第 9 步才发现需要某个对比数据。你在开头一次性检索根本不知道后面会缺什么。检索早了信息用不上还占上下文检索晚了模型已经在编了。更麻烦的是传统 RAG 检索回来的是一整段文档直接塞进 prompt。长推理的上下文本来就紧张塞几段原文进去推理链本身的空间就被挤没了。而且原文里大量内容和当前推理步骤无关模型还得自己过滤等于把信息筛选的负担又丢回给模型。Search-o1 的思路就是把这套先检索后生成改成边推理边检索并且专门解决检索回来的东西怎么用这个问题。2. Search-o1 的两段式结构拆解2.1 主推理模型和检索的协作关系Search-o1 的架构可以理解成两个角色一个负责想一个负责查。主推理模型reasoning model负责生成推理链它有一个特殊能力——在推理过程中输出一个检索触发信号。一旦触发系统暂停推理去执行检索把结果处理后交回主模型主模型接着往下推。这个暂停-检索-恢复的循环是它和传统 RAG 最本质的区别。推理不是一次成型的是被检索切成一节一节的。每一节推理结束后模型有机会抬头看看自己缺不缺东西。触发信号怎么来论文里的做法是让模型在推理链中显式地生成一个查询query这个 query 就是它当前步骤需要的信息的自然语言描述。比如推到需要确认这个定理的适用条件时模型会输出一个类似XX 定理的适用条件的查询。这比固定规则触发灵活得多因为只有模型自己知道它下一步要什么。2.2 Reason-in-Documents检索结果不是拿来就用这是我觉得整篇论文最核心的贡献。检索回来的文档Search-o1 不是直接拼进上下文而是先做一次精炼。具体做法是把检索到的文档和当前推理上下文一起交给模型做一次reason in documents的处理。模型的任务是从文档里抽出和当前推理步骤真正相关的信息并且把它组织成推理链能直接续上的形式。输出的不是原文摘录而是经过理解的、和当前推理语境对齐的中间结论。为什么这一步重要因为检索回来的文档是外部视角写的它的组织逻辑和当前推理链的逻辑不一样。直接拼接模型得自己做一次翻译这个翻译过程容易出错也浪费上下文。Reason-in-Documents 相当于把这个翻译工作显式化、专门化让模型在一个受控的步骤里完成而不是混在长推理里顺手做。我打个比方传统 RAG 像是把一本参考书直接扔给你让你边写论文边翻。Reason-in-Documents 像是先让一个助手把参考书里相关的那两页读一遍用你自己的论文逻辑给你讲一遍你再接着写。显然后者更省心也更不容易跑偏。2.3 两个阶段的分工边界整个流程可以拆成两个阶段推理阶段主模型生成推理链遇到信息缺口时输出查询暂停。检索与精炼阶段执行检索对每篇文档做 Reason-in-Documents 处理把精炼结果合并后交回主模型。这两个阶段的边界很关键。推理阶段只负责想不负责查检索阶段只负责查和整理不负责下结论。职责分离的好处是每一步都可控、可调试。如果最终答案错了你能定位是推理错了还是检索错了还是精炼错了而不是一锅粥。3. Reason-in-Documents 到底在做什么3.1 从文档检索到信息蒸馏Reason-in-Documents 的本质是一次信息蒸馏。输入是若干篇检索文档加当前推理上下文输出是一段精炼后的、可直接续接推理的信息。蒸馏的目标有三个去噪、对齐、压缩。去噪是把文档里和当前步骤无关的内容扔掉对齐是把文档的表达方式转成当前推理链的语言风格和逻辑结构压缩是把长文档压成推理能消化的短片段。这三个目标里对齐最容易被忽略但最重要。举个例子文档里写该方法在 F1 上达到 0.87相比 baseline 提升 3 个点而当前推理链在讨论这个方法是否适合处理类别不平衡数据。直接拼接模型得自己意识到F1 提升和类别不平衡之间的关系。Reason-in-Documents 会直接输出该方法在类别不平衡场景下 F1 达 0.87说明它对不平衡数据有一定鲁棒性——把文档事实和推理需求接上了。3.2 精炼过程中的上下文管理长推理的上下文是稀缺资源。Search-o1 在精炼时有个隐含的取舍精炼结果要足够短短到不挤占推理空间又要足够全全到推理不用再回头查。这个平衡怎么把握论文里的做法是让精炼步骤也由模型完成模型自己判断哪些信息是当前推理必需的。这其实是在赌模型的判断力。实测中这个赌注基本成立因为模型在当前推理需要什么这件事上有天然的优势——它自己就是推理的执行者。但这里有个坑如果精炼步骤本身也幻觉了错误会被直接注入推理链而且因为经过了精炼这道工序看起来更可信。所以精炼步骤的可靠性直接决定整个系统的可靠性。工程上通常会给精炼步骤加约束比如要求它引用原文片段、要求它标注信息来源降低幻觉风险。3.3 多文档精炼结果的合并策略一次检索可能返回多篇文档每篇都做一次 Reason-in-Documents得到多个精炼结果。这些结果怎么合并简单拼接是最直接的做法但会有冗余和冲突。两篇文档可能说了同一件事也可能说了矛盾的事。Search-o1 的处理是让模型在精炼时就把这篇文档提供了什么说清楚合并时按信息点去重冲突的地方保留双方并标注。我自己的经验是合并策略对最终效果影响很大。如果只是拼接模型在后续推理里可能被冗余信息干扰如果过度合并又可能丢掉细节。一个实用的折中是按信息点而不是文档来组织精炼结果每个信息点标注来源冲突信息点并列保留。4. 把 Search-o1 跑起来的关键环节4.1 检索触发时机的判断触发太频繁推理被切得太碎上下文里全是检索结果推理链本身反而被淹没。触发太少模型该查的时候不查继续编。论文里靠模型自己生成查询来触发这依赖模型的自知之明。实际用下来触发质量跟模型能力关系很大。能力强的模型能比较准地判断我这里确实不知道能力弱的模型要么不触发要么乱触发。工程上可以加一些辅助信号比如推理链里出现具体数值、具体日期、具体人名时提高触发倾向出现根据常识一般来说这类模糊表述时也提示可能需要检索。这些规则不是替代模型判断是给模型判断加一层兜底。4.2 查询构造的质量控制模型生成的查询质量参差不齐。有的查询很精准直接命中目标文档有的查询太宽泛检索回来一堆无关内容有的查询太具体检索不到东西。查询构造的质量直接决定检索质量进而决定整个系统的上限。我见过的一个实用技巧是让模型生成查询时同时生成这个查询想找什么类型的信息的说明。检索时可以用这个说明做过滤或重排提高命中率。另一个技巧是查询改写。模型第一次生成的查询可能不理想可以让它对查询做一次自我改写或者生成多个候选查询并行检索再合并结果。代价是检索次数增加延迟上升。4.3 精炼结果注入推理链的方式精炼结果怎么交回主模型也有讲究。直接追加到推理链末尾是最简单的但可能打断推理的连贯性。更好的做法是把它作为参考信息标注出来让模型知道这是外部信息不是我自己推出来的避免模型把外部信息和自己的推理混为一谈。这个标注很重要。如果外部信息和推理链混在一起后续推理可能把外部信息当成自己已经确认的前提一旦外部信息有误错误就被洗白了。显式标注能让模型在后续推理里对这部分信息保持一定的警惕。5. 实测中容易踩的几个坑5.1 检索延迟拖垮推理体验长推理本来就慢每一步都要等模型生成。如果中间还插入检索延迟会成倍增加。一次完整推理如果有 5 次检索触发每次检索加精炼要几秒整体延迟就很可观了。缓解办法有几个并行检索多个查询同时发、缓存相同查询直接命中、异步预取根据推理进度预测下一步可能需要的查询提前检索。这些都需要工程上的额外投入不是模型层面能解决的。5.2 精炼步骤的幻觉放大前面提过精炼步骤幻觉是致命的。我实测中遇到过精炼结果添油加醋的情况原文只说在某个数据集上表现良好精炼后变成在所有数据集上都达到 SOTA。这种放大在后续推理里几乎无法察觉。对策是给精炼步骤加引用约束要求精炼结果必须能对应到原文的具体片段不能引入原文没有的结论。实现上可以让精炼步骤输出时带上原文片段的位置或原文句子便于核查。5.3 推理链被检索结果带偏检索结果有时候会把推理带向一个原本没打算走的方向。比如推理本来在讨论方法 A 的局限性检索回来一篇讲方法 B 的文档模型可能就被带过去讨论 B 了。这个问题的根源是精炼结果和当前推理步骤的对齐不够紧。如果精炼时严格以当前推理步骤需要什么为准就不容易被带偏。所以精炼步骤的 prompt 设计很关键要反复强调只提取和当前步骤相关的信息。6. 这套思路能迁移到哪里Search-o1 的价值不只是一个具体系统更是一套可迁移的模式把检索从生成的前置步骤变成生成过程中的一个可调用能力。这个模式可以迁移到很多场景。比如代码生成模型写到一半发现需要查某个 API 的用法主动触发检索比如数据分析模型推到某一步发现需要某个统计口径的定义主动查比如多轮对话模型发现用户提到一个自己不确定的实体主动查证。迁移的关键是两件事一是让模型有能力判断我什么时候需要外部信息二是让外部信息能以推理友好的形式注入。前者靠模型能力后者靠 Reason-in-Documents 这类精炼机制。这两件事做好了检索就不再是外挂而是推理的一部分。我自己在做的几个小项目里把类似机制用在了文档问答和代码辅助上效果比传统 RAG 稳不少尤其是在需要多步推理的场景。代价是系统复杂度上去了延迟也上去了得看具体场景值不值得。最后分享一个实操体会这套东西调起来最花时间的不是模型本身是精炼步骤的 prompt。精炼 prompt 写得好整个系统就顺写得糙检索回来的信息要么用不上要么用错。我一般会准备一批推理中途缺信息的测试 case专门盯精炼步骤的输出质量反复迭代 prompt直到精炼结果能稳定地接上推理链。这个迭代过程没什么捷径就是磨。