跳转到内容

多模型 Provider 路由

多模型 Provider 路由

学习目标

理解 Khoj 如何通过模型槽位系统和 PriceTier 门控实现多 LLM 提供商的统一管理与路由。

前置知识

本章涉及多模型适配架构的通用原理,建议先阅读:

下文假设你已理解上述概念,直接聚焦 Khoj 的模型槽位系统和 PriceTier 门控实现。

项目实践

三大 Provider 统一抽象

Khoj 的 src/khoj/processor/conversation/ 目录下有三个 provider 子目录:

conversation/
├── openai/
│ ├── gpt.py # OpenAI 聊天接口
│ └── utils.py # 补全、重试、超时处理
├── anthropic/
│ ├── anthropic_chat.py # Anthropic 聊天接口
│ └── utils.py
└── google/
├── gemini_chat.py # Google Gemini 接口
└── utils.py

每个 provider 实现一个 converse() 函数,接受统一的 ChatML 格式消息,返回流式响应。agenerate_chat_response() 根据 chat_model.model_type 枚举(OPENAI / ANTHROPIC / GOOGLE)切换到对应的 provider。

模型槽位系统

Khoj 的 ServerChatSettings 定义了 6 个模型槽位

槽位用途典型模型
chat_default默认对话模型Gemini 2.5 Flash
chat_advanced高级对话模型Claude Sonnet 4
think_free_fast免费用户推理(快速)Haiku 4.5
think_free_deep免费用户推理(深度)Claude 3.7 Sonnet
think_paid_fast付费用户推理(快速)Haiku 4.5
think_paid_deep付费用户推理(深度)Opus 4

这种设计的核心思路是:不同用户群体使用不同模型,同一用户在不同场景下自动切换模型。免费用户的 deepthought 使用免费额度内的模型,付费用户可以使用更强大的模型。

PriceTier 门控

ChatModel:
- price_tier: FREE | STANDARD
- subscribed_max_prompt_size: 订阅用户的最大 prompt 大小

每个 ChatModel 关联一个 PriceTier。用户能否使用该模型取决于其订阅状态:

  • 免费用户:只能使用 FREE 层级的模型
  • 付费用户(STANDARD 订阅):可以使用所有模型,且享受更大的上下文窗口

ChatModel 通过 AiModelApi 关联 API 密钥,实现了模型配置与凭据的分离:管理员统一管理 API 密钥,用户只需选择模型,无需关心密钥配置。

OpenAI 结构化输出自适应

OpenAI provider 实现了三级结构化输出支持检测:

StructuredOutputSupport:
- TOOL: 通过 tool_use 实现结构化输出
- SCHEMA: 原生 JSON schema 支持
- OBJECT: response_format object 模式

在初始化时自动检测当前模型支持的结构化输出等级,选择最合适的 API 调用方式。这意味着同一个 Khoj 部署可以无缝适配不同版本的 OpenAI 模型(如旧版仅支持 response_format,新版支持 tool_use)。

支持模型一览

提供商支持模型
OpenAIgpt-4o, gpt-4o-mini, gpt-4.1 系列, o1/o1-mini, o3/o3-mini/o3-pro, o4-mini, gpt-5 系列
Googlegemini-3-pro, gemini-2.5 flash/pro, gemini-2.0, gemini-1.5
AnthropicClaude 3.5/3.7 Sonnet, 3.5 Haiku, Haiku 4.5, Sonnet 4, Opus 4

问题与规避

问题影响规避策略
模型 API 不可用用户无法对话多个槽位配置不同的模型作为备选;provider utils.py 中实现 backoff 重试
Vision 模型需要额外配置图片理解不可用vision_enabled 字段标记支持视觉的模型,自动获取 vision 模型配置
不同模型的 prompt 大小限制不同长对话可能超出限制model_to_prompt_size 映射表管理每个模型的最大上下文
Reasoning 模型需要特殊提示deepthought 模式输出质量下降为 reasoning 模型使用独立的 system prompt 和参数(如 max_completion_tokens

设计取舍

为什么是 6 个固定槽位而不是动态的模型列表?

固定槽位简化了路由逻辑——系统只需要判断”当前用户 + 当前场景 → 哪个槽位”,而不需要复杂的优先级匹配。缺点是灵活性受限:如果需要新增场景(如”夜间模式使用便宜模型”),就需要增加新槽位。

为什么不使用模型路由/故障转移层?

Khoj 的模型选择是管理员预先配置的,而非运行时动态路由。原因是 Khoj 的目标用户是个人/小企业,模型选择是管理决策而非技术决策——管理员决定用哪些模型、分配给什么层级的用户。这与企业级 SRE 场景中的自动故障转移不同。

参考来源