跳到正文

用脚本做响应脱敏

响应脚本(response_transform)对上游返回的非流式响应体做后处理:脱敏 PII、注入请求元数据、剥离内容降成本。本章给几个实战示例。

前置:先读 写第一个脚本变换 了解基本结构、挂载方式、Context API。

流式响应的脚本暂不支持。response_transform 仅在非流式请求时生效。

例 1:响应中脱敏 PII

把响应里的邮箱地址替换为 [REDACTED]

javascript
function transform(body, context) {
    for (let i = 0; i < body.choices.length; i++) {
        if (body.choices[i].message.content) {
            body.choices[i].message.content = regex_replace(
                body.choices[i].message.content,
                "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
                "[REDACTED]"
            );
        }
    }
    return body;
}

挂在模型或上游的 response_transform 字段。响应返回客户端前,所有邮箱地址都被替换。

安全建议:脱敏脚本建议配 script_error_mode = log-and-reject,确保脚本失败时不放过未脱敏的内容。

例 2:在响应中添加请求元数据

把网关的请求 ID 和耗时注入到响应的 usage 字段:

javascript
function transform(body, context) {
    if (body.usage) {
        body.usage.gateway_request_id = context.requestId;
        body.usage.gateway_elapsed_ms = context.elapsedMs;
    }
    return body;
}

挂在 response_transform。响应里的 usage 字段会多出 gateway_request_idgateway_elapsed_ms,便于客户端排查问题。

例 3:剥离图片内容(降低成本)

把响应里的图片块过滤掉,保留文字:

javascript
function transform(body, context) {
    for (const msg of body.messages) {
        if (Array.isArray(msg.content)) {
            msg.content = msg.content.filter(block => block.type !== "image_url");
        }
    }
    return body;
}

简单场景建议用 filter-content-types request_payload 规则代替脚本,性能更好。

例 4:响应注入告警标记

当上游响应状态码不是 200 时,在响应里注入一个告警字段:

javascript
function transform(body, context) {
    if (context.responseStatus >= 400) {
        body._gateway_warning = {
            reason: "upstream_error",
            status: context.responseStatus,
            elapsed_ms: context.elapsedMs
        };
    }
    return body;
}

Context 响应阶段可用字段

字段类型说明
context.responseStatusnumber上游 HTTP 状态码
context.responseHeadersobject上游响应 headers
context.elapsedMsnumber耗时(毫秒)

加上请求阶段也有的字段(clientModelupstreamModelsourceProtocolaccessKeyNameaccessKeyGrouprequestId 等)。完整表见 脚本 API 参考

安全authorizationcookiex-api-key 等敏感 headers 在脚本可见之前已被移除,context.responseHeaders 也会过滤敏感头。

内置函数参考

响应脱敏常用的内置函数:

javascript
// 正则替换
regex_replace("foo123bar", "[0-9]+", "N");  // "fooNbar"

// 字符串操作
str_contains("hello world", "world"); // true
str_replace("foo-bar", "-", "_");    // "foo_bar"
str_starts_with("hello", "he");     // true
str_trim("  hello  ");              // "hello"

// 编码
base64_encode("hello");    // "aGVsbG8="
base64_decode("aGVsbG8="); // "hello"
sha256("hello");           // "2cf24dba5fb0..."

// JSON 操作
json_encode(body.messages); // 序列化为字符串
json_parse(jsonStr);        // 从字符串解析

完整内置函数表见 脚本 API 参考

错误处理

script_error_mode

模式行为适用场景
log-and-continue(默认)脚本报错时记录 warn 日志,请求以原始 body 继续脚本失败不应阻断请求
log-and-reject脚本报错时拒绝请求,返回网关错误脚本正确性至关重要(如安全脱敏)

脱敏脚本建议配 log-and-reject,避免脚本失败时未脱敏内容流到客户端。

提前返回时必须返回 body

javascript
function transform(body, context) {
    // ✅ 正确
    if (!body.choices) return body;

    // ❌ 错误:return 无值 → 脚本静默失败
    if (!body.choices) return;

    body.choices[0].message.content = "modified";
    return body;
}

常见问题

Q:响应脚本对流式请求生效吗? 暂不支持流式响应的脚本。仅非流式时生效。请求脚本对流式请求有效。

Q:脚本改了 body.choices[0].message.content 但客户端没看到改后内容? 检查:① 请求是否流式(流式不跑响应脚本);② script_error_mode 是否为 log-and-continue(脚本失败时静默继续);③ 脚本是否挂在正确的模型/上游上。

Q:context.responseHeaders 里没有 Authorization? 安全设计:authorizationcookiex-api-key 等敏感 headers 在脚本可见之前已被移除,防止凭证泄露。

Q:脱敏脚本失败时怎么保证安全?script_error_mode = log-and-reject。脚本失败时拒绝请求,确保未脱敏内容不流到客户端。

Q:响应体太大,脚本跑得慢? 脚本执行的开销主要来自 JSON 序列化/反序列化。body 越大越慢。简单字段操作优先用 request_payload(无序列化开销)。避免不必要的 deep_clone / json_encode + json_parse

下一步脚本 API 参考 看完整内置函数表;脚本配置 看运行限制与配置;审计与安全配置 看其他安全设置。