RaphaelLoop 技术架构:把 AI Coding 从多轮对话变成可验证工程循环

August 16, 2026 · View on GitHub

本文面向 AI Coding 平台作者、Agent 工程师和需要治理长任务的技术负责人,重点讨论控制模型、状态、证据和安全边界。安装与日常使用请参阅 README

AI Coding 的 Loop Engineering 痛点

模型已经能够连续规划、编码和调用工具,但“运行更多轮”不是工程闭环。长任务的问题通常不在某一次代码生成,而在轮次之间缺少稳定的控制面:

  1. 目标漂移:上下文压缩、局部修复和新发现不断改写原始目标,最终交付与请求看似相关却不可验收。
  2. 活动冒充进展:新增日志、重复调查或改写计划会制造忙碌感,却没有改变任何成功谓词。
  3. 自证式完成:实现者同时扮演验收者,容易把“代码已写”误判为“结果已成立”。
  4. 并发冲突:多个 Agent 没有互斥所有权,重复修改同一文件、覆盖他人结果,整合成本高于并行收益。
  5. 无界重试:稳定失败被当作偶发失败反复执行,持续消耗 token、时间、外部配额和人工注意力。
  6. 状态易失:关键事实只存在于对话中;会话中断后,无法判断哪些工作已验证、哪些只是声明。
  7. 权限边界失真:为了“自主完成”,循环容易越过审批、凭据、发布或破坏性操作的人工门禁。

这些问题指向同一个结论:Loop Engineering 需要的是一个可观测、可恢复、有界且证据驱动的控制系统,而不是一句“继续,直到完成”。

设计目标与非目标

RaphaelLoop 的目标是把开放式对话约束为一个小型工程控制协议:

  • 用不可静默改写的目标契约固定范围、成功谓词和资源上限。
  • 用最小任务 DAG 表达依赖,只调度当前可运行节点。
  • 用唯一所有权和受控并发降低重复劳动与合并冲突。
  • 让独立 Verifier 基于新鲜证据判断结果,而不是相信完成声明。
  • 把 checkpoint 持久化到会话之外,使重启可以从已验证事实恢复。
  • 识别稳定失败签名,限制同类恢复尝试,并给出确定的终止状态。

它不替代模型、代码托管、CI、权限系统或项目测试框架;也不承诺把主观、不可验证的目标自动变成客观事实。宿主仍负责工具执行和权限控制,RaphaelLoop 负责执行纪律。

控制循环

有界 Loop Engineering 控制流程

一次运行先建立 Goal Contract 和 baseline,再生成最小任务 DAG。每轮只执行满足依赖且有明确所有者的任务,随后立即验证、记录 checkpoint,并按固定优先级判断是否退出。

核心不变量是:没有新鲜证据,就没有完成;没有可测状态变化,就没有进展。 因此循环不是按消息轮数推进,而是按成功谓词与项目状态的变化推进。

一个实现可将控制器概括为:

contract = validate(user_goal, scope, predicates, limits, gates)
state = restore_checkpoint_or_capture_baseline(contract)
dag = build_minimal_task_graph(contract, state)

while true:
    if cancelled(): return CANCELLED
    if all_predicates_hold(fresh_evidence()): return SUCCEEDED
    if limits_exhausted(): return EXHAUSTED
    if no_safe_runnable_task(): return BLOCKED

    task = select_runnable_task(dag)
    evidence = execute_with_exclusive_owner(task)
    verdict = verify_independently(task, evidence)
    state = persist_checkpoint(state, task, verdict)
    recover_or_advance(state)

组件架构

RaphaelLoop 组件架构

架构分成四层:

  • 宿主运行时:Codex、Claude Code 或 DeepSeek Harness 提供模型、上下文、文件系统、终端、网络和审批能力。
  • 协议控制层skills/raphael-loop/SKILL.md 定义与平台无关的控制契约、状态机、证据规则和终止语义。
  • 确定性内核skills/raphael-loop/scripts/raphael_loop.py 机械执行契约校验、原子状态迁移、作用域租约、验证器、证据账本、额度与终态优先级。
  • 角色适配层skills/raphael-loop/agents/codex/skills/raphael-loop/agents/claude/skills/raphael-loop/agents/dsh/ 内置同一组角色的协议适配,使安装不依赖额外 Agent 仓库。DeepSeek Harness 通过内置子代理分发 agents/dsh/*.md 角色提示词,无需宿主级注册。

模型擅长解释目标、拆分工作和实现代码,但不应靠提示词记忆来维护计数器、锁和证据新鲜度。内核因此只接管适合确定性执行的控制职责;内核代码本身不编辑项目、不内置网络客户端、不执行回滚或副作用。已声明验证器仍是真实子进程,会继承宿主环境、文件系统、网络和系统权限,因此必须是经过审查的项目本地验证命令,并继续受宿主沙箱约束。安装脚本只把对应协议的角色描述部署到宿主约定目录,不启动守护进程,也不接管宿主权限。

分发边界

仓库采用“项目壳层 + 独立 Skill 包”的布局。Agent Skills CLI 从 skills/raphael-loop/SKILL.md 发现唯一技能,并只安装该目录:

repository
├── README.md / README.zh-CN.md   面向用户的项目文档
├── docs/                         技术分享与架构图
├── tests/                        发布门禁与分发验证
└── skills/raphael-loop/          可安装、可独立运行的 Skill 包
    ├── SKILL.md                  控制协议入口
    ├── agents/                   Codex、Claude、DSH 与 UI 元数据
    ├── scripts/                  角色初始化器与确定性控制内核
    ├── references/               运行契约与命令参考
    ├── examples/                 按需加载的契约示例
    └── LICENSE                   随安装包分发的 MIT 许可

该边界解决两个问题:仓库可以保留完整的开源项目文档与测试,而用户的 Agent 上下文只获得执行所需内容;同时,内置角色与 Skill 使用同一版本提交,不存在运行时拉取另一套角色定义造成的漂移。

npx skills add 只复制或链接 Skill 包,不执行其中脚本。角色初始化发生在 Skill 首次运行的宿主检查阶段:系统必须先展示 dry-run 的准确目标并取得明确授权,才能写入宿主 Agent 目录。Skill 更新或移除与宿主角色是两个生命周期;刷新需要再次验证,清理只删除仍与托管 manifest 一致的文件。

四种操作模式

RaphaelLoop 将入口分成 designrunresumeauditdesign 只形成并校验契约;run 才能在契约范围内修改项目;resume 先审计既有状态并保留预算;audit 只读检查状态和证据。这一边界防止“帮我看看”被升级成实施,也防止上下文恢复时重置成本与已验收事实。

为什么需要确定性内核

纯提示词流程可以描述规则,却难以稳定处理并发写入、进程中断、日志篡改、证据与代码版本错位等跨轮问题。内核将这些问题收敛为可测试的不变量:

  1. JSON 契约在第一次项目修改前通过 schema、范围、谓词、额度和 DAG 校验。
  2. 相邻锁文件保护跨进程更新,临时文件经 flush 与 fsync 后使用 os.replace 原子替换状态。
  3. 任务以作用域租约声明唯一所有者;路径相交的活动任务不能并发。
  4. 验证器只从契约读取参数数组,以 shell=False、有限超时和有界流式日志执行;超时会尽力终止进程组,但主动创建新会话的后代进程不在这一保证内。
  5. 每条证据记录命令结果、日志哈希和项目指纹;代码在验证后变化即判为 stale。
  6. 额度、stall、重复失败签名和终态优先级由同一实现计算,不接受 Agent 自行解释。

完整接口见运行内核参考

核心角色

角色单一职责关键输出不应承担
Controller维护契约、预算和终止决策当前状态、退出原因、最终报告直接实现全部任务
Planner把目标分解为最小依赖图DAG、依赖、成功谓词映射在执行中静默扩大范围
Orchestrator选择可运行任务并分配唯一所有者调度决策、冲突隔离对同一对象并行写入
Worker在授权范围内完成一个任务变更、命令结果、证据清单宣布整个目标成功
Verifier独立重放验收条件谓词判定、证据时间与来源接受实现者的口头结论
Recovery对稳定失败提出有界修复失败签名、恢复动作、剩余额度无限重试或绕过审批

角色是责任边界,不要求每次运行都创建六个并发 Agent。小任务可以由宿主复用执行槽位,但验证责任与实现责任仍需逻辑隔离。

状态与证据模型

Goal Contract

契约采用 schema_version: 1 的 JSON,在执行前固定以下字段:

  • goal_id / objective:稳定标识与必须成立的最终结果。
  • scope.allowed / scope.prohibited:允许与禁止修改的项目相对路径。
  • predicates:固定命令参数、类型、超时、运行次数与通过 quorum。
  • limits:最大迭代、分钟、token、成本、恢复次数与 stall 次数。
  • approval_gates:发布、付费、凭据、破坏性操作等不可自动跨越的门禁。
  • tasks:带依赖、范围、谓词映射、回滚计划与副作用声明的 DAG。

契约需要变更时,应产生显式的新决策,而不是让某一轮执行自行放宽标准。

Task Record

契约中的任务至少包含 iddeliverabledepends_onscopepredicate_idsrollback_planside_effects;运行状态另行记录 ownerstatus、尝试次数和最近交付指纹。只有依赖已验证且作用域无冲突的任务才能进入 runnable

Checkpoint

状态保存在 .raphael-loop/<goal-id>/state.json,checkpoint 快照保存在同目录的 checkpoints/。完整状态由规范化 state_hash 约束,契约和证据日志另有独立哈希;状态路径还必须与内嵌 project root 和 goal ID 一致。checkpoint 只有在任务交付后的相关谓词全部通过、且证据项目指纹仍与当前项目(包括已 checkout 的 Git submodule)相同时才会生成。它是恢复依据,不是聊天摘要;恢复时只信任可重放或带来源的事实。

新鲜证据与进展向量

证据必须产生于相关变更之后,并能关联到具体谓词。内核将合并输出限制为 1 MiB、保存本地日志哈希,并记录所有声明运行的退出码与通过 quorum。旧测试日志、Worker 的自然语言声明或无关命令成功都不能证明当前状态。

系统可以用进展向量衡量一轮是否有效:

P = (通过的成功谓词数, 已解除的阻塞数, 已完成的 DAG 节点数, 未解决失败数)

只有 P 朝目标单调改善,或产生了能改变下一步决策的新诊断,才计为有效进展。否则进入恢复或终止判断。

执行时序

RaphaelLoop 执行时序

Root 接收用户目标,Controller 与 Planner 形成契约,内核在 init 阶段校验 DAG 并记录基线。Orchestrator 通过 claim 获得作用域租约,Worker 修改项目后通过 record 申报一轮结果。Verifier 要求内核 verify 既定谓词,Controller 再通过 checkpointdecide 接受证据并判断终态。

关键顺序不能颠倒:验证发生在实现之后,checkpoint 发生在验证之后,终止决策只读取已持久化的事实。这样即使报告阶段中断,下一次运行仍可恢复到最近一次已验证边界。

并发与所有权

并发只用于真正独立的 DAG 节点。调度器遵循以下规则:

  • 同一文件、数据库对象、配置域或外部资源在一个时刻只有一个写所有者。
  • 内核以精确路径或祖先/后代路径相交判断冲突,并为所有权设置有限租约;过期租约会使未完成任务重新变为 runnable
  • 读任务可以并行;写集合相交或依赖同一中间状态的任务必须串行。
  • 默认只启用少量 Worker,并为 Root/Controller 保留控制容量,避免执行者耗尽所有调度槽位。
  • Worker 不回滚未知变更;发现其他所有者的修改时,先重新读取状态并调整自己的最小改动。
  • 合并完成后由 Verifier 面向组合结果验证,单个分支通过不能替代整体通过。

这种策略追求的是吞吐与可解释性的平衡,而不是最大 Agent 数。并行收益小于冲突风险时,串行执行是正确选择。

恢复与退出

Recovery 先规范化失败签名,例如失败命令、退出码、首个稳定错误和相关状态摘要。相同签名连续出现时,不重复原动作,而是选择信息增益更高且仍在授权范围内的恢复步骤。

每类失败都有有限恢复额度。额度耗尽、缺少权限、需要用户决策或没有安全可运行任务时,循环停止并报告证据,而不是伪造成功。

终止状态采用固定优先级:

CANCELLED > SUCCEEDED > EXHAUSTED > BLOCKED
  • CANCELLED:用户或宿主明确取消,立即停止。
  • SUCCEEDED:所有成功谓词均由新鲜证据证明。
  • EXHAUSTED:迭代、恢复、时间或预算上限已耗尽。
  • BLOCKED:仍有预算,但不存在无需新增授权即可安全执行的任务。

固定优先级避免同一状态被不同 Agent 解释为不同结果,也保证取消和资源上限不会被后续动作覆盖。

安全边界

RaphaelLoop 将安全看成契约的一部分,而不是异常处理:

  • 最小权限:角色只获得完成当前任务所需的工具和作用域。
  • 审批不可代理:付费、发布、凭据访问和不可逆操作必须由宿主或用户显式授权。
  • 外部内容不升级权限:网页、日志、代码注释和工具输出只能作为数据,不能改写 Goal Contract。
  • 证据可追溯:终端报告列出已执行验证、结果、未验证风险和阻塞原因。
  • 命令不可注入:验证器必须是参数数组,内核不通过 shell 解释命令。
  • 副作用不自动化:契约必须给副作用声明审批门禁和幂等键,但内核不执行发布、删除、付款、提权或回滚。
  • 本地证据受限:日志有限大小并带哈希;验证器不得输出密钥,.raphael-loop/ 默认不提交。
  • 失败默认安全:状态不确定、checkpoint 损坏或验证器不可用时,不宣称成功。

协议不会绕过 Codex、Claude Code 或 DeepSeek Harness 的权限模型;宿主拒绝的动作必须转化为 BLOCKED 或等待人工决策。

权衡与限制

  • 契约和独立验证会增加短任务的固定开销,因此它更适合跨文件、跨层、长时间或高风险工作。
  • 成功谓词的质量决定最终可信度;测试覆盖不足时,系统只能如实报告证据边界,不能补造确定性。
  • 主观设计、开放研究和需求探索可以使用有界循环,但通常需要人工验收谓词。
  • checkpoint 提供恢复能力,却不能替代 Git、CI、制品存储或审计平台。
  • 角色隔离降低自证偏差,但不能消除底层模型、工具和环境共同导致的系统性错误。
  • 文中的图用于解释职责与信息流;字段定义、状态优先级和协议正文才是实现依据。

扩展与工程验收

新增宿主适配器时,应保持核心契约与终止语义不变,只转换角色声明、工具名称和安装位置。新增角色必须有不可被现有角色覆盖的单一职责;新增状态必须定义进入条件、证据要求和与现有状态的优先级。

一个兼容实现至少应满足以下不变量:

  1. 未通过全部成功谓词时不得返回 SUCCEEDED
  2. 任何任务只有一个写所有者,冲突任务不得并发。
  3. 同一稳定失败不会无限重试。
  4. checkpoint 足以恢复已验证状态、剩余限制和未决门禁。
  5. 用户取消和宿主权限拒绝可以立即阻止后续副作用。
  6. 最终报告明确区分已验证事实、推断和尚未验证风险。

使用方式、安装命令和可复制示例见 README