Appearance
pre_tool_call hook 无法阻止工具调用(缺少 REJECT 语义)
问题
pre_tool_call hook 目前是 fire-and-forget 模式——model_tools.py 中调用 invoke_hook("pre_tool_call", ...) 后完全丢弃返回值。这意味着:
- 扩展无法阻止某次工具调用
- 无法实现基于策略的防御深度沙箱
- 无法实现多租户隔离
- 无法实现 RBAC(基于角色的访问控制)
这是一个设计缺陷,不是 "missing feature"——没有 REJECT 语义的 hook 在安全场景下形同虚设。
解决方案
约 10 行代码的改动即可实现。核心思路:
python
# model_tools.py 当前代码(概念示意)
invoke_hook("pre_tool_call", tool_name=name, tool_args=args)
# 直接继续执行工具,不检查 hook 返回值
# 修改后
result = invoke_hook("pre_tool_call", tool_name=name, tool_args=args)
if result and result.get("action") == "REJECT":
reason = result.get("reason", "Blocked by pre_tool_call hook")
raise ToolCallRejected(reason)典型应用场景:
| 场景 | hook 应做什么 |
|---|---|
沙箱:禁止 file 工具访问 /etc/ 以外路径 | 检查路径参数,不合规则 REJECT |
| 多租户:A 用户的 agent 不能操作 B 用户的文件 | 检查 file_path 是否在当前租户目录下 |
RBAC:只有管理员 agent 可以调用 execute_code | 检查当前 profile 权限级别 |
这是 declarative allowed_paths 特性的 imperative 伴侣——一个是配置级声明,一个是运行时动态策略。
Issue:#9388