有个词我越来越喜欢——脆弱。
一个系统越聪明、越强大,就越脆弱。因为它能做的事情越多,出错时的破坏力就越大。反过来说,真正结实的系统,不是因为它聪明,而是因为外面套了一个足够扎实的约束骨架。
Claude Code 的架构里,有五层这样的约束。每一层的目的都不同,但它们加在一起,才能让一个有深度权限的 AI Agent 可以被信任。
第一层:受约束的会话系统
你可能用过 Claude,也知道 system prompt 的概念。它就是"你好,我是个有用的助手"这样的前置文案。
但 Claude Code 的 system prompt 不是这个意思。它不是给模型的人物设定小传,而是一份运行时协议。
具体来说,getSystemPrompt() 返回的不是一个字符串,而是一个数组。数组里的每个元素代表一个不同职责的约束块:
- 身份说明块(你是什么身份,你的能力边界是什么)
- 系统规则块(这个系统里有什么东西是禁区的,比如改 git config、跳过 hooks、随手 git add .)
- 工程约束块(权限检查怎么做,错误怎么处理,上下文怎么用)
- 决策规范块(遇到多个可能的做法时,优先级是什么)
这样设计的妙处在于:每一个约束块都是独立可维护的。如果你要改进某个约束,你不用去改整个 prompt,只改那一块。如果要临时打破某个规则,你可以显式地说"我知道我在违反什么",而不是靠模型去猜测。
这一层的核心原则是:约束必须明确且可组合。模糊的约束就不是约束,那只是祝福。
第二层:代理依赖持续循环
Chat 的模型是这样的:你给我一句话,我回你一句话,对话完成。
Agent 的模型不一样。它有一个叫 queryLoop() 的核心循环——模型说"我要调用工具 A",系统执行工具 A,把结果喂回去,模型再说"我要调用工具 B"。这个循环反复进行,直到模型说"完成"或者用户打断。
为了让这个循环跑得稳,系统要跨轮次维护一堆状态:
- messages:每一轮对话历史,包括用户说了什么、模型说了什么、工具返回了什么
- toolUseContext:每个工具的调用上下文,防止同一个工具在不同轮次里产生冲突
- autoCompactTracking:什么时候消息记录太多了、快要超 token 限制了、要自动压缩
- maxOutputTokensRecoveryCount:如果模型跑了一半突然没了,怎么恢复
这一层的核心原则是:状态管理和错误恢复是主路径的一部分。你不能把它看成"另外处理"的事情,否则会埋下隐患。
第三层:工具调用必须服从调度
假设模型同时要调用三个工具:改文件、跑测试、发网络请求。这三个能不能并发执行?
答案是:不一定。
系统首先会用 partitionToolCalls() 来判断。它问的是:这些工具之间有没有依赖关系?有没有互相冲突的地方?改文件和跑测试能否并发?
然后,如果确定可以并发,系统会用一个非常巧妙的机制:context modifier 先缓存,再按原始 block 顺序回放。
什么意思呢?就是说:虽然工具 A、B、C 实际上是并发执行的,但系统确保从语义上看,它们的执行顺序是确定的。这样做的好处是,不管实际的 CPU 怎么调度,最终的结果都是一致的。
这一层的核心原则是:并发要有序,有序要可验证。不能为了速度而丧失确定性。
第四层:最危险的工具要配最细的规矩
说到权限,最危险的是什么?是 shell 命令。
shell 几乎不受任何领域知识的约束。你可以用 shell 读文件、改文件、删文件、启动进程、改 Git 配置、改网络设置。什么都能做。
这也是为什么,Claude Code 专门给 Bash 写了一整套操作规约。例如:
- 不要乱改 git config——这会影响后续所有 Git 操作
- 不要跳过 hooks——hooks 里往往有很重要的检查逻辑
- 不要随手 git add .——这很容易把你不想提交的文件捞进来
这些规约不是建议,而是硬约束。模型会反复被提醒,不要违反它们。
而且,这些约束是风险等级的。开发一个功能,可能要调用五个工具。系统会按风险等级排序:最危险的工具要最先做、最全面的检查。
这一层的核心原则是:权限越大,约束越细。权限不是二进制的(有或没有),而是要分风险等级的。
第五层:错误是主路径的一部分
这一层最重要,也最容易被忽视。
很多软件架构把失败路径看成例外——"这不应该发生,但如果发生了,我就……"。但 Agent 系统不能这样想。
模型会超 token。系统会遇到 "prompt too long"。工具会被拒绝(比如权限不足、网络超时)。用户会打断。模型会给出不确定的回答。
这些不是偶发事件,是结构性条件。它们发生的概率很高,处理它们的逻辑要和处理"正常情况"一样严谨。
所以 Claude Code 的做法是:把每一种错误情况都编进状态机。超 token 了?有恢复机制。工具拒绝了?有重试逻辑。用户打断了?有保存进度的机制。
这一层的核心原则是:错误处理的代码量,往往比正常逻辑的代码量还要多。这不是坏事,这是成熟系统的标志。
为什么要分这五层
我经常看到 AI 产品团队,把所有的努力都放在"怎么让 Prompt 更聪明"上。很少有人花时间去设计这五层约束。
但现实是,五层约束的贡献,往往比聪明的 Prompt 还要大。
一个聪明的 Prompt 可能能提高模型的准确率 5%。但一个扎实的约束骨架,能降低系统的故障率 50%,甚至更多。
而且,约束的收益是累积的。第一层约束可能只管了 20% 的风险,但加上第二层、第三层、第四层、第五层,每一层都在接住上一层漏掉的情况。最后的效果是指数级的。
最后的对比
我想对比一下两个不同的 Agent 架构:
架构 A(很多初创公司在用):
- 一个超级聪明的 Prompt
- 直接调用工具
- 遇到错误就重试
- 缺少完整的状态管理
架构 B(Claude Code 的思路):
- 五层约束骨架
- 受调度的工具调用
- 结构化的错误处理
- 完整的状态跟踪和恢复
短期来看,架构 A 的成本更低。长期来看,架构 B 的可靠性高一个数量级。
如果你要构建一个真正生产级别的 Agent 系统,你需要的不是更聪明的模型,而是架构 B 这样的骨架。
因为,最终决定系统靠不靠谱的,不是模型的能力,而是约束的扎实程度。
<!-- 改动说明:分别讲解五层约束的设计细节,用"脆弱-权限-破坏力"的链条来开篇,中间穿插原则总结,最后用架构对比来说明约束体系的价值。全文强调架构的重要性,避免只讲 Prompt 工程。 -->