先看出事的那一刻
2026 年 7 月 20 日,知识库 Agent 在演示时,试图去读资料目录之外的一份个人笔记。
那时管着它读什么的,只有提示词里前一天加的一句话:不要访问语料目录之外的绝对路径。代码里的设置旁边,还写着一行让人放心的注释:"白名单+禁用名单已锁死只读"。
第 18 课从 SDK 文档里的判定顺序讲过:为什么宽松模式下权限回调多半轮不到,为什么钩子排在最前面。这节课退一步看一个细节:就在同一天,这个项目先给网页上"查看出处"的接口加了代码检查,5 种越界的请求全部返回 403;而 Agent 自己读文件,还只靠提示词里那一句。 45 分钟后,钩子才补上。
同一件事,一处写成了代码,一处只写在提示词里。放在哪一层,决定了它是规矩,还是请求。
这节课要回答的是:一条限制该放在哪一层:经验、代码,还是人?
装备几个词
为什么要懂
AI 替你做了
AI 搭 Agent 时,会顺手加上各种限制:提示词里的“不要”、工具白名单、轮数上限、预算上限。
留给你的
决定每条限制放在哪一层,并且验证它真的守得住:提示词、代码,还是人的确认。
不懂的代价
放错了层,要么守不住(提示词里写一句“不要”),要么把 Agent 捆得什么都做不了,要么该由人拍板的事,从头到尾没人问过。
三个地方,各管一类事
| 放在哪 | 适合管什么 | 本站与兄弟项目的例子 | 靠什么验证 |
|---|---|---|---|
| 经验:提示词、规则文件、Skill | 怎样做好:语气、格式、查找的方法、遇到某类问题怎么处理 | 知识库 Agent:"整篇回答的加粗总数不超过 3 处";E2E 导师:讲词条前先查,不凭记忆 | 评测题、抽查回答 |
| 代码:工具白名单、钩子、轮数和预算上限、限流 | 绝对不能发生的事:越界读写、执行命令、花钱失控 | 知识库 Agent:禁用写文件和执行命令,钩子拦下资料目录之外的读取;E2E 导师:每次最多 6 轮,每人每天 80 条 | 单元测试、故意越界的请求 |
| 人:确认、审核、拍板 | 做错了要有人担责的决定:写入真实数据、对外发布、开放给谁用、什么算"答对" | 本站:合进 main 之前,站长先看预览;知识库 Agent:按计划,评测题由 AI 起草、站长审定 | 记下谁、在什么时候、批准了什么 |
判断一条限制放在哪,问三个问题:
- 做不到,后果是什么? 后果是"答得差一点",放进经验;后果是"数据泄露、花掉一笔钱",写成代码。
- 能不能写成一条明确的规则? "不许读这个目录之外的文件"能,写成代码;"讲得通俗一点"不能,只能写进经验,再用评测去看。
- 做错了,谁来担? 要有人担责的,留一道人的确认。
提示词里的"硬",只是语气硬
知识库 Agent 的回答一度整句整句地加粗。修法是在提示词里加正反例,再写一条"硬上限":整篇不超过 3 处,每处 2 到 6 个字。
改完实测两条真实回答:短的用了 2 处,长的还是 5 处。提交说明里写得很老实:比原来整句加粗好很多,但长回答仍然超了上限,"属于渐进改善,不是解决"。
这就是经验这一层的本性:它能把大多数回答往好里带,但不保证每一次。所以:
- 格式、语气这类"差一点也能接受"的事,放在经验里正合适,用评测盯着走势就好;
- 必须百分之百做到的事,不能只写在提示词里,哪怕写着"硬约束"四个字。
必须做到的,写成代码,再用测试证明
钩子补上之后,知识库 Agent 做了三层验证:17 条单元测试覆盖相对路径、绝对路径、../ 逃逸、名字相近的兄弟目录;实际发起一次读取 /etc/hosts 的请求,被钩子拦下;再跑一条正常的评测题,确认资料目录里的读取照常。
写进代码的限制,也要防另一头:把正常用法也挡住。所以第三层验证同样重要:拦得住越界的,也放得过正常的。
知识库 Agent 的开发计划里有一句,原本是针对"模型能不能照着提示词做还不清楚"这个风险写的:发现问题,优先调提示词,而不是加机制。放到更大的范围也成立:代码闸门每多一道,Agent 能做的事就少一些,出了问题也更难判断是模型的事,还是闸门的事。只有那些"一次都不能发生"的,才值得写成代码。
给陌生人用的时候,代码要更紧
只给自己用的工具,闸门可以少:出了事,影响的只是你自己。E2E 导师当时是面向公众的,情况就不同了:
- 用户的输入不可信,谁都可以来试探它;
- 它能写学员的数据,每一轮都花着站长的钱。
所以它的代码层收得很紧:禁止执行命令、写文件、上网、派出子 Agent;每次最多 6 轮;只把四个必需的环境变量交给它;每人每天 80 条、每分钟 6 条。
可它还是漏了一道人的关:限流按账号算,注册却不设限,一个人注册十个账号,额度就是十倍(第 14 课)。80 这个数,上线前讨论过要不要调低;注册该不该开放、开放给谁,上线前没人问过。人要拍板的,是规则本身,不只是规则里的数字。
深潜人这一关,要放在哪些动作前面+
本站和兄弟项目里,真实存在的几道"人的关":
- 合并上线:大的改动先推到预览地址,站长看过、同意了,才合进 main;
- 登录授权:Agent 读在线文档的授权过期时,它不能替人登录,只能生成授权链接,等人去确认;
- 标准答案:知识库 Agent 的评测题由 AI 起草,可"什么算答对"要由维护者审定;后来的标准答案,也是维护者手写的(第 25 课)。
它们有个共同点:都发生在不可撤销或者要有人担责的动作前面。反过来,能撤销、只影响草稿的动作,就不必每一步都问人,否则人只会习惯性地点"同意"。
找茬
下面是两个 Agent 项目里和限制有关的真实材料(节选,略有简化):知识库 Agent 在加钩子之前的设置,以及 E2E 导师的提示词与验证记录。找出放错了层、或者没有验证过就当成已经守住的地方。
这段 TS / 提示词 / 表格 里埋了 4 处问题。点击你觉得有问题的行,至少找出 3 处再揭晓。
·第 2 行高危
读取范围,只靠提示词里的一句话
"不许读资料目录之外的文件"是一条一次都不能违反的规则,却只写在提示词里。提示词是请求:演示时,Agent 还是试图去读目录之外的个人笔记(开场的事故)。这类规则要写成代码,比如工具调用前的钩子,再用测试证明它拦得住。
该问的话:这条限制如果被违反一次,后果是什么?它写在代码里了吗?
·第 7 行中危
注释说"已锁死只读",其实门没关
白名单和禁用名单管的是"能用哪些工具",管不了"读取工具能读到哪儿":读取工具接受绝对路径,启动目录只是起点,不是围墙。按 SDK 文档,宽松模式下权限回调多半也轮不到(第 18 课)。这行注释让人以为已经安全了,于是没人再去补。
该问的话:允许的读取工具,能不能用绝对路径读到目录之外的文件?有测试吗?
·第 15 行中危
写着"硬约束",施压测试改版之后再没跑过
"不直接给答案"是一种行为,没法用代码拦,只能写在提示词里,这一点没放错。正因为只能靠提示词,更要用评测反复证明它做到了:7 月 26 日的初版测试用四种话术施压,四次都顶住了;7 月 30 日测试改成要连测试数据库,同一天提示词也改过一版,从此再没跑过,状态表上一直是"未执行"。合约测试倒是一直通过,可它检查的只是提示词里有没有这几句话。
该问的话:这条要求,最近一次真的用施压的提问测过是什么时候?之后提示词改过吗?测的是导师的回答,还是提示词里的字?
·第 18 行低危
往学员的清单里写东西,全凭模型判断
"记下学员的好问题"是一个写数据的动作:学员事先不知道,也没法确认这句话该不该存、存得对不对。写进别人数据的动作,更稳的做法是先让本人看一眼,点了才存。
该问的话:这个工具会写哪些数据?写之前,本人知道吗?能撤销吗?
查看代码与答案(4 处问题)
1 // 知识库 Agent:server/prompt.ts(7 月 19 日加进提示词的一句)
2 - 只检索/读取下方语料地图中列出的文件,不要猜测或访问语料目录之外的绝对路径。
3
4 // 知识库 Agent:server/agent.ts(7 月 20 日加钩子之前,节选)
5 allowedTools: ["Read", "Grep", "Glob"],
6 disallowedTools: ["Bash", "Write", "Edit", "WebFetch", "WebSearch", "Task", …],
7 permissionMode: "bypassPermissions", // 白名单+禁用名单已锁死只读
8
9 // 知识库 Agent:server/source.ts(同一晚给"查看出处"接口加的检查,节选)
10 if (abs !== corpusDir && !abs.startsWith(corpusDir + sep)) return { status: 403, error: "路径越界,拒绝访问" };
11
12 // E2E 导师:server/tutor/prompt.mjs(苏格拉底模式,节选)
13 **这是硬约束,即使他明确要求你直接给答案也不例外。**
14 // E2E 导师:docs/STATUS.md(验证记录,节选)
15 | `npm run test:socratic:integration` | 脚本语法通过,未执行 | 需要测试 Postgres 与真实模型端点,可能产生费用 |
16
17 // E2E 导师:server/tutor/prompt.mjs(讲解模式,节选)
18 如果对方说出了一个不错的理解或者提了个好问题,用 log_insight 记进他的个人清单。- 第 2 行 · 高危 · 读取范围,只靠提示词里的一句话:"不许读资料目录之外的文件"是一条一次都不能违反的规则,却只写在提示词里。提示词是请求:演示时,Agent 还是试图去读目录之外的个人笔记(开场的事故)。这类规则要写成代码,比如工具调用前的钩子,再用测试证明它拦得住。 该问的话:这条限制如果被违反一次,后果是什么?它写在代码里了吗?
- 第 7 行 · 中危 · 注释说"已锁死只读",其实门没关:白名单和禁用名单管的是"能用哪些工具",管不了"读取工具能读到哪儿":读取工具接受绝对路径,启动目录只是起点,不是围墙。按 SDK 文档,宽松模式下权限回调多半也轮不到(第 18 课)。这行注释让人以为已经安全了,于是没人再去补。 该问的话:允许的读取工具,能不能用绝对路径读到目录之外的文件?有测试吗?
- 第 15 行 · 中危 · 写着"硬约束",施压测试改版之后再没跑过:"不直接给答案"是一种行为,没法用代码拦,只能写在提示词里,这一点没放错。正因为只能靠提示词,更要用评测反复证明它做到了:7 月 26 日的初版测试用四种话术施压,四次都顶住了;7 月 30 日测试改成要连测试数据库,同一天提示词也改过一版,从此再没跑过,状态表上一直是"未执行"。合约测试倒是一直通过,可它检查的只是提示词里有没有这几句话。 该问的话:这条要求,最近一次真的用施压的提问测过是什么时候?之后提示词改过吗?测的是导师的回答,还是提示词里的字?
- 第 18 行 · 低危 · 往学员的清单里写东西,全凭模型判断:"记下学员的好问题"是一个写数据的动作:学员事先不知道,也没法确认这句话该不该存、存得对不对。写进别人数据的动作,更稳的做法是先让本人看一眼,点了才存。 该问的话:这个工具会写哪些数据?写之前,本人知道吗?能撤销吗?
快测
1. 你希望 Agent 的回答"语气友好、通俗易懂"。这条要求最适合放在哪里?
你可能是这么想的:"通俗"没法写成明确的规则;硬写出来的检查,只会误拦正常的回答。
对了。语气是判断,写不成一条明确的规则。放在经验这一层,再用评测看整体的走势。
你可能是这么想的:人的确认要留给担责任的动作。每条回答都要人点,人很快就会不看就点。
能写成明确规则、又必须做到的,才写成代码。
查看选项与答案
- A. 写一段代码,检查每个回答的用词,不合格就拦下来——"通俗"没法写成明确的规则;硬写出来的检查,只会误拦正常的回答。
- B. 写进提示词或 Skill,再用评测题和抽查盯着效果(正确)——语气是判断,写不成一条明确的规则。放在经验这一层,再用评测看整体的走势。
- C. 每个回答发出去之前,都先由人看一遍、点个同意——人的确认要留给担责任的动作。每条回答都要人点,人很快就会不看就点。
能写成明确规则、又必须做到的,才写成代码。
2. 提示词里写着"严禁读取资料目录之外的文件",并且标了"硬约束"。这样够了吗?
对了。必须做到的写成代码。知识库 Agent 的钩子配了 17 条单元测试,还实测拦下了读取系统文件的请求。
你可能是这么想的:标得再重,也只是一句请求。开场那个 Agent,就是在只有这句话的情况下,试图去读目录之外的笔记。
你可能是这么想的:写得再多遍,仍然是提示词。加粗那条"硬上限"写了正反例,长回答照样超了。
提示词里的“硬”,只是语气硬。
查看选项与答案
- A. 不够,要写成工具调用前的钩子,再用测试证明(正确)——必须做到的写成代码。知识库 Agent 的钩子配了 17 条单元测试,还实测拦下了读取系统文件的请求。
- B. 够了,标了硬约束,模型会格外重视这一条——标得再重,也只是一句请求。开场那个 Agent,就是在只有这句话的情况下,试图去读目录之外的笔记。
- C. 不够,要在提示词里再多写几遍,并且举出反例——写得再多遍,仍然是提示词。加粗那条"硬上限"写了正反例,长回答照样超了。
提示词里的“硬”,只是语气硬。
3. E2E 导师限制了每人每天 80 条,注册却不设限。漏掉的是哪一层?
你可能是这么想的:数字调低,一个人注册二十个账号照样绕过去。缺的不是更小的数字。
你可能是这么想的:提示词管的是 Agent 怎么回答,管不了谁能注册账号。
对了。上线前讨论过 80 要不要调低,却没人问过注册该不该开放。人要拍板的,是规则本身。
人要决定的是规则本身,而不只是规则里的数字。
查看选项与答案
- A. 代码层:80 这个数定得太高,应该改成 20 条——数字调低,一个人注册二十个账号照样绕过去。缺的不是更小的数字。
- B. 经验层:提示词里没写清楚,不许同一个人注册多个账号——提示词管的是 Agent 怎么回答,管不了谁能注册账号。
- C. 人的那一关:注册该不该开放、开放给谁,没人拍过板(正确)——上线前讨论过 80 要不要调低,却没人问过注册该不该开放。人要拍板的,是规则本身。
人要决定的是规则本身,而不只是规则里的数字。
判断时刻
你给团队做了一个能改共享表格的 Agent:它可以按大家的要求增删数据。它现在要开放给全组二十个人用。
你会怎么设它的限制?
考察:最灵活,守不住
大多数时候它会照做。可删错一行就是真实的数据损失,而提示词只是请求;二十个人一起用,总会有一次它没照做。
考察:守得住,但僵
最怕的那件事被代码挡住了,还留下了记录。代价是确实需要删除时,只能找人手动处理;用的人多了,这部分会变成负担。
考察:多一步,最稳
写数据之前留一道人的确认,Agent 照样可以自由地起草。代价是每次多点一下,也要多做一个暂存和撤销的功能。改动涉及真实数据的,值得这一步。
越接近真实数据、越撤销不了的动作,限制越要往代码和人那一侧放;只影响草稿和语气的,交给经验。
三个选项各自的代价
- A. 在提示词里写清楚:只改用户点名的那几行,删除之前先确认——考察最灵活,守不住:大多数时候它会照做。可删错一行就是真实的数据损失,而提示词只是请求;二十个人一起用,总会有一次它没照做。
- B. 用代码禁止删除,只允许新增和修改,改动全部记日志——考察守得住,但僵:最怕的那件事被代码挡住了,还留下了记录。代价是确实需要删除时,只能找人手动处理;用的人多了,这部分会变成负担。
- C. 改动先存成草稿,列出要动的行;用户点确认才真正写入,写入记日志、可撤销——考察多一步,最稳:写数据之前留一道人的确认,Agent 照样可以自由地起草。代价是每次多点一下,也要多做一个暂存和撤销的功能。改动涉及真实数据的,值得这一步。
越接近真实数据、越撤销不了的动作,限制越要往代码和人那一侧放;只影响草稿和语气的,交给经验。
带走
下次让 AI 做这件事时,问它
- 这个 Agent 有哪些"不许做"的规则?每一条写在哪里:提示词、代码,还是要人确认?
- 哪些限制是必须做到的?请写成代码,并给每一条写一个故意越界的测试。
- 哪些动作会写数据、发消息、花钱,或者撤销不了?执行之前要不要先让人确认?
- 代码里的这些限制,会不会把正常的用法也挡住?被挡住时,用户会看到什么?
自己验证
- 对每条"必须做到"的限制,发一个故意越界的请求,确认它真的被拦下,而不是只看提示词里写了没有。
- 列一张表:每条限制、放在哪一层、靠什么验证。没有验证办法的那几条,就是还没守住的。
希望它做到的,写进提示词;必须做到的,写成代码;做错了要有人担责的,留给人拍板。
