ループエンジニアリングとグラフエンジニアリング
概要
ループエンジニアリング と グラフエンジニアリング は、AI エージェントを「都度プロンプトを打つ道具」ではなく「自律的に反復するシステム」として設計する実践です。本記事では、この2つの概念を整理し、quarto-dsh 自身のエージェントループ実装(src/core/agent.ts など)を題材に「ループ」が実際にどう動くのかを読み解きます。
- Addy Osmani: Loop Engineering(2026-06-07、用語を体系化)
- Getting started with loops — Claude 公式ブログ(2026-06-30)
- TheNewStack: Loop Engineering
ループエンジニアリングとは
Google のエンジニア Addy Osmani 氏は、ループエンジニアリングを次のように定義しました。
Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead. (ループエンジニアリングとは、「エージェントにプロンプトを打つ人間」を自分からシステムに置き換えること。あなたは代わりにそれをやるシステムを設計する)
背景には、Claude Code の開発者 Boris Cherny 氏の言葉があります。
I don’t prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.
エージェント活用の抽象度は次のように段階を上げてきました。
| 階層 | 最適化の対象 | 典型的な問い |
|---|---|---|
| プロンプトエンジニアリング | 1回の指示 | どう書けば狙いどおり答えるか |
| コンテキストエンジニアリング | 毎ターンの文脈 | 何をいつコンテキストに入れるか |
| ハーネスエンジニアリング | 実行環境 | エージェントが仕事を遂行・検証できる環境か |
| ループエンジニアリング | 反復の制御構造 | 次の仕事をどう見つけ、どう検証し、いつ止めるか |
下の層が不要になるわけではありません。プロンプトもコンテキストもハーネスも残ったまま、その上に自律的な反復を足すのがループエンジニアリングです。
Closed loop: 検証と停止条件を仕組みに移す
「テストが失敗しているので直したい」という場面を例にします。
従来(人間がループを回す):
❯ テストが失敗してるので直して → 結果を確認
❯ まだ fizzbuzz(15) が失敗してる。直して → 結果を確認
❯ 直った。じゃあ lint も通して → 結果を確認
結果の観測・検証・次の指示(ループの制御)を都度、人が担当します。
ループエンジニアリング:
❯ /goal ./test.sh が exit 0 で終わる。5ターン試してだめなら停止
検証や継続判断を仕組み(フック・スクリプト等)に移し、人間は停止条件を設計して、最後に結果をチェックする役に回ります。この「人間が担っていた反復の制御を仕組みに置き換える」ことがループエンジニアリングの本質です。
原始的なループ: Ralph ループ
Geoffrey Huntley 氏が紹介した原始的な形は bash のループです。
while :; do cat PROMPT.md | claude-code; done同じプロンプトを毎回新しいコンテキストで流し込むだけですが、「状態を会話ではなくリポジトリ側(PROMPT.md・テスト)に持たせて反復する」という発想は、現在のループエンジニアリングに通じます。
グラフエンジニアリングとは
AI エージェント運用は「プロンプト → ループ → スワーム → グラフ」の順に進化するという 4 段階モデルがあります。グラフエンジニアリングはその到達点です。
| 段階 | 内容 | 限界 |
|---|---|---|
| 1. プロンプト | 人がプロンプトを打ち、出力を読み、次を打つ(人間自身がループ) | ラップトップを閉じれば何も残らない |
| 2. ループ | スクリプトで包みスケジュールで発火。状態を持ち自走する | 「1 エージェント・1 ジョブ」に留まる |
| 3. スワーム | 役割ごとに多数のエージェントへ扇状展開 | 調整コスト・収束の担保が難しい |
| 4. グラフ | ノード(判断)とエッジ(データ受け渡し)で構造を宣言的に設計 | 設計の複雑さ |
グラフエンジニアリングでは、ノード=エージェント(判断)、エッジ=データの受け渡しと捉え、「いつ並列に走り、いつ待ち、いつ再試行し、いつ検証するか」をグラフ自体が知っている状態を目指します。正典形は ダイヤモンド型(fan out → 処理 → 統合)です。
なお、段階が進んでも前の段階は不要になりません。LangChain 自身が「ループは単純なグラフにすぎず、ループエンジニアリングはグラフの代替ではなくその簡略版である」と述べており、グラフの各ノードは依然としてループです。
quarto-dsh のループ実装
quarto-dsh は「ハーネス」として、上記の 4 層モデルのうち ハーネス層 + ループ層 を実装しています。エージェントループは src/core/agent.ts の runAgent が担当します。
step/turn ループの構造
runAgent は「モデルへ問い合わせ → ツールを実行 → 結果を戻す」を最大 maxSteps(既定 8)まで繰り返します。
for (let step = 0; step < maxSteps; step++) {
const { text, toolCalls } = await collectChat(ctx, options, messages, tools);
messages.push({ role: 'assistant', content: text, toolCalls });
ctx.sessions.append(sessionId, 'agent/step', { response: text, toolCalls });
if (!toolCalls || toolCalls.length === 0) {
return { answer: text, steps: step + 1 }; // ← 停止条件
}
for (const call of toolCalls) {
const result = await ctx.tools.execute(call.name, call.arguments, tctx);
ctx.sessions.append(sessionId, 'agent/tool', { name: call.name, ok: result.ok });
messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
}
}
return { answer: 'step limit reached', steps: maxSteps }; // ← 上限で打ち切りポイントは「ループエンジニアリングの原則」と対応します。
- 停止条件を設計する —
maxStepsという機械的な上限と、ツール呼び出しが無くなったら終了する条件をコードで定義しています(agent.ts:66-68)。 - 検証を本物にする — モデルの出力をそのまま受け入れず、
quarto-lint/quarto-scanが機械判定できる根拠で設定・記法を検証します。
検証者としてのツール
ループエンジニアリングで重要なのは「生成役と評価役の分離」です。quarto-dsh では、生成は LLM、検証はツールという役割分担になっています。
- 生成: DeepSeek がコードや設定の修正案を生成
- 検証:
quarto-lintが実在キーで_quarto.ymlを検証し、quarto-scanが callout 型・チャンクオプション・相互参照・Div の開閉を検査
システムプロンプトにも「不確かな設定キーは quarto-lint で検証してから提案してください」と明記し、自己採点に頼らない誘導をしています(src/app.ts:24-44)。
append-only セッションログ(ledger)
各 step・ツール実行は SessionLog に append-only で記録されます(src/core/session.ts)。
createSession()— セッション開始append(sessionId, type, payload)— イベントを追記(agent/step/agent/tool)fork(source, boundary)— 途中から別セッションに分岐- ファイル指定時は JSONL に永続化され、再起動後も復元できます
これは DeepSeek Harness が説明する「append-only ledger(タスクの記録と fleet resume での再開)」と同じ発想です。セッションを途中でフォークできる点は、ループの「分岐」を支えます。
能力シームとイベント
ハーネス層として、quarto-dsh は Cordis 上に3つの能力シームを持ちます。
ctx.llm— モデル呼び出し(LlmService)ctx.shell— コマンド実行(ShellService)ctx.tools— ツール実行(ToolRegistry)
ツール実行の前後には tools/pre-execute / tools/post-execute イベントが発火し(src/core/tools.ts:70-87)、「実行前チェック」「実行後フック」を挿入できます。これは Claude Code の PreToolUse / PostToolUse hook に相当し、実行環境を検証で固めるハーネス層の実装です。
グラフへの発展(今後の余地)
quarto-dsh の現状は「単一のループ」、つまり4段階モデルの ループ層 に留まります。グラフ層への発展余地は以下にあります。
- fork による分岐 —
SessionLog.fork()を使い、検証に失敗したセッションを分岐して並行試行する(fan out) - 検証ゲート —
tools/post-executeフックで lint / scan の結果をチェックし、不合格なら別ループを発火する(reduce / 統合) - 複数ループの編成 — 「生成ループ」「検証ループ」「公開ループ」をノードとして、エッジ(データ受け渡し)でつなぐ
ただし、段階を進める前に「その段階が必要か」を自己診断するのが正しい使い方です。単一ループで足りているうちは、グラフに飛びつく必要はありません。
まとめ
ループエンジニアリングの要点は、モデルの性能ではなく次の2点でした。
- 検証を本物にする — テストや lint のような機械判定できる根拠で「完了」を判定する
- 停止条件を設計する — 機械判定できる形で完了を定義し、上限とセットで運用する
quarto-dsh は maxSteps による停止条件と、lint / scan による機械的検証、append-only セッションログによる履歴という形で、この原則を実装しています。
Osmani 氏の言葉を最後に。
Build the loop. But build it like someone who intends to stay the engineer, not just the person who presses go. (ループを構築しましょう。ただし、単に「go ボタンを押す人」ではなく、エンジニアであり続ける意志を持つ人として構築しましょう)
ループが出力した成果物を読み、検証し、判断する責任は、これまでどおりエンジニアの側にあります。
関連
- chat(LLM エージェント) — quarto-dsh のエージェントの使い方
- lint(設定検証) — 検証者としてのツール
- scan(記法検査)