graphviz-diagrams について
Graphviz DOT で複雑なグラフを作成するスキルです。精密なレイアウトが必要な構造を可視化し、編集可能なソースとレンダリング画像を併せて出力します。
主な機能
- 01DOT による図の作成
- 02図の画像へのレンダリング
- 03Markdown へのソースと画像の添付
- 04複数のレイアウトエンジンの選択
- 05一般的なグラフ形式の例の利用
活用シーン
- 01複雑な依存関係の可視化
- 02呼び出しグラフやコードフローの記録
- 03ネットワーク構成や状態機械の図示
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindmorass/ reflex --skill graphviz-diagramsインストール後、Claude Code で/graphviz-diagramsを入力して読み込まれたか確認します。
詳細
Graphviz 図のスキル
目的
Graphviz DOT 言語を使って、ソースコードとレンダリング済み画像の両方を含む複雑なグラフを作成します。
使用する場面
- 複雑な依存関係グラフ
- 呼び出しグラフとコードフロー
- ネットワークトポロジー
- 階層構造
- 複雑な遷移を持つ状態機械
- 正確なレイアウト制御が必要なグラフ全般
出力形式
Graphviz 図には、必ず次のものを含めてください。
- インライン DOT ソース - 参照や将来の編集用
- レンダリング済み画像 - あらゆる Markdown レンダラーで表示するため
ドキュメント構造
## Diagram: [Name]
### Source
```dot
digraph G {
A -> B
}
```
### Rendered

レンダリングのワークフロー
ステップ 1:DOT ソースを記述する
dot_source = """
digraph G {
rankdir=LR;
A -> B -> C;
}
"""
ステップ 2:画像にレンダリングする
import subprocess
from pathlib import Path
def render_graphviz(
dot_source: str,
output_path: Path,
format: str = "png",
engine: str = "dot"
) -> Path:
"""
Render DOT source to image file.
Args:
dot_source: DOT language source code
output_path: Output file path (without extension)
format: Output format (png, svg, pdf)
engine: Layout engine (dot, neato, fdp, circo, twopi, sfdp)
Returns:
Path to rendered image
"""
output_file = output_path.with_suffix(f".{format}")
result = subprocess.run(
[engine, f"-T{format}", "-o", str(output_file)],
input=dot_source,
text=True,
capture_output=True
)
if result.returncode != 0:
raise RuntimeError(f"Graphviz error: {result.stderr}")
return output_file
ステップ 3:Markdown に埋め込む
def create_diagram_markdown(
name: str,
dot_source: str,
image_path: str
) -> str:
"""Create markdown with both source and rendered image."""
return f"""## Diagram: {name}
### Source
```dot
{dot_source}
レンダリング結果
"""
## DOT Language Reference
### Basic Graph Types
#### Directed Graph (digraph)
```dot
digraph G {
A -> B;
B -> C;
A -> C;
}
無向グラフ(graph)
graph G {
A -- B;
B -- C;
A -- C;
}
グラフ属性
digraph G {
// Graph attributes
rankdir=LR; // Direction: TB, BT, LR, RL
splines=ortho; // Edge style: line, polyline, curved, ortho, spline
nodesep=0.5; // Space between nodes
ranksep=1.0; // Space between ranks
bgcolor="white"; // Background color
fontname="Helvetica"; // Font for labels
// Nodes and edges
A -> B;
}
ノード属性
digraph G {
// Node defaults
node [shape=box, style=filled, fillcolor=lightblue];
// Individual node styling
A [label="Start", shape=ellipse, fillcolor=green];
B [label="Process
Data", shape=box];
C [label="Decision", shape=diamond, fillcolor=yellow];
D [label="End", shape=ellipse, fillcolor=red];
A -> B -> C;
C -> D;
}
よく使われるノード形状
| 形状 | 用途 |
|---|---|
box | プロセス、アクション |
ellipse | 開始/終了、終端 |
diamond | 判断 |
circle | 状態 |
record | 構造化データ |
Mrecord | 角丸のレコード |
cylinder | データベース |
folder | ディレクトリ/コレクション |
component | コンポーネント |
note | 注釈 |
エッジ属性
digraph G {
// Edge defaults
edge [color=gray, fontsize=10];
A -> B [label="step 1", color=blue, penwidth=2];
B -> C [label="step 2", style=dashed];
C -> D [label="step 3", arrowhead=empty];
D -> A [label="loop", style=dotted, constraint=false];
}
矢印のスタイル
| 矢印の先端 | 説明 |
|---|---|
normal | 塗りつぶされた三角形(デフォルト) |
empty | 中抜きの三角形 |
dot | 塗りつぶされた円 |
odot | 中抜きの円 |
diamond | 塗りつぶされたひし形 |
none | 矢印の先端なし |
vee | V 字型 |
box | 塗りつぶされた四角形 |
サブグラフとクラスター
digraph G {
// Cluster (named subgraph with cluster_ prefix)
subgraph cluster_frontend {
label="Frontend";
style=filled;
fillcolor=lightgray;
UI -> Components -> State;
}
subgraph cluster_backend {
label="Backend";
style=filled;
fillcolor=lightyellow;
API -> Service -> Database;
}
// Cross-cluster edges
State -> API [label="HTTP"];
}
レコード(構造化ノード)
digraph G {
node [shape=record];
user [label="User|{id: int|name: string|email: string}"];
order [label="Order|{id: int|user_id: int|total: decimal}"];
user -> order [label="1:N"];
}
HTML ラベル
digraph G {
node [shape=none];
table [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="lightblue"><B>User</B></TD></TR>
<TR><TD ALIGN="LEFT">id: int</TD></TR>
<TR><TD ALIGN="LEFT">name: string</TD></TR>
<TR><TD ALIGN="LEFT">email: string</TD></TR>
</TABLE>
>];
}
レイアウトエンジン
| エンジン | 最適な用途 | 説明 |
|---|---|---|
dot | 階層構造 | 有向グラフ、ツリー、DAG |
neato | ネットワーク | 無向グラフ、ばねモデル |
fdp | 大規模ネットワーク | 力学ベースのレイアウト、スケーラブル |
sfdp | 非常に大規模なグラフ | マルチスケールの力学ベースレイアウト |
circo | 円形 | 円形レイアウト |
twopi | 放射状 | ルートから放射状に広がるレイアウト |
使用方法
# Different engines produce different layouts
dot -Tpng graph.dot -o graph-hierarchical.png
neato -Tpng graph.dot -o graph-spring.png
circo -Tpng graph.dot -o graph-circular.png
よく使われるパターン
依存関係グラフ
digraph Dependencies {
rankdir=BT;
node [shape=box, style=filled, fillcolor=lightblue];
// Packages
app [label="app"];
api [label="api"];
core [label="core"];
utils [label="utils"];
db [label="database"];
// Dependencies (arrows point to dependency)
app -> api;
app -> core;
api -> core;
api -> db;
core -> utils;
db -> utils;
}
状態機械
digraph StateMachine {
rankdir=LR;
node [shape=circle];
// Start state
start [shape=point, width=0.2];
// States
idle [label="Idle"];
loading [label="Loading"];
success [label="Success", shape=doublecircle];
error [label="Error"];
// Transitions
start -> idle;
idle -> loading [label="fetch()"];
loading -> success [label="200 OK"];
loading -> error [label="error"];
error -> idle [label="retry()"];
success -> idle [label="reset()"];
}
呼び出しグラフ
digraph CallGraph {
rankdir=TB;
node [shape=box, fontname="Courier"];
main [style=filled, fillcolor=lightgreen];
main -> init;
main -> process;
main -> cleanup;
init -> loadConfig;
init -> connectDB;
process -> validateInput;
process -> transform;
process -> save;
transform -> normalize;
transform -> enrich;
save -> connectDB [style=dashed, label="reuse"];
}
ネットワークトポロジー
graph Network {
layout=neato;
overlap=false;
node [shape=box];
// Nodes
internet [shape=cloud, label="Internet"];
firewall [shape=box3d, label="Firewall"];
lb [label="Load
Balancer"];
web1 [label="Web 1"];
web2 [label="Web 2"];
app1 [label="App 1"];
app2 [label="App 2"];
db [shape=cylinder, label="Database"];
// Connections
internet -- firewall;
firewall -- lb;
lb -- web1;
lb -- web2;
web1 -- app1;
web1 -- app2;
web2 -- app1;
web2 -- app2;
app1 -- db;
app2 -- db;
}
エンティティ関係
digraph ERD {
rankdir=LR;
node [shape=record, fontname="Helvetica"];
edge [arrowhead=none];
user [label="<pk> User|id: PK\lname: string\lemail: string\l"];
order [label="<pk> Order|id: PK\luser_id: FK\ltotal: decimal\l"];
item [label="<pk> OrderItem|id: PK\lorder_id: FK\lproduct_id: FK\l"];
product [label="<pk> Product|id: PK\lname: string\lprice: decimal\l"];
user:pk -> order:pk [label="1:N", arrowhead=crow];
order:pk -> item:pk [label="1:N", arrowhead=crow];
product:pk -> item:pk [label="1:N", arrowhead=crow];
}
フローチャート
digraph Flowchart {
rankdir=TB;
node [fontname="Helvetica"];
start [shape=ellipse, label="Start", style=filled, fillcolor=lightgreen];
input [shape=parallelogram, label="Get Input"];
validate [shape=diamond, label="Valid?"];
process [shape=box, label="Process Data"];
error [shape=box, label="Show Error", style=filled, fillcolor=lightyellow];
output [shape=parallelogram, label="Output Result"];
end [shape=ellipse, label="End", style=filled, fillcolor=lightcoral];
start -> input;
input -> validate;
validate -> process [label="Yes"];
validate -> error [label="No"];
error -> input;
process -> output;
output -> end;
}
パブリッシャーとの連携
Obsidian との連携
def publish_graphviz_to_obsidian(
vault_path: str,
folder: str,
filename: str,
diagram_name: str,
dot_source: str
):
"""Publish Graphviz diagram to Obsidian with source and image."""
from pathlib import Path
vault = Path(vault_path)
target_dir = vault / folder
attachments_dir = vault / "attachments"
target_dir.mkdir(parents=True, exist_ok=True)
attachments_dir.mkdir(parents=True, exist_ok=True)
# Render image
image_name = f"{filename}-diagram.png"
image_path = attachments_dir / image_name
render_graphviz(dot_source, image_path.with_suffix(""), "png")
# Create markdown with both source and image
content = f"""# {diagram_name}
## Source
```dot
{dot_source}
レンダリング結果
![[{image_name}]] """
note_path = target_dir / f"{filename}.md"
note_path.write_text(content)
### Joplin Integration
```python
def publish_graphviz_to_joplin(
notebook: str,
title: str,
diagram_name: str,
dot_source: str
):
"""Publish Graphviz diagram to Joplin with source and image."""
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
# Render image
image_path = tmpdir / "diagram.png"
render_graphviz(dot_source, image_path.with_suffix(""), "png")
# Create markdown
content = f"""# {diagram_name}
## Source
```dot
{dot_source}
レンダリング結果
"""
md_path = tmpdir / f"{title}.md"
md_path.write_text(content)
# Joplin にインポートする(Markdown と参照先の画像をインポート)
subprocess.run([
"joplin", "import", str(tmpdir),
"--notebook", notebook
], check=True)
## Graphviz vs Mermaid
| Feature | Graphviz | Mermaid |
|---------|----------|---------|
| **Layout control** | Precise, many engines | Automatic only |
| **Complexity** | Handles very large graphs | Better for simpler diagrams |
| **Rendering** | External tool required | Browser-native |
| **Styling** | Extensive options | Limited but sufficient |
| **Learning curve** | Steeper | Easier |
| **Use case** | Complex dependencies, call graphs | Quick diagrams, sequences |
**Use Graphviz when:**
- You need precise layout control
- Graph is large or complex
- You need specific node arrangements
- Creating dependency or call graphs
**Use Mermaid when:**
- Quick inline diagrams
- Sequence diagrams
- Simple flowcharts
- Native browser rendering preferred
## Prerequisites
### Install Graphviz
```bash
# macOS
brew install graphviz
# Ubuntu/Debian
sudo apt-get install graphviz
# Windows (chocolatey)
choco install graphviz
# Verify installation
dot -V
チェックリスト
Graphviz 図を作成する前に:
- Graphviz がインストールされている(
dot -V) - 出力ディレクトリに書き込み可能
- DOT 構文を検証済み
- 適切なレイアウトエンジンを選択済み
- ソースと画像の両方を出力に含めている
- 対象(Obsidian/Joplin)に応じた画像パスが正しい
コメント
Markdown 対応