第 17 章:接入一个新模型

1 | 卷三:工坊实战 |
第 11 章我们理解了 Provider 的策略模式。现在动手——接入一个新的 LLM 提供商。分两种情况:OpenAI 兼容的(简单)和非兼容的(需要新类)。
目标
把 DeepSeek(OpenAI 兼容)和假设的 “XModel”(非兼容)接入 QwenPaw。
情况一:OpenAI 兼容——只需加配置
DeepSeek 的 API 兼容 OpenAI 格式。不需要写新类,只需在 provider_manager.py 中加一个实例:1
2
3
4
5
6
7
8
9
10
11
12
13
14# provider_manager.py,约第 490 行
PROVIDER_DEEPSEEK = OpenAIProvider(
id="deepseek",
name="DeepSeek",
base_url="https://api.deepseek.com/v1",
api_key_prefix="sk-",
models=[
ModelInfo(id="deepseek-chat", name="DeepSeek-V3"),
ModelInfo(id="deepseek-reasoner", name="DeepSeek-R1"),
],
support_model_discovery=True,
freeze_url=True,
)
然后在 _init_builtins() 中注册:1
2
3def _init_builtins(self):
# ... 已有的注册 ...
self._add_builtin(PROVIDER_DEEPSEEK)
完成。不需要:
- 新建文件
- 修改
_provider_from_data() - 改 Agent 代码
为什么?因为 _provider_from_data() 的默认分支就是 OpenAIProvider.model_validate(data)——所有未匹配的 Provider 都走 OpenAI 路径。
完整流程
1 | 1. 定义 PROVIDER_XXX 常量(OpenAIProvider 实例) |
情况二:非 OpenAI 兼容——需要新类
假设 “XModel” 使用完全不同的 API 格式。需要创建新的 Provider 类。
第一步:创建 Provider 类
在 src/qwenpaw/providers/ 下创建 xmodel_provider.py:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56import logging
from typing import List, Tuple
from .provider import Provider, ModelInfo
logger = logging.getLogger(__name__)
class XModelProvider(Provider):
"""XModel uses its own SDK and API format."""
async def check_connection(
self, timeout: float = 5
) -> Tuple[bool, str]:
try:
import xmodel_sdk
client = xmodel_sdk.Client(api_key=self.api_key)
client.ping(timeout=timeout)
return True, ""
except Exception as e:
return False, str(e)
async def fetch_models(
self, timeout: float = 5
) -> List[ModelInfo]:
try:
import xmodel_sdk
client = xmodel_sdk.Client(api_key=self.api_key)
models = client.list_models(timeout=timeout)
return [ModelInfo(id=m.id, name=m.name) for m in models]
except Exception as e:
logger.error(f"Failed to fetch models: {e}")
return []
async def check_model_connection(
self, model_id: str, timeout: float = 5
) -> Tuple[bool, str]:
try:
import xmodel_sdk
client = xmodel_sdk.Client(api_key=self.api_key)
client.chat(
model=model_id,
messages=[{"role": "user", "content": "hi"}],
)
return True, ""
except Exception as e:
return False, str(e)
def get_chat_model_instance(self, model_id: str):
# 需要 XModelChatModel 适配层
return XModelChatModel(
model_name=model_id,
api_key=self.api_key,
generate_kwargs=(
self.get_effective_generate_kwargs(model_id)
),
)
四个抽象方法各实现了什么:
check_connection:测试 API Key 是否有效fetch_models:从 Provider 拉取可用模型列表check_model_connection:测试特定模型是否能响应get_chat_model_instance:返回 agentscope 的 ChatModel 实例(最难的部分)
第二步:注册到 ProviderManager
1 | # provider_manager.py |
get_chat_model_instance 是真正的难点
这个方法要返回一个 agentscope 的 ChatModelBase 子类。如果 XModel 的消息格式不是 OpenAI 兼容的,需要写适配器——把 agentscope 格式的消息转成 XModel 格式,再把响应转回来。
这就是为什么 OpenAI 兼容的 Provider 不需要新类——OpenAIChatModelCompat 已经做了消息格式转换。
现实中的例子
| Provider | 实现方式 | 原因 |
|---|---|---|
| OpenAI | 独立类 | 基础实现 |
| Anthropic | 独立类 | 不同消息格式 |
| Gemini | 独立类 | Google SDK |
| Ollama | 继承 OpenAI | 兼容 /v1 端点 |
| LMStudio | 继承 OpenAI | 几乎完全一样 |
| DashScope/DeepSeek/… | 直接用 OpenAI 实例 | 完全兼容 |
大部分新增 Provider 都走”OpenAI 兼容”路径——只需配置,不需要新类。
自检
- 知道 OpenAI 兼容的 Provider 只需加一个
OpenAIProvider(...)实例 - 知道非兼容 Provider 需要实现四个抽象方法
- 知道
get_chat_model_instance是最难的部分 - 知道
_provider_from_data()负责从 JSON 反序列化为正确的类
下一章我们接入一个新的聊天频道——BaseChannel 的 6 个核心方法怎么实现?怎么把平台特有的消息格式翻译成 QwenPaw 的统一格式?