尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

GraphEngineering:从Loop到Graph的14步实践指南

GraphEngineering:从Loop到Graph的14步实践指南 7月初OpenClaw 创始人在 X 上说我们还在聊 loop还是已经切到 graph 了没过多久就有人跟帖喊出「Loop Engineering 已死Graph Engineering 永生」。所以 Graph Engineering 到底是什么我们把 Codez 总结的 14 步整理了出来全网570w人看过讲的就是怎么从一个人的 loop走到一张能自己路由的 graph。动手前四个问题决定你要不要 Graph EngineeringGraph 不是 loop 的升级版是 loop 的组织方式。它烧的 token 比单个 loop 更多协调开销也更高出了问题你要 debug 的是一整张你没亲眼看着跑的路由图。所以先问自己四个问题都想清楚之后再动手。一、这个任务真的能拆成不同角色吗拆不出清晰的谁负责什么那就还是一个 loop加节点只是加成本。二、有没有真正能并行的子任务没有独立并行的活图比循环贵却不比循环快。三、单个 agent 的上下文装得下全部背景吗装得下就别急着拆拆分是为了腾出上下文不是为了好看。四、失败之后你负担得起跳转分支的成本吗没想清楚重试耗尽后去哪图会在你看不见的地方卡死或者乱跑。还有个附加题比上面四个都重要你已经有一个跑得稳的单体 loop 了吗没有先别建图。图是循环的组织方式不是循环的替代品。谁适合上手已经把至少一个 loop 跑稳的团队任务里有明确能拆开的角色调研、撰写、复核也接受更高的 token 成本换质量和并行度。谁不适合上手还没让一个 loop 稳定跑起来的个人开发者、线性依赖强拆不开的任务、瓶颈在协调开销而不在单节点能力的团队。所以graph engineering 真有用但大部分人现在还用不上因为大部分人的 loop 都还没跑稳更别提图。So do you actually need a graph?01Task splits intodistinct rolesNot just re-run,02Real parallelsub-tasks existFan-out pays off03Context wontfit one agentSplitting frees space04Failure routingis affordableRetries cost even idleAnswer yes to all fourBuild the graphMiss one → keep it a loop先答完四个问题再决定要不要建图Graph Engineering 的四个核心构件一张能跑的图拆开来看就是四个能各自单独验证的部分。Nodes图的最小单位。一个节点就是一个跑着自己 loop 的 agent 或确定性步骤只认一件事也只对一件事负责。节点判断不出完了就不是节点是隐藏依赖。Edges决定谁接下一棒。顺序边永远触发条件边看检查结果并行边一次分给多个节点再汇到一个节点合并。边留给模型运行时判断得越多图越灵活但也越难预判。Shared State大家都要读写的那份数据。下游节点要用的字段必须有上游节点写进去。这个对象会逼你承认这活儿里到底还有多少环节没被真正想清楚。Failure Routing失败之后的退路。一个节点的重试耗尽了控制权去哪退回上一步、转给备用节点还是转人工。没有失败边的图只是一张流程图不是一个能跑的系统。第一步先分清节点和边01 节点是任务边负责传数据一张图其实只有两样东西搞清楚这两样大部分困惑就没了。节点是一个工作单元一个 agent一份边界明确的活一个输入一个输出。边是一种依赖关系它表示这个节点的输出会喂给那个节点做输入仅此而已。最容易犯的错是把然后当成边。总结这个文件然后告诉我天气这两步之间没有边天气根本用不到那份总结。这其实是两个互不相干的节点被一段线性脚本硬凑成了先后顺序。没真用上数据就没有边。02 你的线性脚本其实是一张退化的图当你把一个 agent 写成先做 A再做 B再做 C再做 D其实你已经画出了一张图只不过是一条不分岔的单链每个节点都只有一条边进、一条边出。这样跑是能跑对的但慢也脆因为一条链没有任何冗余C 卡住了D 就永远轮不到A 的产出也被困在上游没地方去。图工程的第一项真本事是重新画这条链。拿着这个线性 agent对每一根箭头问上一步的那个问题。大多数链里都有两三根箭头根本没有携带数据它们只是当初写的时候顺手打的顺序。把这些箭头剪掉链就会塌缩成更宽的东西几个可以同时跑的独立节点喂给一个需要它们全部到齐的节点。第二步给节点和边定契约03 给每个节点定一份契约一个你没法推理的节点就没法拿去并行。解决办法是给它定一份契约输入有边界输出有边界只干一件事。输入是节点要读的东西必须显式传进去不能指望它从共享窗口里蹭到什么。输出是一个定义好的形状最好能校验这样下一个节点不用猜也能直接用。在 workflow 里这份契约靠 schema 强制执行。给 Claude 的 agent() 调用配一份 JSON schemaClaude 派出去的 subagent 就只能返回校验过的结构化数据校验发生在工具调用这一层格式不对 Claude 会自己重试不会甩给你一堆自由文本让你自己解析、自己祈祷。这就是能接进图里的节点和只有人读得懂才行的节点的区别。// 一个有真契约的节点输入有边界输出经过校验只干一件事const ITEM {type: object, additionalProperties: false,properties: {title: { type: string },url: { type: string },impact: { type: string, enum: [high, medium, low] },},required: [title, url, impact],};const result await agent(source.prompt, {label: research:${source.key},schema: ITEM, // 强制返回校验过的结构化数据agentType: general-purpose,});// result 现在是下一个节点能信任的形状不用再靠人工解析04 把边也当成一份数据契约边不只是B 排在 A 后面它是一个关于传的是什么的承诺A 产出这个形状B 就是照着这个形状设计来消费它的。按数据给边命名而不是按顺序命名两件事会立刻变简单能一眼看出这条边是不是真的存在有没有数据真的传过去也能在形状不变的前提下换掉边两端的节点不会弄坏整张图。实际写的时候边就活在普通的 JavaScript 里。派活和合成之间那一步归约压平、去重、过滤就是代码在处理节点返回的形状不需要 agent。图思维一个不太起眼但很重要的收获是很多人花模型 token 去做的事其实就是一条边而边是免费的。第三步构建扇出、扇入与菱形最常用的拓扑05 用 parallel() 扇出把活儿一次性派出去这一步能把前面的投入都赚回来。手上有 N 个独立节点N 个要核实的信源、N 个要审的文件、N 条要查的路由不要把它们串起来跑让 Claude 把它们一次性派出去一起跑。在 workflow 里对应的是 parallel()Claude 拿到一个函数数组给每个函数派一个 subagent全部并发执行最后把结果数组一次性还给你。有两个细节决定它稳不稳。第一parallel() 是一道屏障会等所有函数都跑完才返回下一阶段看到的是完整的结果集合。第二一个抛错的函数会被解析成 null而不是拖垮整个批次一个状态不好的 agent 也拖累不了其他人记得对结果做 .filter(Boolean)。并发数大致按核数封顶多出来的会排队扔进去一百个函数它们都会跑完只是每次跑一小批。phase(Research);// 九个信源九个 agent同时开工const raw await parallel(SOURCES.map((s) () agent(s.prompt, {label: research:${s.key},phase: Research,schema: ITEM_SCHEMA, // 每个节点都返回校验过的 JSONagentType: general-purpose,}),),);const collected raw.filter(Boolean); // 把失败 agent 留下的 null 过滤掉派活这一步是活在 Claude 写的代码里不是活在一轮模型对话里。Claude 自己的上下文从来不会同时装着九个信源每个 subagent 带着自己的一份只有最终答案会传回来。这就是 Claude 能把一次 workflow 扩展到几十上百个 subagent、却不会把这次会话淹没的原因编排这一层不花 token因为它不是 Claude 又想了一轮。06 在屏障处做汇入活儿派出去得有东西能收拢它才有意义。拢回来的这个节点就是边汇聚的地方一个 agent或一段代码一次性看到全部上游结果去做一件必须看到全集才能做的事比如跨信源去重、按影响力排序、总数为零就提前退出。这是整张图里唯一值得让屏障付出等待成本的地方。// 这条边就是普通 JS没有 agent零 tokenconst flat collected.flatMap((c) c.items);log(Collected ${flat.length} items);phase(Curate);// 这个屏障节点需要全部结果凑齐才能去重排序const curated await agent(Dedupe and rank these by impact:\n${JSON.stringify(flat)},{ phase: Curate, schema: CURATED_SCHEMA },);只是把一个列表压平那是一条边直接写在行内就好。判断方法很简单粗暴如果写成了 parallel → transform → parallel中间那个 transform 又没有跨条目的依赖那本该用流水线完全不需要屏障。07 菱形拆分 → 工作 → 合并把派出去和拢回来拼在一起就得到了几乎每张正经 agent 图里都会出现的主力拓扑菱形。一个节点拆任务多个节点并行干活一个节点合并。市场扫描、依赖审计、代码评审、研究报告背后都是这个形状换个信源和提示词同一副骨架照样能用。它的标准写法有个值得记住的名字派发 → 归约 → 合成。先派出去收集广度用普通代码归约压缩再用最后一个 agent 合成写出答案。看懂这颗菱形之后就不会再问怎么让 agent 多做几步而是会问拆分点在哪合并点在哪这才是真正能扩展的问题。第四步路由、验证与隔离08 用条件语句在运行时给边选路不是每张图都是固定的。有时候走哪条边取决于某个节点发现了什么。路由节点检查结果决定走哪条下游路径给工单分类后分流到对应处理节点或者看 diff 大小决定走快速评审还是完整审计。在 workflow 里这就是一个普通的 if 或 switch判断依据是某个节点校验过的输出因为控制流本来就活在代码里。这正好是确定性变成优点而不是限制的地方。路由的判断可以由 Claude 完成一个 subagent 负责分类但路由本身是 Claude 写的代码同样的分类结果每次都走同一条路。节点上拿到 Claude 的判断力边上拿到脚本的可靠性不会出现Claude 自己决定跳过审计这种意外因为跳过这件事必须写进图里才会发生而它没有被写进去。// 路由节点agent 负责分类代码负责选边const { severity } await agent(Classify this diffs risk:\n${diff},{ schema: { type: object,properties: { severity: { enum: [low, high] } },required: [severity] } },);let review;if (severity high) {// 高风险路径完整并行审计review await parallel(FILES.map((f) () agent(Audit ${f})));} else {// 低风险路径一次快速评审review await agent(Quick review of ${diff});}09 在边上放一个验证器一张图真正的杠杆不是塞了更多 agent而是能围绕结果搭起多少确定性。验证器节点蹲在一个结果被放行到下游之前它唯一的工作就是试图推翻这个发现。扛住了就放行扛不住就到不了最终答案。三种模式值得掌握对抗式验证给每个发现派 N 个独立的怀疑者专门去反驳它多数没被驳倒才算站得住。多视角验证让每个验证者盯不同的方面正确性、安全性、能不能复现角度越分散越能揪出 N 个一样的检查都发现不了的问题。评委制从不同角度生成 N 个方案用并行的评委打分挑最好的一版作为主线再把其他几版里的亮点揉进去。真实团队在移植 Bun 运行时的时候就是靠这套对抗式代码评审焊进循环才做成的。10 把节点隔离开别让一个失败污染整张图在一条链里失败会级联C 死了D 就跑不起来整条链停摆。在一张图里失败本该被限制在它自己的节点里。这一点已经部分成立parallel() 里一个抛错的函数会被解析成 null八个正常的 agent 照样能返回结果一个坏的自己掉队.filter(Boolean) 就是那道防线。把每一次汇入都设计成能容忍缺失的输入而不是假设总能凑齐全集。更隐蔽的失败是节点之间互相踩到对方。多个 agent 并行写文件时可能会撞车解法是隔离用 git worktree让每个 agent 在自己的一份工作区里干活在沙盒里完成再干净地合并回去。只在节点真的会并行写入的时候才用它它是那种拓扑真正需要的安全带不是每次运行都要交的税。第五步循环、模型分层与拓扑11 可以加一个循环但一定要让它收敛要是压根不知道这活儿有多大呢只有真的做下去才知道规模未知的探索一次漏洞排查发现一个 bug 又带出三个新的。这时候需要一个循环一条指回更早节点的、受控的边。危险也很明显一个不收敛的循环就是一台不停派 agent 出去、直到预算耗尽才停的死循环机。能收敛的写法叫跑到干为止持续派出发现者直到连续 K 轮都没发现新东西才停下来。真正决定成败的细节也是几乎每个人第一次都会踩的坑是拿什么去做去重比对。要对着见过的一切去重而不是只对着已确认的结果去重。不然被否掉的发现每一轮都会重新冒出来循环永远跑不干最后搭出的是一台专门花钱去反复发现同一批死胡同的机器。const seen new Set(); const confirmed []; let dry 0;while (dry 2) { // 连续两轮空手而归就停下const found (await parallel(FINDERS.map((f) () agent(f.prompt, { schema: BUGS })))).filter(Boolean).flatMap((r) r.bugs);const fresh found.filter((b) !seen.has(key(b)));if (!fresh.length) { dry; continue; }dry 0;fresh.forEach((b) seen.add(key(b))); // 对见过的一切去重不是只对已确认的// 每个新发现都要过一轮多视角验证才算数const judged await parallel(fresh.map((b) () parallel([correctness, security, repro].map((lens) () agent(Judge ${b.desc} via ${lens} — real?, { schema: VERDICT }))).then((v) ({ b, real: v.filter(Boolean).filter((x) x.real).length 2 }))));confirmed.push(...judged.filter((v) v.real).map((v) v.b));}12 给不同节点分配不同档位的模型不是每个节点都需要最好的模型。一张图会用单个 agent 永远做不到的方式把这件事摆明白有些节点干的是有边界、会重复的活比如抽取一个字段、给工单分类有些节点承载真正的判断力比如合成报告、裁定一个发现是否成立。干重复活的节点放到便宜模型上跑token 留着花在真正需要判断力的地方。在 workflow 里Claude 派出去的每个 subagent 默认继承这次会话的模型除非脚本里显式覆盖所以默认情况下一次大规模运行的账单会全按会话档位算。单次 agent() 调用上的 model 选项能让 Claude 单独把这一个节点换到别的模型上跑。大规模运行前先看一眼 /model让 Claude 把派出去的那些重复性节点降到便宜模型合并节点留在高档位这个办法能把一张烧 token 的图从贵变便宜还完全不用动它的形状。13 拓扑结构就是你的成本和延迟图的形状不是装饰它是决定运行时间的最大杠杆。最容易踩坑的选择是 parallel() 还是 pipeline()。parallel() 这道屏障会让所有东西都等最慢的那个节点才进入下一阶段。pipeline() 让每条数据各自独立地依次经过所有阶段没有屏障条目 A 可能已经在第三阶段了条目 B 还在第一阶段跑得快的提前结束不用在慢的后面干等。默认用 pipeline()。只有一个阶段真的需要全部前置结果同时到齐时才用屏障比如跨集合去重、按总数提前退出、需要对照其他发现来写的 prompt。代码更干净和这些阶段感觉是分开的都不是理由屏障带来的延迟是真实的、可测量的、被浪费掉的时间分开不代表必须同步。最后一步让 Claude 自己画图14 让 Claude 自己画图自我路由最后一步是对那些没法提前规划的活儿不再自己动手画图。用 dynamic workflows只要描述目标Claude 会自己写编排脚本拆解任务、决定怎么把活儿派出去、派出一队 subagent、再合成结果。拿到的是一张为这次运行量身定做的图而不是一张你希望它恰好合适的固定图。有三种用法。在 prompt 里说出workflow这个词Claude 就会为这个任务写一份。跑一个已经存好或内置的比如 /deep-research就是一张已经在生产里跑着的真实图定范围 → 并行搜索 → 抓取 → 对抗式验证 → 合成正是这门课从头到尾讲的那副骨架。或者打开 ultracodeClaude 会给这次会话里每个像样的任务都规划一次 workflow。跑得好的时候按 s 把脚本存进 .claude/workflows/从此可以版本控制、按名字重新运行谁 clone 了这个仓库都能直接跑起来。› Run a workflow to audit every route under src/routes/ for missingauth. Spawn one agent per route file, then verify each finding beforereporting.● Claude wrote an orchestration script · launching in background…/workflows — auth-audit · running ✓Scope 1/1 2.1k tokFan-out 18/18 one agent per route fileVerify 11/18 3-vote skeptics per finding…Synthesize 0/1 waiting on verify// 会话保持响应队伍在后台继续跑两年来多 agent 协作的杠杆一直在单个 loop 上更好的 verifier更稳的退出条件更干净的状态文件。而现在把这些 loop 怎么连起来成了新的护城河。
返回列表