
1. 先说说我为什么被“上下文”管理逼到写脚本如果只看名字你可能以为 context-mode 又是什么花里胡哨的编辑器主题。但对我来说它是被 AI 辅助编程工具坑了无数次之后被逼出来的一整套自己的做事习惯在跟模型对话之前先决定好“这一次该给模型看什么、不该看什么”。事情得从一次很普通的 bug 修复说起。当时我要在一个 Python 脚本里补一个字段映射模型第一次回答其实方向是对的只是我手一抖把之前四五轮无关的聊天记录一起粘了过去。结果离谱的事情发生了模型开始坚持用一个我早就淘汰掉的老方案反复给我“修正”而且每一次都很自信。我一开始以为是模型变笨了后来把窗口清空只贴最精简的函数签名和需求描述一次就过了。那一刻我才意识到问题不在模型在“上下文”本身——你以为多给点背景材料是帮助实际上是在给模型制造注意力噪声。后来我去查了不少资料也做了点实验越来越确认一件事大模型对超长上下文的利用能力并不像窗口数字那么乐观。你给它 100K tokens 窗口不等于它能 100% 消化掉这 100K 的内容。越是往后翻越早的信息越容易在注意力机制里被稀释。就像你给同事塞了一大沓背景资料他翻到第 20 页时早就忘了第一页你要他干嘛。于是我开始思考与其每次手动清理聊天记录不如把“该喂什么上下文”这个决策本身做成工具。这就是 context-mode 的起点。它不是某个大厂发布的正式框架而是我基于自己的工作流做的一套可复现方案把和 AI 协作时的上下文策略拆成三种固定模式用配置文件和脚本统一管理让每次对话前都先明确“我现在处在一个什么状态、该给模型什么级别的信息”。这篇文章会把完整的思路、配置、代码和踩坑过程都放出来适合那些天天用 AI 写代码、但总觉得“模型时灵时不灵”的开发者参考。1.1 token窗口是物理上限注意力预算才是真相我先说个结论别把上下文窗口当成你的“内存条容量”它更像是“集装箱的空间”——你是能把所有东西都硬塞进去但模型真正能有效利用的是它注意力机制愿意分给那些关键信息的“注意力预算”。注意力机制本身是 Transformer 架构的核心模型在生成下一个 token 时需要在前文所有 token 之间分配注意力权重。如果上下文太长中间夹着大量无关信息注意力权重会被稀释模型就更倾向于依赖距离最近的内容以及那些重复出现了很多次的模式。这就解释了一个常见现象你在第 1 轮贴了需求第 5 轮贴了报错第 10 轮模型生成的代码突然忘了需求——因为需求那个 token 的注意力权重早就被后面几轮废话给冲淡了。所以我在设计 context-mode 时第一个原则就是宁可少给也要给对。模型不需要“你昨天中午吃了什么”这种背景它需要的是“当前函数的签名、调用方的期待、约束条件”。上下文不是越多越好而是密度越高越好。1.2 一次让我彻底崩溃的bug修复现场那次失败经历值得详细说说因为它几乎包含了所有上下文管理问题的典型特征。项目是个内部数据处理脚本A 函数从 Kafka 取数据B 函数做字段映射C 函数写入数据库。问题是 B 函数里有个字典 key 没对上我让模型帮我改。前几轮对话里我贴过 SDK 文档、贴过数据库表结构、贴过完整日志甚至贴过一段根本不属于这个项目的旧代码。模型每一轮都给我一个看起来很合理的方案但改完一跑还是挂。后来我仔细对比了一下发现它每次都绕回第一轮里那个错误的建议原因是那段话在上下文中反复出现模型把它当成了“高频正确答案”。当我清空上下文只贴 B 函数本体、一行调用代码和最终报错时模型两秒就定位到了问题字典里有个 key 使用的是user_id而实际上游字段叫uid。就这么简单。根因不是模型能力不行是我把上下文搞得太过杂乱让它无法区分“历史讨论”和“当前事实”。这件事之后我就定下了一个死规矩和模型协作时上下文必须经过“结构化裁剪”再进入对话。1.3 从“手工清理聊天记录”到“制度化管理上下文”最开始我靠自觉去清理但人总有犯懒的时候而且项目一多经常分不清哪次对话属于哪个任务。于是我想不如把每次会话的“上下文策略”做成显式的模式。所谓 context-mode本质上就是三个问题的回答我现在要做的是什么类型的任务这个任务需要模型看到哪些文件、哪些背景这个任务希望模型的输出风格是什么把这三个问题固定成几套预设每次开工前选一套就不用再纠结该贴多少代码进去了。这也让团队协作变得更容易——新同事接手时看配置文件就知道这个项目通常用哪种模式和 AI 协作。2. 三套运行姿态专注、审计、探索我给 context-mode 设计了三种模式名字很直白focus、audit、explore。你完全可以按自己的习惯改但这三档基本覆盖了日常 AI 辅助开发的全部场景。2.1 focus模式只解决眼前这一件事focus 模式对应的是“目标非常明确”的任务比如修复一个已知 bug、给函数补一个字段、写一个独立的小工具函数。这类任务的特点是模型不需要了解整个项目只需要看到当前函数、直接依赖和最小约束。focus 模式的上下文预算最小默认最多只喂 5 个文件而且通常是一到三个关键文件加一个简短需求描述。Prompt 风格也偏命令式只输出结论和最小验证步骤不要长篇推导。不夸张地说这个模式解决了我日常 60% 的 AI 使用场景。为什么 focus 这么有用因为大部分 bug 就是局部问题模型拿到一个几千行的完整项目反而会“觉得”这里也可能有问题、那里也可能有问题最后给你改出一堆幺蛾子。让它只看一个文件它就只能在这个文件里找问题命中率直线上升。2.2 audit模式面对存量代码的防守型姿态audit 模式是我第二常用的模式尤其在做代码评审、接手老项目、评估依赖升级影响面的时候。它的思路和 focus 完全相反我要模型“看整个项目的关键文件”但产出的不是修改而是风险清单。这个模式下include 规则通常覆盖项目里的核心源码文件默认最多喂 30 个文件。Prompt 风格要求模型逐文件检查安全、边界、兼容性问题把所有发现整理成结构化清单并给出风险等级。audit 模式的价值在于它把模型从“写代码的助手”切换成“审代码的第三者”视角完全不同。有次升级一个老服务端的 HTTP 框架我就是先切到 audit 模式让模型扫描了所有路由文件和中间件文件它列出了一堆旧 API 的废弃点帮我避开了线上故障。这个模式本质上是把上下文从“局部”扩大到“全局”但保持输出纪律。2.3 explore模式向陌生代码库提问的探路姿态第三种模式 explore适合你刚接手一个新项目、需求还很模糊、或者只是想头脑风暴的场景。它和前两者最大的区别是不追求唯一正确的答案追求多视角的解读方向。explore 模式会喂进去 README、文档、主入口文件大概 15 个文件左右。Prompt 风格刻意降低约束比如“忽略原有架构给出 2-3 种不同的解释方向”“从用户视角描述这个项目解决什么问题”。这种情况下模型不会因为上下文里塞满了一堆实现细节而过早固化思路反而能输出更开放的方案。我用 explore 模式做过不少架构预研效果很不错。但也要说清楚explore 模式只适合“探索”阶段不适合“生成代码”阶段。一旦决定选某个方案你要切回 focus 模式去写具体实现否则模型会继续给你云里雾里的设计稿。下面用一张表总结下三种模式的分工模式典型场景默认上下文预算产出形态输出风格focus修bug、写函数、补字段最多5个文件直接可运行的最小修改结论优先不做长篇推导audit代码评审、升级评估、老项目接手最多30个源文件风险清单、检查报告结构化枚举带风险等级explore新项目调研、需求模糊、方案预研最多15个文档类文件多种方案和解释视角开放发散允许多方向讨论3. 落地实现一个能直接抄的.context-mode目录讲完思路接下来是硬货。我把我实际在用的这套配置和脚本分享出来你可以直接复制到自己的项目里跑一遍。3.1 目录结构设计我把所有模式相关的文件放在项目根目录下的.context-mode/目录里结构如下.context-mode/ ├── modes.json ├── focus.md ├── audit.md └── explore.md选择放在项目根目录是因为这样可以跟着 git 一起提交团队其他人也能用同一套上下文策略。为什么不用 yaml 而用 json纯属个人偏好json 没有缩进歧义解析库也是标准库自带跨端行为稳定。modes.json 存放三种模式的规则定义也就是 include、exclude、max_files、prompt 这些配置。三个 .md 文件则是每种模式对应的“开场白模板”会拼接在收集到的代码文件内容前面告诉模型这时该扮演什么角色。3.2 modes.json里到底放什么下面是我项目里一份比较典型的 modes.json{ focus: { include: [README.md, src/core/, docs/quickstart.md], exclude: [tests/, docs/archive/], max_files: 5, prompt: 只输出结论和最小验证步骤不展开背景。 }, audit: { include: [**/*.py, **/*.js, **/*.ts, **/*.sql], exclude: [node_modules/, dist/, .venv/], max_files: 30, prompt: 逐文件检查安全、边界和兼容性问题输出风险清单。 }, explore: { include: [README.md, docs/**/*.md, src/main.py], exclude: [node_modules/], max_files: 15, prompt: 忽略原有架构给出2-3种不同的解释方向。 } }这里每个字段都有讲究include要喂给模型的路径规则。路径是相对于项目根目录写的。如果路径是目录脚本会递归收集目录下所有文件如果带**通配符就表示按 glob 匹配。exclude黑名单规则。排除掉测试文件、构建产物、依赖包、虚拟环境这类噪音。项目一大这套黑名单能省下大量 token。max_files上下文上限。宁可少喂也不要一次塞爆。audit 模式我最多放到 30 个文件再往上模型的注意力已经开始稀释而且会产生大量重复扫描。prompt模式专属的系统指令。它是整个 context 包的灵魂相当于告诉模型“你现在该以什么姿态工作”。3.3 核心脚本切换模式并生成上下文包我写了一个不到 80 行的 Python 脚本放在~/.local/bin/context_mode.py然后给 shell 配了别名cm用起来很顺手。核心逻辑不复杂就是读配置、收集文件、拼接成一个完整上下文。#!/usr/bin/env python3 import argparse import json import sys from pathlib import Path ROOT Path.cwd() MODE_DIR ROOT / .context-mode MODES_FILE MODE_DIR / modes.json def load_modes(): if not MODES_FILE.exists(): print(fERROR: {MODES_FILE} not found, filesys.stderr) sys.exit(1) return json.loads(MODES_FILE.read_text(encodingutf-8)) def collect(mode_name: str) - str: modes load_modes() if mode_name not in modes: print(fERROR: unknown mode {mode_name}, filesys.stderr) sys.exit(1) cfg modes[mode_name] files [] # 收集 include 规则命中的文件 for pattern in cfg[include]: p ROOT / pattern if p.is_file(): files.append(p) elif p.is_dir(): files.extend(p.rglob(*)) else: files.extend(ROOT.rglob(pattern)) # 剔除目录项再排除黑名单 files [f for f in files if f.is_file()] files [f for f in files if not any(f.match(ex) for ex in cfg[exclude])] files sorted(set(files))[: cfg[max_files]] # 拼接上下文包 blocks [cfg[prompt]] for f in files: blocks.append(f## FILE: {f.relative_to(ROOT)}) blocks.append(f.read_text(encodingutf-8, errorsignore)) return \n\n.join(blocks) def main(): parser argparse.ArgumentParser(descriptioncontext-mode) parser.add_argument(mode, choices[focus, audit, explore]) parser.add_argument(--copy, actionstore_true, help复制到剪贴板) args parser.parse_args() context collect(args.mode) print(context) if args.copy: try: import pyperclip pyperclip.copy(context) print([copied], filesys.stderr) except ImportError: print([skip] pip install pyperclip, filesys.stderr) if __name__ __main__: main()用法很简单cm focus | head -50 cm audit /tmp/context.md cm explore --copy--copy之后会把完整的上下文直接塞进剪贴板然后你切到对话窗口粘贴就行。整个过程不到三秒。需要提醒的是collect函数读入的文件会被原样拼接进上下文所以 include 规则一定要谨慎。如果你把整个 repo 的src目录塞进去一份超长上下文马上就会把 token 预算打爆。这也是 max_files 这个限制存在的意义它是最后的保险丝。3.4 与编辑器、终端的联动方式单纯在终端跑脚本还不够要让它真正融入工作流就得跟日常开发习惯绑定。我目前的做法是在 zsh 配置里加入别名alias cmpython3 ~/.local/bin/context_mode.py。每次进到一个新项目先cm focus --copy然后直接粘贴到 AI 对话窗口。在 VS Code 里用任务系统关联按CtrlShiftP打开 Task配置一个cm focus的任务一键执行并把输出插入一个临时文件方便对比前后改动。把.context-mode/modes.json提交到 git 仓库团队成员用同一套上下文规则减少“为什么你贴的代码跟我不一样”的沟通成本。联动这块不用做得太复杂。真正重要的是每次开工前脑子里先过一遍“我要切到哪种模式”这个动作比脚本本身重要得多。4. 实战三个月的坑模式不是开关是纪律工具做完后我用了整整三个月前后切换了几十次模式。期间踩了不少坑每一个都有实际教训。4.1 上下文精简过头模型开始“脑补接口”最开始我特别激进focus 模式只喂一个文件结果发现模型开始凭空捏造上游字段名称。一次让我印象深刻的例子是我让它修一个调用外部 API 的函数上下文里只有函数本体没有任何接口文档。模型就自信地生成了一段调用代码里面用了api.fetch_user_v2()实际根本不存在这个方法。后来我给 focus 模式加了一条规则include 里面必须包含函数的“调用边界”也就是上游接口定义或者文档片段。你可以精简历史讨论但绝对不能精简接口契约。给模型的信息可以少但每条都必须是关键的。4.2 模式“惯性”导致审计漏掉关键问题还有一个坑模式不是切完就完事了它非常容易产生惯性。我在一个任务里待得太久脑子里还停留着 focus 的局部视角切换到 audit 时没有认真调整结果模型扫描了一堆文件却漏掉了数据一致性问题。后来我给自己定了个规矩每次切换模式时附带写出当前任务的 top-3 目标放在 prompt 的最前面。这不是模式文件能替你完成的所以我把“任务目标”设置成 prompt 之外的必填内容拼在上下文最前面。格式固定为当前任务xxx 目标1xxx 目标2xxx 目标3xxx即使脚本里没有强制校验这个习惯也让我少踩了很多坑。4.3 团队协作中的模式文件漂移另一个典型问题出现在团队协作中.context-mode/modes.json被某个同事改了 include 规则之后我本地的 audit 模式可能就突然开始扫描 node_modules原因是他把 exclude 漏了。如果遇到这个问题建议建立一条约定任何人在修改 modes.json 时不能动 include/exclude 的全局默认值只能改 prompt。如果你想要更保险可以让脚本多读一个modes.local.json本地配置覆盖仓库配置同时只保留max_files字段的全局上限约束。我后来在脚本里加了这么一段逻辑def load_modes_with_local_override(): modes load_modes() local_file MODE_DIR / modes.local.json if local_file.exists(): local json.loads(local_file.read_text(encodingutf-8)) for mode_name, overrides in local.items(): modes.setdefault(mode_name, {}).update(overrides) return modes这样个人偏好和团队默认配置就分开了。汇总一下我踩过的坑和对应解法症状根因对策模型写出不存在的接口focus 精简过头缺边界信息include 里必须含接口契约审计漏掉全局问题局部视角惯性残留切换模式时重写 top-3 目标团队模式文件不一致公共配置被随意改动modes.local.json 做本地覆盖token 消耗超预期include 规则过宽检查 exclude 黑名单控制 max_files4.4 token成本不能只看窗口数字最后再提醒一件很多人忽略的事切换到 audit 模式后一次扫描可能轻松吃掉几万 token如果项目大甚至一次对话就能超过几十万 token。换算成实际花费那可能就是几块钱一次。我当时在三个项目上做了粗略统计focus 模式平均一次上下文包 2000-4000 tokensaudit 模式平均一次 30k-60k tokensexplore 模式大概 10k-20k tokens。如果能提前知道这个量级你就不会下意识地想把整个 src 目录全部塞进去。给个建议在 modes.json 里把 max_files 调小一些优先级是质量大于数量。文件少了输出反而更精准。5. 效果数字与适用边界什么时候别把它当万能药说了这么多优点还得聊聊实测效果和它的局限性。这套东西不是银弹有些场景我明确不建议硬套。5.1 我自己记录的三组数据我在日常开发里做了一段时间记录对比了使用 context-mode 前后的表现。样本不算大但趋势很稳定任务类型之前平均成功轮数无模式管理现在平均成功轮数context-mode产出质量变化修已知bug4-6轮经常绕回旧方案1-2轮一次通过率高改动更小回归风险降低业务函数编写3-5轮需要反复纠正1-3轮基本符合接口定义命名和结构更统一老项目代码评审无固定流程效果不稳定一次输出完整风险清单覆盖更全面可追踪性更好这组数据也符合我的基本判断上下文管理解决的核心问题是“信息信噪比”。当模型拿到的都是高密度、恰到好处的内容时生成质量自然稳定。5.2 两个不适合硬套的场合第一个不适合的场景是高度交互式的多轮设计会话。比如你在和模型讨论一个系统设计你希望能不断追问、变换角度。这时候如果用 focus 把上下文死死锁在一个窄范围模型会很快陷入局部最优回答越来越收敛反而扼杀了发散的潜力。这种场景更适合手动控制上下文或者干脆用 explore 模式但定期清空重来。第二个不适合的场景是需要跨天持久记忆的长周期协作。如果你的任务天然需要“记得昨天聊了什么”比如跟踪一个持续数日的调研项目那 context-mode 这种“每次只给当前快照”的方式就不合适因为你每次都要手动重新把背景贴进去反而增加负担。这种情况下我宁可用带持久记忆的对话工具或者维护一个独立的调研笔记文件作为上下文核心而不是依赖模式切换。准确地说context-mode 的价值在于那些“边界清晰、任务可拆解”的开发工作流。它适合的是一段一段地推进而不是一条线从头聊到尾。6. 项目实际的下一步让上下文管理从“手动”变成“半自动”写到这里context-mode 已经能解决我绝大多数问题但我还在继续改进它。目前我尝试的方向有两个一是给模式文件增加“任务状态”字段比如 done、in-progress、blocked让模型输出时带上任务进度二是把cm collect的结果按任务编号归档方便事后复盘哪类任务系统的失败轮数最多。甚至我还在考虑把模式切换日志记录下来统计自己一天里在 focus、audit、explore 之间切换了多少次借此发现自己时间分配的问题。忍不住说一句这套东西做起来非常简单真正值钱的不是脚本本身而是它强迫我建立的“开工先思考”的习惯。对 AI 辅助编程这件事我最大的感受是当你给模型的信息足够精准、足够有序时它回馈给你的准确度会高得惊人。大多数人之所以觉得模型“有时候聪明有时候蠢”问题往往出在自己给的上下文——不是太长、太乱就是缺了关键的那一块。如果你也在每天跟模型协作又时常被绕圈子的失败代码折磨不妨试着把 context 这个变量单独拎出来管理。先定三种模式再写一组配置文件最后配一个顺手的小脚本。你不需要抄我的设计只要能意识到“上下文也是产品需求的一部分”这件事就已经完成了一半。