ハーネスの心臓部は、意外なほど単純なループです。第1章で見た「モデル以外のすべて」のうち、最も中核にあるのがこのループの制御。 Anthropic の言葉を借りれば、エージェントとは結局のところ「環境からのフィードバックに基づいて、ツールをループのなかで使うLLM」にすぎません。 本章ではこのループを分解し、「いつ止めるか」「どう壊れないようにするか」というハーネス設計の要を見ていきます。
中核のwhile-loop
基本形は「コンテキストを組み立てる → モデルを呼ぶ → ツールを実行する → 結果を観測する → 繰り返す」。 各ステップで環境から「ground truth(ツール実行結果やコード実行の出力)」を得て、進捗を判断するのが肝心です。 Claude Code の分析でも「核となるエージェントループは概念的には単純で、本当のエンジニアリングの妙はそのループの周りすべてに宿る」と表現されています。
graph TD
S[ユーザー入力] --> G[コンテキスト組み立て]
G --> M[モデル呼び出し]
M --> D{stop_reason は?}
D -->|tool_use| T[ツールを実行]
T --> O[結果をコンテキストに追加]
O --> M
D -->|end_turn| E[最終回答・ループ終了]
style S fill:#3b82f6,stroke:#1d4ed8,color:#fff
style M fill:#8b5cf6,stroke:#6d28d9,color:#fff
style D fill:#f97316,stroke:#ea580c,color:#fff
style T fill:#14b8a6,stroke:#0d9488,color:#fff
style E fill:#3b82f6,stroke:#1d4ed8,color:#fffツール呼び出しのフロー
ループの各周回で何が起きているのかを、もう少し具体的に見ましょう。
モデルの応答には「ツール使用ブロック(tool use block)」が含まれることがあります。ハーネスはこれを検出し、ローカルでツールを実行し、
その結果を次のターンの入力として会話履歴に戻します。
Anthropic のメッセージAPIでは、assistant が tool_use を発し、user 側のメッセージとして tool_result を返す、という非対称な構造になっている点が要注意です。
# 擬似コード(説明用): 最小のエージェントループ
conversation = []
while True:
response = model.create(
system=system_prompt,
tools=tool_schemas,
messages=conversation,
)
conversation.append(response) # assistantの発話を履歴へ
if response.stop_reason == "tool_use":
results = []
for call in response.tool_calls: # 応答内のtool_useブロック
out = run_tool(call.name, call.input) # ローカルで実行
results.append(tool_result(call.id, out))
conversation.append(user_msg(results)) # 結果を次ターンの入力へ
continue # 入力待ちにせずループ継続
break # end_turn など → 最終回答
会話配列(conversation)がエージェントの状態そのものです。APIはステートレスなので、毎ターン会話全体を送り直します。
この「毎回全部送る」性質が、第4章のコンテキスト管理と、コスト爆発(第10章)の両方に直結します。
終了判定 — モデル任せにしない
ループをどう抜けるか。素朴には「モデルが end_turn を返したら(=もうツールを使わず最終回答を出したら)終了」です。
しかしこれだけに頼ると、モデルが同じツール呼び出しを延々繰り返したり、終わらないタスクで暴走したりします。
そこでハーネス側にサーキットブレーカー(安全弁)を置く二重化が定石です。
| 終了の仕組み | 誰が判断するか | 役割 |
|---|---|---|
| stop_reason(end_turn 等) | モデル | タスク完了の通常の合図 |
| 最大反復回数(max iterations) | ハーネス | 無限ループの防止 |
| コスト上限・トークン上限 | ハーネス | 暴走課金の防止 |
| 反復検出器(repetition detector) | ハーネス | 同一ツール呼び出しの空回りを検知 |
| 人間へのエスカレーション | ハーネス | ブロッカー遭遇時に判断を委ねる |
耐久実行 — 長時間タスクで壊れないために
タスクが数時間に及ぶようになると、新たな問題が生まれます。Anthropic のマルチエージェント研究では 「最初からやり直すことはできない——再起動は高価だ」と述べ、チェックポイントと知的なエラー処理で「エラー発生時点からエージェントを再開する」耐久実行(durable execution)を重視しています。 フレームワークでは LangGraph がこの永続化レイヤを提供し、各ステップで状態スナップショットを保存します(第7章で再登場)。
さらに興味深い失敗モードが 「コンテキスト不安(context anxiety)」 です。 Anthropic の「Harness design」記事によれば、長尺タスクでモデルは「もう自分のコンテキスト上限だ」と思い込み、作業を早すぎるタイミングで切り上げ始める傾向があります。 対策は「コンテキストリセット」——コンテキストウィンドウを完全にクリアして新しいエージェントを起動し、 前のエージェントの状態と次の手順を引き継ぐ構造化ハンドオフを組み合わせる方法です。
理解度チェック
エージェントループにおいて、ツール実行の結果はどのように扱われますか?
キーボード: 1〜4 で選択、Enter で回答