M Magent eMacs-native LLM AGENT

Agent 工作流状态机

Magent 现在把 agent loop 建模为显式的 thread -> turn -> item ledger。这个 ledger 是 agent workflow state 的 source of truth。旧 session messagescontext-items 仍然保留,但只作为 gptel prompt construction、迁移和旧测试的 derived projections。

Codex 对齐

Codex 在 SDK/app-server 边界暴露 thread event stream:

  • thread.started
  • turn.queued
  • turn.started
  • item.started
  • item.updated
  • item.completed
  • turn.completedturn.failed

Magent 保留同类工作流边界,但适配 Emacs:

  • Provider transport 仍然走 gptel-request
  • Magent loop 自己负责 tool dispatch、continuation outcome、abort 和 Emacs UI rendering;turn layer 遵循 Codex-style continuation:tool output 触发下一次 sampling,assistant completion 结束 turn,即使 assistant text 为空。
  • 不引入 Codex sandbox、seatbelt、bubblewrap 或 shell isolation parity。

Codex 中最接近的参考点是 SDK event names、core sampling loop、app-server event conversion 和 thread status shape。Magent 借鉴的是事件边界和 turn/item 生命周期,不复制 Codex 的隔离和 app-server runtime。

状态对象

magent-ledger.el 定义 canonical ledger:

  • magent-thread
    • 状态: not-loadedidleactivesystem-errorclosed
  • magent-thread-turn
    • 状态: queuedin-progresscompletedinterruptedfaileddropped
  • magent-thread-item
    • 状态: pendingin-progresscompletedfailedcancelled

每个用户 prompt 创建一个 turn。Assistant message、reasoning block、tool invocation 和 tool output 都是这个 turn 下的 item。Reasoning item 永远不会提升成 assistant message text,即使 provider 没有返回可见 content。

对象 运行态 终态
thread not-loaded, idle, active, system-error, closed closed
turn queued, in-progress completed, interrupted, failed, dropped
item pending, in-progress completed, failed, cancelled

Turn lifecycle:

queued -> in-progress -> completed
                      -> interrupted
                      -> failed
                      -> dropped

Item lifecycle:

pending -> in-progress -> completed
                       -> failed
                       -> cancelled

Tool Items

Tool call 和 tool result 是同一个 item lifecycle,不是两条持久记录。

模型请求工具时,item 开始:

(:type tool :status in-progress :name "grep" :input (:pattern "..."))

工具结果可用后,同一个 item 完成或失败:

(:type tool :status completed :output "...")

这和旧的 tool-call + tool-output 分裂不同。为了 prompt reuse,Magent 仍会把 completed tool item 投影成 gptel historical (tool . PLIST) 形状。

如果模型请求工具而结果稍后到达,Magent 按 call-id 更新同一个 item。如果旧路径只记录了 tool result,loop 会创建 synthetic turn,让 item 仍然有 durable turn parent。

持久化

持久化格式是 snapshot + journal

  • journal :内存中的 append-only event log,记录 turn-queuedturn-starteditem-starteditem-completedturn-completed 等生命周期事件。Session 文件只保留由 magent-session-journal-max-events 控制的最近有界尾部;tool/permission 审计由 magent-audit.el 单独负责。
  • snapshot :materialized full thread state。它保存当前 thread、turns 和 items,避免每次 resume 都从头 replay。

Session JSON 同时写入两者:

{
  "snapshot": { "id": "...", "turns": [] },
  "journal": [{ "type": "turn-started", "...": "..." }]
}

messagescontext-items 是 prompt reuse 和迁移用的 projections。加载时,Magent 优先 replay snapshot + journal ;若不存在则把 legacy messages 迁移成 thread ledger。

Replay 语义:

  1. snapshot 载入 materialized thread state。
  2. 附加持久化保留的 journal 尾部,供恢复和 history 检查。
  3. 只应用 seq > snapshot.last-event-seq 的事件。

因此 snapshot 是快速恢复点,持久化 journal 是有界恢复尾部,两者不可互换。

Loop Flow

  1. UI submission 进入 magent-runtime-api.el
  2. magent-runtime-api.el 创建 queued ledger turn,并立即记录 completed user message item。
  3. submission 真正开始时, magent-runtime-api.el 把 turn 转成 in-progress
  4. magent-agent-process 幂等复用该 turn/user item,不重复写用户消息。
  5. magent-agent-loop 消费 normalized LLM events。
  6. Text 和 reasoning deltas 更新 snapshot 中 materialized in-progress items,但不会为每个 chunk 追加 journal event;terminal item event 携带最终内容。
  7. Tool-call events 累积到 provider-neutral tool-call-batch-end
  8. Tool dispatch 启动 tool items,记录 approval metadata,并把同一 item 更新为 completedfailed
  9. Tool output 返回 tool-output 等 continuation outcome; magent-agent-process 决定是否从 session history 重建 prompt 并继续 sampling。
  10. 如果 post-tool continuation 返回空 assistant text,Magent 会进行一次禁用 provider tools 的 final-response retry。若该 retry 只有 reasoning 而没有 text,再进行一次更严格且禁用 reasoning 的 retry;严格 retry 仍为空时 turn 失败。
  11. Assistant completion 记录 assistant message item,并完成 turn。
  12. Abort、failure 和 dropped queued submissions 分别把 turn 转成 interruptedfaileddropped ;aborted turn 下的 in-progress items 标记为 cancelled
  13. magent-max-sampling-requests 是可选兼容 safety guard,默认关闭。Codex 没有 per-turn sampling cap;用户显式启用时,Magent 在到达上限时用一次禁用 provider tools 的 final request 替代后续 continuation。

Prompt reconstruction 是 ledger-driven。知道 current turn id 时,Magent 只包含 completed history 和该 turn,避免后续 queued user submissions 泄漏进 active model request。

UI 投影

受支持的 frontend 是 agent-shell。 magent-agent-shell.el 创建由 magent-acp.el 实现的 in-process ACP client。ACP session/prompt request 提交到 magent-runtime-api.el ,并保持 pending 直到对应 runtime turn completed、failed 或 cancelled。Runtime 发出 Magent-native observer events; magent-acp.el 转换为 ACP session/update 消息。

magent-runtime-queue.el 负责 queued/active turn state。当前实现一次只有一个全局 active turn,但 submission 都带 runtime session id,所以 cancellation 是 session-scoped:取消一个 ACP session 会移除该 session 的 queued work 并 abort 该 session 的 active turn,不会丢掉其他 session 的 queued turns。

magent-ui.elmagent-ui-legacy.el 以及 docs/UI_BACKENDS.org 覆盖的其他 legacy UI 文件是保留但不受支持的代码,不属于受支持的 UI projection 或公共命令面。

保留的 Codex 差异

Magent 刻意保留以下差异:

  • UI 是 Emacs-native;受支持的交互是 agent-shell + in-process ACP。
  • Provider streaming 经 magent-llm-gptel.el 规范化,但 transport 仍是 gptel。
  • Codex core 有 app-server mailbox、steering queue 和更丰富的 intra-turn input 语义;Magent 把 request serialization 放在 runtime queue,用 Codex-style tool continuation 处理工具结果:工具执行记录 model-visible output,然后 turn layer 继续 sampling,直到模型返回 assistant completion。Magent 仍不实现 Codex 的 app-server mailbox、steering queue、stop hooks 或 mid-turn auto-compaction。
  • Magent 用自己的 session JSON snapshot + journal ,不直接使用 Codex rollout files。
  • Tool call 和 output 在 Magent 中是一个 source-of-truth item 的状态更新。
  • Tool execution 当前由 Magent tool queue 串行化;Codex 更丰富的 per-tool runtime、approval、MCP 和 process execution 机制不在当前范围。
  • Child agents 是 AGENT_JOBS.org 中描述的 durable Magent jobs,不是 Codex app-server threads。

Backlog / TODO

Agent-workflow/UI refactor backlog 已无开放 TODO。未来 hardening 候选:

  • 为更多结构化输出增加 per-tool renderer plugins。
  • 增加超大 transcript 和长 streaming session 的 live visual smoke coverage。

设计评审摘要

当前主 agent-loop 相比 Codex 的主要差距已经不再是缺少显式 thread/turn/item lifecycle;Magent 已经拥有并持久化该 lifecycle。

剩余差异是有意的产品/运行时边界:

  • 不提供 Codex app-server lifecycle manager、subscriber model、unloaded thread cache 或 active flags。
  • 不复制 Codex 的 intra-turn input queue/mailbox semantics。
  • 不直接使用 Codex rollout files。
  • 不把 tool call 和 tool output 分成两条 source-of-truth records。