【免费下载链接】gsd-core
Git. Ship. Done - Core
导读
本文讲解 GSD-Core(Git. Ship. Done - Core)自 v1.41 引入的**按阶段类型选择模型(Per-Phase-Type Model Selection)**能力:通过.planning/config.json中的models配置块,你可以不记忆 30+ 个 Agent 的完整分类体系,直接用planning、research、execution、verification等阶段槽位粗粒度地调整每个阶段使用的模型档位。读完本文,你将掌握models.<phase_type>六个合法槽位、opus/sonnet/haiku/inherit四种取值、与model_overrides/model_profile/dynamic_routing组合时的五层解析优先级,并能从源码与测试层面理解其底层解析链路、校验规则与向后兼容保证。
该特性由 issue #3023 与 settings-management 文档,源码实现集中在 src/model-resolver.cts 与 src/model-catalog.cts,回归测试见 tests/model-resolver.test.cjs。
一、为什么需要按阶段类型选择模型
GSD-Core 的每个阶段由多个专用 Agent 协作完成,例如规划阶段由gsd-planner、gsd-roadmapper、gsd-pattern-mapper承担,验证阶段由gsd-verifier、gsd-plan-checker、gsd-code-reviewer等八个 Agent 承担。在引入本特性之前,调整模型档位只有两条路:
- 按 Agent 的
model_overrides:精确但啰嗦。你必须记住gsd-codebase-mapper属于 research、gsd-doc-writer属于 execution,才能写出针对单个 Agent 的覆盖; - 全局
model_profile:粗放且一刀切。一个档位策略作用于全部 Agent,无法表达"规划用强模型、执行用标准模型"这类典型成本分配诉求。
models配置块恰好填补两者之间的空档:以阶段类型为粒度(planning / research / execution / verification),无需学习完整 Agent 分类体系即可表达"规划阶段用 Opus、其余阶段用 Sonnet"这样的意图,代码量只要两行 JSON。
从源码结构看,src/model-catalog.cts 中的AGENT_TO_PHASE_TYPE正是把每个 Agent 静态映射到唯一阶段类型的真相来源(取自model-catalog.json中每个 Agent 的phaseType字段),VALID_PHASE_TYPES则是合法阶段槽位的集合,二者共同支撑了"阶段级调参"这一抽象。
二、配置项:.planning/config.json中的models块
2.1 配置键与存放位置
特性对应的配置键为models,位于项目的.planning/config.json(工作流/项目级覆盖路径由planningDir解析,可参考 src/planning-workspace.cts)。models是动态键模式(dynamic key pattern),合法槽位由 schema manifest 严格约束。
在 config-schema.manifest.json 的动态键模式中,可以找到该特性的校验规则:
{ "topLevel": "models", "source": "^models\\.(planning|discuss|research|execution|verification|completion)$", "description": "models.<planning|discuss|research|execution|verification|completion>" }也就是说,只有六个命名槽位被接受:planning、discuss、research、execution、verification、completion。任何其他阶段类型(如models.deployment)都会被 schema 拒绝(见下文"校验规则")。该 manifest 由 src/config-schema.cts 经 src/configuration.cts 加载,是isValidConfigKey校验与config-set命令的单一事实来源。
2.2 完整配置示例
沿用官方文档 docs/CONFIGURATION.md 中的示例,一个同时使用model_profile、models与model_overrides的完整配置如下:
{ "model_profile": "balanced", "models": { "planning": "opus", "discuss": "opus", "research": "sonnet", "execution": "opus", "verification": "sonnet", "completion": "sonnet" }, "model_overrides": { "gsd-codebase-mapper": "haiku" } }该配置的效果:所有 research 阶段 Agent 解析为sonnet,除了gsd-codebase-mapper被按 Agent 覆盖钉死为haiku;planning / discuss / execution 阶段全部为opus;verification 与 completion 为sonnet。
2.3 阶段类型 → Agent 映射表
models.<phase_type>的每个槽位会应用到一组 Agent。下表与 docs/CONFIGURATION.md 及特性文档 docs/features/per-phase-type-model-selection.md 保持一致:
| 阶段类型(Slot) | 归属 Agent |
|---|---|
planning | gsd-planner、gsd-roadmapper、gsd-pattern-mapper |
discuss | gsd-assumptions-analyzer |
research | gsd-phase-researcher、gsd-project-researcher、gsd-research-synthesizer、gsd-codebase-mapper、gsd-ui-researcher |
execution | gsd-executor、gsd-debugger、gsd-doc-writer |
verification | gsd-verifier、gsd-plan-checker、gsd-integration-checker、gsd-nyquist-auditor、gsd-ui-checker、gsd-ui-auditor、gsd-doc-verifier、gsd-code-reviewer |
completion | (保留给未来子 Agent,当前无映射) |
注意:discuss与completion两个槽位目前被 schema 接受,但尚无 Agent 映射到它们——今天设置它们是一个 no-op(不会报错,也不产生效果),这是为未来子 Agent 预留的向前兼容槽位(对应需求 REQ-PHASE-MODELS-03)。
从实现层面看,这张表不是手写的两份文档,而是由model-catalog.json中每个 Agent 的phaseType元数据统一驱动:src/model-catalog.cts 通过
export const AGENT_TO_PHASE_TYPE: Record<string, string> = Object.fromEntries( Object.entries(_catalog.agents).map(([agent, meta]) => [agent, meta.phaseType]) );生成映射,因此文档与代码不会漂移。测试 tests/model-resolver.test.cjs 中的#3023 phase-type schema测试组还专门断言:MODEL_PROFILES中每个 Agent 都必须有 phase-type 赋值,且赋值必须是六个合法槽位之一。
2.4 合法取值
models.<phase_type>只接受档位别名(tier alias),不接受完整模型 ID:
| 取值 | 效果 |
|---|---|
"opus" | 标准档位——运行时解析会将该阶段 Agent 映射到当前活跃运行时的 Opus 档模型 |
"sonnet" | 标准档位——映射到活跃运行时的 Sonnet 档模型 |
"haiku" | 标准档位——映射到活跃运行时的 Haiku 档模型 |
"inherit" | 该阶段 Agent 跟随会话模型,语义与model_profile: "inherit"一致 |
重要限制:如果你需要完整模型 ID(例如"openai/gpt-5"、"google/gemini-2.5-pro"),不要写在models.*里,而应使用按 Agent 的model_overrides。models.*刻意设计为"仅档位",目的是保证在 Codex / OpenCode / Antigravity CLI 等非 Claude 运行时上,运行时感知(runtime-aware)的档位映射依然正确——完整 ID 会破坏这种跨运行时可移植性。
该限制在源码中有对应的硬性守卫:computeProfileTier(src/model-resolver.cts)读取config['models'][phaseType]后,用VALID_TIERS.has(phaseTypeTier)校验——VALID_TIERS由model-catalog.json的adaptiveTierMap(heavy→opus、standard→sonnet、light→haiku)加'inherit'派生而来。因此,非法档位值会落入 profile 兜底解析,不会污染运行时档位解析链。
三、解析优先级:五层从上到下依次生效
models.<phase_type>处于解析链的第三层。完整的解析优先级(从高到低)为:
1. model_overrides[<agent>] ← 按 Agent;可写完整 ID;定向例外 2. dynamic_routing.tier_models[<tier>] ← 启用时生效(见 Dynamic Routing) 3. models[<phase_type>] ← 粗粒度阶段级档位(本特性) 4. model_profile(每个 Agent 一列) ← 全局档位策略 5. Runtime default ← 兜底五层自上而下组合:model_profile是基础档位;models[<phase_type>]在阶段层面覆盖之;dynamic_routing(启用时)在软失败时按尝试次数升级档位;model_overrides[<agent>]在最顶层雕刻按 Agent 的例外;当没有任何一层生效时,回落到运行时默认档位。
在 2.2 的示例中,五个 research Agent 全部解析为sonnet,唯独gsd-codebase-mapper被按 Agent 覆盖钉为haiku。dynamic_routing默认关闭——当enabled: false或整块省略时,本节行为与未引入该特性之前完全一致。
3.1 优先级在源码中的落地位置
resolveModelInternal(src/model-resolver.cts)严格按上述顺序实现:
- Step 1:读取
config['model_overrides'],命中即返回(Claude 运行时还会经mapClaudeOverrideForRuntime把完整 Claude ID 折叠回档位别名); - Step 2:
computeProfileTier(config, agentType)计算基础档位——其中正是读取config['models'][phaseType]并做VALID_TIERS校验、命中则直接返回的阶段级档位,未命中才回落到MODEL_PROFILES[agent][profile]; - Step 2.5:
model_policy预设(provider 中立策略)解析; - Step 3:非 Claude 运行时的运行时感知档位映射(
_resolveRuntimeTier); - Step 4 / 4.5 / 4.75:
resolve_model_ids: "omit"门、Claude 档位覆盖、dynamic_routing.tier_models查询; - Step 5:profile 兜底查询,最终输出档位别名(或
inherit)。
其中步骤 4.75 的注释明确引用了特性文档的优先级约定:"model_overrides永远优先;dynamic_routing.tier_models[<tier>]解析在models.<phase_type>与model_profile之上"。因此dynamic_routing.tier_models恰好落在本特性与 profile 之间,与 3 的优先级表完全对应。
3.2 关键语义细节:phase-type 优先于 profile=inherit
一个容易被忽视的语义:models[<phase_type>]优先于model_profile: "inherit"。修复前的缺陷是:当model_profile='inherit'且models.execution='opus'时,profile 的短路逻辑先于阶段级覆盖触发,导致gsd-executor错误解析为inherit,违反"models[phase_type] 高于 model_profile"的文档约定。该缺陷在测试 tests/model-resolver.test.cjs 中以CR Major级别回归锁定:
test('phase-type override wins over profile=inherit (CR Major) — model resolver', () => { writeConfig(projectDir, { model_profile: 'inherit', models: { execution: 'opus' }, }); // gsd-executor (execution) must get the phase-type opus, not inherit. assert.equal(resolveModelInternal(projectDir, 'gsd-executor'), 'opus'); });同时,没有设置槽位的 Agent 在model_profile: "inherit"下仍然继承会话模型,两条规则互不干扰。
四、校验规则:schema 拒绝、解析器宽容
4.1config-set命令侧(严格校验)
通过gsd config-set写入配置时,schema 严格拒绝未知阶段类型:
$ gsd config-set models.deployment opus Error: 'models.deployment' is not a valid config key # 合法写法: $ gsd config-set models.research sonnet错误信息来自config-schema的isValidConfigKey校验(对应需求 REQ-PHASE-MODELS-01:六个命名models.*槽位被config-schema接受;config-set拒绝未知阶段类型)。
4.2 直接编辑配置文件侧(宽容兜底)
直接手改.planning/config.json则宽松得多:解析器遇到不认识的取值会静默忽略并回落到 profile 档位,而不是报错中断。因此一个笔误(如haiku3)不会悄悄破坏档位解析。这是文档与测试共同确认的行为:
- 测试
unrecognized tier value falls through to profile (typo safety):models: { research: 'haiku3' }落到balanced档位的sonnet; - 测试
full model ID in models.<phase_type> is rejected; falls through to profile:models: { research: 'openai/gpt-5' }同样回落,不会把完整 ID 注入运行时档位解析链。
两种校验策略各有分工:CLI 入口(config-set)严格到拒绝即报错,配置文件入口宽容到"坏值即回落",保证既有配置在升级后行为不变。
五、向后兼容保证:没有models块时行为逐字节不变
特性需求 REQ-PHASE-MODELS-02 明确要求:未包含models块的配置,行为与 v1.41 之前逐字节一致。这一点由源码的读取方式与测试双重保证:
computeProfileTier中,configModels为空或phaseType未命中时,phaseTypeTier为undefined,VALID_TIERS.has(undefined)为 false,直接进入 profile 查询分支——整体行为等同于没有models配置;- 测试
empty models block is a no-op与no models block at all is a no-op分别验证models: {}和完全省略models两种情形下,gsd-phase-researcher仍解析为sonnet、gsd-planner仍解析为opus(与balancedprofile 默认一致)。
这一"缺省即无感"的设计,让存量项目无需任何迁移即可升级到 v1.41+。
六、实战:如何选择配置粒度
官方文档给出了一张"按需求选工具"的对照表,可直接作为日常决策依据:
| 你的诉求 | 使用 |
|---|---|
| 一个全局档位策略("处处 balanced") | model_profile |
| 粗粒度阶段级调参("规划用 Opus") | models.<phase_type> |
| 按 Agent 精确控制("强制 codebase mapper 用 haiku") | model_overrides[<agent>] |
| 为某个特定 Agent 指定完整模型 ID | model_overrides[<agent>]: "openai/gpt-5" |
这些方式可以自由混用——只要符合第三节的五层优先级规则,任何重叠都会被确定性地解析,不存在歧义。
典型成本分配实践:规划与讨论阶段是需要深度推理的"重脑力"环节(gsd-planner、gsd-roadmapper、gsd-assumptions-analyzer等 Agent 在model-catalog.json中本就归属heavy默认档位),可以用models.planning: "opus"、models.discuss: "opus"保证质量;research / verification 属于高吞吐的"标准负载",用models.research: "sonnet"、models.verification: "sonnet"控制成本;如果某个高频低风险的 Agent(如gsd-codebase-mapper)想进一步省钱,再用model_overrides钉到haiku。三层配合即可在"质量—成本—精确度"三角中自由取点。
七、相关能力与阅读路径
- 本文的权威长文档:Per-Phase-Type Models(docs/CONFIGURATION.md),包含完整的配置示例、取值表、优先级表与校验示例;
- 配置项速查表:settings-management(docs/features/settings-management.md);
- 实现源码:src/model-resolver.cts(
computeProfileTier、resolveModelInternal为核心读取与解析点)、src/model-catalog.cts(AGENT_TO_PHASE_TYPE、VALID_PHASE_TYPES、VALID_TIERS的派生来源)、gsd-core/bin/shared/model-catalog.json(phaseTypes、每个 Agent 的phaseType元数据); - Schema 校验来源:gsd-core/bin/shared/config-schema.manifest.json(
models.<phase_type>动态键模式); - 回归测试:tests/model-resolver.test.cjs(
#3023系列:阶段级覆盖、per-agent 优先、typo 回落、inherit 语义、空块 no-op、CR Major 修复等)。
如果你需要与"失败时自动升级档位"组合使用,可进一步阅读 Dynamic Routing with Failure-Tier Escalation(docs/CONFIGURATION.md) 中的dynamic_routing章节——它定义了tier_models[<tier>]如何在models.<phase_type>之上生效,以及max_escalations、escalate_on_failure等配套参数。
【免费下载链接】gsd-core
Git. Ship. Done - Core
相关推荐
gsd-core 按阶段类型(Phase-Type)配置模型选择:`.planning/config.json` 的 `models` 块深入解析
gsd core 按阶段类型(Phase Type)配置模型选择: .planning/config.json 的 models 块深入解析 导读 gsd co
GSD 模型选择与动态路由完全指南:按阶段选模型、配置兜底与跨供应商成本优化
GSD 模型选择与动态路由完全指南:按阶段选模型、配置兜底与跨供应商成本优化 本篇技术指南围绕 GSD(spec driven development 系统)的
人工智能AI Agent代码智能体Agent 编排CLIAI 应用gsd-core 分阶段粒度覆盖(granularities.<phaseType>)配置指南:按阶段类型精细调控规划粒度
gsd core 分阶段粒度覆盖(granularities.<phaseType )配置指南:按阶段类型精细调控规划粒度 本指南围绕 gsd core 的 g
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考