Files
agcore/design/pdd/9-llm-provider-unified-interface.md
T
徐涛 28ca43ccb2 chore(docs): 将设计文档从 docs 移至 design 目录
将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
2026-07-23 05:45:53 +08:00

114 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LLM Provider 统一接口设计(方案 C)
> 本文档已被拆分为独立的子文档以便深入推演和修改。以下保留背景与架构总览作为索引。
>
> **拆分日期**2026-06-15
> **拆分方式**:原 §1-§2 保留在本文件,§3-§13 移至 `9a`-`9g` 子文档。
> 所有"待深入推演"议题保留在对应子文档的原文位置。
---
## 1. 背景与目标
### 1.1 当前状态
`LlmProvider` trait 的请求/响应类型直接绑定到 OpenAI Chat Completion API 格式:
```rust
pub type ChatRequest = OpenaiChatRequest; // 类型别名
pub type ChatResponse = struct { message: OpenaiChatMessage, ... };
pub type Message = OpenaiChatMessage;
```
所有 "内部统一类型" 都是 OpenAI 格式的直接映射。这导致:
| API 类型 | 兼容性 | 代价 |
|----------|--------|------|
| OpenAI ChatDeepSeek、Qwen 等) | ✅ 原生兼容 | 零 |
| Anthropic Messages | ❌ 语义丢失 | 需逆向映射,丢失 thinking 等特性 |
| OpenAI Response API | ❌ 范式不兼容 | ChatResponse 无法表达多类型 output |
| 非标自定义 API | ❌ 无扩展点 | 只能走 extra_body 逃生舱 |
### 1.2 目标
设计一套**真正与 Provider 无关的内部统一类型(IR)**,使得:
1. 所有 Provider 对外暴露的接口完全一致(统一 trait)
2. 每个 Provider 内部自行完成 IR ↔ 原生格式的映射
3. 上层(LlmCycle、AgentSession)完全感知不到具体 Provider
4. 新 Provider 只需实现一次双向映射即可接入
5. 各 API 的独有特性(thinking、内置工具等)有表达空间
### 1.3 非目标
- 不追求覆盖所有 API 的每一个参数(90% 核心流程即可)
- 不追求在不改上层代码的情况下切换 Provider(接口一致足以)
- 不试图让 OpenAI Response API 的内置工具完全融入消息循环(通过逃生舱 + 可选能力 trait)
---
## 2. 架构总览
```
┌──────────────────────────────────────────────────────────────┐
│ 上 层(AgentSession / TaskAgent
│ 只与 IR 类型和 LlmProvider trait 交互 │
├──────────────────────────────────────────────────────────────┤
│ LlmCycle │
│ 循环 / 重试 / Tool 循环 / Hook / Compact / CostTracker │
│ 内部使用 Vec<Message> + MessageRequest │
├──────────────────────────────────────────────────────────────┤
│ LlmProvider trait(核心接口) │
│ chat(MessageRequest) → Result<MessageResponse, LlmError> │
│ chat_stream(MessageRequest) → Stream<StreamEvent> │
│ capabilities() → ProviderCapabilities │
├────────────────┬──────────────────────┬──────────────────────┤
│ OpenaiProvider │ AnthropicProvider │ DeepSeekProvider ... │
│ IR ↔ OpenAI │ IR ↔ Anthropic │ IR ↔ OpenAI 格式 │
│ JSON │ JSON │ (兼容 Chat API) │
└────────────────┴──────────────────────┴──────────────────────┘
```
### 2.1 分层原则
- **IR 层**:全项目唯一的内部表示,与任何具体 API 格式无关
- **Provider 层**:每个 Provider 实现 IR ↔ 原生格式的双向映射,复杂度隔离在此层
- **上层**:只与 IR 和 `LlmProvider` trait 交互,不感知具体 Provider
---
## 文件索引
### 核心设计
| 文件 | 内容 | 包含待深入推演 |
|------|------|---------------|
| [9a-background-and-architecture.md](9a-background-and-architecture.md) | §1 背景与目标 + §2 架构总览 | — |
| [9b-ir-type-system.md](9b-ir-type-system.md) | §3 IR 类型体系:[ContentBlock](9b-ir-type-system.md#31-contentblock--最小的内容单元)、[Message](9b-ir-type-system.md#32-message--统一消息类型)、[StopReason](9b-ir-type-system.md#33-stopreason--统一停止原因)、[MessageRequest](9b-ir-type-system.md#36-messagerequest--统一请求)、[MessageResponse](9b-ir-type-system.md#37-messageresponse--统一响应) | ToolResult 嵌套约束、extra 类型安全性 |
| [9c-llm-provider-trait.md](9c-llm-provider-trait.md) | §4 LlmProvider Trait[核心接口](9c-llm-provider-trait.md#41-核心接口)、[ProviderCapabilities](9c-llm-provider-trait.md#42-providercapabilities)、[StreamEvent](9c-llm-provider-trait.md#43-流式事件-streamevent)、[汇聚算法](9c-llm-provider-trait.md#44-partialmessageresponse--流式事件的汇聚算法) | — |
| [9d-provider-implementations.md](9d-provider-implementations.md) | §5 Provider 实现:[OpenAI](9d-provider-implementations.md#51-openai-provider兼容-chat-api)、[Anthropic](9d-provider-implementations.md#52-anthropicprovidermessages-api)、[Response API](9d-provider-implementations.md#53-openai-response-api草案)、[DeepSeek/Qwen](9d-provider-implementations.md#54-deepseek--qwen-等兼容-provider-的落地策略) | OpenAI 流式转换、Anthropic 流式状态机、Response API 映射、DeepSeek/Qwen 落地 |
| [9e-llm-cycle-and-upstream.md](9e-llm-cycle-and-upstream.md) | §6-§8LlmCycle 改造(build_request、tool 循环、submit_stream、compact+ 上层影响 + 兼容策略 | system prompt 双重表达冲突、compact 适配 |
| [9f-edge-cases.md](9f-edge-cases.md) | §9 边界情况:工具定义传递、Thinking 端到端、Multiple ContentBlock、多 Choice、内置工具 | — |
### 辅助参考
| 文件 | 内容 |
|------|------|
| [9g-risk-and-migration.md](9g-risk-and-migration.md) | §10 风险评估 + §11 类型差异总结 + §12 迁移路径(4 Phase+ §13 验收标准(A1-A10 |
### 待深入推演完整清单
| # | 议题 | 所在文件 | 优先级 |
|---|------|---------|--------|
| 1 | ToolResult 嵌套约束 | [9b-ir-type-system.md](9b-ir-type-system.md#31-contentblock--最小的内容单元) | ✅ 已推演(方案 C:运行时过滤) |
| 2 | extra 的类型安全性 | [9b-ir-type-system.md](9b-ir-type-system.md#36-messagerequest--统一请求) | ✅ 已推演(方案 BResult-based access |
| 3 | StreamEvent 汇聚为 MessageResponse 算法 | [9c-llm-provider-trait.md](9c-llm-provider-trait.md#44-partialmessageresponse--流式事件的汇聚算法) | ✅ 已推演(方案 B:显式边界 + BTreeMap 分桶) |
| 4 | ToolCallStart index 归一化 | [9c-llm-provider-trait.md](9c-llm-provider-trait.md#43-流式事件-streamevent) | ✅ 已推演(自动解决,ToolCallStart 合并到 ContentBlockStart |
| 5 | OpenAI 流式转换实现 | [9d-provider-implementations.md](9d-provider-implementations.md#51-openai-provider兼容-chat-api) | ✅ 已推演(方案 ASseByteStream 通用层 + OpenaiStreamToEvents 状态机,ToolCallEnd 依赖 finalize 兜底,忽略多 Choice |
| 6 | Anthropic 流式状态机设计 | [9d-provider-implementations.md](9d-provider-implementations.md#52-anthropicprovidermessages-api) | ✅ 已推演(轻量分发器:3 状态 + 7 种事件映射 + 零 index 映射) |
| 7 | OpenAI Response API 完整映射表 | [9d-provider-implementations.md](9d-provider-implementations.md#53-openai-response-api草案) | 低 |
| 8 | DeepSeek/Qwen Provider 落地策略 | [9d-provider-implementations.md](9d-provider-implementations.md#54-deepseek--qwen-等兼容-provider-的落地策略) | 低 |
| 9 | system prompt 双重表达冲突 | [9e-llm-cycle-and-upstream.md](9e-llm-cycle-and-upstream.md#62-build_request--新签名) | ✅ 已推演(方案 D:移除 system 字段,IR 只留一个入口,Provider 层负责映射) |
| 10 | compact 在 IR 上的改法与 token 估算 | [9e-llm-cycle-and-upstream.md](9e-llm-cycle-and-upstream.md#66-compact-逻辑调整) | ✅ 已推演(三个子议题各有方案决策 + 二维决策框架) |
| 11 | Thinking signature 端到端传递 | [9f-edge-cases.md](9f-edge-cases.md#92-thinking-的端到端流程) | ✅ 已推演(方案 CMessageComplete 兜底 + finalize 回填) |