第 4 周 · Day 4:智能体与 ReAct

Transformers and Large Language Models · Week 4 · Day 4。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

时长 7 小时:§1 智能体是什么(0.5h)· §2 ReAct(1.5h)· §3 错误累积与可靠性(1h)· §4 预算与护栏(1h)· §5 记忆(0.5h)· §6 多智能体(1h)· §7 真实的难点(0.5h)· §A–§C(1h)

对应正课 Lecture 7 的最后一部分:智能体的定义、“传统 → 推理 → 智能体”三个层次、ReAct 框架(输入 → 观察 → 计划 → 行动 → 输出,恒温器与泰迪熊的例子)、多个智能体之间怎么通信(A2A 协议)、安全、Lecture 7 的结语。学习计划点名要会的:“Agent = LLM + 工具 + 记忆 + 循环。ReAct 的 Thought → Action → Observation 循环;Plan-and-Execute 的对比;多智能体编排的收益与开销。""Agent 的真实难点:错误累积(每步 95% 正确,20 步后只剩 36%)、无限循环、成本失控、工具调用幻觉。”

配套代码 w4day4_checks.py(纯 numpy):错误累积、检查与重试(公式与蒙特卡洛)、pass@k 与 pass^k 及其无偏估计、卡住的智能体与最大步数、上下文的增长与压缩、单智能体与多智能体的 token 和延迟

§B 参考实现(code/ 目录):w4d4_react_agent.py(文本格式与原生函数调用两种 ReAct、五条护栏、记忆压缩、JSONL 追踪、五个模拟场景、在黄金集上跑并导出答案给 Day 6 评测、A2A 风格的 AgentCard)。

今天的目标:能说清智能体和”调一次工具”的区别,以及什么时候该用工作流而不是智能体;能解释 ReAct 为什么要把推理和行动交替起来,写出它的循环和解析时的坑;能用公式说明错误累积,算出检查与重试能救回多少,区分 pass@k 与 pass^k;能设计一个智能体的护栏和记忆策略;能讨论多智能体的收益与代价。


§0 今天的主线

Day 3 的工具调用是”一问一答,中间调几次工具”。循环由程序写死:模型调用工具 → 程序执行 → 模型回答。智能体把这个循环的控制权交给模型:给它一个目标,它自己决定要做哪些步骤、按什么顺序、什么时候算完成。

传统 LLM:   问题 ──> LLM ──> 答案
推理模型:   问题 ──> LLM ──> 推理链 ──> 答案                               (第 3 周)
智能体:     问题 ──> LLM ──> 调用 ──> LLM ──> 调用 ──> … ──> 答案           (今天)
                        ↑_______ 观察结果 ______|

控制权交出去之后,三个新问题随之而来:怎么让模型会规划(§2 ReAct);长链条上的错误怎么办(§3);怎么防止它失控(§4 护栏、§5 记忆)。最后讨论把一个智能体拆成多个的得与失(§6)。

今天要用到的旧结论:思维链(Week 2 Day 3 §5.1);pass@k 的无偏估计(Week 3 Day 5 §5.1);工具调用的三步与消息格式(Day 3 §2.2);多轮调用的 token 账(Day 3 §2.3);出错时把错误还给模型(Day 3 §2.4);提示注入与最小权限(Day 3 §6);两阶段检索(Day 2 §4)。


§1 智能体是什么

1.1 定义与三个层次

Lecture 7 的定义:“智能体是一个自主地追求目标、代表用户完成任务的系统。“关键词是自主:下一步做什么由模型决定,而不是由程序员事先写好。

上面的图给出三个层次。推理模型已经会”想很久”(第 3 周),但它的思考全在自己脑子里;智能体的每一步思考都可以和外部世界交互:查资料、算数、执行动作,然后根据结果调整下一步。

1.2 LLM + 工具 + 记忆 + 循环

学习计划的公式:智能体 = LLM + 工具 + 记忆 + 循环。

部件作用这一周在哪里
LLM决策:分析现状、决定下一步前三周
工具感知与行动:获取信息、计算、改变外部状态Day 3
记忆记住做过什么、学到了什么;上下文是短期记忆,外部存储是长期记忆§5;RAG 是一种长期记忆(Day 1–2)
循环反复”观察 → 决策 → 行动”,直到完成或放弃§2

触发智能体的不一定是用户:Lecture 7 指出输入可以是用户手动输入的问题,也可以是一个外部事件(“某个指标超过了阈值”)。后者就是一个在后台常驻、按事件行动的智能体。

1.3 工作流还是智能体

“智能体”很吸引人,但它不是默认选项。Anthropic 的工程文章 “Building effective agents”(2024)做了一个有用的区分:

  • 工作流(workflow):LLM 和工具按预先写好的代码路径编排。常见的几种:提示链(上一步的输出是下一步的输入)、路由(先分类,再交给不同的处理流程)、并行(同时做几件事再汇总)、编排者–执行者(一个 LLM 拆任务,几个 LLM 执行)、评审–改写(一个生成,一个评判,循环改进)。
  • 智能体(agent):LLM 动态地决定自己的流程和工具使用。

它的核心建议是:先找最简单的方案,只在需要时增加复杂度。很多应用用一次带检索和示例的 LLM 调用就够了;步骤固定、可预测的任务用工作流;只有当步骤数和路径事先无法确定时才用智能体。原因是 §3 的错误累积:自主性越强,链条越长,出错的机会越多,也越难调试。Lecture 7 的结语说的是同一件事:“先从简单的做起,逐步迭代、扩大规模。”


§2 ReAct

2.1 为什么要把推理和行动交替起来

ReAct = Reason + Act(Yao et al. 2022)。它是在对比两种”只做一半”的方法之后提出的:

  • 只推理(思维链):模型在脑子里一步步推,但所有事实都来自它自己的记忆。常见的失败是幻觉(编造一个事实)和错误传播(一步错,后面全错)。
  • 只行动:模型直接一个接一个地调用工具,不写出推理。它缺少计划和追踪进度的能力:不知道自己已经做到哪一步、下一步为什么要做这个,在长任务里容易迷路。

ReAct 的想法:每一步都先写出思考(“现在知道什么、还缺什么、下一步做什么”),再行动,再根据观察更新思考。推理帮助行动(决定查什么、何时停),行动帮助推理(用真实的观察代替编造的事实)。

论文的结果:在需要查维基百科的问答(HotpotQA)和事实核查(FEVER)上,ReAct 通过调用一个简单的维基百科 API,缓解了思维链的幻觉和错误传播;在两个交互式决策任务(ALFWorld 文本游戏、WebShop 网上购物)上,ReAct 比模仿学习和强化学习的方法在成功率上分别高出 34 和 10 个百分点,而且只用了一两个示范。

2.2 循环:观察 → 计划 → 行动

Lecture 7 用恒温器的例子把循环画成了四个阶段:

输入:  我的泰迪熊很冷,想想办法。
观察:  泰迪熊冷,可能是因为房间温度低,但现在不知道房间温度。        ← 综合已知信息,说清还缺什么(推理最重的一步)
计划:  确定房间温度。                                                ← 决定要完成什么任务、调用什么工具
行动:  get_current_room_temperature()                                ← 通过 API 执行,或者去文档库里查
观察:  房间现在 65°F,比平均低 5°F。需要把温度调高。
计划:  把温度调高 5°F。
行动:  increase_temperature(value=5)
观察:  恒温器现在设在 70°F,应该够暖和了。
输出:  恒温器已调到 70°F,你的泰迪熊很快就会暖和起来。

从外面看,这整个循环就是一个”恒温器智能体”:输入一句话,输出一个结果,中间做了什么都封装在里面。

参考代码的 --demo 场景 1 是同一个结构:

[1] 思考:先在讲义里找 KV Cache 显存的公式在哪一节。
    行动:search_notes({"query": "KV Cache 显存 公式 估算", "k": 3})
    观察:[{"ref": "Week 1 Day 4 §1.4", "title": "…显存:一个 30 秒的公式", …}, …]
[2] 思考:检索结果指向 Week 1 Day 4 §1.4,读原文确认公式。
    行动:read_section({"week": 1, "day": 4, "section": "1.4"})
[3] 思考:公式是 2 × 层数 × KV 头数 × 头维 × 长度 × batch × 字节数……
    行动:kv_cache_gib({… "seq_len": 32768, "batch_size": 8})        观察:{"GiB": 32.0, …}
[4] 行动:calculator({"expression": "32.0 / (80e9 / 2**30)"})        观察:0.429497
[5] 最终答案:约 32.0 GiB,约等于 0.43 张 80 GB 显卡的容量……公式见 Week 1 Day 4 §1.4。

注意第 2 步:智能体没有直接相信检索结果的片段,而是去读原文。这正是 ReAct 的价值:思考让它意识到”片段可能不完整,要核实”。

2.3 提示格式与解析

ReAct 有两种实现方式:

  • 文本格式(原论文的做法):把工具说明写进系统提示,要求模型按”思考 / 行动 / 行动输入”的格式输出,程序用正则解析。任何能跟随指令的模型都能用,而且每一步的思考都清清楚楚。参考代码的 REACT_SYSTEM 和 parse_react。
  • 原生函数调用:思考写在回复的文字部分,行动是 tool_calls(Day 3 §2.2)。格式更可靠,还能一步并行多个调用;需要模型支持。参考代码的 --mode native。

文本格式的经典坑:模型自己编观察。模型在训练数据里见过大量”行动 → 观察 → 思考”的文本,写完”行动输入”之后,它常常顺手接着写一段”观察:“,然后基于这段编出来的观察继续推理。场景 4:

模型输出:思考:算一下 2 的 10 次方。
         行动:calculator
         行动输入:{"expression": "2**10"}
         观察:1000              ← 模型编的
         最终答案:1000

对策:解析时在”观察:“处截断,后面的一切丢掉(parse_react 会标记 faked_observation);调 API 时设停止词(stop sequence)为”观察:“,让模型写到那里就停(§A.1)。场景 4 里,真实的计算器返回 1024,智能体的最终答案也是 1024。

解析失败怎么办:行动输入写成了 Python 字典(单引号)、格式不对……不要去”猜”模型的意思,把具体的错误还给它(“行动输入不是合法的 JSON 对象,请用双引号”),让它改。这和 Day 3 §2.4 是同一个原则:错误信息越具体,模型越容易改对。

2.4 什么时候停

ReAct 的循环有三种正常的结束方式,和几种异常的:

停止原因含义参考代码
answer模型给出了最终答案正常
max_steps、max_tokens、timeout预算用完§4.2
repeated_action同一个动作重复太多次,判定为卡住§4.1
too_many_errors连续出错太多次§3.2

把停止原因记下来是调试智能体的第一步:一批任务里 30% 是 max_steps,说明要么步数上限太紧、要么模型在兜圈子,两种情况的修法完全不同。


§3 错误累积与可靠性

3.1 指数衰减

一个任务要连续做对 nn 步,每步独立地以概率 pp 做对,整体成功率是 pnp^n。w4day4_checks.py 第 1 组:

每步正确率5 步10 步20 步50 步
0.900.5900.3490.1220.005
0.950.7740.5990.3580.077
0.990.9510.9040.8180.605

每步 95% 正确,20 步后只剩 36%(学习计划的例子)。反过来算:要让 20 步的任务有 90% 的成功率,每步要做到 0.91/20=0.99470.9^{1/20}=0.9947。长任务对单步可靠性的要求是指数级的。这和 Week 2 Day 3 §5.1 思维链的错误传播、Week 3 Day 6 §3.2 “一步错整题就错”是同一个数学。

3.2 检查与重试

独立同分布的假设太悲观了:错误如果能被发现,就可以重试。设每步以概率 ss 做对;做错时,一个检查器以召回率 rr 发现错误,发现了就重试(最多 mm 次),没发现就带着错误继续。每步的最终成功率满足递推

Sm=s+(1−s) r Sm−1,S0=s⟹Sm=s∑j=0m((1−s)r)j →m→∞ s1−(1−s)r.S_m=s+(1-s)\,r\,S_{m-1},\quad S_0=s\qquad\Longrightarrow\qquad S_m=s\sum_{j=0}^{m}\big((1-s)r\big)^j\ \xrightarrow{m\to\infty}\ \frac{s}{1-(1-s)r}.

第 2 组(s=0.95s=0.95、n=20n=20,公式与 10 万次模拟吻合):

检查器召回 rr不重试重试 1 次重试 3 次
00.3580.3580.358
0.50.3580.5870.595
0.90.3580.8650.900
1.00.3580.9511.000

检查器的召回率比重试次数更关键:发现不了错误(r=0r=0)时重试多少次都没用;r=0.9r=0.9 时重试 1 次就从 0.358 升到 0.865,而每步平均只多花 0.04 次尝试。

设计智能体时的第一个问题因此是:“每一步的错误能不能被自动发现?” 提高 rr 的手段:参数校验(Day 3 validate_args)、工具明确地报错而不是静默返回空(Day 3 §2.4)、代码任务跑单元测试、关键中间结果加断言、让模型自己核对(召回率有限,但不为零)。编程智能体之所以是目前最成功的一类智能体(Lecture 7 的”个人最爱用例”),一个重要原因就是代码有编译器和测试,rr 天然很高。

3.3 一致性:pass@k 与 pass^k

同一个任务跑 kk 次,单次成功率 pp:

  • pass@k =1−(1−p)k=1-(1-p)^k:至少成功一次。适合”有验证器、可以多试几次挑一个”的场景(Week 3 Day 5 §5.1)。
  • pass^k =pk=p^k:kk 次全部成功。τ-bench(Yao et al. 2024)提出它来衡量可靠性:面向用户的智能体要对每一个用户都办对,而不是”试 8 次总有一次对”。

第 3 组:三个 pass@1 都约 0.5 的智能体:

智能体pass@1pass@8pass^2pass^8
A:每道题都是 0.5 的机会0.5000.9960.2500.0039
B:一半题必成、一半题必败0.5000.5000.5000.5000
C:成功率 ~ Beta(2, 2)0.5060.9490.3060.0542

同样的 pass@1,可靠性可以天差地别。A 每次都在碰运气(pass@8 很高、pass^8 几乎为零),B 会的题一定做对。τ-bench 的结果:最强的函数调用智能体(如 gpt-4o)在它的任务上成功率不到 50%,零售场景的 pass^8 不到 25%。

无偏估计:每个任务跑 nn 次、成功 cc 次,pass^k 的无偏估计是 (ck)/(nk)\binom ck/\binom nk(无放回地抽 kk 次全是成功)。朴素的 (c/n)k(c/n)^k 偏高:xkx^k 是凸函数,由 Jensen 不等式 E[(c/n)k]≥(E[c/n])k=pk\mathbb E[(c/n)^k]\ge(\mathbb E[c/n])^k=p^k。第 3 组:真值 0.1441,无偏估计 0.1472,朴素估计 0.1831。Day 5 会把 pass^k 放进基准测试的框架里。

3.4 设计原则

把 §3.1–3.3 翻译成设计原则:

  1. 减少步数:能用一个工具做完的,不要拆成三个;确定性的部分写成代码(工作流),不要让模型每次都重新决策(§1.3)。
  2. 让每一步可验证:工具返回明确的成功或失败;关键步骤后面加检查。
  3. 尽早失败:错误越早发现,浪费的步骤越少;参数不合法就立刻报错,不要等到下游才崩。
  4. 测可靠性,而不只是准确率:同一批任务跑多次,看 pass^k 和失败的分布。

§4 预算与护栏

4.1 卡住:循环与没有进展

智能体最常见的失控方式不是做了坏事,而是原地打转:用同样的参数反复调用同一个工具、在两个动作之间来回切换、或者一直”再查一次”却从不回答。场景 2(looping_policy)就是这样:同一个检索做了三遍。

参考代码的对策是重复检测:把”工具名 + 参数”规范化成一个键,记录出现次数。第 2 次出现时,在观察前面加一句提醒(“你已经执行过完全相同的动作,结果不会变”);超过上限(默认 2 次)就停止。它比固定的最大步数更早止损,也更精确地告诉你”为什么停”。

第 4 组:把每一步看成一次抽签(以 0.12 完成、0.02 放弃,其余继续),另有 5% 的运行一开始就卡住、永不结束。正常运行成功时的步数,95% 分位是 20:

最大步数 TT成功率被截断的正常运行平均每次运行的步数
100.6330.2115.8
200.7730.0477.4
300.8040.0108.2
500.8130.0019.3
4000.8130.00026.6

不设上限时,5% 卡住的运行让平均成本变成无穷大。上限取在”成功运行步数的 95% 分位”附近(这里 20–30 步),几乎不损失成功率,又把成本封顶。太小(10 步)会截掉大量本来能成功的运行;太大(400 步)时,卡住的 5% 吃掉了大部分预算。上限要从数据里定:先不设(或设得很大)跑一批,看成功运行的步数分布。

4.2 五条护栏

参考代码的 Budget:

护栏防的是什么怎么定
最大步数兜圈子、过度探索成功运行步数的高分位(第 4 组)
token 预算成本失控(上下文随步数平方增长,Day 3 §2.3)按单次任务的可接受成本倒推
时间预算用户等不了;外部 API 卡住按产品的延迟要求
重复动作上限原地打转(§4.1)通常 2–3 次
连续出错上限模型不会用某个工具、工具坏了通常 3 次;连续出错往往说明是系统问题,不是模型再试一次能解决的

护栏触发时要优雅地失败:告诉用户”没能完成,做到了哪一步、卡在哪里”,而不是返回一个半截的、看起来像答案的东西。

4.3 人在回路

有些动作不该让智能体独自决定(Day 3 §6.2):不可逆的(发送、删除、付款)、影响他人的、超出常规权限的。常见的做法是在这些动作之前暂停,请用户确认,其余的自动执行。这是自主性和安全之间的权衡点:确认太多,智能体就退化成了一个需要人一步步点”下一步”的向导。


§5 记忆

5.1 短期记忆:上下文与压缩

智能体的”短期记忆”就是它的上下文窗口:系统提示、问题、以及做过的每一步(思考、动作、观察)。它会一直增长,带来两个问题:成本随步数平方增长;迟早超出窗口。

第 5 组(每步新增思考 100 + 动作 50 + 观察 oo token):

观察长度步数不压缩:累计输入 / 最终上下文压缩(上限 16k,摘要 800):累计输入 / 压缩次数
30050651,250 / 24,500422,850 / 1
1500502,121,250 / 84,500533,750 / 5

压缩:上下文超过上限时,把旧的步骤压成摘要,只保留最近几步的原文。累计成本从平方变回接近线性。

代价:摘要会丢信息。参考代码的场景 5 把记忆上限设得很小:压缩之后,智能体不记得自己已经读过 Week 1 Day 4 §1.4 了,于是又读了一遍,再读一遍,最后被重复检测拦下(repeated_action)。丢掉的恰好是后面要用的,智能体就会重复劳动。

比”事后压缩”更好的是”事前少放”:工具只返回需要的字段(Day 3 §2.3);大的中间结果写进文件或外部存储,上下文里只留一个引用和一句说明;已经用完的信息(比如确认过的中间步骤)主动清掉。这类”决定上下文里放什么”的工作,现在常被称为上下文工程(context engineering),它对智能体表现的影响往往比换提示措辞大得多。

5.2 长期记忆

跨会话的记忆(“用户上周说过他用的是 A100”)需要存到上下文之外:

  • 向量记忆:把过去的对话、观察、总结切块嵌入,需要时检索回来。这就是 Day 1–2 的 RAG,只是语料换成了智能体自己的经历。生成式智能体(Park et al. 2023)按”相关性、新近性、重要性”三者的组合来检索记忆。
  • 结构化记忆:关键事实写成键值或表格(用户偏好、项目配置),读写都精确。
  • 分层管理:MemGPT(Packer et al. 2023)借用操作系统的思路,把上下文当”内存”、外部存储当”磁盘”,让模型自己调用函数在两者之间换入换出。

长期记忆的难点在写什么(全存会被噪声淹没,存得太少又会丢)和什么时候读(每步都检索太贵,不检索又会忘)。


§6 多智能体

6.1 为什么拆成多个

Lecture 7 的例子:一栋智能住宅里可以有恒温器智能体、人员感知智能体、空气质量智能体、能源管理智能体,各管一摊。把一个大智能体拆成几个专门的智能体,好处有三:

  • 专门化:每个智能体的提示、工具、权限都更少更聚焦(§4.1、Day 3 §4.1:工具越少选得越准)。
  • 并行:互不依赖的子任务同时做,延迟短得多。
  • 上下文隔离:每个子智能体在干净的上下文里工作,只把摘要交回;主智能体的上下文不会被子任务的细节撑爆(§5.1)。

代价是 token。第 6 组:任务可以拆成 kk 个互不依赖、各需 mm 步的子任务。单智能体在一个上下文里顺序做完;多智能体由一个编排者写 kk 份说明,kk 个子智能体各自做完交回摘要:

子任务数 × 每个步数单智能体:累计输入 / 顺序步数多智能体:累计输入 / 关键路径步数
2 × 327,000 / 631,400 / 5
4 × 5174,000 / 2099,600 / 7
8 × 102,136,000 / 80492,800 / 12

子任务少而短时,多智能体更贵(每个子智能体都要重付系统提示和说明);子任务多而长时,单智能体的上下文越滚越大,多智能体反而更省,并行还让延迟短得多。

一个真实系统的数字:Anthropic 的多智能体研究系统报告,智能体用的 token 大约是普通对话的 4 倍,多智能体约 15 倍;在他们内部的研究类评测上,多智能体(Opus 4 当主智能体、Sonnet 4 当子智能体)比单智能体的 Opus 4 好 90.2%;而在 BrowseComp 上,token 用量本身就解释了 80% 的表现差异。他们也明确指出:需要所有智能体共享同一份上下文、或者子任务之间依赖很多的领域(比如大多数编码任务),目前不适合多智能体。多智能体本质上是”用更多的 token 换更好的结果”,只有任务价值足够高、而且真正能并行拆分时才划算。

6.2 A2A:智能体之间怎么通信

几个智能体要协作,就需要一种通信方式。A2A(Agent2Agent,Google 2025 年提出)为此定义了标准(Lecture 7 展示的核心概念):

  • AgentCard(名片):智能体的名字、地址、版本,以及它有哪些技能。别的智能体靠它发现”谁能做什么、怎么找到它”。
  • AgentSkill(技能):每项技能有 id、描述和例子(“保持舒适温度:比如’我工作时切换到专注模式’”)。
  • AgentExecutor(执行器):真正处理请求的代码,至少实现”执行”和”取消”。

参考代码的 --agent-card 打印了一个讲义助教智能体的名片(两项技能:讲义问答、显存估算)。

MCP 与 A2A 的分工:MCP 连接应用和工具(工具是被动的,调用了才执行);A2A 连接智能体和智能体(对方是自主的,会自己规划、可能要花很久、可能中途需要更多信息)。一个智能体可以同时是 MCP 的客户端(用工具)和 A2A 的一端(和别的智能体协作)。

6.3 其他组织方式

  • 计划–执行(plan-and-execute):先让一个 LLM 写出完整的计划(步骤列表),再逐步执行,必要时重新规划。和 ReAct”走一步看一步”相比,它更省(不用每步都重新思考全局)、更可控(计划可以先给人审),但对意外的适应性差。
  • 反思(Reflexion,Shinn et al. 2023):任务失败后,让模型写一段”哪里做错了、下次怎么改”的反思,存进记忆,下次尝试时读进来。不更新权重,只用语言形式的”经验”改进,适合可以多次尝试的任务。
  • 评审–改写(evaluator-optimizer):一个 LLM 生成,另一个按标准评审,给出修改意见,循环直到通过。它把 §3.2 的”检查器”做成了一个 LLM。Day 6 的 LLM 评委是它的评审那一半。
  • 代码即行动:让模型直接写一段 Python 来调用工具(可以用循环、变量、条件),而不是一次输出一个 JSON 调用(CodeAct,Wang et al. 2024)。一段代码能完成好几次工具调用,步数更少;代价是必须在沙箱里执行(Day 3 §6.2)。

§7 真实的难点

学习计划列出的四个难点,每一个在今天都有对应的数字或机制:

难点本质今天的对策
错误累积pnp^n 的指数衰减(§3.1)减少步数、提高检查器召回(§3.2)
无限循环没有进展的动作重复(§4.1)重复检测、最大步数(从数据里定)
成本失控上下文平方增长、卡住的运行(§4、§5)token 预算、压缩、事前少放、并行
工具调用幻觉编造不存在的工具、编造观察(§2.3)明确的报错、在”观察:“处截断、停止词

Lecture 7 的结语:

  • 幻觉是个大问题;推理能力是瓶颈(微调有帮助但难做,新能力很受欢迎);评测很难(这是 Day 5–6 的主题)。
  • 先从简单的做起,逐步迭代、扩大规模;先用能力强的模型把事情做成,之后再考虑换小模型省钱。
  • 透明与可观测性帮助建立用户的信任,也让调试成为可能。参考代码把每一步写成一行 JSON(--ask 会写到 logs/w4d4_trace.jsonl):问题、每一步的思考、动作、观察、是否出错、停止原因、token 与耗时。一个智能体出了错,第一件事是读它的轨迹,而不是改提示。Day 6 §3 会用 Lecture 8 的失败模式分类来系统地读轨迹。

§A 智能体:API 用法与边界

A.1 停止词与格式

  • 停止词:文本格式的 ReAct 一定要设 stop=["观察:", "\nObservation:"](OpenAI 兼容接口的 stop 参数;transformers 用 stop_strings 或自定义的 StoppingCriteria)。否则模型会自己编观察(§2.3)。参考代码在解析时截断,两道保险更稳。
  • 温度:智能体的决策通常用低温度(0–0.3),让同样的状态产生同样的动作,便于复现和调试。需要多样性的地方(头脑风暴、多条路径搜索)再调高。
  • 原生函数调用时的思考:有的 API 在返回 tool_calls 时 content 为空。要保留”思考”,可以在系统提示里要求模型先写一句理由再调用,或使用支持推理内容的接口。

A.2 框架

LangGraph、LlamaIndex、smolagents、OpenAI Agents SDK、Claude Agent SDK 等都提供了智能体的骨架(循环、工具注册、记忆、追踪)。用框架之前,先确认你知道它在每一步给模型发了什么:

  • 框架的默认系统提示可能很长,而且每一步都重发(Day 3 §2.3 的成本)。
  • 默认的最大步数、重试策略、错误处理可能和你想要的不一样(§4.2)。
  • 出了问题要能看到原始的消息序列。能打印出每一次发给模型的完整请求,是选框架的一个硬指标。

参考代码只有几百行,没有依赖任何框架,目的就是让每一步都可见。

A.3 追踪与日志

  • 每一步记什么:输入(或其哈希)、模型输出原文、解析结果、工具调用与参数、工具返回(截断后)、耗时、token、是否出错。
  • 一次运行记什么:问题、停止原因、总步数、总成本、最终答案。
  • 不要记什么:密钥、个人敏感信息。工具的返回里可能有,写日志前要过滤。
  • 成熟的做法是用 OpenTelemetry 之类的追踪格式,接入专门的可观测性工具;参考代码的 JSONL 是最小版本,但原理一样。

A.4 评测智能体时的坑

  • 单次运行的结果不可信:同一个任务跑几次,看 pass^k(§3.3)。
  • 按停止原因分组看失败:max_steps 和 too_many_errors 是完全不同的问题(§2.4)。
  • “引用对了”不等于”答对了”:参考代码的 --eval 只算了一个便宜的代理指标(回答里引用的小节是否命中黄金答案的小节),回答的对错要交给 Day 6 的评委,最好再人工抽查。

§B 实操

  1. 跑通 w4day4_checks.py,6 组结论都看懂。第 2 组把 ss 改成 0.9,看要多大的 rr 才能让 20 步任务回到 80%;第 4 组改卡住的比例,看最优的 TT 怎么变。
  2. 推导(45min,纸笔):(a) 检查与重试的递推与闭式解;(b) pass^k 无偏估计为什么无偏,朴素估计为什么偏高;(c) 第 6 组单智能体和多智能体的 token 公式。
  3. 读懂智能体的每一条路径(1h):python code/w4d4_react_agent.py --demo,逐个场景对照 §2–§5。然后自己写一个新的模拟策略:让它在两个动作之间来回切换(A、B、A、B……),看现在的重复检测能不能拦住,不能的话改代码。
  4. 真模型(1.5h,需要一个 LLM):用 Ollama 或 vLLM 起一个 7B 左右的模型,跑 --ask(文本模式和 --mode native 各一次),读 logs/w4d4_trace.jsonl。再跑 --eval --n 20,统计停止原因的分布、平均步数、引用命中率。小模型最常见的失败是什么?
  5. 把 --eval 的答案留好:logs/w4d4_answers_text.jsonl 是 Day 6 用 LLM 评委打分的输入。
  6. 读论文:ReAct(Yao et al. 2022)的第 2–3 节;Anthropic 的 “Building effective agents”;挑读 τ-bench(Yao et al. 2024)关于 pass^k 的部分。

参考实现:code/w4d4_react_agent.py。


§C 自测

动机与直觉层

  1. 智能体和”调用一次工具”的区别是什么?什么时候应该用工作流而不是智能体?
  2. ReAct 相比”只推理”和”只行动”各解决了什么问题?
  3. 为什么编程智能体比很多别的智能体更成功?(用 §3.2 的语言回答)
  4. 多智能体什么时候划算,什么时候不划算?
  5. MCP 和 A2A 各连接的是什么?

推导与计算层

  1. 每步 95% 正确,20 步的成功率是多少?要 90% 的成功率,每步要多准?
  2. 推出”检查器召回 rr、最多重试 mm 次”时每步的成功率,并说明为什么 rr 比 mm 更重要。
  3. 定义 pass@k 与 pass^k,举一个 pass@1 相同但 pass^8 相差 100 倍的例子。写出 pass^k 的无偏估计。
  4. 智能体的上下文每步增加 1000 token,初始 3000 token,50 步的累计输入是多少?压缩能把它降到什么量级?

工程层

  1. 文本格式的 ReAct 为什么会”自己编观察”?有哪两道防线?
  2. 设计一个智能体的五条护栏,说明每条的阈值怎么定。
  3. 记忆压缩会带来什么问题?除了压缩,还有哪些办法控制上下文的增长?
  4. 你的智能体在一批任务上 30% 停在 max_steps,你会怎么排查?

出关标准

  • 能说清智能体的定义、四个部件,以及”先工作流、后智能体”的理由
  • 能写出 ReAct 的循环,说出文本格式解析的坑和对策
  • 能推出检查与重试的成功率,解释为什么提高检查器召回比多重试更重要
  • 能区分 pass@k 与 pass^k,写出 pass^k 的无偏估计
  • 能设计护栏(阈值从数据里定)和记忆策略,说出压缩的代价
  • 能讨论多智能体的收益与代价,说出 A2A 的核心概念
  • 跑通 --demo 的五个场景;有模型的话跑通 --eval 并留下答案文件

疑问清单

← 返回课程目录