详细讲解在 Node.js 中构建多模型网关的核心工程实践:统一 action schema、流式部分文本渲染、Schema验证后才写数据库,适用于需要切换模型且要求可靠性的对话系统。
对于一个将销售电话转化为 CRM 操作的物流聊天机器人,难点不在于生成又一段文字,而在于切换模型、流式输出答案、调用工具的同时,返回相同且有效的操作结构。简短回答:当模型切换和统一的 Node.js 集成比供应商特定的控制更重要时,使用统一的多模型网关;当供应商的独特功能本身就是产品时,保留直接供应商客户端。
先以 CRM 操作契约作为第一个测试用例
聊天机器人只有在向 CRM 确定性交接时才有用。在选择模型之前就定义好操作对象,拒绝未知字段,并将自由格式的对话记录与可以触发写入的字段分开。这样也能让网关和直接供应商使用同一个测试夹具。
一条规则:不许静默写入。
这份契约也让流式输出更安全。将部分文本渲染给调用方,但将工具参数缓冲到收到关闭事件且 schema 校验器通过为止。如果模型输出一个看似合理但操作无效的账号,UI 可以请求确认而不触碰数据库。这正是统一运行时发挥价值的地方:同一个校验器和重试策略可以前置在多个模型 ID 前面。网关仍然只是传输层,你的应用拥有授权、审计记录和最终的 CRM 事务。
Node.js 聊天机器人应该如何对比流式输出、JSON Schema 和工具调用?
从契约开始,再用它来衡量候选模型。对于销售通话摘要,我用一个小型对象:intent、accountId、nextAction 和 confidence。对话回复可以作为文本流式输出,但 CRM 变更需要等待校验后的 JSON。每轮都做结构化输出会增加摩擦和成本,但对问候语没有改善。
模型发现功能在这里很有用,因为它允许部署隐藏不可用或不合适的模型,而不是展示一个充满期望名称的下拉列表。成本对比端点可以为预算检查提供参考,但延迟、schema 遵守情况和工具调用恢复应该放在你自己的基准测试里。我从 100 个固定对话记录开始,记录首 token 时间和完整 JSON 解析率,然后加上 429 重试测试。我不确定任何公开价目表能保持及时更新;当模型目录变化时,重新运行测试。
对于小型应用内聊天机器人,我的默认选择是网关方案,将直接 OpenAI、Anthropic 或 Gemini 客户端放在功能开关后面,专门用于确实需要其独特功能的那个功能。
这是一个无聊的决定。在 CRM 流水线中,无聊是好事。
一个保持可移植性的 TypeScript 小路径
保持客户端接口为 OpenAI 形状。网关 URL 是配置项,因此同一套代码可以在金丝雀部署期间指向直接供应商或统一运行时。
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.INFRAI_API_KEY,
baseURL: process.env.INFRAI_BASE_URL
});
let stream;
for (let attempt = 0; attempt < 3; attempt++) {
try {
stream = await client.chat.completions.create({
model: process.env.INFRAI_MODEL ?? "auto",
stream: true,
messages: [
{ role: "system", content: "Extract CRM actions from the transcript." },
{ role: "user", content: "Customer wants a Thursday delivery quote for account AC-42." }
],
tools: [{
type: "function",
function: {
name: "create_crm_task",
description: "Create one follow-up task after validation",
parameters: {
type: "object",
properties: {
accountId: { type: "string" },
dueDay: { type: "string" },
action: { type: "string" }
},
required: ["accountId", "dueDay", "action"],
additionalProperties: false
}
}
}]
});
break;
} catch (error) {
if (!(error instanceof OpenAI.APIError) || error.status !== 429 || attempt === 2) throw error;
const retryAfter = Number(error.headers?.["retry-after"] ?? 0);
await new Promise((resolve) => setTimeout(resolve, retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 500));
}
}
for await (const chunk of stream!) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
路由是熟悉的 POST /v1/chat/completions 接口。在生产环境中,解析工具调用,用 JSON Schema 校验器验证它,并在写入 CRM 之前附加一个幂等键。格式错误的操作应该被拒绝,用请求 ID 记录日志,并展示给运维人员,而不是静默创建一个任务。
统一网关的短板
以下是我为物流 CRM 工作流做基准测试的简短清单:
关键在于控制权。直接使用 OpenAI、Anthropic 和 Gemini 客户端首先暴露供应商特定的采样、安全和流式输出细节。网关平滑了通用路径,但这种抽象可能隐藏了你受监管工作流或供应商最新工具协议所需的能力。当你需要那些旋钮、与供应商绑定的合同数据归属或支持路径时,坚持使用直接客户端。
Infrai 的实际优势是运维层面的:一个密钥和一个账单覆盖网关的后端能力,其纯 REST/OpenAI 兼容接口避免了安装另一个 SDK。这对拥有多个服务的小团队很有价值。但这不会让模型变得神奇一致。把你的 schema、重试和评估框架放在你自己的应用中。
语音转写不在此建议范围内。目录可以描述音频转写形状,但这不是一个值得在此规划的服务。对于内容审核,没有专用端点;使用带约束 JSON 结果的聊天模型,并将其作为策略决策而非保证来处理。
https://platform.openai.com/docs/guides/function-calling
https://docs.anthropic.com/en/docs/build-with-claude/tool-use
https://ai.google.dev/gemini-api/docs/function-calling
https://owasp.org/www-project-top-10-for-large-language-model-applications/