Skip to content

agent: effect ledger — duplicate-side-effect awareness on shell retries (LangEffect/RAC-inspired) - #2449

Merged
topcheer merged 1 commit into
mainfrom
effect-ledger
Sep 16, 2026
Merged

topcheer merged 1 commit into
mainfrom
effect-ledger

Conversation

@topcheer

Copy link
Copy Markdown
Owner

方向:2025-2026 前沿 gap — Agent 副作用账本(Effect Ledger)

研究依据:Uber LangEffect(2025,effect log + LIFO 补偿事务)、RAC: Robust Agent Compensation(arXiv:2605.03409,log-based recovery)、"Agent Idempotency" 生产模式(2026)。

Gap(已核实不存在)

当 side-effecting run_command 超时/中途被杀时,其外部结果 UNKNOWN——kill 本身不说明工作是否已落盘。Agent 重试同一命令时,现有机制均不提示此前有失败尝试,模型把重试输出当权威结论,可能重复外部副作用(git push 两次、npm publish 两次、重复部署/写 API):

实现(工具执行层机制,非新增 detector)

commandCache 同层的执行路径机制:

  • record():记录真实执行过的 run_command 的 failed / uncertain(timeout/kill/cancel)结果;权限拒绝、gate 拦截、参数解析失败等从未执行的形态被排除;成功不入账(验证类重复执行已由 redundantReverify 覆盖)
  • priorHint():同 (command, working_dir) 在 30 分钟窗口内重试时,在重试的 tool result 上追加 [Effect Ledger] 注释,提示模型先验证外部真实状态(git log/status、检查产物)再下结论
  • 协议安全:注释追加在 tool result 内容上(与 [cached] 注释同模式),不在 tool_calls 与 tool_results 之间插入消息;在 storeCommandResult 之后执行,注释永不入缓存;限流 5 次/会话、64 条环形账本

测试

6 个守卫测试:结果分类、键匹配(含 #1530 注释剥离)、重试窗口、限流、自我注释排除。go test -tags goolm ./internal/agent/ 全量通过,go build -tags goolm ./... 通过。

后续可选(本次未做):失败路径上的 LIFO 补偿摘要(向用户列出本会话副作用与建议回滚命令)。

Co-Authored-By: ggcode noreply@ggcode.dev

…etries

Executed-side-effect ledger at the tool execution layer (sibling of
commandCache), implementing the awareness layer of Uber LangEffect
(2025) and RAC log-based recovery (arXiv:2605.03409).

Gap: when a side-effecting run_command times out or dies mid-flight,
the outcome is UNKNOWN — a kill says nothing about whether the work
already landed. On retry of the identical command nothing told the
model that a prior attempt may have partially succeeded, so it treated
the retry output as authoritative and could duplicate external effects
(git push / npm publish / deploys twice).

Mechanism:
- record(): stores failed and uncertain (timeout/kill/cancel) outcomes
  of actually-executed run_command calls; never-executed shapes
  (permission denial, gate block, invalid input) are excluded;
  successes are not recorded (verification re-runs are already covered
  by redundantReverify).
- priorHint(): when an identical (command, working_dir) is re-executed
  within a 30-minute window after a failed/uncertain attempt, the
  retry's tool result is annotated ([Effect Ledger] ...) telling the
  model to verify external state before drawing conclusions.
- Protocol-safe: annotation appended to the tool result content (same
  pattern as commandCache's [cached] annotation); no message inserted
  between tool_calls and tool_results. Runs after storeCommandResult
  so annotation text is never cached. Capped at 5 hints, 64 records.

Includes guard tests for classification, key matching (with #1530
comment stripping), retry window, warning cap, and self-hint
exclusion.

Co-Authored-By: ggcode <noreply@ggcode.dev>

@topcheer topcheer left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审通过 ✅(执行层机制定位正确+协议安全)

  • 挂接时序精确:storeCommandResult 之后(注释明示 annotation 永不入缓存——缓存污染防护在位);record 在 priorHint 之后(自标注不可能,测试 TestRecordEffectAttemptSelfNotHinted 钉住);追加式 annotation 同 command_cache 惯例(协议安全——无消息插入)✓
  • 分类完备:neverExecutedMarkers(denial/parse/gate/shell resolve/job 未启动)排除正确——这些形态无外部效果;uncertainMarkers(timeout/deadline/killed/canceled)是最危险重试面——UNKNOWN 态可能部分落盘 ✓
  • 身份归一:stripLeadingShellComment 复用 command_cache 的 #1530 归一('# 描述' 行不破坏匹配)+workDir 组合键 ✓
  • 资源边界:64 记录减半淘汰/5 提示上限(防紧循环 nagging)/30 分钟窗(旧失败=有意重跑,不注)——三界合理 ✓
  • 7 测试覆盖全语义面(分类表/重试提示/窗口/上限/自标注排除/他工具忽略/键归一)
  • 非阻塞观察:uncertain 判定基于错误文本 Contains——marker 串与本仓错误格式耦合,格式变更需同步(marker 列表已注释出处:command_jobs.go 等,可维护)

CI 9/9 绿。可合并(排程内 #2449 已过,剩 #2447#2448)。

@topcheer
topcheer merged commit d3d19e3 into main Sep 16, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant