@zimtsui/brainswitch
v0.0.36
Published
<!-- 本文档由 GPT-5 辅助生成 -->
Readme
Brainswitch
Brainswitch 是一个为 AI 工作流设计的 LLM 推理 API 适配器,支持在会话中途切换模型。
Motivation
大多数 LLM 的聊天模板 ChatML 原生不支持严格函数调用,在批处理 AI 工作流中难以达到生产级可靠性。如果仅使用 OpenAI 等支持严格函数调用的服务商,那么可选的模型型号会大幅受限。
Brainswitch 支持在一次会话中途切换模型并保持对话上下文,包括 OpenAI、Google、Anthropic 的深度思考模型交替思考的加密思考内容。有了 Brainswitch 就可以在会话的大量推理阶段使用最合适的模型生成自然语言结果,在最后的总结阶段切换成支持严格函数调用的模型进行结构化提交。
支持服务商 API 类型
- OpenAI Chat Completions
- OpenAI Responses
- 百炼
- OpenRouter
- Anthropic
安装
环境要求:Node.js >= 22。
npm install @zimtsui/brainswitch核心概念
Session:会话状态。InferenceContext:工作流环境,包含 TypeLog Logger、AbortSignal、用户防止并发过载的读写锁。Engine:推理引擎,从一个会话状态生成下一个会话状态。Endpoint:代表一家服务商的一个模型的 API 端点。Adaptor:Engine 工厂。RoleMessage:三类角色消息Developer、User、AI,消息由Text/Function.Call/Response片段组成。Function.Declaration.Map:函数工具声明集合,使用 JSON Schema 描述函数参数。
配置
export type Config = {
brainswitch: {
endpoints: Record<string, {
baseUrl: string;
apiKey: string;
model: string;
name: string;
apiType:
| 'openai-chatcompletions'
| 'openai-responses'
| 'google'
| 'aliyun'
| 'openrouter-monolith'
| 'openrouter-stream'
| 'anthropic'
;
proxy?: string;
inputPrice?: number; // CNY per MToken
outputPrice?: number; // CNY per MToken
cachePrice?: number; // CNY per MToken
rpm?: number; // Requests per minute
timeout?: number; // Time limit in milliseconds
maxTokens?: number; // Maximum number of generated tokens
additionalOptions?: Record<string, unknown>;
};
};
}计费说明
OpenRouter 的成本会自动按服务器返回的美元成本并使用固定汇率(1 USD = 8 CNY)换算为人民币记账。
快速上手
下面演示:定义一个工具函数,先用 Google 做推理与工具调用,再在同一会话中切换到 OpenAI Responses 做最终的结构化总结。
import { Adaptor, agentloop, RoleMessage, Function, type InferenceContext, type Config, Session } from '@zimtsui/brainswitch';
import { Type } from '@sinclair/typebox';
import { RWLock } from '@zimtsui/coroutine-locks';
import { Channel } from '@zimtsui/typelog';
import * as Presets from '@zimtsui/typelog/presets';
// 配置推理服务商 API 接入点
const config: Config = {
brainswitch: {
endpoints: {
'gpt-4o-mini': {
name: 'GPT-4o mini',
apiType: 'openai-chatcompletions',
baseUrl: 'https://api.openai.com/v1',
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4o-mini',
inputPrice: 5, outputPrice: 15, cachedPrice: 1,
rpm: 3000, timeout: 60_000,
},
'o4-mini': {
name: 'o4 mini',
apiType: 'openai-responses',
baseUrl: 'https://api.openai.com/v1',
apiKey: process.env.OPENAI_API_KEY!,
model: 'o4-mini',
},
'gemini-2.5-flash': {
name: 'Gemini 2.5 Flash',
apiType: 'google',
baseUrl: 'https://generativelanguage.googleapis.com',
apiKey: process.env.GOOGLE_API_KEY!,
model: 'gemini-2.5-flash',
},
}
}
}
// 声明函数工具
const fdm = {
get_weather: {
description: '获取某城市的天气',
paraschema: Type.Object({
city: Type.String(),
unit: Type.Optional(Type.Union([Type.Literal('C'), Type.Literal('F')]))
}),
},
submit_result: {
description: '提交最终结果',
paraschema: Type.Object({
weather: Type.String(),
advice: Type.String(),
}),
},
} satisfies Function.Declaration.Map;
type fdm = typeof fdm;
type fdu = Function.Declaration.From<fdm>;
// 实现函数工具
export class Submission extends Error {
public constructor(public weather: string, public advice: string) {
super(undefined);
}
}
const fnm: Function.Map<fdm> = {
async get_weather({ city, unit }) {
const data = { city, unit: unit ?? 'C', temperature: 26, sky: 'sunny' };
return JSON.stringify(data);
},
async submit_result({ weather, advice }) {
throw new Submission(weather, advice);
},
};
// 初始化工作流上下文
const ctx: InferenceContext = {
busy: new RWLock(),
logger: {
message: Channel.create(Presets.Level, message => console.log(message)),
cost(deltaCost) { console.log((-deltaCost).toFixed(2)); },
},
};
// 创建会话
const session: Session<fdu> = {
developerMessage: RoleMessage.Developer.create([
RoleMessage.Part.Text.create('你的工作是为用户查询天气,并给出穿衣建议。调用工具提交最终结果'),
]),
chatMessages: [
RoleMessage.User.create([ RoleMessage.Part.Text.create('请查询现在北京的天气,并给穿衣建议。') ]),
],
};
// 选择推理引擎
const adaptor = Adaptor.create(config);
const engine = adaptor.makeEngine('gpt-4o-mini', fdm, Function.ToolChoice.REQUIRED);
// 使用 agentloop 驱动智能体循环,最多 8 轮对话
try {
for await (const text of agentloop(ctx, session, engine, fnm, 8)) console.log(text);
} catch (e) {
if (e instanceof Submission) {} else throw e;
console.log(e.weather);
console.log(e.advice);
}