Skip to content

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 预设结构

typescript
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 行
typescript
// 核心暴露
const {
  profiles,
  saveProfile,
  deleteProfile,
  getProfileById,
  createFromPreset,
} = useLlmProfiles();

2.2 多 Key 管理 — useLlmKeyManager

支持一个渠道配置绑定多个 API Key,提供轮询 + 可选熔断 + 恢复能力。熔断开关和恢复时长按渠道独立存储,自动熔断默认关闭,建议仅在多 Key 配置且确认需要故障摘除时开启。

轮询策略pickKey()):

  1. 调用 syncKeyStates() 同步所有 Key 的状态
  2. 过滤出可用 Key:isEnabled && !isBroken
  3. lastUsedIndices 记录的下标开始轮询下一个可用 Key
  4. 若无可用 Key,回退到第一个 Key,由下游 API 报错触发反馈

熔断逻辑reportFailure()):

  • 开启自动熔断后,识别 429 Too Many Requests,直接熔断
  • 开启自动熔断后,连续 3 次非 429 暂态错误也触发熔断
  • 仅当同一渠道仍有另一把可用 Key 时才熔断当前 Key;单 Key 不进入熔断态
  • 熔断后的 Key 标记 isBroken: true,记录 disabledTime
  • 错误消息截断至 2000 字符,防止配置文件膨胀(第 196 行

自动恢复pickKey()):

  • autoRecoveryTime 默认 60 秒
  • 轮询时检查已熔断 Key 是否超期,超期则重置

持久化

  • 状态存储于 key-states.json第 16 行
  • 写入采用防抖保存,不阻塞请求流程(第 57 行
typescript
// 核心暴露
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: 支持通过 _forceChatModepreferChat 能力标记,强制使用对话接口进行媒体生成(如 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

typescript
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说明
openaiopenAiAdapteropenaiOpenAI 官方
openai-compatibleopenAiAdapteropenai-compatible第三方中转
openai-responsesopenAiResponsesAdapteropenai-responsesOpenAI 有状态接口
azureazureOpenAiAdapterazureAzure OpenAI
groqopenAiAdaptergroqGroq LPU
mistralopenAiAdapterMistral AI
perplexityopenAiAdapterPerplexity
deepseekopenAiAdapterdeepseek深度求索
togetheropenAiAdapterTogether AI
openrouteropenAiAdapteropenrouterOpenRouter
ollamaopenAiAdapterollamaOllama 本地
lmstudioopenAiAdapterLM Studio
vllmopenAiAdaptervLLM
volcengineopenAiAdapter火山引擎
dashscopeopenAiAdapter阿里百炼
zhipuopenAiAdapter智谱 AI
moonshotopenAiAdapter月之暗面
siliconflowopenAiAdapter (+ image override)siliconflow硅基流动
xaixAiAdapterxaixAI Grok
geminigeminiAdaptergeminiGoogle Gemini
claudeanthropicAdapterclaudeAnthropic
vertexaivertexAiAdaptervertexaiVertex AI
coherecohereAdaptercohereCohere
suno-newapisunoNewApiAdaptersuno-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-versionapi-key 鉴权转换。

3.4 请求构建器 — request-builder.ts

这是适配层的核心逻辑模块,负责:

  • 模型家族识别getModelFamily() 基于元数据系统中的 group 字段判断模型家族(openai / claude / gemini / cohere / deepseek / qwen / xai),以应用特定参数规则。若元数据未匹配,回退到 provider 字符串推断。

  • 多模态消息解析parseMessageContents() 将统一消息数组解析为分类结构,支持:文本、图片、音频、视频、文档、tool_use、tool_result。

  • 智能参数过滤filterParametersByCapabilities() 三重过滤策略:

    1. Provider 级: 基于 supportedParameters 参数定义表初筛
    2. Model 级: 基于 ModelCapabilities 细化
    3. Model Family 级: 基于 getModelFamily() 的结果保护专有参数(如 stopSequences 仅在 claude 家族保留)
  • 自定义参数透传applyCustomParameters() 将不在 KNOWN_NON_MODEL_OPTIONS_KEYS 黑名单中的参数透传到请求体,支持未知参数的灵活下发。

3.5 统一消息与响应格式

消息内容类型LlmMessageContent):

typescript
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 自动路由到对应的适配器实现。嵌入类型定义支持 dimensionstaskType (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 的纯请求构建、非流式解析和增量 Decoder
  • EmbeddingProviderAdapter:OpenAI、Gemini、Cohere、Vertex 单条/批量 Embedding
  • SyncMediaProviderAdapter:OpenAI/xAI/Gemini/SiliconFlow 图片与 OpenAI TTS
  • ModelListProviderAdapter: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 添加新模型能力

  1. ModelCapabilities 中定义新能力
  2. filterParametersByCapabilities() 中添加对应过滤逻辑
  3. KNOWN_NON_MODEL_OPTIONS_KEYS 中注册参数名(防止被透传或清理)
  4. model-metadata-presets.ts 中为对应模型配置该能力

5.3 添加新的预设模板

llmPresets[] 数组中添加新条目:

typescript
{
  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: 优先使用 filterParametersByCapabilitiescleanPayload 处理请求体,确保 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

Released under the Apache-2.0 License.