Post
JA EN

AIエージェントの仕組み——API・ツール・ループで理解する基本アーキテクチャ

AIエージェントの仕組み——API・ツール・ループで理解する基本アーキテクチャ
  • 想定読者: プログラミング経験はあるがAIエージェント開発は初めてのエンジニア
  • 前提知識: REST APIの基礎知識、JSONの読み書き
  • 所要時間: 15分

概要

「AIエージェント」という言葉が氾濫している。だが、実際にコードを書こうとすると、意外なほどシンプルな構造が見えてくる。

AIエージェントの正体は、ループの中でツールを呼び出すLLMだ。Anthropicの公式ガイドはこう定義する——「エージェントとは本質的に、環境からのフィードバックに基づいてツールをループで使用するLLMである」1。この一文に、エージェントの全体像が凝縮されている。

このアーキテクチャを理解するには、3つのレイヤーを順に積み上げればいい。まずChat Completion API——LLMと会話するための基本インターフェース。次にTool Use——LLMが外部の世界に手を伸ばす仕組み。そしてエージェントループ——ツールの結果を受け取り、次の行動を自律的に判断する繰り返し構造。チャットボットとエージェントの違いは、このループの有無にある。

本記事では、実装の詳細よりも概念と仕組みに焦点を当てる。特定のフレームワークに依存しない、LLM APIの「生の姿」を理解することが目的だ。Anthropic自身も「フレームワークではなく、まずAPIを直接使うことから始めるべきだ」と推奨している1

レイヤー1: Chat Completion API——LLMとの対話の基本構造

APIは「ステートレス」である

LLM APIを初めて使う人が最初に驚くのは、APIがステートレスであるという事実だろう。ChatGPTやClaudeのWebインターフェースでは会話が「続いている」ように見えるが、API側には会話の記憶がない。

毎回のAPIリクエストで、会話の全履歴を送り直す必要がある。WebアプリやCLIツールが会話を「覚えている」ように見えるのは、アプリケーション側が会話履歴を保持し、毎回APIに送信しているからだ。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 3ターン目のリクエスト  過去の会話をすべて含める
{
  "model": "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system": "回答は日本語で、コード例を含めて簡潔に答えてください。",
  "messages": [
    {"role": "user", "content": "Pythonでリストをソートする方法は?"},
    {"role": "assistant", "content": "sorted()関数またはlist.sort()メソッドが使えます..."},
    {"role": "user", "content": "逆順にソートするには?"},
    {"role": "assistant", "content": "reverse=Trueパラメータを指定します..."},
    {"role": "user", "content": "安定ソートですか?"}
  ]
}

これはHTTPの設計思想と同じだ。各リクエストは自己完結しており、サーバー側に状態を持たない。

メッセージの構造——ロールとコンテンツ

APIのメッセージはロールコンテンツの2要素で構成される2

ロール役割設定者
systemLLMの振る舞いを定義する指示(※)開発者
userユーザーからの入力エンドユーザー
assistantLLMの応答LLM(または開発者がプリフィルで設定)

systemはAPIによって実装が異なる。Anthropicではメッセージ配列の外に独立したパラメータとして存在し、OpenAIではメッセージ配列内のrole: "system"として含める。いずれの場合もユーザーには見えないが、LLMの振る舞いに大きく影響する。

コンテンツは単なるテキストに限らない。主要なLLM APIはテキストだけでなく、画像、ドキュメント、そして後述するツール呼び出しとその結果もコンテンツブロックとして扱える2。この構造がTool Useの基盤になっている。

コンテキストウィンドウ——LLMの「ワーキングメモリ」

APIに送信できるトークン数には上限がある。これをコンテキストウィンドウと呼ぶ。2026年現在、主要モデルでは128Kトークンから最大10Mトークンまで拡大しているが、実効容量は公称値の60〜70%程度とされる3。上限付近では段階的な劣化ではなく、突然の品質低下が起きる傾向がある。

エージェント開発において、コンテキストウィンドウの管理は決定的に重要だ。会話が長くなれば、古いメッセージの要約や不要な情報の削除が必要になる。ツール定義自体もトークンを消費するため、使わないツールを含めるだけでコストと品質の両面で影響がある。

レイヤー2: Tool Use——LLMが外の世界に手を伸ばす

「テキストを生成するだけ」の限界

Chat Completion APIだけでは、LLMは「テキストを受け取り、テキストを返す」箱にすぎない。今日の天気も、データベースの中身も、ファイルシステムの状態も知らない。

この限界を突破するのがTool Use(別名Function Calling)だ45。プロバイダーによって呼称が異なるが、仕組みはほぼ同一である。

ツールの定義——JSON Schemaで「道具」を教える

開発者はまず、LLMに「どんなツールが使えるか」をJSON Schemaで伝える4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "name": "get_weather",
  "description": "指定した都市の現在の天気を取得する",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "都市名(例: 東京、大阪)"
      }
    },
    "required": ["city"]
  }
}

ここで重要なのは、LLM自身がツールを実行するわけではないという点だ。LLMが行うのは「このツールを、この引数で呼んでほしい」というリクエストの生成だけ。実際の実行はアプリケーション側の責任だ。

リクエスト-レスポンスサイクル——6ステップの往復

Tool Useは以下のステップで動作する45

sequenceDiagram
    participant App
    participant LLM
    participant Tool
    App->>LLM: メッセージ+ツール定義
    LLM->>App: tool_useブロック
    App->>Tool: ツール実行
    Tool->>App: 実行結果
    App->>LLM: tool_result送信
    LLM->>App: 最終応答

ステップ2がこの仕組みの核心だ。LLMは通常の応答(stop_reason: "end_turn")ではなく、stop_reason: "tool_use"という特殊な停止理由を返す。これは「テキストの生成を中断して、ツールの実行を待っている」状態を意味する。

アプリケーションはこのシグナルを検知し、指定されたツールを実行し、結果をtool_resultとして再度APIに送信する。LLMはその結果を踏まえて最終的な応答を生成する。

ツール呼び出しの判断——LLMはどう「選ぶ」のか

LLMがツールを呼ぶかどうかは、ユーザーのリクエストツールのdescriptionの組み合わせで判断される4。ユーザーが「東京の天気は?」と聞き、get_weatherツールの説明文に「都市の天気を取得する」と書かれていれば、LLMはそのツールの呼び出しを選択する。

つまり、ツールのdescriptionはプロンプトの一部だ。Anthropicは「ツール設計にはプロンプトエンジニアリングと同等の注意を払うべき」と述べ、SWE-benchエージェントの開発では「全体のプロンプトよりもツールの最適化に多くの時間を費やした」としている1

tool_choiceパラメータを使えば、ツール呼び出しの挙動を制御できる45

設定挙動
auto(デフォルト)LLMが自動判断
any / required必ず何かのツールを呼び出す
特定ツール指定指定したツールのみ呼び出し
noneツールを使用しない

レイヤー3: エージェントループ——自律的な判断の繰り返し

チャットボットとエージェントの分水嶺

ここまでの2つのレイヤー(API + Tool Use)があれば、「ユーザーの質問に答えるために外部データを取得するチャットボット」は作れる。だが、それだけではエージェントにはならない。

チャットボットとエージェントの違いは、ループの有無だ1

  • チャットボット: ユーザーが質問 → LLMが応答(ツールを使うこともある) → 完了。次のアクションは常にユーザーが決める
  • エージェント: ユーザーがタスクを指示 → LLMが計画し、ツールを実行し、結果を評価し、次のアクションを自分で決める → タスクが完了するまで繰り返す

ReActパターン——思考と行動の交互実行

エージェントループの理論的基盤となったのが、2022年に提案されたReAct(Reasoning + Acting)パターンだ6

ReAct以前のアプローチには2つの限界があった。

  • Chain-of-Thought(CoT): LLMに「推論させる」が、外部情報にアクセスできないためハルシネーションのリスクがある
  • Action-only: ツールを呼び出すが、計画性がなく行き当たりばったりになる

ReActはこの2つを交互に実行することで、両方の弱点を補う6

flowchart TB
    T["Thought(思考)<br>現状を分析し<br>次の行動を推論"]
    A["Action(行動)<br>外部ツールを呼び出す"]
    O["Observation(観察)<br>ツールの結果を受け取る"]
    Done["タスク完了"]

    T --> A
    A --> O
    O -->|さらに情報が必要| T
    O -->|目標達成| Done

具体例で見てみよう。「Pythonのrequestsライブラリの最新バージョンを調べて、変更点をまとめて」というタスクの場合:

  1. Thought: 「まずPyPIでrequestsの最新バージョンを確認しよう」
  2. Action: search_pypi(package="requests") を呼び出す
  3. Observation: 「最新バージョンは2.32.3、2024年5月リリース」
  4. Thought: 「バージョンはわかった。次にCHANGELOGを確認しよう」
  5. Action: fetch_url(url="https://...") を呼び出す
  6. Observation: CHANGELOG内容を受信
  7. Thought: 「必要な情報が揃った。まとめを作成しよう」
  8. 最終応答: 変更点のまとめを出力

推論(Thought)が行動計画を導き、外部ツールの結果(Observation)が推論を根拠ある情報で支える。この相乗効果により、ReActはインタラクティブな意思決定タスク(ALFWorld)で34%の成功率向上を達成した6

エージェントループの実装構造

ReActの概念を実装に落とし込むと、エージェントループは驚くほどシンプルな構造になる。

1
2
3
4
5
6
7
8
9
10
messages = [初期タスク]
while True:
    response = llm.call(messages, tools)
    if response.stop_reason == "end_turn":
        break  # タスク完了
    if response.stop_reason == "tool_use":
        result = execute_tool(response.tool_call)
        messages.append(response)      # LLMの応答を履歴に追加
        messages.append(tool_result)    # ツール結果を履歴に追加
        # ループの先頭に戻り、LLMに再度判断させる

このループの各イテレーションで、LLMは以下を判断する1

  1. 何をすべきか — 次に呼ぶべきツールとその引数
  2. 終わったか — タスクが完了したかどうか

ループの終了条件は2つ。LLMがstop_reason: "end_turn"を返す(自然な完了)か、最大イテレーション数に達する(暴走防止)かだ。

ワークフローとエージェントの違い

Anthropicはエージェント的なシステムを2種類に区別している1

 ワークフローエージェント
制御の所在コード(開発者)LLM
処理の流れ事前定義されたパス動的に決定
ステップ数固定可変
適するタスク定型的で予測可能柔軟性が求められる
コスト・リスク低い高い(エラーが蓄積しうる)

ワークフローは「LLMを部品として使う決まった手順」、エージェントは「LLM自身が手順を決める」。たとえば「コードを生成 → テストを実行 → 結果を評価」という固定の流れはワークフロー。「GitHubのIssueを読んで、必要なファイルを特定し、修正し、テストを通す」はエージェントだ。

Anthropicは明確に、多くのアプリケーションではシンプルなLLM呼び出しの最適化で十分であり、エージェントはその複雑さに見合う場合にのみ導入すべきだと助言している1

本当に難しいのはループの外側

エージェントの構造自体は、ここまで見てきた通り驚くほどシンプルだ。whileループ、API呼び出し、ツール実行——コードにすれば数十行で書ける。実際、Claude CodeのようなAIコーディングツールに「エージェントのひな型を作って」と頼めば、動くコードは数分で手に入る。構造の実装はもはやボトルネックではない。

だが、そのエージェントを「使い物になる」レベルにするのは別の話だ。難しいのはループの構造ではなく、ループに渡す入力の質——つまりシステムプロンプト、ツールのdescription、そしてコンテキストの設計だ。

システムプロンプトの調整がその筆頭に来る。エージェントに「何者であるか」「何をすべきか」「何をしてはならないか」を伝えるのはシステムプロンプトだが、この指示の書き方ひとつでエージェントの振る舞いは劇的に変わる。長すぎれば重要な指示が埋もれる。短すぎれば想定外の行動を取る。曖昧な表現はLLMの解釈任せになる。

ツールのdescriptionも同様だ。前述の通り、Anthropicは「全体のプロンプトよりもツールの最適化に多くの時間を費やした」としている1。descriptionが不適切なら、LLMは正しいツールを選べない。パラメータの説明が曖昧なら、意図しない引数を生成する。ツールの設計はプロンプトエンジニアリングそのものだ。

コンテキストの管理も難題だ。エージェントがループを重ねるたびに会話履歴は膨張する。コンテキストウィンドウを使い切れば品質が崩壊するが、何を残して何を捨てるかの判断には正解がない。

つまり、エージェント開発の難易度は2つの層に分かれる。

 インフラ層設計層
何を作るかAPIクライアント、ループ構造、ツール実行基盤システムプロンプト、ツール定義、コンテキスト戦略
難易度低い(定型パターン)高い(試行錯誤が必要)
AIに任せやすいか任せやすい人間の判断が必要
差別化の源泉ならないなる

インフラ層はコード生成ツールに任せればいい。差がつくのは設計層——「このエージェントに何をどう伝えるか」だ。たとえばClaude Codeでは、スキルファイル(SKILL.md)にツールの振る舞いや判断基準を自然言語で記述するだけでエージェントの行動が変わる。同じループ構造でも、指示の書き方ひとつでまったく異なる品質のエージェントが生まれる。

まとめ

AIエージェントの仕組みは、3つのレイヤーの積み重ねで理解できる。

  1. Chat Completion API: ステートレスなリクエスト-レスポンス。会話履歴はアプリケーション側が管理する
  2. Tool Use: JSON Schemaでツールを定義し、LLMが「呼び出しリクエスト」を生成し、アプリケーションが実行する
  3. エージェントループ: ツールの結果をLLMに返し、次の行動を自律的に判断させることを繰り返す

エージェントの構造は「複雑な技術」ではなく「シンプルなループ」だ。コードを書くだけなら、AIコーディングツールに任せれば一瞬で終わる。しかし、そのループを賢く動かすための「言葉の設計」——システムプロンプト、ツール定義、コンテキスト管理——こそが、エージェントの品質を決める本当の勝負所だ。仕組みを理解することは出発点であり、本当の仕事はその先にある。

関連記事

このテーマに関連する他の記事もご覧ください:

参考資料

本文中の引用番号に対応する参考資料を番号順に記載しています。

  1. Building Effective Agents - Anthropic, Erik Schluntz & Barry Zhang (2024). 【信頼性: 高】 ↩︎ ↩︎2 ↩︎3 ↩︎4 ↩︎5 ↩︎6 ↩︎7 ↩︎8

  2. Messages API Reference - Anthropic (2024-2026). 【信頼性: 高】 ↩︎ ↩︎2

  3. Context Length Comparison: Leading AI Models in 2026 - Elvex (2026). 【信頼性: 中〜高】 ↩︎

  4. Tool use with Claude - Overview - Anthropic (2024-2026). 【信頼性: 高】 ↩︎ ↩︎2 ↩︎3 ↩︎4 ↩︎5

  5. Function calling - OpenAI API - OpenAI (2024-2026). 【信頼性: 高】 ↩︎ ↩︎2 ↩︎3

  6. ReAct: Synergizing Reasoning and Acting in Language Models - Yao et al., ICLR 2023 (2022). 査読済み国際会議論文。 【信頼性: 高】 ↩︎ ↩︎2 ↩︎3

This post is licensed under CC BY 4.0 by the author.