Agent 能力契约为什么不能包办一切?审批人、租户隔离和事务编排为什么不该进入 ACC 核心

简介: 本文探讨Agent能力契约(ACC)的核心设计哲学:为何不将审批人、租户隔离、业务规则等全纳入标准?答案在于坚守“最小可移植治理语义”——ACC只定义Runtime能一致理解、执行与验证的字段(如`approval.required`),而把组织权限、策略引擎、事务编排等交还给权威业务系统,确保开放性、安全性和可落地性。

一份面向 Agent 的能力契约,应该描述到什么程度?

当开发者第一次看到 ACC(Agent Capability Contract,Agent 能力契约)里的 enabledscoperisksubjectapprovalexecution,很容易继续追问:

  • 既然已经有 approval,为什么不直接声明谁能审批、要几级会签?
  • 既然已经有 subject,为什么不把 tenant_id、角色和数据权限一起标准化?
  • 既然已经有条件审批,为什么不支持累计金额、跨工具前置条件和事务回滚?
  • 既然要让 Agent 安全办事,为什么不把整套企业策略都写进同一份契约?

这些问题都很合理。

但答案不是“ACC 还没来得及加字段”,而是:其中很多能力有意没有进入 ACC Core。

一个开放契约的目标,不是用一份越来越大的 YAML 接管企业已有的身份系统、审批平台、租户模型和工作流引擎。它应该只标准化那些能够被不同运行时一致理解、确定执行并通过 Conformance 验证的最小治理语义。

这篇文章只讨论一个核心问题:

为什么审批人、租户隔离、业务规则和事务编排明明都很重要,却不应该被塞进 ACC 核心?

我们也会结合 BailingHub 的实现说明:标准没有定义某项能力,不等于产品不能实现;产品实现了某项能力,也不等于它自动成为开放标准的一部分。

一、先看一份“什么都想管”的巨型注解

假设我们准备给退款接口增加 Agent 能力声明,并试图一次性把所有规则写进去:

# 下面是刻意设计的反例,并不是 ACC 字段
x-agent-capability:
  enabled: true
  scope: refund.execute

  approval:
    approver_role: finance_manager
    levels: 2
    delegates_allowed: true
    timeout: 24h
    escalation_to: finance_director

  tenant:
    source: jwt.claims.tenant_id
    isolation: row_level
    table_field: merchant_id

  policy:
    expression: amount <= order.refundable_amount
    data_source: mysql.orders

  transaction:
    steps:
      - freeze_balance
      - create_refund
      - update_order
      - notify_customer
    compensate:
      create_refund: cancel_refund
      freeze_balance: unfreeze_balance

它看起来很完整,甚至很“企业级”。

但换一个组织,问题马上出现:

  • 审批人可能不来自 role,而来自部门、金额区间、项目归属、临时授权或 OA 流程;
  • 有的系统使用 JWT,有的使用 PHP Session、企微成员 ID、证书、服务账号或签名票据;
  • 有的租户按数据库隔离,有的按 Schema、行级字段、组织树、商户关系或资源归属隔离;
  • refundable_amount 是实时业务状态,不是能力契约中的常量;
  • 退款的补偿不一定等于调用一个反向接口,还可能涉及资金渠道、对账、人工介入和不可逆外部通知;
  • 无 UI 的网关、队列消费者和命令行 Agent,根本没有同一种审批交互界面。

于是,这份看似统一的声明实际上只是把某个产品、某个组织和某套数据库的假设写成了公共字段。

其他实现即使能解析 YAML,也无法以相同语义兑现它。

这不是可移植标准,而是把私有业务系统藏进注解里。

二、一个字段进入 ACC Core,必须先通过六道门

ACC 的设计依据给出了一组核心字段准入测试。一个新字段至少需要同时满足:

  1. 它会改变可移植的 Agent 治理语义;
  2. 相互独立的 Parser 或 Runtime 能实现相同含义;
  3. 不依赖某个产品的私有数据库、UI、身份目录或工作流,就能测试行为;
  4. 目标 Binding 的原生 Schema 和标准机制尚不能清晰表达它;
  5. 它不要求 Agent Runtime 成为最终业务授权方;
  6. 旧运行时忽略它时不会静默降低安全性,否则必须进入新的 Major 兼容边界。

即使全部通过,也只代表这个字段有资格进入治理评审,还要继续回答:

  • 是否在多个独立实现中出现了同一种需求;
  • 默认值、优先级和失败语义是否完整;
  • 是否具备机器可读测试向量;
  • 是否存在更合适的 Binding、扩展或相邻协议;
  • 加入核心后,生态实现成本是否值得。

这组测试背后有一个朴素原则:

字段不是越多越安全。无法被不同实现一致执行的字段,只会制造“已经治理”的错觉。

三、为什么 ACC 声明“需要审批”,却不声明“谁来审批”

ACC 可以声明一项调用需要形成审批意图:

approval:
  required: true

也可以针对本次调用参数声明条件审批:

approval:
  when:
    - param: amount
      op: ">"
      value: 1000
      label: 退款金额超过 1000 元

这两种语义都具有可移植性。

任何合规 Runtime 都可以观察到一次具体调用的 amount,按严格 JSON 类型比较,并确定:

本次调用是否应该先产生审批意图

但下面的问题没有统一答案:

谁有资格批准?
在哪里批准?
需要几个人?
是否允许委托?
审批多久过期?
主管休假时如何升级?
法务和财务是串行还是并行?
批准记录由谁签名并长期保存?

这些答案依赖组织结构、权限目录、业务类型、审批系统、合规要求和实时状态。

如果 ACC 增加一个看似方便的字段:

approval:
  approver: finance_manager

它会立即引入一串无法由 Core 回答的问题:

  • finance_manager 是角色名、用户组、岗位编码还是外部目录 Claim?
  • 谁证明当前审批人属于这个角色?
  • 角色刚刚被撤销怎么办?
  • 跨公司、跨租户时,这个角色属于哪个组织?
  • 代理审批、会签和条件升级怎样表达?
  • 一个无 UI Runtime 应该把审批送到哪里?

因此,ACC 只声明审批意图,不定义审批流程所有权。

BailingHub 怎样承接这条边界

BailingHub 会在高风险或条件命中的调用上冻结具体调用快照,形成 ApprovalIntent,其中包含:

job_id
request_id
subject
tool
scope
risk
args
args_hash

审批可以被投递到业务侧 Webhook、IM/OA 卡片,或由中枢控制台在轻量场景中兜底。业务侧返回 ApprovalDecision 时,中枢会复核任务身份和 args_hash,只放行与批准快照完全一致的那次调用。

但 BailingHub 仍不维护企业的组织关系、审批人权限和多级审批规则。生产环境中,谁能审、在哪里审、是否会签、审批证据如何归档,仍由业务审批所有者决定。

这说明:

ACC:声明需要审批
BailingHub:冻结调用、投递意图、验证决策、精确放行
业务审批系统:决定谁能批准以及流程如何完成

三层缺一不可,但不能互相冒充。

四、为什么 subject.required 不等于标准化租户隔离

ACC 可以声明:这项能力必须存在可信行动主体。

subject:
  required: true

它的可观察结果很明确:当 Runtime 没有可信主体时,该工具不应该暴露给 Agent,也不应该在调用层被放行。

但 ACC 不规定主体必须长什么样。

真实系统可能使用:

  • tenant_1:user_1001 这样的结构化主体;
  • JWT 中经过验证的租户和用户 Claim;
  • 后台 Session 对应的员工 ID;
  • 企微、飞书或钉钉成员标识;
  • mTLS 证书绑定的服务主体;
  • 业务系统签发的短期票据;
  • 服务账号加单独的委托证明。

这些机制的发行者、有效期、信任域和撤销方式完全不同,不能因为都包含一个字符串,就被视为同一种身份保证。

租户隔离为什么必须留在业务系统

假设工具入参中有:

{
   
  "tenant_id": "tenant_2",
  "order_id": "SO-1001"
}

如果 tenant_id 来自模型输出,用户只要要求模型换一个值,就可能尝试访问其他租户。

即使 tenant_id 由网关注入,也仍然不能代替业务系统自己的对象级检查。因为攻击者或内部服务可能绕开 Agent Runtime,直接访问原 API。

真正的租户隔离应该满足:

无论请求来自人类后台、Agent Runtime、内部任务还是直接 API 调用
-> 业务系统都从可信身份上下文恢复租户
-> 查询和写入都限定在授权数据边界内
-> 目标对象不属于该租户时 fail closed

ACC 的 subject.required 可以阻止匿名 Agent 获得这项工具,scope 可以限制某条 Agent 路由最多触达哪些能力,但它们都不是最终租户权限。

BailingHub 的实现方式

BailingHub 把可信业务主体作为不透明值传给业务工具,并将其钉入请求签名。接入方可以使用 tenant_1:user_1001 之类的结构化主体,也可以映射成自己的身份形式。

中枢不解释这个主体究竟对应哪张用户表、哪个商户层级或哪种行级策略。业务 API 验证调用来源后,仍要:

  1. 解析可信主体;
  2. 恢复真实租户上下文;
  3. 查询原业务权限;
  4. 核对目标订单、员工或资源归属;
  5. 在不满足条件时拒绝。

这不是 BailingHub “少做了一层”,而是避免把每个 SaaS 完全不同的租户模型硬编码进通用中枢。

五、为什么单次条件审批不能变成通用策略引擎

ACC approval.when 有意只处理有限、类型安全的单次调用条件。

例如:

approval:
  when:
    - param: amount
      op: ">"
      value: 1000

Runtime 可以确定性地判断当前 JSON 参数是否命中。

但它不表达累计、滚动窗口、跨调用或序列约束。

例如,下面三次调用都没有单次超过 1000 元:

第 1 次退款:400 元
第 2 次退款:400 元
第 3 次退款:400 元

如果业务规则是“同一订单当日累计退款超过 1000 元必须财务复核”,只看每次调用的 amount 就会漏掉累计 1200 元的事实。

要正确计算它,系统必须知道:

  • 哪些历史调用已经成功;
  • 哪些仍在审批或结果不确定;
  • 统计窗口使用哪个时区;
  • 退款撤销后是否释放额度;
  • 多实例并发时如何原子更新;
  • 当前业务数据是否仍然新鲜。

这些已经不是一个参数条件,而是一套有状态策略系统。

同理,下面的规则也不适合直接塞进 ACC Core:

退款金额 <= 当前订单可退余额
员工只能导出自己管理部门的数据
过去 24 小时累计发券不超过 5000 张
先完成库存预占,才能创建订单
合同金额变更后必须重新执行法务检查

它们依赖权威业务数据、读取权限、时效、并发和失败处理。ACC 如果只提供一个通用 expression 字符串,却不定义数据来源和一致性,就会制造半成品策略语言。

一个真正的通用策略引擎需要:

  • 类型系统;
  • 表达式语法;
  • 权威数据源;
  • 数据读取授权;
  • 时效和缓存规则;
  • 确定性失败语义;
  • 执行沙箱;
  • 冲突与优先级规则;
  • 可解释和可审计的决策结果。

这已经是 OPA、Cedar 或企业策略服务所在的责任层,而不是一个能力声明字段应该假装解决的问题。

六、为什么工具顺序、事务和补偿不属于能力声明

考虑一条“创建订单”的业务流程:

校验客户状态
-> 锁定库存
-> 创建订单
-> 扣减余额
-> 发送通知

如果第三步成功、第四步超时,系统应该怎样处理?

  • 回滚订单还是等待余额结果确认?
  • 库存锁定是否可逆?
  • 通知已经发送还能撤回吗?
  • 重试会不会重复扣款?
  • 补偿动作本身需要什么权限和审批?
  • 外部支付渠道返回不确定结果时,是否应该自动重试?

这不是五个工具描述的简单相加,而是一个 Saga、事务或工作流问题。

ACC 声明的是单项业务能力面向 Agent 的治理边界。它可以告诉 Runtime:

  • 某个工具是否启用;
  • 属于哪个 scope
  • 自动发起的最坏后果等级;
  • 是否需要可信主体;
  • 本次参数是否命中审批;
  • 是否只读、幂等、需要超时或限流。

但“工具 A 成功后才能调用工具 B”“工具 B 失败后必须补偿工具 A”属于跨操作的状态机。

把这类顺序塞进能力声明,会让一个描述“能力是什么”的契约变成描述“业务流程怎么跑”的工作流语言。不同组织的重试、补偿、人工介入和一致性要求不可能靠几个通用字段自动统一。

BailingHub、n8n 和业务工作流怎样分工

BailingHub 可以承载持久任务、审批暂停、结果续查、工具调用幂等和 Trace,也可以由 Agent 在一个任务中选择多个工具。

n8n、LangGraph、业务流程引擎或自研服务则可以负责确定性的步骤编排。

但无论由谁编排,真正的业务事务语义仍需要业务所有者明确设计:

哪些步骤可以重试
哪些结果属于不确定
哪些动作需要补偿
补偿是否还要授权
什么时候必须转人工
最终状态由哪个系统记录

产品可以提供这些能力,ACC Core 不需要因此变成另一个工作流引擎。

七、“不进入 Core”不等于“无法表达”

业务私有信息仍然可以放在正确的位置。

1. 优先复用 Binding 原生机制

业务参数继续使用 OpenAPI Schema,响应结构使用标准 responses,认证方案使用协议已有机制。ACC 不复制一套请求和响应 Schema。

2. 使用带命名空间的扩展

OpenAPI Operation 可以保留业务扩展:

x-business-owner: trade-team
x-business-policy:
  approval_scene: order_over_limit
  workflow_key: refund_v3

ACC 兼容 Runtime 可以把它们放进扩展袋,交给明确支持这些字段的产品或二开模块消费。

关键边界是:

未经标准化的扩展不能静默改变 scope、风险、审批、限流、审计或签名等安全行为。

否则,同一份声明在支持和不支持该扩展的 Runtime 中可能得到相反安全结果。

3. 把权威规则放回业务 API

例如:

退款金额不得超过当前可退余额

应由退款预检或执行 API 使用最新订单状态判断。即使调用绕过 Agent Runtime,这条规则也必须成立。

4. 把组织流程交给审批和工作流所有者

接口只声明“这项动作需要审批”,部署配置决定审批意图发往哪里;业务审批系统决定谁能审以及流程怎样完成。

这样既保留开放契约的可移植性,也不会阻止企业使用复杂流程。

八、ACC、BailingHub 和业务系统的完整责任图

可以把整条链路画成:

OpenAPI / MCP / 其他 Binding
  负责接口事实与原生 Schema
          |
          v
ACC 声明
  enabled / scope / risk / subject / approval / audit / execution / guidance
  负责可移植的 Agent 触达与治理意图
          |
          v
BailingHub 等 Agent Runtime
  工具装配、路由白名单、可信主体闸、审批意图、参数快照、限流、幂等、审计、Trace
          |
          +------> 企业身份与委托系统
          +------> OA / IM / 审批平台
          +------> n8n / Saga / 业务工作流
          |
          v
业务系统 Authority
  租户隔离、对象权限、实时状态、业务不变量、最终写入与结果

其中:

  • ACC 负责让不同 Runtime 对最小治理语义形成共同理解;
  • BailingHub 负责消费声明并执行具体控制面机制;
  • 企业现有身份、审批和工作流系统继续承担各自权威责任;
  • 业务系统在调用时做最终授权。

“薄标准”并不是缺少这些组件,而是拒绝对不属于自己的组件作虚假保证。

九、一条真实接入应该怎样落地

第一步:先把业务动作拆成原子能力

不要把“完成整套退款流程”暴露成一个含糊工具。优先拆成:

refund.preview
refund.request.create
refund.status.get
refund.execute

查询、预检、创建申请和真实执行具有不同后果,也应有不同风险与审批策略。

第二步:用 ACC 声明可移植治理意图

x-agent-capability:
  version: 1
  enabled: true
  scope: refund.request.create
  risk:
    level: medium
  subject:
    required: true
  approval:
    when:
      - param: amount
        op: ">"
        value: 1000
  audit:
    sensitive: true
  execution:
    readonly: false
    idempotent: false

这份声明让 Runtime 知道 Agent 是否可触达、是否需要主体、何时先形成审批意图,以及调用应怎样被审计和约束。

第三步:由业务侧建立可信主体

用户登录、租户归属和身份票据应来自可信业务代码,不经过模型生成。Runtime 只接受经过验证的主体上下文。

第四步:接入真实审批所有者

生产环境优先让审批意图进入现有 OA、IM 或业务审批页。审批回调需要绑定任务、工具和精确参数快照,不能只返回一个裸 approved=true

第五步:业务 API 继续执行最终授权

验签成功只证明请求来自可信中枢,不证明该主体有权操作目标对象。业务系统仍要校验租户、角色、资源归属、订单状态和业务不变量。

第六步:多步骤流程交给确定性编排

需要顺序、等待、重试、补偿或人工介入时,使用业务工作流、Saga、n8n 或其他显式状态机。不要把跨步骤事务寄托在模型临场规划上。

第七步:用负向测试验收边界

至少验证:

  • 无可信主体时,受保护工具不可见且调用被拒;
  • 错误 args_hash 的审批决策无法放行;
  • 审批人权限被撤销后,审批系统拒绝决策;
  • 伪造其他租户资源时,业务 API fail closed;
  • 多次小额调用命中业务累计上限时仍被拦截;
  • 工作流中途失败时按已定义状态进入重试、补偿或人工对账;
  • 绕过 Agent Runtime 直接调用业务 API 时,租户和业务规则仍然成立。

十、怎样判断一个新字段应不应该进入 Core

以后再遇到“能不能给 ACC 加一个字段”的问题,可以先用下面的清单过滤。

可移植性

  • 两个没有共享私有代码的 Runtime,能否给出相同解释?
  • 字段是否依赖某个组织的角色名、数据库或 UI?

可测试性

  • 能否用有限输入和预期输出形成机器可读测试向量?
  • 失败时应该拒绝、降级还是产生更多审批,是否足够明确?

层级归属

  • Binding 的原生字段是否已经能表达?
  • 它是否实际上属于身份、授权、审批证据、策略或工作流协议?
  • 是否要求 Runtime 读取业务私有数据并成为最终授权方?

安全兼容

  • 旧 Runtime 忽略它时会不会更宽松?
  • 如果会,是否需要新的 Major 家族或能力协商,而不是悄悄增加一个可选字段?

实现证据

  • 是否已经在多个独立实现中出现同一问题?
  • 是否先通过带命名空间的扩展完成过真实验证?

如果这些问题还没有答案,先作为实现扩展存在,通常比急着进入 Core 更安全。

十一、标准与产品必须允许不同速度演进

ACC 是实现中立的能力契约。

BailingHub 是消费 ACC 的一个开源实现。它可以提供:

  • 审批意图账本和回调;
  • 主体传递与签名;
  • 持久任务和有界等待;
  • 幂等、限流、审计和 Trace;
  • 控制台、渠道适配与运行状态。

这些产品能力可以比标准更快演进,也可以保留部署特有的选择。

但不能反过来说:因为 BailingHub 实现了一个功能,所以 ACC Core 必须增加同名字段。真正进入标准的语义,需要证明它能够跨实现成立,并具备明确的 Conformance 行为。

同样,其他 Runtime 可以用完全不同的数据库、审批系统和身份桥实现 ACC,只要它对规范字段给出一致的可观察结果。

标准与产品分离,带来的不是割裂,而是两个重要自由:

标准可以保持中立、稳定和可移植
产品可以针对真实部署快速完善运行能力

十二、写在最后:边界清楚,才是真正的完整

企业 Agent 当然需要审批人、租户隔离、业务策略和事务编排。

但“需要”不等于“都应该由一个契约定义”。

一份可靠的能力契约,应该准确声明自己能够兑现的最小语义,并明确把其他责任交还给真正拥有权威数据和组织流程的系统。

所以 ACC 的完整性不体现在字段覆盖了多少企业名词,而体现在责任链有没有断裂:

ACC 声明 Agent 触达与治理意图
-> Runtime 确定性执行暴露、主体、审批和审计闸门
-> 身份、审批与工作流系统完成自己的权威职责
-> 业务系统在最新状态下做最终授权

如果把审批人、租户模型和事务编排全部塞进 Core,契约可能更厚,却会更依赖单一产品、更难一致实现,也更容易对外作出无法兑现的安全承诺。

真正成熟的标准,不是看到所有问题都增加字段。

而是知道哪些问题必须标准化,哪些问题必须留在标准之外,并让两边通过清晰接口协作。

项目、规范与真实 API 评估入口

如果你正在评估现有商城、CRM、ERP、OA 或内部管理系统,可以只准备“一条脱敏业务 API + 当前身份与租户方式 + 审批由谁承接 + 一个允许的测试对象”,先验证第一条受治理动作,不需要公开生产凭据或整套后台。

提交公开 Issue 时,请勿附带 Token、模型密钥、个人信息或生产业务数据。

相关文章
人工智能 缓存 前端开发
11591 56
人工智能 JavaScript 开发工具
4579 17
开发工具 Swift git
1849 5
Web App开发 人工智能 API
1078 1
人工智能 Java BI
1214 1
人工智能 JavaScript 测试技术
2032 2
人工智能 JavaScript 测试技术
1030 4
缓存 JavaScript Shell
2027 3