第 06 章 · 工具执行前的那道门
上一章:05 · 流式、取消与事件 下一章:07 · JSONL 会话与用量
这一章要解决的问题
到第 05 章为止,agent 已经能调用工具了。方式很简单:模型说要用哪个工具、参数是什么,代码就照着执行。
这里面有个容易被忽略的事实:工具的参数是模型填的,不是你填的。你给的是一句自然语言任务,模型自己决定要碰哪个文件、执行哪条命令。
第 01 节会让你亲眼看一次:你只说"帮我看看本机 DNS 配置有没有问题",从头到尾没提任何路径,agent 自己去读了 /etc/hosts。
现在这还只是读文件。第 13 章会加写文件,第 18 章会加 shell 命令。同一条链路——模型自己定目标,代码直接执行——后果会从"内容泄露"变成"文件被改"和"命令被执行"。
这一章不加任何新能力,只做一件事:在工具执行之前,建一道判断的关卡。
关卡现在几乎不拦什么,因为还没有危险工具。但位置要现在就留出来。等 shell 上线之后再补,代价大得多。
读完这一章,你能做到
- 你能让 agent 读走一个项目外的文件,也能把它挡住。挡住之后,模型会收到一条说明,然后自己换一条路,程序不会崩。
- 有人问你"这个 agent 到底允许干什么",你能指着一个函数回答,而不是翻三个工具的代码。
- 你能跑一条命令,让测试告诉你这些规则确实生效了。这条命令不需要 API key。
小节
| # | 小节 | 讲什么 |
|---|---|---|
| 01 | 先看它出事 | 给 agent 一个读文件工具,看它自己选路径读到项目外 |
| 02 | 检查该由谁来做 | 先给解法,再拆:要哪三样信息、为什么不能写在工具里 |
| 03 | 判断写成一个纯函数 | 它不读配置不弹窗,为什么值得这么克制 |
| 04 | 把三样信息写成类型 | 三样信息变成类型,编译器列出待办 |
| 05 | 把执行搬出主循环 | 先搬家,不改逻辑 |
| 06 | 为什么用 for,不用 map | 一个看起来是风格、其实不是的改动 |
| 07 | 工具声明自己是什么 | 事实和权限要分开 |
| 08 | 这次 run 的规矩 | 默认值定成最紧的那一档 |
| 09 | 把信息拼成一个请求 | 决策函数只看一个对象 |
| 10 | 两个权限取交集 | 谁更严听谁的 |
| 11 | 决策函数:顺序就是优先级 | 八条规则,为什么是这个顺序 |
| 12 | 最后那一行兜底 | 拿不准的时候,问,而不是放行 |
| 13 | 把决策接进运行时 | 三行代码,四个事件 |
| 14 | 拒绝不是异常 | 为什么是 return,不是 throw |
| 15 | 暂时没法问人怎么办 | 先关闭失败,把接口留好 |
| 16 | 回到第 01 节那条命令 | 同一条请求,现在什么结果 |
| 17 | 让测试替你确认 | 每条测试对应哪一节 |
| 18 | 这一章埋了什么 | 哪些东西是留给后面章节的 |
| 19 | 离生产版还差什么 | 差距,以及补上要付的代价 |
前四节讲清楚"为什么要这么做",第 05 到 15 节是动手写代码,第 16 节之后是验收和回顾。
动手之前
确认你的代码停在第 05 章结束的状态。如果不确定,跑一下测试,应该全绿:
bash
npm test从 01 · 先把它跑坏 开始。