Book 第四十章:模型、Provider 与 Capability 怎样一起决定执行边界
第六部分:迁移与附录

第四十章:模型、Provider 与 Capability 怎样一起决定执行边界

拆 provider 路由、模型解析、capability 缓存和工具级能力门控。

1. 为什么这章必须单独拆

Claude Code 里和模型有关的代码,绝对不只是“用户选一个 model id”。

从源码看,这里至少同时在处理:

  • provider 路由
  • 模型选择与别名解析
  • allowlist / deprecation
  • capability 拉取与缓存
  • 工具级 capability gate
  • 订阅形态对默认模型的影响

这说明 Claude Code 真正在做的是:

让“当前会话到底能用哪个模型、能开哪些能力”变成一套统一路由逻辑。

相关源码锚点:

  • src/utils/model/providers.ts
  • src/utils/model/model.ts
  • src/utils/model/modelCapabilities.ts
  • src/utils/model/modelAllowlist.ts
  • src/utils/model/modelStrings.ts
  • src/utils/model/deprecation.ts
  • src/utils/model/modelSupportOverrides.ts
  • src/services/api/client.ts
  • src/tools/WebSearchTool/WebSearchTool.ts
  • src/main.tsx

2. 先说结论

我对这一块的判断是:

Claude Code 的模型路由是三层叠起来的:provider 选择、model 解析、capability 门控。

最值得抓住的结论有五个:

  1. provider 先决定调用通道,再谈具体模型。
  2. 用户设置的 model id 还会经过 allowlist、alias 和默认值解析。
  3. runtime model 不一定等于静态 settings model。
  4. capability 是独立缓存层,不和模型字符串混在一起。
  5. 工具可用性会直接看 provider + model capability。

3. 总体结构图

flowchart TD
    A["env / settings / subscription / mode"] --> B["providers.ts<br/>provider 路由"]
    A --> C["model.ts<br/>model setting 解析"]
    C --> D["allowlist / alias / default / runtime model"]
    B --> E["services/api/client.ts<br/>provider-specific client"]
    D --> E
    E --> F["模型调用"]

    B --> G["modelCapabilities.ts"]
    D --> G
    G --> H["工具 capability gate"]
    H --> I["WebSearchTool 等功能开关"]

4. Provider 路由:先确定“走哪条云”,再确定“跑哪个模型”

源码锚点:

  • src/utils/model/providers.ts
  • src/services/api/client.ts

getAPIProvider() 这层很干脆,直接按环境路由到:

  • firstParty
  • bedrock
  • vertex
  • foundry

然后 getAnthropicClient() 再根据 provider 建不同 client:

  • Bedrock 走 @anthropic-ai/bedrock-sdk
  • Vertex 走 @anthropic-ai/vertex-sdk
  • Foundry 走 @anthropic-ai/foundry-sdk
  • first-party 走标准 Anthropic client

这意味着 provider 在 Claude Code 里不是 metadata,而是调用栈分叉点。

5. 模型选择:用户写下去的 setting 不是最终答案

源码锚点:

  • src/utils/model/model.ts
  • src/utils/model/modelAllowlist.ts

getUserSpecifiedModelSetting() 的优先级大致是:

  • session override
  • startup override
  • ANTHROPIC_MODEL
  • settings

然后还要经过:

  • allowlist 过滤
  • alias / family 解析
  • 默认模型补位

也就是说,用户写的值只是“意图”,不是运行时最终模型。

6. 默认模型策略:它会看 provider,也会看订阅

源码锚点:

  • src/utils/model/model.ts

这里有两个很值钱的设计:

6.1 不同 provider 的默认模型不完全一样

例如:

  • first-party 默认能更快切到新的 Sonnet / Opus 代际
  • 第三方 provider 的默认值可能更保守

这说明默认模型不是纯产品文案,而是实际可用性和上线节奏的一部分。

6.2 订阅形态会影响默认主模型

比如:

  • Max / Team Premium 默认更偏 Opus
  • 其他用户默认更偏 Sonnet

也就是说,模型选择已经和产品套餐绑在一起了。

7. Runtime Model:为什么它不一定等于 settings model

源码锚点:

  • src/utils/model/model.ts

getRuntimeMainLoopModel() 还会在运行时进一步调整。

典型例子就是 plan mode:

  • opusplan
  • haiku

这些别名不是简单展示名,而是运行时策略名。

这说明 Claude Code 区分得很清楚:

  • 用户配置里想要什么
  • 当前这一段流程真正该用什么

8. Capability:不要把能力判断写死在 model string 上

源码锚点:

  • src/utils/model/modelCapabilities.ts

这层很值得学。

Claude Code 没把“最大上下文”“最大输出”等能力直接硬编码进一堆 if/else。

它会:

  • models.list
  • 把结果缓存到 ~/.claude/cache/model-capabilities.json
  • 提供同步查询能力

而且这层还有 eligibility:

  • 只在 ant 用户
  • first-party provider
  • first-party base URL

这意味着 capability cache 也是带信任边界的。

9. 工具 gating:模型能力会直接改执行面

源码锚点:

  • src/tools/WebSearchTool/WebSearchTool.ts

WebSearchTool 是一个非常直观的例子。

它启用时会看:

  • provider 是不是 firstParty
  • vertex 下模型是不是 Claude 4.x
  • foundry 是否默认支持

这说明工具不是“注册了就能用”,而是会被模型能力直接裁剪。

所以这层真正值钱的,不只是模型调用本身,而是:

模型路由会反过来重塑工具面。

10. Allowlist / deprecation / canonicalization:这层在保护什么

源码锚点:

  • src/utils/model/modelAllowlist.ts
  • src/utils/model/modelStrings.ts
  • src/utils/model/deprecation.ts

这一层的存在,解决了几个现实问题:

  • 家族别名很多
  • provider-specific model id 很杂
  • 某些老模型需要弃用或升级提示
  • 不同后端的具体 id 需要归一成对外稳定名字

这说明 Claude Code 明确不想把业务代码写成一堆散落的 model string 比较。

11. 对 C# 拆分最有用的建议

如果后面做 C# 版,这层非常适合拆成五块:

11.1 ProviderResolver

负责:

  • provider 路由
  • base URL / env 判定
  • provider-specific client family 选择

11.2 ModelSelectionPolicy

负责:

  • override 优先级
  • 默认模型
  • 订阅差异
  • plan/runtime 特殊路由

11.3 ModelCatalog

负责:

  • alias
  • canonical name
  • allowlist
  • deprecation

11.4 ModelCapabilityStore

负责:

  • capability fetch
  • 本地缓存
  • eligibility

11.5 FeatureGateEvaluator

负责:

  • 工具 capability gate
  • provider/model 对功能面的裁剪

12. 一句话收口

Claude Code 的模型路由最值得学的地方,不是“支持多个 provider”,而是它把 provider、model 和 capability 做成了一条统一的执行边界判定链。