ブログ AI / Agent

2026 AI Agent 技術スタック:LLM、MCP、Function Calling、JSON Schema はどう組み合わせる?

AI Agent を組むと、ドキュメントに LLM、MCP、Function Calling、JSON Schema が同時に出てきます。四つとも「モデルにツールを呼ばせる」ように見え、四択になりがちです。

四つの代替案ではありません。LLM は推論し、Function Calling は意思決定を実行可能な JSON にシリアライズし、MCP は外部サービスに接続して構造化結果を返し、JSON Schema は三層を貫く契約言語です。

本記事では次を説明します。

まずこれを覚えてください:2026 年の Agent スタックは「JSON が要るか」ではなく「意思決定・実行・契約をどの層に置くか」を議論します。モデルが考え、Function Calling がシリアライズし、MCP が実行し、JSON Schema が各ホップを検証します。以下、責務 → データフロー → 各層 → 組み合わせ → 取り違えの順です。

四層は何をするか

先に責務を分けます。混ざるとデバッグは「モデルが悪い」「MCP が壊れた」になり、多くはどちらでもなく境界が無いだけです。

責務 入力 出力
LLM 推論し、計画し、次の一手を決める 会話、ツール結果、システムプロンプト 会話を続けるか、「このツールを呼ぶ」
Function Calling 「どのツール、どの引数」を API オブジェクトにする tools array + conversation tool_calls[].arguments JSON
MCP ツール発見、実行、構造化結果の返却 tools/call + arguments structuredContent JSON
JSON Schema 各ホップの JSON を記述し検証する schema 文書 成功 / 失敗理由

一言:LLM が考え、Function Calling が言い、MCP が行い、JSON Schema が検証する。

JSON Schema の書き方、Tool Calling と Structured Output の違いはAI Agent に JSON Schema が必要な理由を参照。本記事は四層のつなぎ方だけを扱います。

一度のツール呼び出しのデータフロー

「チケット番号で照会」を例にします。ユーザーが「T-1042 を見て」と言っても、Runtime はその文をチケットシステムに投げません。次の六ステップを踏みます。

  1. 1
    Runtime がリクエストを組み立てる

    会話、システムプロンプト、tools 配列を LLM API に送ります。各 tool の parameters は JSON Schema です。

  2. 2
    モデルが Function Calling する

    返すのは name=get_ticket と JSON 文字列の arguments であり、「調べてください」という自然文ではありません。

  3. 3
    Runtime が入参を検証する

    json.loads のあと、同じ schema で validate します。欠落や型違いは再試行か拒否。汚れた引数を下流に送らない。

  4. 4
    MCP が実行する

    検証済み arguments を MCP tools/call に入れます。Server が照会し structuredContent を返します。

  5. 5
    Runtime が出参を検証する

    structuredContent は MCP outputSchema に適合する必要があります。通ってからモデルへ戻します。

  6. 6
    モデルが最終回答を出す

    自然言語でも、別 schema の Structured Output で最終 JSON を縛ってもよい。

validate は二回です。モデルの乱参を一度、Server の乱返却を一度。どちらかを省くと、次層は汚れたデータを事実として扱います。

LLM:推論と意思決定だけ

LLM はスタックで唯一「考える」層です。文脈を読み、回答・追加質問・ツール呼び出しを決めます。データベース、決済 API、ファイルシステムには直接触れません。

2026 年、各社のモデル API は同じ意味論に揃っています。tools を渡し、モデルは tool_calls か最終テキストを返します。

OpenAI はFunction Calling / Responses API、Anthropic はTool Use、DeepSeek は OpenAI のフィールド名に合わせます。差はフィールド名と Strict 対応であり、「構造化すべきか」ではありません。

LLM を実行器にすると、秘密が prompt に入り、副作用を監査できません。実行は MCP に残し、モデルは「誰を、どの引数で呼びたいか」だけを出します。

Function Calling:モデル API 層のツール選択

Function Calling はプロトコルではなく、モデル API の応答形です。次の一手は機械可読の「関数名 + 引数」でなければならず、散文ではありません。

モデルへ送る tools 定義(parameters が JSON Schema)
{
  "model": "gpt-5",
  "messages": [
    {"role": "user", "content": "Look up ticket T-1042"}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_ticket",
        "description": "Fetch a support ticket by id",
        "parameters": {
          "type": "object",
          "properties": {
            "ticketId": {
              "type": "string",
              "pattern": "^T-[0-9]+$"
            }
          },
          "required": ["ticketId"],
          "additionalProperties": false
        }
      }
    }
  ]
}

返る arguments は文字列であり、パース済みオブジェクトではありません。Runtime が自分で loads + validate します。Strict Schema でも同じです。API 層の conform は業務検証の代わりになりません。これは2026 Structured Outputの response_format と同じ種類の問題で、拘束対象が最終回答からツール引数に変わっただけです。

Function Calling はここで終わります。ツールの実装や結果の形は知りません。arguments を誰に渡すかは Runtime の仕事です。

MCP:Agent と外界のプロトコル

MCP(Model Context Protocol)は Agent Runtime と外部サービスの標準通話です。ツール一覧、呼び出し、構造化結果。2026-07-28 で session ハンドシェイクは消え、各リクエストは自己記述なので、Server は普通の HTTP と同様に負荷分散できます。背景はAI Conference 2026 の注目点.

MCP ツール定義:inputSchema / outputSchema は JSON Schema 2020-12
{
  "name": "get_ticket",
  "description": "Fetch a support ticket by id",
  "inputSchema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "ticketId": {
        "type": "string",
        "pattern": "^T-[0-9]+$"
      }
    },
    "required": ["ticketId"]
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "ticketId": { "type": "string" },
      "status": { "type": "string", "enum": ["open", "pending", "closed"] },
      "assignee": { "type": ["string", "null"] }
    },
    "required": ["ticketId", "status"]
  }
}

Runtime は Function Calling で検証済みの arguments をそのまま tools/call に入れます。Server の structuredContent は outputSchema を通る必要があります。Client 側でもう一度 validate——データ源がモデルから Server に変わっただけで、工学上の問題は同じです。

公式ツール仕様:MCP Tools。inputSchema の根は依然 object。2026-07-28 以降、outputSchema は任意の JSON 値でよい。

JSON Schema:三層を貫く契約

JSON Schema は第四の「機能モジュール」ではなく、他三層が共有する記述言語です。現在の主流は2020-12.

出現位置 フィールド 何を拘束するか 誰が検証するか
Function Calling tools[].function.parameters モデルが生成した arguments Runtime(MCP 呼び出し前)
Structured Output response_format.json_schema モデルの最終回答 Runtime(業務へ渡す前)
MCP 入力 inputSchema tools/call の arguments Runtime + Server
MCP 出力 outputSchema structuredContent Runtime(モデルへ戻す前)

組み合わせで最も多い落とし穴:parameters と inputSchema を別々に書き、フィールド名や enum がずれる。モデルは A で生成し、Server は B で検証し、「モデルがいつもツールに失敗する」ように見えます。

正しいやり方は一つの契約、二か所へのマウント。同じ schema オブジェクトから parameters と inputSchema を生成します。最終回答も下流に渡すなら Structured Output 用を別に用意し、ツール入力と混ぜない。

参照アーキテクチャ:四層のつなぎ方

本番の既定のつなぎ方は次のとおりです。ローカル関数や自前 HTTP API を当面 MCP の代わりにできますが、境界は動かさないでください。

コンポーネント すること しないこと
LLM API 推論し、tool_calls または最終 JSON を返す 秘密を持たず、内部システムへ直接アクセスしない
Agent Runtime tools を組み立て、検証し、MCP へ経路し、回填し、ループする 自然言語を引数にせず、validate を飛ばさない
MCP Server 副作用を実行し、outputSchema に合う JSON を返す prompt を解析せず、モデルの代わりに計画しない
Schema カタログ 単一ソース。API tools と MCP の両方に供給 二か所に「ほぼ同じ」JSON を手書きしない
Runtime が Function Calling を MCP につなぐ(疑似コード)
schema = catalog.get("get_ticket")          # single source
req.tools = [{ "type": "function",
               "function": { "name": "get_ticket",
                             "parameters": schema.input } }]

resp = llm.chat(req)
args = json.loads(resp.tool_calls[0].arguments)
validate(args, schema.input)                # stop bad model args

content = mcp.call("get_ticket", args)      # MCP executes
validate(content.structuredContent, schema.output)

req.messages.append(tool_result(content))   # feed back, next turn

ループは何周でもよい。モデルは structuredContent を見て次の tool か最終回答を決めます。Schema カタログは変わりません。増えるのは messages に積もるツール結果です。

層を取り違えるとどうなるか

四層が組み合わさるのは、各層が一件だけをするからです。責務が隣へ滑ると、障害はモデル能力の問題に見えます。

誤り 表面上 実際の結果
LLM に直接 HTTP を打たせる フレームワークが一つ減り、早く出せる 秘密が文脈に入り、監査できず、モデルを替えるたびに呼び出しを書き直す
MCP を Function Calling の代わりに使う プロトコルにも name / arguments がある モデルは tools 一覧を見ないので、安定して選べない
prompt だけで JSON を求め、schema を書かない デモは動く 回帰できず、フィールドが流れ、下流の parse が毎日壊れる
parameters と inputSchema を別々に書く どちらも「動く」 enum / 必須がずれ、ランダムな tool 失敗になる
モデル出力を信じ、validate を飛ばす Strict / JSON Mode を入れた 欠落、切り詰め、型エラーが業務に直撃する

判断の口訣:モデルは意思決定 JSON だけを出し、実行は MCP(または明示したバックエンド)だけ、出入りに schema が要る。

JSONNote でこの経路をデバッグする

この経路には少なくとも三つの JSON があります。モデル arguments、MCP structuredContent、最終 Structured Output。どれもローカルで format / validate / diff でき、Key もサンプルもアップロード不要です。

  1. 1
    arguments を整形する

    tool_calls.arguments をJSON フォーマッタに貼り、まず構文壊れと切り詰めを除外します。

  2. 2
    同じ schema で検証する

    parameters / inputSchema と arguments をJSON Schemaに貼る。失敗点が Runtime の拒否位置です。

  3. 3
    二回の呼び出しを比較する

    prompt や schema を一版変えたら、JSON Diffで arguments や structuredContent のどのフィールドが動いたかを見ます。

  4. 4
    デバッグ現場を共有する

    URL Hash 共有で tool call をリンクに書き、同僚が開けば再現できます。データはサーバを通りません。

よくある質問

Function Calling と MCP は必ず併用しますか?

必須ではありません。ローカル関数や HTTP API もツールバックエンドになれます。MCP の価値は発見・呼び出し・structuredContent の標準化。Function Calling は name と arguments を選ばせるだけです。よく組み合わせますが、単独でも成立します。

JSON Schema は何通書けばよいですか?

最低二通。ツール入力(Function Calling の parameters と MCP inputSchema を揃える)と、ツール出力または最終回答(MCP outputSchema または Structured Output の response_format)。モデル側と MCP 側で別契約を書かないでください。

MCP なしで Function Calling だけでも足りますか?

単体デモなら足ります。複数 Agent / ランタイムで再利用し、水平拡張するなら、MCP を省くと tools/list・認証・構造化返却を自作することになります。2026 年の既定は Function Calling で選び、MCP で実行です。

モデルが JSON を返したのに、Runtime で検証は必要ですか?

必要です。Function Calling の arguments も MCP の structuredContent も、欠落・型違い・構文壊れが起き得ます。API 層の conform は業務正しさではありません。検証は Runtime の責任です。

まとめ

2026 年の Agent 技術スタックは三文に収まります。

LLM が意思決定し、Function Calling がシリアライズし、MCP が実行する。JSON Schema は三層共用の契約であり、第四のオプションではない。

入力 schema と出力 schema を一つに固定し、モデル API と MCP Server に同じものを載せます。Runtime は両ホップで validate します。モデルとプロトコルはまだ変わります。契約と検証層が Agent をデモから本番へ連れていく防波堤です。その JSON をデバッグするとき、JSONNote のローカル format / schema / diff が使えます。

← ブログに戻る