跳到正文

DEEP RESEARCH PROJECT INTERVIEW · Q05 / 25

ReAct 为什么短任务好用,做长程研究却会越跑越乱?

从消息累积、重复读取、状态污染和错误传播出发,讲清 ReAct 为什么适合短任务,以及何时升级为演进报告与 ReSum。

作者与复核:吴师兄内容依据:公开教程、代码示例与工程推导查看关联教程 →查看吴师兄公开作品 →

25 道题目Q05
本页内容22 节

30 秒先说结论

ReAct 让下一步根据当前 Observation 动态选择,适合步骤少、结果短的任务。朴素实现会把所有历史不断追加,长任务中每轮重复读取旧内容,临时网页、旧结论和早期错误一起污染状态。应先收紧工具输出和去重,再把原始证据外置、维护受控演进报告,窗口仍有压力时才做动态摘要。

ReAct 是很多人实现 Agent 的第一步。

模型先根据当前问题决定一个动作,程序执行工具,把结果交回模型,模型再决定下一步。这个循环直观、灵活,几十行代码就能跑起来。

做一个两三步的小任务时,它往往很好用:搜索候选,打开正文,给出答案。

问题出现在长程研究。

一个 Deep Research 任务可能要比较多个对象、访问很多来源、处理冲突并生成报告。如果每轮都把之前的问题、动作和网页内容完整追加到上下文,模型看到的历史会越来越长。早期的一段噪声会被反复携带,后面的每一步都要重新读取;关键事实反而可能被埋在大量临时内容中。

于是系统出现一种很典型的现象:它一直在行动,却越来越不知道自己为什么行动。

所以面试官问“ReAct 有什么局限”时,不会满足于一句“上下文会爆”,他还会继续追问:

ReAct 的消息为什么随步骤增长?长上下文怎样放大成本、遗忘和错误?哪些问题仍然适合 ReAct,什么时候应该升级为受控状态或演进报告?

ReAct 的核心是闭环行动

ReAct 的基本循环可以抽象成四步。

第一,读取当前任务和可用状态。

第二,选择一个动作,例如 Search、Visit 或计算。

第三,程序执行动作,返回 Observation。

第四,根据新观察决定继续行动还是给出最终结果。

它比固定 Workflow 灵活,因为下一步不必在任务开始前全部写死。搜索结果暴露一个新实体,Agent 可以继续追;网页打不开,Agent 可以换来源;证据已经充分,Agent 可以收口。

生产系统需要记录可审计的行动、工具参数、观察摘要和决策理由,但不必向用户展示模型内部逐字推理。真正重要的是外部行为能够回放,而不是把隐式思维链当成产品功能。

ReAct 在短任务中的优势与长任务中的代价

循环本身可以保留;真正拖垮长任务的是把不断增长的原始历史直接当成唯一状态。

为什么上下文会线性增长?

最朴素的消息列表通常从系统要求和用户问题开始。

第一轮加入模型动作和搜索结果。

第二轮在原消息后继续加入动作和网页正文。

第三轮再加入新的搜索、错误或计算结果。

如果每一步 Observation 的平均长度接近,消息总长度会随着步骤数近似线性增加。运行到后面,一次模型调用要重新读取之前所有内容。

总调用成本的增长会更快。第一步读取一份上下文,第二步读取更长的上下文,后面每一步都在前一步基础上增加。把每轮读取量累加,长轨迹会产生明显的重复计算。

这里不需要背一个固定公式。面试时要说清两个事实:当前上下文随步骤增加;每一轮都会再次处理累积历史。任务越长,这个结构性开销越明显。

上下文没超过窗口,也可能已经失效

很多回答把问题只归结为“超过模型最大 Token”。实际上,在触碰硬上限前,质量已经可能下降。

第一,临时信息污染。

网页正文里有导航、广告、无关段落和旧版本说明。如果全部保留,模型后续每一步都要在噪声中寻找关键事实。

第二,关键内容被淹没。

任务开头确定的硬约束、早期找到的重要证据,可能落在长上下文中间。模型即使能够读取,也未必稳定使用。

第三,状态不清楚。

同一个主张在不同轮次被修改,消息里同时存在旧结论和新结论。若没有显式状态,模型可能引用已经被推翻的版本。

第四,错误不断累积。

早期搜索把两个相似实体混在一起,后面所有查询都围绕错误对象展开。ReAct 会根据当前历史继续做出看似合理的动作,但链路方向已经偏了。

第五,重复行动。

历史很长时,模型可能忘记某个关键词已经搜索过,再次调用同一工具。动作成功,却没有增加证据。

因此,“窗口还装得下”不是系统健康的充分条件。

ReAct 的两个长任务硬伤

第一个硬伤是状态与原始历史没有分开。

网页正文、工具错误、临时推断、最终事实全部混在消息列表中。模型必须自己从一长串记录里恢复当前状态。

第二个硬伤是错误成本会向后传播。

固定流水线某一步失败,通常只影响一个节点;ReAct 的下一步由当前观察决定,错误观察会改变后续动作。步骤越多,可传播的距离越长。

这并不意味着固定 Workflow 一定更好。Workflow 对已知、稳定的业务步骤更可控,ReAct 对需要根据环境变化选择动作的任务更灵活。工程选型要看任务到底需要多少自由度。

一个教学例子:研究链怎样越跑越偏

下面是教学情境,不代表真实客户或线上 Trace。

任务要求比较三个技术方案的部署和维护边界。

第一轮 Search 返回一篇旧文章,文章把两个版本的能力混在一起。

第二轮 Agent 根据旧文章中的功能名称继续搜索,却没有记录版本条件。

第三轮 Visit 打开一篇转载,正文重复第一篇说法,Agent 误以为得到第二个独立来源。

第四轮开始写比较结论,发现另一个候选资料不足,又重复搜索同一关键词。

第五轮上下文里已经有两份重复正文、一个错误版本和多个临时判断。即使模型语言能力很强,它面对的状态本身也不干净。

真正的修复需要让每一步输出进入证据层和状态层:原始页面单独保存,当前状态只留下来源、版本、已确认事实、冲突和缺口。单纯更换更长上下文模型,只会推迟问题出现;错误版本被发现后,旧主张仍要明确失效。

为什么长上下文模型不能直接解决?

更大的上下文窗口可以延后硬溢出,却不会自动解决信息组织。

如果输入里有大量重复网页,窗口变大只是允许系统保存更多重复。

如果旧结论和新结论并存,窗口变大不会告诉模型哪个状态有效。

如果每轮都重新输入全部历史,窗口变大也不消除重复计算和调用成本。

如果来源关系在摘要前就没有保存,模型能读更多文本也无法恢复证据映射。

所以长上下文是一种资源,不是状态管理方案。它应该与结构化研究状态、外部证据存储和摘要机制组合使用。

哪些任务仍然最适合 ReAct?

第一,步骤较少的开放工具任务。

例如先搜索一个候选,再打开官方页面核实。下一步由当前结果决定,但整个链路不会很长。

第二,Observation 短且结构清楚。

工具返回几个字段,而不是整页网页。上下文增长慢,状态冲突也少。

第三,失败容易恢复。

某个工具失败后可以换一次来源,不会让之前几十步全部失效。

第四,任务结束条件明确。

找到特定事实、完成一次计算或验证一个条件后即可结束。

第五,团队需要先跑通最小闭环。

在还没有证明任务需要长程研究时,ReAct 是更简单的起点。过早引入复杂状态更新和摘要,也会增加新的错误面。

什么时候应该升级?

任务经常超过少量步骤,原始工具结果很长,重复和遗忘开始出现时,需要把原始历史和当前状态分开。

一种升级方向是 IterResearch。它不再把全部 Observation 当作唯一记忆,而是维护一份大小受控的演进报告。每轮用新观察更新报告,保留已确认结论、来源、冲突、缺口和下一步,临时网页正文放到外部。

如果演进报告和当前工具交互仍接近窗口上限,再增加 ReSum 动态摘要。摘要触发时必须保留关键事实和来源,不能只保留通顺结论。

升级顺序应该由问题推动:短任务用 ReAct;长任务出现历史堆积时引入受控状态;更长任务才使用动态摘要。不要因为框架名字更高级就默认上最复杂的一档。

ReAct、受控状态与动态摘要的升级路径

每一级只解决上一级已经真实出现的问题,避免为不存在的复杂度付费。

受控研究状态应该保留什么?

任务目标和硬约束必须保留,否则后续动作可能偏离用户要求。

已确认事实要带来源 ID、时间和适用条件,不能只剩一句结论。

冲突信息要同时保留支持和反对证据,不能在状态更新时静默选择一个。

信息缺口要明确列出,下一步动作才知道为什么执行。

已执行动作需要做去重,避免搜索同一查询或反复访问同一来源。

预算和停止状态也要保留,让 Agent 知道还允许做多少工作。

原始网页、完整工具返回和详细日志不必全部进入模型上下文,但要存在外部存储中,通过稳定 ID 可以重新读取。

状态越短越好吗?也不是。压得太狠会丢证据和限定条件。目标是受控、结构清楚、可以更新,而不是追求最小字符数。

怎样避免演进报告自己写错?

把全部历史压成一份报告,会产生新的单点风险。报告更新错了,后续步骤都基于错误状态。

所以更新过程要校验。

第一,关键字段使用固定 Schema,避免每轮自由改结构。

第二,来源 ID、数字、时间和否定关系做规则检查,不能在重写时悄悄变化。

第三,旧状态和新状态做差异比较。新增结论需要新证据,删除结论需要说明被推翻或不再相关。

第四,定期从外部证据重新核对关键 Claim,避免多次摘要后产生漂移。

第五,保留状态版本。出现错误时能回退到上一个可靠版本,而不是只能从头研究。

这也说明 IterResearch 不是银弹。它用状态更新的复杂度,换取对长上下文的控制。任务很短时,这笔交换未必划算。

ReAct 长任务怎么做故障诊断?

先看上下文组成。统计系统要求、用户问题、模型动作、工具结果和摘要分别占多少,不要只看总 Token。

再看重复率。相同意图的查询、重复 URL、相似正文和已经关闭的任务是否反复出现。

再看关键事实保留。把任务开始时的硬约束和中途确认的关键证据设为检查点,看后续步骤是否还能正确使用。

再看错误传播。某个错误主张第一次出现在哪一轮,后面哪些动作依赖它,是否存在纠错入口。

最后看停止行为。任务是证据充分后结束,还是跑到最大步骤被迫结束。

长程 ReAct 的诊断矩阵

上下文长度只是症状之一,还要定位噪声、重复、遗忘和错误传播。

怎样设计对照实验?

准备同一批研究问题,覆盖短任务、中等任务和长任务。

基线一使用朴素 ReAct,完整保留所有历史。

基线二仍用 ReAct,但工具只返回与当前目标相关的短内容,并增加查询和来源去重。

方案三使用受控演进报告,原始证据外置。

若任务足够长,再测试动态摘要方案。

每种方案观察当前上下文长度、累计输入量、重复动作、关键事实保留、来源映射、任务完成状态和最终报告质量。

不要只比较“能跑多少轮”。系统跑了更多轮,可能只是允许它更久地重复错误。真正的目标是在合理资源内完成可核验研究。

实验还要报告任务分布。短任务上,复杂状态方案可能没有优势;长任务上,受控状态才可能体现价值。把所有问题混成一个平均分,会掩盖选型边界。

从 ReAct 迁移时,不要一次重写全部系统

已有 ReAct 项目出现长任务问题时,可以先做最小改造。

第一阶段只收紧工具输出。Search 保留候选,Visit 根据当前目标抽取相关内容,不再把整页正文塞回上下文。同时保存原始页面,避免以后无法核验。

第二阶段增加动作和来源去重。即使还没有演进报告,也能先减少最明显的无效循环。

第三阶段提取受保护状态。把任务契约、已确认 Claim、来源、缺口和停止原因从聊天历史中独立出来,每轮都显式传入。

第四阶段才替换为演进报告更新。此时可以用前几个阶段收集的 Trace 对比报告是否丢信息,而不是在没有基线时直接切换框架。

第五阶段根据真实窗口压力决定是否引入 ReSum。若大多数任务在受控状态下已经完成,额外摘要只会增加调用和新的漂移风险。

渐进迁移还有一个好处:每一步都能独立回滚。若工具裁剪导致关键信息缺失,可以调整抽取;若报告更新不稳定,可以暂时保留旧执行器,而不必推翻整套 Agent。

状态更新失败时怎样降级?

演进报告本身也是一次模型或程序处理,也可能超时、格式错误或删除关键字段。

更新前先保存旧版本。新状态通过 Schema、来源 ID 和关键约束检查后再提交。校验失败时,可以重试一次结构修复,仍失败则继续使用旧状态并标记当前观察尚未合并,不能用损坏状态覆盖可靠版本。

如果某次工具结果特别长,可以先保存原文和抽取片段,再更新状态。不要在更新失败后把整页正文永久追加回主上下文,否则系统会悄悄退回最初的膨胀模式。

这类降级也要进入 Trace。否则团队只看到最终报告错误,却不知道是搜索、证据抽取还是状态更新阶段丢了信息。

四类 bad case 怎样修?

Bad case 一:同一搜索反复调用

建立标准化动作记录和已访问来源集合。检测到重复后返回可读提示,引导换维度或基于现有证据收口。只在 Prompt 里提醒“不要重复”通常不够。

Bad case 二:后面忘了用户硬约束

把任务契约放进受保护状态,每轮更新不能删除。测试时故意把硬约束放在任务开头,检查长轨迹后是否仍然遵守。

Bad case 三:错误版本污染后续结论

所有关键 Claim 带版本和来源。发现旧资料后,不只新增正确事实,还要把旧 Claim 标为失效,并检查依赖它的任务和结论。

Bad case 四:上下文没超限,但成本失控

查看每轮重复输入量、工具返回长度和无效动作。优先缩短 Visit 输出、外置原文、去重和提前终止,而不是只换更便宜模型。

面试官继续追问,怎么接?

追问一:ReAct 和 Workflow 怎么选?

步骤稳定、规则明确、需要强确定性时用 Workflow;下一步必须根据环境观察动态决定时用 ReAct。可以把高风险固定环节放在 Workflow,局部探索交给 Agent。

追问二:为什么不每轮都做摘要?

摘要需要额外调用,也可能丢信息。短任务没有必要;长任务应在状态增长或窗口压力出现时触发,并验证关键事实与来源保留。

追问三:IterResearch 真的能让上下文恒定吗?

更准确的说法是把主要工作状态控制在设定范围内。工具交互、系统要求和报告本身仍占空间,报告更新也可能丢信息,不能承诺绝对恒定或无限深度。

追问四:原始 Observation 不放上下文,模型需要时怎么办?

原始证据存外部库,状态里保留来源 ID 和摘要。需要复核时按 ID 重新读取相关片段,而不是永久携带所有正文。

追问五:怎样证明错误真的来自上下文?

做分层回放。固定模型和问题,分别使用完整历史、去重后的历史和受控状态,比较同一错误是否消失;同时定位错误 Claim 首次出现和传播路径,不能凭感觉归因。

怎样回答“ReAct 为什么越跑越重”

ReAct 的优势是让决策和行动交替进行,下一步可以根据当前 Observation 动态选择,所以很适合步骤少、工具结果短的任务。它在长程 Deep Research 里的问题,主要来自最朴素实现把所有动作和网页内容一直追加。当前上下文随步骤增长,每轮又要重读旧内容,临时噪声、旧结论和重复来源会污染状态,早期错误也会沿后续动作传播。

我不会因此直接放弃 ReAct,而会按任务长度逐层升级。短任务继续用 ReAct,同时限制 Visit 输出并做动作去重;长任务把原始证据放到外部,只维护一份包含任务约束、已确认事实、来源、冲突、缺口和下一步的受控研究状态;再接近窗口上限时才做动态摘要。状态更新需要固定 Schema、差异检查和版本回退,避免演进报告自己漂移。

验证时我会用短、中、长三类任务比较朴素 ReAct、去重和裁剪后的 ReAct、受控状态方案,观察上下文组成、累计输入、重复动作、关键事实保留、错误传播、停止原因和最终报告质量。轮数本身不决定胜负,能够以可控代价完成可核验研究才是验收重点。

ReAct 长任务改造检查清单

长程 ReAct 改造的验收清单

不要只画外置证据库架构;用短、中、长任务的对照记录证明状态压缩没有丢掉关键事实和来源。

  • 你的工具 Observation 平均多长,是否直接塞入整页正文?
  • 能否展示上下文随步骤增长的实际 Trace,而不是只说“会爆”?
  • 是否记录关键 Claim 第一次出现和被更新的过程?
  • 演进报告是否有固定 Schema、来源 ID 和版本?
  • 没有真实对照实验时,不要宣称上下文成本从某种复杂度降到另一种复杂度。

ReAct 并不过时。真正过时的是:把一条不断膨胀的聊天记录,当成长期研究系统唯一的记忆。

INTERVIEW FOLLOW-UPS

面试官会怎样继续追问?

这些追问会继续检查方案取舍、验证方法和项目事实。
  1. Q01

    ReAct 与固定 Workflow 应该怎样选?

  2. Q02

    为什么不在每一轮都生成一次摘要?

  3. Q03

    IterResearch 真的能让上下文保持恒定吗?

  4. Q04

    怎样证明错误来自上下文而不是模型能力?

PROJECT CHECK

换成你的项目,这几件事能否说清楚?

  • 测量工具结果和历史消息的上下文占比
  • 记录重复查询、来源和错误 Claim
  • 受控状态保留任务、来源、冲突和缺口
  • 使用短中长任务做框架对照
用自己的项目经历练一次