KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
02. 一个动作需要哪些不会含糊的字段? — keel 龙骨
如果整个执行链只传递这段数据,出了问题几乎无法恢复:
如果整个执行链只传递这段数据,出了问题几乎无法恢复:
{"tool": "set_pool_limit", "value": 40}
它没有目标环境、业务意图、稳定标识、资源版本、deadline,也无法判断第二次收到它时是重试还是新请求。
先分清五个对象
| 对象 | 谁创建 | 回答的问题 |
|---|---|---|
ActionProposal |
模型 | “根据当前资料,我建议做什么?” |
ActionCommand |
Harness | “系统准备以哪个可信身份,对哪个资源做什么?” |
ActionAttempt |
执行网关 | “这是该命令的第几次实际尝试?” |
ExecutionReceipt |
Adapter/外部系统 | “外部系统确认了什么?” |
ActionEvent |
Harness/网关 | “状态在何时因什么原因发生变化?” |
Proposal 可以被拒绝或修改;Command 一旦进入审批和幂等记录,就必须保持不可变;Attempt 可以有多个;Receipt 是每次尝试的事实;Event 用来恢复因果链。
ActionCommand 的可信字段
项目中的命令包含以下信息:
class ActionCommand(BaseModel):
command_id: str
run_id: str
intent_id: str
kind: ActionKind
target: ResourceRef
arguments: dict[str, JsonValue]
idempotency_key: str
deadline_at: datetime
这些字段的来源不相同:
kind和arguments可以从模型提议中提取,但必须经过白名单和 schema 校验;run_id、租户和用户身份来自认证后的 Harness 上下文;effect由动作目录决定,模型不能把写操作自报成 read;command_id和idempotency_key由程序生成;deadline_at由运行预算决定,不能让模型无限等待。
标识符各自解决什么
run_id 关联整次 Agent 运行
intent_id 关联用户的一次稳定业务意图
command_id 标识一个不可变命令
attempt_id 标识一次网络/执行尝试
idempotency_key 让外部系统识别重复业务提交
external_reference 让本地记录重新查询外部事实
不要用一个 request_id 代替全部含义。一次业务意图可能经过多个命令,每个命令又可能多次尝试。
Fingerprint 固定动作含义
对以下字段做规范化后再计算摘要:
action kind + target + normalized arguments
同一幂等键再次出现时:
- fingerprint 相同:可以返回首次结果或继续查询同一 Operation;
- fingerprint 不同:必须报冲突,说明同一个业务标识被用于不同意图。
时间戳、attempt ID 这类每次变化的字段不能进入 fingerprint,否则每次重试都会变成“新动作”。
状态转换由程序控制
stateDiagram-v2
[*] --> proposed
proposed --> ready: 校验并固定命令
ready --> running: 创建 attempt
running --> succeeded: 明确成功回执
running --> failed: 明确失败回执
running --> pending: 外部系统异步受理
running --> unknown: 无法确认结果
pending --> succeeded: 轮询/回调确认
pending --> failed: 轮询/回调确认
unknown --> succeeded: 对账确认
unknown --> failed: 对账确认未生效
unknown --> manual_review: 无法自动判断
模型可以建议下一步,但不能直接把 unknown 改成 succeeded。
运行契约示例
python courses/advanced/real-world-execution/course/project/examples/01_action_contract.py
观察 Proposal 如何被 Harness 转成带有可信租户、稳定 ID、deadline 和 fingerprint 的 Command。
检查理解
- 为什么
effect不能相信模型输出? - Command 和 Attempt 为什么是一对多?
- 同一幂等键配不同参数时,为什么不能返回旧结果?