最近给 Codex 配了一个组合:Context7、Serena,再配合合理拆 Session。

这套组合很容易被描述成“省 token 三件套”。但我觉得这个说法不够准确,甚至有一点误导:MCP 不是装得越多越省,Session 也不是拆得越细越省。真正起作用的,是把正确的信息送到正确的阶段,尽量不让无关的文件、文档、日志和历史决策进入当前上下文。

这篇文章用一次真实的前端功能开发流程,把三个工具串起来,也把 token 节省的边界讲清楚。

一句话总结

Context7 负责查对外部文档,Serena 负责缩小代码阅读范围,Session 负责切断已经失去价值的历史上下文。

它们共同优化的不是“工具数量”,而是 context selection:这一轮到底应该让 Codex 看什么。

Context7、Serena 和 Session 的上下文效率组合

先看一次真实工作流

假设现在要给一个 React + Midway 的企业工作台增加技能录制状态展示,同时补上接口错误处理。

我不会一上来就让 Codex“直接改好”。更稳的做法是把任务分成三个阶段。

Session 1:只探索,不改代码

第一轮只确定入口、调用链和最小修改范围:

1
2
3
4
5
6
7
8
9
10
11
12
先不要修改代码。

请定位:
1. 技能录制页面入口
2. 页面使用的 hooks 和 API
3. loading、empty、error 状态目前在哪里处理
4. 最小修改范围和相关调用方

用代码语义检索定位符号和引用关系。
如果涉及 React、Umi 或 Midway 的版本行为,再查当前版本的官方文档。

最后只输出:相关文件、调用链、推荐改法和风险。

这一轮的目标不是产出代码,而是建立一个足够小的工作集。

Serena 用来找页面、函数、类型和引用关系;Context7 只在需要确认外部框架行为时介入。两者都不应该被用来“把整个项目读一遍”。

Session 2:只实现已经确定的范围

探索完成后,我会把结论压缩成一份 handoff,再开始实现:

1
2
3
4
5
6
7
8
背景:
- 页面入口:client/src/pages/SkillRecord/index.tsx
- API:client/src/apis/skill.ts 的 getSkillDetail
- 现有问题:只处理成功态,没有 error/retry
- 目标:补充 loading、empty、error、retry
- 约束:复用现有 UI 组件,不改全局组件

请直接实现,完成后运行相关检查。

这里不需要把上一轮的所有探索日志重新粘进来。保留决策、文件、约束和验收标准就够了。

Session 3:只验证本次改动

实现完成后,如果出现大量构建或测试输出,可以把验证单独放到新的 Session:

1
2
3
4
5
6
请只验证本次改动:
1. 检查 TypeScript 类型
2. 检查 loading、empty、error、retry
3. 运行最小范围测试或构建
4. 如果失败,只分析本次改动相关错误
5. 不要重新扫描整个项目

这三轮不是固定模板。小改动可以一轮完成;当目标、证据和日志明显发生变化时,再切 Session。

Context7:把“查文档”变成定向查询

Context7 是文档检索 MCP。它解决的问题是:

这个 API 在当前版本到底怎么用?

它适合查:

问题适合查的内容
React 行为当前版本的 API、生命周期和边界条件
Umi 配置当前版本的路由、插件和构建配置
Midway 接口Controller、Service 和参数校验方式
第三方依赖当前版本的参数、示例和 breaking change

它替代的是凭模型记忆猜 API,或者手动把一整篇 README 粘进聊天。

正确的问法是:

1
2
查当前 React 版本中 useEffect cleanup 的官方行为。
只返回和这个错误相关的说明、限制和最小示例。

不太好的问法是:

1
把 React 文档都找出来。

第二种问法会让工具返回大量当前任务用不到的内容。Context7 本身也消耗 token,所以它的价值取决于查询是否足够窄。

Serena:从“读文件”切换到“读代码结构”

Serena 是代码语义检索和编辑工具。它更适合回答:

这个项目里,真正应该看哪些代码?

它可以帮助定位:

  • 函数、类、接口和类型
  • 符号引用
  • 调用关系
  • 实现位置和使用方
  • 一个改动可能影响的局部范围

可以用下面这张表理解三个常用工具的分工:

工具最擅长的问题不应该承担的工作
rg某个字符串、错误文案、配置项在哪里理解复杂调用关系
Serena某个符号怎么定义、被谁引用查外部框架文档
Context7某个依赖的当前版本怎么用理解你的业务代码

Serena 的 token 收益,主要来自减少无关文件读取。它不是把代码“压缩”了,而是把阅读入口从目录和全文,缩小到符号和调用链。

当然,Serena 也不是 rg 的替代品。精确字符串搜索仍然是 rg 的强项;语义定位和结构理解才是 Serena 的优势。

Session:真正的上下文边界

一个 Session 里积累的不只是聊天内容,还包括:

  • 之前的用户要求
  • Codex 的分析过程
  • 工具调用结果
  • 读过的文件
  • 测试日志和构建日志
  • 已经被推翻的旧结论

所以 Session 变长以后,常见的问题不是模型“突然变笨”,而是有效信息被噪音埋住了。

这通常表现为:

  1. 反复重新解释已经确定的背景。
  2. 继续参考已经过时的探索结论。
  3. 从旧日志里找错误,而不是看最新结果。
  4. 改动范围逐渐扩大。

新 Session 的价值,就是把上下文重置成一个更干净的工作面。代码和 Git 工作区不会因为换 Session 消失,但对话里的决策不会自动继承,所以需要一份短 handoff。

token 到底省在哪里

可以把每一轮输入粗略看成:

1
2
3
4
5
6
7
本轮输入
≈ 对话历史
+ AGENTS.md 和项目约束
+ 工具定义
+ MCP 返回结果
+ 读取的代码
+ 当前 prompt

这套组合主要从四个地方节省 token。

少读文件

Serena 把“扫描整个目录”缩小成“定位相关符号和引用方”。这是最直接的节省。

少重复背景

稳定的项目规则放进 AGENTS.md,就不需要每个任务都重复说明包管理器、构建命令、目录边界和验证要求。

AGENTS.md 也不能无限膨胀。它可能在每次相关任务里进入上下文,和当前项目无关的长篇背景会变成固定开销。

少复制历史

新 Session 不会携带上一轮的全部探索过程。用几行 handoff 传递结论,通常比复制几十屏命令输出更划算。

少返回日志

验证时不要让 Codex 每次都接收完整日志,可以明确要求:

1
只保留首个错误、错误文件和行号,以及与本次改动相关的 warning。

构建产物、依赖安装日志和重复堆栈,往往只增加上下文负担,不增加判断价值。

MCP 越多,token 越省吗?

不是。

每个 MCP 都可能增加:

  • 工具定义
  • 工具调用
  • 工具返回结果
  • 额外的错误和重试信息

所以应该按职责选择,而不是把所有工具都打开:

1
2
3
4
查外部文档       → Context7
理解代码结构 → Serena
查精确字符串 → rg
修改代码和跑测试 → Codex 原生工具

如果一个任务只需要改一个 CSS 属性,同时打开多个文档和代码 MCP,成本可能比收益更高。

Session 应该什么时候拆

合理拆 Session 是手动做的。判断标准不是“聊了多少轮”,而是当前上下文是否还服务同一个目标。

保持在同一个 Session

  • 目标没有变化。
  • 修改的是同一组文件。
  • 还没有积累大量日志。
  • Codex 仍然能清楚复述当前约束和决策。

新开一个 Session

  • 从调查切换到实现。
  • 从实现切换到大量测试和排障。
  • 开始处理另一个独立问题。
  • 当前对话不断重复背景。
  • 旧的探索过程已经不再有参考价值。

对一个中等规模功能,我通常会按下面的方式拆:

1
2
3
Session A:探索 / 定位 / 方案
Session B:实现
Session C:验证 / 修复

但不要为了“省 token”把一个小任务拆成很多 Session。每次新建 Session 都有重新传递背景和重新建立上下文的成本。

要不要用 / 我的判断框架

这套组合值得用,但应该把它当成上下文管理方案,而不是工具收藏。

值得上

  • 中大型代码库,文件和调用关系比较复杂。
  • 经常需要查 React、Umi、Midway 等版本敏感的文档。
  • 任务经常经历探索、实现、验证多个阶段。
  • 长 Session 里经常出现重复解释和日志污染。

可以先简单一点

  • 小型仓库。
  • 单文件修改。
  • 不涉及外部依赖行为。
  • 一次对话就能完成并验证的任务。

最应该记住的一句话

省 token 的核心不是“少让 Codex 工作”,而是让它少看无关的东西。

Context7 负责文档的准确性,Serena 负责代码阅读的精度,Session 负责上下文的边界。三者放进同一条工作流里,才会把 token 花在真正影响结果的地方。

参考资料