用脚本做响应脱敏
响应脚本(response_transform)对上游返回的非流式响应体做后处理:脱敏 PII、注入请求元数据、剥离内容降成本。本章给几个实战示例。
前置:先读 写第一个脚本变换 了解基本结构、挂载方式、Context API。
流式响应的脚本暂不支持。
response_transform仅在非流式请求时生效。
例 1:响应中脱敏 PII
把响应里的邮箱地址替换为 [REDACTED]:
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 字段:
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_id 和 gateway_elapsed_ms,便于客户端排查问题。
例 3:剥离图片内容(降低成本)
把响应里的图片块过滤掉,保留文字:
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-typesrequest_payload 规则代替脚本,性能更好。
例 4:响应注入告警标记
当上游响应状态码不是 200 时,在响应里注入一个告警字段:
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.responseStatus | number | 上游 HTTP 状态码 |
context.responseHeaders | object | 上游响应 headers |
context.elapsedMs | number | 耗时(毫秒) |
加上请求阶段也有的字段(clientModel、upstreamModel、sourceProtocol、accessKeyName、accessKeyGroup、requestId 等)。完整表见 脚本 API 参考。
安全:
authorization、cookie、x-api-key等敏感 headers 在脚本可见之前已被移除,context.responseHeaders也会过滤敏感头。
内置函数参考
响应脱敏常用的内置函数:
// 正则替换
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
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? 安全设计:authorization、cookie、x-api-key 等敏感 headers 在脚本可见之前已被移除,防止凭证泄露。
Q:脱敏脚本失败时怎么保证安全? 配 script_error_mode = log-and-reject。脚本失败时拒绝请求,确保未脱敏内容不流到客户端。
Q:响应体太大,脚本跑得慢? 脚本执行的开销主要来自 JSON 序列化/反序列化。body 越大越慢。简单字段操作优先用 request_payload(无序列化开销)。避免不必要的 deep_clone / json_encode + json_parse。
