Agent 工程体系 · 第 21/98 篇。内容以 2026 年 9 月可验证的公开规范和稳定接口为基线;框架版本敏感能力会明确标注,不把实验行为写成通用保证。
Agent 人工介入:确认、澄清、升级、接管、恢复和责任边界
Agent 并不是“模型自动调用工具”的同义词。只要 Agent 能够修改数据、发送消息、执行命令、提交审批、转移资金或影响第三方,就必须回答一个更基础的问题:
在什么条件下,Agent 可以继续;在什么条件下,必须询问;在什么条件下,必须暂停并交给人;如果人没有及时响应,系统如何恢复;最终谁对决策、执行和结果负责?
人工介入不是一个按钮,而是一组不同语义的控制机制:确认、澄清、升级、接管、恢复。它们分别解决不同类型的不确定性和风险。把它们都实现成“弹窗让用户点一下”,会导致授权范围不清、旧请求重放、状态丢失、人工审批失效,以及事故后无法判断责任归属。
本文使用以下抽象:
- Agent:能够感知上下文、进行规划、调用工具并维护任务状态的软件系统。
- 工具调用:Agent 请求外部系统执行动作,例如
send_email、refund_order、run_sql。 - 副作用:改变外部世界的结果,例如写数据库、发消息、扣款或删除文件。
- 人工介入:人或代表组织政策的人工审批系统,对 Agent 的下一步行为施加控制。
- 人工介入点:流程中必须等待人工决策、补充信息、接管操作或确认恢复的位置。
OpenAI 的 Agents SDK 将 Agent 描述为能够规划、调用工具、协作并维护足够状态以完成多步工作的应用;其文档也将自动防护与人工审查区分为两种机制:前者验证输入、输出或工具行为,后者在敏感动作前暂停运行,等待批准或拒绝。(developers.openai.com)
一、先区分六种人工介入语义
1. 确认:用户已经给出意图,只差授权执行
确认是对一个已经明确的动作进行最后授权。
例如:
“已准备向供应商付款 38,500 元,收款账户尾号 7621,预计到账时间为今天 18:00。是否提交?”
这里用户的目标、对象、参数和动作都已经确定。系统需要的不是更多信息,而是确认用户是否允许执行。
确认的逻辑条件可以写成:
其中:
- 是待执行动作;
WellDefined表示动作参数完整、类型正确;Authorized表示当前用户有权限执行该动作;Fresh表示动作基于当前状态,没有过期或被新状态替代;Reviewable表示系统能向用户展示足以理解后果的信息。
如果参数还不完整,就不能进入确认,而应进入澄清。如果动作本身超出用户权限,则不能通过确认绕过权限检查。
2. 澄清:目标可能明确,但执行所需信息不足
澄清解决的是信息缺失或语义歧义,不是授权问题。
例如用户说:
“把上个月的订单都退款。”
Agent 至少需要澄清:
- “上个月”按哪个时区计算;
- “订单”是全部订单还是某个店铺的订单;
- 退款是全额还是按商品;
- 是否包含已经发货或已开票订单;
- 退款是否需要人工财务审核。
如果有多个合理解释,Agent 不应擅自选择一个。可以把候选解释表示成集合:
当:
且不同解释会产生不同副作用时,必须澄清。这里的关键不是模型“有没有信心”,而是错误解释的代价是否可接受。
反例是:
用户说“删除测试数据”,Agent 根据最近一次操作猜测要删除
test数据库。
即使模型对猜测有较高置信度,也不应直接执行,因为“删除哪个环境、哪些表、哪些记录”仍然没有明确,且动作不可逆。
3. 升级:系统无法在当前权限、证据或策略下安全决策
升级是将任务交给更高权限、更专业或更有责任资格的处理者。
常见升级原因包括:
- 证据不足,无法满足决策阈值;
- 涉及财务、医疗、法律、人事等高风险领域;
- 当前操作员没有权限;
- 政策要求双人审批或特定角色审批;
- 发生异常,自动回退策略也不适用;
- 多个系统状态冲突,Agent 无法判断哪个是真实状态。
升级和澄清的区别是:
- 澄清:向当前用户询问缺失信息;
- 升级:当前 Agent 或当前用户不具备继续决策的资格。
例如:
“订单金额超过普通客服授权上限,需要升级至财务主管审批。”
这不是“请问是否继续”,而是权限边界已经决定了当前角色不能继续。
4. 接管:人直接成为操作主体
接管不是“人批准 Agent 继续”,而是人接替 Agent 负责后续操作。
适合接管的场景:
- 需要在复杂界面中观察多个动态信号;
- 失败后没有可靠的自动恢复路径;
- Agent 已经执行了部分步骤,但下一步需要专家判断;
- 需要与第三方人工沟通;
- 自动化操作可能造成更大损害。
例如,Agent 在云平台创建实例时发现配额异常:
- Agent 已提交创建请求;
- 平台返回部分资源已分配,但网络配置失败;
- 自动补偿可能重复创建资源;
- 此时应将控制权交给运维人员,而不是再次调用创建工具。
接管意味着系统必须明确:
- 当前谁拥有控制权;
- Agent 是否仍可运行后台步骤;
- 人的操作是否会被 Agent 并发覆盖;
- 人交还控制权时,Agent 应从哪个状态继续。
5. 恢复:暂停、失败或接管后重新建立可继续状态
恢复是从中断状态重新进入可验证的执行状态。
恢复不是简单地“重新调用上一次工具”。因为上一次调用可能已经成功,只是响应丢失;也可能只完成了一部分;还可能被人工修改过。
恢复前必须先判断:
其中:
Durable:状态已经持久化;Consistent:状态与外部系统没有明显冲突;ReplaySafe:重新执行不会造成重复副作用。
如果不能证明 ReplaySafe,应先查询外部系统状态,而不是直接重试。
6. 责任边界:谁对哪一层负责
责任边界不是一句“用户确认后由用户负责”。Agent 系统通常至少包含四层责任:
| 层次 | 负责内容 | 典型主体 |
|---|---|---|
| 意图层 | 用户是否提出了这个目标 | 用户或业务发起人 |
| 决策层 | 是否选择了正确动作和参数 | Agent、业务规则、审批人 |
| 执行层 | 是否按批准参数正确调用工具 | Agent 运行时、工具服务 |
| 结果层 | 外部系统最终产生了什么结果 | 工具提供方、业务系统、运营主体 |
用户点击确认,只能证明“某个身份在某个时间对某个版本的动作表示同意”。它不能自动证明:
- 参数一定正确;
- Agent 没有篡改参数;
- 执行时外部状态仍然有效;
- 工具服务没有重复执行;
- 业务政策允许该动作。
二、人工介入点应由风险和不确定性共同决定
不能只按照工具名称决定是否需要人工介入。update_customer 可能只是修改备注,也可能修改收款账户;send_message 可能发送给自己,也可能发送给数万名客户。
可以将一个动作的介入风险表示为:
其中:
- :影响范围,例如金额、用户数量、数据敏感度;
- :错误发生的可能性;
- :不可逆程度。
例如:
| 动作 | 影响 | 可能性 | 不可逆程度 | 处理 |
|---|---|---|---|---|
| 查询订单 | 低 | 中 | 低 | 通常自动 |
| 修改草稿 | 中 | 中 | 低 | 可自动,保留撤销 |
| 发送单封邮件 | 中 | 中 | 中 | 参数预览后确认 |
| 批量退款 | 高 | 中 | 高 | 人工审批 |
| 删除生产数据 | 极高 | 中 | 极高 | 禁止 Agent 直接执行或必须接管 |
但是风险模型不能替代硬规则。以下条件应直接阻断自动执行:
例如,即使预计金额很小,只要工具是“永久删除个人数据”,也可能被组织策略禁止自动执行。
三、确认必须确认“动作快照”,而不是确认一句自然语言
1. 参数预览包含什么
一个可审计的确认对象应至少包含:
{
"action": "refund_order",
"resource": {
"order_id": "ORD-20260901-1042",
"customer": "张某",
"amount": "128.00",
"currency": "CNY"
},
"effect": "原路退回 128.00 元,提交后不可撤销",
"reason": "商品缺货",
"preconditions": {
"order_status": "PAID",
"refund_remaining": "128.00"
},
"expires_at": "2026-09-01T10:15:00+08:00"
}
预览的目的不是让用户阅读内部 JSON,而是让用户看到足以改变决策的事实:
- 对谁操作;
- 操作什么;
- 数量、金额、范围是什么;
- 会产生什么后果;
- 是否可撤销;
- 哪些条件必须仍然成立;
- 确认何时失效。
“确认退款吗?”不是充分预览,因为用户无法判断退款对象、金额和状态。
2. 确认令牌绑定动作内容
确认令牌不能只绑定一个任务 ID。否则 Agent 可以先生成一个安全动作,用户确认后再替换成危险参数。
设规范化后的动作为:
生成动作摘要:
确认令牌至少应绑定:
{
"approval_id": "APR-7f1b",
"action_hash": "sha256:...",
"actor_id": "user-123",
"approver_id": "user-123",
"scope": "refund_order",
"issued_at": "2026-09-01T10:00:00+08:00",
"expires_at": "2026-09-01T10:15:00+08:00",
"nonce": "random-128-bit-value",
"state": "PENDING"
}
执行时重新计算摘要:
def verify_approval(approval, action, now):
expected_hash = sha256(canonicalize(action))
if approval["state"] != "APPROVED":
raise PermissionError("approval is not approved")
if approval["action_hash"] != expected_hash:
raise PermissionError("action changed after approval")
if now >= parse_time(approval["expires_at"]):
raise PermissionError("approval expired")
if approval["consumed_at"] is not None:
raise PermissionError("approval already consumed")
return True
每一步都不可省略:
- 状态检查防止直接伪造“已批准”;
- 摘要检查防止参数替换;
- 过期检查防止旧状态下的授权继续有效;
- 消费检查防止同一令牌重复使用。
3. 防重放不能只依赖前端按钮禁用
前端禁用按钮只能减少误点击,不能防止:
- 请求包被复制后再次发送;
- Worker 超时后重试;
- 用户打开两个浏览器标签页;
- 消息队列重复投递;
- Agent 崩溃后恢复时再次提交。
服务端应使用原子状态变更:
UPDATE approvals
SET state = 'CONSUMED',
consumed_at = CURRENT_TIMESTAMP
WHERE approval_id = ?
AND state = 'APPROVED'
AND expires_at > CURRENT_TIMESTAMP
AND action_hash = ?;
只有返回影响行数为 1 时,才允许继续执行工具调用。返回 0 表示令牌已过期、已消费、摘要不匹配或不存在。
但这还不够。数据库更新成功后,进程可能在调用外部工具前崩溃,导致审批被消费但动作未执行;也可能工具执行成功后进程崩溃,导致系统误以为未执行。
因此生产实现需要额外的执行记录和幂等键:
idempotency_key = approval_id + ":" + action_hash
工具服务必须保证同一个幂等键最多产生一个业务副作用,或者至少返回同一个执行结果。
四、确认、执行和外部状态之间存在竞态
确认时看到的状态不一定等于执行时的状态。
例如:
- 10:00,订单状态为
PAID; - Agent 生成退款预览;
- 用户在 10:05 点击确认;
- 10:04,另一个系统已经完成退款;
- Agent 在 10:05 再次调用退款接口。
如果只校验确认令牌,仍然可能造成重复退款。确认令牌必须绑定业务前置条件,执行时重新校验:
预览阶段:
order.status = PAID
order.refund_remaining = 128.00
order.version = 42
执行阶段:
只有 order.version = 42 且 refund_remaining >= 128.00 才允许退款
对应的条件是:
如果版本从 42 变成 43,系统应返回:
{
"status": "STALE_APPROVAL",
"message": "订单状态已变化,请重新获取当前状态并确认"
}
这里不能自动沿用旧确认。重新计算金额或对象后,动作已经不是原来被批准的动作。
五、Agent 不确定时,不要用“置信度”替代证据
模型输出的概率或自评置信度不能直接等同于业务可信度。工程上更有用的是把不确定性拆成三类:
1. 事实不确定
系统不知道事实是什么。
例:
“客户是否已经签收?”
如果物流系统返回超时,Agent 不能根据用户语气推断“应该已经签收”。正确处理是查询、等待、升级或拒答。
2. 意图不确定
用户表达可以对应多个动作。
例:
“把这个账户关掉。”
可能是关闭登录、注销服务、解绑银行卡或删除数据。此时应澄清,而不是根据上下文猜测。
3. 后果不确定
动作本身明确,但影响无法可靠估计。
例:
“批量调整所有客户的信用额度。”
即使对象和参数清楚,如果系统不能计算影响范围、上限和合规条件,就应升级。
可以定义一个最小证据条件:
其中:
- 是动作所需证据的完整度;
- 是由风险等级决定的阈值。
低风险查询可能只需一个数据源;高风险退款可能需要订单状态、支付状态、退款余额和权限记录同时满足。
拒答、回退与升级的区别
- 拒答:系统明确不能做,例如政策禁止、权限不足、缺少必要证据;
- 回退:改用风险更低的替代动作,例如只生成草稿、不直接发送;
- 升级:让有资格的人工角色继续处理。
例如:
“我无法确认该账号是否属于当前租户,因此不能删除。可以先生成待审核的删除清单,或升级给租户管理员核验。”
这句话同时完成了拒答和回退,但没有假装任务已经完成。
六、一个完整的人工介入状态机
将人工介入建模为状态机,比在代码中到处写 if need_human 更可靠。
stateDiagram-v2
[*] --> PLANNING
PLANNING --> NEED_CLARIFICATION: 信息不足或存在歧义
PLANNING --> NEED_ESCALATION: 超权限/高风险/证据不足
PLANNING --> READY: 动作明确且无需人工授权
PLANNING --> PENDING_APPROVAL: 需要确认
NEED_CLARIFICATION --> PLANNING: 收到补充信息
NEED_CLARIFICATION --> CANCELLED: 用户放弃或超时
PENDING_APPROVAL --> APPROVED: 获得有效批准
PENDING_APPROVAL --> REJECTED: 用户拒绝
PENDING_APPROVAL --> EXPIRED: 超过有效期
PENDING_APPROVAL --> CANCELLED: 任务取消
APPROVED --> EXECUTING: 原子消费令牌
EXECUTING --> SUCCEEDED: 工具确认成功
EXECUTING --> UNKNOWN_RESULT: 请求超时/连接中断
EXECUTING --> FAILED: 明确失败
NEED_ESCALATION --> HUMAN_TAKEOVER: 人工接管
HUMAN_TAKEOVER --> HUMAN_RESOLVED: 人工完成或给出决策
HUMAN_TAKEOVER --> RECOVERY_REQUIRED: 人工操作中断
UNKNOWN_RESULT --> RECONCILING: 查询外部执行状态
RECONCILING --> SUCCEEDED: 确认已执行
RECONCILING --> RECOVERY_REQUIRED: 确认未执行
RECONCILING --> HUMAN_TAKEOVER: 状态无法确定
FAILED --> RECOVERY_REQUIRED: 存在安全补偿路径
FAILED --> HUMAN_TAKEOVER: 无可靠补偿路径
RECOVERY_REQUIRED --> READY: 重新规划为安全动作
RECOVERY_REQUIRED --> PENDING_APPROVAL: 新动作需要重新确认
RECOVERY_REQUIRED --> HUMAN_TAKEOVER: 需要人工判断
REJECTED --> [*]
CANCELLED --> [*]
EXPIRED --> [*]
SUCCEEDED --> [*]
HUMAN_RESOLVED --> [*]
关键约束如下:
APPROVED不是EXECUTING,批准和执行必须分开;UNKNOWN_RESULT不能直接进入重试;RECOVERY_REQUIRED不能复用旧动作摘要;- 人工接管后,Agent 必须停止可能冲突的自动执行;
- 任何状态转换都应写入持久化事件,而不是只存在进程内存。
七、人工接管时,必须解决控制权并发
接管最容易出现“人和 Agent 同时操作”。
例如:
- Agent 正在编辑客户资料;
- 客服打开页面准备手动修改;
- Agent 后台根据旧上下文再次保存;
- 客服的修改被覆盖。
因此任务需要一个控制权租约:
{
"task_id": "TASK-1001",
"controller": "human:user-88",
"lease_id": "LEASE-a91c",
"lease_expires_at": "2026-09-01T10:30:00+08:00",
"agent_paused": true
}
工具写入时检查:
写操作允许,当且仅当:
lease_id 有效
当前控制者仍为调用方
数据版本未发生冲突
如果人工接管,Agent 的后台循环应收到取消信号:
async def agent_loop(task):
while True:
if await task.is_human_controlled():
await task.wait_for_human_event()
continue
decision = await plan_next_step(task)
if decision.requires_approval:
await task.pause_for_approval(decision)
continue
await execute_with_idempotency(decision)
wait_for_human_event() 不是忙等。任务应被持久化并释放计算资源,由事件、队列或 webhook 唤醒。
人交还控制权时,不能简单地把 agent_paused 改为 false。Agent 应重新读取:
- 人工实际修改了什么;
- 外部资源当前版本;
- 哪些原计划步骤已经完成;
- 原计划是否仍然满足策略;
- 是否需要重新确认。
八、恢复的核心是对账,而不是重试
1. 明确失败
工具返回业务失败,例如:
{
"status": 409,
"code": "INSUFFICIENT_BALANCE",
"message": "可退款余额不足"
}
这类结果通常可以直接进入重新规划:
原动作:退款 128 元
当前事实:可退款余额为 0 元
安全回退:生成退款失败说明,升级财务处理
2. 明确成功
工具返回业务成功,并有外部唯一 ID:
{
"status": "SUCCEEDED",
"refund_id": "RF-8821"
}
系统可以将任务标记为成功,并把外部 ID 写入审计记录。
3. 未知结果
请求超时不等于执行失败。
客户端 -> 退款服务:请求
退款服务:已扣款并提交退款
网络:响应丢失
客户端:超时
此时系统状态是:
不能把它改成 FAILED 后自动重试。正确流程是:
- 使用原幂等键查询执行记录;
- 查询业务对象当前状态;
- 检查是否存在外部交易 ID;
- 如果确认成功,补写本地状态;
- 如果确认未执行,才允许重新提交;
- 如果外部状态仍无法确定,升级人工。
async def reconcile(operation):
result = await provider.query_by_idempotency_key(
operation.idempotency_key
)
if result.status == "SUCCEEDED":
await mark_succeeded(operation, result.external_id)
elif result.status == "NOT_FOUND":
await mark_retryable(operation)
else:
await escalate(operation, reason="external state unknown")
“重试三次”只能解决明确的瞬时失败,不能解决未知结果。对有副作用的操作,重试策略必须由幂等性和对账能力共同决定。
九、人工审批不是把模型的责任转嫁给用户
1. 人工看到什么,决定了审批是否有效
如果审批界面只显示:
Agent 请求调用
refund_order
那么人工并不知道:
- 退款哪个订单;
- 金额是多少;
- 原因是什么;
- 数据是否已经变化;
- 是否会触发其他流程。
这类审批形式上有人点击,实质上没有完成有效复核。
有效审批应绑定:
批准人身份
+ 批准时间
+ 动作摘要
+ 参数快照
+ 前置条件
+ 策略版本
+ 过期时间
+ 执行结果
2. 审批人的责任是判断,不是验证所有底层事实
审批人通常负责:
- 判断动作是否符合业务目的;
- 判断风险是否在授权范围内;
- 判断展示出的对象和参数是否正确;
- 在需要时要求补充证据。
审批人不应被默认为负责:
- 验证 Agent 内部每一步推理;
- 证明底层数据库没有 bug;
- 证明第三方接口一定不会重复执行;
- 替系统承担权限控制和幂等控制。
如果系统把未经验证的模型生成内容直接显示为事实,审批流程本身就有缺陷。
3. 工具服务仍然必须独立校验
即使 Agent 已获得批准,工具服务也应再次检查:
租户边界
用户权限
参数约束
资源当前状态
金额上限
幂等键
数据版本
审计要求
人工审批是业务授权的一部分,不应成为绕过服务端安全校验的特殊通道。
十、一个最小的端到端实现模型
下面给出与具体 Agent SDK 无关的 Python 示例,展示“规划—预览—确认—消费—执行”的核心生命周期。它使用内存字典,仅用于理解;生产环境应替换为事务数据库和具备幂等能力的工具服务。
from dataclasses import dataclass
from hashlib import sha256
from datetime import datetime, timedelta, timezone
import json
import secrets
UTC = timezone.utc
def canonicalize(action: dict) -> str:
return json.dumps(action, sort_keys=True, separators=(",", ":"))
def action_hash(action: dict) -> str:
return sha256(canonicalize(action).encode()).hexdigest()
@dataclass
class Approval:
approval_id: str
action_hash: str
actor_id: str
expires_at: datetime
state: str = "PENDING"
consumed_at: datetime | None = None
approvals = {}
def create_approval(actor_id: str, action: dict) -> Approval:
approval = Approval(
approval_id="APR-" + secrets.token_hex(8),
action_hash=action_hash(action),
actor_id=actor_id,
expires_at=datetime.now(UTC) + timedelta(minutes=15),
)
approvals[approval.approval_id] = approval
return approval
def approve(actor_id: str, approval_id: str):
approval = approvals[approval_id]
if approval.actor_id != actor_id:
raise PermissionError("wrong approver")
if datetime.now(UTC) >= approval.expires_at:
approval.state = "EXPIRED"
raise PermissionError("approval expired")
if approval.state != "PENDING":
raise PermissionError("approval is not pending")
approval.state = "APPROVED"
def consume_approval(approval_id: str, action: dict):
approval = approvals[approval_id]
if approval.state != "APPROVED":
raise PermissionError("approval is not approved")
if datetime.now(UTC) >= approval.expires_at:
approval.state = "EXPIRED"
raise PermissionError("approval expired")
if approval.action_hash != action_hash(action):
raise PermissionError("action changed")
if approval.consumed_at is not None:
raise PermissionError("replay detected")
# 生产环境必须是数据库中的原子 UPDATE,而不是普通赋值
approval.consumed_at = datetime.now(UTC)
approval.state = "CONSUMED"
def execute_refund(action: dict, idempotency_key: str):
# 生产环境由支付服务保证同一 key 不产生重复退款
print({
"tool": "refund_order",
"order_id": action["order_id"],
"amount": action["amount"],
"idempotency_key": idempotency_key,
"status": "SUCCEEDED",
})
action = {
"tool": "refund_order",
"tenant_id": "tenant-a",
"order_id": "ORD-100",
"amount": "128.00",
"currency": "CNY",
"precondition": {
"order_status": "PAID",
"order_version": 42,
},
}
approval = create_approval("user-123", action)
# 用户界面应展示 action 的可读摘要,而不是只展示 approval_id
print("请确认:", action)
approve("user-123", approval.approval_id)
consume_approval(approval.approval_id, action)
idempotency_key = approval.approval_id + ":" + action_hash(action)
execute_refund(action, idempotency_key)
这个示例有几个重要边界:
approval_id不是动作本身,必须同时校验action_hash;approve()只表示授权,不执行退款;consume_approval()只负责一次性消费授权,不应承担业务执行;execute_refund()仍需由下游服务实现幂等;- 示例中的普通字典操作没有并发安全,不能直接用于生产;
- 实际系统还必须在执行前重新查询
order_version和退款余额。
如果用户在审批期间修改了退款金额,应创建新的动作摘要和新的审批记录,而不是更新原记录。审批记录一旦产生,通常应视为不可变事件。
十一、常见错误及其失败表现
错误一:所有不确定性都问用户
表现是 Agent 不断询问:
“要不要继续?”
“这样可以吗?”
“是否确认?”
问题在于用户被迫替系统做类型校验、权限检查和策略判断。低风险、可验证的决策应由系统自动处理;只有涉及用户意图、业务偏好或责任授权的问题才应询问用户。
错误二:只在 UI 层做确认
表现是用户点击“确认”后,服务端直接执行请求参数,而不是执行审批时展示的参数。
后果是攻击者或错误代码可以替换请求体。确认必须在服务端与动作摘要绑定。
错误三:把超时当成失败
表现是支付、退款、发信等操作在超时后自动重试,最终出现重复副作用。
修复方式是引入 UNKNOWN_RESULT 状态和对账流程。
错误四:人工接管后 Agent 仍在运行
表现是人工刚修正数据,Agent 又根据旧上下文覆盖修改;或者人工已经取消订单,Agent 仍继续发货。
接管必须是控制权转移,同时暂停冲突的自动步骤。
错误五:审批通过后不重新检查前置条件
表现是用户批准时订单有效,执行时订单已经退款、过期或转移到另一个租户。
批准确认的是某个动作在某组前置条件下的授权,不是永久授权。执行时必须重新校验状态。
错误六:把“有人工点击”当成责任闭环
表现是系统没有记录批准人、动作版本、工具响应和最终外部结果,事故后只能看到“某人点过确认”。
责任闭环需要完整事件链:
用户请求
→ Agent 计划
→ 证据与策略检查
→ 参数预览
→ 批准/拒绝
→ 动作摘要
→ 令牌消费
→ 工具请求
→ 外部结果
→ 对账或补偿
→ 最终通知
十二、生产诊断应围绕“状态事实”而不是聊天记录
当人工介入流程出错时,优先查询以下字段:
task_id
approval_id
action_hash
policy_version
actor_id
approver_id
controller
task_state
tool_call_id
idempotency_key
external_operation_id
resource_version
created_at
approved_at
consumed_at
completed_at
诊断顺序应是:
- 确认任务状态:系统认为任务处于什么状态;
- 确认动作版本:审批时的摘要是否等于执行时摘要;
- 确认令牌生命周期:是否过期、重复消费或被撤销;
- 确认控制权:Agent 还是人工拥有任务租约;
- 确认外部结果:工具是否已经产生副作用;
- 确认补偿路径:是否可以安全撤销、对账或升级;
- 确认通知结果:用户看到的是计划、批准、执行中还是最终成功。
日志中不要只记录自然语言提示。自然语言适合用户交互,不适合作为一致性判断依据。状态、摘要、版本号和外部 ID 才是恢复和审计所需要的事实。
十三、把人工介入设计成协议,而不是提示语
一个成熟的人工介入协议至少应规定:
确认协议
Agent 生成动作快照
→ 服务端规范化并计算摘要
→ 展示参数、影响和有效期
→ 合格身份批准
→ 执行前重新校验摘要和前置条件
→ 原子消费批准
→ 使用幂等键调用工具
澄清协议
识别缺失字段或多个合理解释
→ 只询问决定性问题
→ 保存原问题和用户回答
→ 重新规划
→ 如果动作变化,重新生成预览
升级协议
记录升级原因
→ 选择具备相应权限或专业能力的角色
→ 附带证据、候选动作和已完成步骤
→ 等待人工决策或接管
→ 记录人工结论
接管协议
暂停冲突的 Agent 步骤
→ 创建人工控制租约
→ 展示当前状态和未完成动作
→ 人工操作
→ 记录人工修改
→ 重新对账
→ 决定恢复 Agent、继续人工或结束任务
恢复协议
读取持久化状态
→ 查询外部系统
→ 区分成功、失败和未知结果
→ 只有在可证明幂等时才重试
→ 生成新的动作或新的审批
→ 继续、补偿或升级
这些协议的实现方式可以不同:工作流引擎、消息队列、数据库状态机、Agent SDK 的暂停与恢复机制都可以承载它们。OpenAI 的相关文档把“人审”定位为在敏感工具调用前暂停运行,并提供结果与状态、可恢复状态等运行时概念;具体应用仍需自行定义业务审批、权限、幂等和责任规则。(developers.openai.com)
Anthropic 关于 Agent 构建的资料也强调,应根据任务选择简单工作流或更具自主性的 Agent,并通过工具、环境和流程约束控制复杂度;这意味着人工介入点应放在确实需要判断、授权或承担责任的位置,而不是用审批掩盖一个不可控的自动流程。(anthropic.com)
最终,人工介入的目标不是让人替 Agent 完成所有工作,而是让系统在关键边界上做到四件事:
- Agent 不清楚时,能明确停下来;
- 用户要承担授权时,能看到准确动作;
- 自动化失去控制时,能安全交给人;
- 任务中断或结果不明时,能依据事实恢复。
只有当确认绑定动作、澄清改变规划、升级改变责任层级、接管转移控制权、恢复重新建立状态一致性时,Human-in-the-loop 才不是流程装饰,而是 Agent 工程体系中的真实安全边界。
系列导航与关联阅读
- 系列入口:Agent 工程完整路线:从运行循环、记忆与协议到安全、评测和生产交付
- 上一篇:Agent 终止与预算:最大步数、Deadline、Token、费用和循环检测
- 下一篇:Agent 短期记忆:消息历史、工具轨迹、Token 窗口和裁剪
- 延伸:Agent 写操作确认:参数预览、确认令牌、过期和防重放
- 延伸:Agent 不确定性与拒答:证据不足、置信边界、升级和回退
官方资料
本文依据 Agent、模型、协议与框架官方资料重新梳理;正文、示例与生产清单由 WR BLOG 编写。

评论
0 条讨论