资讯详情

资讯详情

Claude Code上下文管理实战:从#引用到CLAUDE.md

我刚开始用 Claude Code 的时候犯过一个大意的错误在项目根目录启动claude甩给它一句“把登录页的按钮样式改一下”然后就去接水了。回来一看它确实改了按钮样式顺带把整个组件的结构也重排了我的第一个反应是“这模型怎么这么自以为是”后来才意识到它并不是故意搞事而是我压根没告诉它“登录页组件在哪、依赖哪些样式变量、设计约束是什么”。它只能凭自己的猜测干活而猜测的素材就是所谓的上下文。这一篇是 Claude Code 实战系列的第 04 篇想说的就是“添加上下文”这件事。不是让你无脑把整个仓库拖进对话而是搞清楚 Claude Code 默认能看到什么、看不到什么然后在合适的时候用合适的方式把缺失的信息补给它。读完你会获得一套可复现的上下文管理方法包括手动引用文件、长期记忆文件、上下文膨胀时的排查三板斧以及和 Cursor、Codex、Trae 这些工具的横向观感。这系列之前的文章聊过安装和基本用法这篇默认你已经能在终端里正常跑起claude了。1. 上下文不是越大越好而是“刚刚够用”很多人第一次用 Claude Code 时都会有个错觉它既然能“看到”项目目录那它应该默认知道项目里所有代码。这是最大的误解也是绝大多数翻车案例的起点。1.1 默认上下文里到底有什么Claude Code 启动后它会自动拿到一组基础信息当前目录结构、git 工作区状态、环境变量信息、以及项目里存在的CLAUDE.md文件。这些东西构成了它的“初始视野”。除此之外它并不会主动阅读所有源码而是在需要的时候通过工具去读取指定文件。换句话说它像一个刚入职的工程师知道公司有哪些部门、工位在哪但不会把每个同事的工牌号和岗位职责背下来。你让它“把登录页改好看点”它知道登录页这个目录可能在哪但具体到按钮用的哪个 class、样式变量定义在哪个文件它都是未知的。如果它没有在一开始就主动读取那些文件后面所有的“自信发挥”都是基于猜测。这里还有一个常见盲区命令工具的输出不会自动变成永久的项目知识。你在终端里跑了一堆测试、几个 grep输出结果是会进入对话上下文的但一旦清空对话或者开启新会话这些信息就没了。所以不要指望“我之前跑过那个命令它应该记得”上下文是有生命周期的。1.2 上下文窗口的物理限制和注意力稀释Claude Code 的底层模型有一个上下文窗口哪怕是常用版本里动辄几十万 token 的窗口听起来很大也经不起马拉松式对话加上整仓读取的消耗。一个中大型项目的源码可能上百万行全塞进去既不现实也没有意义。更重要的是注意力稀释问题。模型的上下文窗口就像一张固定大小的办公桌你把一吨文件全堆上去它查阅的时候依然要先“扫一眼”满桌子的纸再决定注意力放哪。你塞进去的无关内容越多关键信息在整段上下文里占的比重就越小模型就越容易抓错重点。这个现象在长上下文里尤其明显它能“看到”你贴的一份 2000 行日志但真正能定位报错的那一行可能反而被淹没在大量重复的堆栈里。所以我一直建议用“关键信息密度”来评估上下文质量而不是 token 数量。上下文不是越满越好而是越精越好。一份 30 行的核心函数片段比整个 500 行文件更可能得到精准的修改因为模型不需要花费精力去分辨哪些行值得关注。1.3 三个典型翻车现场在我自己调试和帮朋友排查的过程中发现没主动添加上下文时翻车基本逃不出下面这三种形态。第一种是跨文件修改。让 Claude Code 改 service 层某个接口的实现它咔咔一顿改函数名改得漂漂亮亮结果调用方全部报错。为什么因为调用方在另一个文件里它压根没看。后来我把调用方文件用#加进去它立刻意识到“原来这里还有个状态参数要同步处理”。第二种是写测试。你告诉它“给 utils 里的formatDate写个测试”它就按自己对常见格式函数的理解写了一套但真实的函数签名、错误处理路径、边界行为它都没看过测试自然是空转的。它甚至可能引用了并不存在的导出。第三种是看错文件版本。项目里存在多个相似目录或历史分支它通过自己的探索找到了一个长得像目标文件的文件但那可能是旧版本或迁移前的副本。最典型的情况是src和lib都有同名文件你不指定路径它就选一个它觉得合理的。这种翻车最隐蔽因为代码风格很像问题在运行时才暴露。这三个场景的共同点不是模型能力不够而是我没把“视角”给到位。Claude Code 的默认视野只是一张地图地图上的每一条路还得靠你点名它才会真正走进去看。2. 手动给 Claude 点菜添加文件上下文的三种有效姿势明确“它默认看不到一切”之后接下来就是最核心的操作问题怎么把缺失的信息喂给它。我常用的方式有三种各自有适用场景可以组合使用。2.1#文件引用最常用也最该养成习惯在 Claude Code 的对话里输入#后面跟文件路径可以直接把该文件内容加入当前上下文。这是我觉得最顺手的一种方式因为它只要一行字就能让 Claude 把某个文件当作“当前必须已知的信息”来对待。实际使用中我一般会把它放在一条指令的最前面把多个#写在一起然后用自然语言说明它们之间的关系。举个例子#src/lib/format.ts #src/lib/date.ts 主改 format.ts 里的 dateFormat 函数date.ts 是它依赖的辅助函数。目前 dateFormat 对时区的处理不符合预期请帮我修复并保持原有导出名不变。这样 Claude Code 会明确知道这两个文件是本次任务的“菜”不是可有可无的参考。它不会再去漫无目的地翻目录而是直接基于这两个文件开始分析。多个#可以同时用但我建议别一次喂太多一般三到五个文件是上限超过了模型很容易失去主次。实操上有个小提示直接输入#的时候终端一般会弹出文件补全你可以直接选如果没弹手动把相对路径写全也可以。但注意别用#去引用依赖目录或整个资源文件夹比如#node_modules这种事千万不要干那是把上下文窗口当成垃圾桶。只引用与本次任务直接相关的文件。2.2引用和拖拽快速指向但要确认是否真的被加载除了#Claude Code 的新版本里也可以用来快速引用文件或目录部分操作界面还支持把文件拖拽到终端窗口。这个方式的优点是省事缺点是容易让你产生“加了就等于懂了”的错觉。从我的体会来说#和在不少版本里的行为是有差异的#更像“把文件内容作为当前对话的一部分”而有时只是“告诉模型这个路径存在让它按需读取”。如果你习惯用我建议发完消息后先做一个验证直接问一句“你看到src/utils/validator.ts里的validateEmail函数了吗”它如果能准确复述函数签名说明加载成功如果支支吾吾说“我没在上下文里找到这个文件”那就说明只是路径被提及内容没进去。拖拽文件同理。你可以拖一个报错截图但 Claude Code 是终端工具拖图片不一定像 IDE 那样友好所以拖文件进终端更稳的是文本文件。不管用哪种方式核心原则都一样每次引用后主动确认不要在信息真伪不明的情况下继续让它往下写代码否则上一节的翻车场景又会重演。2.3 直接粘贴代码片段小范围任务的最优解如果任务只涉及一个函数、一个错误日志、一小段配置我其实更推荐直接把内容复制粘贴到对话里而不是引用整个文件。这样做有两个明显的好处。第一个好处是省 token。一个 500 行的文件真正和 bug 相关的可能只有 30 行。你贴 30 行模型可以集中注意力在这 30 行上你贴整个文件模型还得帮你排除另外 470 行无关噪声。第二个好处是可控。粘贴时你可以手动截取删掉注释、无关依赖、临时调试代码只保留和问题相关的最小上下文这样你等于替模型做了一遍预筛选。我的粘贴习惯是给片段配上文件路径和大概行号比如src/lib/request.js 中第 88 行附近的 fetch 封装 async function request(url, options) { const res await fetch(url, { ...options, headers: { Content-Type: application/json }, }); if (!res.ok) throw new Error(HTTP ${res.status}); return res.json(); } 这个请求在超时的时候会抛异常穿透到页面我不想在调用方到处加 try/catch希望在 request 内部统一处理超时并返回 null可行吗这样模型既能定位代码位置又不用打开整个文件。日志和测试输出也同理不要贴一整屏把关键报错行和堆栈的前十行贴出来就够了。上下文是给模型做决策用的不是给它做存档用的。2.4 提示词结构上下文再多也要能把“重点”翻出来最后一个手动技巧不是引用方式而是消息本身的结构。同样是添加了文件上下文你把指令写成一团散文和写成结构化的背景说明效果差别很大。我常用的模板是这样的背景这个模块是给对接方用的 SDK接口签名一旦变更必须同步更新文档。 当前状态src/client.ts 已实现了新的分页参数但 src/types.ts 里的类型定义还是旧的。 目标把类型定义对齐到新实现并跑通类型检查。 约束不要改动 src/client.ts 的实现逻辑只改类型。 请执行先对比这两个文件给出需要修改的类型清单再逐项修改并运行 tsc。为什么这种结构有效因为 Claude Code 在长上下文里需要显式信号。背景告诉它“为什么要改”当前状态告诉它“现在是什么样”目标告诉它“改到什么样”约束告诉它“哪里不许碰”。这四个信息一给它就不容易跑偏。这也引出一个判断标准一段信息如果只对当前任务有用就放在对话里临时引用如果能长期复用、每个新对话都应该知道那就应该放进CLAUDE.md。临时信息和长期记忆分开管理上下文才不会越积越乱。3. CLAUDE.md把临时上下文固化成项目长期记忆对话里的#和粘贴片段只能解决当下的任务。如果每个新对话都要重新解释一遍“这个项目用什么包管理、代码风格是什么、哪些文件不能动”那就太浪费了。Claude Code 给出的解法是CLAUDE.md一个让上下文具备长期记忆的文件。3.1 三层 CLAUDE.md 和 /init 生成Claude Code 会识别多个层级的CLAUDE.md我平时会用到三个位置。用户级~/.claude/CLAUDE.md存放所有项目通用的偏好比如“我习惯用 pnpm”“提交信息用 conventional commits”“回复尽量用中文”。项目级项目根目录的CLAUDE.md存放这个仓库特有的约定比如技术栈、目录结构、构建命令、部署注意点。子目录级子目录里的CLAUDE.md针对该目录生效比如src/api/CLAUDE.md可以写接口层代码的特殊规范。当多个层级都存在时总体上遵循“越具体越优先”的原则子目录里的约定会覆盖或补充项目根目录的约定。这样你可以搭一套自洽的规则体系用户级管你的个人习惯项目级管项目基线子目录级管局部细节。如果你不知道怎么开头直接在项目根目录执行/init命令Claude Code 会扫描项目结构读取关键配置文件生成一份CLAUDE.md初稿。我第一反应是这功能有点鸡肋生成的东西太泛但后来发现它真正的价值是提供一个起点让你在此基础上删改比自己从零写要快得多。初稿生成后记得把它纳入版本管理它应该和代码一起交给团队。3.2 好文档写“规矩”不写流水账很多人写CLAUDE.md时容易走两个极端要么只写三行废话要么写成几百行的百科全书。两种都不好用。只写“这是一个 Vite TS 项目”基本等于没写因为 Claude Code 自己读 package.json 都能知道。真正有长期价值的是那些它从代码里读不出来、需要你告知的信息比如“这个项目不能用 npm必须用 pnpm workspace”“src/shared里的代码要同时兼容 web 和 node不能引 DOM”“API 的 baseUrl 在.env.local里提交代码时永远不要把真实 key 提交进去”。我建议一份好用的CLAUDE.md至少包含这几类内容技术栈和版本不仅写 Vue3还写用没用 TypeScript 严格模式、构建工具是 Vite 还是 Rspack。目录总览哪个目录是业务代码、哪个目录是自动生成的、哪个目录不能手改。常用命令启动、测试、lint、构建最好精确到包管理器命令。代码约定命名规范、样式方案、状态管理方式、接口封装习惯。常见坑哪些操作会踩雷比如“不要修改dist目录下的文件因为每次构建都会被覆盖”。另外要特别提醒CLAUDE.md的内容是会进入上下文的。它过于冗长时会挤占每次对话的可用空间所以我建议控制在几十行到一百行左右。重点是“规矩”不是文档归档凡是 Claude Code 能从代码里自己看出来的信息不用重复写。3.3 一次迭代实例拿我自己一个 TypeScript CLI 工具项目举例。第一版CLAUDE.md我写了三行技术栈、启动命令、测试命令。用了一阵子后发现每次新开会话让它加功能它都会先花好几轮去摸索目录结构还会把“不要动dist”这种教训重新踩一遍因为第一版根本没写。后来我把CLAUDE.md扩成了四块目录说明、命令速查、发布流程、已知约定。其中“已知约定”里明确写了“所有子命令的配置文件统一放config/目录不要在项目根目录新增配置文件”。从那之后新会话的起步效率明显变高了它不会再自作主张往根目录塞配置文件也不会把生成的产物目录当成源码去改。如果你也在为“同一个项目反复解释同一件事”头疼说明你的CLAUDE.md还没迭代到位。每次在对话里用了超过一次的解释性内容就可以考虑把它沉淀进CLAUDE.md这比每次手打要省得多。4. 上下文膨胀把请求撑爆一次 maximum context 的血泪排查上下文管理做得好能避免一个大坑请求因为上下文过长而被接口直接拒绝。热词里那个claudecode apierror 400 maximum context想必让不少人血压升高。我在一次接一个历史遗留项目时就结结实实地撞上过。4.1 报错出现时我正在做什么那次任务需要同时改一个服务端接口和一个前端页面。为了保险我用了上面说的#引用一口气把接口文件、前端页面、类型定义、还有一份旧的接口文档全加了进去。然后开始和它来回调试连续让它跑了十几次测试命令每次工具输出都挺长。结果在一次让它继续修改的请求里终端直接甩出一行报错大意是请求超过了上下文最大限制HTTP 400。我当时第一反应是“模型窗口怎么这么小”但静下来之后分析问题出在上下文累积上。Claude Code 每次发起新请求时不是只发送你最新的一句话而是要把整个对话历史、所有主动引用的文件内容、历次工具执行的输入输出全部打包发送给模型。当这些内容的总量超过了上下文上限API 就会拒绝这次请求。也就是说这是一个典型的“添加上下文 对话持续累积”双重叠加导致的膨胀问题。不是某一次操作突然把它撑爆而是每一次都在给这个窗口加东西直到溢出。4.2 三步定位“到底谁在占窗口”遇到这种报错别慌也不要盲目清空重开。先用排查的思路搞清楚谁在占用窗口避免下一次再爆。第一步找出当前上下文的使用情况。不同版本的 Claude Code 入口不完全一样你自己敲/看一下命令列表通常会有一个显示上下文或 token 占用的项目比如/context或者/cost。如果没有也可以大致估算数一下当前对话轮数、引用文件数量、CLAUDE.md 长度、以及最近几轮工具输出的长度。第二步列出“嫌疑清单”逐个掂量每个参与者的大小。我当时清点出来四个人对话历史三十多轮每轮都有较长内容。#引用的文件四个其中一个接口文档有几百行。每次测试命令的输出有一次输出甚至长达几百行。CLAUDE.md当时刚写好有一百多行。第三步找出元凶。我用排除法检查后发现大头其实是“旧对话历史 接口文档 超大工具输出”。那个接口文档虽然重要但大部分内容在现有代码里已经能体现没必要全文塞进上下文。测试命令的输出也不是每次都值得完整带进历史早期的几次输出早就没参考价值了。从那之后我再遇到 400 就会先按这个顺序排查先看对话历史和工具输出是不是很久没清理了再看引用的文件是不是有大块冗余最后才是考虑换模型或换窗口。绝大多数情况下问题都出在前两类。4.3 三板斧compact、clear、重建上下文定位到原因之后解决手段主要有三个我按“从轻到重”排序。/compact压缩历史。它会把前面的对话浓缩成摘要保留关键决策和结论丢掉大段的原始内容。适合你还想继续当前任务、只是想给上下文瘦身的情况。/clear清空当前对话。最彻底所有历史都消失相当于换了个全新的会话。代价是你得重新向它交代项目背景所以用之前要确定之前的对话确实没有后续价值了。重建上下文如果问题出在引用了大文件清掉旧对话后重新只用最小集引用。比如把“加整个接口文档”换成“只看接口代码里的函数签名”或者用粘贴片段代替整个文件引用。我那次的做法是先把不重要的旧测试输出用/compact压掉然后移除那个几百行的接口文档引用改成直接粘贴接口实现的关键片段最后继续任务就通了。整个过程大概五分钟。下面这张表可以帮你快速决策该用哪个方案方案保留信息程度适用场景/compact保留摘要丢掉原文对话还有价值只想瘦身/clear全部清空任务已结束或完全可以重来重建上下文保留自己选定的最小集引用文件过大需要换一种喂法4.4 预防像记账一样管理上下文踩过一次坑之后我养成了一个习惯每次给 Claude Code 喂信息之前先问自己三个问题。第一个问题这个任务真的需要这个文件吗有时候我只是让它改一个工具函数根本不用引用整个页面组件只把函数所在文件 调用方签名贴出来就够了。第二个问题能不读全文只读关键部分吗如果一段代码几百行我通常先用 grep 定位到具体函数或行号再让它只看那一段。让 Claude Code 直接看整个文件肯定更省我的事但省事后患无穷。第三个问题能不能把这个任务拆成两个对话一个对话做到后面历史本身就会变成沉重的上下文包袱。与其在同一个对话里塞“改 A 模块 改 B 模块 跑测试 写文档”四件事不如拆成两个或三个独立任务每个任务都用干净上下文重新开始。这三点说起来都很简单难的是每次动手前都执行一遍。说实话“上下文臃肿”和“代码难以维护”很像都是前期省事、后期还账。定期清空、精简引用才不会被maximum context这类错误卡住进度。5. 横向对比Cursor、Codex、Trae 的上下文管理思路写到这里你会发现在 Claude Code 里玩明白的上下文技巧放到别的工具里也大同小异。因为现在这波命令式 AI 编程工具的底层思路都在收敛只不过入口和叫法不一样。5.1 一张对比表我按自己的使用体验把主流工具的上下文管理方式拉了一个简表给你做参考。这里的细节不同版本会有差异但大方向是稳的。工具记忆文件手动引用方式自动索引历史压缩Claude CodeCLAUDE.md#、、拖拽无强索引靠工具自读/compact、/clearCursor.cursor/rules文件、代码库检索有代码库索引能自动找相关代码对话清理、长上下文模式Codex CLIAGENTS.md引用文件依赖工具按需读取有自动压缩机制Trae规则文件 / 记忆文件界面化文件有代码索引对话清理从表格上看殊途同归。Claude Code 用CLAUDE.mdCursor 用.cursor/rulesCodex CLI 用AGENTS.md本质上都是在项目里放一个“规则文件”让 CLAUDE 这类 agent 每次开场就知道项目规矩。引用文件的方式也都很接近#、只是语法糖背后都是“把某份文件纳入本次任务视野”。自动索引这块是 IDE 系工具的优势Cursor 和 Trae 能通过索引快速检索相关代码而 Claude Code 这种 CLI 工具更依赖你自己点菜。5.2 从 Cursor 转过来的三个认知切换我身边不少人是 Cursor 的重度用户第一次用 Claude Code 时会有明显的抽离感有这三个认知切换值得提一下。第一个切换是没有可视化上下文面板了。Cursor 里右侧能清楚看到被 进来的文件列表Claude Code 里你得靠/context或自己心里记账。所以用 Claude Code 时要主动去问自己“我这句话会带上哪些历史信息”而不是等面板告诉你。第二个切换是#批量引用比鼠标点选更克制。Cursor 里鼠标点文件很顺手容易越点越多Claude Code 里手动写路径时会自然思考“这一个真的需要加进去吗”这种思考本身就是上下文管理的第一步。第三个切换是规则文件要当作代码维护。在 Cursor 里你可能半年不碰.cursor/rules但在 Claude Code 里CLAUDE.md是带收益的长期资产。我会像 review 代码一样定期 review 它删掉过时约定补充新踩到的坑这份文件就是整个项目的“入职文档”。5.3 最终建议工具会换习惯不会我不是要说服你把所有工具都换掉。工具选型本来就受团队、项目、平台习惯影响但上下文管理这件事本质上和工具关系不大。无论是 Claude Code、Cursor、Codex 还是 Trae你都可以反手问一下自己我有没有定期维护项目记忆文件我有没有在长对话里及时 compress 或 clear我有没有在每次任务开始前想清楚“最小但完整的上下文是哪几样”如果你能在 Claude Code 里把这些习惯养出来换到任何同类的 agent 工具都不会太难受。反过来只换工具不换习惯到哪都会遇到“它怎么又猜错了”的血压时刻。我个人最大的收获就是在第八次踩进上下文膨胀坑之后彻底把CLAUDE.md和精简引用变成了肌肉记忆从此新开对话不再像开盲盒。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →