🔁

エージェントループ — 実行ループの解剖

whileループ1つがエージェントの全て

エージェントフレームワークは数十個あるが、全て結局同じ構造だ。

基本ループ

messages = [system_prompt]
while True:
    response = llm.call(messages, tools=available_tools)

    if response.stop_reason == 'tool_use':
        for tool_call in response.tool_calls:
            result = execute(tool_call)
            messages.append(tool_result(result))
    else:
        break

これが全てだ。LangChainだろうがCrewAIだろうがAutoGenだろうが、コアはこのループ。

ループの構成要素

メッセージ履歴: すべての会話とツール結果が蓄積される場所。エージェントの「記憶」だ。コンテキストウィンドウの制限があるので、履歴が長くなると要約やカットが必要。

ツール定義: LLMに「こういうツールが使えるよ」と伝えるスキーマ。ツール名、説明、パラメータをJSON Schemaで定義。ツールの説明が良いほどLLMが適切なツールを選ぶ。

終了条件: ループがいつ終わるか。通常、LLMがツールを呼ばずテキストだけ返せば終了。追加で最大反復回数、タイムアウト、コスト上限などの安全装置が必要。

実践的考慮事項

エラーハンドリング: ツール実行が失敗したら?エラーメッセージをLLMに返せば、LLMが別のアプローチを試みる。クラッシュさせてはいけない。

コンテキスト管理: 100回ツールを呼ぶとメッセージ履歴が巨大になる。トークン上限を超える前に古いメッセージを要約・削除する戦略が必要。

並列ツール呼び出し: Claudeは1回の応答で複数のツールを同時に呼べる。独立したツール呼び出しを並列実行すれば速度が上がる。

観測可能性: ループが回るたびに何をしているかロギング。デバッグ時にログがないと地獄だ。

動作フロー

1

システムプロンプト + ユーザーメッセージで初期メッセージ履歴を構成

2

LLM呼び出し → 応答にtool_useがあればツール実行

3

ツール結果をメッセージ履歴に追加 → LLM再呼び出し

4

tool_useがなければループ終了 → 最終応答を返す

ユースケース

シンプルなCLIエージェントの自作 既存エージェントフレームワークの内部動作の理解