get-shit-done 为何放弃"安装期模板渲染 Agent 定义":一次基于确定性、Token 成本与补丁迁移风险的架构决策复盘
导读
本文围绕 get-shit-done(GSD)仓库中一份以 wontfix 关闭的技术决策记录(.out-of-scope/agent-template-rendering.md)展开,剖析"把 agents/*.md 中受配置门控的提示词文本抽成模板、在安装期与 .planning/config.json 变更时渲染"这一提案(#2758)为何被否决。你将看到 GSD 团队如何用"编排期确定性嵌入(orchestrator-time embedding)"替代"LLM 推断期解释条件分支",理解 workflow.* 开关的既定约定(缺失即启用)、gsd-sdk query config-get 的真实调用链、#2279 先行先例,以及一套"可复开条件(re-open criteria)"式的架构评审方法论——对需要向 Agent 提示工程与配置工程相结合的代码库做架构取舍的读者,具有直接的参考价值。
一、背景:GSD 的 Agent 定义与配置门控机制
get-shit-done 将大量行为逻辑承载在人类可读的提示词文档中:agents/*.md 定义各类角色 Agent(executor、planner、verifier、debugger 等),get-shit-done/workflows/ 与 get-shit-done/templates/ 定义分阶段工作流,而 .planning/config.json(其参考文档见 get-shit-done/references/planning-config.md)记录项目级规划配置。
当同一份 Agent 提示词需要同时服务多种运行形态时,自然会产生"这段文字该不该出现"的分支需求。目前仓库内的做法是在提示词中写条件散文(conditional prose),例如:
workflow.use_worktrees:决定 executor 是否运行在隔离 git worktree 中(默认true,见 docs/CONFIGURATION.md 与 get-shit-done/references/planning-config.md);workflow.verifier:控制执行后是否针对阶段目标做验证(默认true,见 docs/CONFIGURATION.md),并在 docs/FEATURES.md 中以需求REQ-POSTVER-04固化"系统必须可通过workflow.verifier: false禁用"。
这类条件分支由 LLM 在推理期读取并解释。提案 #2758 认为这是不确定性(non-determinism)的暴露面,主张将其搬到编译期:把配置门控的散文抽到 agents/templates/*.md.tmpl,在安装时及 .planning/config.json 写入后,通过新的 gsd-sdk agents render 子命令渲染出最终 agents/*.md,使条件分支由确定性代码解析而非 LLM 解释。
二、提案的三个收益主张及其边界
.out-of-scope/agent-template-rendering.md 原文列出的三个收益主张如下,它们也是整场评审的论证靶心:
- 按功能关闭程度等比例削减 Token——被禁用的功能对应散文不再进入上下文;
- 确定性的功能门控——"结构上不可能违反"优于"测试后才可能发现问题"(impossible-by-construction vs. test-for);
- 面向贡献者的门控单一事实来源(single source of truth)——模板是唯一需要维护的开关文本。
提案同时引用 PR #2279(Codex/OpenCode 模型配置在安装期嵌入 Agent 文件)作为"编译期嵌入"的直接先例。
需要强调的是:这三点主张本身没有被全盘否定,被否定的是"引入模板渲染子系统"这一实现路线。从 CHANGELOG.md 可以确认,#2279 确实落地过——Codex/OpenCode model overrides — Embed model_overrides in agent files (#2279)(见 CHANGELOG.md),说明"把运行态配置确定性嵌入提示词"在 GSD 中是已被验证有效的模式;争论点始终是:同一问题是否需要第二套机制。
三、为什么被否决:四条技术理由
3.1 确定性主张是"理论上的",而非"被观测到的"
提案最强论点是"Agent 散文中的配置分支 = 确定性失效面"。但评审指出,当前代码库中的实际模式已经做了大量缓解:
gsd-executor中的 worktree 分支并非由 LLM 解释,而是由 bash 中gsd-sdk query config-get确定性解析。在 agents/gsd-executor.md 可以看到同类调用:AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false");- "Skip if
workflow.Xisfalse" 这类散文短小、稳定,且遵循统一的"缺失键 = 启用"约定(missing key = enabled); - 仓库没有记录过 LLM 因这类散文而误关已启用检查或误开已禁用检查的案例。
也就是说:用一个理论上的失效面,去交换一个真实且高风险的补丁迁移面(见 3.4),在评审看来不是合理交易。记录中明确写道:曾要求提案者提供实证文档,但未获提供。
3.2 Token 浪费量小且有上限
提案称"跨多阶段里程碑的真实开销"可观,但评审依据仓库实测存量给出了相反的规模判断:
- Agent 文件中的
workflow.*开关引用大约 5 处; - 全库"Skip if"条件散文约 20 条,绝大多数仅 1–2 句。
在 agents/ 目录中检索 Skip if 可以验证这一点:命中集中在 agents/gsd-plan-checker.md(4 处)、agents/gsd-phase-researcher.md、agents/gsd-debugger.md、agents/gsd-ui-researcher.md 等文件。评审进一步指出:"多阶段里程碑真实支出"这一说法没有对照 gsd-context-monitor 的输出做基线测量(对应实现见 hooks/gsd-context-monitor.js)。在缺少测量基线的前提下,~20 条短条件句的节省上限不足以支撑一个需要"CI 强制模板/生成产物分离"的全新子系统。
3.3 确定性门控需求已被既有机制满足
对于真正需要确定性解析的场景——模型选择、推理强度、worktree 模式——PR #2279 已经确立了编排期配置嵌入的做法,即在工作流层把配置值嵌入到子 Agent 的提示词中,而不是让子 Agent 自行读配置猜测。
在 get-shit-done/workflows/execute-plan.md 的 Pattern A 中可以清楚地看到这条既有路径的形态:编排器通过 config-get workflow.use_worktrees 读取开关,只有当其不为 false 时才附加 isolation="worktree" 参数,否则走主工作区顺序执行。换言之,开关在子 Agent 诞生之前就被编排器解析完毕,LLM 根本没有机会"误读"。
因此提案自身列出的 "Alternative #1(延续编排器嵌入模式)"虽因"Agent 内部条件句应归属 Agent 层"的理由被驳回,但其背后的诉求——确定性、低 Token 成本——完全可以通过沿既有路径逐开关扩展 #2279 得到满足。评审的核心担忧是:在编排器嵌入之上再叠加模板层,意味着同一问题被两套机制共同拥有,而提案没有给出任何分区规则(partition rule),被要求补充时也未回应。
3.4 补丁迁移风险与收益严重不成比例
这是压倒性的否决理由。GSD 的更新机制依赖 gsd-local-patches/ 的三方合并迁移(/gsd-reapply-patches,对应测试见 tests/reapply-patches.test.cjs、tests/bug-2969-verify-reapply-patches.test.cjs 等);一旦把 agents/*.md 从"手写散文"改成"渲染产物",任何贡献者的既有本地补丁都面临 rebase 逻辑失效的风险——提案者自己在记录中也承认这是"整个改动中风险最高的部分"。
更关键的是:这笔风险纯粹来自内部重构——用户可见的功能面没有变化,换取的却只是"未经测量的 Token 节省 + 理论上的确定性提升"。这被明确判定为 wrong trade(错误的交易)。
记录还顺带评估了缩小范围后的变体 Alternative #5(仅对新安装生效,推迟迁移):它虽然规避了三方合并迁移的具体风险,但仍然会为一个收益未测量、且可被 #2279 既有路径吸收的问题,多交付一套并行机制,因此同样不可取。
四、复开条件:什么证据能推翻这次否决
这份记录最值得借鉴的是它把"否决"写成了可证伪的结论,而不是封闭的结论。以下任一条件满足即可重新讨论:
- 量化的 Token 差异:贡献者通过
gsd-context-monitor对照"全部开关关闭"的代表性配置给出实测 Token 增量,且该增量显著大于沿 #2279 编排期嵌入路径逐开关扩展所能达到的效果; - 真实的误解释案例:记录现存的某个开关条件被 LLM 误解的实例(例如 executor 无视
workflow.use_worktrees: false、verifier 在workflow.verifier: false时仍运行),而不是推测的失效模式; - 清晰的分区规则:明确界定"编排期嵌入(#2279)"与"安装期模板层"各自的职责边界,使两套机制不重叠。
这套"先要证据、再谈方案;保留可复开入口"的处理方式,本身即是处理架构提案的高质量范本。
五、给贡献者的实践启示:今天如何新增一个确定性开关
如果你是一名希望为 GSD 增加新功能开关的贡献者,不要试图引入新的模板渲染层,而应沿评审指明的既有路径操作:
- 在规划配置中声明开关,参考 get-shit-done/references/planning-config.md 中
workflow.use_worktrees(默认true)与workflow.verifier的条目写法,给出类型、默认值、语义说明; - 在编排层做确定性解析:工作流脚本通过
gsd-sdk query config-get workflow.<key>读取(该子命令由 get-shit-done/bin/lib/core.cjs 的 config-get/config-set 区段实现,SDK 侧的configGet()见 sdk/src/gsd-tools.ts),在 spawn 子 Agent 之前决定是否注入相关指令; - 仅在 Agent 内部保留简短的"Skip if"散文,且遵循统一约定——例如"缺失键 = 启用",见
workflow.verifier、workflow.use_worktrees在 docs/CONFIGURATION.md 与 docs/FEATURES.md 中的记录; - 为"关闭态"补测试:仓库既有大量此类回归测试可作模板,例如 tests/config.test.cjs 中
config-set workflow.use_worktrees false后断言配置生效、tests/bug-2772-gitmodules-path-intersection.test.cjs 中验证子模块边界冲突时提示"re-run with workflow.use_worktrees=false",以及 tests/worktree-cleanup.test.cjs 中"use_worktrees为 false 时跳过清理"的分支覆盖。
此外,workflow.use_worktrees: false 还承担着跨运行时兼容语义:例如 get-shit-done/workflows/execute-phase.md 在处理不支持 worktree 隔离的运行时(如 Codex)时明确报错并建议"设置 workflow.use_worktrees=false 或改用支持 worktree 隔离的运行时";get-shit-done/workflows/quick.md 则在检测到子模块边界时建议用户"以 workflow.use_worktrees=false 回退到主工作区顺序执行"。这说明单一开关 + 确定性读取的设计足以承载复杂的运行时分流,无需为此复制一份模板渲染管线。
六、结论与关联资料索引
agent-template-rendering 决策的核心结论可以压缩为一句话:当一个问题的收益未经测量、而其实现要为一个已被证明可行的机制再造一套平行子系统、并承担纯内部重构带来的补丁迁移风险时,正确的工程决策是拒绝它,并沿既有机制逐步演进。
后续读者可在以下位置继续深挖本决策所涉及的证据链:
- 决策原文:.out-of-scope/agent-template-rendering.md;
- 编排期嵌入先例:#2279 对应的模型覆盖嵌入记录见 CHANGELOG.md;
- 确定性读取机制:
gsd-sdk query config-get在工作流中的真实用法见 get-shit-done/workflows/execute-plan.md 与 agents/gsd-executor.md; - 开关语义文档:
workflow.use_worktrees、workflow.verifier见 docs/CONFIGURATION.md、docs/FEATURES.md、get-shit-done/references/planning-config.md; - 补丁迁移风险面:相关回归测试见 tests/reapply-patches.test.cjs 等;
- 上下文量化工具:
gsd-context-monitor的钩子实现见 hooks/gsd-context-monitor.js; - 共享样板抽取背景:v1.37.0 版本说明中提到的共享样板提取(mandatory-initial-read、project-skills-discovery 的 reference 文件),对应目录 get-shit-done/references/ 与 get-shit-done/workflows/。
换言之,这套架构并不拒绝"编译期确定化"这一方向——它只是拒绝在已被证明有效的路径之外另起炉灶;若日后有实测数据证明收益显著超出既有路径所能承载的范围,Re-open criteria 已为这场对话预留了明确、可操作的回归通道。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00