一、下一步查什么,取决于上一步查到什么
第 25 讲留下过一个单次检索解决不了的问题。现在把它放进一个具体的场景里。员工小王在系统里提问:
“我要帮我的直属上级订出差酒店,他住宿每晚最多能报多少?”
公司的制度库比第 25 讲多了一条:
块 4:报销:出差住宿标准为每晚不超过 500 元。
块 5:报销:销售部员工出差住宿标准为每晚不超过 800 元。
要回答这个问题,需要三步:
① 小王的直属上级是谁? → 查员工信息
② 这位上级在哪个部门? → 再查一次员工信息,用的是第 ① 步查到的名字
③ 这个部门的住宿标准是多少? → 查制度库,用的是第 ② 步查到的部门
⭐ 关键在于箭头:第 ② 步要查谁,只有第 ① 步做完才知道;第 ③ 步要查哪个部门,只有第 ② 步做完才知道。 这条路线没法在开始时就完整写好,只能走一步看一步。
第 27 讲讲的是一次调用:模型提出请求,门外执行,结果写回,模型回答。这一讲把它变成一个循环,让模型可以根据每一次的结果,决定下一次做什么。能这样自己一步步推进任务的系统,常被称为智能体(agent)。
二、打个比方:在陌生的城市问路
你在一座陌生的城市里找一家小店,手上没有地图。
你不可能在出发前规划好全部路线,因为你根本不知道路怎么走。你能做的是:走到一个路口,问一个人;根据他的回答,走到下一个路口;再问一个人。 每一次的回答,决定你下一步往哪走。
中途还可能出岔子:有人指错了路,你走到一条死胡同。这时你需要意识到走错了,退回去重新问。如果你没意识到,就会带着这个错误继续往下走,离目的地越来越远。
这两件事——根据反馈决定下一步,以及察觉并纠正错误——就是这一讲的全部内容。
三、循环:想一步、做一步、看结果
循环本身
把第 27 讲的四步放进一个循环里:
重复:
① 模型读当前的全部上下文,生成下一段内容
② 如果生成的是调用请求 → 门外执行,把结果追加进上下文,回到 ①
如果生成的是最终回答 → 结束
循环的每一轮,上下文都会变长一点:多了模型写的内容,多了工具返回的结果。模型在下一轮读到的,是到目前为止发生的一切。
ReAct:把"想"写出来
一种被广泛采用的写法叫 ReAct(取自 Reasoning 和 Acting):每一轮,模型先写下一段思考——现在知道了什么、还缺什么、下一步打算做什么——然后再发起调用。之后门外追加的结果,叫作观察。于是整个过程变成"思考、行动、观察"的反复交替。
为什么要把思考写出来?第 19 讲已经给出了答案:写下的 token 就是草稿纸。 模型每个 token 的计算量是固定的,把"已经查到李经理"“还需要查他的部门"写下来,后面的步骤就可以通过注意力直接读到这些中间结论,而不必每一轮都从头在一次前向传播里重新理清全部线索。第 19 讲在那一讲的结尾预告过:这一讲要把"写下中间步骤"扩展到"中间步骤里还可以调用外部工具”。ReAct 就是这个扩展。
一条完整的执行轨迹
可用的工具有两个:get_employee(按姓名查员工信息)和 search_policy(第 27 讲的制度检索)。沿用第 27 讲的角色标记:
[系统] 可用工具:get_employee(name)、search_policy(query)……
[用户] 我要帮我的直属上级订出差酒店,他住宿每晚最多能报多少?(提问者:小王)
[助手] 思考:需要先知道小王的直属上级是谁。
<call>{"name": "get_employee", "arguments": {"name": "小王"}}</call>
[工具结果] 姓名:小王;部门:市场部;直属上级:李明
[助手] 思考:上级是李明。住宿标准可能按部门不同,需要知道李明在哪个部门。
<call>{"name": "get_employee", "arguments": {"name": "李明"}}</call>
[工具结果] 姓名:李明;部门:销售部;直属上级:张华
[助手] 思考:李明在销售部。查销售部的出差住宿标准。
<call>{"name": "search_policy", "arguments": {"query": "销售部 出差住宿标准"}}</call>
[工具结果] 块 5:报销:销售部员工出差住宿标准为每晚不超过 800 元。
块 4:报销:出差住宿标准为每晚不超过 500 元。
[助手] 您的直属上级李明在销售部,按公司制度,销售部员工出差住宿每晚不超过 800 元。
几个细节值得注意。
第二次调用的参数"李明",来自第一次调用的结果。 第三次的"销售部",来自第二次的结果。这就是第一节那两个箭头。
小王自己在市场部,不在销售部。 如果模型想当然地用提问者的部门去查,会得到通用的 500 元,给出一个错误的答案。第二轮的思考"需要知道李明在哪个部门",正是在避免这个错误。
检索回来了两块。 块 4 是通用标准,块 5 是销售部的特别规定。和第 25 讲一样,选哪一条是模型读完之后的判断。
模型一共被运行了四次,每一次都读到了之前的全部内容。
不是所有任务都要一步一步来
如果几步之间没有依赖,就没必要排队。第 27 讲的思考题问过:“年假和病假加起来一年有几天?“两次检索互不依赖,模型可以在同一轮里同时发起两个调用,门外并行执行,一轮就拿到两个结果。
也有些任务,步骤在开始时就能完整列出。这时可以先规划、后执行:模型先写出完整的计划,再按计划逐项调用。它比逐步决定更省——不用每一步都重新思考——但计划一旦被某个意外结果推翻,就需要重新规划。
⭐ 所以选哪种方式,取决于步骤之间的依赖:互相独立的,并行;能事先列出的,先规划;后一步依赖前一步结果的,只能一步一步来。
四、第一笔账:错误会连乘
成功率的乘法
假设每一步正确完成的概率是 95%——调用选对了工具、参数填对了、读结果没读错。一个需要 n 步的任务,全部做对的概率是:
| 步数 | 每步 95%,全部做对的概率 |
|---|---|
| 3 | 0.95³ ≈ 85.7% |
| 10 | 0.95¹⁰ ≈ 59.9% |
| 20 | 0.95²⁰ ≈ 35.8% |
单步看起来很可靠的 95%,到 20 步时只剩三分之一多一点。
这个乘法你见过两次。第 4 讲的链式法则:一整句话的概率,是每一步概率的乘积。第 13 讲提醒过:生成时模型读的是自己写出的历史,前面错一步,后面会在错误的基础上继续。多步任务把同一件事放大到了"每一步是一次工具调用"的尺度上:第 ① 步查错了上级,后面两步做得再好,答案也是错的。
反馈能把乘法扳回来
但智能体和普通的逐词生成有一个关键区别:它在每一步之后,都能看到外部世界的反馈。
如果第 ① 步把名字写错了,get_employee 会返回"查无此人”。模型读到这个观察,可以意识到出了错,修正参数再试一次。假设出错时总能被察觉、重试一次,且两次出错互相独立,每一步的失败概率就从 5% 降到 5% × 5% = 0.25%:
| 步数 | 不重试 | 出错可察觉、重试一次 |
|---|---|---|
| 3 | 85.7% | 99.3% |
| 10 | 59.9% | 97.5% |
| 20 | 35.8% | 95.1% |
⭐⭐ 20 步的任务,成功率从 35.8% 回到 95.1%。长任务能不能完成,关键不在每一步有多准,而在错误能不能被察觉和纠正。 这就是第二节比方里"意识到走错了,退回去重新问"的分量。
⚠️ 但这张表有一个前提:错误能被察觉。工具报错、返回空结果,这些是看得见的错误。更危险的是看不见的错误:第 ① 步查到了一个真实存在、但并不是小王上级的人,工具正常返回,没有任何报错,模型也就没有理由怀疑。这种错误不会触发重试,会被原样带到最后。第 19 讲说有验证程序的任务更好训练,这里是同一个道理:有没有可靠的反馈,决定了错误能不能被发现。
五、第二笔账:上下文越堆越长
上下文的增长
每一轮,上下文都会多出模型的思考、调用请求和工具结果。用一组示意数字:系统说明和工具说明共 2,000 个 token,每一轮新增约 500 个 token(思考约 100、调用约 50、工具结果约 350),任务一共 20 轮。
第 1 轮开始时: 2,000 个 token
第 20 轮结束时: 2,000 + 20 × 500 = 12,000 个 token
如果每一轮都把全部上下文重新处理一遍,20 轮一共要处理的输入 token 是:
2,000 + 2,500 + 3,000 + … + 11,500 = 135,000 个 token
如果利用第 22、24 讲说的缓存复用,前面已经处理过的部分直接复用 KV Cache,每一轮只处理新增的 500 个:
2,000 + 20 × 500 = 12,000 个 token
两者相差十倍以上。但缓存复用的前提是这些缓存一直占着显存:到最后一轮,按第 22 讲的示意数字,这段对话的 KV Cache 约 5.9 GB,而且在整个任务持续的时间里一直占着。第 24 讲算过,缓存大小和占用时长都随长度增长,两者相乘,是真正的成本。
撞上第 23 讲的墙
第 23 讲说过,上下文越长,第四堵墙越明显:softmax 的权重总和是 1,关键信息分到的注意力被稀释;放在中间的内容容易被忽略。一个运行了几十轮的智能体,最初的用户要求、第 3 轮查到的关键结果,都被埋在一长串后续的观察下面。
⚠️ 关于时效性:应对这个问题的做法还在快速发展,常见的思路有三类。压缩:定期把前面的历史总结成一小段摘要,替换掉原文。外部记忆:把中间结果写进文件或数据库,需要时再检索回来(第 25、26 讲)。分派:把一个子任务交给另一个上下文全新的模型实例去做,只把它的结论带回来。三种思路各有代价——压缩就是第 23 讲说的"压缩进固定状态”,必然丢失细节;外部记忆依赖检索能不能找回来;分派之后,主流程看不到子任务的中间过程。
六、第三笔账:什么时候停
循环在模型给出最终回答时结束。但如果它一直不给呢?
模型可能反复发起同一个失败的调用,一遍又一遍——第 21 讲讲过,重复的内容会让继续重复显得更"合理"。它也可能在两个工具之间来回打转,或者为一个早就可以回答的问题不断查更多资料。
第 27 讲的原则在这里再次适用:能做什么,由门外的程序决定。 循环该在什么时候被强制停下,也不应该依赖模型自觉:
- 步数上限:最多执行多少轮,超过就停止并报告。
- 预算上限:最多消耗多少 token、多少时间、多少次付费调用。
- 重复检测:同一个调用、同样的参数连续出现,就中断。
- 高风险确认:第 27 讲的不可撤销操作,在长流程里同样要停下来等用户确认。
七、这种能力怎么训练
SFT:用完整的多步轨迹做示范。第 27 讲的规则原样适用——模型写的思考和调用请求计算损失,工具结果不计算损失。
强化学习:第 19 讲的 RLVR 可以用在这里:任务最终完成了没有(订单是否正确生成、问题是否答对、代码是否通过测试)作为奖励,让模型在自己生成的轨迹上反复尝试。第 20 讲说过,在模型自己的轨迹上训练,能让它在"自己真正会犯的错"上得到反馈——对多步任务来说,这恰恰是学会"察觉错误、退回重试"的途径。
但第 19 讲的两个难点在这里被放大了。一条轨迹几十步,只在最后给一个对或错,很难知道是哪一步做对了、哪一步拖了后腿。每尝试一次,都要真的执行几十次工具调用,训练的成本和耗时远高于解一道数学题。
八、代价
⚠️ 错误会连乘,看不见的错误无法纠正。 反馈能把成功率扳回来,前提是错误能被察觉;工具正常返回、内容却是错的,这种错误会一路带到最后。
⚠️ 成本和延迟成倍增加。 每一轮都是一次完整的模型运行,加上工具执行的时间;上下文越来越长,每一轮又比上一轮更贵(第 24 讲)。
⚠️ 可能陷入循环,或者停不下来。 需要门外的程序强制设定步数、预算和重复检测。
⚠️ 风险随步数累积。 每一次工具结果都是一段未经审查的输入(第 25、27 讲),步数越多、读进来的外部内容越多,被夹带恶意指令的机会也越多;能执行的动作越多,一次被操纵的后果也越严重。第 30 讲专门讲这个问题。
⚠️ 难以评估。 同一个任务,可能有很多条不同的正确路线;第 21 讲的采样随机性又让每次运行的轨迹都不一样。“这个智能体能不能完成这类任务”,只能靠在大量任务上多次运行、看成功率来回答。
九、和后面课程的关系
Unit 7 到这里结束。四讲合起来回答了"模型怎么和外部世界连接":第 25 讲从外部取知识,第 26 讲怎么找到相关的知识,第 27 讲一次调用的机制,这一讲把调用串成能自己推进的循环。
- 第 29 讲(幻觉):多步任务里,一个中间步骤编造出的事实,会成为后面所有步骤的前提,被一路传下去。
- 第 30 讲(Prompt injection):第八节说的"风险随步数累积",是那一讲的核心场景——一个能读外部内容、又能执行动作的智能体,是最有吸引力的攻击目标。
- 第 31 讲(可解释性):ReAct 里写下的"思考",是不是模型真实的决策过程?第 19 讲提过这个问题,那一讲会正面讨论。
十、本讲小结
- 后一步依赖前一步的结果,路线无法事先写好,就需要一个循环:模型生成 → 门外执行 → 结果写回 → 再生成,直到给出最终回答。
- ⭐ ReAct:每一轮先写思考,再行动,再读观察。写下思考的理由来自第 19 讲——token 是草稿纸。手算一条三步轨迹:查小王的上级、查上级的部门、查部门的标准;每一步的参数都来自上一步的结果。
- 按依赖选择方式:互相独立的步骤并行调用,能事先列出的先规划,有依赖的只能逐步来。
- ⭐⭐ 错误会连乘:每步 95%,20 步只剩 35.8%。但如果出错可察觉、能重试一次,20 步回到 95.1%。长任务的关键在于错误能否被察觉和纠正;看不见的错误会被一路带到最后。
- 上下文越堆越长:20 轮、每轮 500 个 token,不复用缓存要处理 135,000 个 token,复用只要 12,000 个,但缓存一直占着显存;长轨迹同样撞上第 23 讲的注意力稀释。压缩、外部记忆、分派各有代价。
- 何时停止由门外决定:步数、预算、重复检测、高风险确认。
- ⚠️ 代价:错误连乘、成本延迟成倍、可能陷入循环、风险随步数累积、难以评估。
思考题
- 在第三节的轨迹里,如果第二次调用时模型写成了
get_employee("李敏")(多了一个错别字),工具返回"查无此人"。请写出接下来一轮合理的"思考"和调用。如果工具恰好真的有一个叫"李敏"的员工,又会发生什么? - 用第四节的方法算:每步成功率 90%,10 步不重试的成功率是多少?出错可察觉、重试一次呢?如果允许重试两次呢?
- 第三节说"互相独立的步骤可以并行"。请把"帮我比较市场部和销售部的出差住宿标准,并告诉我差多少"拆成步骤,标出哪些可以并行、哪些必须等待。
- 按第五节的示意数字,如果任务从 20 轮变成 60 轮,最终的上下文有多长?不复用缓存时一共要处理多少输入 token?结合第 23 讲,你觉得这样的任务更需要压缩历史,还是更需要外部记忆?为什么?
- 第六节列出了几种强制停止的方式。请设想一个智能体"帮用户整理邮箱"的场景,为它设计一套停止规则和确认规则,并说明每一条防的是什么情况。