跳转到内容

服务适配器模式: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特殊处理
OpenAIAdapterOpenAI Chat Completion流式输出解析、工具调用格式转换
OpenAIAssistantAdapterOpenAI Assistants API线程管理、run 状态轮询
AnthropicAdapterAnthropic Messagessystem prompt 分离、工具定义格式
GoogleGenAIAdapterGoogle Gemini API内容角色映射、安全设置
GroqAdapterGroq API(兼容 OpenAI)复用 OpenAI 格式
BedrockAdapterAWS Bedrock认证签名、模型 ID 映射
LangChainAdapterVercel AI SDK通用适配器,可接入任意 LangChain 支持的模型
UnifyAdapterUnify 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
Google{ 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.ts
  • packages/runtime/src/service-adapters/anthropic/anthropic-adapter.ts
  • packages/runtime/src/service-adapters/openai/openai-adapter.ts
  • packages/runtime/src/service-adapters/langchain/langchain-adapter.ts