ADR-008 — LLM Provider 接口抽象
决策
FinBayes Provider Adapter Pool 内部对所有 LLM Provider 采用 统一 OpenAI-compatible 接口,不直接调用各家 SDK。
M0 阶段最小实施:
- 统一接口:Provider Adapter Pool 内部所有 LLM 调用走 OpenAI Chat Completions API + Function Calling 协议(含 streaming / tool_calls / usage 字段)
- 客户端实现:基于
httpx.AsyncClient自实现最小客户端(不强依赖 LiteLLM / LangChain);接口签名见 m0-walking-skeleton §2call_llm - M0 仅 L1 implement:仅实现 L1 用户配置 Provider 的调用路径(OpenAI-compatible 端点 + API key 从 OS Keychain 读取,详见 m0-walking-skeleton §13 Provider key 加载顺序);L1' / L2 / L3 / L4 全部 stub(详见 m0-walking-skeleton §1 表)
- 统一响应类型:
LLMResponsePydantic 模型(详见 m0-walking-skeleton §3)含content / tool_calls / finish_reason / usage四字段,与 OpenAI 响应字段 1:1 - Provider 配置格式:
providers.yaml内 Provider 条目最小字段:provider_id(格式<provider_type>:<model_id>)/endpoint_url/api_key_ref(指向 Keychain 中的 key 名)/model/enabled
M1+ 演化空间:
- L1' 系统默认 Provider(FinBayes 团队代付小流量兜底)
- L2 本地嵌入式 LLM(Ollama / vLLM 集成,仍走 OpenAI-compatible 端点)
- L3 缓存 + 规则兜底(BM25 + 关键词词典,详见 CHAP-09)
- L4 受限菜单 UI
- 跨 Provider 路由(task_routing.yaml 配置任务类型 → Provider 偏好)
上下文
业界 LLM Provider 接口现状:
| Provider | 接口 |
|---|---|
| OpenAI | OpenAI Chat Completions + Function Calling(原生) |
| Anthropic | 自有 Messages API + tool use,也提供 OpenAI-compatible endpoint |
| DeepSeek / Qwen / Moonshot / Mistral / 等 | 自有 + OpenAI-compatible endpoint |
| Ollama / vLLM / LM Studio | 自有 + OpenAI-compatible endpoint |
| Google Gemini | 自有 Vertex AI + GenAI SDK;第三方代理可转 OpenAI-compatible |
OpenAI-compatible 是事实标准。第三方库(LiteLLM 等)做了同类抽象但增加依赖与版本管理风险。
FinBayes 工程范式(ADR-001 + ADR-003)要求接口抽象不依赖外部库的稳定性 —— 自实现最小客户端比依赖 LiteLLM 风险低(M1+ 视情况再评估是否引入)。
后果
收益:
- 新增 Provider 不动 runtime 业务代码(仅在 providers.yaml 加条目 + Provider Adapter Pool 内部测试 fixture)
- 与 ADR-004 Function Calling 主导一致(OpenAI 协议是 Function Calling 的事实标准)
- LLMResponse 模型固定让综合层 / 审计 / 评估 闭环不受 Provider 变化影响
- 多 Provider 在统一接口下做评估对比变得简单(详见 CHAP-21)
成本:
- 各 Provider 端实现的 OpenAI-compatible 兼容度参差不齐(如
finish_reason枚举值不一致 /tool_calls字段位置差异)→ Provider Adapter 内部做最小归一化 - 个别 Provider 独有能力(如 Anthropic prompt caching / Gemini 多模态)需要绕过统一接口直接调原生 → M0 不支持,M2+ 可在 Provider Adapter 内做"扩展点"承接
残余风险:
- OpenAI 端 API 变化(如 GPT-5 引入新字段)→ fixture 版本检测 + Provider Adapter 内部适配
- Provider 端突然废弃 OpenAI-compatible 端点 → fallback 到 L1' 系统默认(M1+)
备选方案
考虑过未采用:
- 直接调各家原生 SDK:M0 工作量翻倍 / 跨 Provider 测试复杂 / 评估对比难
- 依赖 LiteLLM:增加外部库依赖管理风险 / LiteLLM 自身 API 变化频繁 / 调试链路加长
- 直接抽象到自定义 Protocol:抽象层更高但脱离业界标准 / 新 Provider 接入时仍要做映射
关联
- 触发章节:CHAP-09 Provider Adapter Pool 子系统
- 影响章节:CHAP-09 / CHAP-13 故障与降级路径 / CHAP-15 数据存储划分(Credential Store)
- M0 实施承接:m0-walking-skeleton.md §2 Provider Adapter Pool 接口子集 + §3 LLMResponse 模型 + §13 Bootstrap 模板(httpx 客户端配置)
- 关联 ADR:ADR-004(任务识别策略)/ ADR-010(输出端过滤)
- 后续:M1+ 起新增 L1' / L2 / L3 / L4 实现时本 ADR 不变;任何 OpenAI-compatible 协议层变更走新 ADR 替代或 patch