GSD 是一个用来让 AI 编码代理完成大型、多会话项目的系统。不是玩具演示。不是“给我做个落地页”。而是真正的项目——那种包含大量文件、多个需要相互通信的子系统,并且规模大到任何一个代理都不可能在一个上下文窗口里完成的项目。
它要解决的问题说起来简单,做起来残酷:代理会随着时间失去一致性。他们会忘记自己在前三个任务里做了什么。他们会生成“存在但其实不能用”的文件。他们会在每一轮都烧掉大量 token 去重新阅读项目结构。他们一旦被打断,就无法在不让人类重新解释一遍的情况下继续工作。而当某个东西坏了,也没有干净的方式回滚。
GSD 2.0 解决了这一切。下面是它的做法。
层级结构:Milestones、Slices、Tasks
所有事情都会被拆成三个层级:
Milestones 是可交付的版本——你正在构建的那个“大东西”。
Slices 是可以独立演示的垂直能力。不是“实现数据库层”(水平切分),而是“用户可以注册并登录”(垂直切分)。每个 slice 都有一句演示句:"完成之后,用户可以 ___。" 如果你不能在空格里填入一个人类能观察到的结果,那这个 slice 的范围就划错了。
Tasks 是上下文窗口大小的工作单元。一个 task 必须能装进一个代理会话里。装不下,就拆成两个 task。这是一条铁律,因为一旦违反,代理就会开始失去一致性——他们工作时间太长,早期决策被压缩掉了;上下文被几十次旧工具调用污染;模型的推理质量开始退化。
这个层级不只是为了组织。每一层都有明确的产物、明确的验证标准,以及明确的压缩机制,让下游工作能被告知必要信息,同时不把上下文撑爆。
边界图:在实现前先进行接口思考
这可能是单一影响最大的规划功能。
当一个 milestone 被规划时,每个 slice 都会声明自己产出什么,以及从上游 slice 消费什么。不是模糊地说,而是具体到:函数、类型、接口、端点,以及它们的名字。
S01 → S02
Produces:
- types.ts → User, Session, AuthToken (interfaces)
- auth.ts → generateToken(), verifyToken(), refreshToken()
Consumes: nothing (leaf node)
S02 → S03
Produces:
- api/auth/login.ts → POST handler
- middleware.ts → authMiddleware()
Consumes from S01:
- auth.ts → generateToken(), verifyToken()
这会强迫代理在写实现之前先考虑接口。当 slice 3 在规划时,它不需要猜 slice 1 做了什么——边界图会清楚写出可用的东西,而规划步骤还会验证上游 slice 是否真的产出了边界图所宣称的内容。
不再出现“slice 3 需要一个 slice 1 根本没导出的函数”。不再对“到底有什么东西存在”做默认假设。契约是显式的,而且会被检查。
LLM / 确定性代码的分工
这是核心的架构原则,也是这个系统 token 高效且可靠的主要原因。
规则是:如果你能写出一个 if-else 并且每次都能正确处理,那它就必须是确定性的代码——而不是 LLM 推理。模型花在机械操作上的每一个 token 都是浪费,同时还会引入新的失败模式。
确定性代码负责什么:
- 所有 git 操作(分支、检查点、提交、squash 合并、回滚)
- 所有状态迁移(下一任务、完成任务、完成 slice、推进)
- 文件解析与格式化(读取和写入所有 markdown 工件)
- 目录脚手架(创建正确的文件夹和文件,并写入正确的 frontmatter)
- 状态推导(从磁盘上的文件重建当前进度)
- 上下文组装(加载摘要、预算 token、丢弃旧上下文)
- 静态验证(检查文件是否存在、导出是否存在、导入是否连通、是否存在 stub)
LLM 负责什么:
- 将范围拆分为 slices(架构判断)
- 将 slices 拆分为 tasks(范围判断)
- 编写 must-haves(理解哪些可观察结果重要)
- 和用户讨论灰色地带(解释意图)
- 在 research 阶段侦察代码库(判断相关性)
- 诊断验证失败(溯因推理)
- 写摘要(压缩发生了什么,以及为什么重要)
- 真正去写代码
LLM 做判断工作。确定性代码处理其他一切。代理不会去构造 git 命令;不会去解析 markdown 来判断下一个 task 是什么;不会去格式化 frontmatter。它只需要调用一个工具,得到结果,然后继续做创造性的工作。
两个工具暴露了整个确定性层:gsd_manage(18 个用于状态、git、脚手架、上下文的动作)和 gsd_verify(4 个用于静态验证的动作)。一次工具调用就能替代原本需要模型推理的 5–10 次 bash/read/edit 调用。
上下文裁剪:每个 Task 都拥有新鲜窗口
这彻底改变了多任务的可靠性。
每个 task 都会在对话里注入一条不可见的锚点消息。每次调用 LLM 之前,一个上下文钩子会把消息历史裁剪回当前 task 的锚点。
这在实践中意味着:task 5 看不到 task 1–4 的 40 次工具调用;看不到失败尝试、中间读取、之前的调试。它得到的是一个干净的上下文窗口,里面包含它的任务计划、相关的上游摘要,其它都没有。
没有这个机制,就会出现上下文腐烂(context rot)——多任务代理工作的无声杀手。到了一个 slice 的第 3 或第 4 个 task,上下文窗口会被早期 task 的陈旧工具输出塞满:四个 task 之前读过、但之后已经被重构的文件内容;已经修好的问题留下的调试痕迹;几百行早已不相关的构建与测试终端输出。模型不知道哪些是当前的、哪些是过时的——对它来说都只是窗口里的 token。于是它开始基于过期信息做决定:引用已改名的变量;沿用已重构代码的旧模式;回避曾经失败的方案,但那些失败原因可能已经不复存在。随着上下文里的信噪比崩塌,推理质量会持续下降。
这也是为什么大多数代理系统在连续执行到第 3–4 个 task 时就撞墙:不是模型变笨了,而是上下文被毒化了。每个新 task 都拖着之前探索、死胡同和中间状态的“尸体”一路前行。
锚点裁剪能彻底消除上下文腐烂。每个 task 都是干净窗口:上下文里只有当前任务计划与精心挑选的上游摘要。没有陈旧读取,没有旧工具调用,没有累积噪声。task 7 拥有和 task 1 一样的上下文质量。
上下文注入:零发现调用
在一个 task 开始之前,系统会预先组装代理所需的一切:
- 任务计划(目标、步骤、must-haves)
- 依赖 slice 的压缩摘要
- milestone 级别的上下文与决策
- 若是从中断中恢复,则注入 continue-here 数据
这些都会自动注入。代理不需要 grep 项目结构,不需要 read 状态文件来弄清自己在哪,也不需要搜索之前的 slices 做了什么。如果它做了这些,那说明上下文组装坏了——那是 bug,不是工作流。
目标是零发现调用。代理花在“我在哪、有什么、做过什么决策”上的每一个 token,都是没有花在真正实现上的 token。
分形摘要:可扩展的记忆
当一个 task 完成时,代理会写一份结构化摘要:构建了什么、做了哪些关键决策、修改了哪些文件、建立了哪些模式、下游需要知道什么。
当一个 slice 完成时,task 摘要会压缩成 slice 摘要。完成足够多的 slices 之后,slice 摘要再压缩成 milestone 摘要。每一层都包含向下钻取到更细层级的路径,以便需要时获得更多细节。
当你在规划 slice 6 时,你不需要加载 slices 1–5 的 15 份 task 摘要。你只需要加载一份 milestone 摘要——可能 200 行左右——其中包含关键信息:构建了什么、可用的东西有哪些、应该遵循哪些模式、哪些决策已经锁定。
注入到上下文里的摘要 token 预算上限约为 ~2500 tokens。如果依赖链太长,最旧、最不相关的摘要会优先被丢弃。milestone 级摘要优先于 slice 级,slice 级优先于 task 级。
一条关键规则:永远不要总结“总结”。每一层摘要都必须从下一层摘要 + 实际代码状态重新生成。slice 摘要来自 task 摘要,而不是来自某个旧 slice 摘要的再压缩版本。这可以避免反复压缩压缩文本带来的信息损失叠加。
验证:从目标倒推,而不是按任务顺推
“所有步骤都做了”不是验证。验证要检查实际结果。
每个 task 都会定义 must-haves——不是步骤清单,而是可观察的标准:
Truths 是必须为真的行为:"用户可以用邮箱和密码注册。" "登录返回一个 JWT token。" "CLI 把结果输出到 stdout。" 这些要求代理去真正运行命令、检查浏览器行为或读取输出,来确认结果。
Artifacts 是必须存在且有真实实现的文件:src/lib/auth.ts——JWT 辅助方法,至少 30 行,导出 generateToken 与 verifyToken。不是“auth.ts 存在”,而是“auth.ts 存在、内容足够像真的实现、并且确实导出了应有的函数”。
Key links 是工件之间的连线:login/route.ts 从 auth.ts 导入 generateToken;middleware.ts 导入 verifyToken。这能抓住最常见的代理失败模式:文件彼此独立存在,但实际上没有连起来。
静态验证会用确定性方式检查这些:文件是否存在、行数、导出检测、导入连线、以及 stub 检测。stub 检测会扫描 TODO 注释、FIXME 标记、return null、return {}、console.log 占位、硬编码空响应。一个只返回空对象的 8 行文件是无法通过的。
在静态检查之外,还有一个四层验证阶梯:
- Static —— 文件存在、导出存在、连线连通、无 stubs
- Command —— 测试通过、构建成功、lint 干净
- Behavioral —— 浏览器流程可用、API 响应正确
- Human —— 只有当代理确实无法自行验证时,用户才介入检查
每个 task 会选择它能触达的最强层级。代理不会去让人类检查那些它用 curl 命令就能验证的东西。
Discuss 阶段:先对齐再行动
这是系统里最被低估的功能之一,也是 GSD 能在很少人工干预下运行却不跑偏的原因。
大多数 AI 编码代理的核心问题是:你说“做个鉴权”,它们立刻开始写代码。它们在前 5 分钟就做了 30 个决策——会话存储 vs JWT、要不要邮箱验证、OAuth vs 只支持密码、跳转行为、错误信息格式——而你直到看到最终结果才知道它们选了哪些。
GSD 把讨论变成一等公民阶段。在开始规划之前,代理会阅读范围,找出灰色地带——那些存在多种合理方案、而你的偏好确实重要的地方——并就这些对你进行访谈。不是泛泛地问。它会围绕具体决策生成具体问题,并给出结构化选项:"会话处理:(a) 带刷新轮换的 JWT,(b) 基于 Redis 的服务端会话,(c) 你来定。" 它是思考伙伴,不是清单式的面试官。
让这一切有效的关键行为包括:
它会顺着你的能量走。你强调什么,它就深入什么。如果你花时间谈错误处理体验,它就会更深入地问相关问题。它不会机械地按固定列表推进。
它会挑战含糊表达。“做简单点”会被反问:怎么简单?对用户简单?对实现简单?对未来扩展简单?代理不会接受模糊答案,因为模糊输入会产生发散输出。
它会把抽象变具体。“带我走一遍怎么用。” “屏幕上具体长什么样?” “失败时会发生什么?” 这些问题会迫使你在写一行代码之前就把事情讲清楚。
范围护栏会防止漂移。如果你提出一个更适合放到另一个 slice 的功能,它会把想法记录为延期并拉回焦点:"听起来这是一项新能力——我先记下来以后做。现在我们先专注于鉴权流程。"
产出是一个 context.md 文件——结构化记录每个决策以及你的理由。这个文件会被注入到所有下游工作:规划、执行、验证,全部都会用到。当代理在实现 slice 2 的 task 4 时,它仍然拥有 discuss 阶段的决策上下文。它不会重新争论;也不会因为忘了你说过什么而默默做出不同选择。决策被锁定,并贯穿整个流水线。
这就是为什么“几乎无需看管”的执行成为可能:你在 10 分钟对话里前置完成对齐,之后每个 task 都会自动继承这些决策。相反的做法——在实现过程中不断打断代理纠偏——在 token 和时间上都要昂贵得多。
Research 阶段:先看再跳
这也是可选的。在规划一个 slice 之前,代理可以侦察代码库与相关的库文档。
产出是一份 research.md,包含两个部分,能避免最昂贵的错误:
Don’t Hand-Roll(不要自己造轮子)会指出那些看起来简单、但已有现成解决方案的问题,比如:“不要自己实现 JWT 验证——用 jose。”
Common Pitfalls(常见坑)会记录:哪里会出错、为什么、如何避免、以及预警信号。这些来自库文档、代码库模式与已知失败模式。
这些 research 会被注入规划阶段,因此 task 拆分与 must-haves 会考虑真实世界约束,而不是理想化假设。
Git 策略:每个 Slice 一个分支 + Squash 合并
每个 slice 使用自己的 git 分支。在这个分支上,每个 task 在开始前都会创建一个检查点提交,在验证通过后再做一次正式提交。当 slice 完成后,这个分支会以 squash 的方式合并回 main,变成一个干净的提交。
main 看起来就像一个变更日志:
- feat(M001/S06): verification + summarization + UAT
- feat(M001/S05): task execution + context pruning
- feat(M001/S04): milestone and slice planning commands
- feat(M001/S03): extension scaffold and command routing
- feat(M001/S02): state machine + deterministic operations
- feat(M001/S01): types + file I/O + git operations
一个 slice 一个提交。可以单独回滚。分支会保留每个 task 的历史——git log、git bisect、git blame 都可以在更细粒度的提交上工作。
回滚很直接:
- 坏 task → 在分支上
git reset回检查点 - 坏 slice → 在 main 上回滚那一个 squash 提交
- 合并后 UAT 才发现问题 → 修复任务放到一个
-fix分支上,squash 合并成fix(M001/S01): what was fixed
用户不需要运行任何 git 命令。代理通过确定性工具调用处理所有分支、提交、合并与归档。
Continue-Here:在中断中生存
上下文窗口会结束。会话会超时。用户会按 Ctrl+C。系统会处理这一切。
如果一个 task 被中断——因为压缩、会话结束或手动停止——系统会写一个 continue 文件,捕获:
- 已经完成了什么
- 还剩什么要做
- 任务中做出的决策(避免下一会话重新争论)
- “氛围”——哪里比较棘手、要注意什么
- 恢复时的第一步应该做什么
一个全新的会话会读取这个文件,加载任务计划,把它们注入上下文,然后从中断点继续。continue 文件在恢复时会被消费掉——它是临时的,不是永久记录。
这个机制还挂接在 Pi 的压缩事件上,所以如果运行时自动压缩对话,压缩发生之前会自动写出 continue 文件,不会丢失工作。
UAT:自动生成的“信任但验证”文档
任何使用 AI 编码代理的人都应该被这个问题困扰:当代理说“完成了”,你到底怎么知道?
你可以读代码。你可以检查文件存在。但对大多数人而言,真正的问题是:它真的按我说的方式工作吗?我能去用它,并看到我被承诺的那个东西吗?
GSD 会自动生成这些。每当一个 slice 完成,系统就会产出一份用户验收测试(User Acceptance Test)脚本——一份人类可读的文档,告诉你如何精确验证构建结果。不是事后补的,也不是你要开口要的;它被内置在完成流程里。完成一个 slice,就得到一份测试脚本。
这些不是实现细节。不是“验证 generateToken 返回有效 JWT”——自动化验证已经检查过了。UAT 关注的是你(人类)能观察到的结果:
Test: Sign up flow
Do:
- Open http://localhost:3000/signup
- Enter "test@example.com" in the Email field
- Enter "password123" in the Password field
- Click "Sign Up"
Expected:
- Page redirects to http://localhost:3000/dashboard
- Header shows "Welcome, test@example.com"
- Refreshing the page keeps you logged in
每一步都是可复制粘贴的命令或明确的 UI 操作。每个预期结果都描述你应该看到什么——不是“应该能用”,而是具体文字、具体 URL、具体行为。你不该再需要猜“命令是什么?”“我该去哪一页?”“成功长什么样?”
这些测试来自 slice 的演示句与 must-haves,并与 task 摘要里“实际构建了什么”交叉对照。它们是“代理说完成了”和“我亲眼看到它真的完成了”之间的桥梁。
更关键的是,UAT 不会阻塞。代理写完测试脚本后会立刻进入下一个 slice。你可以在任何方便的时间测试——在 slices 之间、在一个 milestone 末尾,随你。如果你发现问题,告诉代理,它会创建修复任务。无需等待,无需仪式。就是一份文档,随时等你来验证。
在项目任意时刻,你都有每个已完成 slice 的 UAT 文件。这是一条自动化的纸面轨迹:"这就是构建了什么,以及如何证明它真的存在。" 这不仅对信任有帮助,也对上手、演示、以及三周后回忆“这个东西到底干嘛的”很有用。
为什么它有效
关键洞见在于:大多数导致 AI 编码代理不可靠的因素,并不是模型写代码的能力——而是周边的一切:状态管理、上下文污染、连续性丢失、git 机械操作错误、验证只检查过程不检查结果、摘要在层层压缩中丢信息。
GSD 2.0 把这些都变成确定性的。模型负责写代码和做判断;其余一切——状态迁移、文件管理、git 操作、上下文组装、静态验证——都由 TypeScript 处理,要么正确工作,要么抛出清晰错误。
结果就是一个代理,它:
- 每个 task 都能获得新鲜且相关的上下文窗口
- 从不把 token 浪费在机械操作上
- 产出可验证的结果,而不只是完成了清单
- 透明地跨越中断与会话边界
- 自动维护干净且可回滚的 git 历史
- 通过压缩扩展记忆,而不是一遍遍重读一切
一切都以磁盘上的 markdown 文件为基础。没有数据库。没有外部服务。只有文件和 git。
GSD