# LLM Provider 选型与协议策略（v0.1）

> **本文档定位**：补充 [`00-architecture.md`](./00-architecture.md) 的 Q4 / Q7 / Q8 决策详情。回答「为什么走 OpenAI 协议、为什么必须做 ModelProvider 抽象、为什么必须支持 MCP、为什么要预留 inbound 协议适配位」。
>
> **依赖背景**：[`claude-code-reference.md`](./claude-code-reference.md) 整体架构概览。
>
> **更新触发条件**：默认 provider 切换、新增 provider、协议层重大变化、MCP 规范升级。

---

## 1. 决策总览

| 决策点 | 当前选择 | 理由摘要 |
|---|---|---|
| 默认 LLM provider | **OpenAI Codex (GPT-5.4)** | 工具调用能力业界领先；2025 年重启的 Codex 配套产品成熟 |
| 内部协议 IR | **OpenAI Chat/Responses 风格** | 业界事实标准（LiteLLM / OpenRouter / Continue / Cline 共同选择） |
| Provider 抽象 | **必须做**（ModelProvider 接口） | 第一天成本几乎为零，未来换底座成本剧增 |
| 工具暴露形式 | **MCP server**（不是私有 function schema） | MCP 已是 OpenAI/Anthropic 跨厂商事实标准 |
| 远程会话协议 | **SSE**（PR2 实现） | 浏览器原生支持、企业代理友好；保留 WebSocket 升级位 |
| Inbound 兼容 | **预留位置，本期不实现** | 未来加一层 Anthropic Messages 适配，让外部客户端接我们 |

---

## 2. 为什么是 OpenAI 协议而不是 Anthropic 协议

### 误解纠正：两家的底层 API 都开放

| API | 开放程度 |
|---|---|
| OpenAI Chat Completions / Responses API | ✅ 完全开放 |
| Anthropic Messages API | ✅ 完全开放 |
| Claude Code 内部协议（SDKMessage / /v1/sessions WebSocket / Bridge polling） | ❌ 闭源（仅 sourcemap 泄露） |
| Codex CLI 整体实现 | ✅ 完全开源 |

「Anthropic 没开放」**指的是 Claude Code 这个产品**，不是 Messages API 本身。

### 选 OpenAI 协议的真正理由：生态兼容

OpenAI Chat Completions schema 已经成为 LLM API 的事实标准，下面这些全都实现了 OpenAI 兼容接口：

- **本地模型**：vLLM、Ollama、llama.cpp server、LM Studio
- **国内模型**：DeepSeek、通义千问、Kimi、智谱（都有 OpenAI 兼容 endpoint）
- **第三方托管**：Together AI、Groq、Fireworks、Replicate
- **聚合路由**：OpenRouter、LiteLLM
- **Anthropic 自己**：通过 LiteLLM / x5iu/claude-code-adapter 等代理也能用 OpenAI 协议调 Claude

**结论**：选 OpenAI 协议 = 一套代码兼容几乎所有 LLM provider，包括未来可能切换到的 DeepSeek / 通义 / 自部署模型。

### Anthropic 协议的代价

如果选 Anthropic Messages API 作内部 IR：
- 只能直接调 Claude（其他模型都需要单独适配层）
- 失去与开源 IDE agent（Continue / Cline）一致的设计模式
- 违背"协议作 IR"业界共识

但 Anthropic 协议**有独门优势**：prompt caching（成本优化）、extended thinking（推理质量）、citations、computer use。这些在 ModelProvider 抽象层里通过"特性能力位"暴露，按需启用。

---

## 3. ModelProvider 抽象层设计

### 接口

```typescript
// backend/src/agent/providers/ModelProvider.ts
export interface ModelProvider {
  readonly name: string  // 'openai' | 'anthropic' | 'qwen' | ...
  readonly capabilities: ProviderCapabilities

  /** 流式生成。返回内部统一 SDKMessage 流。 */
  stream(request: NormalizedRequest, signal: AbortSignal): AsyncGenerator<SDKMessage>

  /** 估算 token 数（用于预算 / compaction 触发判断）。 */
  estimateTokens(text: string): number

  /** 模型清单。 */
  listModels(): Promise<ModelInfo[]>
}

export type ProviderCapabilities = {
  streaming: boolean
  toolCalling: boolean
  parallelToolCalls: boolean
  promptCaching: boolean        // Anthropic 独有
  extendedThinking: boolean      // Anthropic 独有
  jsonMode: boolean              // OpenAI 独有
  structuredOutputs: boolean     // OpenAI 独有
  visionInput: boolean
  audioInput: boolean
}

export type NormalizedRequest = {
  model: string
  messages: NormalizedMessage[]
  tools?: NormalizedTool[]
  maxTokens?: number
  temperature?: number
  systemPrompt?: string
  // 不直接对应任何一家协议，是我们的内部 IR
}
```

### 实现矩阵

| Provider | 实现位置 | 状态 |
|---|---|---|
| `OpenAIProvider` | `providers/openai.provider.ts` | **PR3 实现**（默认 Codex） |
| `AnthropicProvider` | `providers/anthropic.provider.ts` | 留位，按需实现 |
| `MockProvider` | `providers/mock.provider.ts` | **PR3 实现**（集成测试用） |
| `QwenProvider` | `providers/qwen.provider.ts` | 国内合规场景按需 |
| `LiteLLMGateway` | `providers/litellm.provider.ts` | 后期作为"溢出通道"接入 |

### 业界参考

- **Continue.dev**：`BaseLLM` 抽象类 + `openAICompatible` helper，覆盖 40+ provider
- **Cline**：`ApiHandler` 接口统一暴露 stream / cost / context-window 三个能力点
- **Vercel AI SDK v6**：`Agent` 抽象 + provider 协议 V2（provider-executed tools、provider metadata）

**共同模式**：OpenAI 协议作内部 IR + 少量原生适配器（Anthropic / Bedrock / Vertex）。

### 反模式（明确避免）

- ❌ 直接 `import` LiteLLM / LangChain 类型到业务层 → 换底座时业务代码全要改
- ❌ 把 Anthropic SDK 的 `MessageParam` 当内部类型流转 → 失去 provider 中立性
- ❌ 在 controller / service 层直接判断 `if (provider === 'openai')` → 该判断必须封进 provider 实现

---

## 4. 工具层为什么必须以 MCP 形式暴露

### MCP 已是事实标准（2025-2026）

- **2024-11**：Anthropic 发布 MCP（Model Context Protocol）
- **2025-03**：OpenAI 官宣支持 MCP，集成进 ChatGPT 桌面端
- **2025-09**：ChatGPT Apps 接入 MCP server 生态
- **2026 初**：社区 server 数量进入数千级；Cursor / Continue / Cline / Windsurf 全部原生支持
- **最新协议头**：`anthropic-beta: mcp-client-2025-11-20`

### 我们的策略

**所有业务 Tool 通过 MCP server 暴露**，而不是定义私有 function schema。

```
┌──────────────────────────────────────────────────────────┐
│  我们的 Agent 内核                                        │
│  ┌────────────────────────────────────────────────────┐  │
│  │  Tool Registry                                      │  │
│  │  - 内置工具（AskUserQuestion / Display.render 等）   │  │
│  │  - 我们的 MCP Client                                 │  │
│  └─────────────────────┬───────────────────────────────┘  │
└────────────────────────┼──────────────────────────────────┘
                         │ MCP 协议（stdio / SSE / HTTP）
                         ▼
┌──────────────────────────────────────────────────────────┐
│  我们自家的 MCP Servers（按业务域分）                      │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│  │ Project MCP │ │ Approval MCP│ │ Knowledge MCP       │ │
│  │ - search    │ │ - submit    │ │ - search            │ │
│  │ - get       │ │ - list      │ │ - get               │ │
│  │ - create    │ │ - act       │ │                     │ │
│  └─────────────┘ └─────────────┘ └─────────────────────┘ │
│      │ NestJS Service 调用                               │
│      ▼                                                   │
│  现有业务服务（ProjectService / ApprovalService / ...）  │
└──────────────────────────────────────────────────────────┘
```

**好处**：
1. **未来零改造接入外部客户端**：Claude Code / ChatGPT Apps / Cursor 都能直接消费我们的 MCP server
2. **业务规则单一来源**：MCP server 内部仍调 NestJS Service，复用事务 / 审计 / 权限 / 事件
3. **跨 agent 复用**：同一份 MCP server 可以同时被我们的内核 + 外部 agent 用

**vs 私有 function schema**：私有方案锁死在我们自家 agent 内部，无法对外开放，未来想做"开放给企业外部 agent 调用"时要全部重写。

---

## 5. Inbound 协议适配位（Q8 详解）

### 业界反推现状（启发本决策）

Claude Code 源码 2026-03-31 因 sourcemap 误打包泄露后，社区涌现一批**反向兼容项目**——不是去连 Anthropic CCR，而是**做服务端假装成 Anthropic 给 Claude Code 用**：

| 项目 | 做什么 |
|---|---|
| `1rgs/claude-code-proxy` | 假装成 Anthropic Messages API，让 Claude Code 跑 OpenAI / Gemini 后端 |
| `x5iu/claude-code-adapter` | Anthropic Messages ↔ OpenRouter Chat Completions Go 网关 |
| `free-claude-code` | 路由到 NVIDIA NIM / DeepSeek / Ollama |
| `ccproxy` | 让 Anthropic Messages API 走个人 Claude Max 订阅 |

**关键洞察**：Anthropic Messages API 已经成为**客户端侧的 de-facto IR**——谁在服务端实现 Messages API，谁就能接入海量已有客户端（Claude Code / Cursor 部分模式 / Continue / Cline 等）。

### 我们的预留方案

**本期（v0.1）不实现**，但架构上预留：

```
┌──────────────────────────────────────────────────────────┐
│  外部客户端                                                │
│  Claude Code / Cursor / Continue / Cline                  │
└──────────────────────────┬───────────────────────────────┘
                           │ Anthropic Messages API 协议
                           │ POST /v1/messages
                           ▼
┌──────────────────────────────────────────────────────────┐
│  ★ Inbound Adapter（未来 PR）                            │
│  将 Anthropic Messages 请求 → 我们的 NormalizedRequest    │
│  将我们的 SDKMessage 流  → Anthropic SSE 响应             │
└──────────────────────────┬───────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────┐
│  我们的 QueryEngine（不感知请求来自哪家协议）              │
└──────────────────────────────────────────────────────────┘
```

### 对 v0.1 的硬约束

为了让未来 inbound 适配可行，**SDKMessage 类型设计必须满足两条**：

1. **字段命名贴近业界共识**（参考 OpenAI Responses / Anthropic Messages 的命名习惯，避免拍脑袋造词）
2. **Tool use 块结构与 Anthropic `tool_use` content block 同构**（id / name / input 三字段；不要折叠成 string）

这两条在 PR3 的 SDKMessage 类型 PR 必须卡口审查。

---

## 6. 不做什么（明确边界）

为了避免范围蔓延，**以下事情我们不做**：

| 不做 | 理由 |
|---|---|
| 反向连 Anthropic CCR /v1/sessions 端点 | 违反 ToS、配额无法谈、客户端指纹检测可能升级 |
| 自己实现 LLM 网关（替代 LiteLLM） | 重复造轮子，溢出场景直接接 LiteLLM |
| 自研 MCP 协议 | 用 Anthropic 官方 SDK，跟随社区演进 |
| 直接 fork openclaude / open-claude-code | 协议吸收即可，运行时自研以避免单一 maintainer 风险 |
| 在 v0.1 实现多 provider 路由策略（按价格 / 延迟自动选） | YAGNI，先做单 provider，需要时再上 LiteLLM |

---

## 7. 风险与缓解

| 风险 | 概率 | 缓解 |
|---|---|---|
| Codex / GPT-5.4 服务降级或停用 | 低 | ModelProvider 抽象 + 实现一个 AnthropicProvider 即可切换 |
| OpenAI 协议大版本不兼容（Responses API 取代 Chat Completions） | 中 | 已经在发生。先用 Chat Completions 稳态，Responses API 在第二阶段切换 |
| MCP 规范不稳定 | 低 | 跟随官方 SDK 升级；MCP 已被两家巨头采纳，不会废弃 |
| 国内合规场景需要落地通义 / 文心 | 高 | ModelProvider 接口已留位，新增一个 QwenProvider 即可 |
| 字段命名与未来某协议冲突 | 中 | PR3 SDKMessage 设计阶段强制 review，参考 OpenAI Responses 命名 |

---

## 8. 路线图与本文档关系

| PR | 范围 | 本文档相关内容 |
|---|---|---|
| PR1 | 三栏 UI 骨架 | — |
| PR2 | 后端 agent 模块 + SSE 链路 | 用 Vercel AI SDK 还是手写 SSE → 见 §3 实现矩阵 |
| **PR3** | QueryEngine MVP | **§3 ModelProvider 接口定义 + OpenAIProvider + MockProvider 实现，是这个 PR 的硬要求** |
| PR4 | Tool Registry + 批次 1 工具 | **§4 工具以 MCP server 形式实现，是这个 PR 的硬要求** |
| PR5 | Skill Registry + Display Panel | — |
| 后期 PR | Inbound Adapter（Anthropic Messages 入口） | §5 |
| 后期 PR | 第二个 Provider（Anthropic / Qwen） | §3 实现矩阵 |

---

## 9. 决策待复审清单

以下决策**需要在 PR3 启动前复审一次**：

- [ ] OpenAI Codex 在 2026-Q2 是否仍是工具调用能力最强的 provider？（对照 Claude Opus 4.7 / Gemini 3 / GPT-5.4 在 SWE-bench 等评测）
- [ ] Vercel AI SDK v6 的 `Agent` 抽象是否足够覆盖我们的需求？还是要自己写 ModelProvider 接口？
- [ ] MCP 是否仍是 2026-Q2 最值得押注的工具协议？是否有新协议（如 OpenAI 的 Function Registry）出现并威胁 MCP 地位？

---

## 10. 参考资料

- [LiteLLM (BerriAI)](https://github.com/BerriAI/litellm)
- [LiteLLM streaming tool_calls bug #17246](https://github.com/BerriAI/litellm/issues/17246)
- [OpenRouter Provider Routing](https://openrouter.ai/docs/guides/routing/provider-selection)
- [Vercel AI SDK 6 release](https://vercel.com/blog/ai-sdk-6)
- [Continue LLM Abstraction Layer](https://deepwiki.com/continuedev/continue/4.1-extension-architecture)
- [LangChain 1.0 vs LangGraph 1.0](https://www.clickittech.com/ai/langchain-1-0-vs-langgraph-1-0/)
- [Anthropic — Introducing MCP](https://www.anthropic.com/news/model-context-protocol)
- [Claude Agent SDK (Promptfoo docs)](https://www.promptfoo.dev/docs/providers/claude-agent-sdk/)
- [OpenAI — The next evolution of the Agents SDK](https://openai.com/index/the-next-evolution-of-the-agents-sdk/)
- [OpenAI Developers 2025 recap](https://developers.openai.com/blog/openai-for-developers-2025)
- [leeyeel/claude-code-sourcemap](https://github.com/leeyeel/claude-code-sourcemap) — 我们使用的源码还原
- [ruvnet/open-claude-code](https://github.com/ruvnet/open-claude-code) — clean-room 实现参考
- [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude) — MIT 复刻 26k+ stars
- [1rgs/claude-code-proxy](https://github.com/1rgs/claude-code-proxy) — Inbound 适配参考
- [x5iu/claude-code-adapter](https://github.com/x5iu/claude-code-adapter) — Anthropic↔OpenAI 网关参考
