关键词:Agent 登录引导、可信行动主体、工具失败语义、
subject.required、AI 订单助手
用户在一个还没有登录的 Agent 入口里说:
帮我查一下订单 SO-1001 的物流。
Agent 回答:
请先登录后再试。
这句话看起来没有错,但用户接下来可能发现:
- 页面上没有可以执行的登录入口;
- 登录完成后,原任务没有继续;
- Agent 不知道登录状态已经变化,继续重复“请先登录”;
- 真实原因其实是订单无权访问,登录多少次都没用;
- 模型为了“帮用户完成任务”,自行编造了一个登录链接或
user_id。
“请先登录”只是一句自然语言。它没有说明系统缺少的到底是什么,也没有提供确定、可验证的恢复路径。
当 Agent 开始调用订单、售后、库存或员工等私有能力时,系统需要把这件事拆成四层:
能力声明:这项能力是否要求可信行动主体?
发现阶段:前置条件不满足时,工具是否仍会交给模型?
执行阶段:主体在调用前失效时,系统如何关闭式失败?
产品恢复:用户怎样完成真实动作并回到原任务?
安全边界不能只给出一句正确的话,还要给出机器可区分的失败原因和真实可执行的下一步。
1. 前序问题已经解决了“主体是谁”,这篇只讨论“缺少主体后怎么办”
可信行动主体不是模型生成的 user_id,也不是未经验证的上游消息。运行时必须从自己的可信上下文或明确的身份、委托绑定中解析或验证主体。
这是身份信任边界问题。
本文继续向下拆一层:
运行时原本拥有可信主体
-> 工具进入当前候选集合
-> 模型开始规划或准备调用
-> Session 过期、用户退出或委托被撤销
-> 真正调用时主体已经不存在
发现阶段正确隐藏工具,并不能消除发现与执行之间的状态漂移。系统仍需回答:这次调用应该返回什么状态?Agent 能做什么?用户又怎样恢复?
这不是再次定义“什么是可信主体”,而是把主体前置条件落实为可执行、可测试的运行时行为。
2. ACC 只声明前置条件,不规定登录产品
在 ACC v1 的 OpenAPI Binding 中,一项私有订单能力可以声明:
paths:
/orders/{orderId}:
get:
operationId: getPrivateOrder
x-agent-capability:
version: 1
enabled: true
scope: order.private.read
risk:
level: low
subject:
required: true
execution:
readonly: true
idempotent: true
其中:
subject:
required: true
表达的是:这项能力在暴露或调用前需要可信行动主体。当前运行时没有可信主体时,不应把它展示给 Agent;真正调用时,业务系统仍必须按照自己的权限模型验证主体。
ACC 不规定:
- 登录页面长什么样;
- 使用 Session、JWT、证书还是身份目录;
- 按钮、二维码、设备授权或企业工作台如何交互;
- 登录完成后是否自动恢复任务;
- 产品采用什么错误码和响应结构;
- 当前主体是否有权读取某张具体订单。
因此,subject.required 提供的是一项跨实现的能力前置语义,不是一套登录协议或账户系统。
本文后续使用的错误码、状态对象和补救动作都是实现中立的工程示例,不是 ACC v1 新增字段,也不表示所有兼容实现必须使用同一种 JSON 结构。
3. 发现阶段:缺少主体时,工具应先被排除
在匿名会话中,运行时可能拥有这些能力定义:
product.catalog.search
store.public.read
order.private.read
refund.request.create
address.private.update
当前工具集合可以抽象为:
可见能力
= enabled=true
∩ scope 有效并被当前路由允许
∩ subject.required 的前置条件已经满足
∩ 其他部署侧暴露条件通过
没有可信主体时,模型应该只看到当前真正具备前提的能力:
可见:product.catalog.search
可见:store.public.read
排除:order.private.read
排除:refund.request.create
排除:address.private.update
这样做并不是为了替代业务 API 鉴权,而是为了避免模型围绕当前无法合法执行的能力规划、生成参数或作出承诺。
管理员诊断界面仍然可以记录某项能力因主体前置条件未满足而被排除,但这不等于继续把它的名称、描述和参数 Schema 发送给模型。
4. 为什么发现阶段隐藏之后,执行阶段仍然会失败
工具发现和工具调用通常不是同一个原子动作。
10:00:00 运行时生成工具集合,可信主体有效
10:01:20 模型完成规划,准备查询订单
10:01:25 用户在另一个页面退出登录
10:01:27 Agent 发起工具调用
类似漂移还可能来自:
- Session 到期;
- 委托票据被撤销;
- 工作负载身份轮换失败;
- 当前路由或 scope allowlist 更新;
- 上游主体声明在当前信任边界验证失败;
- 会话迁移到无法访问原可信上下文的执行节点。
所以运行时不能因为工具曾经可见,就把旧结论当成永久执行许可。调用前仍需重新检查主体和其他治理条件。
如果此时主体缺失,正确结果应当是:
在业务操作发生前关闭式失败
而不是:
让模型补一个 user_id 继续调用
也不是:
继续请求业务 API,等通用 401 再猜原因
5. 三种失败不能都叫“请先登录”
一次能力不可用,至少可能属于三种完全不同的状态。下面的名称只是实现示例,用于说明分类,不是 ACC 规定的枚举值。
| 概念状态 | 发生在哪里 | 意义 | 合理下一步 |
|---|---|---|---|
tool_not_exposed | 发现或路由层 | 该能力没有进入当前 Agent 可达范围 | 不调用;按当前可见能力重新规划 |
subject_missing | 调用前治理层 | 能力要求可信主体,但当前主体不存在、失效或未被当前边界验证 | 进入受控认证或身份恢复流程 |
final_authority_denied | 业务授权层 | 可信主体存在,但无权操作当前业务对象或当前状态不允许 | 不重复登录;按业务规则处理或请求适当授权 |
三者如果都被压缩成:
请先登录
会产生不同的错误恢复。
5.1 tool_not_exposed
能力可能被关闭、scope 不在当前路由 allowlist 中,或者声明无效。模型不应该通过改参数、重新登录或直接拼接 URL 绕过这层边界。
5.2 subject_missing
能力原本允许当前场景触达,但调用前缺少可信主体。这时可以提供真实认证动作;认证完成后重新计算工具集合并重新验证任务。
5.3 final_authority_denied
主体有效,但例如:
- 订单不属于当前用户;
- 员工没有售后权限;
- 退款状态不允许继续;
- 数据范围不包含目标门店。
这不是“没登录”。继续要求用户登录会形成死循环,还可能掩盖越权尝试。业务系统应返回适当的拒绝,产品层再根据可披露的信息提供帮助。
6. 结构化失败,是为了让恢复逻辑不依赖模型猜测
当调用前发现主体缺失时,一个实现可以返回类似下面的内部结果:
{
"status": "blocked",
"code": "subject_required",
"retryable": false,
"side_effect_started": false,
"remediation": {
"kind": "authenticate",
"action_ref": "auth/start"
}
}
再次强调,这只是说明语义的示例,不是 ACC v1 规定的响应 Schema。
这里每个信息都有确定作用:
code让编排层知道缺少的是可信主体,而不是普通网络错误;retryable: false阻止 Agent 在相同状态下反复调用;side_effect_started: false表明失败发生在业务操作前;remediation.kind告诉产品层需要认证,而不是让模型自由生成解决方案;action_ref指向部署方控制的动作,由可信应用解析,不是模型随意编造的外部链接。
实现也可以采用异常类型、状态机事件、RPC 状态或其他等价结构。关键不在字段名称,而在于:
失败原因可机器区分
恢复动作由可信系统提供
模型不能把治理失败改写成业务参数
7. 为什么不能让模型自己生成登录入口
如果系统只把下面这句话交给模型:
用户未登录,请引导登录
模型可能:
- 编造一个不存在的
/login地址; - 给出不属于当前客户端的网页登录步骤;
- 把外部内容中的钓鱼链接误当成登录入口;
- 要求用户把 Token、验证码或密码直接发到对话中;
- 让用户提供
user_id,然后把它当成可信身份; - 登录成功后仍不知道如何恢复原任务。
更可靠的分工是:
运行时识别 subject_missing
-> 产品后端返回受控 remediation
-> 当前客户端把 action_ref 渲染成真实按钮、二维码或设备授权流程
-> 可信身份系统完成认证
-> 运行时重新建立主体
-> 重新计算工具集合
模型可以解释“为什么现在不能查询订单”,但不能创造身份入口、接收秘密或宣布认证已经成功。
8. 可执行补救必须回答四个问题
一个真正有用的恢复路径至少需要回答:
8.1 用户现在能做什么
例如显示由当前产品提供的“登录并继续”按钮,而不是只输出抽象建议。
8.2 谁确认动作已经完成
认证成功必须由可信身份系统或应用后端确认,不能靠用户在对话中说“我已经登录了”。
8.3 原任务怎样恢复
产品需要保存可以安全恢复的任务上下文,例如:
用户原始目标
待重新解析的业务对象引用
当前任务 ID
阻塞原因
这不表示应保存密码、Token 或让模型生成可信主体。
8.4 恢复前要重新检查什么
认证完成后不能直接重放旧调用。运行时至少应重新检查:
- 当前主体是否已经由可信系统建立;
- 工具现在是否进入当前可见范围;
- scope 与路由策略是否仍允许;
- 业务参数是否仍与用户意图一致;
- 审批、风险或有效期条件是否发生变化;
- 业务系统是否最终授权当前操作。
对于写操作,恢复后静默执行尤其危险。主体或参数变化时,旧确认和旧审批不能自动沿用。
9. “登录并继续”不是“登录后盲目重试”
认证恢复可以让任务继续,但继续的含义应当是:
重新进入治理与授权链路
而不是:
原始 HTTP 请求自动重放
更稳妥的恢复顺序是:
任务因 subject_missing 暂停
-> 用户执行受控认证动作
-> 可信系统确认主体
-> 重新生成或过滤工具集合
-> 重新验证能力、参数、风险与审批状态
-> 模型或确定性编排器重新确认下一步
-> 业务系统执行最终授权
-> 才允许实际调用
这样可以处理登录期间发生的变化:订单可能被取消,退款金额可能改变,旧审批可能过期,用户也可能换成另一个账号。
10. 不同观察面应该看到不同的信息
失败语义结构化,不代表把所有内部原因原样发给每个人。
| 观察面 | 需要知道什么 |
|---|---|
| 模型 | 当前能力不可执行、允许使用的受控下一步;不接触凭证和内部策略细节 |
| 最终用户 | 简洁原因、真实可操作入口、任务是否保留 |
| 运行时 | 精确状态、是否可重试、是否产生副作用、恢复前置条件 |
| 管理员与审计 | 能力、主体状态类别、路由与策略版本、排除或拒绝原因、恢复结果 |
例如,业务系统因订单不属于当前主体而拒绝时,面向用户的提示未必应泄露目标订单是否存在;但审计侧仍应保留足以调查的拒绝类别和请求关联。
结构化的目的不是暴露更多敏感信息,而是让每一层得到完成自身职责所需的最少信息。
11. 一个实现中立的状态流
不同系统可以使用 Web、App、CLI、MCP 客户端、工作台或消息入口。ACC 不要求它们使用同一种 UI 或协议。
但整体行为可以抽象为:
解析并校验能力声明
-> 按 enabled、scope、路由与主体前置条件生成工具集合
-> 模型选择当前可见工具
-> 调用前重新验证可信主体
主体缺失:
-> 在业务副作用前 fail closed
-> 返回结构化 subject_missing
-> 产品提供受控认证动作
-> 认证成功后重新进入治理链路
主体存在:
-> 调用业务系统
-> 业务系统执行最终授权
业务拒绝:
-> 返回 final_authority_denied 或等价业务拒绝
-> 不错误引导重复登录
这条链路把声明层、运行时和产品层分开:
- ACC 声明能力前置条件;
- 运行时负责暴露与调用前门禁;
- 身份系统负责建立可信主体;
- 产品负责提供真实可执行的恢复入口;
- 业务系统负责最终授权。
12. 七个常见误区
误区一:工具已经隐藏,所以执行期不用再检查
发现与执行之间存在并发和状态漂移。调用前必须重新验证。
误区二:返回 401 就已经有完整失败语义
401 不能说明工具是否曾暴露、业务副作用是否开始、能否重试,也不能给出受控恢复动作。
误区三:提示词写“不得伪造登录”就足够
身份入口、状态判断和恢复动作必须由确定性系统控制,不能依赖模型遵守文本规则。
误区四:拿到 user_id 就可以继续
模型参数和用户输入不构成可信行动主体。主体必须由可信上下文解析或验证。
误区五:所有拒绝都让用户重新登录
最终业务授权失败与主体缺失不同。错误恢复会造成死循环,也会模糊越权边界。
误区六:登录成功后自动重放最省事
工具范围、主体、参数、审批和业务状态都可能变化。恢复必须重新进入治理链路。
误区七:定义一个统一错误码就完成了产品体验
错误码只让机器理解原因。用户仍需要当前客户端真实存在、可验证、可返回原任务的操作入口。
13. 一张最小测试矩阵
| 场景 | 发现阶段 | 调用前状态 | 期望结果 |
|---|---|---|---|
| 匿名用户请求私有订单工具 | 主体不存在 | 未调用 | 工具不进入模型候选集 |
| 模型尝试调用从未暴露的工具 | 工具不在当前集合 | 不适用 | tool_not_exposed 或等价关闭式拒绝 |
| 生成工具列表后 Session 过期 | 当时主体有效 | 当前主体缺失 | 业务调用前返回 subject_missing,无副作用 |
模型参数中携带 user_id | 主体未被可信系统建立 | 当前主体缺失 | 不把参数升级为主体,关闭式拒绝 |
| 用户通过受控入口完成登录 | 重新建立可信主体 | 当前主体有效 | 重算工具集合并重新验证任务,不盲目重放 |
| 已登录用户读取他人订单 | 主体有效、工具可见 | 业务无权 | final_authority_denied 或等价业务拒绝,不提示重复登录 |
| 登录后更换账号 | 主体发生变化 | 新主体有效 | 旧审批与旧主体绑定不静默复用 |
还应验证:
- 主体缺失失败是否发生在业务副作用之前?
- 相同状态下 Agent 是否会被阻止无限重试?
- 登录入口是否来自可信产品配置,而不是模型文本?
- 用户在对话中声称“已登录”时,系统是否仍要求可信确认?
- 登录完成后工具目录是否会刷新?
- 恢复时是否重新检查 scope、参数、审批和最终业务授权?
- 用户提示是否避免泄露内部策略与目标资源存在性?
- 管理员是否能区分未暴露、主体缺失和业务拒绝?
14. ACC 在这里负责什么,不负责什么
ACC v1 负责表达:
一项能力在暴露或调用前是否要求可信行动主体
兼容运行时应把这项要求纳入工具发现和调用前门禁。
ACC 不负责定义:
- 全行业统一的登录协议;
- Session、JWT、证书或委托票据格式;
- 通用错误响应 Schema;
- 登录按钮、二维码或设备授权 UI;
- 任务暂停与恢复产品;
- 业务系统角色、数据范围和对象权限。
tool_not_exposed、subject_missing、final_authority_denied 以及示例中的 remediation 都是用于阐明实现责任的概念模型,不是对 ACC v1 的字段扩展提案。
是否将这些失败类进一步标准化,需要独立实现需求、明确的互操作场景、失败语义和机器可读测试证据,不能因为一个产品需要登录按钮就直接加入 ACC Core。
本文也不表示任何具体运行时、网关或产品已经实现文中列出的全部工程建议。
15. 结语:一句正确提醒,不等于一条可靠恢复链路
“请先登录”可以是面向用户的最后一句话,却不能是系统内部唯一的状态。
一条可辩护的链路应该能够说明:
为什么工具当前不可见
为什么调用在副作用前被阻止
失败是否允许重试
登录动作由谁提供和验证
任务怎样安全恢复
最终业务授权为什么通过或拒绝
ACC 的 subject.required 提供了这条链路的前置语义,但它不会替实现方完成登录产品、状态机和业务授权。
真正可靠的 Agent 体验,不是让模型更会说“请先登录”,而是让系统在主体缺失时确定地停下来,给出真实可执行的入口,并在身份恢复后重新走完必要的治理与授权检查。
自然语言负责解释阻塞;结构化状态负责守住边界;受控补救负责让用户真正继续。
延伸阅读
- ACC 规范与示例:https://agentcapability.org/docs/
- ACC GitHub:https://github.com/agent-capability/agent-capability-contract
- 前序拆解:《Agent 代表谁行动:可信主体为什么不能来自模型输出?》
- 前序拆解:《Agent 不是看见所有 API 才更聪明:为什么工具暴露必须显式 opt-in?》

1780

被折叠的 条评论
为什么被折叠?



