iceCoder: 反构图核心逻辑深度解析

1. 什么是「反构图」

「反构图」是 iceCoder 双模 L2 接管阶段的核心动作,英文对应 RetrospectiveGraphBuilder。

它的本质是:在模型已经自由执行了一段代码之后,系统接管时「逆向重建」一张任务图,把已经完成的工作回填为 done,而不是从零开始重跑模板。

复制代码
用户意图 → 模型自由段(无图) → 偏离触发 takeover → 反构图 → replaceGraph → 强约束段继续

与正向构图(buildGraph 在首轮 initGraph)的区别:

维度 正向构图 反构图
触发时机 任务首轮 turnCount === 1 L2 takeover 后(§10 恢复主路径)
输入依据 goal + intent goal + intent + WorkspaceSnapshot
进度状态 全部 pending 按快照回填 done
游标位置 第一个节点 推进到第一个未完成节点
失败后果 无图自由段 降级为二级强提示(§19.2)

2. 反构图的完整调用链

反构图不是单一类的行为,而是 M5→M6→M7→M8 四阶段流水线:

scss 复制代码
Harness 主循环
  │
  ▼
evaluateAfterRound 返回 decision.action === 'takeover'
  │
  ▼
bridge.commit(nextSnapshot)  →  supervisorPhase 变为 'takeover'
  │
  ▼
applyTakeoverRecoveryMainPath()          [harness-recovery-main-path.ts]
  │
  ├─ M5: WorkspaceStateExtractor.extract()        → WorkspaceSnapshot
  │     [workspace-state-extractor.ts]
  │
  ├─ M6: SnapshotConfidenceEvaluator.evaluate()   → confidence ∈ [0,1]
  │     [snapshot-confidence-evaluator.ts]
  │
  ├─ M7: RecoverySafetyChecker.check()            → recoverable: boolean
  │     [recovery-safety-checker.ts]
  │
  └─ M8: RetrospectiveGraphBuilder.build()        → graph + markedDone
        [retrospective-graph-builder.ts]
        │
        ├─ buildGraph(goal, intent)                → 模板图(全 pending)
        ├─ applyKnownProgress(graph, snapshot)     → 回填 done
        └─ advanceCursorPastDone(graph)            → 游标推进
  │
  ▼
GraphExecutor.replaceGraph(graph)          [task-graph-executor.ts]
  │
  ▼
enterTakeover()  →  强约束段接管

3. 四阶段深度解析

3.1 M5 --- WorkspaceStateExtractor:工作区快照提取

文件:src/harness/supervisor/workspace-state-extractor.ts

职责:从 Harness 已有的 TaskStateSnapshot + RepoContextSnapshot 中,纯启发式地拼出 WorkspaceSnapshot,不发起任何 IO。

输入:

  • task: TaskStateSnapshot --- 任务状态快照(含 filesChanged, filesRead, verificationStatus)
  • repo: RepoContextSnapshot --- 仓库上下文快照(含 filesChanged, recentDiagnostics)
  • buildSummary?: string --- 最近一次构建摘要(由调用方从 verification buffer 推导)
  • testSummary?: string --- 最近一次测试摘要
  • lintSummary?: string --- 最近一次 lint 摘要
  • gitSummary?: string --- Git 工作区简述(可选,缺省由 repo.filesChanged 推导)

输出:WorkspaceSnapshot

typescript 复制代码
interface WorkspaceSnapshot {
  snapshotId: string;          // `snap-${now}-${shortRand()}`
  at: number;                  // 时间戳
  gitSummary: string;          // 如 'clean' / 'M:3 diag:1'
  filesAdded: string[];        // 新增文件
  filesModified: string[];     // 修改文件
  filesDeleted: string[];      // V1 恒空
  buildSummary?: string;
  testSummary?: string;
  lintSummary?: string;
}

核心算法 --- classifyFiles:

javascript 复制代码
function classifyFiles(input: WorkspaceStateExtractorInput) {
  const changed = new Set([...input.task.filesChanged, ...input.repo.filesChanged]);
  const known = new Set(input.preExistingFiles ?? input.task.filesRead);
  
  for (const file of changed) {
    if (known.size > 0 && !known.has(file)) {
      added.push(file);       // 不在已知集合 → 新增
    } else {
      modified.push(file);    // 在已知集合 → 修改
    }
  }
  return { added, modified, deleted: [] };
}

关键设计点:

  • filesAdded vs filesModified 的判定依赖 preExistingFiles(通常由初始任务 snapshot 与当前 repo 的差集得出)
  • V1 不区分 deleted,保留接口兼容性
  • gitSummary 缺省推导:filesChanged.length === 0 → 'clean',否则 'M:n' 或 'M:n diag:m'

3.2 M6 --- SnapshotConfidenceEvaluator:快照可信度评估

文件:src/harness/supervisor/snapshot-confidence-evaluator.ts

职责:给 WorkspaceSnapshot 打一个 0, 1 的综合分,判断是否「够格」走模板图。

五因子加权求和:

因子 权重 满分条件 评分函数
gitClean 0.25 gitSummary === 'clean' 或 files 集合为空 空集→1.0;非空→线性衰减
snapshotAge 0.15 roundsSinceExtract <= 1 ≤1→1.0;≥5→0.0;中间线性
verifyPassed 0.20 lastVerifyPassed === true true→1.0;false→0.0
repoContextMatch 0.25 repoFilesChanged ⊆ snapshot.filesModified ∪ filesAdded 子集→1.0;否则按重叠比
buildSignal 0.15 buildSummary 不含 fail/error,testSummary 不为 failed 无 bad 标记→1.0;否则 0.0

阈值:templateGraphMin(默认 0.65)

关键实现细节:

  • 权重归一化:weights = weights / sum(weights),避免配置漂移导致总分越界
  • 缺失信号中性处理:任一因子输入为 undefined → 按 0.5 计入,避免「无信号 = 低分」
  • meetsTemplateGraphThreshold 暴露给 RetrospectiveGraphBuilder 复用,不重复计算

3.3 M7 --- RecoverySafetyChecker:恢复安全性检查

文件:src/harness/supervisor/recovery-safety-checker.ts

职责:在反构图前做最后一轮安全闸门,防止在危险工作区上换图。

检查项:

检查项 触发条件 说明
critical_file_missing 注入的 criticalFiles 列表有文件不在 existingFiles 中 关键文件丢失
repo_unhealthy gitSummary 含 conflict/detached/rebase/merge/unmerged 仓库状态损坏
branch_unhealthy 调用方显式注入 branchHealthy=false 分支异常(合并冲突等)
baseline_broken buildSummary 含 fatal/crash/panic/segfault 编译基线损坏

输出:

ini 复制代码
interface RecoverySafetyCheckResult {
  recoverable: boolean;
  reasons: RecoverySafetyReason[];
  missingFiles: string[];
  humanReason: string;  // 如 'ok' 或 'critical_file_missing:src/foo.ts+2|repo_unhealthy'
}

关键设计点:

  • criticalFiles 缺省(undefined)→ 跳过该检查,避免假阳性
  • existingFiles 未注入 → 不误报缺失(调用方没准备好)
  • repoHealthy / baselineBroken 支持调用方显式覆盖,避免启发式误判

3.4 M8 --- RetrospectiveGraphBuilder:反构图核心

文件:src/harness/supervisor/retrospective-graph-builder.ts

这是反构图的核心引擎,负责「建模板图 + 回填进度 + 推进游标」。

3.4.1 入口:build(input)

scss 复制代码
build(input: RetrospectiveGraphBuildInput): RetrospectiveGraphBuildResult {
  // Step 1: 调用 buildGraph 得到全 pending 的模板图
  graph = buildGraph({ goal, intent, workspaceRoot, now, graphId });
  
  // Step 2: 按 WorkspaceSnapshot 回填已完成节点
  const markedDone = applyKnownProgress(graph, input.snapshot);
  
  // Step 3: 把游标推到第一个未完成节点
  advanceCursorPastDone(graph);
  
  return { ok: true, graph, markedDone, signalsSummary };
}

失败处理:buildGraph 抛错或返回空图 → { ok: false, reason: 'build_threw' | 'empty_template' },不抛错,由调用方按 §19.2 降级。

3.4.2 核心算法:applyKnownProgress

这是反构图最关键的逻辑,决定哪些节点可以被标记为 done。

V1 规则(硬编码):

节点类型 标为 done 的条件
inspect / search snapshot.filesAdded.length + snapshot.filesModified.length > 0
verify snapshot.testSummary === 'passed'

实现细节:

ini 复制代码
function applyKnownProgress(graph: TaskGraphData, snapshot: WorkspaceSnapshot): string[] {
  const markedDone: string[] = [];
  const hasFileEvidence = snapshot.filesAdded.length + snapshot.filesModified.length > 0;
  const verifyPassed = snapshot.testSummary === 'passed';
  
  for (const node of graph.mainBranch.nodeIds.map(id => graph.nodes[id]).filter(Boolean)) {
    if (node.status !== 'pending') continue;  // 只处理 pending 节点
    
    let shouldMark = false;
    if ((node.type === 'inspect' || node.type === 'search') && hasFileEvidence) {
      shouldMark = true;
    } else if (node.type === 'verify' && verifyPassed) {
      shouldMark = true;
    }
    
    if (shouldMark) {
      node.status = 'done';
      node.startedAt = node.startedAt ?? now;
      node.endedAt = now;
      graph.nodeHistory.push({ nodeId, status: 'done', startedAt, endedAt, retries });
      markedDone.push(node.id);
    }
  }
  
  // 更新 graph.progress
  if (markedDone.length > 0) {
    graph.progress = Math.min(100, Math.round((markedDone.length / totalNodes) * 100));
    graph.updatedAt = now;
  }
  
  return markedDone;
}

重要约束:

  • 只修改 status === 'pending' 的节点,不碰已完成的
  • startedAt 采用「缺失才补」策略(??=),保留原始时间
  • 所有被标记节点共享同一个 now 时间戳(提取时刻)
  • 不修改节点内容、不修改边、不修改分支结构

3.4.3 游标推进:advanceCursorPastDone

回填完成后,需要把执行游标从已完成区域推到待办区域:

ini 复制代码
function advanceCursorPastDone(graph: TaskGraphData): void {
  let idx = graph.cursor.nodeIndex;
  
  // 跳过所有 done 节点
  while (idx < branchIds.length) {
    const node = graph.nodes[branchIds[idx]];
    if (!node || node.status !== 'done') break;
    if (!graph.cursor.completedNodeIds.includes(node.id)) {
      graph.cursor.completedNodeIds.push(node.id);
    }
    idx += 1;
  }
  
  // 重写 cursor(不使用 advanceCursor 以避免副作用)
  graph.cursor.nodeIndex = Math.min(idx, branchIds.length - 1);
  graph.cursor.nodeId = branchIds[graph.cursor.nodeIndex] ?? '';
  
  if (idx < branchIds.length) {
    // 停留在 pending 节点 → 显式启动
    if (graph.nodes[graph.cursor.nodeId]?.status === 'pending') {
      startCurrentNode(graph);
    }
  } else {
    // 全部完成 → 触发 markGraphDone
    if (last.status === 'done') {
      advanceCursor(graph);
    }
  }
}

关键设计点:

  • 直接重写 cursor.nodeIndex / cursor.nodeId,不调用 advanceCursor(后者会改 updatedAt 和 status 副作用)
  • 同步维护 cursor.completedNodeIds,确保快照一致性
  • 当游标停在 pending 节点时,调用 startCurrentNode 启动该节点(与正常 lifecycle 一致)
  • 全部完成时,调用 advanceCursor 触发 markGraphDone 流程

4. 与 GraphExecutor 的集成

反构图完成后,通过 GraphExecutor.replaceGraph 注入运行中的图实例:

4.1 replaceGraph

ini 复制代码
// task-graph-executor.ts
replaceGraph(graph: TaskGraphData): void {
  this.graph = graph;
  this.contractValidator = new ContractValidator(graph);
  this.deviationDetector = new DeviationDetector();
  // ... 重置其他依赖 graph 的组件
}

替换时机:仅在 supervisorPhase === 'takeover' 时允许,由 harness-recovery-main-path.ts 的 applyTakeoverRecoveryMainPath 调用。

4.2 takeover 段的 graph 行为变化

方法 free 段 takeover 段
evaluateRound 返回 inject_hint 文案 evaluationMode = 'metrics_only',不返回 inject 文案
checkToolCall 节点合约检查 + 偏离检测 同左,但偏离信号经 CorrectionPort 以 C 类注入
advanceCursor / completeCurrentNode 正常推进 正常推进,但受 RecoveryBudgetManager 限制
force_switch 允许 允许,但经 bridge.composeGraphHint 路由

5. 失败降级路径(§19.2)

反构图不是强制的。当以下任一条件不满足时,走二级降级:

ini 复制代码
M5 提取快照
  │
  ▼
M6 置信度 < templateGraphMin (0.65)  ──┐
  │                                    │
M7 recoverable === false              │
  │                                    │
M8 buildGraph 失败 / 空图              │
  │                                    │
  ▼                                    ▼
一级成功:replaceGraph              二级降级:strong_hint
  │                                    │
  ▼                                    ▼
timeline: recover:template_graph    timeline: recover:strong_hint
                                    │
                                    ▼
                              inject [System Recovery] 块
                              forced 不回落 free

二级降级的表现:

  • 不替换图(graphExecutor 保持原状或 null)
  • 向 messages 注入一条 System Recovery 强提示块
  • forcedDegradedTier = 'step_queue'
  • 模型收到明确的接管信号和行动建议

6. 关键数据结构流转

6.1 WorkspaceSnapshot → markedDone 的映射逻辑

bash 复制代码
WorkspaceSnapshot
  ├─ filesAdded / filesModified 非空
  │   └─ 所有 type ∈ {inspect, search} 的 pending 节点 → done
  │
  ├─ testSummary === 'passed'
  │   └─ 所有 type === 'verify' 的 pending 节点 → done
  │
  └─ 其他类型(edit, context, final 等)
      └─ V1 不自动标记,保持 pending

V1 的局限性:

  • 不区分 edit 节点是否真的被完成(V1 没有语义分析能力)
  • verify 节点只认 testSummary === 'passed',不认 build/lint 通过
  • 不支持部分完成(节点要么全 done 要么全 pending)

6.2 游标推进的边界情况

场景 行为
首个节点就是 done 游标直接跳到下一个 pending
连续多个 done while 循环批量跳过
全部 done advanceCursor 触发 markGraphDone
无节点 空保护,直接 return

7. 与 RecoverySupervisor 的协作

反构图发生在 RecoverySupervisor 的 takeover 相位内,但不由 RecoverySupervisor 直接调用。

协作流程:

scss 复制代码
ModeDecisionEngine / 信号累积
  │
  ▼
RecoverySupervisor.evaluate()
  │
  ├─ 条件满足 → decision: { action: 'takeover', signals }
  │
  ▼
bridge.commit(nextSnapshot)  →  phase = 'takeover'
  │
  ▼
Harness 调用 applyTakeoverRecoveryMainPath()
  │
  ▼
bridge.runRecoveryMainPath()  →  内部串联 M5→M6→M7→M8

职责分离:

  • RecoverySupervisor:决定是否 takeover(状态机)
  • SupervisorRuntimeBridge:决定 takeover 后做什么(恢复策略)
  • RetrospectiveGraphBuilder:执行具体构图(模板图 + 回填)

8. 代码级关键路径追踪

8.1 从 Harness 主循环到反构图

css 复制代码
harness.ts: run()
  │
  ▼
harness-round-prep.ts: prepareHarnessRound()
  │   └─ turnCount === 1 && shouldInit → initGraph(正向构图)
  │
  ▼
callHarnessLlm() → tool_round / no_tool_round
  │
  ▼
evaluateAfterRound()  [supervisor-bridge.ts]
  │   ├─ PassiveObserver.observe() → signals
  │   ├─ GoalDriftDetector.evaluate() → driftSignal
  │   └─ RecoverySupervisor.evaluate() → decision
  │       └─ action === 'takeover' ?
  │
  ▼ (if takeover)
bridge.commit(nextSnapshot)
  │
  ▼
applyTakeoverRecoveryMainPath()  [harness-recovery-main-path.ts]
  │   ├─ buildRecoverySummaries() → build/test/lint 摘要
  │   ├─ buildRecoveryExtractInput() → M5 input
  │   └─ bridge.runRecoveryMainPath()
  │       ├─ M5: extractor.extract()
  │       ├─ M6: confidenceEvaluator.evaluate()
  │       ├─ M7: safetyChecker.check()
  │       └─ M8: retrospectiveBuilder.build()
  │           ├─ buildGraph()
  │           ├─ applyKnownProgress()
  │           └─ advanceCursorPastDone()
  │
  ▼
graphExecutor.replaceGraph(graph)
  │
  ▼
enterTakeover()  →  executionMode = forced

8.2 关键文件索引

文件 核心导出 反构图中的角色
retrospective-graph-builder.ts RetrospectiveGraphBuilder, applyKnownProgress, advanceCursorPastDone M8 核心引擎
workspace-state-extractor.ts WorkspaceStateExtractor M5 快照提取
snapshot-confidence-evaluator.ts SnapshotConfidenceEvaluator M6 置信度评估
recovery-safety-checker.ts RecoverySafetyChecker M7 安全闸门
harness-recovery-main-path.ts applyTakeoverRecoveryMainPath, buildRecoveryExtractInput 主路径编排
supervisor-bridge.ts runRecoveryMainPath, commit 桥接与状态提交
task-graph-executor.ts GraphExecutor.replaceGraph 图替换执行
task-graph-builder.ts buildGraph 模板图构建(被 M8 复用)
task-graph.ts advanceCursor, startCurrentNode, markGraphDone 图游标操作
recovery-supervisor.ts RecoverySupervisor L2 状态机(触发 takeover)

9. V1 约束与未来扩展

9.1 V1 明确不做的事

  1. 不做 LLM 重规划:模板图完全由 buildGraph 规则驱动,不调用 LLM
  2. 不做语义分析:applyKnownProgress 只看文件存在性和测试结果,不读代码内容
  3. 不做部分完成判定:节点要么全 done 要么全 pending,无中间状态
  4. 不区分 deleted:filesDeleted 恒空,不影响回填逻辑
  5. 不修改图结构:只改节点 status 和游标,不增删节点或边

9.2 V2 潜在扩展方向

扩展点 说明 影响文件
语义回填 用 LLM 分析 diff,判断 edit 节点是否实质完成 retrospective-graph-builder.ts
部分完成 引入 partial 状态,支持节点粒度更细的回填 types/task-graph.ts
动态权重 根据任务域调整 applyKnownProgress 的判定条件 snapshot-confidence-evaluator.ts
多分支回填 回填时考虑 fallback branch 的完成情况 task-graph.ts
快照语义摘要 WorkspaceSnapshot 增加 semanticSummary 字段 types/supervisor.ts

10. 调试与观测

10.1 Timeline 事件

反构图过程会在 EventTimeline 中留下以下事件:

| 事件类型 | 触发点 | 关键字段 |
|---------|-----------------------------|-----------------------------------|-------------------|------------------------------|
| recover | runRecoveryMainPath 入口 | tier: 'template_graph' | 'strong_hint' |
| failure | M6 置信度不足 / M7 不安全 / M8 构图失败 | reason: 'snapshot_confidence_low' | 'recovery_unsafe' | 'retrospective_build_failed' |
| switch | executionMode 切换 | from: 'free' -> 'forced' |

10.2 关键日志点

在 retrospective-graph-builder.ts 中,以下位置是调试热点:

  1. buildGraph 返回空图 → 检查 goal / intent 是否匹配模板库
  2. applyKnownProgress 标记 0 个节点 → 检查 snapshot.filesAdded / testSummary
  3. advanceCursorPastDone 游标未推进 → 检查 graph.cursor.nodeIndex 初始值
  4. replaceGraph 后图状态异常 → 检查 graphExecutor.graph 是否被正确替换

11. 总结

反构图是 iceCoder 双模机制中「弹性信任」理念的集中体现:

  1. 不预置全流程:首轮不 init 图,让模型自由探索
  2. 偏离才接管:通过 RecoverySupervisor 三条件判定是否需要 takeover
  3. 状态优先恢复:接管时用 WorkspaceSnapshot 回填已完成工作,避免重复劳动
  4. 安全降级:快照置信度不足或工作区不安全时,不强行构图,降级为强提示

其核心数据结构流转为:

scss 复制代码
WorkspaceSnapshot
  → SnapshotConfidence (≥0.65?)
  → RecoverySafety (recoverable?)
  → buildGraph (模板图)
  → applyKnownProgress (回填 done)
  → advanceCursorPastDone (游标推进)
  → replaceGraph (注入执行器)

整个链路不依赖 LLM,纯规则驱动,保证了接管过程的确定性和可观测性。

相关推荐
Filwaod2 小时前
MCP 接入模式对比:Agent - Gateway - 业务项目 vs Agent - Adapter - 业务项目
java·agent·mcp
明月(Alioo)3 小时前
为什么用 Skill 做需求澄清
人工智能·ai·agent
Jing_jing_X3 小时前
我做了一个 Agent Learning Lab:把 AI 应用开发过程做成白盒实验台
ai·agent·个人开发·ai应用开发
视觉&物联智能4 小时前
【杂谈】-人类写作渐趋人工智能化
人工智能·ai·aigc·agent·agi
jonyleek4 小时前
AI与现有系统“两张皮”:如何无缝集成、快速落地?
人工智能·ai·agent·jvs·ai套件·jvs-ai套件
沐籽李5 小时前
从问答到执行:Biomni 如何重构生物医学研究工作流
数据库·agent·aidd·抗体设计·biomni
love530love6 小时前
Hermes-Agent 本地化部署与详细交互式配置实战指南 [LM Studio + QQ ]
人工智能·windows·python·aigc·agent·hermes·hermes-agent
天涯明月19936 小时前
vibe-coding核心方法论
人工智能·大模型·agent·研发流程