get-shit-done 按阶段类型选择模型:.planning/config.json 中 `models` 块的原理与实战
models 是 get-shit-done(GSD)在 .planning/config.json 中新增的一个配置块,用于按"阶段类型"(phase type)统一选择 Agent 使用的模型档位。它让用户不必逐个记住 30 余个子 Agent 的名称,就能用两行配置表达"规划用 Opus、其余用 Sonnet"这类策略。本文围绕这一功能,讲解其配置格式、六个命名槽位、完整的解析优先级链、与运行时(Claude / Codex 等)的交互细节,并结合仓库源码与测试用例给出可验证的依据。
为什么需要一层"阶段级"模型配置
在此之前,GSD 提供两级模型控制手段:
model_profile:全局档位策略,取值为quality、balanced、budget、adaptive、inherit,一次作用于所有 Agent(见 docs/CONFIGURATION.md);model_overrides.<agent>:针对单个 Agent 的精确覆盖,可以填入完整模型 ID(如openai/gpt-5)。
问题在于:子 Agent 多达数十个(agents/ 目录下可见 planner、executor、researcher、verifier 等各类角色),普通用户很难记住每一个 gsd-* 名字。如果只想表达"重度脑力活(规划、讨论、写码)用好模型,轻量校验用便宜模型",逐 agent 配置显然过重。
于是 #3023 提出、PR #3030 合入的 models 块作为一层新的中间解析层登场:它按阶段类型分档,位于"per-agent model_overrides"与"model_profile 档位表"之间。本功能的发布说明见 .changeset/per-phase-type-models.md,配置项说明见 docs/CONFIGURATION.md。
models 块速览:两行配置完成分阶段选型
在项目根目录的 .planning/config.json 中新增一个 models 对象即可。以下配置表达了变更集文档中的经典诉求 —— "Opus for planning, Sonnet for the rest":
{
"model_profile": "balanced",
"models": {
"planning": "opus",
"research": "sonnet"
}
}
只覆盖默认档位里"不一样"的两行,其余阶段自动回落到 model_profile: "balanced" 决定的值,完全不需要了解 Agent 分类学。更完整的六槽位写法如下:
{
"model_profile": "balanced",
"models": {
"planning": "opus",
"discuss": "opus",
"execution": "opus",
"research": "sonnet",
"verification": "sonnet",
"completion": "sonnet"
}
}
六个命名槽位
models 块只接受下列六个阶段类型键(测试断言见 tests/feat-3023-model-phase-types.test.cjs):
| 槽位 | 含义 | 典型 Agent(来自 model-catalog) |
|---|---|---|
planning |
计划 / 路线图阶段 | gsd-planner、gsd-roadmapper、gsd-eval-planner、gsd-framework-selector、gsd-pattern-mapper |
discuss |
需求讨论 / 假设分析阶段 | gsd-assumptions-analyzer |
research |
研究与调研阶段 | gsd-phase-researcher、gsd-project-researcher、gsd-codebase-mapper、gsd-research-synthesizer、gsd-domain-researcher 等 |
execution |
执行 / 编码 / 调试阶段 | gsd-executor、gsd-debugger、gsd-code-fixer、gsd-doc-writer、gsd-debug-session-manager |
verification |
验证 / 评审 / 审计阶段 | gsd-verifier、gsd-plan-checker、gsd-code-reviewer、gsd-integration-checker、gsd-security-auditor 等 |
completion |
收尾阶段 | 作为合法枚举保留;当前 catalog 尚无映射 Agent |
注意:Agent 具体属于哪个阶段类型,以 sdk/shared/model-catalog.json 的
agents表为准,每个 Agent 都有唯一的phaseType字段。
取值只能是档位别名
每个槽位的取值必须是四个档位别名之一:
| 取值 | 含义 |
|---|---|
opus |
顶级档位(Claude 生态即 Opus 系,其它运行时映射到各自旗舰模型) |
sonnet |
标准档位 |
haiku |
轻量档位 |
inherit |
跟随会话模型(与 model_profile: "inherit" 语义一致) |
models.* 故意只接受档位别名、不接受完整模型 ID。完整 ID 属于 model_overrides 的职责范围(见下节"安全防护")。
数据模型与"Agent → 阶段类型"映射从何而来
models 块不是写死在解析函数里的硬编码。运行时从 model-catalog.json 动态加载三张关键结构:
phaseTypes:合法阶段类型枚举(六个);agents:每个 Agent 的 golden / balanced / budget 档位、phaseType归属与routingTier;runtimeTierDefaults:各运行时(claude / codex / gemini / qwen / opencode / copilot 等)对opus/sonnet/haiku的模型 ID 映射。
model-catalog.json 的加载与解析集中在 get-shit-done/bin/lib/model-catalog.cjs:它通过优先候选路径解析目录定位该文件(co-located 安装路径 → 源码仓库 dev 路径 → GSD_MODEL_CATALOG 环境变量覆盖),随后构造 MODEL_PROFILES(Agent → 各档位模型)、AGENT_TO_PHASE_TYPE(Agent → 阶段类型)与 VALID_PHASE_TYPES(合法阶段集合)并导出(model-catalog.cjs),再由 model-profiles.cjs 向外统一转发。
解析优先级:完整的四级分层链
models[phase_type] 生效后,某 Agent 最终模型的解析优先级(从高到低)如下,对应 resolveModelInternal 的实现(get-shit-done/bin/lib/core.cjs):
model_overrides[agent](最高):针对该 Agent 的精确覆盖永远优先,通常承载完整模型 ID;models[phase_type](本次新增):该 Agent 所属阶段类型的档位;model_profile档位表:由全局 profile 推导出的每 Agent 档位;- 运行时默认:以上都没有命中时,Claude 原生默认或对应 runtimes 的 fallback。
测试 tests/feat-3023-model-phase-types.test.cjs 对关键分支做了结构化断言,例如:
- 仅配置
models: { research: "haiku" }且model_profile: "balanced"时,gsd-phase-researcher、gsd-codebase-mapper解析为haiku(阶段槽位生效),而未设置槽位的gsd-planner回落为 balanced 档位的opus; - 同时配置
model_overrides: { "gsd-phase-researcher": "opus" }时,per-agent 覆盖在该 Agent 上胜出(仍为opus),但同属 research 的其它 Agent 继续吃到haiku; model_profile: "quality"(本应让 research 变成opus)会被models.research = "haiku"压过,验证了"阶段槽位 > profile"的优先级。
在 core.cjs 中,层级 1 通过提前读取 config.model_overrides?.[agentType] 短路实现(core.cjs);层级 2 依据 AGENT_TO_PHASE_TYPE[agentType] 反查该 Agent 的阶段类型,再读取 config.models[phaseType](core.cjs)。
安全防护:非法值一律回落,不污染解析链
models 块内置了两道防线,均有对应测试:
其一,未知档位别名自动回落。 只有命中 opus / sonnet / haiku / inherit 四个合法别名的值才被采纳;像 models.research = "haiku3" 这种拼写错误会被 VALID_TIERS 守卫拦截,回落到 profile 推导值,避免脏值渗入运行时解析链(core.cjs)。测试验证拼写错误 haiku3 最终按 balanced 档位解析(feat-3023-model-phase-types.test.cjs)。
其二,完整模型 ID 被拒收。 在 models.<phase_type> 中写入 openai/gpt-5 这类完整 ID 会被拒绝并回落到 profile——完整 ID 应当放进 per-agent 的 model_overrides(feat-3023-model-phase-types.test.cjs)。
此外,配置键本身受到 schema 校验约束(isValidConfigKey,见 config-schema.cjs 与 feat-3023-model-phase-types.test.cjs):
models.planning等六个槽位都是合法配置键;- 未知阶段类型(如
models.deployment)与把 Agent 名写进models.*(如models.gsd-planner)均被拒绝; - 单独的
models(不带槽位)不是合法的细粒度 set 键——整块 JSON 需要直接编辑.planning/config.json。
命令行的表现可参考 docs/CONFIGURATION.md:
$ gsd config-set models.research sonnet # 合法
$ gsd config-set models.deployment opus
Error: 'models.deployment' is not a valid config key # 拒绝
与运行时(runtime)的交互:档位别名保持映射正确
models.* 之所以坚持只收档位别名,是为了让"运行时感知解析"(#2517 引入)保持正确。当用户在 .planning/config.json 显式设置非 Claude 的 runtime 时,档位别名会经由 runtimeTierDefaults 映射为对应运行时的真实模型 ID。以 sdk/shared/model-catalog.json 为例:
- Claude:
opus → claude-opus-4-7、sonnet → claude-sonnet-4-6、haiku → claude-haiku-4-5; - Codex:
opus → gpt-5.4(reasoning_effort: xhigh)、sonnet → gpt-5.3-codex(medium)、haiku → gpt-5.4-mini; - Gemini / Qwen / OpenCode / Copilot 等各有独立映射。
正因为 models.* 是档位而非硬编码 ID,同一份配置在 Claude、Codex、Gemini CLI 上都能得到各自生态的对应模型(docs/CONFIGURATION.md 明确说明此设计意图)。
reasoning_effort 与模型档位必须同源(#3030)
Codex 这类带推理档位的运行时,还要求 reasoning_effort 与模型源自同一个档位。resolveReasoningEffortInternal(core.cjs)会镜像 models[phase_type] 的解析结果:例如 runtime: "codex" + models.execution: "opus" 时,gsd-executor 不仅模型取 opus 档,推理档也会从 profile 推导的 sonnet/medium 纠正为 opus/xhigh,避免"模型已升级、推理档仍停留在原档"的错配(feat-3023-model-phase-types.test.cjs)。
该修复还覆盖了一个 CR Major 缺陷:model_profile: "inherit" 叠加 models.execution: "opus" 时,阶段覆盖必须胜出,而不是被 inherit 的早期短路吞掉(feat-3023-model-phase-types.test.cjs)。行为边界如下:
- 阶段槽位值为
inherit时 effort 返回null(inherit 无运行时条目); - 命中
model_overrides完整模型 ID 时 effort 同样返回null(交由 per-agent 处理); - Claude 运行时没有 reasoning_effort 概念,恒返回
null。
与其它层的组合
完整的模型解析还包含动态路由层(dynamic_routing,#3024,由 resolveModelForTier 处理,见 core.cjs)。总体从上到下为:model_overrides[agent](顶部逐 agent 例外)→ dynamic_routing(开启时按尝试次数升级档位)→ models[phase_type](阶段级覆盖)→ model_profile(基础档位表)→ 运行时默认。dynamic_routing 默认关闭,未开启时其行为与旧版完全一致(docs/CONFIGURATION.md)。
向后兼容性:无 models 配置行为完全不变
models 块是纯增量特性,变更集文档与测试共同保证其向后兼容:
- 完全不写
models块:解析走原有 profile 链路,行为与今日一致; - 写空对象
models: {}:等价于不配置(feat-3023-model-phase-types.test.cjs); - 任何一个槽位缺失时,该阶段内 Agent 自动按 profile 档位表回落,不会报错。
选型建议:三级配置怎么取舍
| 诉求 | 使用哪一层 |
|---|---|
| 全部 Agent 统一一套档位策略 | model_profile |
| 面向工作阶段的粗粒度调档("规划上 Opus") | models.<phase_type> |
| 精确指定某个 Agent,甚至给完整模型 ID | model_overrides[<agent>] |
适用场景示例(整理自 docs/CONFIGURATION.md):使用 OpenRouter / 本地 provider 时把 model_profile 设为 inherit 让所有 Agent 跟随会话模型;想只给执行阶段的 gsd-executor 等换模型时,就再加一条 models.execution;而"只把某个验证 Agent 换成特定模型"则用 model_overrides。整体原则是:能用阶段槽位表达的,不必下沉到 Agent 级;需要下沉到 Agent 级时,model_overrides 永远兜底。
如何验证
仓库在 tests/feat-3023-model-phase-types.test.cjs 中为该功能提供了完整的结构化测试套件(基于 node:test),覆盖:六个槽位枚举与 schema 校验、Agent→阶段类型映射完整性、per-agent 覆盖优先级、阶段覆盖对 inherit profile 的胜出、typo 与完整 ID 的回落、Codex reasoning_effort 同源一致性等。可在仓库根目录用 Node 内置测试运行器执行:
node --test tests/feat-3023-model-phase-types.test.cjs
若要亲手验证解析结果,也可以对照上文给出的 resolveModelInternal 优先级链,在一个临时目录写入带 models 块的 .planning/config.json,观察不同 Agent 的解析输出是否符合预期。
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 StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00