“不要读取敏感文件”“执行命令前必须确认”“只能访问指定服务”这类提示词,可以指导模型,却不能构成可靠的安全边界。智能体一旦能够调用 shell、浏览器、数据库或内部 API,治理就必须进入运行时:由模型之外的组件拦截操作、检查策略、限制资源,并记录实际执行结果。
关键变化在于,系统不再假设智能体会始终遵守建议,而是假设模型输出可能出错、被提示注入影响,或者在多步任务中偏离原定范围。提示词仍然有用,但它负责表达意图;运行时负责执行不可绕过的约束。
为什么提示词不能承担强制治理
提示词属于模型上下文的一部分。系统消息可以规定行为,但模型看到的网页、邮件、文档和工具返回值也可能包含冲突指令。上下文越长、工具越多,依赖模型自行判断的风险就越高。
运行时治理需要把几个决定从模型手里拿出来:
- 智能体是否有权调用某个工具。
- 哪些参数、目录、域名和数据库表可以访问。
- 单次调用可以消耗多少时间、内存和网络资源。
- 高风险操作是否需要人工批准。
- 操作失败或越权时,系统如何终止、降级和审计。
例如,“不要执行 rm -rf”是建议;工具网关根本不提供任意 shell,或者只允许结构化的 list_files、read_report 操作,才是执行。即使模型生成了危险命令,它也没有直达操作系统的路径。
三层运行时边界
1. 隔离执行环境
代码执行、文件解析和浏览器自动化应进入独立容器、微型虚拟机或受限沙箱。隔离层负责控制文件系统、网络、进程、系统调用和资源配额。
容器并不自动等于完整安全沙箱,但可以作为基础限制。下面的命令可以直接运行,用于观察一个只读、无网络、限制资源的 Python 执行环境:
docker run --rm \
--network none \
--read-only \
--cap-drop ALL \
--security-opt no-new-privileges \
--pids-limit 64 \
--memory 256m \
--cpus 0.5 \
--tmpfs /tmp:rw,noexec,nosuid,size=32m \
python:3.12-alpine \
python -c 'import os; print("sandbox uid:", os.getuid()); print("files:", os.listdir("/tmp"))'
生产环境还需要结合宿主机加固、镜像来源控制、内核隔离方案和出站网络代理。尤其不要因为设置了 --read-only,就默认容器能够抵御所有内核级攻击。
2. 策略执行点
每次工具调用都应经过一个位于模型之外的策略执行点。它接收结构化请求,根据智能体身份、任务、租户、工具、参数和当前风险状态作出允许、拒绝或要求审批的决定。
策略检查应发生在副作用之前。只在执行后写审计日志,可以帮助调查,却无法阻止事故。对于转账、删除数据、发送外部邮件等操作,还应把“生成计划”和“提交变更”拆成两个权限不同的阶段。
3. 受控工具网关
不要把数据库密码、云密钥或通用 shell 直接交给模型。更稳妥的设计是让工具网关持有凭据,并向智能体暴露范围较窄的结构化能力,例如:
{
"tool": "read_report",
"arguments": {
"name": "daily-sales.csv"
},
"task_id": "task-42"
}
网关可以校验文件名、绑定租户目录、设置超时、截断过大的输出,并为每次调用生成审计事件。这样,模型拿到的是一项能力,而不是能力背后的长期凭据。
可以这样实践:实现一个最小工具代理
下面是一个仅使用 Python 标准库的最小示例。假设智能体通过 JSON 请求调用工具,代理只允许列出指定目录或读取其中的 .txt 文件。路径校验在实际文件访问前完成,所有决定都会写入标准输出。
将代码保存为 tool_broker.py,然后运行 python tool_broker.py:
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
WORKSPACE = Path("agent_workspace").resolve()
MAX_BYTES = 4096
def audit(event: dict[str, Any]) -> None:
print(json.dumps(event, ensure_ascii=False))
def resolve_allowed_path(raw_path: str) -> Path:
candidate = (WORKSPACE / raw_path).resolve()
if candidate != WORKSPACE and WORKSPACE not in candidate.parents:
raise PermissionError("path escapes the workspace")
return candidate
def execute(request: dict[str, Any]) -> dict[str, Any]:
tool = request.get("tool")
args = request.get("arguments", {})
if tool == "list_files":
files = sorted(p.name for p in WORKSPACE.iterdir() if p.is_file())
return {"files": files}
if tool == "read_text":
path = resolve_allowed_path(str(args.get("path", "")))
if path.suffix != ".txt":
raise PermissionError("only .txt files are allowed")
if not path.is_file():
raise FileNotFoundError(path.name)
if path.stat().st_size > MAX_BYTES:
raise PermissionError("file exceeds the read limit")
return {"path": path.name, "content": path.read_text(encoding="utf-8")}
raise PermissionError(f"tool is not allowed: {tool}")
def handle(request: dict[str, Any]) -> dict[str, Any]:
task_id = str(request.get("task_id", "unknown"))
try:
result = execute(request)
audit({"task_id": task_id, "tool": request.get("tool"), "decision": "allow"})
return {"ok": True, "result": result}
except (PermissionError, FileNotFoundError, OSError) as exc:
audit({
"task_id": task_id,
"tool": request.get("tool"),
"decision": "deny",
"reason": str(exc),
})
return {"ok": False, "error": str(exc)}
if __name__ == "__main__":
WORKSPACE.mkdir(exist_ok=True)
(WORKSPACE / "status.txt").write_text("build: healthy\n", encoding="utf-8")
requests = [
{"task_id": "task-1", "tool": "read_text", "arguments": {"path": "status.txt"}},
{"task_id": "task-2", "tool": "read_text", "arguments": {"path": "../secret.txt"}},
{"task_id": "task-3", "tool": "shell", "arguments": {"command": "id"}},
]
for item in requests:
print(json.dumps(handle(item), ensure_ascii=False))
这个示例不是完整的生产级沙箱,但展示了治理边界应放在哪里:模型只能提交结构化意图,代理负责验证路径、文件类型、大小和工具名称。实际系统可以继续加入身份认证、租户隔离、速率限制、审批状态、调用超时以及不可篡改的审计存储。
需要特别注意符号链接和检查后使用之间的竞态条件。处理敌对文件系统内容时,应使用更强的文件描述符级约束或成熟沙箱,而不能只依赖字符串路径检查。
上线前检查治理是否真的不可绕过
运行时治理的目标不是增加一层形式化审批,而是保证智能体无法绕开执行点。接入生产环境前,可以检查以下事项:
- 模型是否能够直接获得底层凭据或绕过工具网关。
- 默认策略是否为拒绝,并按任务授予最小权限。
- 文件、网络、进程、时间和费用是否都有明确上限。
- 高风险操作是否需要短期授权或人工确认。
- 策略判断和真实副作用是否使用同一个身份与请求标识。
- 日志是否记录策略版本、参数摘要、决策原因和执行结果,同时避免泄露敏感数据。
- 工具返回内容是否被视为不可信输入,并在重新送入模型前限制大小和类型。
- 撤销权限、终止任务和轮换凭据是否可以快速完成。
隔离、策略执行和受控工具访问会增加延迟与工程复杂度,也可能限制智能体的灵活性。合理的做法不是让所有调用都经过人工审批,而是按风险分级:只读、低影响操作自动放行;写入和外部通信接受更严格的参数约束;不可逆操作要求显式授权。提示词描述期望行为,运行时决定系统实际上允许发生什么。