Graph Engineering with Claude_ 14-Step roadmap from 0 to graph architect (Full Course).diff-with-graphlemon.md 12.7 KB

对照笔记:《Graph Engineering with Claude: 14-Step roadmap》vs graphlemon 实际代码

  • 对照对象:Graph Engineering with Claude_ 14-Step roadmap from 0 to graph architect (Full Course).md(本文简称"14 步版")
  • 和已有对照的关系:docs/graphlemon_architecture.md §10 在 2026-07-29(设计阶段,尚未开始实现) 就对 14 步版做过一轮逐条映射,并在 §10.1 决策"Work Graph 执行层直接复用 Claude Code 原生动态工作流"。当时是设计意图,不是已验证的实现。本笔记的定位不同:现在全部 43 个 Unit 已实现并自举运行,所以这次是拿 14 步版去对照 src/work_graph_orchestrate.py、src/work_graph_execute.py、src/work_graph_plan.py 的真实代码,检验当初 §10.1 的设计意图有多少真的落地、有多少在实现过程中被推翻或修正。
  • 状态:讨论草稿,待后续会话继续展开。
  • 整理日期:2026-08-04

0. headline:Work Graph 执行层并没有真的用上 Claude 原生 parallel()/pipeline()

这是本次对照发现的最重要的一条,也是对 §10.1 原决策的一处实质性修正,值得放在最前面单独讲清楚。

src/work_graph_orchestrate.py 的模块 docstring 直接写明了这个偏离,是有意为之:

Design: each node's actual work happens via its OWN one-node dynamic workflow script (write_workflow_script + execute_work_graph), not one big multi-node script for the whole graph. This is a deliberate trade-off, not the "amortize orchestration cost across many nodes in one claude -p call" pattern 03-C §3.5 originally motivated execute_work_graph with — REQ-06's breakpoint-recovery requirement (resume after a process restart without re-running already-done nodes) is only achievable with per-node, Python-level persisted state, which is fundamentally incompatible with wrapping the WHOLE graph in one opaque claude subprocess call Python has no visibility into mid-flight.

再看 run_graph() 的实际实现(work_graph_orchestrate.py:196-270):

ordered = topological_order(graph_manifest)   # Kahn 算法,拒绝有环图
outputs = {}
for node in ordered:                          # 纯 Python 顺序 for 循环
    ...
    run_fn = _make_run_fn(...)                 # 每个节点单独渲染成一个"单节点"JS 脚本
    output = work_node_run.run_work_node(...)  # 一次一个节点地跑,串行
    outputs[node_id] = output

也就是说:

  • graph_manifest 里没有依赖关系的两个节点(本该 fan-out 并行)也是一个跑完再跑下一个,没有任何线程池/并发调度。14 步版第 5 步 parallel() 描述的"N 个独立节点同时起飞"在 graphlemon 现在的执行引擎里完全没有发生。
  • 每个节点被单独渲染成一个只有一次 agent() 调用的"单节点"JS 脚本(_render_node_script),再通过 write_workflow_script + execute_work_graph(一次独立的 claude -p "Run /<script>" 子进程)跑掉。Claude Code 原生动态工作流引擎在这里只是被当作"带 schema 校验的单次 agent 调用"的壳,14 步版真正的价值——同一个脚本里 parallel()/pipeline() 编排一整批子智能体、编排本身零 token——完全没被用上。
  • 原因写得很诚实:这是为了换取 REQ-06 的断点续跑能力。如果把整个 Work Graph 塞进一次 claude 子进程调用(14 步版和 §10.1 原方案设想的做法),Python 侧在执行过程中对每个节点的完成状态完全不可见,进程一旦重启就没法"从已完成的节点之后继续",只能整图重跑。graphlemon 选择了牺牲并行度换取可恢复性。

对 §10.1 的修正:架构文档 §10.1 说"Work Graph 内部的 fan-out/fan-in/loop/verifier 直接用 Claude Code 写的 JS 编排脚本执行"——这句话在真实代码里没有成立。真正发生的是:graphlemon 自己的 Python 代码(work_graph_orchestrate.py)承担了图的拓扑调度(DAG 排序、逐节点执行、状态持久化),Claude 原生动态工作流退化成了"给单个节点调用套一层 schema 校验壳"的工具,而不是图执行引擎本身。这个决策本身未必是错的(断点续跑对 7×24 黑灯工厂是硬需求,14 步版也没有讨论进程重启这个场景),但它意味着 14 步版里跟"图内部拓扑"相关的第 5/6/7/11/12/13 条技法,在 graphlemon 目前的 Work Graph 执行层里基本都不适用或未生效,详见下表。

1. 逐条对照表

# 14 步版技法 graphlemon 现状 依据
1 节点是工作,边是数据流转 ✅ 概念对应到位:build_prompt(node, outputs) 把已完成节点的 output_payload 显式传给依赖它的下游节点提示词,拓扑序保证依赖已就绪 work_graph_orchestrate.py::run_graph
2 线性脚本是退化的图 🔶 反例现状:目前观察到的真实 Planner 运行(如任务 43dc7333)里 graph_manifest 就是 n_brd → n_prd → n_sdd → n_benchmark 的纯单链,架构文档 §6.1 设想的 PRD/SDD/测试用例 fan-out 候选点没有被 Planning 阶段实际生成——目前跑出来的就是 14 步版说的"退化的图" 本会话早前查询该任务的 work_graph_runs[0].graph_manifest
3 给每个节点一个契约(schema 强制) ✅ 已实现:每个 Work Node 都有 input_schema_ref/output_schema_ref,execute_work_graph 通过 --json-schema 让 Claude Code 在工具调用层强制校验单节点输出 work_graph_orchestrate.py::_ensure_node_row;work_graph_execute.py::execute_work_graph
4 边是数据契约,简单聚合应零 token ⚪ 不适用:由于没有真正的 fan-out,也就没有需要"聚合"的多分支结果——这条原则目前在 graphlemon 里没有用武之地 同上(无 fan-in 场景)
5 parallel() 扇出 ❌ 未实现:见第 0 节,run_graph 是纯顺序 for 循环,即使 graph_manifest 里两个节点互不依赖也不会并发执行 work_graph_orchestrate.py:248
6 parallel() 是屏障,仅在需要全量结果时用 ⚪ 不适用:没有并行分支,自然没有"要不要设屏障"的选择 同上
7 菱形拓扑:split→work→merge 🔶 仅存在于架构文档的设计意图(§6.1 fan-out 候选点、§6.2 Coder 模块级并行),未见于当前执行引擎产出的真实 graph_manifest 架构文档 §6.1/§6.2 vs 实际 run_graph 实现
8 路由节点:智能体分类,代码选边 ✅ 但只在 Org Graph 层成立:Validator 打 `route: fast_lane\ full_replan,org_loop_route.py用NEW_STATUS/NEW_ORG_NODE查表纯代码路由。**Work Graph 内部**(单次rungraph调用里)没有条件分支能力——topologicalorder` 是静态 DAG,不支持运行时按结果选边
9 Verifier 三种打法(对抗式/多视角/评审团) 🔶 部分实现:root_cause_trace.judge_root_cause 是"多视角差异化校验"的实现(正确性/需求完整性/Skill 缺陷三视角,≥2/3 一致才定案),但只用在 Validator 的 bug 根因溯源场景。run_graph 自身的 verify_fn 是单一验证器 + 失败重试一次,不是多视角/对抗式/评审团模式 root_cause_trace.py;work_graph_orchestrate.py::_verify_and_maybe_regenerate
10 worktree 节点隔离 ✅ 已实现,且比 14 步版描述的更严格:self-target 场景无条件强制 worktree(不只是"并行写入时才需要") work_node_run.py;CLAUDE.md「Isolation」
11 循环收敛:loop-until-dry ❌ 未实现,且结构上不可能在单次 run_graph 内实现:topological_order 对有环的 graph_manifest 直接抛异常("graph_manifest has a dependency cycle")——graphlemon 的 Work Graph 被设计成严格 DAG,不支持图内回路。收敛/重试目前只发生在两个更粗的粒度:Org-level Loop(固定 3 轮上限,跨 Org Graph)和 verify_fn 的单节点重试一次 work_graph_orchestrate.py::topological_order;docs/sdd/04-P-流程与状态流转.md
12 按节点分层选模型 🔶 管线已打通但未接线:_render_node_script 确实支持往生成的 JS 脚本里塞 model 选项,run_fn(cwd, model=None) 参数也在,但没有任何调用方真正传入非 None 的 model——graph_manifest 的节点结构里也没有 model 字段。基础设施就位,缺的是"谁来决定每个节点该用哪个模型"这层策略 work_graph_orchestrate.py::_render_node_script/_make_run_fn;agent_profile.py(Org Agent 固定 claude-sonnet-5)
13 拓扑即成本延迟(pipeline 优先于 parallel) ⚪ 不适用:这条讨论的是"该用 parallel() 还是 pipeline()",但 graphlemon 现在两个都没用上,是第三种情况——Python 层顺序调度,不存在这个选择 同第 0 节
14 让 Claude 自己画图(self-routing / 动态工作流) 🔶 只在"规划"层成立,"执行"层不成立:plan_work_graph() 确实是让 Claude 动态生成本次任务专属的 graph_manifest(节点+依赖,"画图"这件事本身是 Claude 做的),但落地执行时不是交给 Claude 自己写的 agent()/parallel()/pipeline() 编排脚本去跑——是 graphlemon 自己的 Python run_graph() 拿着这份"图纸"逐节点手动施工 work_graph_plan.py::plan_work_graph(画图)vs work_graph_orchestrate.py::run_graph(施工)

2. 这次对照带来的认知更新

  • 上一份对照笔记(5 步版)里"Work Graph 层直接用 Claude Code 原生动态工作流,直接拿到零 token 编排"这句话需要修正:这是 §10.1 的设计意图和已验证的技术可行性(spike 确认了单次 agent()/pipeline() 脚本能被 claude -p 非交互调用),但生产代码选择了不这么用,原因是断点续跑的硬需求和"整图塞进一次不透明子进程调用"根本冲突。这不是疏漏,work_graph_orchestrate.py 的 docstring 把权衡写得很清楚,但确实是本轮对照之前没有校准准确的一点。
  • "退化的图"不是历史遗留,是当前生产状态:14 步版第 2 步讲的"你的线性脚本已经是一张图了,只是最惨的那种单链"——这句话现在精确描述的是 graphlemon 真实跑出来的 Planner Work Graph(BRD→PRD→SDD→Benchmark 纯串行),而不是一个已经被超越的反面教材。架构文档 §6.1 设想的并行候选点目前停留在文档层面。
  • loop-until-dry 不是"优先级不够高还没做",而是当前 DAG 执行模型结构性不支持:之前那份对照笔记(5 步版)把它归类为"已知简化项/未来优化候选",这次深入代码后发现更准确的说法是——要支持它,work_graph_orchestrate.py 得先支持图内有环(topological_order 目前直接拒绝),这是比"加一个循环判断逻辑"大得多的变更,涉及整个执行模型。

3. 待讨论的开放问题(供下一轮会话展开)

  • [ ] 现在的"每节点一次独立 claude -p 子进程"设计,断点续跑收益是否值得放弃 fan-out 并行?如果要两者兼得,是否可以在 Python 层(而非 Claude JS 层)用线程池并行执行 graph_manifest 里同一拓扑层级(同一批无依赖节点)的多个节点,同时仍然保留逐节点的持久化状态?这样能同时保住 REQ-06 和 14 步版的并行收益,不需要依赖 Claude 的 parallel()。
  • [ ] Planning Skill(plan_work_graph 的 prompt,work_graph_plan.py:67 附近)目前只要求"一个 artifact_type 一个节点",没有引导 Claude 去识别哪些节点其实互不依赖、可以并行——要不要把 14 步版第 1/2 步"对每个箭头问是否真的有数据流动"这个提问显式加进 Planning Skill 的提示词,让它在生成 graph_manifest 阶段就标注可并行的节点组,即使执行层暂时还是串行,至少数据模型上先把"这两个节点无依赖"的信息留出来?
  • [ ] _render_node_script 已经预留了 model 参数但没人用——按节点选模型这层策略该由谁决定?是 Planning Skill 在生成 graph_manifest 时就给每个节点标注推荐模型,还是在 run_graph 里按 org_node/artifact_type 写一张静态映射表?
  • [ ] root_cause_trace 的三视角评审团目前只服务于 Validator 的 bug 归因,run_graph 的 verify_fn 是单一验证器——要不要把三视角评审团模式也下沉到 verify_fn 里,作为高风险节点(比如 SDD、代码变更)的可选验证强度?