可信执行
安全协调外部副作用
在执行前记录意图,重放已完成结果,并在写操作可能已发生时失败关闭。
为什么副作用需要边界
模型调用、Tool 或 Worker 可能在外部系统已经接受写操作后、调用方收到成功前失败。 此时“重试”可能意味着“扣款两次”“发送两次”或“删除下一个资源”。
Runifold 把它称为 Ambiguous Effect,不会用普通临时错误掩盖这种不确定性。
需要恢复结果的外部写操作都应使用 Effect 边界,例如支付、通知、资源开通、审批与 破坏性操作。纯计算不需要。
写前生命周期
EffectExecutor 通过持久状态协调一个逻辑操作:
| 状态 | 含义 | 安全的下一步 |
|---|---|---|
| Prepared | 意图已持久化,Handler 尚未开始 | 执行 |
| Started | Handler 可能已经到达目标 | 应用恢复策略 |
| Completed | 规范化输出已持久化 | 重放输出,不再 I/O |
| Failed | 明确的 Handler 错误已持久化 | 返回相同失败 |
use std::sync::Arc;
use runifold::{
EffectExecutor, EffectRecoveryPolicy, InMemoryEffectStore,
};
let executor = EffectExecutor::new(Arc::new(InMemoryEffectStore::new()));
let outcome = executor
.execute(
effect_request,
&run,
&handler,
EffectRecoveryPolicy::RejectAmbiguous,
)
.await?;需要跨进程重启恢复时使用持久 EffectStore。内存 Store 只适合测试与进程内行为。
恢复策略
默认 RejectAmbiguous 会拒绝重复结果未知的 Started 操作。
即使选择 RetrySafe,也只会在请求契约证明安全时重试:
- Pure 与 ReadOnly;
- 带稳定幂等 Key 的 IdempotentWrite。
它不会把非幂等写操作变安全。幂等 Key 必须代表目标系统中的同一个逻辑请求,而不是 每次 Retry 都生成新 Key。
Agent 工具与 Effect
Tool Descriptor 声明 effect 与 risk,Agent 根据这些语义协调 Callable。已完成调用
可以从共享 Effect Store 重放;同一执行位置出现不同请求时会被拒绝,不会拿到错误的
缓存结果。
Agent Checkpoint 可能在另一进程恢复时,应注入持久共享 Executor。ToolError 只保留
适合模型看到的信息,详细运行诊断放在 Transcript 外。
取消不是回滚。Handler 执行期间发生取消,Effect 仍保持 Ambiguous,直到对账完成。
生产检查清单
- 按目标系统真实行为分类每个写操作;
- 从业务身份派生稳定幂等 Key;
- 发送外部请求前先持久化意图;
- 使用有界 Deadline,但超时后保留 Ambiguous 状态;
- 未经明确批准,不把完整 Effect 输出写入 Journal Event;
- 为高风险不确定 Effect 提供对账队列;
- 故障注入测试“目标已提交,但成功响应丢失”。
如果无法解释最后一种失败如何恢复,这个操作就不应该自动重试。