3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントの内部構造: シンプルなコード実装で理解するAIエージェントとMCP

3
Last updated at Posted at 2026-03-06

「AIエージェントを作ってみたいけど、LangChainとかLangGraphって難しそう……」
そう思っていませんか? 実は、AIエージェントの本質は驚くほどシンプルです。

本記事では、特定のライブラリを一切使わず、OpenAIのAPIとPythonだけで「最小構成のAIエージェント」を実装し、その仕組みを丸裸にします。


1. AIエージェントとは?

AIエージェントとは、LLM(大規模言語モデル)を「脳」として使い、自律的に「考えて行動(ツール実行)」 するシステムのことです。

単なるチャット(ChatGPT)との違いは、AIが自分自身の知識だけで答えるのではなく、「外の世界にある道具(天気予報、計算機、Google検索など)」を自分の判断で使いこなす点にあります。

  • チャット: 「今日の東京の天気は?」 → 「学習データによると……(古い情報の可能性)」
  • エージェント: 「今日の東京の天気は?」 → 「(ツールを使って最新情報を調べよう)……晴れですね!」

2. AIエージェントはどのように動いているのか?(ReAct)

AIエージェントの動きの基本は、「ReAct(Reasoning + Acting)」 と呼ばれるモデルです。

  1. Reasoning(思考): ユーザーの依頼を見て、「何が必要か」を考える。
  2. Acting(行動): 必要な道具(ツール)を呼び出す。
  3. Observation(観察): 道具の実行結果を受け取り、状況を確認する。
  4. Thinking Again(再考): 結果を元に、さらに行動が必要か、回答できるかを判断する。

このサイクルを、回答が出るまで繰り返します。


3. 実装してみる:世界一シンプルなAIエージェント

「天気予報を教えるツール」を持つ最小のエージェントを書いてみます。

import os
import json
from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY")

# --- ツール実装 ---
def get_weather(location):
    return f"{location}の天気は『晴れ』、気温は25度です。"


# --- ツールレジストリ ---
tool_functions = {
    "get_weather": get_weather
}


# --- OpenAIに渡すツール定義 ---
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "指定された場所の現在の天気を取得する",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {"type": "string"}
            },
            "required": ["location"]
        }
    }
}]


messages = [{"role": "user", "content": "東京の天気を教えて"}]


for _ in range(5):

    response = client.chat.completions.create(
        model="gpt-5-mini-2025-08-07",
        messages=messages,
        tools=tools
    )

    res_msg = response.choices[0].message
    messages.append(res_msg)

    if not res_msg.tool_calls:
        break

    # --- ツール実行 ---
    for tool_call in res_msg.tool_calls:

        function_name = tool_call.function.name
        # AIが呼び出してほしいツールの関数名を取り出す
        arguments = json.loads(tool_call.function.arguments)
        # 続いて引数も取り出す

        # 対応する関数を取得
        function_to_call = tool_functions[function_name]

        # 実行
        result = function_to_call(**arguments)

        # AIに結果を返す
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result
        })


print("最終回答:", messages[-1].content)

4. 実際の動きの流れ

実際に weather_agent.py を実行した際の、リアルなデータのやり取りを追ってみましょう。AIエージェントが「思考」し、「行動」し、その結果を元に「回答」するまでの全記録です。

STEP 1: 初回の思考(Loop 1)

まず、ユーザーの問いかけと「ツールの定義」が OpenAIのAPI に送られます。

送信データ (Request Messages):

[
  { "role": "user", "content": "東京の天気を教えて" }
]

送信されるツール定義

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "指定された場所の現在の天気を取得する",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string"}},
            "required": ["location"]
        }
    }
}]

その後、下記のAPIレスポンスがOpenAIから返ってきます。

AI からの返答 (Response):

{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_bTQc4vyuTlvxW4D9ZIdkRHH1",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"location\":\"東京\"}"
      }
    }
  ]
}

このレスポンスでは、AIは通常の回答文(content)を返していません。
その代わりに、tool_calls を使って 「ツール(関数)を実行してほしい」という指示を返しています。

具体的には、

get_weather という関数を

location = 東京 という引数で

実行してほしい、という意味です。

つまりAIは、この時点ではまだ最終回答を出しておらず、
「東京の天気を取得するために get_weather を実行してください」
と、プログラム側に依頼している状態です。

この指示を受け取ったプログラムは、get_weather("東京") を実行し、その結果を再びAIに渡すことで、最終的な回答を生成します。


STEP 2: ツールの実行と結果の報告

AIからのレスポンスに tool_calls が含まれている場合、
それは 「このツール(関数)を実行してほしい」 というAIからの指示です。

プログラム側では、この tool_calls の内容を読み取り、
指定された関数を実行し、その結果を 次のリクエストの会話履歴(messages)に追加 します。

以下のコードでは、

  1. tool_calls が存在するか確認
  2. AIが指定した関数名と引数を取り出す
  3. 対応する関数を実行
  4. 実行結果を messages に追加

という流れで処理を行っています。

# OpenAIのレスポンスにtool_callsが含まれるかチェック
if not res_msg.tool_calls:
    break

# --- ツール実行 ---
for tool_call in res_msg.tool_calls:

    # AIが呼び出してほしい関数名を取得
    function_name = tool_call.function.name

    # 引数(JSON文字列)をPythonの辞書に変換
    arguments = json.loads(tool_call.function.arguments)

    # 対応する関数を取得
    function_to_call = tool_functions[function_name]

    # 関数を実行
    result = function_to_call(**arguments)

    # 実行結果をAIに返す
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": result
    })

関数呼び出しの仕組み

上記のコードの次の部分では、
AIが指定した関数名をもとに、実際のPython関数を取得して実行しています。

# 対応する関数を取得
function_to_call = tool_functions[function_name]

# 実行
result = function_to_call(**arguments)

ここで参照している tool_functions は、
ツールとして利用できる関数を登録した辞書(ツールレジストリ) です。

def get_weather(location):
    return f"{location}の天気は『晴れ』、気温は25度です。"

# --- ツールレジストリ ---
tool_functions = {
    "get_weather": get_weather
}

この仕組みにより、AIが

get_weather(location="東京")

Python側では自動的に get_weather 関数が実行されます。

ツール実行結果の保存

get_weather の実行が終わると、その結果は
OpenAIとの会話履歴(messages)に「ツールの実行結果」として追加 されます。

追加されるデータは次のような形式になります。

{
  "role": "tool",
  "tool_call_id": "call_bTQc4vyuTlvxW4D9ZIdkRHH1",
  "name": "get_weather",
  "content": "東京の天気は『晴れ』、気温は25度です。"
}

ここが重要

tool_call_id を付けることで、

「さっきAIが依頼したツール実行(call_bTQc4vyu...)の結果はこれです」

という形で、AIのリクエストとツールの実行結果を正しく紐付けることができます。

これによりAIはツールの結果を理解し、
その情報をもとに 最終的な回答を生成できるようになります。

STEP 3: 最終的な判断(Loop 2)

ここまでで、会話履歴には次の3つの情報が揃いました。

  1. ユーザーの質問
  2. AIが依頼したツール実行
  3. ツールの実行結果

この すべての履歴(messages) を、もう一度 OpenAI に送ります。

送信データ(Request Messages

[
  { "role": "user", "content": "東京の天気を教えて" },
  { "role": "assistant", "tool_calls": [...] },
  { "role": "tool", "content": "東京の天気は『晴れ』..." }
]

この情報を受け取ったAIは、

  • ユーザーの質問

  • 自分が依頼したツール実行

  • そのツールの実行結果

をすべて確認し、
「もう回答できるだけの情報が揃っているか?」 を判断します。

もし 情報がまだ足りない と判断した場合は、
再び tool_calls を返して、別のツールを呼び出すよう指示します。

今回は、ツールの結果だけで十分だと判断されたため、
AIは次のような最終回答を返してきました。

AIからの最終返答(Response)

{
  "role": "assistant",
  "content": "東京の天気は晴れで、気温は25度です。"
}

このレスポンスでは tool_calls が存在せず、
content人間への回答文 が入っています。

つまりAIは、

「必要な情報はすべて揃った」

と判断し、
ツールの結果をもとに 最終的な回答を生成した ということです。

実行結果のまとめ

最終的にターミナルには、次のような結果が表示されます。

=== Final Answer ===
東京の天気は晴れで、気温は25度です。

この一連の流れを見ると、AIは次のステップで問題を解決していることが分かります。

  • ユーザーの質問を理解する

  • 必要な情報を取得するためにツールを呼び出す

  • ツールの結果を確認する

  • 最終的な回答を生成する

つまり、AIは単に文章を生成しているだけではなく、
ツール(外部機能)を使いながら問題を解決するエージェントとして動いている のです。

5. LangGraph やほかのフレームワークはどう動いている?

有名なLangGraphやそのほかのフレームワークも、
根本的な仕組みは ここまで見てきたコードとほぼ同じ です。
では、これらのフレームワークは何をしてくれるのでしょうか?

  • 処理の自動化
    今回は for ループで AI → ツール → AI の流れを自分で実装しましたが、フレームワークではこの処理を自動で回してくれます。

  • ワークフロー管理
    「Aの処理のあとにBを実行する」「失敗したら別の処理へ分岐する」といった流れを、コードやグラフ構造で分かりやすく定義できます。

  • 状態管理
    長い会話の履歴やエージェントの状態を保存し、途中で処理を止めたり、後から再開するような仕組みも簡単に扱えます。

つまりフレームワークは、
今回作ったシンプルなエージェントのループを、より大規模で複雑な用途でも扱えるように整理・拡張したものと言えます。

ですが、最後に大事なポイントとして、
フレームワークを使っても 基本的な仕組みは変わりません

AIがツールを呼び出し、ツールの結果を受け取り、再度判断して回答を生成する。

この一連の流れを 頭の中にイメージしておくことが、AIエージェントを理解する一番の近道です。

6. ツールとMCPの関係

ここまでの例では、AIがツールを呼び出す仕組みとして
get_weather というPythonの関数を登録し、AIの指示に従ってプログラム側で実行しました。

流れを整理すると、次のようになっていました。

  1. ユーザーが質問する
  2. AIが「ツールを使うべきか」を判断する
  3. ツール(関数)を呼び出す
  4. ツールの結果をAIに返す
  5. AIが最終回答を生成する

この 「AI → ツール → AI」 の流れが、AIエージェントの基本構造です。

しかし実際のアプリケーションでは、ツールはPythonの関数だけではありません。
たとえば次のような外部サービスと連携することも多くなります。

  • データベース
  • 社内API
  • GitHub
  • Slack
  • ファイルシステム

もしツールごとに接続方法が違うと、AIから利用するたびに
個別の連携コードを書く必要があり、実装が非常に複雑になります。

そこで登場したのが
MCPです。

MCPは、AIが外部ツールを呼び出すための共通ルール(プロトコル) を定義しています。
ツールを MCPサーバー として公開することで、AIはさまざまなツールを同じ方法で利用できるようになります。


USBの仕組みに例えると理解しやすい

この関係は、PCの USB の仕組みによく似ています。

PCにはさまざまな機器を接続できます。

  • マウス
  • キーボード
  • USBメモリ
  • 外付けSSD

これらはすべて種類の違う機器ですが、
USBという共通規格があるおかげで、PCは同じ方法で接続できます。

AIエージェントの世界でも同じで、

  • ツール → USB機器
  • MCP → USB規格

のような関係になっています。

つまりMCPは、
AIと外部ツールの間に共通ルールを作り、さまざまなサービスを簡単に接続できるようにする仕組みです。


ここまで理解すると、AIエージェントの全体像は次のように整理できます。

ユーザー
↓
LLM(判断)
↓
ツール呼び出し
↓
MCP / API / 関数
↓
結果をLLMへ返す
↓
最終回答

今回作ったシンプルなエージェントの仕組みは、
実は 現在のAIエージェントフレームワークやMCPベースのツール連携の基礎そのもの になっています。

まとめ:仕組みを知れば、エージェントは怖くない

AIエージェントの本質は、

「状態(メッセージ履歴)を持ちながら、外部関数の実行結果をフィードバックするループ処理」

に過ぎません。

特定のライブラリの使い方を覚える前に、
この標準機能のみ の実装を理解することで、
AIを思い通りに制御する真のスキルが身につきます。

「脳(LLM)」に「手足(ツール)」を繋ぐ「神経(ループ)」を自分で書く。これこそがエージェント開発の基礎となるでしょう。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?