使用 Claude 進行圖形工程:從 0 到圖形架構師的 14 步路線圖

@0xCodez
英語1 天前 · 2026年7月20日
410K
947
151
37
2.4K

TL;DR

本綜合課程教授如何從線性 AI 提示詞轉向複雜的基於圖形的 Agent 架構。課程詳細介紹了透過平行化與動態編排來優化 Claude 工作流程的 14 個步驟。

大多數嘗試建立多步驟 Agent 的人,最終都只畫出一條直線。第一步、第二步、第三步——每一步都乖乖等待上一步完成才開始。

10 個人裡有 9 個會發現,那些步驟裡有一半根本不需要等待。

它們不會路由。不會分支。不會平行化。只是排隊——一個腦袋、一個上下文、一次一件事,直到視窗塞滿,Agent 忘記自己正在做什麼。

追蹤我的 Substack 以獲取最新的 AI 資訊:

movez.substack.com

這是一份 14 步驟的路線圖,能將那條單一檔案排隊線轉變為一個圖形:一個能分散到整個 Agent 群、驗證自身發現、並收斂出單一 Agent 永遠無法掌握的結果的圖形。

Codez - inline image

這裡有一個沒人明說的轉變。提示詞是一個句子。迴圈是一個週期。框架是 Agent 立足的地板。

工作本身的形狀——什麼先執行、什麼可以同時執行、什麼必須等待其他所有事情——這個形狀就是一個圖形。節點負責思考。邊負責傳遞結果。

Claude Code 提供了直接建立這些圖形的工具:動態工作流程

Claude 會撰寫一個純 JavaScript 的編排腳本,然後生成一個協調的子 Agent 群來執行它——而協調本身不消耗任何模型 token,因為它是程式碼,而不是對話。

01. 節點是任務。邊是 流動 的東西。

一個圖形只有兩樣東西,搞清楚它們就能解決大部分困惑。一個節點是一個工作單元——一個 Agent、一個有邊界的任務、一個輸入和一個輸出。

一條是一個依賴關係:它表示這個節點的輸出會餵給那個節點的輸入。僅此而已。

Codez - inline image

常見的錯誤是把「然後」當作一條邊。「總結這個檔案,然後告訴我天氣」這兩者之間沒有邊——天氣並不會消費那個摘要。

那是兩個不相關的節點,被一個線性腳本不必要地串聯起來。邊只存在於資料實際流經的地方。

學會對 Agent 中的每個「然後」提問:下一步驟會讀取上一步驟的輸出嗎? 如果不會,就沒有邊,等待就是浪費。

python
1把它畫成方塊和箭頭。一個方塊就是一個 agent() 呼叫。
2一條箭頭就是從一個呼叫的返回值傳遞到另一個呼叫的
3提示詞中的變數。如果你畫不出那條箭頭——如果沒有變數跨越——
4那兩個方塊就是獨立的,而獨立性正是你在本課程後續部分
5將要利用的東西。

02. 你的線性腳本是一個 退化 的圖形

當你把一個 Agent 寫成「做 A,然後 B,然後 C,然後 D」時,你已經畫出了一個圖形——一條單一、不分枝的鏈條。每個節點恰好有一條邊進入和一條邊出去。

它執行起來是正確的。但它也執行得緩慢且脆弱,因為鏈條沒有冗餘:如果 C 停滯了,D 永遠不會發生,A 的工作就被困在上游,無處可去。

Codez - inline image

圖形工程的第一個真正技能是重新繪製這條鏈條。拿起你的線性 Agent,對每一條箭頭,問第 1 步的那個問題。

大多數鏈條都有兩三條不攜帶資料的箭頭——它們只是你碰巧輸入事物的順序。

砍掉那些箭頭,鏈條就會崩解成更寬廣的東西:幾個可以同時執行的獨立節點,餵給一個需要它們全部結果的單一節點。

03. 給每個節點一個 合約

一個你無法推理的節點,就是一個你無法平行化的節點。解決方案是合約:有邊界的輸入、有邊界的輸出、剛好一個任務。

輸入是節點讀取的任何東西——明確地傳入,絕不從共享的視窗中假設。輸出是一個定義好的形狀,最好經過驗證,這樣下一個節點就可以直接消費它,無需猜測。

Codez - inline image

在工作流程中,這個合約透過一個結構描述來強制執行。當你交給 Claude 一個帶有 JSON 結構描述的 agent() 呼叫時,它所生成的子 Agent Claude 會被強制回傳經過驗證的結構化資料——驗證發生在工具呼叫層,所以 Claude 會在資料不符時重試,而不是交給你需要解析和祈禱的自由文字。

這就是一個 Claude 可以連接到圖形中的節點,與一個只有人類閱讀其輸出才能運作的節點之間的區別。

python
1// 一個有真正合約的節點:有邊界的輸入、驗證過的輸出、單一任務。
2const ITEM = {
3 type: 'object', additionalProperties: false,
4 properties: {
5 title: { type: 'string' },
6 url: { type: 'string' },
7 impact: { type: 'string', enum: ['high', 'medium', 'low'] },
8 },
9 required: ['title', 'url', 'impact'],
10};
11
12const result = await agent(source.prompt, {
13 label: `research:${source.key}`,
14 schema: ITEM, // 強制輸出經過驗證的結構化資料
15 agentType: 'general-purpose',
16});
17// result 現在是下一個節點可以信任的形狀——不再是自由文字。

04. 邊視為資料合約

一條邊不僅僅是「B 在 A 之後」。它是一個關於什麼東西會跨越的承諾:A 產生這個形狀,而 B 被設計來消費這個形狀。當你用它的資料——而不是它的順序——來命名邊時,兩件事會變得更容易。

Codez - inline image

你可以立刻看出這條邊是否真實存在(資料真的有移動嗎?),而且只要形狀保持不變,你就可以交換邊任一端的節點,而不會破壞圖形。

在實務上,邊存在於純 JavaScript 中。在扇出和綜合之間的 reduce 步驟——扁平化、去重、過濾——只是對你的節點回傳的形狀進行操作的程式碼。

不需要 Agent。 圖形思維的一個低調勝利:人們花費大量模型 token 處理的事情,實際上常常只是一條邊,而邊是免費的。

python
1誘惑是生成一個 Agent 來「合併結果」。抵制它。
2如果合併意味著扁平化和去重,那就是 results.flatMap(...)
3和一個 Set —— 確定性、即時、零 token。把 Agent 留給
4判斷,而不是管線。一個每一條邊都是一個 Agent 的圖形,
5是一個在為自己的佈線支付租金的圖形。

05. 使用 parallel() 進行扇出

這是能回報所有投資的關鍵動作。當你有 N 個獨立節點時——N 個要檢查的來源、N 個要審查的檔案、N 條要稽核的路徑——你不會將它們串聯起來。

你告訴 Claude 將它們扇出並同時執行。在工作流程中,這就是 parallel():Claude 接收一個 thunk 陣列,為每個 thunk 生成一個子 Agent,全部同時執行,然後將結果陣列交還給你。

Codez - inline image

兩個細節讓它變得穩健。首先,parallel() 是一個屏障——它會等待每個 thunk 完成才回傳,所以下一個階段會看到完整的集合。其次,一個拋出錯誤的 thunk 會解析為 null,而不是拒絕整個批次,所以一個不穩定的 Agent 不會導致整個執行失敗。

記得總是 .filter(Boolean) 過濾結果。並行數會根據你的核心數量和超額佇列進行限制,所以你可以傳入一百個 thunk,它們最終都會完成——只是一次處理少數幾個。

python
1phase('Research');
2
3// 九個來源,九個 Agent,全部同時進行。
4const raw = await parallel(
5 SOURCES.map((s) => () =>
6 agent(s.prompt, {
7 label: `research:${s.key}`,
8 phase: 'Research',
9 schema: ITEM_SCHEMA, // 每個節點回傳經過驗證的 JSON
10 agentType: 'general-purpose',
11 }),
12 ),
13);
14
15const collected = raw.filter(Boolean); // 丟棄失敗 Agent 的 null 結果

扇出存在於 Claude 編寫的程式碼中,而不是模型對話中。Claude 自己的上下文永遠不會同時容納九個來源——每個子 Agent 攜帶自己的來源,只有最終答案會回傳。

這就是讓 Claude 能將工作流程擴展到數十或數百個子 Agent 而不淹沒會話的原因。編排層花費零 token,因為它不是 Claude 思考的另一輪。

06. 在 屏障 處扇入

扇出只有在有東西能匯集它時才有用。扇入是邊收斂的節點——一個 Agent(或一段程式碼)同時看到所有上游結果,並執行需要整個集合才能完成的事情:跨來源去重、按影響力排序、如果總數為空則提前退出。這是屏障值得花費其牆上時鐘時間的唯一地方。

Codez - inline image

保持圖形快速的規則是:只在一個階段真正需要所有先前的結果在一起時才使用屏障。 跨所有來源去重?屏障——正確。

python
1// 邊:純 JS,沒有 Agent,零 token。
2const flat = collected.flatMap((c) => c.items);
3log(`Collected ${flat.length} items`);
4
5phase('Curate');
6// 屏障節點:需要整個集合來進行去重 + 排序。
7const curated = await agent(
8 `Dedupe and rank these by impact:\n${JSON.stringify(flat)}`,
9 { phase: 'Curate', schema: CURATED_SCHEMA },
10);

只是扁平化一個列表?那是一條邊,直接內聯處理。嗅探測試既殘酷又簡單:如果你寫了 parallel → transform → parallel,而中間的 transform 沒有跨項目的依賴關係,你應該使用管線並完全跳過屏障。

07. 菱形:拆分 → 工作 → 合併

將扇出和扇結合在一起,你就得到了每個嚴肅 Agent 圖形的主力拓撲結構:菱形

一個節點拆分任務,多個節點平行工作,一個節點合併。它是市場掃描、依賴稽核、程式碼審查、研究報告背後的形狀——交換來源和提示詞,同樣的骨架就能適應。

Codez - inline image

其標準形式有一個值得記住的名稱:扇出 → 縮減 → 綜合。 扇出以收集廣度,用純程式碼縮減以壓縮,最後用一個最終 Agent 綜合以撰寫答案。

一旦你看到了菱形,你就不再問「如何讓我的 Agent 做更多步驟」,而是開始問「拆分點在哪裡,合併點在哪裡」——這才是真正能擴展的問題。

08. 使用 條件 在執行時路由邊

並非每個圖形都是固定的。有時要採取的邊取決於一個節點發現了什麼。一個路由器節點會檢查結果,並決定觸發哪個下游路徑——對工單進行分類,然後分支到正確的處理程序;檢查 diff 大小,然後要嘛進行快速審查,要嘛啟動完整的稽核。

在工作流程中,這只是一個基於節點驗證輸出的 JavaScript if 或 switch,因為控制流程存在於程式碼中。

Codez - inline image

這就是確定性成為一個功能,而不是限制的地方。路由器的決策可以由 Claude 驅動(一個子 Agent 進行分類),但路由是 Claude 編寫的程式碼——所以對於相同的分類,它每次都會以相同的方式執行。

你在節點獲得 Claude 的判斷力,在邊獲得腳本的可靠性。不會有突然出現的「Claude 決定跳過稽核」的驚喜——因為跳過必須被寫入圖形中,而它沒有。

python
1// 路由器節點:一個 Agent 進行分類,程式碼選擇邊。
2const { severity } = await agent(
3 `Classify this diff's risk:\n${diff}`,
4 { schema: { type: 'object',
5 properties: { severity: { enum: ['low', 'high'] } },
6 required: ['severity'] } },
7);
8
9let review;
10if (severity === 'high') {
11 // 重路徑:完整的平行稽核
12 review = await parallel(FILES.map((f) => () => agent(`Audit ${f}`)));
13} else {
14 // 輕路徑:一次快速檢查
15 review = await agent(`Quick review of ${diff}`);
16}

09. 在邊上放置一個 驗證器

圖形的真正槓桿作用不在於更多的 Agent——而在於你可以圍繞它們構建以產生信心的結構。

一個驗證器節點位於結果被允許向下游傳遞之前的邊上,它的唯一任務是嘗試推翻這個發現。如果它存活下來,就通過。如果沒有,它就永遠不會到達答案。

Codez - inline image

有三種模式值得掌握。

  • 對抗性驗證: 對於每個發現,生成 N 個被提示要反駁它的獨立懷疑論者;只有在多數存活時才保留它。
  • 多視角驗證: 給每個驗證器一個不同的鏡頭——正確性、安全性、是否可重現——因為多樣效能捕捉到 N 個相同檢查永遠無法發現的失敗模式。
  • 評審團: 從不同角度生成 N 個嘗試,用平行評審員打分,從勝者綜合,同時嫁接亞軍的最佳部分。

這正是讓一個真實團隊在循環中內建對抗性程式碼審查來移植 Bun 執行環境的模式。

10. 隔離節點,使單一失敗無法 汙染 圖形

在鏈條中,失敗會級聯——C 死了,D 永遠不會執行,整個事情停擺。在圖形中,失敗應該被限制在其節點內

這已經部分成立:在 parallel() 內部拋出錯誤的 thunk 會解析為 null,所以八個好的 Agent 仍然會回傳,而一個壞的 Agent 會脫落。你的 .filter(Boolean) 就是這個限制。

設計每個扇入節點時,要讓它能容忍缺失的輸入,而不是假設一個完整的集合。

Codez - inline image

更微妙的失敗是節點互相干擾。當 Agent 平行寫入檔案時,它們可能會衝突。

解決方案是隔離:「工作樹」——每個 Agent 在自己的 git 工作樹中執行,在沙盒中完成工作,然後乾淨地合併。

只在節點實際平行寫入時才使用它。它是針對需要它的特定拓撲結構的安全帶,而不是每個執行的預設稅。

11. 加入一個循環——但要讓它 收斂

有時你直到身在其中才知道任務有多大:未知大小的探索,一個錯誤掃描中發現一個錯誤又揭示了三個。這需要一個循環——一條回到較早節點的可控邊。

危險顯而易見:不收斂的循環就是一個無限迴圈,它會不斷生成 Agent,直到你的預算耗盡。

Codez - inline image

能夠收斂的模式是循環直到乾涸: 持續生成尋找器,直到連續 K 輪沒有發現新東西,然後停止。一個決定成敗的細節——也是幾乎每個人在第一次嘗試時都會犯的錯誤——是你用來去重的對象。

要針對所有見過的東西去重,而不僅僅是針對已確認的結果。否則,被拒絕的發現會在每一輪重新出現,循環永遠不會乾涸,你就建造了一台不斷花錢重新發現相同死胡同的機器。

text
1const seen = new Set(); const confirmed = []; let dry = 0;
2
3while (dry < 2) { // 在 2 輪空結果後停止
4 const found = (await parallel(
5 FINDERS.map((f) => () => agent(f.prompt, { schema: BUGS }))
6 )).filter(Boolean).flatMap((r) => r.bugs);
7
8 const fresh = found.filter((b) => !seen.has(key(b)));
9 if (!fresh.length) { dry++; continue; } // 沒有新東西 → 趨向乾涸
10 dry = 0;
11 fresh.forEach((b) => seen.add(key(b))); // 針對 SEEN 去重,而非 confirmed
12
13 // 在計入之前,用多視角驗證每個新發現
14 const judged = await parallel(fresh.map((b) => () =>
15 parallel(['correctness', 'security', 'repro'].map((lens) => () =>
16 agent(`Judge "${b.desc}" via ${lens} — real?`, { schema: VERDICT })))
17 .then((v) => ({ b, real: v.filter(Boolean).filter((x) => x.real).length >= 2 }))));
18
19 confirmed.push(...judged.filter((v) => v.real).map((v) => v.b));
20}

12. 跨 節點 分層使用模型

並非每個節點都需要你最好的模型。圖形讓這一點變得顯而易見,這是單一 Agent 永遠做不到的:有些節點是有邊界且重複性的(提取這個欄位、分類這個工單),而有些則承載著真正的判斷力(綜合報告、裁定發現)。

在較便宜的模型上執行無聊的節點,並將你昂貴的 token 花在判斷力真正所在的地方。

Codez - inline image

在工作流程中,Claude 生成的每個子 Agent 都會繼承你的會話模型,除非腳本覆蓋它——所以預設情況下,一個大型執行會完全按照你的會話層級計費。單一 agent() 呼叫上的 model 選項告訴 Claude 將該節點路由到別處。

在大型執行前檢查 /model,然後讓 Claude 將扇出的重複性節點路由到較便宜的模型,並將合併節點保留在高階模型。這是一個槓桿,可以在不觸及其形狀的情況下,將一個消耗大量 token 的圖形從昂貴變為經濟。

13. 拓撲結構 就是 你的成本和延遲

圖形的形狀不僅僅是裝飾性的——它是影響牆上時鐘時間的最大單一槓桿。一個讓每個人都困擾的選擇:parallel() 與 pipeline()。一個 parallel() 屏障會讓所有東西等待最慢的節點完成,然後下一個階段才能開始。

一個 pipeline() 則讓每個項目獨立地流經所有階段,沒有屏障——項目 A 可以在階段 3,而項目 B 仍在階段 1。快的項目會提前完成,而不是在慢的項目後面閒置。

Codez - inline image

預設使用 pipeline()。 只在一個階段真正需要所有先前的結果在一起時才使用屏障——跨集合去重、基於總數的提前退出、一個比較「其他發現」的提示詞。「程式碼更乾淨」和「階段感覺是分開的」不是理由;屏障延遲是真實的、可測量的、浪費的時間。分開不等於同步。

14. 讓 Claude 繪製圖形—— 自我路由

最後一步是停止為你無法預先計劃的任務手動繪製圖形。

使用動態工作流程,你描述目標,Claude 自己編寫編排腳本——分解任務、選擇扇出、生成一個協調的子 Agent 群、並綜合結果。你得到一個為這次執行量身定制的圖形,而不是一個你希望適用的固定圖形。

Codez - inline image

有三種入門方式。在你的提示詞中說出「workflow」這個詞,Claude 就會為該任務編寫一個。執行一個已儲存或捆綁的——/deep-research 是一個在生產中運作的真實圖形:範圍界定 → 平行搜尋 → 擷取 → 對抗性驗證 → 綜合,正是本課程中的骨架。

或者開啟 ultracode 模式,Claude 會為會話中的每個重要任務規劃一個工作流程。當一次執行結果良好時,按 s 將其腳本儲存到 .claude/workflows/ 中——受版本控制、可按名稱重新執行、任何克隆此儲存庫的人都可以啟動的圖形。

python
1› 執行一個工作流程來稽核 src/routes/ 下每個路由的遺漏
2驗證。為每個路由檔案生成一個 Agent,然後在報告前驗證每個發現。
3● Claude 編寫了一個編排腳本 · 在背景啟動中…
4/workflows — auth-audit · 執行中 ✓ 範圍界定 1/1 2.1k token ·
54s ✓ 扇出 18/18 每個路由檔案一個 Agent ◯ 驗證 11/18 每個發現 3 票懷疑論者…
6○ 綜合 0/1 等待驗證 會話保持回應 — 在 Agent 群執行時繼續工作

本週用 Claude 建立的六個圖形

Codez - inline image
  • 跨每個路由的安全掃描。 Claude 為每個路由檔案生成一個子 Agent,每個 Agent 尋找遺漏的驗證檢查,然後一個驗證器階段在每個發現到達報告前進行確認。單一上下文無法容納的廣度。
  • 使用 /deep-research 生成附有引用的報告。 Claude Code 中已內建的一個圖形。Claude 將你的問題分解成不同的角度,執行平行搜尋,對來源去重,然後在撰寫前用三票懷疑論者對抗性地驗證每個聲明
  • 逐個檔案移植一個模組。 Bun 天花板,擴展到你的儲存庫。Claude 將翻譯工作扇出到各個檔案,將測試套件作為每個檔案的閘門執行,並將失敗的循環回去——對抗性審查捕捉單次傳遞會遺漏的錯誤。
  • 對 diff 進行對抗性審查。 Claude 根據 diff 大小進行路由:小變更進行一次快速檢查,大變更觸發一個完整的平行稽核,審查員從不同的角度——正確性、安全性、效能——進行審查,然後由評審團綜合。
  • 按排程進行生態系統掃描。 儲存一次,永久重新執行。Claude 平行檢查多個來源——發布、部落格、討論——在屏障處按影響力排序,並撰寫摘要。在 .claude/workflows/ 中受版本控制,可按名稱啟動。
  • 未知大小的探索。 你不知道有多少錯誤存在。Claude 平行執行尋找器,將每個新發現針對所有見過的東西去重,驗證倖存者,並持續循環直到兩輪沒有發現新東西——然後停止。

結論:

一個提示詞工程師提出一個問題。一個架構師繪製一個圖形。

線性 Agent 從來就不是天花板——它只是第一種形狀,每個人都因為它符合我們打字的方式而使用它。一行、一個腦袋、一次一件事。

一旦你能看到節點和邊,你就不再要求 Agent 做得更多,而是開始要求圖形做得更廣:在工作獨立的地方扇出,在信心重要的地方設置邊閘門,在判斷力不關鍵的地方分層使用模型。

大多數人會繼續在一條線上排隊步驟。那些學會繪製圖形的人將運行一個 Agent 群——並且永遠不會注意到其他人被卡住的那個天花板。

一鍵儲存

使用 YouMind AI 深度閱讀爆款文章

保存原文、追問細節、總結觀點,並在一個 AI 工作空間裡把爆款文章沉澱成可複用筆記。

了解 YouMind
寫給創作者

把你的 Markdown 變成乾淨的 𝕏 文章

圖片上傳、表格、程式碼區塊,往 𝕏 上手動重排太痛苦。YouMind 把整篇 Markdown 一鍵轉成乾淨、可直接發佈的 𝕏 文章草稿。

試試 Markdown 轉 𝕏

更多可拆解樣本

近期爆款文章

探索更多爆款文章