核心机制

上下文系统解析:Git、CLAUDE.md 与系统提示词注入

Claude Code 为什么看起来“懂项目”

Claude Code 给人的一个强烈印象是:
它不像在面对一段孤立代码,而像是在理解整个项目。

这背后最关键的原因之一,就是上下文系统。

context.ts 让系统在对话开始前,就准备好一些高价值项目信息,再注入到后续主循环里。

加载图表中...

getSystemContext():补充系统级背景

从源码看,getSystemContext() 至少会处理一类非常重要的信息:Git 状态

它会尝试收集:

  • 当前分支
  • 主分支
  • 工作区状态
  • 最近提交
  • Git 用户信息

这带来的好处非常直接:

  • 模型知道当前仓库是不是脏的
  • 模型知道你现在在哪条分支上
  • 模型知道近期代码变化的大致方向

这对工程任务判断非常重要。

对应源码片段

const [branch, mainBranch, status, log, userName] = await Promise.all([
  getBranch(),
  getDefaultBranch(),
  execFileNoThrow(gitExe(), ['--no-optional-locks', 'status', '--short'], {
    preserveOutputOnError: false,
  }).then(({ stdout }) => stdout.trim()),
  execFileNoThrow(
    gitExe(),
    ['--no-optional-locks', 'log', '--oneline', '-n', '5'],
    {
      preserveOutputOnError: false,
    },
  ).then(({ stdout }) => stdout.trim()),
  execFileNoThrow(gitExe(), ['config', 'user.name'], {
    preserveOutputOnError: false,
  }).then(({ stdout }) => stdout.trim()),
])

这里一眼就能看出,Claude Code 不是随便问一句“你当前分支是什么”。
它会主动并行收集:

  • 当前分支
  • 主分支
  • 工作区状态
  • 最近提交
  • Git 用户名

这就是工程上下文。

从源码意图上看,它在做“上下文压缩”

原始工程信息是极其杂乱的:

  • 仓库里有很多文件
  • Git 状态可能很长
  • 记忆文件可能很多

context.ts 的工作不是把一切原样塞给模型,而是提炼出最值得注入的高信号信息。

getUserContext():补充用户与项目约束

另一个重点是 getUserContext()
这里会处理 CLAUDE.md 等记忆文件,并注入当前日期。

CLAUDE.md 的意义非常大,你可以把它理解为项目级工作说明书,例如:

  • 编码规范
  • 仓库结构约束
  • 特殊命令
  • 团队约定
  • 某些需要避免的操作

有了这层记忆,Claude Code 对同一个项目的行为就会更稳定。

对应源码片段

const shouldDisableClaudeMd =
  isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS) ||
  (isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)

const claudeMd = shouldDisableClaudeMd
  ? null
  : getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))

setCachedClaudeMdContent(claudeMd || null)

这段代码说明 CLAUDE.md 并不是“固定总会注入”的。
Claude Code 会根据模式和设置判断是否启用,然后再把结果缓存给后续系统使用。

加载图表中...

这不是单纯“拼 prompt”,而是上下文治理

表面上看,这像是在往系统提示词里加文本。
但从工程角度看,本质更接近 上下文治理

  • 什么信息值得长期注入
  • 什么信息要缓存
  • 什么信息在 bare mode 下跳过
  • 什么信息需要避免循环依赖
  • 什么信息应该控制长度

这些都不是“写个 prompt”那么简单,而是长期运行系统必须面对的问题。

对应源码片段

return {
  ...(gitStatus && { gitStatus }),
  ...(feature('BREAK_CACHE_COMMAND') && injection
    ? {
        cacheBreaker: `[CACHE_BREAKER: ${injection}]`,
      }
    : {}),
}

这段返回值看起来简单,但它很能说明 Claude Code 的思路:
上下文不是散落在系统里的临时字符串,而是被统一整理成结构化上下文对象,再交给后续主循环使用。

为什么 CLAUDE.md 这类文件特别关键

因为它把项目经验从“靠人临时口述”变成了“可注入的系统知识”。

这会直接影响三件事:

  • 输出风格更稳定
  • 修改更符合仓库约定
  • 不容易反复犯同样的错误

Git 状态为什么这么关键

很多人低估 Git 状态的价值。
实际上,对工程代理来说,Git 状态是判断风险和上下文边界的重要信号:

  • 工作区干净还是脏
  • 有没有未提交修改
  • 现在在功能分支还是主分支
  • 最近改动和当前任务有没有关系

Claude Code 把这些信息纳入系统上下文,意味着它不是把代码当静态文本看,而是把仓库当动态工程资产看。

这层设计带来的直接结果

有了上下文系统,Claude Code 在很多场景下会表现得更像“进入项目现场”:

  • 它更容易遵守仓库约定
  • 它更容易避免和当前工作区状态冲突
  • 它更容易理解为什么当前任务要这么做

这也是为什么同一个模型,放在普通聊天框里和放在 Claude Code 里,体验会差很多。

这层机制也有明显边界

上下文注入并不意味着 Claude Code 天然“全知”。
它仍然会受到以下限制:

  • 注入内容长度有限
  • Git 状态是快照,不会自动实时刷新
  • 记忆文件质量决定了部分行为上限

所以“上下文系统”是增强器,不是万能钥匙。

小结

context.ts 告诉我们一件很重要的事:

AI 编程工具之所以强,往往不是因为模型凭空更聪明,而是因为系统在模型开口前,就已经把“该知道的项目背景”准备好了。

Claude Code 的“懂项目”,很大一部分就来自这里。