17-run-script.md 4.7 KB

17 脚本调用工具(run_script)

状态:已实施(2026-10-29)。决策见 ADR-0026。

1. 问题

MCP 服务的工具往往比较底层,一个分析任务可能要调用几百次(例如查询 500 台设备的状态再汇总)。模型逐次调用时:

  • 每次调用都要一轮模型推理,慢,token 消耗随次数急剧增长;
  • 几百个结果堆在上下文里,会触发压缩,或者模型改为抽样,结果不完整。

模型自己的沙箱里写脚本调用不到 MCP 工具(工具只经网关提供给引擎)。而事先为每种分析方式都在服务端做好汇总工具,又不够灵活。

2. 方案

网关新增工具 run_script(code, tools, description):模型写一段 Python 脚本,由网关在沙箱中执行。脚本通过 agent_tools 模块调用 MCP 工具:每次调用都经过网关(工具策略、结果处理与普通调用相同),只是不经过模型。循环、筛选、汇总、写文件都在脚本中完成,只有脚本打印的内容交回模型。

from agent_tools import call, call_many, call_raw, progress, ToolError
rows = call_many("fleet__get_device_status", [{"sn": sn} for sn in sns])   # 平台并发执行
progress(len(rows), len(sns), "汇总")
函数 说明
call(tool, args) 返回结构化结果(dict)或文本;工具报错时抛出 ToolError
call_many(tool, args_list, return_errors=False) 批量调用同一个工具,按顺序返回;每批 50 个请求,平台按并发上限执行
call_raw(tool, args) 返回 {text, structured, is_error},不抛出工具错误
progress(done, total, message) 报告进度,显示在对话中
  • 只有 Profile 勾选了第三方服务(有可用的第三方工具)时才提供 run_script。
  • 工具说明(RUN_SCRIPT_DESCRIPTION)中写明了用法和规则,模型据此决定何时使用。

3. 执行

  • 沙箱:bubblewrap,与文档转换相同的配置(services/office.ts 的 sandboxArgs):系统目录只读、Python 运行环境只读、会话工作区可读写(只读模式下只读)、不能联网、/tmp 独立。当前目录为工作区。
  • 通信:脚本经 fd 3 发请求、fd 4 收回复,每行一个 JSON;标准输出留给脚本自己打印。辅助模块在启动时写入 data/script-runtime/agent_tools.py,沙箱中只读。
  • 限额(系统设置 scripts):每个脚本最多调用 2000 次工具、同时 4 次、最长 30 分钟。超时或取消时结束整个进程树。
  • 转后台:与其他长调用相同(docs/design/15-background-calls.md),超过 backgroundCalls.autoAfterSec 转为后台,完成后把结果作为新消息发给发起方;可以在后台调用栏取消。服务重启后中断,与"保持连接等待"的后台调用一样按失败通知。
  • 返回模型:一行概况(是否正常结束、调用次数与失败次数、用时、调用明细路径)+ 脚本打印的内容(超过 2 万字截断,完整输出存为文件);出错时附上错误信息的最后一部分。

4. 权限

工具 在脚本中
只读工具 直接调用
写工具(跟随权限模式、直接调用) 必须在 tools 参数中列出;"需审批"模式下,执行前统一审批一次(卡片列出这些工具和脚本内容,只有"允许一次"和"拒绝");已"本会话允许"的不再询问;自动模式不询问
破坏性工具(总是审批) 不能在脚本中调用,请模型直接调用、逐次确认
只能以长任务方式调用的工具 不能在脚本中调用
关闭的工具、只读模式下的写工具 不提供

没有列出的写工具在脚本中调用时,脚本收到 ToolError。

5. 记录与进度

  • 调用明细:会话工作区 .agent/scripts/<脚本 id>/:script.py(脚本)、calls.jsonl(每次调用一行:时间、工具、参数、耗时、是否出错、结果摘要)、output.txt(完整输出)。
  • 审计:每个脚本一条(gateway_call):说明、调用次数与失败次数、各工具的次数、用时、退出码、明细目录、脚本内容。不为每次调用写审计,避免淹没审计页面。
  • 进度:事件 script.progress(只推送、不保存,每秒最多一次):已调用次数、失败次数、脚本报告的进度、用时、状态。<AgentChat> 在时间线底部显示进度(有总数时带进度条),结束后几秒移除。工作组中暂不显示。

6. 不做

  • 其他语言(只支持 Python)。
  • 脚本中调用网关的内置工具(文档转换、网页读取)和资源读取:需要时由模型直接调用。
  • 服务重启后接续脚本。