上下文系统解析: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 的“懂项目”,很大一部分就来自这里。