首页
/ get-shit-done:修复 `resolve-model` 在 Claude 运行时忽略 `resolve_model_ids: true` 的完整解析链剖析

get-shit-done:修复 `resolve-model` 在 Claude 运行时忽略 `resolve_model_ids: true` 的完整解析链剖析

2026-09-04 13:28:25作者:江焘钦

本篇基于 changeset 文档 3643-resolve-model-claude-runtime.md 展开,讲解 get-shit-done(GSD)中模型 ID 解析器 gsd-sdk query resolve-model 的一个缺陷及其修复:当 runtime: "claude"resolve_model_ids: true 时,解析器此前只返回 opus / sonnet / haiku 这类 tier 别名,而不是消费方真正需要的完整 Claude 模型 ID(如 claude-sonnet-4-6)。读完后你将掌握:resolve-model 的完整优先级链(model_overrides → 阶段类型档位 → 运行时解析 → resolve_model_ids 处理 → profile 查询)、Claude 作为“隐式默认运行时”造成的解析分支漏洞成因,以及共享模型目录(model catalog)如何成为所有运行时派生模型 ID 的唯一事实来源。

问题背景:tier 别名与完整模型 ID 的两层语义

GSD 内部用统一的 tier 别名(opus / sonnet / haiku)描述“这一档应该用多强的模型”,而具体运行时再把它映射为可提交给 API 的模型 ID。resolve-model 命令的作用就是把某个 agent 名按当前配置解析出最终模型标识,调用形式见 CLI-TOOLS.md

# Get model for agent based on current profile
node gsd-tools.cjs resolve-model <agent-name>
# Raw output returns the selected model ID/tier.
# JSON output also includes profile and, when the active runtime supports it,
# reasoning_effort.

其中 resolve_model_ids 是控制这层映射的开关,语义定义在 CONFIGURATION.md:非 Claude 运行时安装时自动写入 resolve_model_ids: "omit",让所有 agent 使用运行时自身配置的默认模型;而 Claude 侧显式开启 true 时,应返回完整 Claude ID。本 bug(#3643)就是 SDK 移植路径(TS 版)在 Claude 运行时下没有兑现 true 的承诺。

缺陷成因:Claude 在运行时解析中的“提前退出”

SDK 的解析器实现位于 config-query.ts。其中 resolveRuntimeTier 负责“非 Claude 运行时”的档位到模型映射:

// sdk/src/query/config-query.ts(L195-L199)
function resolveRuntimeTier(config: Record<string, unknown>, tier: string): RuntimeTierEntry | null {
  if (!isRuntimeTierName(tier)) return null;

  const runtime = typeof config.runtime === 'string' ? config.runtime : '';
  if (!runtime || runtime === 'claude') return null;
  ...

这里的设计意图是:Claude 是隐式/默认运行时,tier 别名(sonnet 等)本就是 Claude API 原生可识别的模型标识,所以 Claude 不需要运行时解析这一步,函数直接返回 null 退出。问题在于:退出后代码沿“profile 查询”路径落到文件末尾的 alias 返回,而 TS 移植版在返回 alias 之前没有检查 resolve_model_ids。changeset 原文描述的正是这条链路:

previously the resolver bailed out of resolveRuntimeTier for Claude (because Claude is the implicit/default runtime in config-query.ts:149) and fell through to the alias-return path ... without consulting the catalog. Consumers asking for resolved model IDs received the tier alias (opus / sonnet / haiku) instead of the full Claude model ID.

于是同一个配置在 CJS 路径与 SDK 路径上出现行为漂移:CJS 版本(core.cjs L1348-L1350)有对应的兜底分支:

// resolve_model_ids: true — map alias to full Claude model ID.
// Prevents 404s when the Task tool passes aliases directly to the API.
if (config.resolve_model_ids) {
  return MODEL_ALIAS_MAP[alias] || alias;
}

changeset 指出的核心事实是:这个 CJS 分支在 TS 移植时丢失了

修复方案:让 Claude 也走共享模型目录

修复在 resolveModel 处理器的 alias 返回之前新增了一个 Claude 专属分支(config-query.ts L293-L313):

// #3643: runtime:claude bails out of resolveRuntimeTier (line 149) because
// Claude is the implicit/default runtime, but consumers that asked for
// resolved model IDs still need the full ID (e.g. "claude-sonnet-4-6"), not
// the tier alias. Mirror the CJS branch at get-shit-done/bin/lib/core.cjs
// (`if (config.resolve_model_ids) return MODEL_ALIAS_MAP[alias] || alias;`)
// by consulting the catalog's claude runtime defaults for the resolved tier.
const runtime = typeof (config as Record<string, unknown>).runtime === 'string'
  ? ((config as Record<string, unknown>).runtime as string)
  : '';
// Empty/missing runtime is implicit Claude (per the resolveRuntimeTier bail-out
// at line ~149); without this branch the resolved-IDs path silently fell
// through to the alias return for projects that never set `runtime` explicitly.
const isClaudeRuntime = runtime === '' || runtime === 'claude';
if (resolveModelIds === true && isClaudeRuntime && isRuntimeTierName(tier)) {
  const claudeDefault = resolveRuntimeTierDefault('claude', tier);
  if (claudeDefault?.model) {
    return { data: { model: claudeDefault.model, profile } };
  }
}

return { data: { model: alias, profile } };

三个实现要点值得注意:

  1. runtime === ''runtime === 'claude' 等价处理。源码注释明确说明:空的/缺失的 runtime 就是隐式 Claude(与 resolveRuntimeTier 的提前退出条件一致),如果不把空字符串也计入,那些从未显式设置 runtime 的项目会继续静默漏到 alias 返回路径上。

  2. 调用共享目录而不是私有映射表。分支通过 resolveRuntimeTierDefault('claude', tier) 取 Claude 默认 ID,该函数定义于 model-catalog.ts L60-L62,读的是 model-catalog.jsonruntimeTierDefaults.claude 表:

    "claude": {
      "opus":   { "model": "claude-opus-4-7" },
      "sonnet": { "model": "claude-sonnet-4-6" },
      "haiku":  { "model": "claude-haiku-4-5" }
    }
    

    changeset 强调这一设计决策的价值:“both runtimes derive Claude IDs from the same source of truth”——Claude 原生路径与 opencode/copilot 等经由目录映射 Claude 的路径(同文件中 opencodecopilot 条目分别映射到 anthropic/claude-sonnet-4-6 等带 provider 前缀的 ID)从此共用同一份 Claude 档位表,避免 CJS 侧 MODEL_ALIAS_MAP 与 SDK 侧目录各自维护而再次漂移。

  3. 只在 tier 是合法档位别名时才查目录isRuntimeTierName(tier)),且查不到时优雅降级回 alias 返回,不抛错。

完整优先级链:修复后的解析顺序

结合 core.cjs L1270-L1354 的注释结构与 SDK 实现,resolve-model 的完整决策顺序(修复后 CJS/TS 双路径对齐)为:

步骤 条件 返回
1 model_overrides[agentType] 存在 覆盖值原样返回(最高优先级)
2 计算 tier config.models[phaseType](阶段类型档位,#3023)优先,否则按 model_profileMODEL_PROFILES[agent]
3 运行时解析 runtime 显式设为非 Claude 值且 tier 非 inherit 时走 resolveRuntimeTier(内置默认 + model_profile_overrides[runtime] 合并,非推理型运行时剥离 reasoning_effort
4 resolve_model_ids: "omit" 返回空字符串,让运行时用自己的默认模型(安装器为 Codex 等自动设置)
5 resolve_model_ids: true 且 runtime 为(隐式或显式)Claude(本次修复) 查共享目录 runtimeTierDefaults.claude[tier],返回完整 Claude ID
6 其余情况 返回 tier 别名(opus/sonnet/haiku

changeset 明确保证前几级优先级不受影响:model_overrides, the phase-type tier override (config.models[phaseType]), and resolve_model_ids: "omit" all retain their existing precedence.” 也就是说 omit 仍然先于步骤 5 生效(见 SDK 实现中 resolveModelIds === 'omit' 分支位于新增 Claude 分支之前,config-query.ts L289-L291)。

一个可直接复现的行为对照示例:

// .planning/config.json
{
  "model_profile": "balanced",
  "runtime": "claude",
  "resolve_model_ids": true
}

gsd-sdk query resolve-model gsd-executor 修复前返回 { model: "sonnet", profile: "balanced" },修复后返回 { model: "claude-sonnet-4-6", profile: "balanced" }gsd-executor 在 balanced profile 下对应 sonnet 档位,见 model-catalog.jsonagents 表)。

测试验证:RED→GREEN 的回归防护

sdk/src/query/config-query.test.ts 中针对 #3643 新增了一组断言,覆盖修复行为与两条回归护栏:

  • balanced/quality/budget 三档全覆盖runtime: 'claude' + resolve_model_ids: true 下,gsd-executor(balanced)返回 claude-sonnet-4-6gsd-planner(quality)返回 claude-opus-4-7gsd-verifier(budget)返回 claude-haiku-4-5
  • 阶段类型档位优先model_profile: 'budget'models: { execution: 'opus' } 时,gsd-executor 返回 claude-opus-4-7,证明步骤 2 的 phase-type 覆盖在 Claude 解析路径上依然生效;
  • 回归护栏一runtime: 'claude'设置 resolve_model_ids 时,仍返回别名 sonnet——未显式开启解析的项目零行为变化;
  • 回归护栏二resolve_model_ids: "omit" 在 Claude 运行时下仍然胜过别名映射,返回空模型参数。

测试注释直接标注了症状与根因位置:"aliases (opus/sonnet/haiku) leaked through to consumers that asked for resolved model IDs because resolveRuntimeTier bails for runtime:claude and the alias-return fall-through ignored resolve_model_ids. CJS branch at get-shit-done/bin/lib/core.cjs:1348-1350 has the missing guard."

小结与适用边界

这个 changeset 修复的本质是跨运行时移植时的分支遗漏:CJS 中“别名 → 完整 ID”的兜底逻辑在 TS 移植时被省略,而 Claude 作为隐式默认运行时恰好是 resolveRuntimeTier 的提前退出点,两条逻辑叠加使得缺陷只在“SDK 路径 + Claude 运行时 + 显式 resolve_model_ids: true”这一组合下暴露。修复方式不是复刻一张 MODEL_ALIAS_MAP,而是让 Claude 分支复用 model-catalog.jsonruntimeTierDefaults 表,使 CJS 与 TS、原生 Claude 与第三方运行时统一从同一份数据源派生模型 ID。需要注意的适用前提:该修复作用于 gsd-sdk query resolve-model 的 SDK 查询路径(对应 CLI 侧 gsd-tools.cjs resolve-model 的既有行为),完整 ID 具体取值随 model-catalog.json 中 Claude 档位表更新而变化,仓库中当前为 claude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5

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

项目优选

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