旧会话越聊越笨,新会话又得重讲?我给 Matt Pocock Skill 炼了套《影分身之术》:本体想清楚,分身写代码,完事回来汇报
先跟大家聊下背景
平时写代码,不管你用 Codex、Cursor、Claude Code 还是别的 Agent,基本都是在一个会话里聊问题、聊需求、聊项目背景。你告诉它怎么做,有什么注意点,把你知道的上下文都交给它。等双方把需求和方案确认下来,再让它开始写代码、做实施。
一般都是这样一个流程。
这样聊出来的内容其实非常有价值。里面有项目背景、有用户真正想要什么、有已经做过的取舍,也有那些看起来没写进需求、但做错了就会跑偏的细节。
问题是,后面还一直在同一个会话里写代码。文件内容、搜索结果、命令输出、测试日志会不断往里塞,会话越来越长,也会不断压缩。每压一次,前面聊过的东西都会占一部分上下文,剩余空间就越来越小。
打个比方,最开始可能还有 100%,压缩以后可能是 80%、60%,最后只剩百分之十几。这个时候聊不了几句话、做不了多少事,又得压缩,非常费时间。上下文太多,模型速度会越来越慢,效果也可能越来越差。
但你又不太敢直接开新会话。因为前面已经把需求和上下文都对齐了,新会话还得重新读仓库、理解现有代码,再把这些内容和任务对应起来。
怕丢上下文,又不太敢开新会话。
这就是我想解决的问题。
第一版:先把共识编成一份 Goal
我是在 Matt Pocock Skills 的基础上做这个改良的。
前半段还是它原来的思路:有想法或需求,先 Grill,把问题和细节聊清楚;然后运行 /to-spec,把双方确认的内容整理成一份 Spec。
用户确认以后,我会打一个状态:SPEC READY。
它代表这份 Spec 已经和用户达成共识,可以开始实施了。
text
想法 / 需求
↓
Grill
↓
/to-spec
↓
用户确认 → SPEC READY
我最早做的改进,是在 SPEC READY 以后用 /to-goal 把这份已经确认的 Spec 编成 Goal。
Goal 里面会带着当前状态、执行顺序、完成标准、约束和验证要求。然后把它放进一个干净的新会话,或者放进另一个 Agent 里执行。执行完,再把结果带回原来的主会话。
这样做的原因很简单:聊需求和写代码,本来就是两类工作。
主会话负责把事情想清楚、把控细节和方案;新会话围绕已经确认的 Goal 去读代码、做实施。代码执行产生的大量中间上下文,不再把主会话塞得越来越挤。
Goal 还有两个很实际的好处。
第一,它不绑定 Codex,在任意 Agent 里都可以运行。你可以拿去 Cursor、Claude Code、Pi,甚至其他 Agent 里继续实施。
第二,大任务可以拆成多个 Goal,每个 Goal 放进一个独立的 Worktree 里并行运行,提升整体速度。
大概的流程是:
text
SPEC READY
↓
Goal A → Worktree A → Agent A
Goal B → Worktree B → Agent B
Goal C → Worktree C → Agent C
不过用了一段时间以后,我又发现了一个问题。
Goal 能把已经确认的目标、范围和完成标准带过去,却不能把探索仓库时积累的全部上下文一起带过去。
所以新的执行会话拿到 Goal 以后,还是要重新读一遍仓库:先看项目说明和当前代码,找到相关文件,理解现有实现,再把这些代码和 Goal 对上。
这个过程会重新消耗一遍 Token 和时间。而这些仓库上下文,主会话在前面讨论方案时其实已经读过、理解过了,这部分会产生一些额外开销。
这就是我后来继续做 Fork 的直接原因。
第二版:能继承上下文,就直接 Fork
有些任务刚刚在当前会话里讨论完成。主会话已经读过仓库、理解了现有代码,也和用户确认了最终 Spec。
这个时候再开一个完全干净的会话,从头读取同一份仓库上下文,确实有点重复,也会产生额外开销。
所以这类任务,我会直接从当前会话 Fork 一个新会话。
Fork 出来的会话会继承前面的讨论和已经建立好的仓库理解。它知道相关代码在哪里、为什么这样改,也知道用户的真实意图,可以直接根据最终的 SPEC READY 开始实施。
还有一个很实际的好处:Fork 前后的上下文前缀是一致的,可以继续利用已有的上下文缓存。
前面读取仓库、分析代码、讨论方案形成的缓存不需要全部重新计算,启动更直接,Token、时间和成本也会更低。
所以我又加了 /spec-executor。
Fork 完以后,在这个新会话里直接运行它。它会按照最新的 SPEC READY 去开发、验证和 Review,最后输出一张 SPEC EXECUTION RECEIPT。
这张 Receipt 会把下面这些内容写清楚:
- 修改了什么;
- 进行了哪些验证;
- 哪些完成标准已经通过;
- 还有哪些内容没有完成。
然后把它复制回主会话,主会话就能知道这个任务到底做到了什么程度。
这版省掉了新会话重新读取仓库、重新建立任务理解的成本,但还需要手动 Fork、手动运行命令、手动复制 Receipt。
都已经用 Agent 了,还在两个窗口之间搬文字,多少有点不甘心。
加上最近 Claude Code 出了 Session 之间发送消息的能力,我也给 Codex 做了一套类似的功能,用来自动化这部分人工流程。
于是就有了第三版。
第三版:让 Fork 自己回来汇报
后来我又做了 Codex Task Messenger 和 /execute-spec-in-fork。
现在主会话已经达到 SPEC READY 以后,可以直接调用 /execute-spec-in-fork。
接下来的流程会自动完成:
- 主会话自动创建 Fork;
- 主会话向子会话发送执行任务;
- 子会话在 Fork 中调用
/spec-executor; - 子会话完成开发、测试和 Review;
- Task Messenger 把 Receipt 主动回传给主会话;
- 主会话检查结果并决定是否继续修改。
如果主会话检查出问题,它会把问题发回同一个 Fork,让它继续修改。修改完成后再次汇报,直到结果没有问题,再自动归档这个 Fork。
text
主会话:需求、方案、决策、检查结果
↓
Fork 会话:开发、测试、Review、Receipt
↓
Task Messenger:自动把结果送回主会话
我现场演示过一个任务:Fork 出去的会话运行了 56 分钟,完成后自动把 Receipt 回传。主会话检查通过以后,再将它归档。
以前需要手动完成的 Fork、派工、复制结果和收尾,现在由这套流程自动完成。
Fork 和 Goal 怎么选?
Fork 和 Goal 不是谁替代谁,而是两种不同的上下文策略。
- 当前会话刚读完仓库、谈清需求,而且任务一次能做完:使用 Fork,继承仓库理解并利用上下文缓存;
- 任务需要跨人、跨天、跨引擎、多个 Worktree 并行,或者当前历史已经很乱:使用 Goal,把任务整理成一份可独立执行的合同。
一句大白话:
能顺着刚才的讨论接着干,就 Fork;需要搬运、并行或者换引擎,就 Goal。
另外一些简单但实用的改动
除了主流程,我还改了一些日常会用到的东西。
Batch Grill:减少来回问答
原来的 Grill 一轮只问一个问题,复杂需求可能要来回二三十次。
我把三个 Grill 都改成了按依赖批量提问:当前能够一起回答的问题放在同一轮,回答完以后,再展开下一层。
实际用下来,经常两三轮就能把主要分支聊完。
Typed Emoji:让长对话更容易扫读
我也加了一些带类型的 Emoji,用来区分范围、风险、取舍、建议和待确认项。
这不只是为了装饰,主要是为了在长文本里更快找到重点。
最近同步的实用 Skill
上游最近增加的几个 Skill,我也同步了:
/research:让子 Agent 查阅官方文档、源码和一手资料,最后留下一份带引用的研究文档;/wizard:把登录后台、配置 Token、写入 Secret 这类必须由人参与的流程做成交互式脚本,之后还能分享给同事复用;/wait-what:当 Agent 又开始说黑话时,让它结合当前项目的语境,把刚才那件事重新讲明白。
最后
现在这套方法其实很简单:
主会话负责想清楚,执行会话负责写代码,Receipt 负责把结果和证据带回来。
如果任务还说不清什么叫做完,就继续聊;如果已经达到 SPEC READY,再决定是 Fork 还是 Goal。
项目和资料
- 改良版仓库:tt-a1i/matt-skills-with-to-goal
- GitHub 主页:tt-a1i
- B 站视频:《Agent 开发焚诀------字节周会技术分享》
这个视频的反响还不错,发布 3 天收藏已经超过 1000。
具体命令、完整演示、PPT 讲解,以及 Task Messenger 如何自动回传,视频里都讲得比较清楚。感兴趣的话可以去看一下。