首页
/ get-shit-done 按阶段类型选择模型:.planning/config.json 中 `models` 块的原理与实战

get-shit-done 按阶段类型选择模型:.planning/config.json 中 `models` 块的原理与实战

2026-09-07 19:53:46作者:虞亚竹Luna

models 是 get-shit-done(GSD)在 .planning/config.json 中新增的一个配置块,用于按"阶段类型"(phase type)统一选择 Agent 使用的模型档位。它让用户不必逐个记住 30 余个子 Agent 的名称,就能用两行配置表达"规划用 Opus、其余用 Sonnet"这类策略。本文围绕这一功能,讲解其配置格式、六个命名槽位、完整的解析优先级链、与运行时(Claude / Codex 等)的交互细节,并结合仓库源码与测试用例给出可验证的依据。

为什么需要一层"阶段级"模型配置

在此之前,GSD 提供两级模型控制手段:

  • model_profile:全局档位策略,取值为 qualitybalancedbudgetadaptiveinherit,一次作用于所有 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-plannergsd-roadmappergsd-eval-plannergsd-framework-selectorgsd-pattern-mapper
discuss 需求讨论 / 假设分析阶段 gsd-assumptions-analyzer
research 研究与调研阶段 gsd-phase-researchergsd-project-researchergsd-codebase-mappergsd-research-synthesizergsd-domain-researcher
execution 执行 / 编码 / 调试阶段 gsd-executorgsd-debuggergsd-code-fixergsd-doc-writergsd-debug-session-manager
verification 验证 / 评审 / 审计阶段 gsd-verifiergsd-plan-checkergsd-code-reviewergsd-integration-checkergsd-security-auditor
completion 收尾阶段 作为合法枚举保留;当前 catalog 尚无映射 Agent

注意:Agent 具体属于哪个阶段类型,以 sdk/shared/model-catalog.jsonagents 表为准,每个 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):

  1. model_overrides[agent](最高):针对该 Agent 的精确覆盖永远优先,通常承载完整模型 ID;
  2. models[phase_type](本次新增):该 Agent 所属阶段类型的档位;
  3. model_profile 档位表:由全局 profile 推导出的每 Agent 档位;
  4. 运行时默认:以上都没有命中时,Claude 原生默认或对应 runtimes 的 fallback。

测试 tests/feat-3023-model-phase-types.test.cjs 对关键分支做了结构化断言,例如:

  • 仅配置 models: { research: "haiku" }model_profile: "balanced" 时,gsd-phase-researchergsd-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_overridesfeat-3023-model-phase-types.test.cjs)。

此外,配置键本身受到 schema 校验约束(isValidConfigKey,见 config-schema.cjsfeat-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-7sonnet → claude-sonnet-4-6haiku → claude-haiku-4-5
  • Codex:opus → gpt-5.4reasoning_effort: xhigh)、sonnet → gpt-5.3-codexmedium)、haiku → gpt-5.4-mini
  • Gemini / Qwen / OpenCode / Copilot 等各有独立映射。

正因为 models.* 是档位而非硬编码 ID,同一份配置在 Claude、Codex、Gemini CLI 上都能得到各自生态的对应模型(docs/CONFIGURATION.md 明确说明此设计意图)。

reasoning_effort 与模型档位必须同源(#3030)

Codex 这类带推理档位的运行时,还要求 reasoning_effort 与模型源自同一个档位resolveReasoningEffortInternalcore.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 的解析输出是否符合预期。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395