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


# 脚本运行限制与配置

本章讲脚本引擎的环境变量、限制与安全、脚本测试入口。API 参考见 [脚本 API 参考](/zh-CN/reference/scripting-api.md)，入门见 [写第一个脚本变换](/zh-CN/howto/write-script-transform.md)。

## 脚本相关环境变量

| 变量 | 默认值 | 作用 |
|------|--------|------|
| `SCRIPT_MAX_OPERATIONS` | `2000` | 每次脚本执行的最大操作数 |
| `SCRIPT_MEMORY_LIMIT_MB` | `64` | 解释器堆上限（MB） |
| `SCRIPT_LAZY_BODY` | `true` | 是否对请求/响应体做懒投影；`false` 走旧整段物化分支 |

这三个控制操作数上限与内存；其他资源限制（字符串大小、数组元素、栈大小等）由沙箱硬编码，不可配置。改完环境变量需重启容器生效。完整环境变量见 [环境变量配置参考](/zh-CN/reference/configuration.md)。

## 资源限制

| 限制 | 值 | 可配置 |
|------|------|--------|
| 最大操作数 | 2,000 | `SCRIPT_MAX_OPERATIONS` |
| 解释器堆上限 | 64 MB | `SCRIPT_MEMORY_LIMIT_MB`（运行时创建期约束，变更需重启） |
| 并发执行槽 | 4 | `server.script_pool_size`（TOML 字段，非环境变量；同时执行的脚本数，每槽堆内存受 `SCRIPT_MEMORY_LIMIT_MB` 约束） |
| body 懒投影 | 开启 | `SCRIPT_LAZY_BODY`（默认 `true`；`false` 走旧整段物化分支，回滚靠显式切回；变更需重启） |
| 最大字符串 | 10 MB | 固定 |
| 最大数组/对象元素 | 10,000 | 固定 |
| 正则缓存 | 256 条，5 分钟 TTL | 固定 |
| 栈大小 | 1024 帧 | 固定 |

> ℹ️ **body 视图语义**：`SCRIPT_LAZY_BODY=true` 时，`body` 是按需取件的懒投影视图，整份 body 不整段进 JS 堆，JS 堆占用只与脚本访问的字段挂钩。视图在 `Array.isArray` / `instanceof` / `typeof` / `Object.keys` / 解构 / 展开 / `JSON.stringify` / 数组方法等标准判断下与真对象一致。**已知边界**：`body.x === body.x` 为假（不缓存壳），依赖同路径引用相等的脚本须改用值比较。

## 沙箱限制

### 脚本可以做什么

- 读取和修改 `body`
- 读取 `context` 元数据
- 使用所有内置函数（json_path、str_*、regex_*、array_* 等）
- 定义辅助函数
- 使用标准 JavaScript（循环、条件、闭包、解构等）

### 脚本不能做什么

| 被禁止 | 原因 |
|--------|------|
| `console.log` / `print` | 防止信息泄露 |
| 文件 I/O | 无文件系统访问 |
| 网络 / `fetch` | 无网络访问 |
| `import` / `require` | 不能加载外部代码 |
| `eval` / `Function` | 不能动态执行代码 |
| `globalThis` 修改 | 全局对象已冻结 |

## 错误处理

### script_error_mode

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

### 常见错误

| 错误 | 原因 | 解决 |
|------|------|------|
| `transform() must return a value` | 某个 return 路径忘了返回 body | **每个 return 都必须返回 body** |
| `TooManyOperations` | 超过操作数上限（默认 2000） | 优化循环 |
| `TypeError` | 访问 null/undefined 的属性 | 用 `json_path()` 或 `in` 运算符检查 |
| `ReferenceError` | 使用未定义的变量或函数 | 检查拼写 |

### 提前返回时必须返回 body

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

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

    body.messages[0].content = "modified";
    return body;
}
```

## 脚本测试 {#script-testing}

部署前先测试脚本。测试接口 `POST /console/api/scripts/test` 需要控制台认证——用环境变量 `$CONSOLE_TOKEN` 带上（它**不是**上游访问密钥，是控制台 API 凭证）。拿法二选一：

::: tip 获取 CONSOLE_TOKEN
**方式 1：配 `CONSOLE_SECRET_KEY`（推荐脚本 / CI 用）**——设环境变量 `CONSOLE_SECRET_KEY`，控制台把它当 Bearer token 用，长期有效、不怕过期：

```bash
# 启动网关时注入
docker run -d -e CONSOLE_SECRET_KEY="my-secret-key-xxx" ... <镜像>

# 测试脚本时带上
export CONSOLE_TOKEN="my-secret-key-xxx"
```

**方式 2：用控制台账号登录拿 session token**——不配 `CONSOLE_SECRET_KEY` 时走这条。`POST /console/api/login` 用账号密码登录，返回的 `token` 字段即 session token（默认 24 小时有效，调用会滑动续期）：

```bash
export CONSOLE_TOKEN=$(curl -s http://localhost:7890/console/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"protoflux","password":"你的密码"}' | jq -r .token)
```

`CONSOLE_SECRET_KEY` 与登录密码（`CONSOLE_PASSWORD`）相互独立，可只配其一。详见 [控制台登录与角色](/zh-CN/console/login-and-roles.md)。
:::

```bash
curl -X POST http://localhost:7890/console/api/scripts/test \
  -H "Authorization: Bearer $CONSOLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "script": "function transform(body, context) { body.model = context.upstreamModel; return body; }",
    "body": {"model": "gpt-4o", "messages": []},
    "context": {
      "clientModel": "gpt-4o",
      "upstreamModel": "gpt-4o-2024-05-13",
      "upstreamName": "openai",
      "sourceProtocol": "OpenAIChatCompletions",
      "targetProtocol": "OpenAIChatCompletions",
      "stream": false
    }
  }'
```

响应：
```json
{
  "result_body": {"model": "gpt-4o-2024-05-13", "messages": []},
  "execution_time_ms": 0.12
}
```

## 性能建议

::: tip 性能与内存
脚本开销主要来自 JSON 序列化，耗时随 body 线性增长。after 槽大 body OOM 反模式、耗时参考与写法建议见 [脚本性能与内存](/zh-CN/practices/script-performance.md)。
:::

## 常见问题

**Q：脚本在控制台界面哪里配？**
上游详情页和模型详情页都有「请求脚本」「响应脚本」输入框。也可以直接通过 Console API 设置。

**Q：脚本改了 body.model，为什么路由没变？**
`body.model` 只改请求体的字段，不改路由。要切换路由用 `context.useModel("模型名")`。

**Q：流式请求的响应脚本生效吗？**
暂不支持流式响应的脚本。请求脚本对流式请求有效，但响应脚本仅在非流式时生效。

**Q：脚本报错了但请求正常通过？**
默认的 `log-and-continue` 模式下，脚本报错不会阻断请求。查看 warn 日志了解报错原因。如需脚本必须成功，改用 `log-and-reject`。

**Q：context.headers 里为什么没有 Authorization？**
安全设计：`authorization`、`cookie`、`x-api-key` 等敏感 headers 在脚本可见之前已被移除，防止凭证泄露。

**Q：怎么调大操作数上限？**
`SCRIPT_MAX_OPERATIONS` 可调。但建议先优化脚本——`TooManyOperations` 通常意味着循环复杂度过高。

**下一步**：[脚本 API 参考](/zh-CN/reference/scripting-api.md) 看完整内置函数表；[写第一个脚本变换](/zh-CN/howto/write-script-transform.md) 看入门结构；[环境变量配置参考](/zh-CN/reference/configuration.md) 看全部环境变量；[审计与安全配置](/zh-CN/reference/audit-and-security-config.md) 看其他安全设置。
