第 17 章:Agent 与 Tool Calling¶
1. 本章要解决的问题¶
第 15 章和第 16 章里,我们已经把一条很重要的应用路线讲清楚了:
- 第 15 章解决“怎样把外部知识做成可检索对象”
- 第 16 章解决“怎样把检索结果喂给模型,生成带依据的回答”
但如果你真的继续往前做产品,很快会遇到一类更复杂的任务。
用户要的往往不再只是:
而更像是:
或者:
这类任务有几个共同特点:
- 不是只靠模型参数就能完成
- 不是只检索一次上下文就结束
- 需要根据中间结果决定下一步动作
- 需要访问外部世界中的工具、API、文件、数据库或搜索系统
这时问题就从“模型如何回答”进一步变成了:
当模型需要主动查、算、读、写、调用外部能力时,我们怎样把它做成一个可控的 Agent。
这就是本章要解决的核心问题。
从全书结构上看,这一章承接 RAG,又通向生产化:
- 第 16 章重点是“把知识拿给模型看”
- 本章重点是“让模型决定何时使用什么工具,以及怎样把多步任务串起来”
- 第 18 章则继续讨论“这样的系统上线后怎样评估、监控、部署和控制成本”
如果说 RAG 解决的是:
模型怎样访问外部知识。
那么 Agent 要解决的就是:
模型怎样在任务执行过程中访问外部能力,并根据结果持续推进。
2. 你学完后应该会什么¶
- 能解释什么是 Agent,以及它和普通聊天、RAG、工作流编排之间的区别
- 能理解 tool calling 的基本机制:工具定义、参数生成、工具执行、结果回填
- 能描述一个最小 Agent loop:思考、选工具、执行、观察、继续
- 能区分“看起来像 Agent”的 prompt 玩法和“真正接入外部工具”的系统能力
- 能理解 ReAct、planning、memory、reflection 这些常见概念在系统里分别干什么
- 能设计一个最小可展示的 Agent 项目
- 能从工程视角识别常见风险:误调用、越权、幻觉动作、prompt injection、无限循环和成本失控
3. 为什么 RAG 还不够¶
很多人学完 RAG 之后,会自然地问:
是不是有了 RAG,就已经等于有 Agent 了?
不完全是。
RAG 很强,但它主要解决的是:
从外部知识源中找信息,再基于信息生成回答。
而很多真实任务不只是“找信息”,还包括“做动作”。
3.1 有些任务需要计算,而不是只要文字回答¶
例如用户问:
如果只靠语言模型裸答,它可能会:
- 自己心算,但容易算错
- 忽略边界条件
- 直接编出一个看起来合理的数字
这时更可靠的方式是:
让模型调用计算工具,而不是让它自己猜。
3.2 有些任务需要动作链,而不是一次性生成¶
例如:
这类任务通常要拆成多步:
- 先列出文件
- 再读取相关日志
- 识别失败模式
- 最后汇总
这不是单轮问答,而是一个带状态推进的过程。
3.3 有些任务要根据中间结果动态分支¶
例如:
这里的下一步不是固定的,而取决于前一步观察到的结果。
这正是 Agent 和固定流程最大的区别之一:
它不是预先写死每一步,而是让模型在一个受约束的动作空间里做条件决策。
3.4 RAG 更像“查资料”,Agent 更像“做任务”¶
可以先建立一个粗略但很有用的直觉:
RAG:重点是把外部知识拿回来Tool Calling:重点是让模型调用外部能力Agent:重点是让模型围绕目标,多步使用这些能力完成任务
这三者并不是互斥关系。
现实里最常见的情况反而是:
Agent 把 RAG 当作其中一个工具来使用。
4. 什么是 Agent¶
先给一个尽量不神秘的定义。
Agent 可以理解为:以大模型为决策核心、能在多步过程中调用外部工具并根据反馈继续行动的系统。
这个定义里有四个关键词。
4.1 以大模型为决策核心¶
这里的大模型不一定负责“亲手执行所有事”,但它负责:
- 理解用户目标
- 判断当前缺什么信息
- 选择下一步动作
- 组织最终输出
所以 Agent 的核心不是“模型更聪明了”,而是:
模型开始参与决策,而不只是负责把话说出来。
4.2 能调用外部工具¶
这些工具可以非常简单,也可以非常复杂,比如:
- calculator
- search
- file reader
- database query
- code executor
- browser
- retrieval system
- internal API
只要模型可以通过某种协议发起调用,并拿到结构化返回结果,这就属于工具使用。
4.3 是多步过程¶
一次 tool call 还不一定叫 Agent。
比如一个系统只是:
- 接收问题
- 固定调用搜索
- 把搜索结果交给模型总结
这更像一个“带工具的工作流”。
而 Agent 的特征通常是:
- 工具不一定只调一次
- 下一步取决于前一步结果
- 任务可能需要多轮决策和状态推进
4.4 有目标而不是只有回复¶
普通聊天系统的重点常常是“生成一句回复”。
Agent 系统的重点更像是“围绕目标推进任务”。
因此你可以把 Agent 理解成:
从 response generation 走向 task execution。
5. Agent 不等于“让模型自由发挥”¶
一听到 Agent,很多人会自动联想到:
是不是把模型放出来,让它自己想、自己做、自己决定一切?
这通常是最危险的误解之一。
一个好的 Agent 系统从来不是“无限自主”,而是:
在明确边界内给模型有限行动权。
现实工程里更准确的说法是:
- 给模型定义可用工具集合
- 给每个工具定义输入 schema
- 给调用过程加权限、校验、预算和停止条件
- 让模型在这个受控空间里做选择
所以 Agent 的关键不是“彻底自治”,而是:
受约束的决策。
6. Tool Calling 到底是怎么工作的¶
现在进入本章最核心的工程机制。
很多模型产品里说的 function calling、tool use、tool calling,本质上都是同一类思路:
不是让模型直接输出自然语言动作描述,而是让它输出一个结构化的工具调用意图。
一个最小流程通常如下。
6.1 第一步:给模型声明可用工具¶
例如系统告诉模型:
[
{
"name": "search_docs",
"description": "在课程资料中检索相关内容",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string"
}
},
"required": ["query"]
}
},
{
"name": "calculator",
"description": "执行基础数学计算",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string"
}
},
"required": ["expression"]
}
}
]
这一步的本质是:
先把动作空间定义清楚。
6.2 第二步:模型决定是否调用工具¶
用户问:
模型可能会判断:
- 关于 LoRA / QLoRA 的解释,可以先检索文档
- 关于缩小多少倍,应该调用 calculator
于是模型不直接回答,而是先返回类似这样的结构化输出:
或者:
6.3 第三步:系统在外部真正执行工具¶
这里非常重要。
工具不是模型自己执行的,而是宿主程序执行的。
也就是说:
- 模型只能提出调用请求
- 真正的代码执行、数据库访问、文件读取、HTTP 请求,都发生在你的应用侧
这也是安全边界成立的前提。
6.4 第四步:把工具结果回填给模型¶
比如外部程序执行后返回:
或者:
{
"tool_name": "search_docs",
"result": [
"LoRA 训练低秩适配器,不更新全部参数。",
"QLoRA 在量化基座模型上训练 LoRA 适配器,从而进一步降低显存占用。"
]
}
这时模型再基于这些观察结果继续推理,决定:
- 是否还要调用下一步工具
- 还是已经可以给最终回答
6.5 第五步:生成最终答案¶
当信息足够时,模型才输出对用户可见的最终回答。
所以整个机制可以总结成:
7. 一个最小 Agent Loop 长什么样¶
如果把上面的流程抽象成一个通用循环,最小 Agent loop 可以写成:
- 接收用户任务
- 把当前上下文和可用工具发给模型
- 模型返回:
- 要么是最终答案
- 要么是工具调用请求
- 如果是工具调用请求,系统执行工具
- 把工具结果追加到上下文
- 再次调用模型
- 直到满足停止条件
用伪代码表示就是:
messages = [user_message]
for step in range(max_steps):
response = llm(messages=messages, tools=tool_schemas)
if response.type == "final":
return response.content
if response.type == "tool_call":
tool_name = response.tool_name
args = response.arguments
tool_result = run_tool(tool_name, args)
messages.append(response.as_message())
messages.append(tool_result.as_message())
return "任务未在步数限制内完成"
这个循环很简单,但已经包含了 Agent 的三个核心部件:
state:当前上下文和历史观察action:本轮要调用哪个工具observation:工具执行后返回了什么
8. ReAct:为什么很多 Agent 都长得像“想一想,再做一步”¶
在 Agent 设计里,一个很常见的思路叫 ReAct,名字来自:
ReasonAct
它想表达的是:
让模型把“推理”和“行动”交替组织起来。
一个非常简化的 ReAct 风格过程可能像这样:
Thought: 我需要先确认报错来自依赖缺失还是配置错误。
Action: read_log(file="train.log")
Observation: ModuleNotFoundError: bitsandbytes
Thought: 这是依赖缺失,不需要继续检查配置文件。
Action: search_docs(query="bitsandbytes install fix")
Observation: ...
Final Answer: ...
这里最重要的不是格式本身,而是它体现出的系统思想:
- 先根据目标形成局部判断
- 再执行一个最有信息增益的动作
- 根据观察结果更新判断
- 循环直到完成任务
8.1 ReAct 的价值¶
它的好处主要有三点:
- 把复杂任务拆成更小步,减少一次性胡编
- 中间每一步都可以接工具,而不只是脑内推理
- 更容易调试,因为你能看到系统是怎么走到结论的
8.2 ReAct 也不是万能药¶
如果放任模型写很长的思考链,它也可能:
- 空转很多步
- 做无意义调用
- 在错误方向上越走越远
所以现实里通常不会只靠 prompt,而会加上:
- 步数上限
- 工具白名单
- 参数校验
- 强制结束条件
9. Planning、Memory、Reflection 分别在干什么¶
很多 Agent 框架会抛出一堆术语。
如果第一次接触,很容易觉得概念很多、边界很乱。
其实可以用一个很工程化的视角理解。
9.1 Planning:先把任务拆一下¶
planning 解决的是:
任务太大时,模型要不要先形成一个阶段性计划。
例如:
planning 的价值在于:
- 降低多步任务中的遗忘
- 让执行路径更稳定
- 更适合长任务或复杂任务
但它也不是越重越好。
很多简单任务,一边做一边决策反而更省 token。
9.2 Memory:让系统别每次都从零开始¶
memory 可以粗略分成两类。
第一类是短期记忆,也就是当前会话里的上下文历史。
它帮助模型记住:
- 用户刚刚说了什么
- 上一步工具返回了什么
- 当前任务进行到哪里
第二类是长期记忆,也就是跨会话保存的信息,例如:
- 用户偏好
- 常用项目路径
- 常见失败模式
- 历史任务摘要
但要注意:
memory 不是把一切都塞进上下文,而是决定什么值得保留、什么时候召回。
否则上下文会膨胀,系统反而更混乱。
9.3 Reflection:做完一步后回头检查¶
reflection 解决的是:
模型能不能对自己的中间结果做一次轻量复盘。
例如:
- 当前证据够不够支持结论
- 这一步有没有算错
- 是否已经偏离用户目标
- 有没有更高效的下一步
reflection 常常能提升复杂任务稳定性,但代价是:
- 更多 token
- 更高延迟
- 可能出现“反思过度”
所以它更适合:
- 高价值任务
- 复杂推理任务
- 需要多工具协作的任务
10. 工作流和 Agent 到底怎么区分¶
这是应用面试里很高频的问题。
一个很实用的区分方式是看:
下一步是不是由代码预先写死。
10.1 Workflow 的特点¶
工作流通常是:
- 固定先做 A
- 再做 B
- 最后做 C
比如:
它的优点是:
- 稳定
- 可控
- 好调试
- 好评估
10.2 Agent 的特点¶
Agent 更像是:
它的优点是:
- 更灵活
- 更能处理开放任务
- 更适合多工具、多分支问题
缺点也明显:
- 更难评估
- 更难保证稳定性
- 更容易出现不可预测行为
10.3 一个很重要的工程判断¶
不是所有系统都应该做成 Agent。
如果任务路径高度确定,优先做 workflow 往往更合适。
只有当问题具备下面这些特征时,Agent 的价值才会明显上升:
- 任务分支多
- 中间步骤依赖观察结果
- 用户目标开放
- 工具选择不能预先完全写死
很多团队最后采用的并不是“纯 workflow”或“纯 Agent”,而是:
外层用 workflow 控住主流程,局部节点再放一个小 Agent。
11. 一个最小可展示的 Agent 项目该怎么做¶
如果你想做一个适合学习和面试展示的项目,不需要一上来就做“通用超级 Agent”。
更推荐做一个边界明确的小项目。
例如:
课程资料助教 Agent
它可以有以下工具:
retrieve_notes(query):检索课程资料read_file(path):读取指定讲义或实验说明calculator(expression):计算指标或复杂度summarize_table(rows):把比较结果整理成结构化总结
11.1 一个典型任务¶
用户说:
系统可能这样工作:
- 先检索课程讲义里关于 LoRA、QLoRA、全参数微调的内容
- 如有必要再读取指定页或指定文档
- 如果用户给了具体显存数字,就调用 calculator 计算比例
- 最后输出结构化对比和依据
11.2 这个项目为什么合适¶
因为它同时覆盖了几个关键点:
- 有知识检索,所以能衔接 RAG
- 有工具调用,所以能体现 Agent 能力
- 工具集合有限,边界清楚
- 容易做 demo,也容易解释评估方法
11.3 面试时你可以怎么讲¶
你可以很自然地从工程角度描述:
- 为什么不用纯 prompt 直接答
- 为什么把 retrieval 当成工具
- 为什么给工具加 schema 校验
- 怎么限制最大步数和最大成本
- 怎么记录轨迹做调试和回放
这会比空谈“我做了一个 Agent”更有说服力。
12. 工具设计为什么比 prompt 更重要¶
很多初学者容易把注意力全放在 system prompt 上,觉得只要 prompt 写得够好,Agent 就会自然变聪明。
实际上,到了工程层面,往往更关键的是:
你给了模型什么工具,以及这些工具设计得是否清晰。
12.1 好工具的几个特征¶
- 单一职责明确
- 输入参数少而清楚
- 返回结果结构稳定
- 错误信息可被模型理解
- 权限边界明确
例如一个不好的工具可能叫:
这几乎等于没给边界。
而更好的做法是拆成:
search_docs(query)read_file(path)run_sql(query)calculate(expression)
这样模型更容易学会“什么时候该用什么”。
12.2 返回值也要为模型设计¶
工具设计不是只看输入。
如果你的工具返回:
- 一大段混乱文本
- 没有字段名
- 错误和成功格式完全不同
模型就更容易误读。
更好的返回通常是结构化的,例如:
这样后续决策会更稳定。
13. 常见失败模式与安全问题¶
Agent 真正进入工程后,最值得重视的不是“它能不能偶尔惊艳”,而是:
它会怎样稳定地失败。
13.1 工具幻觉¶
模型可能会:
- 调用不存在的工具
- 传错参数名
- 漏掉必填参数
- 明明不需要工具却硬调一次
所以系统层必须做:
- schema 校验
- 参数默认值处理
- 非法调用拦截
13.2 无限循环和空转¶
模型可能反复:
- 查同一个东西
- 连续进行低价值调用
- 拿到足够证据还不肯结束
因此一定要有:
- 最大步数
- 最大 token 预算
- 最大工具调用次数
- 超时与强制终止机制
13.3 Prompt Injection¶
如果工具会读取外部文本,例如网页、PDF、知识库内容,那么其中可能出现类似:
这就是 prompt injection 的经典形态。
关键要明白:
被检索到的内容不一定可信,它既是信息源,也可能是攻击载体。
常见防护思路包括:
- 区分“用户指令”和“外部文档内容”
- 不把工具结果默认当成高优先级指令
- 对高风险动作设置额外确认或硬编码规则
- 对敏感工具做权限隔离
13.4 越权与危险动作¶
如果 Agent 能访问:
- 文件系统
- 数据库
- shell
- 发邮件
- 外部 API
那就必须考虑:
- 哪些路径可读
- 哪些命令可执行
- 哪些操作需要人工确认
- 哪些结果要审计留痕
现实里真正安全的 Agent,靠的从来不是“模型自己会谨慎”,而是:
系统把危险能力关进笼子里。
14. 怎样评估一个 Agent¶
到了 Agent,这件事会比普通 QA 更难。
因为你不只要看最终答案,还要看过程质量。
14.1 结果层评估¶
先看最终任务是否完成,例如:
- 答案是否正确
- 是否引用了足够依据
- 是否真正完成目标
- 格式是否符合要求
14.2 过程层评估¶
还要看它是怎么完成的,例如:
- 工具是否选对
- 是否多调了很多无用步骤
- 有没有明显绕路
- 有没有越权或危险调用
14.3 成本层评估¶
Agent 特别需要关注成本,因为它天然容易把一次请求变成多次模型调用和多次工具调用。
常见指标包括:
- 平均步数
- 平均 tool calls 数量
- 平均 token 消耗
- 平均延迟
- 单任务成本
14.4 一个很实际的结论¶
在很多场景里,一个“稍微没那么聪明,但路径稳定、成本可控”的 Agent,往往比一个“偶尔很惊艳,但经常乱跑”的 Agent 更有产品价值。
15. 面试里高频会问什么¶
Q1:Agent 和 RAG 的关系是什么?¶
RAG 主要解决外部知识访问,Agent 主要解决围绕目标的多步工具使用。
RAG 可以是 Agent 的一个工具,也可以作为固定工作流独立存在。
Q2:Function Calling 和 Agent 是一回事吗?¶
不是。function calling 是一种工具调用机制,Agent 则是围绕目标、多步使用工具并根据反馈继续决策的系统。
前者更像“能力接口”,后者更像“任务执行框架”。
Q3:什么时候不该用 Agent?¶
当任务路径高度固定、规则明确、分支很少时,优先用 workflow。
Agent 适合开放任务和动态分支场景,但会增加复杂度、成本和不稳定性。
Q4:Agent 最核心的工程风险是什么?¶
常见风险包括工具幻觉、无限循环、prompt injection、越权调用、延迟上升和成本失控。
核心思路不是相信模型自觉,而是通过 schema、权限、预算、日志和人工确认来约束系统。
Q5:如果让你做一个最小 Agent 项目,你会怎么设计?¶
我会选边界清晰的场景,例如课程资料助教或代码库问答助手。
先定义少量高价值工具,再实现最小 loop、日志追踪、步数限制和基本评估,而不是追求“大而全”。
16. 本章小结¶
这一章最重要的,不是记住多少术语,而是建立一个稳定判断:
Tool Calling解决的是“模型怎样请求外部能力”Agent解决的是“模型怎样围绕目标,多步使用这些能力推进任务”RAG往往是 Agent 的一个工具,而不是它的全部
同时也要记住:
Agent 不是让模型无边界自由发挥,而是在受控动作空间里做决策。
当你真的开始做系统时,影响效果的往往不只是模型大小,更包括:
- 工具是否定义清楚
- 循环是否可控
- 观察结果是否结构化
- 安全边界是否扎实
- 评估与日志是否完善
从全书路径上看,到这里我们已经从:
- 模型基础
- 训练流程
- 后训练对齐
- 检索与 RAG
一路走到了更接近真实产品形态的 Agent 系统。
下一章我们会继续回答一个更现实的问题:
当这些系统不只是 demo,而是要真的上线、监控、迭代、控成本时,LLMOps 和生产化到底要做什么。
17. 扩展阅读与前沿补充¶
如果你想把这一章继续往前沿方向延伸,可以把新出现的 Agent 框架、tool use 系统、skills 机制统一记到这里:
建议的用法是:
- 核心概念先收录到前沿文档里
- 真正稳定、值得长期保留的内容,再回写到正式章节
- 更偏 demo 或项目实践的内容,可以先作为扩展阅读保留