连载加餐01-claude code 源码泄漏 ---一起吃透 Claude Code,告别 AI coding 迷茫

加餐:源码曝光之后,我们真正学到了什么

AI Coding 系列 · 加餐篇 01


事情是这样的

昨天,Claude Code 的源码以某种方式流传开来,社区里有人做了完整的逆向解析,把核心模块一一拆开了看。

大多数人的第一反应是"这东西里面藏了什么秘密?"------结果发现没什么阴谋,全是工程决策。但正是这些工程决策,解释了很多用户长期困惑的问题:为什么 CLAUDE.md 有时候管用有时候不管用?为什么 Claude 有时候会改你说"不要动"的文件?为什么对话到一半感觉 Claude 越来越糊涂?

这篇不讲源码的架构细节(那是给写框架的人看的),讲的是源码暴露出来的、对你日常使用 Claude Code 直接有用的七件事

每一条都是"看了源码之后,你会改变的一个使用习惯"。


洞见一:Claude 看到的不是你输入的------CLAUDE.md 是纠偏器,不是文档

很多人以为自己在和 Claude 直接对话------你打字,Claude 回答。

源码告诉你实际不是这样。Claude Code 是一个"Harness",中文叫执行框架,它在你的输入和真正的 Claude 模型之间,做了大量的预处理和后处理。

你每次发消息,Claude 收到的实际上是这样一个结构:

css 复制代码
[Anthropic 的硬编码系统提示词]  ← 你看不到,优先级最高
[你的 CLAUDE.md 内容]          ← 你写的项目规则
[当前 Git 状态、工具列表]        ← 系统自动注入
[对话历史]                      ← 压缩或完整的历史
[你刚才输入的内容]               ← 才轮到你说的话

你"说的话"只是这个大包裹里的最后一部分。但这里有两件事值得深想,光知道结构是不够的。

第一件:你的 CLAUDE.md 盖不住 Anthropic 的系统提示词。

Anthropic 的系统提示词优先级最高,你看不到它写了什么。这意味着某些 Claude 的行为你无论怎么在 CLAUDE.md 里说都不管用------不是你写错了,是那个行为被更高优先级的指令固定住了。理解这一点可以避免很多无谓的挣扎:如果一个行为怎么写都改不掉,先考虑这个可能性,而不是反复调 CLAUDE.md

第二件:CLAUDE.md 的真正作用是纠偏,不是加信息。

Claude 从训练中形成了大量默认行为偏好------它倾向于加解释性注释、倾向于在回答里加免责声明、倾向于用它认为"规范"的代码风格。这些是训练出来的,不是你选的。

CLAUDE.md 的核心价值不是"告诉 Claude 你的项目背景",而是覆盖那些与你项目不符的默认行为

typescript 复制代码
❌ 背景类(低价值):本项目是一个电商平台,使用 TypeScript 开发...
✅ 纠偏类(高价值):
- 禁止在代码里写解释性注释,注释只用于说明"为什么"而非"是什么"
- 所有异常必须用 AppError 类,禁止 throw new Error()
- 函数签名中的类型标注必须完整,禁止使用 any

背景信息写进去 Claude 会"知道",但不会改变它的行为。纠偏指令写进去 Claude 会"执行",每轮都会执行。区别在于:前者是信息,后者是路由------把 Claude 的行为从它的默认轨道导向你项目的轨道。

深度阅读:第 04 篇:CLAUDE.md 完整指南


洞见二:CLAUDE.md 的长度问题,有两个力在同时作用

关于 CLAUDE.md 的长度,你会看到两种截然相反的说法,都有道理,都不完整。

第一个力:Prompt Cache 让长 CLAUDE.md 变便宜。

源码里有 Prompt Cache 的实现:System Prompt 和 CLAUDE.md 的内容会在第一次请求后被 API 层缓存。同一个会话的后续所有对话,这部分直接命中缓存,几乎不产生 token 费用。

sql 复制代码
第一轮:System Prompt(含 CLAUDE.md 3000 tokens)+ 你的输入 → 完整计费
第二轮:System Prompt(缓存命中) + 你的输入 → 只计你输入的部分
第三轮起:同上

你在 CLAUDE.md 里写的 3000 个 token,在一个 20 轮的对话里只付一次钱。而每次在 Prompt 里重复说背景,每轮都付费。从成本角度:把长期稳定的背景信息和规则放 CLAUDE.md 是对的,不用怕长。

第二个力:上下文越长,注意力越稀释。

语言模型对上下文的注意力不是均匀分布的。研究和大量实践都表明:开头和结尾的内容被记住的概率更高,中间的内容容易被"稀释"------这个现象有个名字,叫 lost in the middle。

CLAUDE.md 越长、越杂,中间那些规则被忽略的概率越高。这也是为什么很多人抱怨"我明明写了,Claude 还是不遵守"------规则在那里,但 Claude 的注意力没落在上面。

两个力同时作用,结论不是"越长越好",也不是"越短越好",而是:

长度不是问题,结构和密度才是。具体来说:

  • 最关键的规则放在文件顶部。注意力在开头最集中,不要把"禁止修改 /lib/auth.ts"埋在第 50 行后面。
  • 用清晰的分节标题。Claude 理解文档结构,标题帮助它在执行不同类型任务时定位相关规则,类似路由。
  • 只放能"纠偏"行为的指令,不放纯背景信息。背景信息("本项目是电商平台")即使被稀释了影响也不大;行为规则("禁止用 any")被稀释就意味着规则失效。
  • 定期修剪。随着项目演进,有些规则已经内化为团队习惯,不再需要 CLAUDE.md 来强制------把它们删掉,让剩下的规则注意力更集中。

一份 500 字、结构清晰、全是纠偏指令的 CLAUDE.md,效果远好于一份 2000 字、掺杂背景说明和模糊描述的版本。

深度阅读:第 11 篇:成本控制与效果衡量


洞见三:Claude 改了不该动的文件------你有三道防线,但不是一道

这是最常见的抱怨之一:"我说了不要动这个文件,它还是动了。"

源码里权限系统由多层检查构成,理解这几层的性质,你才知道该用哪层来保护什么。

第一层:.claudeignore------物理屏蔽,最硬的约束

写进 .claudeignore 的路径,在权限系统里会被直接拒绝,这一步发生在 Claude 的判断之前。Claude 不会看到这些文件的内容,也无法对它们执行任何操作------不是"Claude 被告知不要动",而是"Claude 拿不到操作这个文件的权限"。就像文件系统的 chmod,不依赖 Claude 的理解和自律。

第二层:CLAUDE.md------软约束,依赖 Claude 的理解

CLAUDE.md 里写的规则是自然语言指令,Claude 会理解并尽量遵守,但它本质上是在请求 Claude 的配合,而不是技术封锁。当 Claude 判断某个操作"对完成任务是必要的",它可能会权衡利弊后仍然执行。"不要修改 /lib/auth.ts"写在 CLAUDE.md 里,是在说"尽量别动",不是"物理上不能动"。

第三层:Hooks(PreToolUse)------可编程的拦截逻辑

Hooks 介于前两层之间,它能做 .claudeignore 做不到的事:在工具执行前运行你写的脚本,根据条件决定是否拦截。

bash 复制代码
# .claude/hooks/pre-tool-use.sh 示例
# 检查是否要修改 migrations 目录
if [[ "$TOOL_NAME" == "Edit" && "$FILE_PATH" == *"/migrations/"* ]]; then
  echo "migrations 目录已锁定,请手动执行数据库变更" >&2
  exit 1
fi

Hooks 是硬约束,但比 .claudeignore 灵活------它能执行逻辑判断,能给出有意义的报错信息,能区分"什么情况下可以,什么情况下不行"。.claudeignore 只是路径规则,不带逻辑。

三层防线的搭配原则:

绝对不能动的文件(密钥、支付模块、auth)→ .claudeignore,不商量。 需要有条件保护的逻辑(只有通过测试才能动某目录)→ PreToolUse Hook。 风格和约定类规则(不要用 any、注释规范)→ CLAUDE.md

很多人只用 CLAUDE.md 保护所有东西,然后发现 Claude 偶尔还是越界------因为他们用了一把"软刀"去做"硬刀"该做的事。

深度阅读:第 13 篇:企业落地方案


洞见四:/compact 之后什么被保留了,什么消失了

源码里上下文压缩有三种触发方式:

  • 接近上限时自动触发:Claude Code 在后台静默生成摘要,任务继续
  • 超过上限时阻塞触发:强制等待压缩完成
  • 你主动 /compact:手动触发,可以指定保留内容

自动压缩和手动压缩的区别,不只是"能不能指定保留内容",还有一个更根本的差异:

压缩的是对话历史,不是系统提示词和 CLAUDE.md

每次对话(无论是否压缩),CLAUDE.md 的内容都会被系统重新注入。所以压缩之后,你在 CLAUDE.md 里写的规则一条都不会丢,下轮继续生效。

会丢的是:对话过程中口头达成的共识

"这个接口我们先用 mock 数据,等后端好了再接真实 API"------这是你在第 3 轮说的。"auth 模块的重构我们先跳过,下个 sprint 再做"------这是第 18 轮决定的。这些对话里的决策,不在 CLAUDE.md 里,压缩时很可能消失,或者被模糊化。

自动压缩生成的是通用摘要,它不区分"已确认的决策"和"讨论过的想法",对两者的处理力度一样。手动 /compact 的真正价值不只是提高摘要质量,而是强迫你主动声明哪些东西必须保留:

bash 复制代码
/compact 保留:1)认证用 JWT,refresh token 存 Redis,已完成实现;
              2)payment 模块跳过本次重构;
              3)接下来要做 order 服务的三个接口

这样写,摘要就围绕这些锚点生成,后续对话里 Claude 对这些决策的记忆是清晰的。

什么时候主动 /compact: 不是看"轮数",而是看"阶段"。一个自然的压缩时机是完成了一个明确的子任务之后(比如某个模块的重构完成),在开始下个子任务前 compact 一次,声明上个阶段的结论。这比被动等到系统强制触发要好得多。

深度阅读:第 11 篇:成本控制与效果衡量


洞见五:Agent Loop 是 while(true)------丢失目标比超时更常见

Claude Code 的核心执行模式是一个循环:

arduino 复制代码
while (true) {
    发送消息给 Claude
    获取响应
    如果是工具调用 → 执行工具 → 把结果加入消息 → 继续循环
    如果是纯文本 → 输出给用户 → 退出
    如果超过 maxTurns → 强制退出
}

大多数介绍到这里就停了,说"给 Claude 清晰的完成条件就行"。这没错,但不够------因为 Agent Loop 里真正常见的问题不是"没有终止条件",而是Claude 在循环过程中逐渐丢失了原始目标

这是怎么发生的:每次工具调用的结果都会被加入消息历史,循环越长,消息历史越多,原始任务描述被稀释得越严重。第 1 轮 Claude 知道自己在做什么,第 20 轮之后,它可能已经开始处理一个工具调用的边缘情况,完全忘了这个工具调用是为了什么而触发的。

两个对策,搭配使用:

对策一,任务里写明完成条件:

arduino 复制代码
❌ 帮我重构用户模块
✅ 重构用户模块:1)所有现有测试通过,2)新增边界情况测试,
   3)完成后输出 "DONE: 所有测试通过" 作为结束信号

完成条件让 Claude 知道什么时候可以停,不会无限"再优化一下"。

对策二,拆分任务粒度,加中间检查点:

长任务更容易丢失目标。把一个大任务拆成几个阶段,每个阶段结束时显式确认,比一口气扔给 Claude 一个跨越十个文件的重构任务要稳得多。这不是不信任 Claude,这是在设计一个不容易偏航的协作流程。

你可以在 CLAUDE.md 里写入一条元规则:

复制代码
在执行多步骤任务时,每完成一个阶段,输出一行阶段摘要再继续下一步。

这不会让 Claude 更慢,但会让它在循环中持续意识到自己的位置。

深度阅读:第 06 篇:Sub-agents 实战 · 第 07 篇:Multi-agent 与 AgentTeams


洞见六:子 Agent 的工具权限是物理隔离的------你能控制的部分在 SKILL.md

当你用 Claude Code 的 Task 工具启动一个子 Agent 时,子 Agent 的工具列表是在调用时注入的,而不是继承自父 Agent。父 Agent 有的工具,子 Agent 不一定有。

这个设计的含义:子 Agent 的权限边界是物理隔离 ,不是靠提示词告诉它"你不应该写文件"------它的工具注册表里根本没有 WriteEditBash,它在技术上拿不到这些工具,就算它"想"操作也无从下手。

这不是 Claude 的自律,这是系统设计。

你能直接控制的部分在 SKILL.mdallowed-tools 字段:

yaml 复制代码
---
name: code-review
description: 审查 PR 中的代码变更
allowed-tools:
  - Read
  - Grep
  - Glob
  - Bash(git diff *)   # 只允许读差异,不允许其他 Bash 命令
---

这里写了什么,这个 Skill 启动时注册的工具就是什么。没有写 WriteEdit,Code Review 的 Agent 就物理上不能修改任何文件,无论任务里怎么描述。

Bash(git diff *) 这种语法尤其值得注意------它不是允许全部 Bash 命令,只允许匹配 git diff * 模式的命令。这是非常精细的权限控制,把一个高风险工具(Bash)限制到只能执行特定操作。

设计原则:给每个 Agent 只开它真正需要的工具。 做代码分析不需要写权限,做文档生成不需要跑测试,做安全审查不需要修改文件。最小权限不是谨慎,是架构设计------Agent 出了问题,破坏半径被硬约束在最小范围。

深度阅读:第 06 篇:Sub-agents 实战


洞见七:Claude 会自动提取记忆------但你需要知道它记在哪、记了什么

每一轮对话结束后,Claude Code 会在后台启动一个独立的子 Agent,分析这轮对话里有没有值得记住的东西------用户的偏好、项目里确认的约定、重复出现的模式。这个子 Agent 的权限极其有限,只能读和往特定的 Memory 目录写,提取到的信息按主题归类,写入项目的 Memory 文件,下次启动时自动注入。

这个机制是真实的,但有几个地方需要讲清楚,不然容易形成错误的依赖。

第一:你能看到它记了什么,也应该去看。

Memory 文件就在你的项目目录下(通常在 .claude/memory/CLAUDE.md 里的 # Memory 分节)。不是黑盒,打开就能读。你应该定期检查这些内容------自动提取并不总是准确的,有时候它会把一次讨论记成一个决策,把你否决的方案记成你确认的方案。发现记错了就直接修改或删掉,它只是个文本文件。

第二:明确说出来的约定比隐含在操作里的偏好更容易被正确提取。

"我们这个项目禁止用 moment.js,改用 date-fns"------这句话说出来了,被提取的概率很高。但如果你只是在某次代码里把 moment 换成了 date-fns,没有解释原因,自动提取可能记不到这个约定,或者记错意图。规则越明确,提取越准。

第三:自动记忆不能替代 CLAUDE.md,两者的作用不同。

自动记忆是 Claude 学到的,优先级低于 CLAUDE.mdCLAUDE.md 是你明确要求的,优先级高。如果两者冲突,CLAUDE.md 赢。

真正关键的、不容有偏差的规则(安全规范、架构约束、必须遵守的编码标准)只应该在 CLAUDE.md 里,不应该只依赖自动提取来维护。自动记忆适合记住"这个项目的用户倾向"、"上次讨论的技术选型"这类可以有偏差的信息,不适合记住"不得修改 payment 模块"这类零容忍的约束。

一个好的习惯: 在每次重要决策之后,主动说一句"记住这个约定:[内容]"。这不是在触发某个特殊命令,只是让提取 Agent 更容易识别这是一条值得记住的规则,而不是一次普通的讨论。

深度阅读:第 04 篇:CLAUDE.md 完整指南


结语:源码泄露,对用户反而是好事

工具的源码被看见了,你可以根据它真实的工作方式来使用它,而不是根据猜测。

这七个洞见的共同指向很清楚:Claude Code 的行为完全是工程决定的,不是玄学。 理解这些工程决策,你对 Claude 行为的可预测性就会大幅提升------不会再因为"CLAUDE.md 不管用"而困惑,不会再因为"Claude 改了不该动的文件"而被动,不会再因为"对话越来越糊涂"而重启。

每一个让你困惑的行为背后,都有一个工程原因。找到原因,就找到了对策。

这个系列剩下的文章都在这个方向上------不讲 Claude 是什么,讲它怎么工作,以及你怎么根据它的工作方式设计更好的 AI 协作流程。


系列文章索引

篇号 主题 相关洞见
02 篇 Prompt 工程实战 洞见一(纠偏而非叙述)
04 篇 CLAUDE.md 完整指南 洞见一、洞见二、洞见七
05 篇 Skill 提炼 洞见六(allowed-tools 物理隔离)
06 篇 Sub-agents 实战 洞见五、洞见六
07 篇 Multi-agent 编排 洞见五
09b 篇 Hooks 工作流自动化 洞见三(PreToolUse 可编程拦截)
11 篇 成本控制与效果衡量 洞见二、洞见四
13 篇 企业落地方案 洞见三(三层防线搭配)

AI Coding 系列持续更新。源码能看见的,行为就能预测。

相关推荐
jeCA EURG2 小时前
Spring Boot 2.7.x 至 2.7.18 及更旧的版本,漏洞说明
java·spring boot·后端
FastBean3 小时前
BizAssert:一个轻量级、生产就绪的 Java 业务断言工具类
java·后端
子昕3 小时前
Claude Code 源码意外泄露,我连夜拆了个底朝天:29 个子系统、6 层压缩、100+ 隐藏命令
ai编程
疯狂的程序猴3 小时前
iOS 多技术栈混淆实现,跨平台 App 混淆拆解与组合
后端·ios
莫物3 小时前
vue过滤表格数据导致的索引错乱问题
前端·javascript·vue.js
带刺的坐椅3 小时前
SolonCode CLI v2026.4.5 发布(编码智能体)
ai·llm·ai编程·cli·claudecode·opencode·sloncode
竹林8183 小时前
从监听失败到实时更新:我在NFT铸造项目中搞定合约事件监听的全过程
前端·javascript
光影少年3 小时前
手写防抖和节流
前端·javascript·前端框架
是Smoky呢3 小时前
springAI+向量数据库+RAG入门案例
java·开发语言·ai编程