跳转至

长时操作

本配方展示应用如何通过 Core Tool 暴露远端长时操作,同时不混淆 Agent Run 与远端操作。

Core 负责本地 Run、工具调用、Policy 与 Checkpoint 投影。远端执行系统负责 operation ID、 accepted/running 状态、取消确认与最终效果证据。两者通过稳定引用关联,不共享同一状态机。

必须区分的状态

概念 含义
Run 取消 本地 Agent 循环停止;远端操作不一定停止
取消请求 已向执行权威发送请求;取消尚未确认
取消确认 执行权威报告操作已停止
Accepted 执行权威接受了操作
Completed 执行权威报告操作结束
Effect Verified 环境或业务证据确认预期效果

不要把这些合并成一个布尔值。Run 报错不证明远端操作失败,完成事件也不证明业务效果已提交。

模式

  1. 使用稳定的业务幂等键。新的 Run ID 或模型 tool-call ID 不一定适用。
  2. 通过 Tool 提交,并持久化返回的 operation reference。
  3. 响应丢失或重启时,先按幂等键/operation ID 查询执行权威,再决定是否重新提交。
  4. 单独发送取消请求,并记录执行权威的确认结果。
  5. 最终效果证据保留在执行系统或效果账本。
  6. 不得从 Checkpoint 或进程内去重表声明 exactly-once。

受测示例

可运行示例是独立的 L4 配方模块 examples/recipes/long-running-go。 在该目录运行:

GOWORK=off go test -mod=readonly -run LongRunning -v ./...
检查 预期结果
相同幂等键重复提交 同一 operation ID;一次效果
响应丢失后恢复 用相同键查询/提交得到现有操作;不重复
取消请求 状态为 cancel_requested,不是 canceled
取消确认 执行权威仅一次将操作转为 canceled
Run 取消 本地 Run 停止;远端仍为 accepted;不误报远端已取消
文件持久化重启 operation、幂等键、取消请求与确认在新服务实例中保留
效果验证 completed 不等于 effect_verified;验证需要证据并持久化
效果收据 证据必须携带 verifier 签发的收据;伪造收据被拒绝,状态保持 completed
HTTP 响应丢失 提交已成功但响应丢失;同幂等键重试返回现有操作
HTTP 重启 新 loopback 服务加载文件并回答状态/恢复查询
跨进程重启 独立测试进程提供 HTTP;杀掉后用同一文件重启,主进程恢复现有操作
残留临时文件 忽略残留 .tmp;最后提交状态与幂等键保持完整
Fencing token 持有旧 epoch 的 worker 不能完成;新 owner 递增 epoch 并只完成一次
持久化 fencing owner、epoch 与完成状态跨重启保留;旧 epoch 仍被拒绝
lease 过期 过期的 lease 不能完成;新 owner 获得下一个 epoch
lease 续期 过期前续期会延长 lease 并保留完成能力
lease 撤销 撤销阻止旧 owner;新 owner 可以重新获取
lease 重启 过期与撤销状态跨重启保留
并发写入者 两个独立文件写入者可能互相覆盖;边界测试记录该丢更新,不计为通过
独立 verifier 进程 执行服务调用独立 verifier 进程且不持有签名密钥;伪造收据被拒绝
Bearer/TLS 边界 缺少或错误 token 在产生效果前被拒绝;使用测试 TLS 证书
rename 前故障 模拟 rename 边界前崩溃,保留最后提交状态;临时文件被忽略
rename 后故障 模拟 rename 边界后崩溃,保留新提交状态
有效但未提交的临时文件 有效的未提交临时文件不会覆盖已提交文件
已提交文件损坏 启动失败关闭,而不是静默从空状态启动
rename 前进程崩溃 独立服务在提交边界退出;重启保留最后提交状态
rename 后进程崩溃 独立服务在提交后退出;重启保留新操作

Fixture 可持久化到 JSON 文件,并通过 loopback HTTP 暴露,从而在真实传输边界上测试响应 丢失与重启。它仍只展示契约形状,不是持久 worker、lease/fencing 实现或生产效果账本。

更大规模负载/故障复现:

PRAXO_LOAD_SUBMISSIONS=100 go test -run TestStandaloneServiceLoadFault -v ./cmd/longrunning-service
python scripts/test_longrunning_service.py

第一条命令并发提交 100 个操作,在首次确认后杀掉服务,重启并验证所有已确认操作仍存在。 第二条从干净检出运行模块测试、负载/故障测试与已发布包配方。

独立服务

示例模块也可构建独立服务:

GOWORK=off go build -mod=readonly -o longrunning-service ./cmd/longrunning-service
./longrunning-service -addr 127.0.0.1:8080 -state ./operations.json -token "$PRAXO_TOKEN"

它暴露 /healthz、/readyz、/metrics 和操作端点,支持 -tls-cert/-tls-key, 并在收到中断时优雅退出。配置可来自参数或 PRAXO_STATE、PRAXO_TOKEN、 PRAXO_VERIFIER_SECRET、PRAXO_LOCK_DSN。token 与 verifier 密钥也可通过 -token-file、-verifier-secret-file(verifier 使用 -token-file/-secret-file); 文件值在每个请求重新读取,因此替换文件即可轮换凭据而无需重启。凭据签发仍属外部身份 提供商。cmd 集成测试会构建二进制、在真实 TCP 端口启动,并检查 health、readiness、 提交、状态、并发提交、token 轮换与 metrics。另有 cmd/longrunning-verifier 使用自己的 bearer token 与签名密钥提供 /verify 及 health/readiness。它仍是本地 fixture:没有生产 身份、密钥签发、数据库或部署自动化。

Lease 后端

变更通过可配置 lease 串行化:

后端 参数 说明
文件 -lock-backend file(默认) -lock-path、-lock-ttl;过期锁会被清理
SQLite -lock-backend sqlite -lock-dsn <dsn> 创建 praxo_leases 表
Postgres -lock-backend postgres -lock-dsn <dsn> 创建 praxo_leases 表
无 -lock-backend none 仅单写入者;边界测试记录丢更新

SQL 表存储 name、owner 与 epoch 毫秒的 expires_at。变更会获取 lease、重新加载状态 文件、应用变更、提交并释放。并发写入存储测试使用 file lease;SQL lease 串行化已用 SQLite 测试;Postgres 路径使用相同语句与驱动,但需要 PRAXO_POSTGRES_DSN 才能运行其 gated 测试。当前 Windows 环境的本地 Postgres 以 0xC0000142 退出,因此这里仍未验证 Postgres。 这是示例级协调,不是生产 lease 服务。

监控

/metrics 暴露 praxo_longrunning_http_requests_total、 praxo_longrunning_http_errors_total 与 praxo_longrunning_operations_total。 monitoring/prometheus.yml、monitoring/alerts.yml 与 monitoring/prometheus.args (15 天留存)提供示例 scrape 与告警规则。python scripts/test_longrunning_monitoring.py 校验配置与指标名。仓库不包含 live collector、留存存储或通知渠道。

运维

  • 监听地址保持私有或置于 TLS/网关之后;health、readiness 与 /metrics 不鉴权。
  • 通过 PRAXO_TOKEN、PRAXO_VERIFIER_SECRET、PRAXO_LOCK_DSN 传密钥,避免进入命令历史, 或使用 token/secret 文件。
  • 替换 token/secret 文件即可无重启轮换凭据;签发、撤销通知与重叠策略仍属外部。
  • 状态目录仅限服务账号访问,备份已提交状态文件,临时/锁文件与状态文件位于同一文件系统。
  • 单机优先 file,单共享卷用 SQLite,多主机用 Postgres;TTL 不是生产 lease 服务。
  • 抓取 /metrics 并对错误/readiness 告警;使用 monitoring/ 示例配置设置 scrape 间隔、 留存与告警规则;计数器本身不含 collector、留存或告警。
  • 独立运行 cmd/longrunning-verifier,使用自己的 PRAXO_VERIFIER_SECRET 与 PRAXO_VERIFIER_TOKEN,执行服务不持有其密钥。

限制

  • 文件存储先写临时文件、同步,再 rename 覆盖主文件。确定性测试会注入 rename 边界前后的 故障,验证有效的未提交临时文件不能覆盖已提交状态,并要求损坏的已提交状态失败关闭。 这不是完整崩溃一致性证明:没有同步目录,未演练 OS 级断电和已提交文件损坏恢复。 未配置 lease 时,多进程并发写入可能丢更新;独立服务支持 file/SQLite/Postgres lease, 但生产 lease 协调、可信时钟和数据库运维仍属部署责任。
  • HTTP 服务是 loopback 测试 fixture。Bearer token 是静态值,TLS 是测试证书;没有身份 提供商、token 轮换、租户绑定、限流或生产部署保证。
  • 重启包含杀掉子进程并用同一文件重启。进程级崩溃测试会让独立服务在 rename 边界前后退出; OS 级断电和已提交文件损坏恢复仍未演练。
  • 进程内或单文件去重不是生产 exactly-once 机制。真实服务必须持久化幂等键与结果,并 定义并发与留存语义。
  • 状态查询必须访问权威执行系统,不能只查缓存的 Checkpoint。
  • 取消请求不是取消确认;执行权威必须报告最终状态。
  • Verifier 是独立 loopback 进程 fixture,但使用静态密钥与 token。真实部署需要密钥管理、 轮换、撤销与独立运营的验证权威。
  • Fencing fixture 现可在 JSON 文件中模拟 lease 过期、续期与撤销;它仍没有协调服务、 可信时钟保证、生产 lease 存储或并发写锁。边界测试记录了两个独立写入者可能丢失更新; 并发进程应使用真实 lease 或协调服务。
  • 效果验证、生产 lease/fencing、对账与留存属于执行系统或持久工作流产品。
  • 本配方不提供 exactly-once。

参见受治理执行设计、安全模型 与何时不应选择 Core。