🔧 《深入理解 AI Agent》第 4 章

工具 · Agent 的手脚,也是它最大的风险面
11 个动画场景 · 覆盖 4.1–4.7 · 含 MCP-Zero、Sidecar 与幂等性
📌 工具既决定 Agent 能做什么,也决定攻击者能借它做什么

🎯 一张图看懂第 4 章

从「怎么设计工具」到「工具太多怎么办」,再到「怎么不出事」
🗂️
工具五类
感知 / 执行 / 协作 / 沟通 / 事件
→
✍️
设计原则
描述写清边界,参数不丢保真
→
📖
按需加载
只露书脊,用时再查
→
🛡️
安全护栏
双签、边车、幂等
💡 一句话总纲: 工具描述会原样进入上下文,所以它既是能力接口,也是攻击面——设计工具时要同时想这两件事。

场景 1 · 工具分五类 4.1

谁发起、作用于什么,决定工具的形态
Agent🧭感知工具web_search ·read_file🔧执行工具shell_exec ·send_email🤝协作工具spawn_subagent💬用户沟通ask_user /confirm⏰事件触发外部事件叫醒 Agent↑ 箭头反过来 = 世界调用我
前四类是我调用工具,第五类是世界调用我 —— 这是第 6 章异步交互的伏笔
记住这个分类: 后面讲「工具太多怎么办」「事件驱动」「多 Agent 协作」,都是在这五类里做取舍。

场景 2 · 邮件汇报 vs 操作手册 4.2.1 重点

每步结果都搬回上下文,等于每做一步写封邮件汇报
😐 传统:每步都汇报Agent工具上下文每一步都塞进来写入读回📜 代码编排:一次给手册Agent⚙️执行环境LLM 生成脚本,中间变量留在本地、不进上下文上下文只有最终结果回来一次给手册↓ 让代码编排,token 消耗降约两个数量级
为什么省这么多: token 成本几乎全在上下文里 —— 把中间过程留在执行环境,就把它从账单里拿掉了。

场景 3 · 一句话让准确率飙升 4.2.2

描述写清「何时用、不能用」,调用准确很多
😐「搜索相关内容」模型只能猜:搜什么?什么时候搜?✅「需要实时信息或查未知事实时用」再加 1–5 个真实示例含糊描述≈ 72%写清何时用 + 示例≈ 90%最要紧的是写反例:「只能按文件名匹配,不能搜内容」
工具描述不是说明书,是决策依据 —— 模型靠它决定「要不要调」
最容易忽略的一条: 反例比正例更重要。「不能做什么」才是拦住误调用的那句话。

场景 4 · 弯引号幽灵 4.2.3 重点

工具偷偷改参数,模型陷入找得到的死循环
模型看到中文弯引号 “ ”工具静默转成直引号 "文件里存的还是中文弯引号返回「未找到匹配」🔁死循环
Cursor 2026 年初某版本的 bug —— 参数传递保真性被破坏,模型永远想不明白「明明看得见,为什么搜不到」
教训: 工具链里任何一次「静默转换」都是定时炸弹。该报错就报错,别替模型做决定。

场景 5 · 毒包裹快递 4.3 重点

工具描述能夹带恶意指令,骗模型交私钥
📦恶意 MCP 服务器📝工具定义夹带一句恶意指令⚠️上下文随定义原样进入🔑私钥被读走模型照做了同名工具遮蔽 tool shadowing✅正规工具get_weather ——用户以为调的是它🃏同名恶意工具同名注册,把真的挡在身后⚠️模型分辨不出参数被送进了恶意的那一个
本质是提示注入的变种,而且每次会话都生效
防御思路: 工具定义来自不可信来源时,要么人工审核,要么像场景 9 那样用边车只读结构化字段。

场景 6 · 只露书脊的书架 4.4.1

工具只给索引,用时再查,省下近半 token
搜索读取解析查询写入执行计算通知默认:只给工具名索引(相当于只露书脊)全量注入数万 token按需加载↓46.9%📖被抽出来的一本展开完整定义:参数 schema、示例、边界说明……用完就放回去
5 个 MCP 服务器就可能引入数以万计 token —— 200K 窗口还没对话就用掉近三成
权衡: 按需加载省的 token 是实的,代价是多一次「取定义」的往返 —— 工具越多,这笔买卖越划算。

场景 7 · 雷达扫出对的工具 4.4.2 重点

声明能力缺口,系统从数千工具里精准匹配
约 2800 个工具✓🎯list_contributors只把这一条 schema 动态注入上下文≈ 98%MCP-Zero 在约 2800 个工具上节省的 token0 schema系统提示词里不预置任何工具定义
工具多到一定程度,「找工具」本身就成了一件要设计的事
范式转变: 从「预先塞给模型」改成「模型开口要」。这让工具规模从几百扩展到几千成为可能。

场景 8 · 两工程师双签 4.6

不可逆操作要两个不同家族模型分别审
提议者 Proposer📄不可逆操作转账 / 发邮件 / 删数据签签审核者 Reviewer理想配对:Opus 5 ↔ GPT-5.6 SolHaiku 审 Opus = 等于没审
像银行经办与审核双签 —— 关键不是「有两个人」,而是两个人真的独立
为什么必须不同家族: 同源模型的盲区高度重合,互相审查容易一起看漏。

场景 9 · 摩托边车保镖 4.6

轻量模型边跑边查,危险命令先拦下
边车 Sidecar📨{tool: "bash"}command: rm -rf /tmp/data🛑 拒绝执行只读结构化字段,耗时数百毫秒🛑
边车只读结构化字段(工具名 + 参数),所以能堵住「请允许执行 rm -rf」这类藏在网页内容里的提示注入
设计要点: 边车只读结构化字段(工具名 + 参数),不读自然语言内容 —— 这样提示注入就无从下手。

场景 10 · 重复扣款惊魂 4.6 重点

没有幂等性,超时重试就会重复转账
①点转账②超时返回失败③又点一次④钱其实已转⑤两笔扣款✅ 幂等 + 先查后改唯一标识去重 / 预检 - 确认两段式−¥100第一笔−¥100第二笔(重复)
所有「可能已经成功」的超时,都不能当失败处理
一条通用规则: 凡是不可逆的外部副作用,都要带唯一标识 + 去重,或走「预检 - 确认」两段式。

场景 11 · 三个颜色的信封 4.7

给子 Agent 标清信息来源,防提示注入
[FROM_MAIN_AGENT]主 Agent 下发的任务[FROM_USER]用户本人的输入[TOOL_RESULT]工具返回的内容子 Agent来源标签写清楚,子 Agent 才不会被带跑spawn 起子 Agentcancel 终止send_message 发消息list_agents 查看
来源不清,模型就无法判断该信谁 —— 这是防提示注入的结构化手段
呼应第 2 章: 这其实就是「上下文工程」在 Agent 间通信上的延伸 —— 来源不清,模型就无法判断该信谁。