Claude Code 源码泄漏,普通人能学到什么
你好啊,我是阿杰,一个探索普通人 AI 破局之路的程序员,欢迎来到我的进化日常。👉 加入 AI 交流群,请公众号后台发送【进群】
前段时间,Claude Code 的 npm 包里被人发现了一个 cli.js.map 文件,里面的 sourcesContent 字段存着完整的 TypeScript 源码。把它提取出来,得到将近 4756 个文件。
有人用 AI 对这些源码做了一份深度分析报告,地址如下:https://github.com/tvytlx/ai-agent-deep-dive
于是我再蒸馏一些有价值的东西——Claude Code 是如何通过提示词设计和行为约束来驾驭 AI 的。
这些思路,不需要你懂编程,直接就能迁移到你日常使用 AI 的场景里。
今天把精华提炼出来,内容有点多,但每一条都是实打实能用的。
怎么用提示词让AI不乱来
源码里有个函数叫 getSimpleDoingTasksSection(),可能是整个 prompt 里最有价值的部分。它做的事情就一件:告诉模型什么该做,什么不该做。
看看它具体写了什么:
-
不要加用户没要求的功能。你让它改个 bug,它顺手重构了半个文件;你让它加一个功能,它加了三层抽象和五个你没要的错误处理——这些问题大家都遇到过。 -
不要过度抽象,三行重复代码好过一个不成熟的抽象。AI 特别喜欢搞过度设计,明明三行能解决的事,它非要给你造一个通用的、可扩展的、面向未来的框架出来。 -
不要给你没改的代码加注释和文档字符串。AI 还有个毛病,就是顺带把你没动过的代码也给”美化”一遍,加一堆没用的注释。 -
不要做不必要的错误处理和兜底逻辑。别设计面向未来的抽象,能简单解决的就简单解决。 -
先读代码再改代码。不要上来就动手,先把现有逻辑搞明白。 -
不要轻易建新文件。 -
不要给时间估计。 -
方法失败了先诊断,不要盲目重试,也不要一次失败就放弃。 -
结果要如实汇报,没跑过的不要说跑过了。
源码里写得很直白:这些问题的根源在于模型的行为没有被约束。Claude Code 的做法是把行为规范写成制度,不依赖模型临场发挥。
这个思路你完全可以搬过来用。不管你用什么 AI 工具,在你的提示词里加上类似的”不要做”清单,效果会好很多。很多人写提示词只写”要做什么”,但忘了写”不要做什么”。而 Claude Code 告诉我们,”不要做什么”可能比”要做什么”更重要**。
做事的人和验收的人要分开
Claude Code 至少有 6 个内建 Agent:通用执行、纯只读探索、纯规划、对抗性验证、使用指导、状态栏配置。
这个设计选择的出发点很朴素:让一个 Agent 同时研究、规划、实现、验证,每件事都做不扎实。
其中最有意思的是 Verification Agent(验证 Agent)。它的 prompt 写了 130 行,可能是整个源码里最精心设计的一段文本。它的核心方向就一件事:”想办法搞坏它”(try to break it)。
prompt 开头就点出两种常见的验证失败模式:
-
Verification avoidance:只看代码,不跑检查,写个 PASS 就走了。 -
被前 80% 迷惑:UI 看着不错,测试也过了,就忽略剩下 20% 的问题。
更狠的是,它列出了验证者常见的逃避借口,然后要求”识别你自己的合理化倾向”:
-
“代码看起来是对的” → 看是不是验证。跑一下。 -
“实现者的测试已经通过了” → 实现者也是 LLM。独立验证。 -
“大概没问题” → 大概不是验证。跑一下。 -
“这个太费时间了” → 不是你说了算的。
源码里还有一段解释:在传统软件工程里这是常识——写代码的人不应该是验收代码的人。但在 AI Agent 系统里,大部分产品还没做到这一步。
Claude Code 把它做成了一个独立角色,有自己的 system prompt、工具集和评判标准。写代码的 Agent 可能会倾向于觉得自己写得没问题,但验证 Agent 没有这个偏见,它的工作就是找问题。
这个思路普通人也能用。当你让 AI 帮你完成一项重要工作时,别让同一个对话去验收。开一个新对话,给它一个不同的角色定位——”你现在是一个挑剔的审查者,你的任务是找出上一轮输出里的问题”。效果会完全不一样。
另外还有个 Explore Agent(探索 Agent)的设计也值得说。它被明确禁止:创建新文件、修改已有文件、删除文件、运行任何改变系统状态的命令。它能用的工具只有 Glob、Grep、FileRead。
为什么要这么极端?因为探索阶段如果不小心改了东西,后面实现阶段就会出问题。把权限彻底隔离,是一种朴素但有效的安全设计。
这告诉我们一个原则:给 AI 分配任务时,要把”调研”和”执行”分开。调研阶段只让它看不让它动,执行阶段才放开权限。这样即使 AI 理解错了你的意图,也不会在调研阶段就把东西搞乱了。
Prompt 组装的”静态+动态”分离思维
Claude Code 的 system prompt 不是一大坨文本,而是分成两大块:
静态部分(可缓存):
-
身份定位 -
系统运行规范 -
做任务的行为规范 -
风险动作规范 -
工具使用语法 -
语气风格 -
输出效率
动态部分(按会话状态注入):
-
当前启用了哪些工具 -
CLAUDE.md 内容 -
环境信息(OS、shell、当前目录、模型名称) -
语言偏好、输出风格 -
MCP server 的使用说明 -
Token 预算说明
中间用一个 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 标记隔开。
这个设计有什么用?API 层可以对 system prompt 做前缀缓存。如果两次请求的 system prompt 前缀完全一样(字节级一致),第二次就可以跳过对前缀部分的处理。所以把不变的内容放前面,会变的放后面,缓存命中率就上去了。 源码注释里写得很直白:不要随意修改 boundary 之前的内容,否则会破坏缓存。
虽然普通人不用管 API 缓存的技术细节,但这个”不变的放前面,会变的放后面“的思路,对你写提示词也很有启发。
比如你日常用 AI,可以把你的个人背景、偏好、常用规则放在提示词前面(这些基本不变),把具体的任务要求放在后面(每次不一样)。这样你在同一个对话里反复提问时,AI 对你的理解会更稳定。
还有一个细节:动态部分的 section 不是每次都重新计算的。systemPromptSections.ts 里有一个 section registry,用 systemPromptSection() 创建的 section 会被缓存,直到执行 /clear 或 /compact。只有用 DANGEROUS_uncachedSystemPromptSection() 创建的才会每次重算。
这背后的原则是:能复用的不要重算,能按需加载的不要一开始就塞进去。
Token 就是预算:上下文经济学
源码里有一个很形象的比喻——Token 就是预算。每次调用模型之前,消息列表会经过四道处理:
-
Snip Compact:把历史消息中过长的部分裁剪掉。 -
Micro Compact:更细粒度的压缩,基于 tool_use_id 做缓存编辑。 -
Context Collapse:把不活跃的上下文区域折叠成摘要。 -
Auto Compact:当总 token 数接近阈值时,触发全量压缩。
这四道不是互斥的,它们可以叠加执行。优先级是:先做轻量的(snip、micro compact),再做重量的(context collapse、auto compact)。如果轻量压缩把 token 数压到阈值以下了,重量压缩就不需要跑。
如果四道压缩都没能把 token 数压下来,API 返回了 413(prompt too long),还有一个 reactive compact 机制。它会在收到 413 之后立刻触发一次紧急压缩,然后重试。但这个机制有防循环设计:每个 turn 只尝试一次。
这对普通人的启示是:AI 的上下文窗口是有限的,不是无限的。 当你在一个对话里聊太久,塞了太多内容,AI 的表现就会下降。这不是 AI 变笨了,而是它用来”思考”的空间被挤压了。
几个实用的做法:
-
一次对话对应一个任务。不要在同一个对话里又让它写文章又让它写代码。 -
关键信息要重复强调。因为长对话中早期信息可能被压缩掉了,重要的规则可以在关键节点再提醒一次。 -
适时开新对话。对话太长时,与其让 AI 自己压缩,不如你手动开新对话,只带上最核心的上下文。
源码里还有一个 Token Budget 系统:当用户指定一个 token 目标(比如”+500k”),系统会追踪每个 turn 的输出 token,在接近目标时注入 nudge message 让模型继续工作。以前模型可能自己觉得”差不多了”就停了,现在有了明确的预算概念。
还有几个上下文优化细节也值得了解:
-
Skill 按需注入:匹配到了才注入,启动时不会全部塞进去。 -
MCP instructions 按连接状态注入:没连上的 server 的说明不占空间。 -
Tool result budget:单个工具结果太大时,持久化到磁盘,只保留摘要在上下文里。
三层防护:安全层互不绕过
Claude Code 的安全设计有一个核心原则:三层防护可以互相配合,但任何一层不能绕过另一层。
三层是:
-
Speculative Classifier(提前预判):BashTool 的风险分类器,在 Hook 执行的同时并行运行。这个分类器可以在 Hook 执行的同时并行运行,不阻塞主流程。等到需要做权限决策的时候,分类结果可能已经出来了。这种”提前开始异步计算”的做法,减少了用户等待权限弹窗的时间。 -
Hook Policy Layer(策略层):PreToolUse hooks 可以做权限决策、修改输入、阻断流程。 -
Permission Decision(权限决策):综合规则配置和用户交互的最终决策。
其中有个关键规则:Hook 说 allow,不一定绕过 settings 里的 deny/ask 规则。 具体逻辑是:
-
Hook allow + 工具要求用户交互 + Hook 没提供 updatedInput → 仍然走权限检查 -
Hook allow + settings 里有 deny 规则 → deny 规则生效 -
Hook allow + settings 里有 ask 规则 → 仍要弹窗 -
Hook deny → 直接生效
源码里评论说:”强大但受控”是工程成熟度的标志。 Hook 有足够的表达力来做运行时策略调整,但它不能绕开核心安全模型。这意味着即使某个 Hook 写了 bug 或者被恶意利用,它也不能让一个被 deny 掉的操作悄悄通过。
这个思路普通人也能借鉴。 当你给 AI 设定规则时,要确保规则之间互不矛盾、互不绕过。比如你告诉 AI “不要删除任何文件”,同时又告诉它”按需清理临时文件”,这两条规则就可能冲突。好的做法是明确优先级:安全规则 > 功能规则。
还有一个有意思的设计:工具系统采用 fail-closed 默认值。新写一个工具的时候,如果忘了声明并发安全性,系统会默认它不安全,串行执行。忘了声明只读,系统会默认它会写,走更严格的权限检查。这种”忘了就严格”的设计,避免了一个很常见的问题:某个工具忘了配置,结果在没有权限检查的情况下执行了危险操作。
原则就是:默认严格,显式放宽。 你在给 AI 设定规则时也一样,默认不信任,需要放开的地方显式说明。
让模型”感知到”自己的能力
源码里有句话我觉得特别到位:
很多平台也有插件系统、也有工具市场,但模型本身不知道这些东西存在。就像你给一个人配了一整套工具箱,但他不知道箱子里有什么。
Claude Code 通过 skills 列表、agent 列表、MCP instructions、session-specific guidance、command integration 这些通道,让模型感知到自己当前有哪些扩展能力。
举个例子,MCP(Model Context Protocol)不只提供新工具。当 MCP server 连接上来的时候,如果 server 提供了 instructions,这些 instructions 会被拼进 system prompt。也就是说,一个 MCP server 能同时给模型两样东西:
-
新工具(通过协议注册) -
怎么用这些工具的说明(通过 instructions 注入 prompt)
这让 MCP 的价值远高于一个简单的工具注册表。模型不只知道”有这个工具”,还知道”什么时候该用、怎么用”。这才是生态真正发挥作用的前提。
这个洞察对普通人特别有用。 你有没有遇到过这种情况:你明明在 AI 工具里装了某个插件或开了某个功能,但 AI 就是不会主动用它?原因很可能就是——AI 不知道自己有这个能力。
解决方法很简单:在提示词里明确列出你希望 AI 使用的工具或能力。比如”你可以使用网页搜索功能来获取最新信息”,而不是默认 AI 会自己去搜。
工具执行不是”模型说调就调”
当模型决定调用一个工具的时候,toolExecution.ts 里有一条完整的执行流水线,一共 14 步:
-
找工具:通过名字或别名找到对应的 Tool 对象 -
解析 MCP 元数据:如果是 MCP 工具,提取 server 信息 -
Zod schema 校验:用输入 schema 做第一道校验,挡住乱传参数 -
validateInput():工具自己的细粒度校验 -
Speculative classifier:如果是 BashTool,启动一个预测性分类器,在权限决策之前就开始分析命令的风险等级 -
PreToolUse hooks:运行所有注册的 pre-hook -
解析 Hook 权限结果:Hook 可能返回 allow、ask、deny,也可能修改输入 -
走权限决策:综合 Hook 结果、规则配置、用户交互,做出最终允许/拒绝决策 -
修正输入:如果权限决策或 Hook 修改了输入,用修改后的版本 -
执行 tool.call():真正跑工具 -
记录 analytics / tracing / OTel:遥测和可观测性 -
PostToolUse hooks:成功后的 post-hook -
处理结果:结构化输出、tool_result block 构建 -
PostToolUseFailure hooks:如果失败了,跑失败 hook
为什么说这个有意义?因为它体现了 Claude Code 的核心设计哲学:模型说要调工具,中间还要过输入校验、权限检查、风险预判。执行完了还有后处理和失败处理。这层治理决定了系统在异常情况下的表现。
还有一个优化值得说:StreamingToolExecutor。传统做法是等模型完整输出,收齐所有 tool_use block,再一起执行。Claude Code 做了个优化:模型还在输出的时候,已经完成的 tool_use block 就开始执行了。一次 turn 里如果有 5 个工具调用,传统做法要等模型输出完(可能 5-30 秒),然后串行或并行执行工具。streaming 模式下,第一个工具在模型还在生成第二个 tool_use 的时候就已经跑完了。
这个思路虽然普通人用不上,但它说明了一个原则:好的设计不是让每一步都完美,而是让整体流程尽可能高效。
七条设计原则
源码最后提炼了七条设计原则,每一条都有对应的源码实现做支撑。我把它们翻译成普通人能直接用的版本:
原则一:不信任模型的自觉性。 你希望模型先读代码再改代码,就写进 prompt。你希望模型不要乱加功能,就写进 prompt。你希望模型遇到风险操作停下来,就在运行时加权限检查。不要指望一个 LLM 每次都”想到”该怎么做。制度化的行为比临场发挥稳定得多。
原则二:把角色拆开。 至少把”做事的人”和”验收的人”分开。哪怕用同一个模型,职责拆开也会有明显改善。因为同一个 Agent 既实现又验证,天然倾向于觉得自己做得没问题。
原则三:工具调用要有治理。 模型说要调工具,中间还要过输入校验、权限检查、风险预判。执行完了还有后处理和失败处理。这层治理决定了系统在异常情况下的表现。
原则四:上下文是预算。 每个 token 都有成本,每条信息都占空间。能缓存的要缓存,能按需加载的不要一开始就塞进去,能压缩的要压缩。
原则五:安全层要互不绕过。 三层防护可以互相配合,但任何一层不能绕过另一层。Hook 的 allow 不能绕过 settings 的 deny。这样即使某一层出了问题,整体安全性不会崩塌。
原则六:生态的关键是模型感知。 你给系统接了十个插件,但模型不知道什么时候该用哪个,那这十个插件就等于不存在。扩展机制的最后一步,是让模型看到自己的能力清单。
原则七:产品化在于处理第二天。 第一天跑起来不难。难的是任务中断怎么续、脏状态怎么清、进程泄漏怎么办、session 怎么恢复。这些问题不解决,产品就只能是 Demo。
以上就是从 Claude Code 源码分析文档里提取出来的、对普通人最有价值的部分。可以看到,Anthropic 在设计 AI 系统时的核心思路其实很朴素:不信任自觉性,靠制度约束行为;不贪大求全,把角色拆开各司其职;不忽视异常,从第一天就考虑第二天的事。
这些原则不只是在做 Agent 系统时有用,你日常和任何 AI 工具打交道,都能用上。
👨🏻💻 我是阿杰,一个 All in AI 的前端摸鱼工程师,专注分享普通人拿来即用的AI 实战与副业变现。🌟 如果觉得文章有用,欢迎「关注 + 星标」,交个朋友,一起进化。
夜雨聆风