Skip to main content

模型发现与缓存

ModelDiscoveryManager 负责从提供商 API 动态发现可用模型列表,管理发现结果的缓存,并与 AgentModelsManager 配合实现模型路由和 Follow-Provider 特性。


文件位置

文件路径
ModelDiscoveryManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ModelDiscoveryManager.ts
AgentModelsManagerpackages/desktop/app/main/services/capabilities/llm/config-service/AgentModelsManager.ts
LLMConfigServicepackages/desktop/app/main/services/capabilities/llm/config-service/LLMConfigService.ts
Provider Presetspackages/desktop/app/shared/provider-presets.ts
工具函数packages/desktop/app/main/services/capabilities/llm/config-service/utils.ts
配置合并packages/desktop/app/main/services/capabilities/llm/config/ (mergeModelLists)

架构上下文

graph TB
subgraph LLMConfigService
MDM[ModelDiscoveryManager]
AMM[AgentModelsManager]
end

subgraph 缓存层
SQLiteCache[(SQLite<br/>llm_model_cache)]
FileCache[(文件缓存<br/>model-cache/*.json)]
ChainCache["Transformer Chain Cache<br/>Map (10min TTL)"]
end

subgraph Provider API
OpenAIModels["/v1/models"]
AnthropicModels["/v1/models"]
GeminiModels["/v1beta/models"]
end

subgraph 数据源
ProviderConfig[Provider 配置<br/>modelConfigs / models]
Templates[PROVIDER_TEMPLATES<br/>内置模板]
Presets[Provider Presets<br/>预设配置]
Mappings[PROVIDER_MODEL_MAPPINGS<br/>模型映射]
end

MDM -->|读写| SQLiteCache
MDM -->|回退| FileCache
MDM -->|API 调用| Provider API
MDM -->|回退模型| ProviderConfig
MDM -->|回退模型| Templates

AMM --> ChainCache
AMM --> Mappings

数据结构

模型发现结果

// 单个模型发现条目
interface ProviderModelDiscoveryEntry {
id: string; // 模型 ID(如 'gpt-4o')
name: string; // 显示名称
description?: string; // 描述
contextLength?: number; // 上下文窗口长度
maxTokens?: number; // 最大输出 Token
category?: string; // 分类: 'chat' | 'reasoning' | 'image' | ...
capabilities?: string[]; // 能力标签: 'vision' | 'function_call' | 'reasoning'
}

// 发现结果
interface ProviderModelDiscoveryResult {
success: boolean;
source: string; // 'api' | 'cache' | 'fallback'
endpoint: string; // 实际调用的 API 端点
models: ProviderModelDiscoveryEntry[];
raw?: unknown; // API 原始响应
fetchedAt?: string; // ISO 时间戳
error?: string;
}

// 可用模型列表(合并后)
interface AvailableModelsResult {
models: ProviderModelDiscoveryEntry[];
source: string;
lastUpdated?: string;
}

Transformer Chain 缓存

// LLMConfigService 中的 chain 缓存
interface ChainCacheEntry {
chain: ResolvedTransformerChain;
cachedAt: number; // 缓存时间戳
}

// 缓存 TTL
const CHAIN_CACHE_TTL_MS = 10 * 60 * 1000; // 10 分钟

模型路由配置

// 路由器配置
interface RouterConfig {
default?: string; // 默认模型 ("providerId,modelId")
background?: string; // 后台模型
think?: string; // 推理模型
longContext?: string; // 长上下文模型
longContextThreshold?: number; // 长上下文触发阈值
webSearch?: string; // 搜索模型
image?: string; // 图片模型
vision?: string; // 视觉模型
followProviderBackground?: boolean; // 后台模型跟随主模型提供商
followProviderVision?: boolean; // 视觉模型跟随主模型提供商
}

// Code ↔ Chat 路由
interface CodeToChatRouterConfig {
providerId: string;
modelId: string;
actualProviderId: string;
actualModelId: string;
}

interface ChatToCodeRouterConfig {
providerId: string;
modelId: string;
actualProviderId: string;
actualModelId: string;
}

算法/逻辑说明

模型发现流程

flowchart TD
Start[discoverModels] --> GetProvider[获取 Provider 配置]
GetProvider --> LoadPreset[加载预设模型<br/>getFallbackModels]

LoadPreset --> DeriveEndpoint[推导 API 端点<br/>deriveModelsEndpoint]
DeriveEndpoint --> HasEndpoint{端点有效?}

HasEndpoint -->|否| CheckCache[检查缓存]
HasEndpoint -->|是| CheckForce{强制刷新?}

CheckForce -->|是| CallAPI[调用 API]
CheckForce -->|否| CheckCacheAge[检查缓存新鲜度]
CheckCacheAge --> HasFreshCache{缓存有效?}
HasFreshCache -->|是| ReturnCache[返回缓存]
HasFreshCache -->|否| CallAPI

CallAPI --> APISuccess{API 成功?}
APISuccess -->|是| ParseModels[解析模型列表]
APISuccess -->|否| FallbackCache[使用缓存/预设回退]

ParseModels --> MergeModels[合并 API 结果 + 预设模型]
MergeModels --> WriteCache[写入缓存]
WriteCache --> ReturnResult[返回结果]

CheckCache --> HasCacheAtAll{有缓存?}
HasCacheAtAll -->|是| ReturnCache
HasCacheAtAll -->|否| ReturnFallback[返回预设模型]

FallbackCache --> HasCacheAtAll

步骤详解

discoverModels(providerId, options?):
1. provider = delegate.getProvider(providerId)
如果不存在 → 返回 { success: false, error: "not found" }

2. presetModels = getFallbackModels(provider)
// 从 modelConfigs → models → PROVIDER_TEMPLATES 逐级查找

3. endpoint = deriveModelsEndpoint(provider)
// 从 modelsEndpoint 或 api_base_url 推导

4. 如果没有有效端点:
尝试从缓存返回,否则返回预设模型(source: 'fallback')

5. 如果不是强制刷新:
检查缓存 → 如果有效 → 返回缓存结果

6. 调用提供商 API:
- OpenAI 兼容: GET /v1/models
- Anthropic: GET /v1/models
- Gemini: GET /v1beta/models
使用 Electron net 模块发起请求

7. 解析响应,提取模型列表
处理不同提供商的响应格式差异

8. 合并: API 发现的模型 + 预设模型(去重)

9. 写入缓存(SQLite 或文件)

10. 返回 { success: true, source: 'api', models, ... }

回退模型解析(getFallbackModels)

当 API 发现不可用时,按以下优先级回退:

getFallbackModels(provider):
fallbackModels = []

// 优先级 1: Provider 自身的 modelConfigs
if provider.modelConfigs?.length:
convertModelConfigs(provider.modelConfigs)
// 提取 id, name, contextLength, maxTokens, capabilities

// 优先级 2: Provider 自身的 models 数组
else if provider.models?.length:
convertModelsArray(provider.models)
// 只有 id,无详细信息

// 优先级 3: 匹配的内置模板
if fallbackModels.length === 0:
template = PROVIDER_TEMPLATES.find(匹配 provider.id 或 provider.name)
if template:
// 使用模板的 modelConfigs 或 models

模型分类映射

分类说明
chat通用对话模型(默认)
reasoning推理模型(o1, Claude thinking)
image图片生成模型
video视频生成模型
embedding嵌入模型
code代码生成模型

缓存策略

双层缓存(SQLite + 文件)

flowchart LR
subgraph 读取路径
Read[readModelCache] --> CheckSQLite{useSQLite?}
CheckSQLite -->|是| SQLiteRead[db.llmModelCacheGet]
CheckSQLite -->|否| FileRead[fs.readFile]
SQLiteRead -->|失败| FileRead
end

subgraph 写入路径
Write[writeModelCache] --> CheckSQLite2{useSQLite?}
CheckSQLite2 -->|是| SQLiteWrite[db.llmModelCacheSet]
CheckSQLite2 -->|否| FileWrite[fs.writeFile]
SQLiteWrite -->|失败| FileWrite
end

SQLite 缓存表

字段类型说明
providerIdTEXT主键
endpointTEXTAPI 端点
sourceTEXT来源('api' / 'cache')
modelsJSON序列化的模型列表
rawJSON原始 API 响应
fetchedAtINTEGER获取时间戳
expiresAtINTEGER过期时间戳(24 小时)

文件缓存

  • 目录:userData/model-cache/
  • 文件名:{providerId}.json(特殊字符替换为下划线)
  • 无过期机制(依赖强制刷新)

Transformer Chain 缓存

LLMConfigService 维护 Transformer Chain 的内存缓存,避免重复解析:

chainCache: Map<string, { chain: ResolvedTransformerChain, cachedAt: number }>
CHAIN_CACHE_TTL_MS = 10 * 60 * 1000 // 10 分钟

getTransformerChain(key):
cached = chainCache.get(key)
if cached && (Date.now() - cached.cachedAt < TTL):
return cached.chain
// 否则重新解析
chain = transformerService.resolve(...)
chainCache.set(key, { chain, cachedAt: Date.now() })
return chain

模型路由

Chat → Code / Code → Chat 路由

resolveRoutedModel(providerId, modelId):
config = loadConfig()

// 检查 Code → Chat 路由
for route in config.routers.codeToChat:
if route.providerId === providerId && route.modelId === modelId:
return { actualProviderId: route.actualProviderId, actualModelId: route.actualModelId }

// 检查 Chat → Code 路由
for route in config.routers.chatToCode:
if route.providerId === providerId && route.modelId === modelId:
return { actualProviderId: route.actualProviderId, actualModelId: route.actualModelId }

// 无匹配路由
return null

Follow-Provider 模型映射

resolveEffectiveModels():
router = config.router
agentDefaults = config.agentDefaultModels

background = agentDefaults.background
vision = router.vision

if (followProviderBackground || followProviderVision) && router.default:
defaultProviderId = router.default.split(',')[0]
provider = getProvider(defaultProviderId)
if provider:
mapping = resolveFollowProviderModel(provider)
// 使用 PROVIDER_MODEL_MAPPINGS 查找对应模型
if followProviderBackground: background = mapping.background
if followProviderVision: vision = mapping.vision

return { background, vision }

Provider 搜索配置(PROVIDER_SEARCH_CONFIGS)

定义每个提供商如何实现网页搜索:

搜索类型说明示例提供商
model-param请求参数中启用搜索DashScope, Baidu
builtin-tool注入内置工具定义Kimi, Volcengine
mcp通过 MCP 服务器提供搜索工具自定义 MCP
sdk-nativeSDK 原生支持(服务端工具)Anthropic
none不支持搜索Ollama

Provider 模型映射(PROVIDER_MODEL_MAPPINGS)

用于 Follow-Provider 特性,自动选择同一提供商的 background/vision 模型:

提供商主力模型 (primary)后台模型 (background)视觉模型 (vision)
zhipuglm-5glm-4.5-airglm-4.6v
volcengineark-code-latestdoubao-seed-2.0-litedoubao-seed-2.0-code
kimikimi-k2.5kimi-k2-0905-previewkimi-k2.5
............

IPC 集成表

IPC 通道方向说明
llmConfig:discoverModelsR → M触发模型发现(支持 forceRefresh 参数)
llmConfig:getProvidersR → M返回 Provider 包含的模型列表

扩展点

添加新提供商的模型发现支持

  1. 确保提供商模板中设置了 modelsEndpoint(如 /v1/models
  2. 如果提供商有特殊的 models API 格式,在 ModelDiscoveryManager.discoverModels() 中添加解析逻辑
  3. utils.tsderiveModelsEndpoint() 中添加 URL 推导规则

添加 Follow-Provider 模型映射

packages/desktop/app/shared/provider-presets.tsPROVIDER_MODEL_MAPPINGS 中添加:

// 伪代码
PROVIDER_MODEL_MAPPINGS['newProvider'] = {
primary: 'main-model-id',
background: 'lightweight-model-id',
vision: 'vision-model-id', // null 表示无视觉模型
};

自定义缓存 TTL

  • 模型发现缓存:SQLite 中通过 expiresAt 字段控制(当前 24 小时)
  • Transformer Chain 缓存:修改 LLMConfigService.CHAIN_CACHE_TTL_MS(当前 10 分钟)

关联文件表

文件关联方式
capabilities/llm/config-service/LLMConfigService.ts宿主服务,初始化 ModelDiscoveryManager
capabilities/llm/config-service/utils.ts工具函数:deriveModelsEndpoint, resolveApiKey 等
capabilities/llm/config-service/AgentModelsManager.ts模型路由、Follow-Provider 解析
shared/llm-config.tsPROVIDER_TEMPLATES、类型定义
shared/provider-presets.tsPROVIDER_MODEL_MAPPINGS、PROVIDER_SEARCH_CONFIGS
capabilities/llm/config/model-discovery.ts模型列表合并去重工具(mergeModelLists
workers/DbClient.tsSQLite 缓存读写
routers/llm/ProviderRouter.tsIPC 层 discoverModels
capabilities/llm/completion/ThinkingResolver.ts消费 discoveredModelMaxTokens