> 原始 Markdown 孪生体（构建期从源 Markdown 生成）。渲染页：https://docs.gatellm.io/zh-CN/howto/script-protocol-translate · 文档索引：https://docs.gatellm.io/zh-CN/llms.txt


# 用脚本做协议翻译修正

跨协议翻译是网关的核心能力，但有时候你需要做些超出默认翻译的事情——按客户端协议分支处理字段、按分组注入不同参数、修正某协议的方言差异等。本章给几个 before / after 槽脚本的实战例子。

前置：先读 [写第一个脚本变换](/zh-CN/howto/write-script-transform.md) 了解基本结构、挂载方式、Context API。

按 Claude Code plan 模式切换模型的完整脚本已独立成页，见 [用脚本按 Claude Code plan 模式切换模型](/zh-CN/howto/script-switch-model-by-plan-mode.md)。

## 例 1：多模态检测 + 路由切换（before 槽）

检测图片输入或联网搜索参数，切换到多模态模型。放在 **before 槽**（`request_transform_before`）：

```javascript
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`、Anthropic `image`、Google `inlineData` / `fileData`）
- 按 `context.sourceProtocol` 分支处理，否则跨协议客户端会漏判
- `context.useModel` 做完整路由切换（base_url、协议、凭证一起换）

## 图片/联网切路由：优先用原生 `switch-route`（非脚本）

例 1 的 before 槽脚本能按协议分支检测图片，适合**跨字段计算 / 复杂判定**。但若判定只是「请求里有没有图片块 / 有没有打开联网」这类**结构检查**，应改用原生 `request_payload` 的 `switch-route` 规则，不必写脚本：它不读写 body 内容、同协议跨协议皆可、谓词不解引用二进制字节，大请求体的磁盘暂存保持开启，不会在多模态大 body 上 OOM 或被静默跳过。

完整的条件路径写法（含 Anthropic 客户端**容易漏的 `tool_result` 嵌套图片深层路径**）、JSON 形态与验证步骤，见 [把含图片的请求路由到视觉模型](/zh-CN/howto/route-image-requests-to-vision-model.md)——那是 `switch-route` 的唯一权威页。只有当判定逻辑超出结构检查能表达的范围时，才回退到例 1 的脚本方式。

## 例 2：按访问密钥分组设置参数

根据访问密钥分组（`context.accessKeyGroup`）设置不同的请求参数：

```javascript
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

```javascript
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 参考](/zh-CN/reference/scripting-api.md) 看完整内置函数表；[脚本配置](/zh-CN/reference/scripting-config.md) 看运行限制与配置；[用脚本按 Claude Code plan 模式切换模型](/zh-CN/howto/script-switch-model-by-plan-mode.md) 看按会话标记切路由的实战。
