跳到正文

第 2 章:Agent 执行框架,ReAct → IterResearch

讲清 ReAct 为什么在长程调研里会「上下文爆炸」,IterResearch 怎么用「演进报告」把它压成常量空间,并能在 ReAct / IterResearch / ReWOO / Reflexion 之间按场景选型

📊 学习时长:55-70 分钟 🎯 完成后能力:讲清 ReAct 为什么在长程调研里会「上下文爆炸」,IterResearch 怎么用「演进报告」把它压成常量空间,并能在 ReAct / IterResearch / ReWOO / Reflexion 之间按场景选型 🔗 关联面试题:5 道(覆盖 5 个角度,见 Part 3)

这一章你会学到什么

  • ✓ 零依赖也能跟着做:用一段代码亲眼看到 ReAct 的上下文怎么「线性暴涨」,而 IterResearch 怎么「纹丝不动」
  • ✓ 看懂 ReAct 的核心:推理(Reasoning)和行动(Acting)交织,以及它的消息格式怎么设计
  • ✓ 想透 ReAct 在长任务里的两个硬伤:上下文爆炸 + 信息污染
  • ✓ 掌握 IterResearch 的关键创新:把调研看成一个「状态机」,用演进报告当常量大小的中央记忆
  • ✓ 学会一条「问题逐层加码、方案逐层升级」的主线:ReAct → IterResearch → ReSum
  • ✓ 建立框架选型直觉:ReAct / IterResearch / ReWOO / Reflexion / Research-Synthesis 各适合什么,以及测试时扩展怎么用计算换质量

本章按三段式组织:

段落 给谁看 内容 占比
🚀 Part 1 · 主线实战 零基础,想先跑起来 ReAct 原理 + 消息格式 + 上下文增长对比 demo ~45%
🎯 Part 2 · 面试深度 想讲透、备战面试 IterResearch / ReSum / 其他框架 / 选型 / 测试时扩展 + 私货 ~45%
🏆 Part 3 · 验收串题 检验学到位没 关联面试题 + 自检清单 ~10%

🚀 Part 1 · 主线实战 | ~45% 这部分零依赖,直接跑。你会亲眼看到「为什么长任务下 ReAct 会撑不住」,这是理解后面一切的前提。

先讲个真实场景(这是本章的「为什么」)

上一章那个「三选一」选型问题,我们的调研 Agent 跑通了。但当工程师把问题换成更狠的:

「帮我梳理近一年主流开源向量库的能力演进格局,列出主要玩家、各自的索引 / 部署形态 / 生态,和最新进展。」

这个问题逼着 Agent 查了二十多轮:一个个项目查、一版版更新翻、索引和部署形态交叉比对。结果跑到第十几轮,它开始胡言乱语,把 A 项目的特性安到 B 项目头上、把一年前的旧版本当成最新的。

扒日志一看,根因不是模型变笨了,而是:我们用最朴素的 ReAct,把每一轮搜到的文档正文,原样堆进上下文。二十多轮下来,上下文又长又乱,一方面快撑爆模型的窗口,另一方面前面几十段不相关的旧文档内容,把模型的注意力彻底稀释了。

一句话:ReAct 在短任务里又快又好,但「把观察只增不减地堆进上下文」这个朴素做法,扛不住长程调研。 这一章,就是讲怎么扛住它。

概念 1:ReAct 是什么

ReAct(Reasoning + Acting),是最基础也最常用的一种 Agent 框架。核心一句话:把「想」和「做」交织起来,想一步(Thought)、做一个动作(Action)、看观察结果(Observation),再想下一步,如此循环。

上一章那个 mini-agent 就是一个最朴素的 ReAct:思考 → search → 观察 → 再思考。它的循环长这样:

消息格式怎么设计

ReAct 靠特定标签区分每一类内容,让程序能解析、让模型能学:

  • Thought: 模型的推理(我现在该干什么)
  • Action: 要调用的工具和参数(search("..."))
  • Observation: 工具返回的结果(由程序填进去,不是模型生成的)
  • 循环往复,直到模型输出 Final:(最终答案)

这套「标签化的对话」就是 Agent 的「汇编语言」,朴素、通用、好实现。绝大多数 Agent 框架,本质都是在这套循环上做加法。

概念 2:ReAct 的两个硬伤(长任务下)

ReAct 有个朴素做法:每一轮的观察(搜来的文档正文),都原样塞回上下文。短任务没事,但调研类任务动辄几十轮,两个问题就暴露了:

  • 上下文爆炸:观察越堆越多,很快撑爆模型的上下文窗口(开篇那个二十多轮就是这么崩的)。
  • 信息污染:前面几十段临时文档内容混在一起,大部分对「当前这一步」根本没用,却稀释了模型的注意力,越查越迷。

这两个硬伤,不是把模型换强一点就能解决的,它是**「只增不减地堆上下文」这个结构**本身的问题。下面我们用代码把它「逼」出来给你看。

🛠️ 跟着做:亲眼看上下文怎么「越堆越长」

下面这段零依赖代码,对比两种做法在 6 轮调研后的「上下文大小」。新建 ctx_growth.py:

# 零依赖,直接 python ctx_growth.py
# 模拟 6 轮工具返回,每轮约 200 字的文档正文
observations = [f"第{i}轮搜到的文档内容:" + "正文" * 100 for i in range(1, 7)]

def react():
    """ReAct:每轮观察都原样堆进上下文"""
    context = "系统提示 + 用户问题"
    for i, obs in enumerate(observations, 1):
        context += "\n[思考]\n" + obs            # ← 观察全堆进去,只增不减
        print(f"ReAct  第 {i} 轮后,上下文 = {len(context):>5} 字符")

def iter_research(report_cap=320):
    """IterResearch:只维护一个固定大小的「演进报告」"""
    report = "【演进报告】"
    for i, obs in enumerate(observations, 1):
        # 把新观察「融进」报告:只保留确认的关键结论,丢弃临时正文,大小受控
        report = ("【演进报告】已确认要点:" + "; ".join(f"结论{j}" for j in range(1, i + 1)))[:report_cap]
        print(f"Iter   第 {i} 轮后,报告   = {len(report):>5} 字符(上限 {report_cap})")

react(); print(); iter_research()

运行

python ctx_growth.py

你应该看到(ReAct 线性暴涨,IterResearch 稳在上限内,这是确定性输出,你跑出来一模一样):

ReAct  第 1 轮后,上下文 =   228 字符
ReAct  第 2 轮后,上下文 =   445 字符
ReAct  第 3 轮后,上下文 =   662 字符
ReAct  第 4 轮后,上下文 =   879 字符
ReAct  第 5 轮后,上下文 =  1096 字符
ReAct  第 6 轮后,上下文 =  1313 字符

Iter   第 1 轮后,报告   =    15 字符(上限 320)
Iter   第 2 轮后,报告   =    20 字符(上限 320)
Iter   第 3 轮后,报告   =    25 字符(上限 320)
Iter   第 4 轮后,报告   =    30 字符(上限 320)
Iter   第 5 轮后,报告   =    35 字符(上限 320)
Iter   第 6 轮后,报告   =    40 字符(上限 320)

看那两列数字:ReAct 每轮稳定 +217,一路线性涨;IterResearch 始终被 report_cap 摁在 320 以内。 轮数越多,差距越夸张。

🐛 跑不通看这里

  • 报错 SyntaxError?这段是 Python 3,确认用 python3 跑。
  • 数字和上面差一两位?正常,取决于你机器上字符串长度的细微差异(比如换行符计法),趋势才是重点:ReAct 一路涨、Iter 被摁住。
  • 看不出差距?把 range(1, 7) 改成 range(1, 30) 再跑(见下方动手实验)。

🛠️ 动手实验:把轮数从 6 改成 30

把两处 range(1, 7) 都改成 range(1, 30),再跑一次。你会看到 ReAct 的上下文冲到 6000+ 字符(真实场景里就是这样,几十轮直接撑爆几千 / 几万 token 的窗口),而 IterResearch 的报告几乎纹丝不动,稳在两百字符以内。这就是 IterResearch 能支持「无限深度」调研的根本原因,用常量大小的工作空间,换取无限的调研深度

📌 说清楚:这是教学最小实现。真实的「演进报告」不是简单截断字符串,而是用一次 LLM 调用,把「旧报告 + 新观察」重写成一份更新后的报告(保留关键结论、丢弃临时正文);这里用字符串拼接 + 截断,只为让你零成本看到「常量 vs 线性」这个本质差别。真实做法见下面 Part 2。

🧭 你刚才做的,等于真实系统的什么? 你写的 iter_research() 把「堆全部历史观察」换成了「维护一份大小受控的报告」,这正是 IterResearch 的核心思想。真实系统里,这份报告由模型每轮智能地重写,而不是机械截断。


🎯 Part 2 · 面试深度 | ~45% 这部分讲透 IterResearch 的设计、ReSum 兜底、其他主流框架,以及怎么选型,这些是 Agent 岗面试的高频深水区。

2.1 IterResearch:把调研看成一个「状态机」

IterResearch 的关键创新,是不再把「全部历史」喂给模型,而是维护一个固定大小的「演进报告」(Evolving Report)当中央记忆。你可以把整个调研过程理解成一个状态机(学术上常用马尔可夫决策过程 MDP 来刻画):

  • 状态(State) = 当前的演进报告,大小固定;
  • 每一轮:模型读「报告 + 最新观察」→ 把有用的信息融进报告、丢弃临时内容 → 决定下一个动作;
  • 关键:每轮只携带固定大小的状态往前走,临时信息用完即弃,从根上避免上下文污染。

注:把调研「看成 MDP」是一种便于理解和工程化的建模视角(当前状态决定下一步,与更早的历史无关),不必纠结它是否严格满足马尔可夫性,重点是「只带固定状态往前走」这个工程思想。

演进报告怎么设计

演进报告是 IterResearch 的灵魂,它要同时满足三个要求,这三个要求互相拉扯,是设计的难点:

  • 信息完整:已发现的关键结论都得在,不能丢;
  • 结构清晰:分点、带出处,模型好读也好更新;
  • 大小可控:不能无限增长,否则又退化成 ReAct。

实务里,报告通常按「已确认结论 / 待验证假设 / 信息缺口 / 下一步计划」这样的结构组织,既是中央记忆,也天然就是最终调研报告的雏形。

2.2 上下文还兜不住怎么办:ReSum 动态摘要

就算用了演进报告,有些超长任务(比如要查上百个来源)还是会逼近上下文上限。这时再加一层 ReSum(动态摘要):当上下文将满时,触发一次摘要,把历史压缩成精华,再带着精华继续

把这三者串起来,就是一条非常漂亮的「问题逐层加码、方案逐层升级」的主线:

  • ReAct:朴素堆叠,短任务够用
  • IterResearch:演进报告把状态控成常量,扛住长程调研
  • ReSum:上下文真要满了,动态摘要兜底,扛住超长任务

业内行话:面试聊到「长任务上下文管理」,能把这三层串起来讲(每一层解决上一层扛不住的那个新问题),而不是只甩一个「IterResearch」的名词,面试官立刻知道你不是看过一篇博客,而是真在长任务里被上下文坑过、并且知道每一层在治什么病。这条「问题在哪、方案怎么逐层升级」的叙事,是本章最值钱的面试资产。

2.3 其他主流框架(建立坐标系)

ReAct 不是唯一的执行框架。面试常问「你还知道哪些」,知道这几个的取舍就够:

ReWOO(Reasoning WithOut Observation):先规划,再批量执行。

模型先把「要做哪几步、每步用什么工具」一次性规划好,再批量执行、最后汇总。好处是省 LLM 调用(不用每步都问一次模型);代价是不够灵活,计划定死了,中途发现方向错了不好改。适合「步骤相对确定」的任务。

Reflexion:引入自我反思。

Agent 做完一轮后,自己反思「哪里做得不好」,把教训记下来,下一轮带着教训重做。好处是能在多次尝试里自我改进;代价是更慢更贵。适合「允许多次尝试、追求质量」的任务。

Research-Synthesis:并行多 Agent,再综合。

同时跑多个独立的调研 Agent(从不同角度查),最后把结果综合。好处是提高可靠性(交叉验证、降低单条链路跑偏的影响);代价是成本翻倍。适合「对可靠性要求高、预算充足」的场景。

2.4 怎么选:一张决策图

框架 一句话 适合
ReAct 想一步做一步 短任务、几轮就完,首选(简单直观)
IterResearch 演进报告控常量 长程调研,几十轮
ReSum 动态摘要兜底 超长任务,上下文要爆
ReWOO 先规划后批量 步骤确定,想省调用
Reflexion 自我反思迭代 允许多试、追质量
Research-Synthesis 并行多 Agent 重可靠性、预算足

讲师私货:别把 IterResearch 当银弹、无脑上。短任务用 ReAct 又快又简单,硬上 IterResearch 反而增加「维护演进报告」的开销和出错面(报告写错了,后面全错)。判断点还是那句话:任务会不会长到把上下文堆爆? 会,才上 IterResearch;再要爆,才叠 ReSum。选最低够用的那档,这和上一章「该不该上 Deep Research」是同一种工程审美。

2.5 测试时扩展(Test-Time Scaling):用计算换质量

最后一个高频考点:推理时,怎么用「多花点计算」来换「更好的结果」?

  • Best-of-N:同一个问题跑 N 次(不同采样),再选/综合最好的一个,简单粗暴但有效。
  • 自我检查与修正:让模型对自己的答案再检查一遍、发现问题就修,类似 Reflexion 的轻量版。

核心思想:训练时的能力是上限,但推理时多投入计算,能把这个上限更充分地兑现出来。代价当然是延迟和成本,所以也要按场景权衡。

🧭 这一章到这儿:执行框架的原理、ReAct 与 IterResearch 的取舍、推理侧实现都讲透了,demo 也能跑起来。但有个根本问题本章没解决:模型本身「会不会用这些框架」?一个没经过训练的通用模型,未必懂得「该收手时收手、该更新报告时更新报告」。怎么用数据和强化学习把它训得真正擅长这些,正是第 5-6 章的主题。


🏆 Part 3 · 验收串题 | ~10% 学到这一步,做几道题验证一下。点进去是题库里的完整解析。

关联面试题(5 道,覆盖 5 个角度)

  1. 【ReAct 工作流】 以 ReAct(Reasoning + Acting)框架为例,详细解释它的工作流:Thought / Action / Observation 怎么交织?
  2. 【长上下文表现】 ReAct 与 Plan-and-Execute 在处理长上下文任务时各有什么表现差异?
  3. 【框架取舍】 ReAct 与 Plan-and-Execute 两种推理框架的取舍是什么?什么场景选哪个?
  4. 【主流框架对比】 系统比较主流 Agent 框架(LangChain / LlamaIndex 等)的架构差异。
  5. 【长上下文改进】 如何设计 / 改进大模型的记忆机制,提升长上下文建模与任务持续能力?

自检清单

  • 能讲清 ReAct「推理 + 行动交织」的核心思想和消息格式(Thought/Action/Observation)
  • 能在本机跑通上下文增长 demo,并解释 ReAct 为什么会爆、IterResearch 为什么不会
  • 能讲清 IterResearch「演进报告 = 常量大小中央记忆」的设计,以及报告的三个要求
  • 能把 ReAct → IterResearch → ReSum 三层「问题逐层加码、方案逐层升级」串成一段叙事
  • 能说出 ReWOO / Reflexion / Research-Synthesis 各自的取舍,以及框架选型的判断标准
  • 能讲清测试时扩展(Best-of-N / 自我检查)是「用计算换质量」

学完这一章,你已经理解了 Agent 执行框架这条主线,从 ReAct 的朴素,到 IterResearch 的常量工作空间,再到一系列变体的取舍。下一章,我们深入「工具集」:Search / Visit / 计算器 怎么设计,以及统一工具网关、容错和缓存怎么做,让 Agent 真正「会用工具」。