跳转到内容

基于文件的持久化目标执行:审计日志与动态转向模式

基于文件的持久化目标执行:审计日志与动态转向模式

学习目标

读完本章后,你将能够:

  • 设计基于文件的多目标持久化执行系统
  • 实现 JSONL 格式的追加式审计日志
  • 构建动态转向(Steering)系统支持运行时计划修改
  • 理解聚合目标与逐目标两种执行模式
  • 设计最终质量门控的三方独立验证

前置知识


核心概念

1. 为什么需要基于文件的持久化目标执行

在多目标 AI Agent 执行场景中,传统方案面临以下问题:

问题后果基于文件方案的优势
会话重启丢失进度需要从头开始JSON 文件跨会话持久化
无法审计每个决策出问题后难以追溯JSONL 追加式审计日志
计划无法动态调整需要停止重来转向系统支持运行时修改
多个目标无法独立追踪进度不透明每个目标有独立状态

文件与数据库方案的对比

维度数据库(原子 checkout)文件(JSON + JSONL)
部署复杂度需要 DB 服务器零依赖,本地文件
并发控制行级锁,数据库事务文件锁(wx 独占打开)
审计能力需要额外的审计表JSONL 天然追加式审计
可读性需要 SQL 查询直接 cat 查看
适用场景多 Agent 并发操作同一系统单 Agent 顺序执行多目标

2. 三文件架构

基于文件的目标执行系统使用三个核心文件:

.omx/ultragoal/
├── brief.md # 原始简报(不可变参考)
├── goals.json # 完整计划:目标列表 + 聚合目标
└── ledger.jsonl # 追加式审计日志

goals.json 结构

伪代码:
{
"version": 1,
"createdAt": "2026-06-05T...",
"updatedAt": "2026-06-05T...",
"codexGoalMode": "aggregate", // aggregate | per_story
"codexObjective": "Complete the...", // 聚合目标字符串
"goals": [
{
"id": "G001",
"title": "build the CLI parser",
"objective": "Implement...",
"status": "pending", // pending | in_progress | complete | failed | review_blocked | needs_user_decision
"attempt": 0,
"steeringStatus": null, // superseded | blocked | null
"supersededBy": [],
"supersedes": [],
"blockedReason": null,
"nonRetriable": false
}
],
"aggregateCompletion": null // 任务作用域的聚合完成记录
}

ledger.jsonl 结构

每行一个 UltragoalLedgerEntry,记录每次状态变更:

伪代码:
{"ts": "...", "event": "plan_created", "goalId": null, "message": "Plan created with 5 goals"}
{"ts": "...", "event": "goal_started", "goalId": "G001", "message": "Starting G001"}
{"ts": "...", "event": "goal_completed", "goalId": "G001", "message": "G001 completed"}
{"ts": "...", "event": "goal_failed", "goalId": "G002", "message": "G002 failed: timeout", "failureReason": "..."}

3. 目标解析器

从用户输入的 markdown 简报中解析出离散目标:

伪代码:
function deriveGoalCandidates(briefMarkdown):
// 从顶层列表项提取目标,忽略非目标章节
candidates = []
for line in parseMarkdownList(briefMarkdown):
if not isNonGoalSection(line.parent):
candidates.push({
id: generateSlugId(), // "G001", "G002", ...
title: extractTitle(line),
objective: line.text,
status: "pending",
attempt: 0
})
return candidates

解析规则

规则说明
忽略章节标题”Acceptance criteria”、“Verification checklist” 等非目标内容不解析为目标
忽略子列表只提取顶级列表项,子列表项视为目标的细节
自动生成 ID使用 G001G002 等 slug ID
初始状态所有目标初始状态为 pending

4. 聚合目标 vs 逐目标模式

维度聚合模式(aggregate)逐目标模式(per_story)
Codex 目标数1 个N 个(每个目标 1 个)
聚合目标字符串稳定的全局指针
本地微目标ledger 中的 microgoalCodex 原生目标
适用场景大型项目,需要全局上下文简单任务,目标间独立
完成判定聚合目标状态为 complete所有 Codex 目标为 complete

聚合模式的优势

  • Codex 只维护一个活跃目标,减少了目标管理的复杂度
  • 本地 ledger 作为微目标追踪,灵活度高(可随时添加/拆分/重排序)
  • 最终完成时可通过任务作用域的聚合调和(Task-Scoped Aggregate Reconciliation)判定完成

5. 动态转向系统(Steering)

转向系统允许在运行时修改计划,而不需要停止重启。

六种允许的变异类型

变异类型效果示例
add_subgoal追加新目标发现遗漏的测试场景
split_subgoal将目标拆分为子目标”构建 API” → “设计 schema” + “实现 CRUD” + “编写测试”
reorder_pending重排 pending 目标顺序优先处理高风险目标
revise_pending_wording修改 pending 目标的标题/目标根据反馈调整描述
annotate_ledger向审计日志追加注解记录重要决策点
mark_blocked_superseded标记目标为阻塞或替代原方案不可行,用新方案替代

转向不变量(禁止的修改):

禁止项原因
修改聚合目标字符串聚合目标是稳定的全局指针
修改简报约束简报是用户原始需求的不可变参考
修改质量门控质量标准不得在运行时降低
硬删除目标审计日志需要完整记录所有历史目标
自动完成未完成的工作完成必须由实际执行产出证明
削弱验证要求验证是安全网,不得削弱

转向验证流程

伪代码:
function validateSteeringProposal(proposal):
// 检查不变量
for key in PROTECTED_KEYS: // ["aggregateCompletion", "brief", "codexObjective", "qualityGate", "status"]
if proposal.after[key] != undefined:
return { valid: false, reason: "Cannot modify protected key: " + key }
// 检查证据
if not proposal.evidence or proposal.evidence.length == 0:
return { valid: false, reason: "Steering requires evidence" }
// 检查理由
if not proposal.rationale or proposal.rationale.length == 0:
return { valid: false, reason: "Steering requires rationale" }
// 幂等性检查
if existsIdempotencyKey(proposal.idempotencyKey):
return { valid: false, reason: "Duplicate steering proposal" }
return { valid: true }

6. 完成判定

目标计划完成的两种路径:

路径一:聚合调和

伪代码:
if aggregateCompletion != null && aggregateCompletion.status == "complete":
return true // 任务作用域的聚合调和已完成

当外部完成的 Codex 目标与 ultragoal 简报内容重叠(通过内容重叠和 .omx/ultragoal 工件证据验证),可以跳过逐个目标的完成检查。

路径二:微目标全部完成

伪代码:
function isUltragoalDone(plan):
// 查找最终运行的完成候选目标
finalCandidate = findFinalRunCompletionCandidate(plan.goals)
if finalCandidate == null:
return false
// 检查该目标是否完成
return finalCandidate.status == "complete"

完成阻塞判定:目标在以下情况下阻塞完成:

条件说明
steeringStatus == 'superseded'且不是所有替代目标都已解决
steeringStatus == 'blocked'被明确标记为阻塞
status != 'complete'status != 'review_blocked'未完成且未被审查阻塞

7. 最终质量门控

最终故事完成时需要通过三方独立验证:

质量门控结构

伪代码:
{
"aiSlopCleaner": { "status": "passed" },
"verification": {
"status": "passed",
"commands": ["npm run build", "npm test", "npm run lint"]
},
"codeReview": {
"recommendation": "APPROVE",
"architectStatus": "CLEAR",
"independentReview": [
{ "agent": "code-reviewer", "evidence": "..." },
{ "agent": "architect", "evidence": "..." }
]
}
}

为什么需要三方独立验证

  • AI Slop Cleaner 清除 AI 生成的冗余/重复代码
  • 验证测试确保功能正确性和代码质量
  • 代码审查需要两个独立 Agent(代码审查员 + 架构师)的证据,防止单一审查者的盲点

8. 审查阻塞流

当最终故事的代码审查非清洁时:

  1. recordFinalReviewBlockers 将当前目标标记为 review_blocked
  2. 自动追加一个新的 pending blocker 解决故事
  3. Codex 目标保持活跃
  4. 调度器在下一轮启动 blocker 解决故事
  5. blocker 解决后,原故事可以完成

9. 突变锁与并发安全

文件级互斥锁防止并发修改:

伪代码:
async function withMutationLock(operation):
lockFile = open(".omx/ultragoal/.mutation.lock", "wx") // 独占打开
try:
return await operation()
finally:
lockFile.close()
unlink(".omx/ultragoal/.mutation.lock")

锁策略

  • 使用 wx 模式(独占创建),如果文件已存在则失败
  • 重试退避(最多 100 次尝试)
  • 自动清理死亡 PID 的锁文件

陷阱与对策

陷阱后果对策
JSONL 文件过大读取审计日志时内存溢出流式读取 + 分页,或定期压缩归档
转向无限拆分目标目标碎片化,管理开销增大设置最大拆分层级(如 3 层)
文件锁持有者崩溃后续操作永久阻塞PID 活跃度检查,超时自动释放
聚合目标字符串漂移最终完成判定失效转向不变量禁止修改聚合目标
Ledger 与 goals.json 不一致状态不同步每次 mutations 同时更新两个文件(原子写入)
审查阻塞无限循环blocker 解决后又阻塞设置最大 blocker 解决次数上限

参考来源

  • OpenAI Codex CLI — Goal tool specification
  • JSONL (JSON Lines) 规范 — https://jsonlines.org/
  • File locking patterns — open(2) O_EXCL flag