Community程式設計與開發github.com

aviralmansingka/c4

C4 架構視圖 — 透過 scout 子代理從程式碼庫收集系統上下文、容器和元件層級。在 visualize 畫布上建立層級階梯(L0 系統上下文、L1/L1b 容器、L2/L2b 元件),每個盒子外部都有外部系統,每個層級有一個包裝描述。

c4 是什麼?

C4 技能是一個專業的 AI 代理技能,專為 Claude Code、Codex 和 Cursor 等平台設計,可自動從程式碼庫產生 C4 架構圖。它通過定義「層級階梯」(L0 系統上下文、L1/L1b 容器、L2/L2b 元件)來回答「這個系統是什麼、由什麼組成、誰呼叫誰」的問題。該技能會同時啟動四個 scout 子代理(範圍、關係、邊界、層級)以唯讀方式檢查程式碼庫,收集基於證據的元素與關係清單,然後將其整合到統一的事實庫中。生成的輸出是一個五個區段的 visualize 畫布,每個層級有一個有界容器方塊和描述,確保所有繪製的關係都有可驗證的程式碼引用,且未經 scout 證實的元素絕不憑空捏造。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aviralmansingka/dotfiles/tree/HEAD/agents/.agents/skills/c4

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

c4 是做什麼的?

C4 以三個縮放層級回答「這個系統是什麼、由什麼組成、誰呼叫誰」。底層是 visualize 引擎:一個 rataflow 畫布、每個層級一個描述區段、用於所有權的 containers 方塊。請閱讀 agents/.agents/skills/visualize/SKILL.md 以了解渲染器本身 — 此技能僅涵蓋 C4 新增的內容。

C4 新增了三項 visualize 未提供的功能:

  1. 層級階梯。 L0 上下文,接著是 L1/L1b 容器,然後是 L2/L2b 元件。每個層級的存在都是為了引出下一個層級,而讀者所在的層級是從方塊本身可見的,無需查閱圖例。
  2. 事實收集。 C4 必須完整呈現誰呼叫什麼以及為什麼。在產生任何規格之前,scout 會讀取程式碼庫並產生一個有證據支援的元素與關係清單。
  3. 包含作為一種主張。 一個容器方塊容納其元件的自身部分,不包含其他任何東西。這是一個所有權主張,因此需要來源,並且必須經得起讀者根據程式碼檢查該方塊。

不要憑空發明任何元素、關係或邊界。如果 scout 無法引用它,就不要繪製它。

路由

提問使用
真實程式碼庫中元件的 C4 層級此技能
根據已有事實繪製一個關係圖visualize
在聊天回覆中的圖表mermaid 工具
靜態 ANSI / PNGmermaid 技能
架構散文頁面或簡報artifact 技能

/c4 <component> 或「給我 X 的 C4 視圖」是呼叫方式。此技能可在其從未見過的儲存庫中運作:scout 是其學習任何資訊的唯一方式,因此這裡不需要事先了解程式碼。

步驟 0 — 鎖定元件

在任何 scout 執行之前,請說明:

component: machine manager
repo root: /Users/aviral/modal
anchor:    machine_manager/  (一個套件,一個可部署單元)
  • Component 是讀者可以在程式碼中指向的一個東西:一項服務、一個套件、一個可部署單元。如果提問命名了一個跨越多個可部署單元的子系統,請選擇該可部署單元並說明它將哪些鄰居視為外部系統。
  • Repo root 是 scout 開始的地方。確認 checkout 存在;不要假定一個路徑。
  • Anchor 是表明該元件為一體的最強單一證據(套件 manifest、Helm chart、Terraform 模組、proto 服務、main.go)。如果你無法指出一個,則該元件很可能是兩個元件 — 請詢問人類,不要猜測。

最多問一個澄清問題(元件邊界是唯一值得問的)。否則自行決定並記錄。

步驟 1 — 啟動 scout

prompts/ 中有四個提示;每個都是一個完整的子代理任務。將它們作為 subagent 呼叫啟動,使用 agent: "scout"(唯讀偵查 — scout 不得編輯儲存庫),四個提示在一次回合中全部啟動以便並行執行,cwd: 為步驟 0 中解析出的儲存庫根目錄,並將元件名稱替換為 <component>:

Scout提示回傳
範圍prompts/scope.md元素清單:id、一句話角色、進入點、可部署單元、儲存、外部服務、角色、檔案路徑
關係prompts/relationships.mdfrom → to : verb + transport 行,每行附帶 file:line
邊界prompts/boundary.md成員 vs 外部 + 證明每個成員資格的模組/部署邊界
層級prompts/levels.md每個元素的層級分配,以及哪些層級必須拆分為 L1/L1b (L2/L2b)

每個 scout 都會回傳帶有 file:line 引用的證據。沒有這個引用的行是謠言;將其丟棄或要求該 scout 重新提供來源。

從儲存庫根目錄啟動 scout — 在那裡執行工作階段(或使 cwd: 與工作階段所在的目錄相同)。cwd 與產生它的工作階段不同的 scout 可能會在其程序啟動前死亡,而失敗訊息會顯示為「其窗格中未出現 pi 程序」。

不要輪詢子代理。結果會自行到達;當全部四個都返回時繼續。如果其中一個失敗,請說明是哪一個,並在生成記錄中重試或記錄此缺口 — 切勿憑記憶填補。

步驟 2 — 整合成一個總帳

將四個回傳結果合併到生成記錄中的三個表格中。這是整個事實庫;規格可能不包含任何不在這裡的內容。

elements:   id | label | role | class (actor|external|member|store) | evidence
relations:  id | from → to | verb + transport | level | evidence
boundary:   id | inside/outside | justifying boundary | evidence
  • 衝突通過引用更強的來源解決(manifest 或部署 chart 勝過註解;呼叫站點勝過文件字串),並且失敗的主張會被記錄下來,而不是刪除。
  • 未解決的衝突和沒有倖存來源的關係會被記錄為開放問題。沉默是缺陷;可見的疑問則不是。
  • 每個元素保留一個 id,並跨層級重用,因此追蹤 q → reg 的讀者在 L1 和 L2 看到的是相同的兩個方塊。

步驟 3 — 要繪製什麼(優先排序)

C4 視圖是關於重要性的判斷,而不是呼叫圖的傾倒。總帳包含 scout 找到的每個關係;圖表包含讀者需要的那些關係,並明確說明它省略了什麼。

當一個關係至少滿足以下其中一項時,它就獲得一席之地:

id測試範例
I1它跨越元件邊界(成員 ↔ 外部,外部 ↔ 外部)webapi → Latitude API,machines → sshx.io
I2它是來源明確指出的主要請求、控制或資料路徑(佈建、心跳、稽核、調和、啟動)control plane → private gRPC,River queue → workers
I3它攜帶讀者需要的協定或限定詞:RPC 動詞、傳輸、埠、儲存桶、間隔、逾時register + heartbeat every 1 min,gcore sync every 1 min
I4它解釋了讀者無法從方塊推斷出的資料儲存或角色store → Postgres,ops + mmctl → webapi
I5它是必須保持可計數的冗餘或並行路徑中的一條machines → boot 和 boot → machines;兩個獨立的拉取

丟棄類別 — 一個關係僅在符合以下其中一項時才從圖表中移除:

id類別說明
C1內部細節完全在一個容器內部,不增加邊界、協定或角色資訊(當儲存的寫入者已經繪製時,worker → store 的逐查詢行)
C2重複呼叫站點與已繪製的關係相同的躍點,僅在作業類型或參數上不同,而已繪製的標籤已包含這些資訊
C3輔助一個唯一工作是重新進入同一容器的元素(兩個已繪製方塊之間的客戶端包裝器)
C4冗餘途徑一條路徑的進一步並行分支,其可計數性讀者不需要(來自同一儲存桶、相同傳輸的第二個成果)

保持判斷誠實的規則:

  • 一個箭頭可以攜帶多個呼叫站點,只要標籤包含它們的動詞(create / terminate / repair)。這是 C2 摺疊,而不是刪除。
  • 邊界跨越贏得平局(I1 優先於 I3,I3 優先於 I2)。為了容納內部躍點而丟棄外部躍點的圖表已經顛倒了自己的優先級。
  • 有疑問時,就繪製它,並讓層級的描述說明它為什麼存在。疑問不是丟棄的理由;C1–C4 才是。
  • 切勿靜默丟棄。 總帳中的每一行最終都是以下之一:已繪製、摺疊到 <已繪製的關係>(並以 C2 證明摺疊合理),或已省略 — C1|C2|C3|C4 附帶一行說明原因。覆蓋圖(步驟 7)會說明四個計數:已繪製、已摺疊、按類別省略、未說明。
  • 範圍縮減是不同的東西,需要呼叫者的話語。「僅控制平面視圖」或「最多 20 個關係」是範圍要求:將其記錄為基礎(basis: caller scope, N relations),與計數後的總帳一起。優先排序是預設行為;子集是呼叫者做出的決定,而不是生成器推斷的決定。

步驟 4 — 層級階梯

目標形狀是五個區段,從上到下:

L0 · 系統上下文       元件的一個節點 + 其鄰居
L1 · 元件             元件打開一次,容器包含其部分
L1b · <第二側>        相同深度,系統的另一側
L2 · 內部 — <切片 A>  成員之間的關係,容器包含它們
L2b · 內部 — <切片 B>  相同深度,另一個切片

這就是階梯:一個五區段的畫布是交付物。更多的區段是缺陷,除非滿足下面的拆分紀律(附帶數字)。

每個層級僅在其上一個層級存在後才繪製,並在繼續之前為每個層級提供其描述。

層級繪製內容方塊行為
L0 系統上下文元件作為一個普通節點,加上周圍的每個角色和外部系統,以及它們之間的粗略箭頭無容器、無 focus、無雙框線 — 重點是周圍的世界,因此元件看起來像任何其他方塊
L1 容器元件打開一層:其自身的可部署/執行階段單元在一個容器內;L0 的外部保持在外部成員上的容器方塊
L1b 側視圖當一個 L1 確實無法容納流程時(集群端、資料平面、批次路徑)的可選第二個切片 — 經測量,絕非新主題容器,規則相同
L2 元件一個容器內部的關係 — L1 的成員,現在互相呼叫在它們周圍的容器方塊
L2b當一個 L2 無法路由其關係,或兩個故事需要不同層次時,相同深度的可選第二個切片容器,規則相同

不可協商的層級規則:

  • L0 是作為一個普通節點的元件。 整個元件一個方塊,其普通邊框,加上周圍的角色和外部系統。不是其部分、其監聽器、其表格 — 這些是下一個層級。如果 L0 有超過一個屬於元件內部的節點,則它不是上下文視圖。L0 上的包含會聲稱讀者尚未看到的內部細節,而 focus 會指向一個尚不存在的層級。
  • L1/L1b 打開 L0 的方塊。 每個成員都坐在容器內部;每個在 L0 是外部的元素都坐在它外部。無法在方塊邊界框之外繪製的成員會獲得自己的容器條目(請參閱連續性規則) — 絕非湊合的。
  • L2/L2b 保持在一個方塊內部。 L2 關係的兩個端點都是容器成員。如果關係離開方塊,則它屬於 L1/L1b。
  • 一個層級描述自身。 標題是簡短的(L0 · 系統上下文);描述說明方塊內部是什麼、外部是什麼,以及它與相鄰層級的關係。目標是 2–3 行。
  • 動畫不是 C4 屬性。 層級 0–1b 是靜態的(dots: 0)。其關係是即時流程的內部層級可以每條路線攜帶恰好一個點(flow: true, dots: 1);記錄此選擇。切勿為裝飾階梯而動畫化外部箭頭。

拆分紀律 — 一個層級是一個深度,而不是一個主題

C4 層級以一個縮放回答一個問題。相同深度的兩個層級的存在僅是因為一個區段無法容納內容;一個新的主題不是添加區段的理由。

  • 切勿添加 L1c、L1d、L1e 或第五個側。 階梯有三個深度(上下文、容器、元件),並且在第一個深度以下最多有兩個切片。一個每個子系統或每次提到就增長一個切片的畫布是一個主題列表,戴著層級頭銜。
  • 第二個切片必須命名其測量標準: --check 拒絕的路線、它所報告的共享通道,或發生衝突的層次(兩個外部端點,沒有共同的外部)。「感覺很擠」不是一個。
  • 相同深度的切片共享方塊、標籤和 id。 L1 和 L1b 繪製相同的元件,使用相同的成員 id;L2 和 L2b 繪製相同的成員。只有邊集合和層次不同,並且每個描述說明它是哪個切片。
  • 一個切片命名為 b(l1b、l2b);切勿發明一個深度。
  • 不繪製 L2 以外的深度。 如果元件內部仍然分解,請在記錄中說明並停止:程式碼類別和函數不是讀者要求的 C4 層級。階梯在要求結束的地方結束。
  • 總帳中所有未在階梯中繪製的內容都是一個範圍決定,需記錄下來(步驟 6),不是靜默刪除,也不是一個新的區段。

步驟 5 — 規格機制

一個畫布、一個閱讀方向、每個層級一個區段:

{
  "direction": "lr",
  "theme": "gruvbox",
  "diagrams": [
    {
      "id": "l0",
      "title": "L0 · 系統上下文",
      "description": "系統上下文:管理器是一個方塊。...",
      "nodes": [ ... ],
      "edges": [ ... ]
    },
    {
      "id": "l1",
      "title": "L1 · 管理器",
      "description": "L0 的方塊打開一層:...",
      "containers": [ { "label": "Machine manager", "members": ["api", "q", "db"] } ],
      "layers": [ ["actor"], ["api", "q"], ["db"], ["prov"] ],
      "nodes": [ ... ],
      "edges": [ ... ]
    }
  ]
}
  • direction: "lr" 一次,在畫布上。切勿有每個圖表的覆蓋:每個區段都重新定向的讀者已經失去了階梯。
  • 五個區段。 l0、l1、l1b、l2、l2b,按此順序。第六個區段需要記錄步驟 3 的測量;一個有十個或二十個區段的畫布是一個主題列表,而不是一個階梯 — 將其摺疊回三個深度各兩個切片。
  • containers 是一個每個圖表的列表,包含 {label, members}。方塊是一個真實的雙邊框矩形,圍繞成員生長,標籤嵌入其頂部邊緣。每個圖表允許多個方塊。
  • 成員 id 必須存在於該圖表的 nodes 中。 命名了不存在節點的成員 id 會被解析器靜默丟棄 — 然後方塊圍繞比你聲稱的更少的節點繪製,而 --check 不會發出警告。對照你宣告的成員來計數已繪製的方塊。
  • 連續性規則。 一個容器是一個矩形

…

[翻譯截斷]

相關技能