阶段 7 · 用起来

Agent:让模型从"会说"
变成"会做"

上一章《RAG 检索增强》给模型配了本参考书,但它还是只会说出来。 它再聪明也改变不了世界——你说"帮我查一下天气",它只能编一个听起来合理的数字。
Agent 的全部魔法就是给它一双手:让它能真的调用工具。

1

模型只会说,不会做

场景 / 问题普通模型Agent
能做什么生成文字生成文字 + 触发真实世界的操作
"现在几点?"它不知道,但可能编一个时间 调用 get_time(),拿到真实答案
"算一下 17 × 23"容易算错(数字被切碎了,回看分词那章) 调用计算器,100% 正确
"帮我订明天的机票"只能写一段"订票指南" 真的调用订票 API
本质文本 → 文本文本 → 动作 → 观察 → 文本(观察 = 程序把执行结果告诉它;循环也由程序转)
一句话

Agent = 模型 + 工具 + 一个循环。
少了任何一个都不成立:没有工具它就只是聊天;没有循环它就只能做一步。
而真正难的部分不是"调用工具"——是知道什么时候该调、调哪个、参数怎么填、结果不对怎么办。

图解 · Agent 其实只有三个零件,缺一个都不成立

缺了它只是文本 模型 会写文字 + 缺了它只能聊天 工具 能碰真实世界 + 缺了它只能做一步 循环 把两者串起来 三个零件里,真正难的不是「调用工具」,而是知道什么时候调、调哪个、参数怎么填、结果不对怎么办。
2

它其实一直在自言自语

2022 年那篇 ReAct(Reason + Act,边推理边行动)论文给出了至今最通用的形式: 让模型交替输出「思考」和「行动」。

① 思考 Thought"我需要先查北京的天气"
② 行动 Action输出一个结构化的调用请求(一段按固定格式写好的 JSON,程序能直接解析)
③ 观察 Observation外部代码被执行,把结果拼回上下文
④ 回到 ①模型看到新信息,决定下一步……直到它能给出最终答案

关键:这个循环由外部代码驱动,不是模型自己在跑。 模型只是每次输出一句话,然后"停下来等"。是外面的程序决定要不要把这句话当成工具调用来执行。

🎯 类比

像一个只能打电话、看不见外面世界的顾问。
你问他"北京今天多少度",他没法直接知道。但他可以说:"请帮我打给气象台,问北京的温度。"
你打完电话,把结果告诉他。他接着说:"再帮我算一下,23 摄氏度是多少华氏度。"
顾问全程没有离开房间——他只负责说"该做什么",动手的永远是外面那个人。
这个类比管到「谁在想、谁在做」为止:真实 Agent 的顾问能看到你贴进上下文的全部资料,也能在一轮里同时要求打好几个电话。

图解 · ReAct 循环:四步转一圈,直到能给出答案

① 思考 Thought ② 行动 Action ③ 观察 Observation ④ 回到 ①,带着新信息再想一次 —— 而这个圈是外部程序在转,不是模型自己在跑
3

亲手指挥一个 Agent 走完任务

下面的循环是实际执行的:你选动作,工具被调用、返回结果,上下文随之变长。 任务是 「查一下北京今天的气温,并换算成华氏度」。

互动 · ReAct 循环模拟器

执行轨迹

下一步做什么?

💡 这一章的全部内容都藏在上面这张图里

模型一个字节也没执行,执行的是你的代码。上面这个循环里,模型每轮只负责说一句"下一步做什么", 真正解析、执行、把结果拼回去的是外面那段程序。这一条同时解释了 它为什么灵活(换工具不用改模型),也解释了它为什么危险——第 4 节的安全后果、第 6 节那四个失败模式,都是从这里长出来的。

💡 故意去选错的那几个

· 选「查上海天气」:结果对了,但城市错了——Agent 没有任何机制知道"北京≠上海", 它会拿着上海的温度继续往下算。
· 选「换算成开尔文」:单位错了,但它会一本正经地给你 296.15K。
· 选「再查一次北京天气」:结果一样,但你白白多花了一轮的钱和时间。 这就是"循环卡死"的雏形。
· 选 「计算器 23×9/5+32」:这条其实也正确—— 换算是可以绕开专用工具、直接用计算器算的。同一个任务往往有多条正确路径。

4

模型一个字节也没执行

这是最容易被误解的一点。「模型调用了一个函数」听起来像是它在执行代码—— 完全不是。

⚠️ 这个区别有真实的安全后果

因为模型只是在"生成一段看起来像工具调用的文字", 所以如果你把用户输入直接拼进上下文,用户就可以伪造出一段工具调用, 让模型以为"系统刚才已经批准了删除数据库的操作"。
所以:工具调用的结果必须由你的代码注入并标记来源(比如在消息里写明 role: tool、加上固定前缀,用户输入永远不带这个标记),绝不能允许用户输入伪装成工具输出。 真正执行前的权限校验,必须放在你的代码里,而不是指望模型"不会乱来"。

图解 · 「模型调用工具」的四步:真正执行的永远是第 ③ 步

1 你把工具的名字、说明、参数格式写进系统提示 模型只是「知道」它们存在 —— 它手里没有这些函数的代码 2 模型输出一段结构化文本,比如 get_weather(北京=…) 这只是一种「它生成的文字」,和生成一句诗没有任何机制上的区别 3 你的代码解析这段文字,然后自己执行那个函数 真正的函数调用发生在你的代码里 —— 模型从头到尾没有碰过任何 API 4 你把结果拼回上下文,再问一次模型 模型看到「观察结果」,继续下一轮 —— 「循环」是你的程序在转

图解 · prompt injection(提示注入)是从哪进来的

用户输入 网页 / 邮件 / 文件 全部被拼进同一个上下文 模型分不清哪一句是谁说的 模型以为「系统批准了」 于是一个危险动作被执行 任何被拼进上下文的文字,模型都分不清是谁说的 —— 所以危险永远来自「谁有权执行」。
5

上下文在疯长

每一轮工具调用,都要把之前所有内容重新发给模型一次。 模型是无状态的——它不记得上一轮,是你把整段历史重发给它。 这一节算完整账:除任务和工具本身,每轮还要重发一段约 600 token 的系统提示(第 3 节的读数不含它)。

互动 · 上下文增长的账本

不压缩 · 总消耗
—
带摘要 · 总消耗
—
省下
—

为什么 Agent 比聊天贵几十倍

Agent 每一轮都要把历史重发一遍,所以越往后越贵:第 N 轮要发前 N−1 轮的全部内容, 总成本是 1+2+3+…+N = N(N+1)/2,平方增长。 10 轮下来累计约是单轮的 20 倍,30 轮就是 130 倍。 这些输入每次还要重新做一遍前向计算(KV cache 能省掉一部分,但长上下文本身还是贵)。
「Agent 比聊天贵」不是定价策略问题,是结构决定的。

图解 · 每一轮都要把历史整段重发一次

第 1 轮 任务 第 2 轮 任务 第 1 轮 第 3 轮 任务 第 1 轮 第 2 轮 本轮新增 同一批内容被反复重发 —— 这是模型「没记性」的代价 摘要压缩:把浅色的那几块 压成一条定长摘要 第 N 轮要重发前面 N−1 轮的全部内容 —— 所以「该忘什么」不是品味问题,是成本算术题。
6

四个真实存在的失败模式

失败模式长什么样 · 为什么会发生怎么缓解
① 循环卡死 反复调用同一个工具、参数几乎一样,跑了 30 轮还在原地。 模型没意识到"我已经试过了":失败和成功的记录混在一起,没有"这个已经试过了"的标记 设最大轮数硬上限;记录已调用过的「工具+参数」组合并在提示里显式列出
② 工具误用 参数格式错(日期写成"明天"这种自由文本,而不是 ISO 格式「2026-09-29」); 选错工具(该用计算器却用了搜索);编造不存在的工具名。 模型对工具的理解只来自文字说明,说明写得含糊,它就只能猜 工具描述要写清参数类型和示例;参数做严格校验并把错误信息返回给它
③ 错误累积 第一步查错了城市,后面每一步都基于这个错误,最后给出一个完全自信但完全错误的答案。 Agent 没有"我可能错了"的内在信号——错误的结果在上下文里读起来和正确结果一模一样 关键结果做交叉验证;引入独立的校验步骤;把"不确定"显式写进工具返回里
④ 过早停止 任务只做了一半就说"已完成",比如只查了天气、没做换算。 模型倾向于"给出一个完整回答",而且看不到自己的进度条——没有机制告诉它"还剩几步" 把任务拆成显式的检查清单放进上下文;用一个独立的评判步骤检查是否真的完成
⚠️ 这四个有一个共同点

它们都不是模型"不够聪明",而是信息不对称: 模型不知道自己已经试过了、不知道自己错了、不知道自己还没做完。
所以解决 Agent 可靠性问题的主要手段,不是在模型上下功夫, 而是把你的程序和状态管理做得更好——把模型看不见的东西显式写进上下文。

图解 · 模型看不见自己已经试过什么

模型看得到的(只有这个窗口) 「任务:查北京天气并换算」 「思考 / 行动 / 观察 …」 「思考 / 行动 / 观察 …」 它看不到「已经试过几次」 显式写进去 你的程序知道的(真实状态) · 已经调用过:[查天气(北京), 换算(C→F)] · 还差一步:把结果写成最终答案 · 工具返回是否是模糊文字 · 有没有权限、有没有超预算 所以提高 Agent 可靠性的主要手段在工程侧:把模型看不见的东西,显式写进上下文。
7

Agent 这条线的几代方案

互动 · 工具调用能力是怎么长出来的

方案核心做法解决了什么
ReAct 2022 把「思考」和「行动」交替输出 给出了通用范式。今天几乎所有 Agent 都是它的变体
Toolformer 2023 训练模型自己学会"什么时候该调工具" 把工具调用从"提示工程"变成了模型的内在能力
Function Calling 2023 API 层面原生支持结构化输出 不用再靠字符串解析,可靠性大幅提升。成为今天的标准做法
Code Interpreter 2023 给模型一个真的 Python 沙箱 一个"万能工具"——需要什么现算,不用预先定义每个工具
MCP 2024 把"工具怎么接"标准化成一个协议 工具从"每个应用自己写"变成"写一次到处能用"。 这是把 Agent 生态从作坊推进到工业化的那一步
Computer Use 2024-25 直接控制鼠标键盘、看屏幕截图 不需要 API 了——任何人能用的软件它都能用。 代价是慢、贵、而且容易点错
SWE-Agent 等垂直 Agent 2024-25 把循环和工具针对一个领域深度定制 在软件工程等任务上,专用 Agent 远超通用 Agent—— 因为工具集和反馈信号都是精心设计的
M

数学 · 概率相乘,上下文平方涨

这一章有两句听上去像口号的话:「Agent 的可靠性是相乘的」「上下文是平方增长的」。 它们其实都能算。这一节就真的算一遍——公式里每一个符号,下面图上都有一块。

动画 · 每一轮到底重发了多少内容:它其实是一个三角形

N—总轮数 Σ—实际重发的段数(Σ = 求和) N(N+1)/2—三角形的公式 省—摘要压缩省下的

互动 · 每个符号管图上的哪一块

图解 · 「可靠性是相乘的」:每一步都乘一次,不是加一次

第 1 步 ×p 第 2 步 ×p 第 3 步 ×p ⋯ 整体 = p^N 单步 95% 听上去接近满分,但 0.95 连乘十次只剩 60% —— 这就是「相乘」和「相加」的全部差别。

互动 · 重试为什么是最划算的杠杆

互动 · 单次问答 vs Agent 走完:token 账对比

🎬 自己验一遍

把「Agent 轮数」从 3 拖到 30:三角形公式那一栏从 6 涨到 465,而实际重发段数一模一样。 再把「摘要窗口」从 30 调到 2——三角被削成一根细柱,省下的比例直接跳到 85% 以上。
这就是第 5 节那条粉色曲线为什么向上弯,也是「该忘什么」为什么是一道成本算术题。

8

可靠性是绕不过去的墙

互动 · 单步 95% 正确,10 步之后还剩多少

代价说明什么时候真的会痛 → 换什么
可靠性随步数衰减 每一步单独看都很准(95%+),但组合起来会衰减。 10 步之后可能只剩 60%。而人类用户对"助手"的期望是 99% 任务要 10 步以上、又要求接近 99% → 把长任务拆短,每步加重试与校验
成本是聊天的几十倍 如上所述,上下文平方增长。一个跑 20 轮的 Agent, 花费可能是同样问题的单次问答的 约 60 倍 轮数多、上下文长,账单按 token 计 → 上摘要压缩/检索,把确定性步骤移出模型
延迟不可控 每一步都要等一次模型推理。10 轮就是 10 次串行等待, 用户可能要等好几分钟 用户在等一个交互式结果 → 并行工具调用、减少轮数、换更小的模型
几乎没法测试 同一个输入每次走的路径可能都不同(模型有随机性)。 传统单元测试那套"给定输入期望输出"在这里基本失效(单元测试 = 每次只测一个小函数), 只能做端到端的成功率评估(端到端 = 整条任务跑一遍看结果) 要上线、要回归测试 → 固定温度/随机种子,断言整条轨迹,把确定性步骤移出模型
安全面极大 一个能执行代码的 Agent,就能删掉你的数据库、发邮件给你的客户、 花掉你的钱。而且 prompt injection 可以通过它读到的任何一个网页、 任何一封邮件进入。权限必须收得比你能接受的更紧 Agent 能做写操作 → 白名单 + 人工确认,永远别给全权
工程上的实际结论

不要追求"全自主 Agent"。今天的可靠做法是 把 Agent 嵌在一个确定性的程序里:程序负责流程和校验, 模型只负责那些"真的需要判断"的小步骤。
让模型做决定,让你的代码做执行和把关。 这也正是再下一章"多智能体与工作流"要讲的核心——大多数时候你要的是一个工作流,不是一个 Agent 群。

互动 · 延迟不可控:串行等待是墙,并行工具调用是唯一便宜的出口

图解 · 工作流还是 Agent:这是本章真正要你带走的选择

工作流:路线写死在程序里,可测、可靠、便宜 程序定死步骤 模型只做局部判断 校验与重试 Agent:路线由模型决定,灵活,但要付可靠性、成本的代价 模型决定下一步 你的代码执行 结果拼回上下文 这个圈是模型在转,风险也在里面
9

小结

Agent 不属于第一性原理那张图上的任何一条线,这不是偷懒—— 这一章讲的是怎么把前面所有章的模型当成一个零件来用,它的第一性原理就是「组装」。

它对应哪条线 不直接对应任何一条。它不是关于模型的原理,而是关于组合的原理: Agent 的瓶颈从来不是「模型不够聪明」,而是把多个步骤串起来之后, 概率是相乘的,不是相加的——所以它更像一条工程定理,而不是一条学习规律
一句话 这一章的做法,本质上是在给一个无状态的函数套上一层循环和一层外部记忆—— Agent 的「智能」不在模型里,在循环、在上下文、在你的程序里。
它牺牲了什么 牺牲了可靠性和可预测性。单次问答只有一次失败机会; Agent 把 10 次机会串起来,成功率就变成了 0.95¹⁰ ≈ 0.60—— 而用户对「助手」的期望是 99%。
同时还丢掉三样:延迟(N 次串行等待)、成本(上下文 N² 增长)、 可测试性(同样的输入每次走的路径都不同)。 换来的东西也很实在:它真的能改变外部世界,而聊天只能输出文字。
🎬 自己验一遍

回到第 8 节。把「单步成功率」从 0.95 拖到 0.99—— 看 10 步后那条曲线抬起来多少。再把「每步失败后重试次数」从 0 拖到 2,看整条曲线往上移。

那个瞬间你看到的就是「相乘」这件事:单步那点提升,被十步一乘就放大成了天壤之别; 而这就解释了为什么 Agent 的可靠性不能靠「换个更强的模型」解决。

再去第 5 节,把「Agent 轮数」从 3 拖到 30——看粉色曲线怎么向上弯(那是 N²,不是 N)。 两张图合起来,就是「为什么今天不能真的全自动」的全部理由。

为什么这章的数学不在模型里,而在组合里

看起来像实际上是什么后果
「模型在自主思考、自己决定下一步」 模型没有执行任何东西。它只是把下一步写成文字, 执行的是你的代码(第 4 节) 权限校验、参数校验、循环上限——全得写在你的代码里
「它有记忆,记得上一轮干了什么」 模型是无状态的。是你把整段历史重发了一遍(第 5 节) “该忘什么”变成一道成本算术题,而且它直接影响可靠性
「单步 95% 已经很准了」 成功率是相乘的,不是相加的; 而且 95% 是平均值,长尾任务(少数特别难、特别长的)远低于它 要么提高单步、要么重试、要么把长任务拆短。没有第四条路

它在暗线里站在哪

暗线这一章的回答
A 信息流动 每一轮的输入 = 之前所有轮次的拼接 + 本轮新内容,输出只是一小段文字(可能是工具调用); 上下文窗口大小固定,所以「信息增长」和「容器不变」必然冲突,只能靠摘要、丢弃、检索来压
C 参数账本 模型参数量一点没变——Agent 不训练模型,这是它和前面所有章最大的区别; 变的是 token 账本:跑 20 轮的累计输入约为单次问答的 60 倍。另一笔是 KV Cache 的显存—— 8B 级 GQA 模型(8 个 KV 头)32k 上下文约占 4 GB,而且它随对话历史涨、不随模型涨
一句话带走 Agent

Agent = 模型 + 工具 + 循环;模型只吐出"看起来像函数调用的文字", 真正执行和把关的是你的代码——这也是它灵活、又容易被 prompt injection 的原因。 所以别追求全自主:把它嵌进确定性的程序里,程序管流程和权限,模型只负责需要判断的那几步。

下一章《上下文工程与记忆》接住。

10

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式