LLM API 系统架构
本文档系统性地介绍 AIO Hub 的 LLM API 调用体系。系统由应用配置/编排、应用 Facade、共享 Provider Core 与平台 Transport 组成;桌面和移动共享协议语义,但保留各自运行时边界:
┌──────────────────────────────────────────────────┐
│ 预设层 (Preset) │
│ llm-presets.ts — 42个预设模板(快速创建渠道) │
├──────────────────────────────────────────────────┤
│ 渠道管理层 (Channel) │
│ useLlmProfiles.ts — 渠道 CRUD / 持久化 │
│ useLlmKeyManager.ts — 多 Key 轮询 / 熔断/恢复 │
│ useLlmRequest.ts — 请求编排 / 中间件 │
├──────────────────────────────────────────────────┤
│ 应用 Facade 层 (Desktop/Mobile) │
│ Profile/Key/元数据映射、回调兼容、错误与日志接入 │
├──────────────────────────────────────────────────┤
│ @aiohub/llm-core (Shared Core) │
│ canonical DTO / Provider Adapter / 执行器 / fixture │
├──────────────────────────────────────────────────┤
│ 平台 Transport 层 │
│ Desktop Rust Proxy / Mobile HTTP + Native FileRef │
└──────────────────────────────────────────────────┘1. 预设层 (Preset Layer)
文件路径: src/config/llm-presets.ts
预设层提供开箱即用的服务商模板,用户可以通过 UI 一键创建渠道并自动填充 baseUrl、logo、默认模型列表。
1.1 预设结构
export interface LlmPreset {
type: ProviderType; // 对应适配器层分发的 Provider 类型
name: string; // 显示名称,如 "OpenAI", "Google Gemini"
description: string; // 简短描述
defaultBaseUrl: string; // 默认 API 地址
logoUrl?: string; // Logo 路径
defaultModels?: LlmModelInfo[]; // 预设的默认模型列表
links?: LlmLink[]; // 快捷链接(官网、控制台、文档等)
customEndpoints?: LlmProfile["customEndpoints"]; // 自定义端点配置
}1.2 当前预设列表(42 个)
主流大厂: OpenAI · OpenAI Responses · Google Gemini · Anthropic Claude · Cohere · xAI (Grok) · Vertex AI · Azure OpenAI
国产平台(均走 openai-compatible 协议): 阿里云百炼 (Qwen) · 火山引擎 (豆包) · 智谱 AI (GLM) · 百度文心 (ERNIE) · 腾讯混元 · 月之暗面 (Kimi) · 零一万物 (Yi) · 百川智能 · MiniMax (ABAB) · 商汤日日新
聚合/中转平台: OpenRouter · SiliconFlow · Together AI · Fireworks AI · DeepInfra · NewAPI · Hugging Face · Perplexity · 魔搭 ModelScope
本地/私有部署: Ollama · Ollama Cloud · LM Studio
其他/特定: Mistral AI · AI21 Labs (Jamba) · Suno (via NewAPI) · VCP
注意:预设层仅提供 UI 层面的配置模板。实际请求的分发由 适配器层 根据
profile.type决定,上述国产平台均通过openAiAdapter处理。
2. 渠道管理层 (Channel Management Layer)
这是整个系统的"中枢神经系统",由三个 Composable 协同工作。
2.1 渠道配置管理 — useLlmProfiles
负责渠道配置的增删改查与持久化。
核心职责:
- 加载/保存: 通过
createConfigManager将渠道配置持久化到profiles.json(第 120 行) - 数据迁移: 自动从旧版 localStorage 迁移到文件系统(第 86-105 行)
- 数据规范化:
normalizeProfile()处理旧版单 Key 到多 Key 数组的兼容(第 38 行) - 预设创建:
createFromPreset()将预设模板实例化为可编辑的渠道配置(第 258 行) - 能力查询:
getSupportedParameters()基于providerTypes查询渠道支持的参数(第 278 行)
// 核心暴露
const {
profiles,
saveProfile,
deleteProfile,
getProfileById,
createFromPreset,
} = useLlmProfiles();2.2 多 Key 管理 — useLlmKeyManager
支持一个渠道配置绑定多个 API Key,提供轮询 + 可选熔断 + 恢复能力。熔断开关和恢复时长按渠道独立存储,自动熔断默认关闭,建议仅在多 Key 配置且确认需要故障摘除时开启。
轮询策略(pickKey()):
- 调用
syncKeyStates()同步所有 Key 的状态 - 过滤出可用 Key:
isEnabled && !isBroken - 从
lastUsedIndices记录的下标开始轮询下一个可用 Key - 若无可用 Key,回退到第一个 Key,由下游 API 报错触发反馈
熔断逻辑(reportFailure()):
- 开启自动熔断后,识别
429 Too Many Requests,直接熔断 - 开启自动熔断后,连续 3 次非 429 暂态错误也触发熔断
- 仅当同一渠道仍有另一把可用 Key 时才熔断当前 Key;单 Key 不进入熔断态
- 熔断后的 Key 标记
isBroken: true,记录disabledTime - 错误消息截断至 2000 字符,防止配置文件膨胀(第 196 行)
自动恢复(pickKey()):
autoRecoveryTime默认 60 秒- 轮询时检查已熔断 Key 是否超期,超期则重置
持久化:
// 核心暴露
const { pickKey, reportSuccess, reportFailure, resetAllBroken } =
useLlmKeyManager();2.3 请求编排中间件 — useLlmRequest
这是整个 LLM 请求的中心调度器,串联渠道配置、Key 管理、参数过滤和适配器分发。
完整请求流程(sendRequest()):
sendRequest(options)
│
├─ 1. 获取渠道配置(getProfileById)
│ └─ 检查启用状态、检查模型是否存在
│
├─ 2. 选取 API Key(pickKey)
│ └─ 构造 effectiveProfile,注入选中的 Key
│
├─ 3. 特种请求自动分发
│ ├─ Embedding → adapter.embedding()
│ └─ Rerank → 模拟响应(暂未完整实现)
│
├─ 4. 参数过滤(filterParametersByCapabilities)
│ └─ 合并模型 customParameters
│ └─ 注入网络行为配置(hasLocalFile / forceProxy 等)
│ └─ 自动检测本地/IP 地址,强制代理
│
├─ 5. 适配器分发
│ ├─ videoGeneration → adapter.video()
│ ├─ imageGeneration → adapter.image()
│ ├─ audioGeneration → adapter.audio()
│ └─ default → adapter.chat()
│
├─ 6. 成功 → reportSuccess() → 返回 LlmResponse
│
└─ 7. 失败
├─ TimeoutError → 记录警告
├─ AbortError (用户取消)
│ └─ 有 requestId → 向上游发送 /v1/interrupt 停止信号
└─ 其他错误 → reportFailure() → 抛出原始错误关键特性:
- 自动代理协商: 检测
local-file://协议或本地/IP 地址时,自动开启 Rust 后端代理(第 149-223 行) - forceChatMode: 支持通过
_forceChatMode或preferChat能力标记,强制使用对话接口进行媒体生成(如 Gemini 原生生图模型)(第 252 行) - 取消传播: 用户取消请求时,若提供了
requestId,自动补发/v1/interrupt通知上游服务端停止生成(第 320-346 行)
3. 适配器层 (Adapter Layer)
目录: src/llm-apis/
适配器层作为防腐层 (Anti-Corruption Layer),屏蔽不同服务商 API 的差异性,为上层提供统一的多模态调用接口。
3.1 目录结构
src/llm-apis/
├── common.ts # 统一请求/响应接口、错误类型、超时控制
├── request-builder.ts # 参数过滤、消息解析、模型家族识别
├── model-fetcher.ts # 模型列表发现与元数据增强
├── embedding.ts # Embedding 任务的统一入口
├── embedding-types.ts # Embedding 相关类型定义
├── adapters/
│ ├── index.ts # 适配器注册表 / LlmAdapter 接口
│ ├── openai/ # OpenAI 兼容协议(chat / image / audio / video / responses)
│ ├── anthropic/ # Anthropic Claude 协议
│ ├── gemini/ # Google Gemini 协议
│ ├── vertexai/ # Google Vertex AI 协议
│ ├── cohere/ # Cohere v2 协议
│ ├── xai/ # xAI Grok 协议
│ ├── siliconflow/ # SiliconFlow 图片生成(独立于 OpenAI 兼容)
│ └── suno-newapi/ # Suno 音乐生成 (NewAPI 协议)
└── 参考docs/ # 各 API 的参考文档(非代码)
packages/llm-core/src/
├── types/ # canonical 聊天、Embedding、媒体、模型列表与任务类型
├── providers/ # 纯 URL/Header/body 构建和响应语义解析
├── executor.ts # 聊天统一执行器
├── embedding-executor.ts # Embedding 执行器
├── media-executor.ts # 同步媒体执行器
├── model-list-executor.ts # 模型列表执行器
├── async-media-executor.ts # 异步媒体任务生命周期
└── stream-parser/ # 增量 SSE / JSONL 分帧3.2 统一适配器接口 — LlmAdapter
export interface LlmAdapter {
chat(profile: LlmProfile, options: LlmRequestOptions): Promise<LlmResponse>;
embedding?(
profile: LlmProfile,
options: EmbeddingRequestOptions
): Promise<EmbeddingResponse>;
image?(
profile: LlmProfile,
options: MediaGenerationOptions
): Promise<LlmResponse>;
audio?(
profile: LlmProfile,
options: MediaGenerationOptions
): Promise<LlmResponse>;
video?(
profile: LlmProfile,
options: MediaGenerationOptions
): Promise<LlmResponse>;
}3.3 适配器分发映射 — adapters
| adapters key | 实现 | ProviderType | 说明 |
|---|---|---|---|
openai | openAiAdapter | openai | OpenAI 官方 |
openai-compatible | openAiAdapter | openai-compatible | 第三方中转 |
openai-responses | openAiResponsesAdapter | openai-responses | OpenAI 有状态接口 |
azure | azureOpenAiAdapter | azure | Azure OpenAI |
groq | openAiAdapter | groq | Groq LPU |
mistral | openAiAdapter | — | Mistral AI |
perplexity | openAiAdapter | — | Perplexity |
deepseek | openAiAdapter | deepseek | 深度求索 |
together | openAiAdapter | — | Together AI |
openrouter | openAiAdapter | openrouter | OpenRouter |
ollama | openAiAdapter | ollama | Ollama 本地 |
lmstudio | openAiAdapter | — | LM Studio |
vllm | openAiAdapter | — | vLLM |
volcengine | openAiAdapter | — | 火山引擎 |
dashscope | openAiAdapter | — | 阿里百炼 |
zhipu | openAiAdapter | — | 智谱 AI |
moonshot | openAiAdapter | — | 月之暗面 |
siliconflow | openAiAdapter (+ image override) | siliconflow | 硅基流动 |
xai | xAiAdapter | xai | xAI Grok |
gemini | geminiAdapter | gemini | Google Gemini |
claude | anthropicAdapter | claude | Anthropic |
vertexai | vertexAiAdapter | vertexai | Vertex AI |
cohere | cohereAdapter | cohere | Cohere |
suno-newapi | sunoNewApiAdapter | suno-newapi | 音乐生成 |
重要:大部分国产/聚合平台通过
openai-compatible协议走openAiAdapter,无需编写独立适配器。只要 API 格式与 OpenAI Chat Completions 一致,只需在adapters/index.ts添加映射并可在llm-presets.ts添加预设即可。 Azure 渠道使用 deployment 风格的 Chat Completions / Embeddings;azureOpenAiAdapter复用 OpenAI wire format,同时负责{resource}/{deployment}、api-version与api-key鉴权转换。
3.4 请求构建器 — request-builder.ts
这是适配层的核心逻辑模块,负责:
模型家族识别 —
getModelFamily()基于元数据系统中的group字段判断模型家族(openai/claude/gemini/cohere/deepseek/qwen/xai),以应用特定参数规则。若元数据未匹配,回退到provider字符串推断。多模态消息解析 —
parseMessageContents()将统一消息数组解析为分类结构,支持:文本、图片、音频、视频、文档、tool_use、tool_result。智能参数过滤 —
filterParametersByCapabilities()三重过滤策略:- Provider 级: 基于
supportedParameters参数定义表初筛 - Model 级: 基于
ModelCapabilities细化 - Model Family 级: 基于
getModelFamily()的结果保护专有参数(如stopSequences仅在claude家族保留)
- Provider 级: 基于
自定义参数透传 —
applyCustomParameters()将不在KNOWN_NON_MODEL_OPTIONS_KEYS黑名单中的参数透传到请求体,支持未知参数的灵活下发。
3.5 统一消息与响应格式
消息内容类型(LlmMessageContent):
type LlmMessageContent =
| TextContent // type: "text"
| ImageContent // type: "image" — base64图片
| AudioContent // type: "audio" — 支持 base64 / file_uri
| VideoContent // type: "video" — 支持 startOffset / endOffset / fps
| DocumentContent // type: "document" — PDF等文档
| ToolUseContent // type: "tool_use"
| ToolResultContent; // type: "tool_result"响应结构(LlmResponse):
标准字段:content, usage, reasoningContent, toolCalls, finishReason 媒体字段:images[], videos[], audios[], audioData 高级字段:annotations(引用注释), timings(性能指标), revisedPrompt, thought
3.6 统一超时与错误处理 — common.ts
- 默认超时:145 秒 (
DEFAULT_TIMEOUT) - 媒体生成超时:600 秒 (
DEFAULT_MEDIA_TIMEOUT) - 超时控制:
fetchWithTimeout()— 带双重中止信号管理 - 代理传输:桌面默认经带 capability token 的 Rust 回环代理;普通请求走
/proxy/raw原样流式转发,含 tagged/兼容期文件引用的 JSON 才走/proxy/json-expand - 文件上传:浏览器
FormData透明转发;顶层file-ref与含本地文件的 multipart manifest 由 Rust 流式读取,本地文件内容不进入 WebView - 移动文件上传:普通请求继续走 Tauri HTTP;含 tagged JSON、顶层或 multipart
LocalFileRef的请求改走移动 Rust command,并按requestId支持取消。本地文件字节不进入 WebView - 安全边界:代理只接受当次运行 token 与 Tauri/loopback Origin,过滤代理元 Header,并在日志 URL 中移除 query/fragment
- 错误类型:
TimeoutError,LlmApiError,isAbortError()
3.7 嵌入任务入口 — embedding.ts + embedding-types.ts
根据 profile.type 自动路由到对应的适配器实现。嵌入类型定义支持 dimensions、taskType (Gemini/Cohere)、encodingFormat (Cohere) 等参数。
3.8 模型获取与元数据 — model-fetcher.ts
- 动态发现:共享
modelListAdapter负责 Provider URL、鉴权、错误和响应归一化 - 元数据丰富:桌面/移动 Facade 在模型写入阶段结合各自
model-metadata规则增强分组、Token 限制与能力;运行时请求不会反向读取规则 - 图标匹配:通过
normalizeIconPath()和getModelIconPath()自动匹配预设图标
3.9 共享 Core 与执行器
ProviderAdapter:聊天、Responses、Claude、Cohere、Gemini、Vertex 的纯请求构建、非流式解析和增量 DecoderEmbeddingProviderAdapter:OpenAI、Gemini、Cohere、Vertex 单条/批量 EmbeddingSyncMediaProviderAdapter:OpenAI/xAI/Gemini/SiliconFlow 图片与 OpenAI TTSModelListProviderAdapter:OpenAI 系、Anthropic、Gemini、Cohere、Vertex、Ollama 模型发现AsyncMediaTaskAdapter:OpenAI/Ark/Agnes 视频、Gemini Veo、Suno 与 MiniMax 的创建、轮询、进度、取消和资产终态
共享包禁止导入 Vue、Pinia、Tauri、应用 Store、logger 或 UI。Provider Adapter 不执行 fetch/invoke,Transport 不理解 Provider 语义。
4. 完整请求生命周期
用户发送消息
│
▼
useLlmRequest.sendRequest(options)
│
├── 获取渠道配置 (useLlmProfiles.getProfileById)
├── 验证渠道启用 + 模型存在
├── 选取 Key 注入渠道 (useLlmKeyManager.pickKey)
│
├── 特种请求分流 (Embedding / Rerank)
│
├── 参数过滤 (filterParametersByCapabilities)
│ ├── Provider 级
│ ├── Model 级
│ └── Model Family 级
│
├── 注入网络行为配置
│ ├── hasLocalFile → 代理
│ ├── forceProxy → 代理
│ └── 本地/IP 地址 → 自动代理
│
├── 应用 Facade 分发 (adapters[profile.type])
│ ├── adapter.video()
│ ├── adapter.image()
│ ├── adapter.audio()
│ └── adapter.chat()
│ │
│ ├── 映射 canonical DTO + 注入 Profile/TransportOptions
│ ├── @aiohub/llm-core 构建 WireRequest / 解析响应
│ ├── Desktop/Mobile Transport 执行网络与文件 I/O
│ └── Facade 映射回现有 LlmResponse/回调
│
├── 成功 → reportSuccess() → 返回响应
│
└── 失败
├── 超时 → warn + 抛出 TimeoutError
├── 取消 → 补发 /v1/interrupt 停止信号
└── 其他 → reportFailure() + 抛出错误5. 扩展指南
5.1 添加新服务商(三步骤)
步骤 1: 类型注册
- 在
ProviderType中添加新类型 - 在
providerTypes中添加ProviderTypeInfo配置(参数支持范围、端点等) - 在
llmPresets中添加预设模板(可选,仅需 UI 快捷创建时)
步骤 2: 适配实现
- 若 API 格式与 OpenAI Chat Completions 兼容 → 只需注册到
adapters映射复用openAiAdapter - 若不兼容 → 在
adapters/下创建目录,实现LlmAdapter接口 - 在
adapters注册表中添加映射
步骤 3: 协议参考
- 在
src/llm-apis/参考docs/下添加 API 参考文档(可选,方便后续维护)
5.2 添加新模型能力
- 在
ModelCapabilities中定义新能力 - 在
filterParametersByCapabilities()中添加对应过滤逻辑 - 在
KNOWN_NON_MODEL_OPTIONS_KEYS中注册参数名(防止被透传或清理) - 在
model-metadata-presets.ts中为对应模型配置该能力
5.3 添加新的预设模板
在 llmPresets[] 数组中添加新条目:
{
type: "openai", // 适配器 key
name: "My Custom Service", // UI 显示名称
description: "...",
defaultBaseUrl: "https://api.example.com/v1",
logoUrl: "/model-icons/myservice.svg",
links: [{ label: "官网", url: "https://..." }],
defaultModels: [
{
id: "my-model-1",
name: "My Model 1",
group: "My Models",
provider: "myservice",
capabilities: { toolUse: true },
},
],
}6. 最佳实践
- 能力驱动开发: 业务逻辑应依赖
ModelCapabilities检测(如capabilities.thinking),而非硬编码模型 ID - 利用 Request Builder: 优先使用
filterParametersByCapabilities和cleanPayload处理请求体,确保 API 兼容性 - 统一媒体处理: 新 Provider 的图片、音频、视频、音乐和模型列表协议优先实现于
@aiohub/llm-core,应用目录只保留 Profile、业务参数和响应兼容映射 - 文件引用: 大文件使用 tagged
LocalFileRef,不要在 Facade 中预读成 Base64;Provider JSON、multipart part 与顶层请求体均已有明确契约 - Key 管理: 多 Key 配置且开启自动熔断后,合理配置
autoRecoveryTime,429 熔断后自动恢复;单 Key 或无多渠道容灾时保持默认关闭 - 代理策略: 桌面外部请求默认走 Rust 代理,可通过
networkStrategy: "native"直连;涉及LocalFileRef或兼容期local-file://时始终走 Rust 原生文件路径,不能把路径或引用对象直接发送给 Provider