报告实际描述的是什么
当一个 agent 在无人于当下发出指令的情况下触达某个政府端点或企业 API 时,随之而来的叙事通常是关于模型的。某个实验室、某个版本号、某个能力阈值。这种框架易于理解,也便于传播,但它回答的是一个事件响应人员实际上并不需要回答的问题。决定下一步走向的问题要更狭窄:出示的是哪个凭证,它携带了什么权限范围,以及它通过哪条委托链抵达了那个端点。
近期有几份报告描述了 agent 以操作者当时并未直接指令的方式与外部系统交互。细节各有不同,但形态是一致的:一个自主或半自主的流程构造了一个请求,而该请求被接受,是因为某个有效凭证授权了它。模型产生了意图。凭证产生了访问。这两者中只有一个能在接下来的十分钟内被撤销。
这里的归因不是关于能动性的哲学思辨。它是一个数据模型问题。谁在行动,代表谁,依据哪项授权,在运行的哪一步。如果你的系统能把这作为一个查询来回答,那么一起事件就是一个有界的运营事件。如果不能,事件就变成一项在时间压力下进行的取证工程,而你最终得出的答案将是一种推断,而非一份记录。
为什么委托链缺失
大多数技术栈无法重建这条链,原因在于它们从未被设计为承载这样一条链。三种模式解释了这一缺口的绝大部分。
第一,凭证的作用域是服务,而非任务。agent 被发放一个带有集成所需权限的 token 或密钥,而这项授权在该 agent 此后执行的每一次运行中都持续有效。授权比使其正当化的意图活得更久。一项为了让 agent 在某个排程任务中读取日历而发放的授权,六个月后当同一个 agent 在做完全不同的事情时依然有效,因为凭证中没有任何东西编码了任务。
第二,中间跳转复用父凭证,而非对其进行衰减。一个 planner 派生出一个 sub-agent。sub-agent 调用一个工具代理。代理调用第三方端点。在每一跳,同一个 bearer token 原封不动地向前传递。sub-agent 持有的权限与根主体完全相同,即便它的实际工作可能只需要其中一小部分。这很方便,而它是 agent 系统中爆炸半径的最大单一贡献因素。
第三,日志记录的是请求,而非产生该请求的保管链。访问日志显示某个请求在某个时间戳携带有效 token 抵达。它不显示是哪个 planner 步骤产生了意图,是哪个 sub-agent 执行了它,或者整个序列属于哪个任务。因此,事后重建依赖于跨系统关联时间戳和会话标识符,而这些系统从未被设计为可被连接。这种关联就是推断。推断对于事后复盘没问题,对于撤销则毫无用处。

一份最小可行的归因记录
修复之道不是升级日志。而是改变出站动作所携带的内容,以及记录被写入的位置。
每一个出站动作至少应携带四个字段:
- 一个 principal —— 该动作所依据的授权身份,可解析到一个真实账户或服务身份
- 一个 任务标识符 —— 该动作所服务的工作单元的稳定 id,不是会话,也不是进程
- 一个 父步骤标识符 —— 产生该动作的步骤,使这条链成为链式结构而非扁平列表
- 实际行使的权限范围 —— 该动作所消耗的具体权限,用执行点所校验的同一套词汇表达
这四个字段足以从任意动作重建出一张有向图,回溯到其根主体,并从任意主体向前追溯到在其之下采取的每一个动作。
使这一切得以运作的机制是每一跳的权限范围衰减。当一个 sub-agent 被派生时,它收到的授权是其调用者权限的严格子集——绝不是超集,也绝不是等集,除非任务确实需要完整权限且该需求是显式的。衰减在创建子级的边界处强制执行,而非由子级自行声明。一个收到请求的工具代理出示的是它自己衰减后的授权,而非调用者的 token。
第二个机制是记录被写入的位置。归因数据必须在执行点发出——即真正决定请求是否放行的位置——而非事后从应用日志中拼装。应用日志由可能带有缺陷、可能被绕过、或在某些路径上干脆缺失的代码写入。执行点按定义会看到每一个请求,而且它已经知道自己校验了哪个权限范围。在那里写入 principal、task id、parent step id 和实际行使的权限范围,产生的是一份在构造上完整的记录,而非在约定上完整的记录。
{ "principal": "svc:reporting-agent",
"task_id": "tsk_8f21",
"parent_step": "stp_04",
"scope": "calendar.read",
"ta
像这样的一条记录,在传统意义上不是一行日志。它是图中的一条边,并且由那个有权说不的组件写入。
## 设计后果
一旦这条链存在,事件响应就改变了性质。“agent 做了什么”这个问题变成一次遍历:从 principal 出发,向前走到任务,向前走到每一步,列出所触及的每一个端点。“它是怎么到那里的”这个问题变成从端点向根授权的一次反向行走。两者都是对你已经维护的结构进行的查询,而非你在事后开展的调查。
撤销的作用域变成任务,而非服务账户。如果单个任务行为异常,你撤销为该任务发放的授权及其下游的一切。agent 的其余工作继续。这就是遏制一起事件与禁用一项能力之间的区别。这也是在数秒内生效的撤销,与需要先审计服务账户所触及的每一个集成才敢将其关闭的撤销之间的区别。
责任论证获得了事实基底。当一个动作受到争议时,这条链显示了被授权的是什么——所授予的权限范围、行使它的步骤、站在其背后的主体。这与供应商就模型行为所作的断言是不同性质的声明,而且双方都可核查。
诚实的权衡是,衰减和逐动作记录需要工程投入并引入摩擦。每一跳都必须协商一个更窄的授权,这意味着派生 sub-agent 的组件需要在该 sub-agent 运行之前就知道它需要什么。这是一个设计约束,而且它确实比直接传递一个 token 更难。在每个执行点写入一条边记录,而非对日志采样,也存在真实的性能与存储成本。对于高流量的 agent 流量,记录量正比于动作量,而这并非没有代价。
反论是,这一成本只在设计时支付一次,而替代方案则在每一次事件中、在更糟的条件下反复支付。一个无法重建自身委托链的系统,在正常运行期间并没有归因问题。它在有人提出一个它无法回答的问题的那一刻就有了,而那恰恰是答案最重要的时刻。
归因不是螺栓式附加到 agent 技术栈上的安全功能。它是权威如何被建模和传播的一种属性。把数据模型做对,安全讨论就有了具体可依凭的东西。做错了,每一份事件报告都会变成一场关于模型的争论,因为那条本可定论的链从未被写下来。
_封面照片:panumas nikhomkhai / Pexels。_
