
最近朋友问我同时推进三个项目还能都接住是不是记性特别好。我说不是是我不太靠脑子记事了。过去我打开一天工作的方式通常是浏览器挂着十几个标签页IDE里躺着两个分支的改动终端还停着上次没跑完的构建日志。一旦中途被拉去处理另一个任务回来后至少要花十几分钟才能重新进入状态这还算是运气好的。后来我把这堆混乱收拾成了一个叫 context-mode 的工作流它把“当前在做什么”这个问题从人脑记忆变成机器可保存、可恢复、可交接的状态任务切换变得像关灯开灯一样简单。这篇文章就把这套东西完整拆开讲讲背后的设计思路、每一层的实现方法以及我在落地过程中踩过的坑。1. 上下文切换为什么这么贵一次“换挡”的隐藏成本1.1 多任务不是并行而是频繁换挡很多人以为同时推进几个任务效率会像多核 CPU 一样成倍增长。真实情况恰恰相反。在写代码这件事上人脑干的多线程其实是“快速切换 恢复现场”而每一次恢复现场都要付出额外开销。举个很典型的例子你正在改一个支付回调的签名逻辑代码已经看到“这里需要处理超时重试”脑袋里正推演着异常分支。这时候同事过来问了个部署问题你答完回来屏幕上的代码还是刚才那几行但你脑中那个“推演到一半”的状态已经被打断了。你得重新看一遍方法名、参数类型、上下文里的调用关系才能找回那种“我知道下一步要改哪里”的感觉。这个过程短则三五分钟长则十几分钟而且越复杂的任务恢复成本越高。有个概念叫注意力残留大意是切换任务后大脑的一部分资源仍然停留在上一个任务上。这也是为什么频繁看手机消息会显著降低写代码质量的原因——表面上你只分心了一两秒但大脑从消息里挣脱出来还需要额外的缓冲时间。我做过一段时间的粗略统计一天里被打断三次以上并且每次切换都涉及不同项目时光是“重新进入状态”的时间加起来就能超过四十分钟。这还没算切换时顺手刷两下浏览器、翻翻消息记录这些隐性时间。对我来说这就是 context-mode 最开始想解决的问题——不是让你不切换而是让每次切换后的“恢复成本”趋近于零。1.2 上下文真正包含的东西状态、目标、线索在动手搭工具之前得先搞清楚“上下文”到底指什么。我自己的拆解结果是三样东西状态当前这个任务进行到哪一步了。比如哪几个文件改过、服务是否在运行、依赖装没装、测试跑到哪个用例挂掉了。目标这个任务最终要交付什么。是修复一个 bug还是实现一个接口验收标准是什么。线索你刚才“想到哪了”。比如下一步要改哪个函数、那个深藏的配置项在哪里、某个临时绕开的逻辑要不要回来清理。这三样东西在脑子里通常是隐性的不落到纸面上。而它们散落在机器里的形式又极其碎片化Git diff 里有状态分支名里可能藏着目标终端历史记录里留着线索。context-mode 的核心不是发明新东西而是把这三个信息统一收纳到固定的、可快速检索的位置让你只需要看一眼就知道“我在哪、要去哪、下个动作是什么”。1.3 先给上下文“打分”才知道自己的瓶颈在哪我不建议一上来就照着别人的配置抄。因为不同人的切换场景差别很大有人是纯编码任务之间的切换有人是编码和会议之间的切换还有人是在两个项目组之间横跳。先做一周的审计记录以下信息切换事件切换前在做什么切换后要做什么花了多久找回状态最卡的点去回同事消息改用户登录逻辑继续改用户登录逻辑约8分钟忘了刚才推演到哪里切到B项目看报错写A项目接口定位B项目线上日志约15分钟不记得B项目的部署路径吃完午饭回来写README继续写README约3分钟目标明确状态仍在记录三四天后基本就能看清自己最痛的切换场景。我当时的结论是跨项目切换最伤因为不仅代码状态要恢复连项目如何启动、日志在哪、分支情况如何都是独立的。所以后面的方案也优先解决“跨项目切换”这件事。2. 第一层实现用 tmux 和 direnv 把终端变成可恢复的工作台2.1 tmux 会话布局一个项目一个 session终端是我干活的主场所以第一层先解决“终端上下文”的问题。我选了 tmux理由很简单它能让每个项目拥有独立的会话session会话里再按需开窗口window而且脱开会话后所有状态都保留在服务器端下次 attach 回来就是原封不动的现场。以前我开终端的方式很随意一个窗口切目录到处跑窗口一关历史全没。现在的规范是一个项目对应一个 tmux session常年驻留不关。session 内的窗口布局固定默认四个窗口shell跑各种命令行操作editor打开编辑器或者直接文件预览server跑开发服务器日志常年驻扎在这里logs查看指定服务的实时日志这样的好处是肌肉记忆很直观想看服务状态就切到 server 窗口想查日志就切到 logs 窗口不用每次重新敲命令。我写了一个小函数来新建项目会话省得每次手动摆窗口ctx-new() { if [ -z $1 ]; then echo usage: ctx-new project-name return 1 fi local name$1 local dir$HOME/work/$name if [ ! -d $dir ]; then echo directory $dir does not exist return 1 fi tmux new-session -d -s $name -c $dir tmux rename-window -t $name:0 shell tmux new-window -t $name:1 -n editor -c $dir tmux new-window -t $name:2 -n server -c $dir tmux new-window -t $name:3 -n logs -c $dir tmux select-window -t $name:0 tmux attach -t $name }需要恢复某个项目的现场时只需要tmux attach -t project-name窗口布局、历史输出、当前目录全部还在。这一步不用任何插件tmux 原生就能做到。2.2 direnv进入目录自动切换环境解决了终端布局还有个隐性问题每个项目的环境变量、工具链版本完全不同。比如 A 项目用 Node 18B 项目用 Python 3.11C 项目需要设置一个特殊的API_BASE_URL。如果靠人肉记忆去 export那又是一个需要恢复的上下文。direnv 解决的就是这个。它会在你进入目录时自动加载.envrc文件里声明的环境变量离开目录时自动卸载。配置方式简单粗暴# 在项目根目录创建 .envrc export NODE_VERSION18.18.0 export API_BASE_URLhttp://localhost:8080 export DEBUG_MODEtrue首次进入目录时执行direnv allow放行即可。之后每次cd进去环境变量就已经按项目设好了终端提示符上甚至可以显示当前激活的项目名。这一步把“项目相关配置”从人脑记忆里彻底转移到了文件里换项目时连 export 都不用敲。有人可能觉得 .envrc 和.env文件不是一回事吗区别在于 .env 多数是给应用本身读取的配置而 .envrc 是开发环境层面的开关直接注入到 shell 环境里对命令行工具同样生效。两者完全可以配合使用。2.3 上下文快照与恢复的一键脚本tmux 能保存终端现场direnv 能切换环境但还有一类上下文它们存不了你脑子里正在想的“下一步动作”。我一开始也以为没救后来想通了——不能靠工具自动记录脑内活动那就人工制造一个“记忆锚点”。做法很简单定义一对ctx-save和ctx-load命令。ctx-save 把当前分支、改动文件、最近执行的命令、随手写的备注打包成一个快照文件ctx-load 在恢复时把这个快照打印到终端并打开对应编辑器ctx-save() { local note${1:-.ctx/quick-note.md} mkdir -p $(dirname $note) { echo # Context Snapshot: $(date %Y-%m-%d %H:%M) echo echo ## current branch git branch --show-current 2/dev/null || echo (not a git repo) echo echo ## changed files git status --short 2/dev/null | head -30 echo echo ## recent commands history 15 | tail -15 echo echo ## next step (self note) echo - } $note echo snapshot saved to $note } ctx-load() { local note${1:-.ctx/quick-note.md} if [ -f $note ]; then echo CONTEXT RESTORE cat $note else echo no context note at $note fi }这套灵感来自一个很朴素的生活经验书看到一半合上之前夹个书签第二天翻开就知道看到哪了。ctx-save 就是那个书签。它不需要写长篇大论只要记录“目标、卡点、下一步”三个关键词就够。3. 第二层实现编辑器和 Git 分支组成的上下文闭环3.1 编辑器的会话恢复方案让打开的文件也记得终端层面恢复后编辑器成了下一个断层。以前我切回项目要重新打开一堆文件入口文件、组件、测试、配置文件少说也要花一两分钟回忆“上次改了哪些文件”。把编辑器的会话状态也纳入 context-mode 管理之后这个动作变成了自动的。我用 Neovim 的时候用的是 persistence 插件。它能自动保存当前打开的文件列表、光标位置、折叠状态并在启动时恢复。核心配置也就几行require(persistence).setup({ dir vim.fn.stdpath(state) .. /sessions/, options { buffers, curdir, tabpages, winsize } }) vim.api.nvim_create_autocmd(VimEnter, { callback function() require(persistence).load() end, })这样每次打开项目编辑器自动切回上次的打开状态不用自己想起底改了哪些文件。如果你用 VS Code思路也一样把.code-workspace文件提交到仓库里它可以声明这个项目的多根目录、调试配置、推荐插件。值得留意的是工作区文件里尽量别存机器相关的绝对路径否则换电脑后一堆报错。3.2 分支名即上下文把任务状态挂载到分支上编辑器恢复的是“文件状态”但任务层面的上下文还需要一个载体。我最终选了 Git 分支——它本来就是任务的最小单元。现在我的分支命名规范是类型/任务号-简短描述比如feat/42-user-login、fix/103-timeout-refactor。分支名本身就是一个上下文标签看到分支就知道在做什么。但这还不够真正让我觉得闭环的是把“任务笔记”也塞进分支里。具体做法每个任务分支在创建时就生成一个.ctx/NOTES.md随分支一起走git checkout -b feat/42-user-login mkdir -p .ctx cat .ctx/NOTES.md EOF # Task: 用户登录功能 ## Goal (一句话) 实现邮箱密码 JWT 的登录接口并补充集成测试。 ## Acceptance Criteria - [ ] POST /api/login 返回 accessToken - [ ] 密码错误超过5次锁定账号 - [ ] 集成测试覆盖正常、错误密码、锁定场景 ## Progress - [x] 搭建用户模块 - [ ] 编写登录服务 - [ ] 接入 Redis 做失败计数 ## Blockers - 暂无 ## Next Step - 编写 AuthService.login() 的具体实现 EOF这个 NOTES.md 跟着分支走切回主分支时它不在工作目录里不会碍事。等分支合并后整个.ctx目录随分支一起被清理或保留完全不用单独维护。它的作用不是写日报而是在你切走又切回时只花十秒钟就能恢复任务全貌。3.3 上下文文档模板别高估自己的笔记习惯很多人的笔记系统之所以坚持不下去是因为从“想记”到“记下来”之间摩擦太大。模板的作用就是把这个摩擦降到最低。我在.ctx/template.md里固定了五个小标题没有多余字段Goal一句话表达最终要交付什么Acceptance Criteria验收标准用 checkboxProgress当前进展控制在十条以内Blockers卡点只写暂时解不了的Next Step下一步的具体动作这套模板的核心逻辑是无论上下文多么复杂最终落到行动上的只有“下一步做什么”。只要 Next Step 是清晰的这个任务就不会丢如果 Next Step 写不出来说明你还没真正理解任务这时候写下来本身就是一种思考。我自己吃过亏曾经把模板设计成十几个字段负责人、预计时间、依赖方、风险等级……结果记了两天就再也不想打开。模板越轻越容易被记录。写 NOTES.md 的时间我会控制在五分钟以内超出五分钟说明记的不是有效上下文而是流水账。4. 第三层实现AI 辅助编程时代的上下文投喂与回收4.1 为什么 AI 总在“答非所问”上下文窗口不等于有效信息随着 AI 辅助编码成为日常我意识到 context-mode 又多了一层新任务管理人与模型之间的上下文。很多人觉得 AI 回答不靠谱其实一半情况是上下文给得不到位。大语言模型的上下文窗口是有限的而且窗口内塞入大量无关代码后模型对关键信息的注意力会被稀释。这跟给人交接任务一样——你把一个项目压缩包直接发过去对方也不知道该从哪看起。最常见的失败姿势有两种一是只丢一句“帮我看看这个报错”没有任何文件路径、代码片段、环境信息模型只能瞎猜二是把整个项目代码粘进去动辄几百行结果模型被无关逻辑带跑回复看似完整但完全不对症。有效上下文的核心是“信息密度”不是“信息总量”。我给 AI 喂上下文时的原则是让它在没有额外猜测的情况下能直接理解三件事——要解决什么问题、相关代码在哪个文件负责什么、期望的输出形式是什么。4.2 一套固定的上下文打包模板为了解决这个问题我把给 AI 提问时的上下文整理成了固定模板保存成一个 snippet每次直接套用当前任务修复登录接口在密码错误时返回 500 的问题 涉及文件 - server/routes/auth.ts登录路由入口调用 AuthService.login - server/services/auth.service.ts核心逻辑密码校验和 JWT 签发在这里 相关报错 POST /api/login 返回 500 日志Error: Password verification failed: invalid signature 已尝试的方法 - 用正确密码可以登录说明接口和 JWT 签发逻辑基本正常 - 确认数据库连接正常 目标定位密码校验失败的具体原因输出修复后的代码 diff 和原理说明注意看这个模板的结构任务目标、涉及文件路径及每文件的作用、报错原文、已尝试过的方向、期望输出。它能用的原因在于它把模型需要“脑补”的空间压缩到了最小同时给出了足够强的检索信号。我还发现一个小细节当你列出“已尝试的方法”时模型会明显减少重复建议回复会更聚焦到剩余的可能性上。这本质上是在给模型的推理过程划定上下文边界。4.3 让 AI 自己总结上下文长对话的接力技术上下文管理还有一个更隐蔽的问题同一个任务在聊天窗口里聊了很久新开窗口时之前的内容全丢了。重新粘贴一遍所有信息不仅累还容易漏。我的做法是把“总结”作为对话的一部分而不是事后的工作。在长时间任务的关键节点我会让 AI 生成一份“当前状态确认”请基于我们刚才的讨论输出一份简短的上下文交接文档 1. 当前要解决的问题 2. 已经确定的方案 3. 已修改的文件和修改要点 4. 尚未解决的问题 5. 下一步建议动作 用清单形式控制在一屏以内。这样获得的状态文档可以直接粘贴给新对话也让自己的 NOTES.md 有了可复制的来源。每次开新对话时先粘贴这段状态再继续提问效果接近无缝衔接。另外我强烈建议在项目根目录维护一个AGENTS.md或CLAUDE.md文件用来记录项目级的长期上下文技术栈、目录结构、启动命令、代码风格约束。把这种稳定信息写入文件临时对话时就不用反复粘贴它们。短期上下文靠模板长期上下文靠文件两者配合AI 辅助编程的体验会稳定很多。5. 落地过程中踩过的坑与最终的简化版本5.1 坑一方案太重装完就不想用一开始我构建这套东西时犯了个典型错误——追求“完整”而不是“好用”。我给 tmux 加了 statusline 插件给 Neovim 配了自动 session 选择器写了一个支持参数解析的巨型.bashrc脚本甚至计划把浏览器标签页也用插件统一管理。结果呢搭建用了整整两天第三周就开始觉得维护成本太高脚本报错不知道改哪插件升级后又出现兼容问题最后连ctx-new这个命令都懒得用了。后来我把大量自定义脚本砍掉只保留 tmux 原生命令、direnv、编辑器插件和极简的两个 shell 函数。真正留下来被高频使用的是那些“一眼看得懂、坏了不用修”的东西。自动化本身是有成本的每个自定义脚本都意味着未来的维护负担在真正常驻工作流之前尽量先用现成方案。5.2 坑二上下文笔记写成了流水账我一度认为笔记记得越多越安全于是变成了每半小时记录一次。结果是 NOTES.md 长得像工作日志列了一堆已完成操作却找不到一句明确的“下一步”。切换回来时看这种笔记等于重新读一遍历史根本没有起到快速恢复的作用。后来我给 NOTES.md 设了硬限制只保留模板里的五个字段Progress 出现超过十条就必须清理合并每次更新只允许在 Next Step 上花最多五分钟思考。这个约束逼着我提炼真正关键的信息而不是把所有动作都囤积在文件里。笔记的价值不在厚而在“能让人在三十秒内做出继续行动的决定”。5.3 坑三只存上下文不设恢复检查点我早期做了ctx-save却没做对应的恢复动作。刚开始挺得意觉得有个快照总比没有强。结果真实场景中我切回来时经常忘记执行ctx-load或者执行后发现快照是两天前的根本没有恢复到最新的状态。快照如果不在“切回任务”的那一刻被使用就只是一串静默躺在磁盘上的文字。这也是为什么我现在把 save 和 load 设计成一对并且要求“长时间离开前必须 save回来后必须先 load”。load 不仅仅是打印内容更是一个仪式感动作它强迫你接收“任务已恢复可以继续”的心理信号。没有检查点的上下文管理等于没有存档的游戏——你保存了但读取的入口不在手边最终还是会被打回原形。5.4 我最后留下的最小闭环折腾一圈后真正长期在用的其实是一套非常朴素的最小闭环一个 tmux session 对应一个项目关掉终端也不影响现场每个任务分支都有一个.ctx/NOTES.md只记目标、验收、卡点、下一步给 AI 提问前套用固定模板打包必要上下文长任务用 AI 生成的“状态确认”做交接长时间离开任务前执行 ctx-save回来后先 ctx-load 再动手这套东西加起来搭建时间不到半小时并且大部分依赖都是现成工具坏了随时能重来。它不华丽但足够稳。如果你也想搭一套属于自己的 context-mode我的建议是别从我的完整配置抄起。先只做一件事给当前正在进行的任务建一个.ctx/NOTES.md把目标和下一步写进去。等这个习惯稳固了再去加 tmux 会话、再加编辑器恢复、再加 AI 上下文模板。上下文管理这种东西最贵的从来不是技术实现而是你愿不愿意在每个“切换点”上多花十秒钟给未来的自己留一条回来的路。