跳到主要内容

工具、MCP 与沙盒

适用版本

本文档基于 Aivory v2.4.7 编写。相关界面:“能力与集成 → 工具 / MCP 服务 / 技能 / 提示词”。沙盒架构与 HTTP 契约另见 Python 沙箱说明沙箱 API

工具把模型回复变成对外部系统、网络、文件或代码执行环境的操作,是一个独立的权限面:模型能调用工具,不等于所有用户、所有模型、所有工作区都应获得该能力。

工具启用存在两层控制

  1. 全局可用性(“能力与集成 → 工具”):停用后该工具从所有模型中移除,包括模型页明确允许它的。
  2. 模型默认项(“AI 与模型 → 模型 → 内置工具 / MCP 工具”):决定该模型对话中默认勾选哪些工具;用户仍可在单个对话中调整。

内置工具清单与每轮预算

平台工具及单轮对话上限(超出后模型只能基于已有结果作答):

工具作用单轮上限未配置后端时
aivory_web_search联网搜索16禁用搜索后端即不可用
web_fetch抓取网页内容12始终可用
fetch_image抓取图片16始终可用
image_generate图像生成8需要先配置图片模型
python_execute沙盒内执行 Python16沙盒未配置时对所有模型隐藏
save_memory保存长期记忆随记忆功能
use_skill按需加载技能随技能库

所有工具共享每轮总量上限(默认 48 次,AIVORY_LLM_MAX_TOOL_CALLS_PER_TURN);“快速”模式收紧到 12;深度研究模式放宽到约 150。相应环境变量与服务商轮次上限见环境变量进阶

对外的措辞

README 里“48 次工具调用、12 个服务商轮次”的说法并不精确:48 是正常模式每轮工具调用总上限,服务商请求轮次上限是独立配置(默认 20)。对外承诺能力时以本页数值为准。

工具页:全局可用性、代码沙箱与联网搜索配置

联网搜索

“工具”页的“联网搜索”区块配置 aivory_web_search 的后端:

字段说明示例默认
搜索后端SearXNG 自部署(无需密钥);Serper / Brave 为付费SearXNG(自部署)禁用
Base URLSearXNG 实例根地址https://searx.example.com
SearXNG 搜索引擎逗号/空格分隔的引擎名或快捷键,留空用实例默认bing, wikipedia
API KeySerper / Brave 必填

启动默认值由环境变量 SEARCH_PROVIDER / SEARCH_API_KEY / SEARCH_BASE_URL 提供,后台保存后以数据库为准。

推荐上线顺序:选定满足地区、隐私、额度和延迟要求的提供方 → 保存后用确定答案的问题验证结果质量 → 确认目标模型允许调用搜索、为用户群设置积分/配额 → 观察费用、失败率与不当内容风险,再决定开放深度研究与网页抓取。

SSRF 与数据外泄

搜索 Key 是服务端机密,不要进前端变量。可访问内网的实例还要评估 web_fetch / fetch_image 的抓取目标风险:不要让模型有理由去抓 http://169.254.169.254 或内部服务地址。

MCP 服务

Aivory 只接受 Streamable HTTP 传输的 MCP 服务(不支持 stdio 子进程)。注册流程:

  1. 进入“能力与集成 → MCP 服务”→“新建 MCP 服务”。
  2. 填写:
字段说明示例
名称 / 图标 / 工具描述均必填;描述决定模型何时选用该服务,写成清晰的“能做什么”12306 火车查询
Streamable HTTP URL端点必须能接收 MCP JSON-RPC 请求https://mcp.example.com/mcp
请求头可选,随每次 MCP 请求发送;已保存的敏感值以掩码显示Authorization: Bearer …
对用户可用建议先测试并同步工具后再开启
  1. 点“测试”验证连接(状态:未测试 / 连接正常 / 连接错误),再点“同步工具”拉取方法快照;列表显示“已发现 N 个 / 上次同步于 …”。
  2. 需要给某些模型默认勾选时,到模型编辑页的“MCP 工具”区块选择。未同步的服务即使启用也无法运行;服务被删除或停用后,模型默认项会显示“已删除/已停用/未同步”的失效标记,应清理或恢复。

MCP 服务页(空状态)

添加前先回答四个问题
  1. 该服务可读取或修改哪些外部数据?
  2. 请求头 / Bearer Key / OAuth 凭据是否只授予最小权限?
  3. 哪些模型、用户组或工作区应当看到这些工具?
  4. 服务不可用、工具输出异常或出现高频调用时如何降级?

把生产写入型 MCP + 个人访问令牌直接开放给所有聊天模型,等于把提示注入变成外部系统风险。先添加一个只读测试服务,检查发现的方法与参数,再小范围启用。每次服务端更新、方法变化或凭据轮换后都要重新同步和测试。

技能与提示词

“技能”是可复用的操作手册(名称、图标、何时使用(一句话)、完整指令 Markdown、附件资产),“提示词”是供用户加入个人库的公共模板。技能增强工具使用,但不能替代权限控制

  • 技能中不要写长期 API Key、数据库密码、Cookie 或一次性登录链接。
  • 一个技能应说明它需要哪些工具、可访问的数据范围、失败时的安全行为。
  • “何时使用”会被注入系统提示索引并决定召回率,写成触发条件而不是宣传语。
  • 上传资产前检查版权、隐私与文件类型;管理员资产与用户附件的备份和访问控制不同。
  • 每个新技能建立可重复的测试对话,确认模型不会因模糊指令调用写入型工具。
  • 用户组页的“权限”区块可以按组限制可见的提示词、技能、工具和 MCP。

技能页(空状态)

Python 沙盒:三种启用方式

python_execute 依赖可访问的沙盒服务。“工具”页“代码沙箱”区块显示当前状态;没有可用沙盒时显示**“请先配置沙盒”**,Python 开关保持关闭——这在个人版和完整版都一样,它反映实例能否安全连接到沙盒,与管理员权限无关。不要试图强制打开解释器绕过它。

运行方式适用配置要点
完整版内置 sidecar标准完整版Compose 已在内部网络提供 http://sandbox:8000,应用侧默认值已接好,无需暴露端口
个人版可选 profile可信单机--profile sandbox 启动,并在 .env.personal 设置 SANDBOX_BASE_URL=http://sandbox:8000SANDBOX_API_KEY=aivory-personal-sandbox
外部沙盒已有执行平台或需要更强隔离在“工具”页(或环境变量 SANDBOX_BASE_URL / SANDBOX_API_KEY)填写 HTTPS 地址与相同 Bearer Key;独立部署的 sidecar 默认发布在 48217 端口,绝不可公网裸露

后台字段(“工具”页“代码沙箱”区块):

字段说明默认
沙箱地址留空使用部署环境配置的地址环境值
沙箱密钥调用沙盒的 Bearer 密钥环境值
执行超时(秒)单次运行最长秒数,范围 10–600;留空 = 120120
闲置回收(秒)空闲多久回收会话(回收前归档工作区),范围 60–864001800
部署后的真实超时上限

官方两个 compose 文件都把 sidecar 的执行上限设为 SANDBOX_EXEC_TIMEOUT_CAP_MS=600000(10 分钟);后台“执行超时”会被夹到 [10, 600] 秒并预留 120 秒客户端余量。文档与 README 中偶尔出现的“2 分钟”是 sidecar 未显式配置时的代码默认值,不是部署值。

会话生命周期与持久工作区

  • 每个会话对应一个锁定严格的 Docker 容器:永远 --network none(不可配置),会话内不能 pip install 或访问网络;需要依赖就构建进运行镜像(Dockerfile.runner 已预装 numpy/pandas/matplotlib/python-docx 等)。
  • 会话按对话维度维护 /workspace;空闲超过回收阈值后被 reap,工作区 tar 归档到存储(local 或 S3/OSS,归档键为对话 ID),下次执行自动恢复。多应用副本必须使用共享对象存储,本地归档只适合单节点。
  • 归档受“存储与上传”中的归档保留天数控制(默认 30 天)。持久工作区 ≠ 永久保留所有输出:文件保留、删除与用户导出策略仍要管理员制定。
  • 完整版与个人版沙盒 profile 都会启动镜像保活服务,防止宿主机 docker image prune -a 清掉约 600MB 的运行镜像后,下一次 python_execute 冷拉取导致超时 500。不要把它当作多余容器删除。

安全边界

sidecar 通过宿主 Docker socket 创建会话容器,non-root、--cap-drop ALLno-new-privileges、只读根文件系统、限额的 tmpfs。但 Docker socket 本身近似宿主机 root,因此:

  • 只在完全受控的服务器启用本机 sidecar,不放不受信任的共享宿主机。
  • 不给 sandbox 服务加 ports,不通过反向代理暴露它;沙盒 Bearer Key 泄露即可被用来创建/访问执行会话。
  • 保留 SANDBOX_READ_ONLY_ROOTFS=1,除非完全理解关闭后的磁盘与写入风险。
  • SANDBOX_MEMORY(默认 2g)、SANDBOX_CPUS(1)、SANDBOX_PIDS_LIMIT(256)、SANDBOX_MAX_SESSIONSSANDBOX_WORKSPACE_SIZE(512m)、SANDBOX_MAX_UPLOAD_BYTES(40MiB)给宿主机保留余量。
  • 当前隔离等级不是 gVisor/microVM 级;更强需求可替换执行后端——保持沙箱 API 的 HTTP 契约不变即可。

模型对话中的真实效果(推理 + 工具调用过程可见):

工具调用与推理过程(英文界面示例)

运行与故障检查

Python 不可用时依次检查:

  1. “工具”页是否显示沙盒已配置,地址与密钥是否匹配(两边留空时都用环境值,容易“都以为对方配了”)。
  2. 完整版或本机 profile 的 sandbox 容器是否运行、是否健康(GET /healthz 返回 {ok, docker, image})。
  3. 宿主 Docker daemon 与 socket 是否可用,运行时镜像是否已被误删(看 keepalive 服务)。
  4. 执行超时、并发会话上限、上传大小是否设置过低。
  5. 目标模型是否被全局禁用工具、模型内置工具默认项或工作区策略限制。
不要这样“修”

不要通过公开沙盒端口、关闭鉴权(SANDBOX_ALLOW_NO_AUTH 仅限本机开发)或无限增大资源上限来消除报错。先读脱敏后的应用与沙盒日志,定位是网络、鉴权、镜像、资源还是模型授权问题。

手工探活(外部沙盒)

沙盒是普通 HTTP 服务,可以直接验证契约(把 $KEY 换成沙盒密钥):

curl -s http://<sandbox-host>:48217/healthz -H "Authorization: Bearer $KEY"
# {"ok":true,"docker":true,"image":"..."}

SID=$(curl -s -X POST http://<sandbox-host>:48217/sessions \
-H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{}' | jq -r .session_id)

curl -s -X POST http://<sandbox-host>:48217/exec \
-H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d "{\"session_id\":\"$SID\",\"code\":\"print(6*7)\"}"

/healthzdocker:false 说明 socket 挂了或权限不对;返回 401 说明两边密钥不一致。

工具类故障速查

症状优先检查
工具页显示“请先配置沙盒”后台或环境里 SANDBOX_BASE_URL + SANDBOX_API_KEY 成对配置了吗
Python 偶发 500,日志提到 pull image运行镜像被 prune——检查 keepalive 服务与镜像策略
执行总在同一秒数被掐断后台“执行超时”、sidecar SANDBOX_EXEC_TIMEOUT_CAP_MS、代理读超时三者取最小
换副本后工作区文件消失归档写的是本地存储而应用多副本——改共享对象存储
搜索总是“不可用”搜索后端选择与 Key 是否匹配(SearXNG 无需 Key、Serper/Brave 必须有)
模型说没有某工具全局禁用 > 模型内置工具默认项 > 用户组权限 > 工作空间策略,四层任一拦截
MCP 已启用但模型用不了“MCP 工具可用性”里该服务是否勾选、是否同步过工具