乐于分享
好东西不私藏

Claude Code 源码泄漏,普通人能学到什么

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 开头就点出两种常见的验证失败模式:

  1. Verification avoidance:只看代码,不跑检查,写个 PASS 就走了。
  2. 被前 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 就是预算。每次调用模型之前,消息列表会经过四道处理:

  1. Snip Compact:把历史消息中过长的部分裁剪掉。
  2. Micro Compact:更细粒度的压缩,基于 tool_use_id 做缓存编辑。
  3. Context Collapse:把不活跃的上下文区域折叠成摘要。
  4. 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 的安全设计有一个核心原则:三层防护可以互相配合,但任何一层不能绕过另一层。

三层是:

  1. Speculative Classifier(提前预判):BashTool 的风险分类器,在 Hook 执行的同时并行运行。这个分类器可以在 Hook 执行的同时并行运行,不阻塞主流程。等到需要做权限决策的时候,分类结果可能已经出来了。这种”提前开始异步计算”的做法,减少了用户等待权限弹窗的时间。
  2. Hook Policy Layer(策略层):PreToolUse hooks 可以做权限决策、修改输入、阻断流程。
  3. 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 能同时给模型两样东西:

  1. 新工具(通过协议注册)
  2. 怎么用这些工具的说明(通过 instructions 注入 prompt)

这让 MCP 的价值远高于一个简单的工具注册表。模型不只知道”有这个工具”,还知道”什么时候该用、怎么用”。这才是生态真正发挥作用的前提。

这个洞察对普通人特别有用。 你有没有遇到过这种情况:你明明在 AI 工具里装了某个插件或开了某个功能,但 AI 就是不会主动用它?原因很可能就是——AI 不知道自己有这个能力。

解决方法很简单:在提示词里明确列出你希望 AI 使用的工具或能力。比如”你可以使用网页搜索功能来获取最新信息”,而不是默认 AI 会自己去搜。

工具执行不是”模型说调就调”

当模型决定调用一个工具的时候,toolExecution.ts 里有一条完整的执行流水线,一共 14 步:

  1. 找工具:通过名字或别名找到对应的 Tool 对象
  2. 解析 MCP 元数据:如果是 MCP 工具,提取 server 信息
  3. Zod schema 校验:用输入 schema 做第一道校验,挡住乱传参数
  4. validateInput():工具自己的细粒度校验
  5. Speculative classifier:如果是 BashTool,启动一个预测性分类器,在权限决策之前就开始分析命令的风险等级
  6. PreToolUse hooks:运行所有注册的 pre-hook
  7. 解析 Hook 权限结果:Hook 可能返回 allow、ask、deny,也可能修改输入
  8. 走权限决策:综合 Hook 结果、规则配置、用户交互,做出最终允许/拒绝决策
  9. 修正输入:如果权限决策或 Hook 修改了输入,用修改后的版本
  10. 执行 tool.call():真正跑工具
  11. 记录 analytics / tracing / OTel:遥测和可观测性
  12. PostToolUse hooks:成功后的 post-hook
  13. 处理结果:结构化输出、tool_result block 构建
  14. 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 实战副业变现。🌟 如果觉得文章有用,欢迎「关注 + 星标」,交个朋友,一起进化。