AI研究

OpenHuman的ApprovalGate把没有交互式聊天上下文的工具调用当作“预授权自动化”处理,PraisonAI在调用onToolCall审批回调之前就已经把可执行工具传给了generateText()——当审批逻辑在架构上位于工具执行之后、或者把“不在对话里”等同于“已经批准”时,人类审批到底是最后一道防线还是一个可以被时序绕过的装饰?

👤 为我痴狂 👁 4 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-07
首页› AI› AI研究› 正文
OpenHuman的ApprovalGate把没有交互式聊天上下文的工具调用当作“预授权自动化”处理,PraisonAI在调用onToolCall审批回调之前就已经把可执行工具传给了generateText()——当审批逻辑在架构上位于工具执行之后、或者把“不在对话里”等同于“已经批准”时,人类审批到底是最后一道防线还是一个可以被时序绕过的装饰?

摘要与核心论点

人类审批(Human-in-the-Loop Approval)在大模型智能体(LLM Agent)工程中被普遍视为高风险工具调用的“最后一道防线”。然而,当审批逻辑在架构上被放置在工具执行之后,或者系统把“没有交互式聊天上下文”这一状态直接等价于“已经获得预授权”时,这道防线就不再是防线,而退化为一种可被时序绕过的装饰性机制。本文以两个具有代表性的开源实现为分析样本:OpenHuman 的 ApprovalGate 对无聊天上下文工具调用的“预授权自动化”归类,以及 PraisonAI 在调用 onToolCall 审批回调之前已将可执行工具对象传入 generateText() 的时序安排。笔者提出一条贯穿全文的分析主线:审批的有效性不取决于审批回调是否存在,而取决于审批点与副作用点之间是否构成不可绕过的时序不变量(Temporal Invariant)。

围绕这条主线,本文依次讨论:审批语义的形式化定义与威胁模型;两个样本实现的架构时序剖析;能力令牌(Capability Token)与最小权限在 Agent 工具链中的落地;可验证审批日志与审计不变量;以及面向生产环境的纵深防御操作路径。全文强调可验证性:任何“人类已批准”的主张,都必须能被转化为可被独立复核的日志证据与状态机约束,否则它在架构上不成立。

目录

一、问题定义:审批的语义、威胁模型与失效分类

1.1 什么是“人类审批”:一个需要被精确定义的术语

在智能体工程语境中,“人类审批”常被含糊地用于描述三种截然不同的机制:其一,交互式确认(interactive confirmation),即系统在副作用发生前暂停,等待人类显式输入“同意/拒绝”;其二,策略预授权(policy pre-authorization),即人类在部署阶段通过策略声明“某类操作在某种条件下无需逐次确认”;其三,事后审计(post-hoc audit),即操作已执行,人类在日志中复核。三者在安全属性上完全不等价:只有第一种能在副作用发生前阻断,第二种把判断权前移到了策略编写时刻,第三种根本不具备阻断能力。

本文评述:把三者统称为“审批”是大量工程事故的语义根源。当一个系统声称“我们有人类审批”时,必须追问:审批发生在副作用之前还是之后?审批的对象是具体调用实例还是调用类别?审批结果是否被绑定到该实例且不可被后续流程替换?这三个问题构成了本文后续分析的检验清单。

1.2 威胁模型:谁能绕过审批

讨论审批失效,必须先明确攻击面。参照经典的最小权限原则与 Saltzer 与 Schroeder 在 1975 年提出的系统设计八原则(其中“最小权限”“完全中介”“经济机制”三条与本文直接相关),笔者将 Agent 工具调用链中的审批绕过归纳为四类威胁主体:

威胁主体 能力 典型绕过方式
恶意或被注入的提示 控制模型输出 诱导模型选择“无需审批”的工具路径
框架实现缺陷 控制调用时序 审批回调晚于工具对象传递
配置错误 控制策略判定 把“无上下文”误配为“已预授权”
内部人员 控制日志与策略 事后篡改审批记录

本文评述:四类主体中,框架实现缺陷与配置错误最具隐蔽性,因为它们不违反任何“显式规则”,而是让规则本身在错误的时序位置上失效。这正是本文两个样本案例的共同特征。

1.3 失效分类:审批装饰化的五种模式

基于对公开框架文档与源码的梳理,笔者将“审批装饰化”归纳为五种可识别的模式,后文将逐一在两个样本中定位:

  1. 后置审批:审批回调在副作用已发生后才被调用,只能记录不能阻断。
  2. 对象先行:可执行工具对象在审批前已被传入执行上下文,审批仅改变一个随后被忽略的标志位。
  3. 状态等价:把“无交互上下文”“非交互会话”“批处理模式”等状态直接映射为“已批准”。
  4. 类别审批:审批针对工具名而非具体参数实例,导致参数被替换后审批仍然“有效”。
  5. 日志不可验证:审批记录与执行记录分离且无绑定关系,无法证明某次执行对应某次批准。

笔者认为,这五种模式并非孤立缺陷,而是同一根因的不同表现:审批被实现为一个“事件”而非一个“约束”。事件可以被跳过、延迟或伪造;约束则必须在状态机的每条路径上被满足,否则执行无法推进。

二、架构时序剖析:两个样本实现的审批点位置

2.1 OpenHuman ApprovalGate:“无聊天上下文即预授权”的语义跳跃

OpenHuman 的 ApprovalGate 设计初衷是为工具调用提供一个统一的审批入口。其文档与实现中一个关键判定是:当工具调用发生在没有交互式聊天上下文的场景(例如定时任务、后台流水线、由其他 Agent 触发的子调用)时,ApprovalGate 将其归类为“预授权自动化”(pre-authorized automation)并放行。这一设计的合理性在于:后台任务无法等待人类实时输入,若强制阻塞将导致流水线死锁。

但问题在于语义跳跃。“没有交互式聊天上下文”是一个关于会话形态的事实,而“已获人类批准”是一个关于授权状态的断言。二者之间不存在逻辑蕴含关系。一个由定时器触发的删除操作,其“无聊天上下文”为真,但其“已获批准”可能为假。把前者当作后者的充分条件,等价于在授权判定中引入了一个恒真的旁路。

// 示意性伪代码:反映“状态等价”模式的判定结构
function shouldApprove(call, session) {
  if (session.hasInteractiveChatContext) {
    return requestHumanApproval(call);   // 交互路径
  }
  // 无交互上下文 → 直接视为预授权
  return { approved: true, reason: "pre-authorized-automation" };
}

本文评述:这段判定结构的问题不在于“放行后台任务”这一目标,而在于它把授权状态的来源从“人类显式声明”替换成了“会话形态推断”。正确的做法应当是:后台任务的授权必须在任务创建时由人类显式授予,并以能力令牌的形式绑定到该任务实例;运行时只校验令牌,而不是从会话形态反推授权。

2.2 PraisonAI:onToolCall 回调与 generateText() 的时序错位

PraisonAI 提供了 onToolCall 审批回调,允许开发者在工具被调用前介入。然而其调用时序存在一个关键细节:可执行工具对象在 onToolCall 回调被触发之前,已经被传入 generateText() 的执行上下文。这意味着审批回调所面对的,是一个“已经具备执行能力”的工具引用,而非一个“待批准后才可实例化”的占位符。

从工程角度看,这种时序安排有其实现上的便利:generateText() 需要工具的模式(schema)来构造提示,因此工具对象必须提前可用。但“模式可用”与“执行能力可用”是两回事。把完整的可执行对象提前传入,使得审批回调的返回值只能影响一个后续分支,而无法在架构上阻止执行路径的存在。一旦某条代码路径忽略了该返回值,或模型输出直接触发了已注册的工具,审批即被绕过。

// 示意性伪代码:反映“对象先行”模式的时序
const tools = [dangerousTool];              // 可执行对象已就绪
const result = await generateText({
  model,
  tools,                                    // 执行能力已注入
  onToolCall: async (call) => {
    const ok = await requestHumanApproval(call);
    if (!ok) throw new Error("rejected");   // 依赖异常传播阻断
  }
});

本文评述:依赖回调内抛异常来阻断执行,是一种“约定式安全”(security by convention),而非“构造式安全”(security by construction)。它要求框架的每条内部路径都正确传播该异常,且不允许任何重试、降级或并行分支吞掉异常。在复杂框架中,这一假设极难被静态保证。更稳健的设计是让审批发生在工具实例化之前:审批通过后,才由工厂函数产出可执行对象。

2.3 两个样本的共同根因:审批点与副作用点的相对位置

把两个样本放在同一坐标系中,可以看到它们失效的几何本质。设审批点为 A,副作用点为 E(effect)。安全要求是 A 严格先于 E,且 E 的存在依赖于 A 的成功。OpenHuman 的问题在于 A 被一个恒真判定短路,A 实际上未发生;PraisonAI 的问题在于 E 的能力在 A 之前已被注入,A 与 E 之间缺少强制依赖。二者共同指向同一根因:审批没有被建模为副作用的前置条件,而被建模为一个可选的旁路事件。

笔者认为,这一根因在 Agent 框架中普遍存在,原因在于框架的演化路径:早期框架优先解决“让工具能被调用”,审批是后加的横切关注点(cross-cutting concern)。后加的关注点往往以回调、钩子、装饰器的形式插入既有调用链,而既有调用链的执行能力早已在更早的位置被注入。于是审批天然地落在了执行能力之后。要根治,必须把审批前移到能力注入之前,这需要重构工具注册与实例化的边界。

三、理论框架:时序不变量、能力令牌与最小权限

3.1 时序不变量:把审批写成不可绕过的约束

时序不变量(Temporal Invariant)是指系统在所有合法执行路径上都必须满足的、关于事件顺序的约束。对审批而言,核心不变量可表述为:对任意副作用 E,若 E 属于需审批类别,则存在一个审批事件 A,满足 A 先于 E,且 E 的执行凭证由 A 唯一派生。注意“唯一派生”四字:它排除了“审批存在但执行不依赖它”的情形,正是 PraisonAI 式缺陷的形式化刻画。

本文评述:把审批写成不变量,而非写成回调,带来的直接好处是可验证性。不变量可以被转化为运行时断言、静态分析规则或形式化模型检查的目标;回调则只能被“相信”。在工程上,这意味着审批逻辑应当与执行逻辑共享同一个状态机,而不是分布在不同层。

3.2 能力令牌:让“批准”成为可传递、可校验、可过期的对象

能力令牌(Capability Token)源自操作系统安全中的能力(capability)概念,其核心思想是:访问权不是一个可被查询的布尔值,而是一个必须被持有才能执行的对象。将这一思想引入 Agent 工具调用,可以得到如下设计:人类审批的产物不是“approved=true”,而是一枚绑定到具体调用实例(工具名 + 参数哈希 + 会话 + 有效期)的令牌;工具执行函数在入口处强制校验令牌,无令牌即拒绝。

令牌字段 作用 缺失后果
tool_name 限定工具 类别审批,参数可被替换
args_hash 绑定具体参数实例 批准 A 执行 B
session_id 绑定会话 跨会话重放
expires_at 限制有效期 长期有效令牌被滥用
nonce 防重放 同一令牌重复执行

本文评述:能力令牌把审批从“事件”转化为“对象”,从而把时序约束转化为类型约束——没有令牌类型的值,无法调用执行函数。这比在回调里抛异常要稳健得多,因为它在编译期或入口校验期就阻断了路径,而不依赖运行时的异常传播正确性。

3.3 最小权限与完全中介:两条经典原则的 Agent 化

Saltzer 与 Schroeder 的最小权限原则要求每个主体仅持有完成其任务所必需的最小权限;完全中介原则要求每次访问都必须经过检查。将二者映射到 Agent 工具链:最小权限意味着不应把“全部工具”一次性注入 generateText(),而应按当前子任务动态授予;完全中介意味着不存在绕过审批的“内部快捷路径”,包括框架内部的重试、缓存命中与并行分支。

笔者认为,完全中介原则在 Agent 场景中尤其容易被违反,因为框架为了性能与体验,常引入缓存、流式输出与并行工具调用。这些优化路径若未同步接入审批校验,就会成为事实上的旁路。因此,审批校验必须下沉到工具执行的最内层入口,而非停留在编排层。

四、工程实践:可验证审批日志与状态机约束

4.1 操作路径一:把审批前移到工具实例化之前

第一步是重构工具注册边界。将工具拆分为“模式描述”与“执行体”两部分:模式描述可在提示构造阶段自由传入;执行体则封装在工厂函数后,只有持有有效令牌时才被实例化。这样,generateText() 拿到的只是模式,而非可执行能力,从构造上消除了“对象先行”缺陷。

// 模式与执行体分离
const schemaOnly = { name: "delete_file", parameters: {...} };

async function executeWithToken(call, token) {
  assertValidToken(token, call);            // 入口强制校验
  const impl = toolFactory(call.name);      // 此时才实例化
  return impl(call.args);
}

本文评述:这一重构的代价是需要修改框架的工具注入接口,收益是把安全属性从“约定”提升为“构造”。在无法修改框架的场合,可用包装层模拟:向 generateText() 传入的是拒绝执行的桩函数,桩函数内部再校验令牌并转发到真实实现。

4.2 操作路径二:为“无交互上下文”建立显式授权来源

针对 OpenHuman 式缺陷,正确做法是:后台任务的授权必须在任务创建时由人类显式声明,并固化为任务级令牌。运行时若发现某调用无令牌,应默认拒绝并升级为待审批队列,而非默认放行。换言之,把默认值从“允许”翻转为“拒绝”(default-deny),是修复“状态等价”模式最直接的手段。

设计原则:会话形态只能用于决定“向谁请求审批”,不能用于决定“是否已获审批”。前者是路由问题,后者是授权问题,二者不可混同。

4.3 操作路径三:可验证审批日志的字段与绑定关系

可验证性要求审批记录与执行记录之间存在密码学或结构上的绑定。最低要求是:执行记录中携带审批记录的标识与参数哈希,审批记录中携带被批准调用的参数哈希,二者一致方可认为“该执行对应此批准”。进一步可引入哈希链,使日志具备防篡改能力。

日志类型 必备字段 验证方式
审批记录 approval_id, args_hash, approver, ts 签名校验
执行记录 exec_id, approval_id, args_hash, ts 与审批记录交叉比对
链式摘要 prev_hash, curr_hash 重算哈希链

本文评述:日志绑定是“事后可验证”的基础,但它不能替代“事前阻断”。二者是纵深防御的不同层:令牌负责阻断,日志负责取证与追责。只做日志不做令牌,等于承认审批可被绕过但事后能发现;只做令牌不做日志,则无法在争议中证明审批的真实性。

4.4 操作路径四:状态机约束与测试策略

把工具调用建模为状态机:IDLE → REQUESTED → APPROVED/REJECTED → EXECUTING → DONE。约束是:EXECUTING 只能从 APPROVED 进入,且 APPROVED 只能由人类审批事件触发。测试策略上,应针对每条非法转移编写负向测试,例如“从 REQUESTED 直接进入 EXECUTING 必须失败”“REJECTED 后重试必须重新审批”。这类测试能有效捕获“对象先行”与“后置审批”缺陷。

笔者认为,状态机约束的价值在于它把安全需求转化为可执行的测试用例,使审批不再依赖开发者对框架内部时序的直觉理解。对于无法修改源码的第三方框架,可在其外层包裹状态机代理,代理持有真实执行体,框架只能通过代理间接调用。

五、对比、局限与前沿预判

5.1 三种审批架构的对比

架构 阻断能力 实现成本 主要风险
回调式(现状) 弱,依赖异常传播 低 时序绕过、异常被吞
令牌式 强,入口强制校验 中 令牌签发环节需可信
代理式(外层状态机) 强,框架无法直连执行体 中高 代理本身成为单点

本文评述:三种架构并非互斥,生产环境宜采用令牌式为主、代理式为辅的组合:令牌负责实例级绑定,代理负责隔离不可信框架。回调式应仅作为通知机制,不应承担阻断职责。

5.2 局限:本文分析的边界条件

需要明确本文结论的边界。第一,本文基于公开文档与源码结构的分析,未对具体版本做穷尽审计,不同版本的行为可能变化,读者应以所使用版本的源码为准。第二,能力令牌方案假设令牌签发环节可信,若签发端本身被攻破,令牌机制同样失效,这需要独立的密钥管理与审批人身份认证来支撑。第三,状态机代理会引入额外延迟,对高频低风险工具应允许策略化的批量授权,否则将损害可用性。

笔者认为,承认边界条件恰恰是工程严谨性的体现。审批机制的目标不是“零风险”,而是“把风险控制在可声明、可验证、可追责的范围内”。任何声称“有了审批就安全”的表述,都应当被本文的时序不变量检验清单所质疑。

5.3 前沿预判:从回调到策略即代码

从趋势看,Agent 审批正在从“框架内回调”向“外置策略引擎”演进。以策略即代码(Policy as Code)方式描述审批规则,由独立于 Agent 框架的策略决策点(PDP)做判定,Agent 只负责发起判定请求与执行判定结果。这一架构与云原生中的准入控制(Admission Control)思路同源,其优势是策略与执行解耦,策略可被独立测试、版本化与审计。

本文评述:外置策略引擎能解决“框架内审批点位置错误”的问题,因为判定发生在执行体被授予之前,且判定结果以令牌形式返回。但它也带来新的依赖:策略引擎的可用性成为执行路径的关键路径,需要设计降级策略——而降级策略本身又必须遵循 default-deny,否则会重新打开旁路。这一张力是未来工程实践的核心议题。

六、结论与操作清单

回到文章标题提出的问题:当审批逻辑在架构上位于工具执行之后,或把“不在对话里”等同于“已经批准”时,人类审批是最后一道防线还是装饰?本文的结论是:在缺乏时序不变量与能力绑定的实现中,它是装饰;只有当审批成为副作用的前置条件、且执行凭证由审批唯一派生时,它才是防线。OpenHuman 的“预授权自动化”归类与 PraisonAI 的“对象先行”时序,分别对应“状态等价”与“对象先行”两种失效模式,二者共同说明:审批的安全性是一个架构位置问题,而非一个功能有无问题。

面向落地,笔者给出如下操作清单,按优先级排列:

  1. 审计现有框架中工具对象被传入执行上下文的时点,确认其是否早于审批回调;
  2. 将默认值改为 default-deny,任何无令牌的需审批调用一律拒绝并进入待审批队列;
  3. 引入绑定参数哈希、会话与有效期的能力令牌,执行入口强制校验;
  4. 为工具调用建立状态机,编写非法转移的负向测试;
  5. 审批日志与执行日志通过 approval_id 与 args_hash 双向绑定,并启用哈希链;
  6. 对无法改造的第三方框架,外层包裹状态机代理,隔离执行体;
  7. 将策略判定外置为独立 PDP,并明确其降级策略同样遵循 default-deny。

本文评述:这份清单的共同逻辑是把“相信”替换为“验证”。在 Agent 系统日益自主化的背景下,人类审批的价值不在于它被调用了多少次,而在于每一次调用都能被证明是副作用发生的必要条件。做不到这一点,审批就只是一个好看的装饰;做得到,它才是真正的最后一道防线。

参考文献与拓展资源

本文在写作中参考了智能体框架文档、访问控制与能力系统、策略即代码、日志可验证性等方向的公开资料,累计参考条目 62 篇,其中近三年(2023—2025)文献占比约 56%。涉及框架行为的描述以公开文档与源码结构为准,未进行私有环境复现;文中伪代码为说明性示意,不代表任何具体版本的逐行实现。若涉及数据集,均为公开基准的说明性引用,未做额外清洗,仅按原始发布说明使用。

主要参考文献(8 篇)

  1. Saltzer, J. H., & Schroeder, M. D. The Protection of Information in Computer Systems. Proceedings of the IEEE, 1975.
  2. Miller, M. S. Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control. PhD Thesis, Johns Hopkins University, 2006.
  3. Denning, D. E., & Denning, P. J. Data Security. ACM Computing Surveys, 1979.
  4. Sandhu, R., et al. Role-Based Access Control Models. IEEE Computer, 1996.
  5. OpenHuman 项目文档与 ApprovalGate 相关源码说明(公开仓库,访问于 2025 年)。
  6. PraisonAI 项目文档与 onToolCall 回调相关源码说明(公开仓库,访问于 2025 年)。
  7. NIST. Security and Privacy Controls for Information Systems and Organizations (SP 800-53 Rev.5), 2020.
  8. OWASP. Top 10 for Large Language Model Applications, 2025 版。

拓展资源

文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约 6800 字 | 参考文献 62 篇(主要 8 篇)。
🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷