我第一次意识到 context-mode 这个开关到底有多重要,是在连续加班的一个晚上。当时我在用终端里的 AI 编码助手改一个老项目,它非常活跃:我让它改一个函数,它能把三个调用点之外的实现全翻出来;我报一个报错,它能把整条调用链的上下文都贴回来。问题不在能力,在它“看得太多”。后来我把工具的上下文模式切到 strict,同一句话问出来的结果立刻收敛到只跟当前任务有关。从那天起我确信:在 AI 辅助开发这件事上,真正考验工程能力的不是提示词写得有多漂亮,而是怎么控制它“看什么”。
这篇文章想聊的就是 context-mode——一个用来控制 AI 工具可见范围的模式开关。它解决的是真实项目里非常普遍的问题:默认全仓扫描让 AI 回答发散、改动越界、token 消耗失控。无论你用的是命令行 Agent、编辑器里的补全扩展,还是自己封装的一套检索增强流程,只要工作对象是几千行以上的代码仓库,这个模式就值得花时间真正理解。
1. 一个模式开关,为什么值得单独拿出来研究
1.1 默认的“全仓库视野”其实是一种浪费
大多数终端 AI 工具、编辑器扩展的默认行为,是把整个仓库做成索引,每次提问都从全仓检索相关内容。这个设计初衷是防止 AI 回答“没根据”,但放到真实项目里,全仓视野通常不是聪明,而是低效。
我拿一个中等规模的仓库大致估过账:假设项目有 5 万行代码,平均每行折算 15 个 token,整体就是 75 万 token。而很多模型的上下文窗口只有几万到二十万,工具只能把窗口填满,填进去的大部分内容却和当前问题无关。这就好比让一个实习生入职第一天,把公司所有部门的文档全部看完再回答你一个具体问题——他当然只能泛泛而谈,甚至把不相关的信息也当成依据塞进回答里。
1.2 context-mode 的本质:把可见范围变成显式配置
context-mode 不是某个工具的独占功能,而是一类控制策略的统称。它决定模型在做决策时,能访问哪些目录、能引用哪些文件、保留多少轮会话记忆、检索结果取多少条。当模式被激活,工具就不再盲目全仓扫描,而是按规则构造一份“工作区视图”。
我习惯把它理解为:给 AI 划定一张工作台。工作台上放什么文件、什么约定、什么历史记录,由模式决定。放得太少,AI 信息不足;放得太多,AI 分不清重点。context-mode 就是要在这两者之间找一个工程上可控的位置。
1.3 最该用它的三类场景
第一类是改老项目和大型仓库。历史包袱多、文件组织混乱,全仓模式很容易让 AI 改错地方。我在处理一个多年迭代的后端服务时,就频繁遇到过它把相邻模块的注释和逻辑一并改掉的情况。
第二类是 AI 辅助 Code Review。只希望 AI 检查某个改动范围时,一点都不想让模型把整个项目重读一遍。它会给你一大堆“潜在问题”,绝大部分跟本次改动无关,Review 反而变成大型噪音现场。
第三类是反复修改同一个模块的任务。AI 需要保持对模块内部结构的完整记忆,同时又要不受相邻模块干扰。这种场景最适合固定一个严格模式,让每一次追问都落回同一批文件上。
2. context-mode 到底在切换什么:三类上下文的边界划分
2.1 指令上下文:你直接说的那些话
指令上下文是你当前输入给工具的提示词、问题、命令,是最容易理解的一层。context-mode 并不会屏蔽指令,而是告诉工具“这句话的服务范围是哪块区域”。
举个例子,同样是“把支付模块的余额校验补全”,在 strict 模式下,工具会老老实实围绕支付模块的代码做改动;在 full 模式下,它可能顺手把订单模块里所有跟余额相关的逻辑都端上来,理由是“它们看起来相似”。指令没变,输出边界完全不同。
2.2 项目上下文:仓库结构、相关文件、配置
这是 context-mode 最核心的作用对象。项目上下文包括仓库目录结构、配置文件内容、相关源码文件。模式不同,AI 能“看见”的仓库范围就不同。
我在模拟项目 X 里验证过一组对比:strict 模式只导入api/pay目录;balanced 模式额外导入shared和utils;full 模式则把整个services目录都带上。改一个支付接口时,strict 给出的方案最贴合模块内部现有风格,而 full 给出的方案虽然视野更宽,却掺杂了大量其他服务的偏好,反而不利于局部一致性。
2.3 会话上下文:多轮对话的“记忆”
会话上下文是此前多轮对话保留的信息,包括你提过的约束、命名习惯、已经做出的决定。它容易被人忽略,但恰恰是 context-mode 切换时最大的变数。
有些工具在切换到 strict 模式时,会大幅裁剪会话记忆,以腾出空间给白名单文件。结果就是你前两天在会话里约好的错误码格式,AI 突然“不认了”。所以不管用什么工具,切换模式后一定要检查一次 AI 是否还记得关键约定,这点我在后面会专门展开。
2.4 三种模式的注意力分配对比
| 模式 | 项目上下文 | 会话上下文 | 典型用途 |
|---|---|---|---|
| strict | 只开放白名单目录与明确文件 | 只保留最近几轮关键信息 | 定点修 bug、改动高风险模块 |
| balanced | 白名单加常用共享目录 | 保留近十几轮对话 | 日常功能开发、局部重构 |
| full | 全仓库可检索 | 尽量完整保留 | 跨模块搜索、全局重构、技术调研 |
日常开发我会默认用 balanced,既保证视野,也不至于把 token 浪费在无关文件上。精度要求高的任务切到 strict,例如改支付、结算、鉴权这类核心模块;只有需要跨模块搜索时才临时开 full,并且把 token 成本预期放在心里。这个选择逻辑,比记任何工具的快捷键都重要。
3. 把 context-mode 用起来:从配置到日常命令
3.1 选型思路:内置模式还是自己接一层控制器
市面上的工具大致分两类。一类自带 context-mode 入口,配置好之后就能直接用,适合团队内部没有特殊定制需求的场景。另一类工具不提供完整模式,只支持自由指定文件或目录,这时我会自己接一层检索控制器:不改模型本身,而是在把内容打包交给模型之前,先按模式过滤一遍候选集。
团队小、项目边界清晰时,我优先用自带模式,省维护成本。仓库很复杂、私有规则又多时,外置配置更灵活,因为它可以让上下文管理完全独立于具体工具,以后换工具也能带走整套规则。
3.2 一份最小可用的配置文件
下面是一份我在小型项目中验证过的配置骨架,字段语义尽量通用,具体工具可能略有差异,但思路可以直接复用。
{ "mode": "balanced", "project_context": { "include": ["src/core", "src/api"], "exclude": ["tests", "vendor", "dist", "node_modules"] }, "retrieval": { "top_k": 12, "similarity_threshold": 0.25 }, "session": { "max_turns": 8, "token_budget": 6000 } }mode是默认模式,project_context定义可见目录和排除目录,retrieval.top_k控制每次检索最多返回多少文件块。similarity_threshold是相似度阈值,低于这个值的文件不会进入上下文,用来过滤无关匹配。session.max_turns限制保留多少轮对话,保护窗口给项目上下文腾位置。
你可能注意到我没有把max_turns设到很大的值。原因是机会成本:每多留一轮旧对话,就少一段空间存放当前模块的有效代码。对多数任务而言,最近 8 轮对话加上一份清晰的模块白名单,已经足够。
3.3 完整启用步骤
第一步,初始化会话并指定模式。我习惯先用 strict 起步,确认当前任务没有被无关文件干扰:
ctx start --mode strict第二步,查看当前上下文预览。这步非常重要,直接输出当前可见路径、排除路径和检索参数,帮你在动手前就确认“AI 将看到什么”。
ctx status第三步,切换模式。中途任务范围变化时,直接切换即可:
ctx mode balanced第四步,把某个目录临时加进当前会话白名单。这比改全局配置更安全,因为只影响当前任务,不会波及其他会话:
ctx add src/core第五步,验证配置是否真的生效。我会用一个容易触发误判的问题测试,比如让 AI“找出所有和订单状态有关的判断逻辑”。如果结果里仍然出现tests或dist文件,说明 exclude 没生效,先调配置再继续。
3.4 高频命令片段
日常开发中,我经常把“切换模式”和“临时加目录”组合使用,形成一套肌肉记忆:
# 做定点修复 ctx start --mode strict ctx add src/modules/payment # 做跨模块调研 ctx mode full ctx search "订单超时任务的核心流转" # 完成任务后恢复默认 ctx mode balanced ctx drop src/modules/payment一个小技巧:任务结束后主动drop掉临时目录,避免下一个会话打开时残留一堆无关文件。很多人只记得加目录,不记得清理,结果上下文越来越臃肿,最后又怪工具不聪明。
4. 上下文污染、模式串台与 token 预算失控:实测踩坑记录
4.1 坑一:默认 full 模式让一次简单问答烧掉大量 token
有一次我在模拟项目 X 里问一个简单的上报逻辑,工具一次性把仓库里三十多个文件都传了进去,一次对话就消耗了近 3000 token 的输入。连续问几个问题后,当天预算就见了底。
排查时我先跑ctx status,发现模式停在 full,检索列表里出现了大量无关文件。改成 balanced 后,同一个问题只用了几百 token,答案质量反而更高。这之后我养成了一个习惯:任何会话开始前先确认模式,绝不信默认值。
4.2 坑二:切到 strict 后“失忆”
还是模拟项目 X,我前几天在会话里约好统一用某种错误码格式,结果切到 strict 后 AI 突然不认了,给出的代码全按另一种风格来。第一反应是模型坏了,查了之后才发现,strict 模式下会话上下文被大幅裁剪,早期的约定根本不在这轮可见范围里。
修复办法是双管齐下:把关键约定写进CONTEXT.md并加入 include 白名单;切换模式后先问一句“你还记得我们约定的返回格式吗”来验证记忆是否还在。记忆不在,就重新导入清单,不要闷头继续改。
4.3 坑三:自动识别把测试目录当成了生产代码
有次我图省事,让工具自动识别模式,结果它把_test.go文件当成生产代码来改,改完后单测全挂。这种问题非常隐蔽,因为它不是立刻报错,而是等到跑测试才发现。
排查链路是:复现问题 → 查看上下文预览 → 发现 include 规则匹配到了*_test.go→ 在 exclude 里补上**/*_test.go和tests/→ 重新验证。经验是:exclude 规则和 include 规则同等重要,而前者更容易被忽略。
4.4 从症状到根因:一次完整的排查链路
我举一个更完整的排查例子。现象是 AI 反复修改legacy/payment_v2里的代码,但每次都会把legacy/payment_v3的旧注释风格带进来,导致风格混乱。
第一步是复现问题,连续问三个相关问题,确认每次输出都不稳定。第二步打开上下文预览,发现payment_v3目录始终在可见列表中。第三步检查 include 配置,发现目录结构里有一条祖先级规则"legacy/**"把两个版本都包进去了。第四步把规则改成只匹配legacy/payment_v2/**,并单独把v3加入 exclude。最后重新提问,输出立即恢复局部一致性。
这个排查思路并不复杂,核心是不要越过上下文预览直接改提示词。看不到 AI 实际读入的内容,任何调整都是盲打。
4.5 几个文档里没有写的小经验
文档很少提醒你:切换模式后要主动确认一次会话记忆。工具通常会显示“已切换”,但不会告诉你哪些历史被裁掉了。这是最容易踩的暗坑。
文档也不会告诉你,多人共用同一会话时,模式会被彼此的指令带跑。团队协作时我建议大家各自起新会话,再共享同一份 CONTEXT 清单,而不是硬挤在一个上下文里。
另外补充一个判断技巧:错误的 include 最多是浪费 token,错误的 exclude 却会静默地挡掉必要代码。后果不是“变慢”,而是 AI 在关键逻辑上看不见全貌,生成明显有缺陷的方案,你还以为是模型能力不行。
5. 进阶:把 context-mode 沉淀成项目级上下文清单
5.1 为什么需要 CONTEXT.md
模式配置解决的是工具层面的“每次读什么”,但如果整个团队都依赖各自的 include/exclude 碎片,维护成本会越来越高。最好的做法是把上下文策略上升为一份项目共享说明,让清单和模式配置相互配合。
CONTEXT.md就承担这个职责:它记录模块边界、业务核心目录、禁止修改的区域、命名与编码约定,并且与 context-mode 的 white/blacklist 对齐。新工具接入时,照着这份文档配置一份自己的模式规则即可;老工具升级时,也只是同步更新目录变化,不需要每次重新脑测。
5.2 一份可以直接套用的模板
下面是我在模拟项目 X 里使用的模板,缩略了几个次级小节,骨干结构可以直接抄走。
# Context 清单 ## 项目拓扑 - src/api:对外接口层 - src/core:业务核心逻辑,属于高频白名单区 - src/integration:外部系统对接,建议按需加入 - tests:测试目录,任何情况下不进入默认上下文 ## 核心目录白名单 - src/core/domain - src/api/handlers ## 禁止修改目录 - legacy/payment_v3 - 所有 *_test.go 文件 ## 命名与编码约定 - 错误码统一使用 ErrXxxCode 格式 - 数据库字段统一 snake_case - 新增接口必须带 OpenAPI 注释 ## 会话切换建议 - 修 bug:strict + src/core + src/api - 重构:balanced + src/core - 跨模块调研:full,用完立刻切回这份文档的价值,不只在于给 AI 看,也在于给团队里的人看。新人拿到它,就能快速知道哪些区域可以大胆改,哪些区域碰都不能碰。
5.3 把模块边界和 Git 分支对齐
context-mode 与模块化设计是天然互利的。我习惯让每个 context 模式对应一定范围的模块,做某个 feature 时,只激活该 feature 涉及的目录。比如做购物车功能,就只把src/cart和必要的共享依赖放进白名单,而不是把src/user整个放进去。
如果项目本身模块边界乱成一团,上下层互相引用,context-mode 也会跟着难受,因为无论如何切,AI 都可能需要跨大范围文件才能自圆其说。这也是我逐渐想明白的一点:这模式不只是在约束 AI,其实也是倒逼项目结构变得更清晰。
5.4 团队协作里的维护约定
任何共享文件都会经历同一个问题:不更新就会腐烂。所以我会在团队里立三条简单约定。第一,CONTEXT.md由模块负责人维护,谁最了解当前交付范围,谁负责更新清单。第二,目录变化时,要同步改 context-mode 配置,两件事算一个提交,方便回滚。第三,新成员入职先读清单,再上手改代码,避免一上来就用 full 模式横扫全仓。
这个做法还有一层好处:上下文清单是工具无关的。将来团队换任何新工具,只要有这份文档,就能快速重建一套可控的 AI 工作环境,不需要重新摸索一遍。
5.5 我个人使用中最舒服的一次体验
最近一次让我真正觉得这模式值回票价的,是在模拟项目 X 里重构结算模块。整个会话固定 strict 模式,白名单只有src/core/domain和src/api/handlers,外加一份写清楚返回结构约定的CONTEXT.md。连续改了两天的代码,AI 生成的风格始终稳定,没有一次越界改动到支付模块。只在跨模块排查时临时开了几分钟 full,查完立刻切回。
那种感觉,不是 AI 变聪明了,而是它终于“坐对了工位”。最后再分享一个小技巧:我给自己的终端配了一条简短的别名,切分支的时候自动把模式切到对应分支的默认配置,比如checkout feature/cart时就自动切到 cart 的 balanced 模式。这个动作帮我把上下文管理的成本降到了几乎可以忽略的程度,也让我越用越离不开这个看似简单的开关。