从 Prompt 到 Agent
第 21 课:从 Prompt 到 Agent
前三部分我们把一个模型"训练好、调好、发出去、评明白"了——但它始终只会做一件事:接到一段输入,吐出一段输出。这一课换一个问题:一次问答怎么变成循环?一个能自己跑起来的循环必须有哪些零件?为什么"多轮对话"不等于 Agent?为什么每步 95% 正确、十步之后却只有约六成能全对?这一课把 Agent 的四件套、错误累积的数学、ReAct(推理与行动交错,边做边拿外部结果纠错)的"边想边做"算清,最后回答一个最容易被忽略的问题:什么时候根本不该用 Agent。
② 用「模型 / 工具 / 状态 / 终止条件」四件套检查任何一个 Agent,并算出缺了终止条件的代价
③ 用 p^N 算出错误累积、用 ReAct 的「边想边做」解释怎么把它拦回来,并判断一个需求该用 workflow 还是 Agent
先回顾一下我们手上的东西:前三部分训练、微调、部署、评测的那个模型,本质是"一次输入、一次输出"。它不会自己再问一句、也不会去动任何外部系统。
因为一次问答是"封闭"的:模型只看当前这段输入,答完就结束,世界没有任何变化。而一个任务("帮我把这批客诉工单按严重度分好并回复")往往要经过好几步、每步都要看上一步的结果才能决定下一步——就像"点一次外卖"和"做一桌菜":前者一个动作,后者要买菜、下锅、尝味、补救,中间任何一步都要根据现场情况调整。所以下面用一笔账,把"多步"这个结构带来的第一个后果算出来。
更早、更自然的做法是「把要求一次全写进一段提示,让模型一口气输出完整结果」。但一次调用没有任何中间反馈:只要 10 个字段里有一个填错,整段结果就得整份重来——设单字段正确率 0.95,一次全对只有 0.95¹⁰ ≈ 0.599。循环把「一次全对」拆成「每步可检查」,错的那一步可以单独重来,不必推倒重来。
| 项 | 数值 |
|---|---|
| 模型调用次数 | 1 |
| 送入的输入 | 问题本身 ≈ 200 token |
| 总输入量 | 200 token |
| 步 k | 该步送进模型的输入 | 累计 |
|---|---|---|
| 第 1 步 | 200 | 200 |
| 第 2 步 | 200 × 2 = 400 | 600 |
| … | … | … |
| 第 10 步 | 200 × 10 = 2,000 | 11,000 |
| 步数 N | 总输入(200 token/步) | 相对单次问答 |
|---|---|---|
| 10 | 200 × 55 = 11,000 | 55× |
| 20 | 200 × 210 = 42,000 | 210× |
一次问答是函数调用:输入→输出,结束。一次任务是一个循环:做一步 → 看结果 → 再决定。这两者不是"复杂度不同",而是结构不同——循环带来了状态、带来了外部动作,也带来了随步数平方增长的成本(命中 prompt caching 可降前缀单价,但 token 计数不变)。后面五张卡片都在处理这个循环带来的新问题。
上一张卡片我们看到一次问答只有一次调用;一旦要跑多步,就必须有一个循环,把每一步的结果带回给模型。这一张把这个循环拆开,看它由哪几件东西组成。
因为循环要能持续运转,四件事缺一不可:谁决定下一步(模型)、谁能真的改变世界(工具)、上一轮发生了什么放在哪里(状态)、以及什么时候该停(终止条件)。好比一台自动售货机:选货、出货、找零、复位,少任何一环都会卡住——尤其"复位"(终止)最容易被忽视。所以下面重点算一笔"缺了终止条件"的账。
更笨的做法是「只写语义终止条件、不设步数上限」,赌模型自己会判断「已完成」。若每步判断已完成的概率只有 1%,期望步数 E ≈ 1 ÷ 0.01 = 100 步;而每步都要重发全部历史(第 1 张卡片口径),100 步累计输入约 1,010,000 token(量级示意)。没有步数上限,就等于把成本交给运气。
| 件 | 作用 | 缺了会怎样 |
|---|---|---|
| 模型 | 决定下一步做什么 | 循环没有大脑 |
| 工具 | 真正改变世界 | 只能空想、答不出事实 |
| 状态 | 记住已经做过什么 | 每步从零开始、反复做同一件事 |
| 终止条件 | 决定何时停 | 可能永远停不下来 |
| 项 | 无上限 | 加"最多 20 步" |
|---|---|---|
| 步数 | 期望 100 步 | ≤ 20 步 |
| 总输入(200 token/步、重发历史,同第 1 张卡片口径) | 200×(1+…+100) ≈ 1,010,000 | 200×(1+…+20) = 42,000 |
| 回合 | 模型 | 工具 | 状态 | 终止 |
|---|---|---|---|---|
| 1 | 决定"查库存" | 调用查询接口 | 写下查询结果 | 未完成,继续 |
| 2 | 决定"下单" | 调用下单接口 | 写下订单号 | 未完成,继续 |
| 3 | 判断目标达成 | 不再调用 | 收尾 | 满足终止,结束 |
审查任何一个 Agent,只要问四个问题:① 谁在决策?② 它能不能改变世界(工具)?③ 过去发生过什么放在哪(状态)?④ 它凭什么停? 四个里最常被漏的是第四件——很多"跑飞了"的 Agent 不是不够聪明,而是没有终止条件。
上一张卡片说,循环里必须有一件"能改变世界的工具"。这一张就用这条线,把最容易混淆的两个东西分开。
因为多轮对话的每一轮都只是"追加一段文字",它没有动作、也没有观察——你问十句,世界一句都没变。就像打电话咨询(只动嘴,账户没变)和真的去柜台办业务(会改变账户状态)的区别。所以判断标准不是"轮数多少",而是"这一轮有没有工具调用、世界有没有被改变",再用一笔轨迹长度的账说明为什么 Agent 必须把状态外置。
更省事的做法是「把 Agent 的历史全部堆在上下文里,只追加不整理」。但 Agent 每步的轨迹(推理 + 观察)约 2,000 token,10 步就累积 20,000 token,早已超过 8k 窗口——所以状态不能只靠上下文,必须能外置。
| 任务 | 工具调用次数 | 世界状态改变次数 | 是多轮对话还是 Agent |
|---|---|---|---|
| 咨询"退票规则是什么" | 0 | 0 | 多轮对话 |
| "帮我把这张票退掉" | 3(查询/下单/确认) | 2 | Agent |
| 项 | 数值 |
|---|---|
| 步数 | 10 |
| 每步轨迹(推理 + 观察,示意) | ≈ 2,000 token |
| 轨迹总长 | 10 × 2,000 = 20,000 token |
| 以 8K 小窗口模型为例 | 8,000 token |
| 做法 | 状态载体 | 能撑多长 | 风险 |
|---|---|---|---|
| 多轮对话 | 仅上下文 | 受上下文上限约束 | 超长即丢历史 |
| Agent | 上下文 + 外部存储 | 理论上可长跑 | 要自己管读写与一致性 |
区分多轮对话与 Agent,不看轮数、只看两件事:这一轮有没有工具调用(动作),有没有外部观察回来(反馈)。没有这两样,再多轮也只是"聊得更久";有了这两样,你就必须承担状态外置与一致性管理的工程成本。
上一张卡片把"多轮对话"和"Agent"分开了。一旦进入真正的多步循环,一个反直觉的问题立刻出现:单步看起来都不错,为什么整体经常失败?
因为任务要求的是"每一步都对",而这是把每步的正确率连乘起来——步数越多,乘积掉得越快。就像多米诺骨牌:每一块倒错的概率只有 5%,但十块之后整体还站着的概率只剩六成。所以下面把这条"错误累积曲线"算出来。
更自然的想法是「单步错一点、平均一下就没事」。但任务要求的是「每一步都对」,独立事件的联合概率是相乘而不是取平均:0.95¹⁰ ≈ 0.599,而不是 95%。这就是为什么「单步 95%」听起来很高,十步之后却只剩约六成能全对、约四成会翻车。
| 步数 N | 全对概率 = 0.95^N | 直观 |
|---|---|---|
| 1 | 0.95 | 95% |
| 10 | 0.599 | 约 60% |
| 20 | 0.359 | 约 36% |
| 50 | 0.077 | 约 7.7% |
| 单步正确率 | 10 步全对 | 20 步全对 |
|---|---|---|
| 0.95 | 0.599 | 0.359 |
| 0.99 | 0.904 | 0.818 |
| 情形 | 第 3 步出错 | 之后每一步 |
|---|---|---|
| 无纠错 | 把错误结论写进上下文 | 都在错误前提上继续 → 全盘偏 |
| 有外部校验 | 外部结果与预期不符 | 立刻在这一步停下重来 |
多步任务的成功率不是"平均一下"(95% 就不会掉太多),而是连乘:0.95¹⁰ ≈ 60%、0.95²⁰ ≈ 36%。所以设计 Agent 的第一原则是减少步数,第二原则是让每一步都有能被外部验证的机会,把错误按在原地、不让它写进上下文传给下一步。
上一张卡片算出:10 步连乘会从 95% 掉到 60%,错误一旦写进上下文还会被后续继承。要挽回它,就得让每一步都能被外部结果纠正。
因为纯推理(思维链)是"闭着眼睛想完全程",中间没有东西能反驳它;而 ReAct 在每一步做了就看,让真实世界的返回结果来打断错误传播。就像开车时不断看路况微调方向,而不是蒙着眼一路踩到底。所以下面看"插入一个观察"到底能把成功率抬多少。
更早、更自然的做法是「先想完全程、再一次性输出」,也就是纯思维链(CoT,让模型先写推理再给答案)。但 CoT 在生成过程中没有任何外部信息能反驳它:第 3 步想错,第 4 步仍会当成事实继续推,错误一路带到结尾。ReAct 把「想」与「做」交错,每步用外部观察对一次账——等于把判定对错的时机从「结尾一次」提前到「每一步一次」。
| 做法 | 等效单步正确率(示意) | 10 步全对 |
|---|---|---|
| 纯推理(CoT,无外部校验) | 0.95 | 0.599 |
| ReAct(每步有外部观察纠错) | 0.99 | 0.904 |
| 基准 | 相对最强基线(模仿/强化学习)的绝对成功率提升 |
|---|---|
| ALFWorld(交互决策) | +34% |
| WebShop(交互决策) | +10% |
| 上下文示例数 | 1–2 个 |
| 阶段 | 模型输出 | 作用 |
|---|---|---|
| Thought | "我需要先查库存" | 推理、给下一步定方向 |
| Act | 调用查询工具 | 改变世界 / 取外部信息 |
| Observation | "库存 3 件" | 真实反馈,拼回上下文 |
ReAct 的价值不在于"让模型多写几句推理",而在于把外部观察插进循环:每个回合都拿真实反馈对一次账,错误在写进上下文之前就被拦住。这也解释了为什么 Agent 的工具设计往往比提示词更重要——没有能给出可靠观察的工具,ReAct 就退化成了又一个 CoT。
上一张卡片给了 Agent 的核心范式,但它并不便宜——延迟更高、token 更多、还可能因为错误累积而不稳定。最后一张卡片回答:什么时候值得用。
因为Agent 换来的是"灵活性"(路径不用提前写死),付出的却是"不可预测"(步数不定、成本不定、成功率随步数下降)——就像寄一个同城快递,能一步到位就别开一辆卡车跑全程。所以下面用一笔成本账,说明"能用一次调用解决就别开循环"。
| 方案 | 输入量 | 相对成本 | 延迟 |
|---|---|---|---|
| 单次调用 | 200 token | 1× | ≈ 1 秒 |
| 10 步 Agent | 11,000 token | 55× | ≥ 10 秒 |
| 问题 | 若"是" | 选它 |
|---|---|---|
| 一次调用 + 好示例能解决吗? | 是 | 就一次调用 |
| 步骤能提前写死吗? | 是 | workflow(固定流程) |
| 步骤写不死,但每步可验证吗? | 是 | Agent(循环 + 工具) |
| 连验证都无法做吗? | 是 | 先别上 Agent,补上可验证的反馈 |
| 卡片 | 结论一句话 |
|---|---|
| 21-1 | 任务 = 循环,成本随步数平方增长 |
| 21-2 | 四件套:模型 / 工具 / 状态 / 终止条件 |
| 21-3 | 有"动作 + 观察"才是 Agent |
| 21-4 | 成功率 = p^N,会随步数衰减 |
| 21-5 | ReAct:把外部观察插进循环、拦住错误 |
| 21-6 | 能简单就别复杂,Agent 是最后的选择 |
判断标准只有两条:① 步骤能不能提前写死?能 → workflow;② 步骤写不死,但每一步能不能拿到可信反馈?能 → Agent。如果两条都不满足,缺的不是 Agent,而是"可验证的反馈"——因为 Agent 的全部优势(ReAct 的纠错、错误累积的收敛)都建立在"每步能对账"之上。