第 14 章 · 自定义 Provider 与 OpenAI 兼容端点
本章目标:用 createProvider() 接入本地推理服务器、企业网关与 DeepSeek 等 OpenAI 兼容端点,并用 compat 标志位解决兼容性差异。
14.1 为什么需要自定义 Provider
内置 Provider 目录覆盖主流服务商,但真实世界还有三类需求:
- 本地推理——Ollama、llama.cpp 等本地服务器;
- 企业代理/网关——统一鉴权、审计、限流的内部 LLM 网关;
- OpenAI 兼容三方——DeepSeek、Together AI 等兼容
openai-completionsAPI 的服务商。
pi-ai 的答案是一个统一的组装函数:createProvider()。
14.2 createProvider() 四要素
createProvider() 从四个部分构建一个 Provider:身份(id)、认证(auth)、模型列表(models)、API 实现(api):
import { createModels, createProvider, envApiKeyAuth, type Model } from '@earendil-works/pi-ai';
// 组装一个 Ollama 本地 Provider
const ollama = createProvider({
id: 'ollama',
name: 'Ollama (local)',
auth: envApiKeyAuth('OLLAMA_API_KEY'), // 认证解析方式
api: 'openai-completions', // 复用 OpenAI 兼容 API 实现
baseUrl: 'http://localhost:11434/v1',
models: [
{
id: 'qwen3:32b',
name: 'Qwen3 32B',
reasoning: false,
input: ['text'],
// ... 其他 Model 元数据
} as Model,
],
});
const models = createModels();
models.setProvider(ollama);动态发布机制
createProvider() 自动处理模型的动态发布与持久化。自定义 Provider 的 refreshModels() 会收到只读的 context.stored 快照,并通过 context.publish({ persist?, update? }) 发布变更——不要在发布前直接改状态。
14.3 接入 DeepSeek 等 OpenAI 兼容端点
DeepSeek 是最典型的 OpenAI 兼容服务商。好消息是 pi-ai 对已知兼容商自动检测兼容性设置(DeepSeek 在内置列表中),零配置可用:
import { createModels, createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
const deepseek = createProvider({
id: 'deepseek',
name: 'DeepSeek',
auth: envApiKeyAuth('DEEPSEEK_API_KEY'),
api: 'openai-completions',
baseUrl: 'https://api.deepseek.com/v1',
models: [
{
id: 'deepseek-chat',
name: 'DeepSeek Chat',
reasoning: false,
input: ['text'],
},
{
id: 'deepseek-reasoner',
name: 'DeepSeek Reasoner',
reasoning: true, // R1 支持推理
input: ['text'],
},
],
});14.4 compat:细粒度兼容性开关
对于不在自动检测名单里的自定义代理,可以用 compat 字段手动声明差异。常用的标志位:
const proxy = createProvider({
id: 'corp-proxy',
name: 'Corp Gateway',
auth: envApiKeyAuth('CORP_LLM_KEY'),
api: 'openai-completions',
baseUrl: 'https://llm-gateway.corp.internal/v1',
models: [/* ... */],
compat: {
supportsStore: false, // 网关不支持 store 字段
supportsDeveloperRole: false, // 只认 system 不认 developer 角色
maxTokensField: 'max_tokens', // 老网关不认识 max_completion_tokens
thinkingFormat: 'deepseek', // 思考参数按 DeepSeek 格式发送
},
});常用 compat 标志速查:
| 标志 | 作用 |
|---|---|
supportsStrictMode | 工具定义是否支持 strict JSON schema |
requiresToolResultName | 工具结果是否必须带 name 字段 |
requiresThinkingAsText | 思考块是否必须转成文本 |
thinkingFormat | 推理参数格式(openai/deepseek/qwen 等) |
cacheControlFormat: 'anthropic' | Anthropic 风格的提示缓存控制 |
部分设置即继承检测结果
compat 只需写有差异的字段——未指定的字段沿用基于 baseUrl 的自动检测默认值。
14.5 直接调用 API 实现与模型级 headers
更底层的玩法是绕过目录直接调用 API 实现;另外单个模型还可以携带专属 headers(比如需要绕过 bot 检测的代理),这些头会自动并入请求:
// 自定义模型可携带 headers,请求时自动合并
const gateway = createProvider({
id: 'gateway',
name: 'LLM Gateway',
auth: envApiKeyAuth('GATEWAY_KEY'),
api: 'openai-completions',
baseUrl: 'https://gw.example.com/v1',
models: [
{
id: 'internal-chat',
name: 'Internal Chat',
headers: { 'X-Bot-Detection-Bypass': 'team-token' }, // 模型级请求头
},
],
});
// 多租户网关:同一实现不同 baseUrl
const tenantGateway = createProvider({ /* 类似,换 baseUrl 与租户 key */ });本章小结
- 三类场景需要自定义 Provider:本地推理、企业网关、OpenAI 兼容三方;
createProvider()由 id + auth + models + api 四部分组装,动态发布由库托管;- DeepSeek 等已知兼容商的 compat 设置会按 baseUrl 自动检测,通常零配置;
compat只需声明有差异的字段,其余继承检测结果;- 模型级
headers可注入绕过检测等自定义请求头。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. createProvider() 构建一个 Provider 需要哪几个核心部分?
2. 使用 DeepSeek 这类已知 OpenAI 兼容商时,compat 设置如何处理?
3. compat 中只设置了 thinkingFormat 一个字段,其他字段会怎样?
4. 某个模型的响应被企业网关的 bot 检测拦截,文档建议的做法是?
🛠️ 动手实践
- 用 createProvider() 把第 4 章的 Agent 切换到 Ollama 本地模型,验证离线运行。
- 配置 DeepSeek 的两个模型(chat/reasoner),分别跑同一个问题对比效果与成本。
- 搭一个最小 Nginx 反代作为"企业网关",故意去掉某个字段的支持,用 compat 标志修复。
测试驱动开发离不开 mock——下一章介绍专为测试设计的 Faux Provider。