news 2026/10/2 7:55:58

Claude Code源码真相:终端AI代理的四层内核与可观察性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Claude Code源码真相:终端AI代理的四层内核与可观察性

简介:Claude Code 源码是一份面向 AI 编程工具研究者、前端/Node.js 开发者及开源爱好者的完整代码仓库,适合用于学习现代 CLI 应用架构、模块化拆分与类型安全实践。资源共 1903 个文件,以 1332 个 TypeScript 文件与 552 个 TSX 组件文件为主,辅以少量 JS 工具脚本和 1 份 Markdown 说明文档,压缩包约 9.43MB,目录结构清晰,便于按功能模块检索阅读。目前已有 286 人浏览学习。源码不仅覆盖核心算法与数据处理逻辑,还包含详尽注释、测试用例、开发者指南、API 文档和用户手册,能够帮助读者理解大型开源项目如何通过 Git 管理版本、组织模块和保障代码质量。对于希望提升工程能力、研究 Agent 类工具实现原理或参与社区贡献的开发者而言,这份源码提供了难得的参考资料。

1. 找不到源码的“Claude Code源码”:终端AI代理值得啃的其实是四层内核

把“Claude Code源码”当成一个能直接下载的仓库去找,多半会扑空。Claude Code 是 Anthropic 出品的终端 AI 编码代理,核心并不开源,网上流传的各种“源码”也基本都是包装脚本或者二次封装。可我更愿意把“源码”理解成另一件事:这个工具靠什么逻辑跑起来、改了哪里能改变它的行为、出了问题从哪一层开始查。实际操作下来,它的可观察内核一共四层:磁盘上的配置目录、工具调用循环、钩子与 MCP 扩展点、以及会话日志。这四层不需要你读过一行闭源代码,也能照样定位问题、加能力,甚至照着复刻一个最小可用版本。适合谁读呢:想基于 Claude Code 做二次开发的人,和那些在“源码”里翻半天却连配置目录都找不到的新手。

2. 先拆磁盘:~/.claude 与项目 .claude 目录就是离你最近的“源码”

所有闭源终端工具最诚实的那份“源码”,就是它写在磁盘上的配置与状态。Claude Code 把全局状态放在~/.claude,项目级设置放在.claude/,这两片目录看明白了,剩下的运行逻辑全是顺着它们展开的。

2.1 全局层:~/.claude 里值得先读的六个文件

完成 Claude Code 安装、跑过一次会话之后,主目录下会出现~/.claude。别急着删,这一目录就是终端工具“源码级”的现场。真正值得读的六个东西我按排查频率排个序:

  • ~/.claude.json:账号级状态文件,注意它是个文件,不在~/.claude目录里面。它记录已登录账号、projects 列表、每个项目的 git 仓库路径与运行次数。我一般说它是“户口本”,后面排查权限和上下文污染都用得上。
  • ~/.claude/settings.json:全局配置,权限、环境变量、钩子都允许在这里出现。它的典型长这样:
{ "env": { "CLAUDE_CODE_MAX_OUTPUT": "12000" }, "permissions": { "allow": ["Bash(npm test:*)", "Read(.*)", "Edit(.*)"], "deny": ["Bash(rm -rf:*)", "Write(.*/secrets.*)"] } }

逻辑说明:env里的变量会当启动参数注入子进程;permissions.allow是白名单,deny是黑名单。我习惯把“防止误删”这类规则写进全局deny,因为它真的救过我一次,细节在第 5 章展开。参数说明:allow里每一项的语法是工具名(正则模式),比如Bash(npm test:*)只在命令以npm test:开头时免确认。这种精确授权法比直接写Bash(.*)稳得多,权限检查是顺序匹配,先看 deny 再看 allow,通配符越多越容易给出意想不到的放行。

  • ~/.claude/logs/:会话日志目录,文件以SL_开头,例如SL_<时间戳>.jsonl。这是我觉得最接近“源码运行时”的东西,每一轮工具调用、HTTP 请求、token 统计都在里面。
  • ~/.claude/hooks/:全局钩子的落点。
  • ~/.claude/commands/:全局个性化指令,也就是/命令名。
  • ~/.claude/CLAUDE.md:全局记忆文件,每次会话都会注入,适合写跨项目的通用约束。

这里有个从源码视角特别容易误会的点:~/.claude.json和~/.claude/是两回事,前者是文件、后者是目录,备份和导出时要分开处理。我见过同事把.claude.json当成目录删了,登录态全丢,全部项目历史记录一起没。

提示:~/.claude.json里可能包含与账号相关的敏感信息,提交到 git 仓库前务必确认已经被.gitignore排除。

2.2 项目层:.claude/ 与 CLAUDE.md 的加载顺序

进到项目根目录,Claude Code 会把.claude/当成项目级“源码目录”。常见的结构是这样:

.claude/ ├── settings.json # 项目级权限与 env ├── settings.local.json # 个人本地覆盖,不进 git ├── .mcp.json # MCP 服务器声明 ├── commands/ │ └── review.md # 自定义指令 /review ├── hooks/ │ └── post_tool_use.sh # 工具后置钩子 └── CLAUDE.md # 项目说明文档

settings.local.json是最容易被忽略的一个点:它按机器生效,冲突时覆盖settings.json。我会把本地代理、私有 token 放这里,这样整个仓库继续提交给别人也不会泄露个人信息。它和settings.json的关系,类似 git 里config和config.local的分工。

CLAUDE.md的加载顺序是团队协作里反复出问题的点。实际顺序是:全局~/.claude/CLAUDE.md→ 仓库根目录CLAUDE.md→ 当前子目录的CLAUDE.md。也就是说,在子目录执行claude时,它会同时读多层。用表格看一眼就明白:

加载顺序文件位置作用域典型用途
先读~/.claude/CLAUDE.md全局个人代码风格、跨项目禁用项
次读仓库根CLAUDE.md项目架构约定、构建命令、行为红线
后读当前目录CLAUDE.md会话当前模块的局部说明

一个关键理解:越后读的越靠近最终提示词,但这不代表“后读优先”。相反,经验上后加载的内容更容易把前两层覆盖掉。如果你发现模型突然像变了一个人,多半是根目录那份和子目录那份打架了,改其中任意一份前先确认是谁在生效。

2.3 会话层:/status 暴露的运行时状态

在会话里敲/status,可以得到当前会话快照:项目路径、模型、权限模式、当前目录,以及最近一次工具调用结果。这是我理解“源码可观察性”的第一现场。新手排查问题第一反应是去翻日志,我的习惯是先/status三秒钟,确认模型、路径和权限模式没有跑偏,再往下查。

/status里还有一个容易被忽略的点:它会显示当前会话的工作目录。很多人把 Claude Code 当成项目根目录专属工具,其实它在任意子目录都能启动,而“它以为自己身处哪里”直接决定了工具调用的相对路径。同一个命令在根目录和子目录启动,行为可能完全不同,这算是终端类 agent 特有的心智负担。

3. 运行时四件套:工具调用循环、上下文构建、日志落盘与进程权限

读出配置只是开始。Claude Code 的“源码级”行为,全在一个循环里:Claude 模型收到上下文 → 决定调用一个工具 → 工具执行并返回结果 → 结果附加进上下文 → 再来一轮。理解这个循环,就能解释 90% 的实际现象。

3.1 工具调用循环:六类内建工具与两次确认

Claude Code 内置工具大致分六类:读文件(Read)、编辑(Edit)、写文件(Write)、执行命令(Bash)、搜索代码与文件(Glob/Grep)、子任务(Task)。每次调用,如果权限没被白名单放过,终端会弹确认;半自动模式下你只要按回车就会通过,全自动模式下完全跳过。

用Bash举例子,常见的调用景观是:

# 让 Claude 运行测试并在失败时尽早停 pytest tests/ -x --tb=short --no-header

逻辑说明:Bash工具默认以你的系统 shell 执行,输出截断进入上下文,失败时退出码也是可见信息。我建议在所有常用命令前加上--no-header或--quiet类似的参数,因为它能把“成功但不重要”的输出压下去,减少上下文膨胀。参数说明:-x让 pytest 在第一个失败处停住,--tb=short压缩错误回溯为几行,模型更容易把失败原因读出来。这一步不是玄学,是给上下文省字。

观察日志你会发现一个高频行为模式:Claude 做完批量修改之后,紧接着会调Read或Glob去复核,然后才进入下一轮计划。Edit 后面跟 Read,这个“写后读”节奏是复现它方案时最值得模仿的一点,自己写 agent 时保留这个习惯,能少犯一半低级错误。

3.2 上下文构建单元:CLAUDE.md 与工具结果如何拼进提示词

每次请求真正发给模型的,不只是你刚输入的那句话。常见的上下文组装顺序是:

  1. 系统提示,含安全约束与工具定义
  2. ~/.claude/CLAUDE.md全局记忆
  3. 根目录和当前目录的CLAUDE.md
  4. 最近会话历史,带着工具调用结果
  5. 当前用户输入

当 token 逼近模型上限时,历史会被压缩:对最早几轮做摘要,较近的轮次保留原文。这个“压缩点”你看不到,但能感觉到——如果你发现 Claude 突然忘了三天前让它记住的某个路径,多半是历史被摘要吃掉,而不是它变笨。对应解法是把关键路径写回CLAUDE.md,因为文件内容在每次请求都会重新读取,比历史可靠得多。

这里有个值得专门强调的边界:压缩后的摘要是不透明的。你无法从日志直接还原“模型到底忘掉了哪句”。所以我的原则是:凡是“必须记住”的内容,不依赖历史,全放进CLAUDE.md;凡是“想起来才用”的内容,放进按需指令或者技能里。

3.3 日志落盘:SL_ 文件里能挖出什么

会话日志~/.claude/logs/SL_<时间戳>.jsonl是逐行的 JSON。大体是一个事件一行,常见能看到工具调用类型、请求方向、时间戳。我排查问题时最常用的三件事:

  • 搜工具调用行,看某个工具是否真的被调用,以及调用顺序。
  • 搜结果行,看工具输出是否异常截断。
  • 搜 token 相关字段,看哪一轮把上下文吃爆了。
# 在最近一个会话日志里统计工具调用频率 cd ~/.claude/logs && ls -t SL_*.jsonl | head -1 | xargs grep -o '"name":"[A-Za-z_]*"' | sort | uniq -c | sort -rn

逻辑说明:ls -t取最近修改的日志文件,grep -o提取工具名再统计频率,能快速看出当前会话里哪个工具是被反复用的,从而判断是不是陷入了某种死循环。参数说明:head -1取最新一份就够了;如果要跨会话统计,把head -1去掉,直接cat SL_*.jsonl再管道,代价是数据量大会拖慢统计速度。

日志还藏着权限相关的行为:本地权限规则是“先匹配 deny,再匹配 allow,都没有则弹窗”。我把这个顺序背下来以后,很多“为什么偏偏它要问”的问题就迎刃而解。如果是 deny 命中,它不弹窗而是直接拒绝;如果是 allow 命中,它不弹窗直接执行。只有两边都没命中才出现那个你熟悉的确认框。

3.4 进程模型:为什么编辑配置不热加载

Claude Code 的每次会话是一个常驻进程,配置只在启动时读一次。你在另一个终端改了settings.json,正在跑的会话不会热加载。这一条看着简单,实际踩坑率极高。很多人开着一个长会话,外面改了 MCP 配置,回来敲命令测试,发现新工具没出现,第一反应是“坏了”,第二反应是去翻网络配置,折腾一圈才发现是进程根本没重新读文件。

我在实际工作里的判断顺序是:先敲/status看当前会话状态,再敲对应的管理命令(比如 MCP 相关就敲/mcp),最后才决定要不要重启会话。盲重启十有八九会发现“状态根本没问题,是配置本身没写对”。

4. 扩展层就是可变源码:hooks、个性化命令与技能、MCP 接入

闭源软件找“源码”的意义,一半是为了改行为。Claude Code 给普通用户留的可变面是 hooks、个性化命令和 MCP——不动主程序,也能在工具调用前后插自己的判断。

4.1 hooks:在工具调用前后插一手的标准写法

hooks 配置在settings.json的hooks字段里,事件名我常用的有:

  • UserPromptSubmit:用户输入后、发给模型前触发
  • PreToolUse:工具执行前触发
  • PostToolUse:工具执行后触发
  • Stop:Claude 完成一轮回复后触发
  • Notification:通知回调
  • PreCompact:历史压缩前触发

一个典型用法:在PreToolUse里拦截Bash,把危险路径挡在外面。

{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "test \"$TOOL_INPUT\" = \"rm -rf /\" && exit 2 || exit 0" } ] } ] } }

逻辑说明:PreToolUse的 hook 退出码非 0 表示拒绝该调用,0 表示放行。上面这个例子虽然粗暴,但能挡住最危险的那条命令。参数说明:matcher可以指定工具名,事件触发时TOOL_INPUT、TOOL_NAME、HOOK_EVENT_NAME这些环境变量会被自动填入,你的脚本可以读取它们做判断;hook 自身的输出会作为一条观察结果回到上下文,所以不用怕“写不出后台日志”。

在一个安全要求高的环境里,我会把所有Write(.*\.env)都通过 hook 拒绝,同时输出提示“请在 .env.example 里写模板”。这个逻辑放在代码层去做会漏,放在工具调用层做反而漏不了,因为它是绕不过去的必经之路。

4.2 个性化命令与技能:/命令名 的落点

在.claude/commands/下放一个.md文件,就能获得一个斜杠命令。文件名去掉.md就是命令名,文件内容的第一段描述会显示在命令面板,剩下的全是给 Claude 的指令。

--- description: 在提交前做一次上下文自检 --- 检查当前工作区未提交改动里是否存在调试代码(console.log / print / TODO); 如果有,先列出文件清单,逐个询问我是否删除; 全部处理完再输出一份改动摘要。

逻辑说明:自定义命令本质上是一段可复用提示词,适合把高频动作固化成团队约定。参数说明:文件头的description会在命令面板里优先显示,建议写成“动词 + 对象”的结构,比如上面的“在提交前做一次上下文自检”,比单写“自检命令”好用得多。

技能(Skill)这类更高级的自动化,起手式也类似:先拆成一段可复用的指令文件,再逐步给配套脚本和资源目录。我见过不少人一上来就想做“全自动技能”,结果第一版就把所有逻辑塞进一条命令里,跑不通也难调试。更稳的做法是先把单文件命令跑顺,再从命令升级成技能。

4.3 MCP:把外部工具挂进工具调用循环

MCP 接入的本质,是让外部服务暴露成新的工具名。项目级配置写在.claude/.mcp.json,用claude mcp add命令也能登记。

{ "mcpServers": { "internal-docs": { "command": "npx", "args": ["-y", "@your-org/doc-mcp"], "env": { "DOC_BASE_URL": "https://docs.example.internal" } } } }

一个关键认知:.mcp.json第一次导入后,工具定义会固化到会话,之后改文件不一定能热生效。常见现象是“我改了配置但新工具没出现”。我顺手排 MCP 故障的顺序是:先claude mcp list看注册状态,再打开.mcp.json检查命令路径,最后翻日志看握手是否成功。把这个顺序反过来的人,往往先怀疑网络,最后才发现是npx版本路径不对。

提示:MCP 服务器的日志通常不和主会话日志混在一起。先确认进程真的被拉起来了,再去查网络和鉴权,能省掉 80% 的排查时间。

5. 源码向使用中的五个避坑现场:现象、原因、解决

基于上面的机制,我把常见问题压成五条。每一条都是我在真实跑动中踩过或者看别人踩过的坑,按“现象 → 原因 → 解决”写。

5.1 权限拒绝:命令能在我终端跑,Claude 却说没有权限

现象:让 Claude 执行npm run build,它反复说需要授权,甚至出现权限相关的报错。原因:settings.json里 deny 规则写得太宽,或某一项规则的正则语法写错,权限检查直接抛异常而不是平滑降级。解决:先敲/status确认当前权限模式,再打开settings.json检查正则。我的原则是 deny 里只写确定不干的,allow 里让 Claude 精确到命令前缀,比如Bash(npm run build:*),而不是Bash(.*)。通配符太多,权限检查看起来优雅,实际上把安全兜底也删了。

5.2 钩子输出污染上下文:一条 stdout 让模型前后判若两人

现象:加了PostToolUse钩子之后,Claude 开始重复“我已经处理过”这种话,或者在某次大改后突然忘了之前的约定。原因:钩子的 stdout 会被当成工具结果注入上下文;如果钩子里有echo "ok"这类无意义输出,模型会把“ok”理解成一次成功信号,并在后续推理中引用它。解决:钩子的命令遵循“安静”原则——成功时输出为空,失败时只输出一行错误。调试钩子期间,我故意让命令打印当前事件名定位问题,定位完立刻删掉输出。

5.3 中途改配置不生效:同一个会话里摸不到新能力

现象:开了新 MCP 服务或者改了 hooks,当前会话里测试新命令永远失败。原因:Claude Code 进程启动时就把配置持久化在状态里,运行中不会重新读文件。解决:涉及 MCP、hooks、permissions 的修改,直接退出会话重进。我前面说过,重进前先确认配置本身没有写错,这个顺序比啥都重要。

5.4 CLAUDE.md 越长,约束越弱

现象:CLAUDE.md写到 300 行以后,模型开始频繁遗漏关键约束,甚至在犯错之后自我辩解。原因:长指令在信息密度上不如短指令,而且历史压缩发生时,靠后的约束最容易被摘要掉。解决:把CLAUDE.md拆成两层,根目录那份只留“行为红线”和“项目架构一句话描述”,具体的命名规范、目录说明挪进.claude/commands/里按需调用。本质上是把指令从常驻内存移到按需加载,压缩损失会小很多,这个做法我下一章给出完整方案。

5.5 会话日志里找不到想看的东西

现象:想定位一次模型回答为什么崩,翻SL_*.jsonl却没找到完整的请求负载。原因:日志默认是事件流,不是 HTTP 抓包,它记录“发生了什么”,不记录“完整请求体长什么样”。解决:别在日志里硬翻,复现问题时先开调试模式,把复现步骤固定下来,再把现象、日志片段、配置一起存下来。我一般用一个最小仓库,三句话描述复现步骤,再附上引发问题的工具调用日志,比漫无目的地翻日志效率高一个量级。

6. 进阶:把三千行 CLAUDE.md 瘦身成可按需加载的“指令源码”

高约束项目的终局,是把记忆文件做成“索引 + 按需加载”。做法是:根目录CLAUDE.md只保留四段——项目一句话、行为红线、目录索引、以及“遇到 X 请先读.claude/specs/下的 Y.md”。

# 项目 CLI 一句话:这是一个处理订单导出的 Node CLI,只允许操作 orders 表。 行为红线: - 不允许删除任何记录,只允许打 soft-delete 标记。 - 不允许直接改 prod 库,只允许通过迁移脚本。 按需加载: - 涉及订单状态机 → 先读 .claude/specs/order-state.md - 涉及导出格式 → 先读 .claude/specs/export-format.md 目录索引: - 业务代码在 src/modules - 测试在 tests/e2e

逻辑说明:把原来的 3000 行拆成specs/下若干独立文件,CLAUDE.md退化成一张索引表。模型平时只扛着索引跑,真正遇到订单状态时才会去读order-state.md。这比把 3000 行全部塞进每次请求省下的 token 不是一点半点,而且压缩发生时索引本身的稳定性远高于长文。

配合 hooks,可以做成更自动的按需加载:在UserPromptSubmit事件里 grep 用户输入,如果命中“订单”“状态机”这类关键词,就把order-state.md的内容追加到本次上下文,再交回模型。这段逻辑用不到 20 行 bash 就能写完,但它把“CLAUDE.md 长了就不遵守”的毛病直接改成了“不长就不会不遵守”。

这套做法的边界我也说清楚:它依赖目录规整到“索引一句话能找到”。如果仓库本身结构混乱,索引写不好,反而会带偏模型。我会先用/status和日志确认模型当前是否真的在遵守指令,再做这个改造,别一上来就拆。

收个尾:我最常用的排错动作,永远是先/status、再翻SL_日志,最后才看代码——这个顺序帮我省下的时间,比任何一条配置技巧都多。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 7:53:57

Hoppscotch 完整自托管指南:1 条命令跑通开源 API 调试工具

Hoppscotch 完整自托管指南&#xff1a;1 条命令跑通开源 API 调试工具 【免费下载链接】hoppscotch Open-Source API Development Ecosystem • https://hoppscotch.io • Offline, On-Prem & Cloud • Web, Desktop & CLI • Open-Source Alternative to Postman, In…

作者头像 李华
网站建设 2026/10/2 7:52:48

RK3588零拷贝视频流水线实践:Mpp硬解+GStreamer+DRM显示

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 7:52:41

Python抖音数据抓取实战:Web端接口解析与无水印下载

做内容分析和账号运营的朋友&#xff0c;多少都遇到过这样的需求&#xff1a;想批量看一个对标账号最近发了什么视频&#xff0c;想统计竞品账号的点赞评论趋势&#xff0c;或者纯粹想把自己账号的历史作品备份下来。这种时候&#xff0c;“抖音视频数据抓取”就成了绕不开的话…

作者头像 李华
网站建设 2026/10/2 7:51:55

绝缘体上硅SOI技术详解:从结构原理到射频与低功耗应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 7:51:35

Orcad Allegro补丁本质是Windows系统兼容性工程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 7:51:33

机器学习赋能雷达辐射源识别:从传统分类器到集成学习全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华