简体中文
Provider 配置
Provider 无关的消息、配置、服务、重试和用量契约可从 @blade-ai/agent-sdk 导入。该入口是 browser-safe 的,不加载具体 Provider SDK。
支持的 Provider
| Provider | type 值 | adapter 包 |
|---|---|---|
| OpenAI | 'openai' | 内置 @ai-sdk/openai |
| Anthropic | 'anthropic' | 可选 peer @ai-sdk/anthropic |
| Azure OpenAI | 'azure-openai' | 可选 peer @ai-sdk/azure |
| Gemini | 'gemini' | 可选 peer @ai-sdk/google |
| DeepSeek | 'deepseek' | 可选 peer @ai-sdk/deepseek |
| OpenAI 兼容 | 'openai-compatible' | 内置 @ai-sdk/openai-compatible |
Provider adapter 按需加载。使用非内置 Provider adapter 时安装对应 peer,例如:
bash
pnpm add @blade-ai/agent-sdk @ai-sdk/anthropicProviderConnectionConfig
ts
type BuiltinProviderType =
| 'openai'
| 'anthropic'
| 'azure-openai'
| 'gemini'
| 'deepseek'
| 'openai-compatible';
interface ProviderConnectionConfig {
id?: string;
type: BuiltinProviderType | (string & {});
apiKey?: string;
baseUrl?: string;
headers?: Record<string, string>;
organization?: string;
apiVersion?: string;
projectId?: string;
requestTimeoutMs?: number;
streamIdleTimeoutMs?: number;
}type 选择 wire protocol adapter;id 标识逻辑 Provider,默认等于 type。OpenAI-compatible 网关如需在模型切换和持久化 Session 恢复后保留 真实 Provider 身份,应显式设置 id;id 不会改变 adapter 选择:
ts
provider: {
id: 'openrouter',
type: 'openai-compatible',
apiKey: process.env.OPENROUTER_API_KEY!,
baseUrl: 'https://openrouter.ai/api/v1',
}requestTimeoutMs 限制一次非流式模型操作的总时长(包括重试等待),默认 10 分钟。streamIdleTimeoutMs 限制等待下一个流式 chunk 的时长,默认 5 分钟。两者都必须是以毫秒为单位的正整数。超时会主动中止底层 provider 请求,并抛出 ModelTimeoutError;错误码分别为 MODEL_REQUEST_TIMEOUT 和 MODEL_STREAM_IDLE_TIMEOUT,不会伪装成用户取消。
重试
重试由 SDK 独占。SDK 会关闭 AI SDK 自带的重试(每个请求都设置 maxRetries: 0),因此一次失败产生的请求次数与 SDK 通过 ModelRetryEvent 上报的次数一致,不会出现分层各重试一遍的情况。
两种模式的覆盖范围不同:
- 非流式请求整体重试,重试等待时间计入
requestTimeoutMs。 - 流式请求只在建立阶段重试。一旦已向调用方交付过 chunk,中途的 provider 失败会抛出
ModelStreamError(错误码MODEL_STREAM_FAILED),而不是静默结束响应; 这种情况不会重试——重放已部分交付的响应会让调用方看到重复文本。请按失败请求处理, 自行决定是否重发。
assistant 历史会记录生成响应时的逻辑 Provider ID、API adapter 和模型。 只有来源三元组完全相同时,原生 reasoning block 才会按原格式回放。切换 Provider、adapter、模型,或恢复不含来源信息的旧历史时,reasoning 会降级为 普通 assistant 文本,同时保留 tool call 关联,避免把 Provider 专属 payload 发送给不兼容的 API。
内置 Provider 的选择由 services/modelProvider.ts 中唯一的 typed factory table 负责。消息、工具 schema、tool call、usage 与 provider options 的转换集中在 services/modelAdapter.ts;VercelAIModelService 只编排请求、重试和流消费。
自定义 Provider Adapter
ProviderRegistry 是实例级 Registry,不包含进程全局注册状态,因此不同 Session 可以为同一个 type 使用不同 adapter。
ts
import {
createSession,
ProviderRegistry,
type ModelServiceConfig,
type ModelService,
type ProviderAdapter,
} from '@blade-ai/agent-sdk';
const adapter = {
type: 'acme-chat',
async create(config: Readonly<ModelServiceConfig>): Promise<ModelService> {
return new AcmeModelService(config);
},
} satisfies ProviderAdapter;
const session = await createSession({
provider: {
id: 'acme-production',
type: 'acme-chat',
apiKey: process.env.ACME_API_KEY!,
},
providerRegistry: new ProviderRegistry([adapter]),
model: 'acme-reasoner',
});Adapter 返回现有 ModelService 契约,因此 model middleware、request/stream idle deadline、durable model attempt、subagent 和 compaction 会继续走同一运行时链路。 自定义 adapter 也可以只在当前 Registry 实例中覆盖内置 type。重复、非法注册 以及未注册的未知 adapter type 都会以 ProviderRegistryError fail-closed。
配置示例
OpenAI
ts
{ type: 'openai', apiKey: 'sk-xxx' }Anthropic
ts
{ type: 'anthropic', apiKey: 'sk-ant-xxx' }Azure OpenAI
ts
{
type: 'azure-openai',
apiKey: 'xxx',
baseUrl: 'https://my-resource.openai.azure.com',
apiVersion: '2024-02-15-preview',
}Gemini
ts
{ type: 'gemini', apiKey: 'xxx' }DeepSeek
ts
{
type: 'deepseek',
apiKey: process.env.DEEPSEEK_API_KEY!,
// 可省略,默认使用 https://api.deepseek.com
baseUrl: 'https://api.deepseek.com',
}DeepSeek 使用原生 provider 分支,默认模型建议使用 deepseek-v4-pro。旧别名 deepseek-chat、deepseek-reasoner 会继续兼容,但 SDK 会优先按当前 V4 模型路由。
ts
const session = await createSession({
provider: { type: 'deepseek', apiKey: process.env.DEEPSEEK_API_KEY! },
model: 'deepseek-v4-pro',
});Thinking mode 可通过模型配置的 providerOptions 透传:
ts
{
id: 'deepseek-pro',
name: 'DeepSeek V4 Pro',
provider: 'deepseek',
model: 'deepseek-v4-pro',
providerOptions: {
deepseek: {
thinking: { type: 'enabled' },
},
},
}DeepSeek Context Caching 默认由官方服务端启用,SDK 不需要额外开关。响应 usage 会保留缓存命中与未命中口径:cacheReadInputTokens 对应 prompt_cache_hit_tokens,cacheMissInputTokens / billableInputTokens 对应 prompt_cache_miss_tokens。成本策略属于应用配置,SDK 不内置会随服务端 价格变化而过期的定价表。
DeepSeek 缓存命中优化
DeepSeek 服务端会自动缓存 prompt 前缀。SDK 会在 DeepSeek provider 下对带稳定缓存标记的首轮上下文做安全重排:保留开头 system 消息不动,把首个 assistant/tool 消息之前标记为稳定的 user 上下文提前,使多次请求共享更长的相同前缀。
ts
import {
optimizeDeepSeekCachePrefix,
} from '@blade-ai/agent-sdk';
const messages = optimizeDeepSeekCachePrefix([
{ role: 'system', content: 'You are a repository assistant.' },
{ role: 'user', content: '本轮问题:解释构建流程' },
{
role: 'user',
content: '大型、稳定、跨请求复用的仓库摘要...',
providerOptions: { deepseek: { cache: 'stable' } },
},
]);这项优化不会重排已经进入多轮对话的 assistant/tool 历史,避免破坏 tool call 因果关系。
OpenAI 兼容
适用于 Ollama、vLLM、LiteLLM 等兼容 OpenAI API 的服务:
ts
{
type: 'openai-compatible',
baseUrl: 'http://localhost:11434/v1',
apiKey: 'ollama',
}运行时切换模型
ts
const session = await createSession({
provider: { type: 'openai', apiKey: process.env.OPENAI_API_KEY! },
model: 'gpt-4o-mini',
});
// 简单任务用小模型
await session.send('列出 src 目录下的文件');
for await (const event of session.stream()) { /* ... */ }
// 复杂任务切换到大模型
await session.setModel('gpt-4o');
await session.send('重构这些文件的架构');
for await (const event of session.stream()) { /* ... */ }查看支持的模型
ts
const models = await session.supportedModels();
for (const m of models) {
console.log(`${m.id}: ${m.name}`);
}日志
SDK 不内置日志实现,通过 AgentLogger 接口接受外部注入:
ts
import type { AgentLogger, LogEntry } from '@blade-ai/agent-sdk';
const logger: AgentLogger = {
log(entry: LogEntry) {
const prefix = `[${entry.timestamp}][${entry.level.toUpperCase()}][${entry.category}]`;
console.log(`${prefix} ${entry.message}`);
},
};
const session = await createSession({
provider: { type: 'openai', apiKey: process.env.OPENAI_API_KEY! },
model: 'gpt-4o',
logger,
});LogEntry
ts
interface LogEntry {
level: 'debug' | 'info' | 'warn' | 'error';
category: string;
message: string;
timestamp: string;
sessionId?: string;
args?: unknown[];
}