服务适配器模式:10+ 个 LLM 提供商的统一抽象
服务适配器模式:10+ 个 LLM 提供商的统一抽象
学习目标
理解 CopilotKit 的服务适配器架构:
- CopilotServiceAdapter 统一接口
- 各提供商适配器的差异处理
- RuntimeEventSource 事件发射机制
- LangChain 通用适配器
前置知识
本章涉及多模型适配架构的通用原理,建议先阅读:
项目实践
CopilotServiceAdapter 接口
统一接口(源码验证):interface CopilotServiceAdapter { provider?: string, // 提供商名称 model?: string, // 模型名称 name?: string, // 适配器名称 process(request): Promise<CopilotRuntimeChatCompletionResponse>, getLanguageModel?(): LanguageModel // 可选:返回 Vercel AI SDK 的 LanguageModel}请求结构:
CopilotRuntimeChatCompletionRequest:{ eventSource: RuntimeEventSource, // 事件发射器 messages: Message[], // 聊天消息历史 actions: ActionInput[], // 可用工具列表 model?: string, // 指定模型 threadId?: string, runId?: string, forwardedParameters?: ForwardedParametersInput, extensions?: ExtensionsInput, agentSession?: AgentSessionInput, agentStates?: AgentStateInput[]}响应结构:
CopilotRuntimeChatCompletionResponse:{ threadId: string, runId?: string, extensions?: ExtensionsResponse}已实现适配器
| 适配器 | 目标 API | 特殊处理 |
|---|---|---|
OpenAIAdapter | OpenAI Chat Completion | 流式输出解析、工具调用格式转换 |
OpenAIAssistantAdapter | OpenAI Assistants API | 线程管理、run 状态轮询 |
AnthropicAdapter | Anthropic Messages | system prompt 分离、工具定义格式 |
GoogleGenAIAdapter | Google Gemini API | 内容角色映射、安全设置 |
GroqAdapter | Groq API(兼容 OpenAI) | 复用 OpenAI 格式 |
BedrockAdapter | AWS Bedrock | 认证签名、模型 ID 映射 |
LangChainAdapter | Vercel AI SDK | 通用适配器,可接入任意 LangChain 支持的模型 |
UnifyAdapter | Unify API | 模型路由、最优模型选择 |
EmptyAdapter | 无(占位) | 开发/测试用 |
RuntimeEventSource 事件发射
每个适配器通过 eventSource 向运行时发射事件:
事件发射模式(伪代码):adapter.process(request) { const source = request.eventSource;
// 流式文本 source.emit({ type: "TextMessageStart", messageId: "msg_1" }); source.emit({ type: "TextMessageContent", delta: "Hello" }); source.emit({ type: "TextMessageEnd", messageId: "msg_1" });
// 工具调用 source.emit({ type: "ActionExecutionStart", toolCallId: "tc_1" }); source.emit({ type: "ActionExecutionArgs", delta: '{"arg1":' }); source.emit({ type: "ActionExecutionEnd", toolCallId: "tc_1" });
// 完成后 return { threadId, runId };}消息转换
各提供商的消息格式不同,适配器负责统一转换:
| 提供商 | 消息格式 | 转换方向 |
|---|---|---|
| OpenAI | { role: "user/assistant/tool", content } | → CopilotKit Message |
| Anthropic | { role: "user/assistant", content: [{ type: "text/tool_use" }] } | → CopilotKit Message |
{ role: "user/model", parts: [{ text/functionCall }] } | → CopilotKit Message |
LangChain 通用适配器
LangChainAdapter 通过 Vercel AI SDK 的 LanguageModel 接口接入任意模型:
const adapter = new LangChainAdapter({ modelInstance: openai("gpt-4"), // 任意 Vercel AI SDK 模型 // 或 anthropic("claude-sonnet-4-6-20250514") // 或 google("gemini-2.5-pro")});优势:
- 只需实现一次适配器逻辑,支持 Vercel AI SDK 的 100+ 模型
getLanguageModel()返回配置的模型,用于BuiltInAgent
错误处理
error-utils.ts 提供统一的错误处理:
通用错误转换(伪代码):function ensureCopilotKitError(error): CopilotKitError { if (error instanceof CopilotKitError) return error; return new CopilotKitError({ message: error.message, cause: error });}问题与规避
| 问题 | 规避策略 |
|---|---|
| 新提供商的流式输出格式不同 | 统一通过 RuntimeEventSource 发射事件;每种适配器自行解析自己的流格式 |
| OpenAI Assistant API 的轮询延迟 | AssistantAdapter 通过轮询 run 状态实现事件模拟;延迟比直接流式高 |
| Bedrock 认证签名复杂 | BedrockAdapter 内部处理 AWS V4 签名 |
| 工具定义的格式差异 | 各适配器的 utils.ts 处理工具格式转换 |
设计取舍
独立适配器 vs 通用适配器
独立适配器(OpenAI、Anthropic 等):
- 优势:针对每个 API 优化,性能更好
- 代价:每个提供商需要单独维护
通用适配器(LangChainAdapter):
- 优势:一行代码支持 100+ 模型
- 代价:通过 Vercel AI SDK 间接层,可能丢失某些提供商的特殊功能
选择策略:常用提供商用独立适配器,长尾模型用 LangChainAdapter。
参考来源
packages/runtime/src/service-adapters/service-adapter.tspackages/runtime/src/service-adapters/anthropic/anthropic-adapter.tspackages/runtime/src/service-adapters/openai/openai-adapter.tspackages/runtime/src/service-adapters/langchain/langchain-adapter.ts