summarize 模型/Provider 解析架构:从共享能力注册表到自动选型与执行链路
【免费下载链接】summarizePoint at any URL/YouTube/Podcast or file. Get the gist. CLI and Chrome Extension.项目地址: https://gitcode.com/GitHub_Trending/summarize/summarize
导读:本文基于 summarize 项目的 docs/model-provider-resolution.md,完整拆解其"模型/Provider 解析"设计:从无依赖的共享能力注册表(provider registry)出发,梳理自动模型选择(auto model selection)、LLM 执行路由(generate-text)、错误整形(error shaping)三层结构的职责边界与调用链。读完你将掌握:新增或修改一个模型 Provider 时应该改哪个文件、自动选型(含 CLI 回退与 OpenRouter 映射)的候选生成逻辑、各 Provider 能力位(文档/流式/视频理解)如何在源码中落地,以及确保这些约束不被破坏的契约测试。
设计目标:减少隐式 Provider 知识
文档开门见山提出了核心目标:reduce implicit provider knowledge(减少隐式的 Provider 知识)。也就是说,Provider 的名称、所需环境变量、默认模型、传输方式、能力位(文档/流式/视频理解)等元数据,不应散落在各处常量、测试或调用分支里,而应收敛到一个单一的、可被多处消费的共享注册表,让"新增一个 Provider"变成"改一处注册表 + 补一个执行实现",而不是在几十个文件里打补丁。
围绕这一目标,系统被划分为四个层次:
- 共享能力注册表(src/llm/provider-registry.ts)——元数据的唯一事实来源;
- 自动模型选择(src/model-auto.ts)——把规则与能力映射为候选尝试(attempts);
- 执行(src/llm/generate-text.ts)——按传输类型分发请求、归一化重试与回退;
- 错误整形(error shaping)——能力缺失、模型不可用等错误在正确的位置抛出。
以下各节逐一深入。
共享能力注册表:Provider 元数据的唯一事实来源
文档规定 src/llm/provider-registry.ts 是一个**无依赖(dependency-free)**的注册表,被配置解析、模型解析和运行时能力辅助函数共同消费。它拥有:
- 每个 Provider 的必需环境变量(required env)
- CLI 默认模型(CLI default models)
- 自动 CLI 回退顺序(auto CLI order)
- 文档支持(document support)
- 流式支持(streaming support)
- Provider 名称及其 TypeScript 联合类型
- 默认端点与传输方式选择
网关 Provider 画像(Gateway Provider Profiles)
注册表中GATEWAY_PROVIDER_PROFILES为每个网关型 Provider 声明了一个GatewayProviderProfile,字段如下(见 src/llm/provider-registry.ts):
| 字段 | 含义 |
|---|---|
requiredEnv | 该 Provider 必需的模型环境变量名(如OPENAI_API_KEY) |
execution | 传输类型:simple/google/anthropic/openai-http/openai-compatible |
supportsDocuments | 是否支持文档附件 |
supportsStreaming | 是否支持流式输出 |
supportsVideoUnderstanding | 是否支持视频理解 |
defaultBaseUrl | 默认端点(可被配置覆盖) |
forceChatCompletions | 是否强制走 Chat Completions 协议(而非 Responses 协议) |
当前 9 个网关 Provider 的完整画像(源码原文):
| Provider | requiredEnv | execution | Documents | Streaming | Video | defaultBaseUrl / 备注 |
|---|---|---|---|---|---|---|
xai | XAI_API_KEY | simple | ✗ | ✓ | ✗ | — |
openai | OPENAI_API_KEY | openai-http | ✓ | ✓ | ✗ | — |
google | GEMINI_API_KEY | google | ✓ | ✓ | ✓ | — |
anthropic | ANTHROPIC_API_KEY | anthropic | ✓ | ✓ | ✗ | — |
zai | Z_AI_API_KEY | openai-compatible | ✗ | ✓ | ✗ | https://api.z.ai/api/paas/v4,强制 Chat Completions |
nvidia | NVIDIA_API_KEY | openai-compatible | ✗ | ✓ | ✗ | https://integrate.api.nvidia.com/v1,强制 Chat Completions |
minimax | MINIMAX_API_KEY | openai-compatible | ✗ | ✓ | ✗ | https://api.minimax.io/v1,强制 Chat Completions |
github-copilot | GITHUB_TOKEN | openai-http | ✗ | ✓ | ✗ | https://models.github.ai/inference,强制 Chat Completions |
ollama | OLLAMA_BASE_URL | openai-compatible | ✗ | ✓ | ✗ | http://localhost:11434/v1,强制 Chat Completions |
这里有一个值得注意的细节:openai与github-copilot都走openai-http执行类型,但后者被标记为forceChatCompletions: true,且默认端点指向 GitHub Models;而zai/nvidia/minimax/ollama走openai-compatible,同样强制 Chat Completions。supportsVideoUnderstanding目前只有google为true,这正是自动选型时"视频内容只投递到具备视频理解能力的 Provider"这一策略的数据来源。
CLI Provider 画像与自动回退顺序
除网关 API 外,系统还支持调用本机安装的 CLI 二进制(Claude CLI、Codex CLI、Gemini CLI 等),它们由CLI_PROVIDER_PROFILES描述(src/llm/provider-registry.ts):
| Provider | requiredEnv | defaultModel | pathEnv | missingBinaryLabel / installLabel |
|---|---|---|---|---|
claude | CLI_CLAUDE | sonnet | CLAUDE_PATH | Claude CLI |
codex | CLI_CODEX | null | CODEX_PATH | Codex CLI |
gemini | CLI_GEMINI | flash | GEMINI_PATH | Gemini CLI |
agent | CLI_AGENT | auto | AGENT_PATH | Cursor Agent CLI |
openclaw | CLI_OPENCLAW | main | OPENCLAW_PATH | OpenClaw CLI |
opencode | CLI_OPENCODE | null | OPENCODE_PATH | OpenCode CLI |
copilot | CLI_COPILOT | null | COPILOT_PATH | GitHub Copilot CLI |
agy | CLI_AGY | null | AGY_PATH | Antigravity CLI /agy |
pi | CLI_PI | null | PI_PATH | piCLI |
DEFAULT_AUTO_CLI_ORDER定义了自动回退时的默认探测顺序:claude → gemini → codex → agent → openclaw → opencode → copilot。源码注释特别说明:agy与pi有意被排除在默认自动回退顺序之外,用户必须显式通过--cli agy或--model cli/agy等方式主动启用(src/llm/provider-registry.ts)。
运行时别名与能力面的组合
文档强调:注册表负责元数据,而src/llm/provider-profile.ts负责运行时环境变量别名与客户端配置,src/llm/provider-capabilities.ts则把两者组合成统一的能力面(combined capability surface)导出。从源码看,provider-profile.ts中的envHasRequiredKey处理了多套环境变量别名(src/llm/provider-profile.ts):
GEMINI_API_KEY缺省时,GOOGLE_GENERATIVE_AI_API_KEY或GOOGLE_API_KEY任一存在即视为可用;Z_AI_API_KEY缺省时,ZAI_API_KEY也可用;GITHUB_TOKEN走resolveGitHubModelsApiKey解析;OLLAMA_BASE_URL恒为真(本地服务,无需校验密钥)。
resolveRequiredEnvForModelId则负责从任意模型 ID(cli/...、openclaw/...、openrouter/...、provider/model)反推出所需的环境变量名。provider-capabilities.ts作为一个纯 re-export 层,把注册表与 profile 的能力统一暴露给model-auto与generate-text消费。
自动模型选择:从规则到候选尝试
文档定义 src/model-auto.ts 的职责为:
- 解析自动规则(resolve auto rules)
- 前置追加 CLI 候选(prepend CLI candidates)
- 在安全前提下把原生候选映射到 OpenRouter(map native candidates to OpenRouter when safe)
- 产出带必需环境变量与传输方式的尝试列表(emit attempts with required env + transport)
同时给出明确边界:保持"选型聚焦",不要在model-auto里加 Provider 专属能力分支,除非注册表无法表达。
第一步:规则候选解析
规则本身定义在 src/model-auto-rules.ts 的DEFAULT_RULES中。规则按输入类型(video/image/website/youtube/text/file)分组,when匹配输入类型,命中后返回candidates列表;website/youtube/text还额外支持按 token 分档的bands:
promptTokens ≤ 50_000:google/gemini-3-flash、openai/gpt-5-mini、anthropic/claude-sonnet-4-5promptTokens ≤ 200_000:同上- 更大输入:追加
xai/grok-4-fast-non-reasoning video:仅google/gemini-3-flash与google/gemini-2.5-flash-lite-preview-09-2025(与注册表中 google 支持视频理解一致)- 兜底规则:
gemini-3-flash、gpt-5-mini、claude-sonnet-4-5、grok-4-fast-non-reasoning
这些默认规则可通过配置文件覆盖:model配置为{ "mode": "auto", "rules": [...] }时使用自定义规则,否则回落默认规则(见 src/config/model.ts 与resolveRuleCandidates)。配置解析器还会校验bands的token.min/token.max非负且min ≤ max,并禁止同一规则同时声明candidates与bands。
第二步:前置 CLI 候选
src/model-auto-cli.ts 的prependCliCandidates决定是否以及在哪些 CLI Provider 前置于 API 候选:
- 若配置显式给出
cli.enabled列表,则以该列表为准(cli.autoFallback/ 旧名cli.magicAuto的自动回退此时不生效); - 否则按
resolveCliAutoFallbackConfig解析出的顺序(默认DEFAULT_AUTO_CLI_ORDER)探测,且默认onlyWhenNoApiKeys: true——即只有在没有任何 API 密钥配置时才启用 CLI 自动回退(hasAnyApiKeysConfigured检查 OPENAI/GEMINI/ANTHROPIC/XAI/OPENROUTER/Z_AI 等密钥); - 上一次成功过的 CLI Provider 会被优先提到最前(
prioritizeCliProvider),形成"粘性"回退体验; - 候选 ID 形如
cli/claude/sonnet、cli/gemini/flash,未指定模型时使用DEFAULT_CLI_MODELS。
配置解析层会拒绝cli.<provider>.enabled这种写法(提示改用cli.enabled),并强制cli.autoFallback与旧名cli.magicAuto二选一(见 src/config/sections.ts)。
第三步:OpenRouter 映射与尝试生成
buildAutoModelAttempts(src/model-auto.ts)是核心循环,对每个候选执行:
- 视频过滤:当输入需要视频理解(
requiresVideoUnderstanding)时,跳过 OpenRouter/CLI 候选以及所有非视频理解能力的原生模型 ID; - 显式前缀识别:
cli/...走 CLI 传输,openrouter/...走 OpenRouter 传输(透传openrouterProvidersFromEnv),其余走原生(native)传输; - 密钥校验:CLI 候选检查
cliAvailability,网关候选检查envHasKey,缺密钥直接跳过; - token 预算过滤:借助 LiteLLM 目录(
resolveLiteLlmMaxInputTokensForModelId),若promptTokens > maxIn则跳过该候选; - 成本估算:
estimateCostUsd结合输入/输出 token 与 LiteLLM 单价估算,并写入debug字段; - OpenRouter 回退:当
OPENROUTER_API_KEY存在且不需要视频理解时,尝试把原生provider/model映射为 OpenRouter 的author/model。映射采用"精确byId匹配 → 唯一 slug 匹配 → 忽略标点后的 slug 匹配"三级策略(如xai→x-ai、grok-4-1-fast→grok-4.1-fast这类差异),歧义时宁可跳过;OpenRouter 模型索引按进程懒加载缓存,测试可注入固定列表; - 去重:按
transport + openrouter 标志 + userModelId + providers组合去重后返回有序的AutoModelAttempt[]。
每个AutoModelAttempt携带transport: "native" | "openrouter" | "cli"、userModelId、llmModelId、requiredEnv、openrouterProviders、forceOpenRouter与debug字符串,成为后续执行层的直接输入。
执行链路:按 execution 类型分发
src/llm/generate-text.ts 的generateTextWithModelId职责为:解析请求的模型 ID、校验输入形状、路由到 Provider 传输、归一化重试/回退。源码显示它先parseGatewayStyleModelId拆出provider与model,再根据注册表中的execution类型分支:
| execution | 分发目标 | 关键实现 |
|---|---|---|
simple | xAI | completeSimple+resolveXaiModel,校验XAI_API_KEY |
google | completeGoogleText,校验GEMINI_API_KEY(含别名) | |
anthropic | Anthropic | completeAnthropicText,校验ANTHROPIC_API_KEY |
openai-http | OpenAI / GitHub Copilot | completeOpenAiText+resolveOpenAiClientConfig |
openai-compatible | zai / nvidia / minimax / ollama | resolveOpenAiCompatibleClientConfigForProvider+completeSimpleText |
模型 ID 规范化
在执行之前,模型 ID 已由 src/llm/model-id.ts 的normalizeGatewayStyleModelId规范化:无前缀写法按启发式推断(grok-*→xai、gemini-*→google、claude-*→anthropic、其余→openai);历史别名(grok-4.1-fast-non-reasoning→xai/grok-4-fast-non-reasoning)与 Anthropic 短别名(claude-sonnet-4→claude-sonnet-4-0)被映射到 API 可接受的 ID;github-copilot前缀走resolveGitHubCopilotBackendModelId;未知 Provider 前缀会抛出带可用前缀清单的错误。契约测试 tests/provider-registry.contract.test.ts 专门验证了zai/nvidia/minimax/github-copilot/ollama等 Provider 的无前缀裸名必须报 "Unknown model",防止用户误把 Provider 名当模型名使用。
重试、回退与超时
执行层围绕maxRetries与timeoutMs做了多层防御:
- 超时:每次尝试新建
AbortController+setTimeout,AbortError被归一化为 "LLM request timed out after Xms"; - 文档回退:
maybeGenerateDocumentText失败时可通过retryWithModelId递归切换到其他模型; - Google 空摘要回退:检测
isGoogleEmptySummaryError后切换到resolveGoogleEmptyResponseFallbackModelId指定的模型重试; - GPT-5 token 上限重试:
shouldRetryGpt5WithoutTokenCap命中时移除maxOutputTokens重新发起; - Anthropic 访问错误归一化:
normalizeAnthropicModelAccessError把模型访问错误转换成用户可读信息; - 通用重试:
isRetryableLlmError+ 指数退避(computeRetryDelayMs)+onRetry通知回调。
值得注意的是,Provider 专属的 SDK/HTTP 细节被隔离在 src/llm/providers/* 目录下(如anthropic.ts、google.ts、openai.ts、minimax.ts),generate-text.ts只做路由与归一化,这与文档"Provider-specific SDK/http details belong undersrc/llm/providers/*"的规定一一对应。
错误整形:错误应该在正确的层抛出
文档给出三条错误整形原则,源码中均有对应实现:
- 访问/模型可用性归一化保持 Provider 本地:只有当错误确实 Provider 专属时才在其执行实现内处理(如 Anthropic 的
normalizeAnthropicModelAccessError); - 通用能力错误来自共享注册表:如
envHasRequiredKey校验失败、resolveOpenAiCompatibleClientConfigForProvider抛出的 "Missing XXX_API_KEY for provider/... model",都源自注册表/profile 层,保证所有调用方得到一致的错误文案; - 不支持的功应当在建立传输之前抛出:如 src/llm/model-id.ts 对未知 Provider 前缀在解析阶段即报错,CLI 缺失时
formatMissingCliModelError会在构造客户端之前给出 "Install ... or set ..." 的安装指引(missingBinaryLabel/installLabel/pathEnv均来自注册表)。
配置面隔离:legacy apiKeys 与 base-URL 白名单
文档特别强调"配置层面的白名单保持分离":新增一个模型 Provider 绝不能隐式扩大 legacyapiKeys名称集合或 Provider base-URL 配置面。
- legacy API 密钥映射集中在 src/config/legacy-api-keys.ts 的
LEGACY_API_KEY_ENV_MAP(openai→OPENAI_API_KEY、nvidia→NVIDIA_API_KEY、minimax→MINIMAX_API_KEY、anthropic→ANTHROPIC_API_KEY、google→GEMINI_API_KEY、xai→XAI_API_KEY、openrouter→OPENROUTER_API_KEY、zai→Z_AI_API_KEY,以及apify/firecrawl/fal/groq/assemblyai/elevenlabs等非模型用途密钥); - 契约测试验证了对
deepgram、ollama、github-copilot等未列入 legacy 名单的 Provider 写入apiKeys会报 "unknown apiKeys provider"(tests/provider-registry.contract.test.ts); - 同一测试还验证了
parseCliProvider、parseRequestedModelId、parseCliConfig、resolveOpenAiCompatibleClientConfigForProvider对每个 CLI Provider / 兼容 Provider 的默认行为(默认模型、默认 base URL、强制 Chat Completions 等),形成"注册表与配置/解析器一致"的契约保障。
维护规则:新增或修改 Provider 的操作手册
文档最后给出三条工程纪律,是参与本项目开发时的第一手操作指南:
- 能力只加一次,两处消费:在注册表中声明能力后,
model-auto(选型)与generate-text(执行)都从注册表读取,不复制分支; - Provider 环境变量别名集中管理:所有别名(如 Google 三键、Z_AI 双键)收敛在
provider-profile.ts的envHasRequiredKey,避免各处散落各自的判断逻辑; - 默认 CLI 模型改动只进注册表:不要散落到测试或常量文件,否则契约测试与自动选型会产生漂移。
对照实现可以给出一个清晰的改动检查清单:
- 新增网关 Provider:先在 src/llm/provider-registry.ts 的
GATEWAY_PROVIDER_PROFILES添加画像(requiredEnv/execution/能力位/defaultBaseUrl/forceChatCompletions),再视execution类型在 src/llm/providers 补执行实现,最后确认 legacyapiKeys与 base-URL 配置面不需要(也不应)隐式扩展; - 新增 CLI Provider:在
CLI_PROVIDER_PROFILES注册 requiredEnv/defaultModel/pathEnv 等,并按需加入DEFAULT_AUTO_CLI_ORDER(或有意排除、要求显式启用); - 调整自动选型:优先修改
DEFAULT_RULES(src/model-auto-rules.ts)或通过model: { mode: "auto", rules: [...] }配置覆盖,而不是在执行层加分支; - 修改默认模型/顺序:一律改注册表,随后运行 tests/provider-registry.contract.test.ts 与 tests/llm.provider-capabilities.test.ts 验证契约未破坏。
小结
summarize 的模型/Provider 解析是一条"注册表 → 选型 → 执行"的单向依赖链:provider-registry.ts用一张无依赖的表承载全部 Provider 元数据;provider-profile.ts与provider-capabilities.ts在其上叠加运行时别名与能力面;model-auto.ts(连同model-auto-rules.ts、model-auto-cli.ts)把规则、密钥、token 预算与 OpenRouter/CLI 回退编译成有序的尝试列表;generate-text.ts再按execution类型分发到 src/llm/providers 的具体实现,并在统一的重试/超时/回退框架下归一化结果与错误。这套设计把"隐式 Provider 知识"压到最低——新增 Provider 只需改注册表与对应执行实现,其余层通过契约测试保证一致性。对于想扩展模型生态或自定义自动选型策略的开发者,以上文件与测试即是完整的操作地图。
【免费下载链接】summarizePoint at any URL/YouTube/Podcast or file. Get the gist. CLI and Chrome Extension.项目地址: https://gitcode.com/GitHub_Trending/summarize/summarize
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考