用脚本做协议翻译修正
跨协议翻译是网关的核心能力,但有时候你需要做些超出默认翻译的事情——按客户端协议分支处理字段、按分组注入不同参数、修正某协议的方言差异等。本章给几个 before / after 槽脚本的实战例子。
前置:先读 写第一个脚本变换 了解基本结构、挂载方式、Context API。
按 Claude Code plan 模式切换模型的完整脚本已独立成页,见 用脚本按 Claude Code plan 模式切换模型。
例 1:多模态检测 + 路由切换(before 槽)
检测图片输入或联网搜索参数,切换到多模态模型。放在 before 槽(request_transform_before):
function transform(body, context) {
const proto = context.sourceProtocol;
let hasImage = false;
if (proto === "OpenAIChatCompletions") {
const msgs = body.messages;
if (Array.isArray(msgs)) {
for (const msg of msgs) {
if (Array.isArray(msg.content) &&
msg.content.some(b => b.type === "image_url")) {
hasImage = true;
break;
}
}
}
} else if (proto === "Anthropic") {
const msgs = body.messages;
if (Array.isArray(msgs)) {
for (const msg of msgs) {
if (Array.isArray(msg.content) &&
msg.content.some(b => b.type === "image")) {
hasImage = true;
break;
}
}
}
} else if (proto === "Google") {
const contents = body.contents;
if (Array.isArray(contents)) {
for (const c of contents) {
if (Array.isArray(c.parts) &&
c.parts.some(p => p.inlineData || p.fileData)) {
hasImage = true;
break;
}
}
}
}
if (hasImage) {
context.useModel("multimodal-model");
}
return body;
}要点:
- 必须放在 before 槽:要读客户端原始格式的图片字段,且不同协议字段名不同(OpenAI
image_url、Anthropicimage、GoogleinlineData/fileData) - 按
context.sourceProtocol分支处理,否则跨协议客户端会漏判 context.useModel做完整路由切换(base_url、协议、凭证一起换)
图片/联网切路由:优先用原生 switch-route(非脚本)
例 1 的 before 槽脚本能按协议分支检测图片,适合跨字段计算 / 复杂判定。但若判定只是「请求里有没有图片块 / 有没有打开联网」这类结构检查,应改用原生 request_payload 的 switch-route 规则,不必写脚本:它不读写 body 内容、同协议跨协议皆可、谓词不解引用二进制字节,大请求体的磁盘暂存保持开启,不会在多模态大 body 上 OOM 或被静默跳过。
完整的条件路径写法(含 Anthropic 客户端容易漏的 tool_result 嵌套图片深层路径)、JSON 形态与验证步骤,见 把含图片的请求路由到视觉模型——那是 switch-route 的唯一权威页。只有当判定逻辑超出结构检查能表达的范围时,才回退到例 1 的脚本方式。
例 2:按访问密钥分组设置参数
根据访问密钥分组(context.accessKeyGroup)设置不同的请求参数:
function transform(body, context) {
if (context.accessKeyGroup === "premium") {
body.max_tokens = 8192;
} else {
body.max_tokens = 4096;
}
return body;
}挂在 after 槽(request_transform_after),按分组差异化配额。
例 3:注入 System Prompt
function transform(body, context) {
if (body.messages.length === 0 || body.messages[0].role !== "system") {
const systemMsg = { role: "system", content: "You are a helpful assistant." };
body.messages = [systemMsg, ...body.messages];
}
return body;
}挂在 after 槽。简单的"不存在则注入"逻辑也可以用 request_payload 的 append-if-missing 规则(性能更好),脚本适合需要复杂判断的场景。
常见问题
Q:脚本切路由后,原路由的 after 槽还会跑吗? 不会。一旦 before 槽触发了 立即 useModel 切换路由,整套 pipeline 换成目标路由的,原路由的 after 槽不执行。所以原路由和目标路由上的 request_payload 规则要各配一份。
Q:context.useModel 切换的目标模型与当前模型协议不同,能切吗? 能。before 槽支持跨协议切换。但 after 槽的 useModel 只能同协议切换——after 槽拿到的是上游格式 body,跨协议会报错。
Q:脚本跑得太慢? 脚本执行的开销主要来自 JSON 序列化/反序列化。body 越大越慢。建议简单字段操作优先用 request_payload(无序列化开销),避免不必要的 deep_clone / json_encode + json_parse。
Q:脚本报 TooManyOperations? 超过操作数上限(默认 2000)。优化循环,把 O(n²) 改成 O(n)。SCRIPT_MAX_OPERATIONS 可调(但建议先优化脚本)。
下一步:脚本 API 参考 看完整内置函数表;脚本配置 看运行限制与配置;用脚本按 Claude Code plan 模式切换模型 看按会话标记切路由的实战。
