上下文工程(12):Claude Code 的实践

QuibblerAgentQuibblerAgent 2026-10-28 约 17 分钟 5 次阅读

让 Agent 修一个函数,它能径直扎进你五年没敢动的祖传模块;让它排查一个偶发 bug,它 grep 两下就锁定病灶。这类体验的背后是同一个问题:**百万行代码库里,这一轮该让模型看到哪几百行?**代码是上下文工程最苛刻的场景——内容量级远超窗口,相关性判断千头万绪,还多出一个自然语料没有的变量:代码有语法结构。

这是《上下文工程》系列第 12 篇。预算(第 4 篇)、检索(第 10 篇)、隔离(第 9 篇)这些原则在代码场景全部生效,而 Claude Code 把它们捏合成了一件开发者每天在用的工具。本文先算难题的账,再拆开 Claude Code 的策略地图,然后是检索式供给的 token 数学、代码地图模式、增量上下文维护、与 IDE 补全的对比,最后给一套可直接落地的实战流程。


1、代码上下文的独特难题

代码上下文难在三件事叠在一起。

**量级。**一行代码平均折合 8~12 个 token,200k token 的窗口折算下来约 2 万行。一个 30 万行的中型项目要 15 个满窗口才装得下,近 3000 万行的 Linux 内核需要 1400 个。结论只有一个:任何"把代码库装进上下文"的念头都不成立,能做的只有让模型按需看到正确的局部。

**相关性。**哪些文件与"修复登录偶发超时"相关?直觉答案是文件名里带 login 的——然后你会发现 handler.go 这类名字在仓库里有几十个。Sourcegraph 的上下文工程指南对代码场景下朴素 RAG(Retrieval-Augmented Generation,检索增强生成)的判断很直接:切块会把调用点和定义切开,常见文件名制造噪声,向量相似度分不清一个函数的定义和它的测试 mock——三刀都砍在要害上。

**结构。**代码与自然语言最大的不同:它有确定性的语法结构。抽象语法树(AST)里定义在哪、谁引用了它、调用链怎么走,这些不是"语义相近"的问题,是编译器能精确回答的问题。Sourcegraph 给出的组合是:有结构的地方用编译器背书的符号导航(跳定义、查引用),没结构的地方(注释、文档、commit message)才交给语义检索。他们的 CodeScaleBench 基准(370 个任务、40 多个仓库、9 种语言)还给出一个规模阈值:只靠本地 grep/glob/read 的 Agent 在仓库超过约 40 万行后开始力不从心,结构化工具的收益在那个区间才拉开。

2、Claude Code 的策略地图

把 Claude Code 每轮实际进入上下文的东西摊开,是五层结构:

任务:修复登录接口偶发超时
│
├─ 常驻层   每轮都在、字节稳定、整段吃缓存
│           system prompt + CLAUDE.md(命令、规范、踩坑记录)
│
├─ 按需层   用到才进窗口
│           Glob/Grep 先定位 → Read 局部读取(分页 + 行号)
│           @文件路径 手动引用 → 整文件一次性注入
│
├─ 治理层   进了窗口也要瘦身
│           Read 分页截断 · Bash 输出限长 · Edit 只回显变更块
│
├─ 隔离层   大探索不进主窗口
│           Task 派子代理独立探索,只回摘要
│
└─ 兜底层   窗口快满时
            /compact 手动压缩 + 自动压缩兜底

逐层看设计意图。

**常驻层小而精是刻意的。**CLAUDE.md 里每一行都是"税"——每一轮对话都为它付 token,还占注意力。官方记忆文档因此强调它放高频、稳定、全局的信息(构建命令、代码规范、已知坑),而不是架构文档全文。想临时注入某个完整文件,用 @文件路径 引用,这是使用者手里的手动按需阀门。

按需层的顺序有讲究:先定位后读取。Grep 默认回命中文件列表或行级片段,几百 token;Read 才把正文拉进窗口,还带分页与行号。连工具本身也是按需的——MCP(Model Context Protocol,模型上下文协议)服务器的完整工具定义与 skill 说明,都是用到时才加载。治理层与隔离层分别对应本系列第 8 篇(工具结果治理)与第 9 篇(子代理隔离),机制相同不再展开;/compact 就是第 5 篇的 compaction。官方把这套机制做成了交互式窗口模拟,能看到一次会话里各层怎么把窗口填满。

3、检索式供给的 token 账

用同一任务算两种供给方式的账。病灶在 RetryInterceptor.kt 的一个 40 行函数加 20 行配置,真正需要的上下文约 600 token。

方案 A,凭直觉塞文件:挑 6 个"感觉相关"的文件整个读入,每个 500 行,共 3000 行、约 3 万 token 进窗口,其中与病灶相关的不足 2%。

方案 B,检索式供给:Grep 关键词 timeout 回 3 个命中文件约 300 token;Read 其中 2 个文件的相关段落 120 行约 1200 token。合计约 1500 token,相关内容占比四成。

差距是 20 倍 token,但真正贵的不是钱。本系列第 2 篇讲过上下文腐烂:无关 token 稀释模型对关键信息的注意力,Sourcegraph 的结论更狠——无关上下文让输出变差而不是变慢。方案 A 那 98% 的无关代码,每一行都是错误关联的素材:模型可能把另一个文件里的降级逻辑错牵进这次修复。

检索式供给对代码格外有效,有个自然语言没有的原因:代码里的标识符是全局唯一锚点。函数名、类名、错误字符串、配置键——grep 一个精确标识符的命中率天然远高于 grep 一段自然语言描述。CodeScaleBench 里有个扎眼的数据:即便提供了语义检索工具,Agent 仍做了 4813 次关键词搜索、只有 587 次自然语言查询——模型自己也在用脚投票。

grep 的盲区是语义问题:"用户会话过期在哪处理"没有任何稳定标识符可 grep。这正是下一节代码地图要补的洞。

4、代码地图模式

思路:与其让模型每轮从零摸索仓库结构,不如预先给它一张地图——目录树加每个文件一句话职责,一两千 token,常驻或按需引用。模型的探索从"盲 glob"变成"按图索骥",第一跳就落对目录。

地图放哪要按仓库大小算账。一两百个文件的小仓库,地图直接进 CLAUDE.md 当常驻——它的定位收益大于 token 税;更大的仓库把地图存成独立文件,CLAUDE.md 只留一行指针,模型需要时再 @引用,税就变成了按需支出。

生成地图的骨架脚本只做机械工作:走目录树、过滤噪声目录、尝试取每个文件的首行注释。代码如下:

import os, re

SKIP = {".git", "node_modules", "build", "dist", "__pycache__"}
FIRST = re.compile(r'^\s*(?:#|//|/\*|<!--|\*)\s*(.{6,60})$')
CODE_EXT = (".ts", ".tsx", ".py", ".kt", ".go", ".rs", ".java")

def describe(path):
    """首行注释当一句话职责,取不到留空"""
    with open(path, encoding="utf-8", errors="ignore") as f:
        for line in f:
            m = FIRST.match(line)
            if m:
                return m.group(1).rstrip("*/ ")
            if line.strip():
                return ""               # 首个非注释行出现即放弃
    return ""

def skeleton(root, max_depth=3):          # max_depth 层目录树
    lines = []
    for dirpath, dirs, files in os.walk(root):
        dirs[:] = sorted(d for d in dirs if d not in SKIP)
        rel = os.path.relpath(dirpath, root)
        if rel.count(os.sep) >= max_depth:
            dirs[:] = []                # 深层目录不再下钻
            continue
        lines.append(f"{rel}/ ({len(files)})")
        lines += [f"  {os.path.join(rel, f)}  # {describe(os.path.join(dirpath, f))}"
                  for f in sorted(files) if f.endswith(CODE_EXT)]
    return "\n".join(lines)
print(skeleton("src"))

解读:骨架脚本的职责是穷举而不是理解——describe 只在文件恰好有首行注释时有产出,多数文件的职责栏是空的。这一步故意留给模型,下一节实战里补。

5、增量上下文维护

编辑会话还有第二重账:改了 10 个文件之后,模型上下文里应该是什么?

Claude Code 的答案是变更块而不是全文件。Edit 工具的返回只有替换成功与否的确认与变更处的短摘录,不把整个文件重发一遍;连续改同一个文件 5 次,窗口里是 5 个几十 token 的变更块,而不是 5 份几万 token 的完整快照。自己写编码 Agent 时,同一个模式可以用一个会话状态类实现:

class EditSession:
    """维护变更块增量视图,不重读全文件"""

    def __init__(self):
        self.blob = {}    # 文件 -> 模型已见过的版本
        self.patch = {}   # 文件 -> [(old, new), ...]

    def edit(self, path, old, new):
        assert old in self.blob[path]      # 锚点必须在已见版本中
        self.patch.setdefault(path, []).append((old, new))
        self.blob[path] = self.blob[path].replace(old, new, 1)

    def view(self, path):
        """回给模型的轻量指针:文件 + 变更数"""
        n = len(self.patch.get(path, []))
        return f"{path}(已变更 {n} 处,以变更块为准)"

三个要点。锚点校验保证编辑始终建立在模型真实见过的内容上,挡住幻觉漂移;replace(…, 1) 限定单点替换,同名代码块只改第一处;view 回的是指针而不是全文,模型需要时会自己重新 Read 对齐。进的少、留的准——这与本系列第 8 篇的工具结果治理是同一原则在编辑场景的落点。

6、与 IDE 补全的对比

Copilot 式补全与 Claude Code 式 Agent 是两种上下文哲学。

补全的上下文围绕光标:当前文件、打开的标签页、少量路径线索,策略是就近拼装——它假设你正在写的那一行与你正看的代码最相关。这个假设在写样板代码时成立,在跨文件改动时不成立:光标周围没有那个三天前刚改过签名的函数。

Agent 的上下文围绕任务:它不假设你在看什么,而是自己去查该看什么——grep 定位、read 拉取、必要时翻地图。代价是工具调用的多轮往返,换来跨文件一致性:改了函数签名,它能 grep 全部调用方逐一修掉。

维度 IDE 补全 编码 Agent
上下文中心 光标位置 任务目标
供给策略 就近拼装、被动跟随 主动检索、按需拉取
典型尺度 数百~数千行 数千~数万行跨文件
一致性保障 靠人复核 靠 grep、编译、测试闭环
失效模式 看不见调用方、风格漂移 检索漏文件、上下文过期
适合任务 样板代码、局部续写 跨文件重构、排查、脚手架

两种策略不可互换:给补全塞任务级上下文会拖慢每一次按键,给 Agent 只看光标周围等于废掉它的手脚。工具选择本身就是上下文策略选择——写新页面的样板代码用补全快,动老模块的重构交给 Agent。

7、实战生成项目代码地图

给自己的项目配一张地图,完整流程四步:

  • 生成骨架:脚本机械穷举目录树
  • 补职责:让 Claude 为每个文件写一句话
  • 挂引用:CLAUDE.md 加一行指针
  • 防过期:定期重跑并标日期

第一步跑第 4 节的骨架脚本,输出存为 tmp/map-skeleton.txt。第二步把机械穷举的产出交给模型做语义概括,一条命令完成:

claude -p "读取 tmp/map-skeleton.txt,为每个文件补一句不超过 20 字的
职责描述,输出到 docs/codebase-map.md,格式:路径 — 职责。没把握的标 TODO"

-p 是无交互的 print 模式,跑完即退出,适合这类一次性批处理。第三步在 CLAUDE.md 里挂一行引用:

仓库地图见 docs/codebase-map.md,动手改代码前先读地图再检索。

第四步防过期。地图是快照不是实时视图:

注意:结构演进后不更新的地图比没有地图更糟——模型会按旧图自信地走进死胡同。

两个廉价保险:把前两步塞进 CI 或 git 钩子定期重跑;地图头部标注生成日期与对应 commit。Sourcegraph 指南把记忆失效当作受追踪的工程属性而不是假设——地图同理。

8、总结

代码上下文的三个难题是量级、相关性与结构;Claude Code 的答案是五层策略:CLAUDE.md 小而精的常驻层、Grep/Glob 先定位后 Read 的按需层、工具结果瘦身治理、子代理隔离大探索、compaction 兜底。检索式供给用 20 倍的 token 差距证明"grep 定位加读片段"胜过"塞整个文件";代码地图给模型第一跳的方向感;编辑会话维护变更块而非全文件快照;补全与 Agent 是围绕光标与围绕任务两种不可互换的上下文哲学。

对于正在用或准备引入编码 Agent 的团队,建议本周做两件事:审计项目的 CLAUDE.md,删掉与任务无关的段落;跑一遍第 7 节的流程给仓库配上地图。前者减税,后者指路,都是一小时内的投入。

练习:找一个你超过三个月没动过的项目,先让 Claude Code 只凭现有 CLAUDE.md 回答"登录态在哪里维护",记录它的检索轮数与最终命中率;再按第 7 节生成代码地图挂进 CLAUDE.md,新开会话问同样的问题。对比两次的轮数、token 消耗(/cost 可查)与答案准确度,体会一张地图对第一跳的影响。

相关推荐

​Jev 详解:不做生成的判断模型
精选AI

​Jev 详解:不做生成的判断模型

Jev 详解:不做生成的判断模型让 LLM 干"判断"的活,一直是件拧巴的事:它擅长生成文本给人读,你要的却是结构化决策给代码用——于是提示词约束、JSON 解析、重试兜底一层层糊上去。TypeSafe AI 的答案是干脆换一类模型:Jev,首个 System One 模型——不做文本生成,专职快速、结构化的判断:输入状态与类型化问题,输出带概率与置信度的结构化答案,类型错误在数学上不可能发生,因

578
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

359
时隔三年,再次备份:从手搓博客到 AI 全面自动接管
精选AI

时隔三年,再次备份:从手搓博客到 AI 全面自动接管

时隔三年,再次备份:从手搓博客到 AI 全面自动接管今天打开尘封多年的备份目录,看到了一个熟悉的文件名——2023_07_09.sql。那一刻,恍如隔世。三年前的今天,我曾经认真地导出过一次数据库;三年后的今天——2026 年 7 月 9 日,自动备份启用。整整三年,1095 天,我的博客就这样安安静静地跑着,没有人备份,也没有人管,它就那么稳稳当当地活着。感慨万千,大概是此刻最准确的四个字——不

331