Skip to content

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