メインコンテンツへスキップ
Code & Craft
AI活用 約8分で読めます

Claude API Function Calling で複雑なLLMワークフローを実装する完全ガイド【2026年4月最新】

Claude 3.5 Sonnet の Function Calling を使った実践的な実装方法を解説。エラーハンドリング、マルチステップ処理、コスト最適化まで網羅した開発者向けガイド。

Claude API Function Calling とは?なぜ今注目されるのか

2024年10月にリリースされた Claude 3.5 Sonnet の Function Calling(Tool Use)機能は、2026年4月現在、LLMを使った複雑なワークフロー構築の標準手法として確立しています。

従来のプロンプトベースのアプローチでは、「外部APIを呼び出してその結果を使って次の処理を実行する」といったマルチステップの処理を実装する際、プロンプトインジェクションのリスクや出力パースの不安定さが課題でした。

Function Calling は、LLMが構造化されたJSON形式で関数呼び出しを返す仕組みにより、これらの問題を根本から解決します。ChatGPT の Function Calling と異なり、Claude は複数ツールの並列呼び出しエラー後の自動リトライ提案など、より実用的な機能を備えています。

本記事では、2026年4月時点の最新ドキュメント(Anthropic公式 2026年3月29日更新)に基づき、実際のコード例とともに実装方法を解説します。

Claude Function Calling の基本実装

基本的なツール定義と呼び出し

Claude API でツールを使用するには、tools パラメータでツールのスキーマを定義し、モデルに渡します。

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

// ツールの定義
const tools = [
  {
    name: "get_weather",
    description: "指定された都市の現在の天気情報を取得します",
    input_schema: {
      type: "object",
      properties: {
        city: {
          type: "string",
          description: "都市名(例: Tokyo, New York)",
        },
        unit: {
          type: "string",
          enum: ["celsius", "fahrenheit"],
          description: "温度の単位",
        },
      },
      required: ["city"],
    },
  },
];

// Claude API 呼び出し
const message = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  messages: [
    {
      role: "user",
      content: "東京の現在の天気を教えてください",
    },
  ],
});

console.log(message.content);

ツール実行結果の返却とマルチターン処理

Claude がツールの使用を提案した場合、stop_reason"tool_use" になります。この場合、開発者側でツールを実行し、結果を次のリクエストで返す必要があります。

// Claude のレスポンスからツール呼び出しを検出
const toolUseBlock = message.content.find(
  (block) => block.type === "tool_use"
);

if (toolUseBlock && toolUseBlock.type === "tool_use") {
  // ツールを実行(例: 天気API呼び出し)
  const weatherData = await fetchWeatherAPI(toolUseBlock.input.city);

  // 実行結果を Claude に返す
  const followUpMessage = await client.messages.create({
    model: "claude-3-5-sonnet-20250929",
    max_tokens: 1024,
    tools: tools,
    messages: [
      {
        role: "user",
        content: "東京の現在の天気を教えてください",
      },
      {
        role: "assistant",
        content: message.content, // Claude の最初のレスポンス全体
      },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: toolUseBlock.id,
            content: JSON.stringify(weatherData),
          },
        ],
      },
    ],
  });

  console.log(followUpMessage.content);
}

処理フローの全体像

sequenceDiagram
    participant User as ユーザー
    participant App as アプリケーション
    participant Claude as Claude API
    participant Tool as 外部ツール/API

    User->>App: "東京の天気を教えて"
    App->>Claude: messages.create() + tools定義
    Claude->>App: stop_reason: "tool_use"<br/>tool_use block
    App->>Tool: fetchWeatherAPI("Tokyo")
    Tool->>App: { temp: 15, condition: "晴れ" }
    App->>Claude: tool_result を含む messages
    Claude->>App: 最終回答テキスト
    App->>User: "東京は現在15度、晴れです"

上図は Claude Function Calling の典型的な処理フローを示しています。ユーザーの質問→ツール提案→実行→結果返却→最終回答という流れが1サイクルです。

複数ツールの並列実行と依存関係の制御

並列実行可能なツールの定義

Claude 3.5 Sonnet は1回のレスポンスで複数のツールを同時に提案できます。これにより、独立した処理を並列実行してレイテンシを削減できます。

const tools = [
  {
    name: "get_stock_price",
    description: "指定された銘柄の現在の株価を取得",
    input_schema: {
      type: "object",
      properties: {
        symbol: { type: "string", description: "銘柄コード(例: AAPL)" },
      },
      required: ["symbol"],
    },
  },
  {
    name: "get_company_news",
    description: "指定された企業の最新ニュースを取得",
    input_schema: {
      type: "object",
      properties: {
        company: { type: "string", description: "企業名" },
      },
      required: ["company"],
    },
  },
  {
    name: "calculate_portfolio_value",
    description: "ポートフォリオの現在価値を計算",
    input_schema: {
      type: "object",
      properties: {
        holdings: {
          type: "array",
          items: {
            type: "object",
            properties: {
              symbol: { type: "string" },
              shares: { type: "number" },
            },
          },
        },
      },
      required: ["holdings"],
    },
  },
];

const message = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 2048,
  tools: tools,
  messages: [
    {
      role: "user",
      content: "AppleとMicrosoftの株価と最新ニュースを調べてください",
    },
  ],
});

// 複数のツール呼び出しを検出
const toolUseBlocks = message.content.filter(
  (block) => block.type === "tool_use"
);

console.log(`${toolUseBlocks.length} 個のツールが並列実行されます`);
// 出力例: "4 個のツールが並列実行されます"
// get_stock_price(AAPL), get_stock_price(MSFT), get_company_news(Apple), get_company_news(Microsoft)

並列実行の実装パターン

// 並列実行とエラーハンドリング
const toolResults = await Promise.allSettled(
  toolUseBlocks.map(async (toolUse) => {
    if (toolUse.type !== "tool_use") return null;

    try {
      let result;
      switch (toolUse.name) {
        case "get_stock_price":
          result = await fetchStockPrice(toolUse.input.symbol);
          break;
        case "get_company_news":
          result = await fetchCompanyNews(toolUse.input.company);
          break;
        default:
          throw new Error(`未知のツール: ${toolUse.name}`);
      }

      return {
        type: "tool_result" as const,
        tool_use_id: toolUse.id,
        content: JSON.stringify(result),
      };
    } catch (error) {
      // エラーをClaudeに返す(エラー処理の推奨パターン)
      return {
        type: "tool_result" as const,
        tool_use_id: toolUse.id,
        content: JSON.stringify({ error: error.message }),
        is_error: true,
      };
    }
  })
);

// 成功した結果のみを抽出
const successfulResults = toolResults
  .filter((r) => r.status === "fulfilled" && r.value !== null)
  .map((r) => (r.status === "fulfilled" ? r.value : null));

ツール間の依存関係を持つワークフロー

一部のツールが他のツールの結果に依存する場合、複数回のAPI呼び出しを連鎖させます。

// ステップ1: ユーザーIDから注文履歴を取得
const step1 = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: [
    {
      name: "get_user_orders",
      description: "ユーザーの注文履歴を取得",
      input_schema: {
        type: "object",
        properties: {
          user_id: { type: "string" },
        },
        required: ["user_id"],
      },
    },
  ],
  messages: [
    { role: "user", content: "ユーザーID user_123 の注文履歴を分析して" },
  ],
});

// ツール実行(注文履歴取得)
const orders = await getUserOrders("user_123");

// ステップ2: 注文履歴から推奨商品を計算
const step2 = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: [
    {
      name: "recommend_products",
      description: "注文履歴から推奨商品を計算",
      input_schema: {
        type: "object",
        properties: {
          order_history: { type: "array" },
        },
        required: ["order_history"],
      },
    },
  ],
  messages: [
    { role: "user", content: "ユーザーID user_123 の注文履歴を分析して" },
    { role: "assistant", content: step1.content },
    {
      role: "user",
      content: [
        {
          type: "tool_result",
          tool_use_id: step1.content.find((b) => b.type === "tool_use")!.id,
          content: JSON.stringify(orders),
        },
      ],
    },
  ],
});
flowchart LR
    A["ユーザー入力"] --> B["Claude API 呼び出し 1<br/>get_user_orders"]
    B --> C["注文履歴取得<br/>外部API"]
    C --> D["Claude API 呼び出し 2<br/>recommend_products"]
    D --> E["推奨商品計算<br/>外部API"]
    E --> F["最終レスポンス"]

上図は依存関係のあるツール呼び出しのフローです。前のツールの結果が次のツールの入力になる場合、複数回のAPI呼び出しが必要です。

エラーハンドリングとリトライ戦略

is_error フラグを使ったエラー通知

Claude 3.5 では、ツール実行失敗時に is_error: true フラグを付けて結果を返すことで、モデルにエラー発生を明示的に伝えることができます。

const toolResult = {
  type: "tool_result" as const,
  tool_use_id: toolUse.id,
  content: JSON.stringify({
    error: "API rate limit exceeded. Please try again in 60 seconds.",
    error_code: "RATE_LIMIT",
    retry_after: 60,
  }),
  is_error: true, // エラーフラグ
};

const response = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  messages: [
    { role: "user", content: "最新のニュースを取得して" },
    { role: "assistant", content: previousResponse.content },
    { role: "user", content: [toolResult] },
  ],
});

// Claude は自動的に代替案を提案する
// 例: "APIのレート制限に達しました。1分後に再試行するか、別の情報源を使用しますか?"

自動リトライとエクスポネンシャルバックオフ

async function executeToolWithRetry(
  toolName: string,
  input: any,
  maxRetries = 3
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await executeTool(toolName, input);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;

      const waitTime = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      console.log(`リトライ ${attempt + 1}/${maxRetries} (${waitTime}ms 待機)`);
      await new Promise((resolve) => setTimeout(resolve, waitTime));
    }
  }
}

部分的な成功の処理

複数ツールの並列実行時、一部が失敗した場合でも成功した結果を Claude に返すことで、可能な範囲で回答を生成できます。

const toolResults = await Promise.allSettled(toolUseBlocks.map(executeTool));

const resultsForClaude = toolResults.map((result, index) => {
  const toolUse = toolUseBlocks[index];
  if (result.status === "fulfilled") {
    return {
      type: "tool_result" as const,
      tool_use_id: toolUse.id,
      content: JSON.stringify(result.value),
    };
  } else {
    return {
      type: "tool_result" as const,
      tool_use_id: toolUse.id,
      content: JSON.stringify({ error: result.reason.message }),
      is_error: true,
    };
  }
});

// 部分的な結果でも Claude に返す
const response = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  messages: [
    { role: "user", content: originalQuery },
    { role: "assistant", content: previousResponse.content },
    { role: "user", content: resultsForClaude },
  ],
});

// Claude: "株価は取得できましたが、ニュースAPIがエラーを返しました。株価情報のみでよろしいですか?"

tool_choice による実行制御とコスト最適化

tool_choice パラメータの使い方

tool_choice パラメータを使うことで、ツール使用の強制/禁止/特定ツール指定が可能です。

// パターン1: ツール使用を強制(必ず何かしらのツールを呼び出す)
const forceToolUse = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  tool_choice: { type: "any" }, // いずれかのツールを必ず使用
  messages: [{ role: "user", content: "データを取得して" }],
});

// パターン2: 特定のツールを強制
const forceSpecificTool = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  tool_choice: { type: "tool", name: "get_weather" }, // get_weather を必ず使用
  messages: [{ role: "user", content: "今日の予定を教えて" }],
});

// パターン3: ツール使用を自動判断(デフォルト)
const autoToolUse = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: tools,
  tool_choice: { type: "auto" }, // Claude が必要に応じて判断
  messages: [{ role: "user", content: "こんにちは" }],
});

コスト最適化のベストプラクティス

2026年4月現在、Claude 3.5 Sonnet の料金は入力: $3.00 / 1M tokens、出力: $15.00 / 1M tokensです。Function Calling を多用すると、以下の理由でコストが増加します。

  1. ツール定義のスキーマがトークンを消費(毎回リクエストに含まれる)
  2. マルチターンの会話履歴が累積(tool_result を含む全履歴を毎回送信)
// ❌ コストが高い例: 不要なツールまで毎回定義
const allTools = [
  weatherTool,
  stockTool,
  newsTool,
  databaseTool,
  emailTool,
  calendarTool,
]; // 6個のツール × 平均200トークン = 1200トークン/リクエスト

// ✅ コスト最適化: 必要なツールのみ動的に選択
function selectRelevantTools(userQuery: string) {
  if (userQuery.includes("天気")) return [weatherTool];
  if (userQuery.includes("株価")) return [stockTool];
  return []; // ツール不要な場合は空配列
}

const message = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  tools: selectRelevantTools(userInput), // 動的に絞り込む
  messages: [{ role: "user", content: userInput }],
});

Prompt Caching との組み合わせ

2026年3月のアップデートで、Function Calling がPrompt Caching と併用可能になりました。ツール定義を system プロンプト内でキャッシュすることで、トークンコストを最大90%削減できます。

const response = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 1024,
  system: [
    {
      type: "text",
      text: "あなたは天気予報アシスタントです。",
      cache_control: { type: "ephemeral" }, // この部分をキャッシュ
    },
  ],
  tools: tools.map((tool) => ({
    ...tool,
    cache_control: { type: "ephemeral" }, // ツール定義もキャッシュ
  })),
  messages: [{ role: "user", content: "東京の天気は?" }],
});

// 2回目以降のリクエスト: キャッシュヒットで入力トークンが1/10に

実践的なユースケース: RAGシステムの実装

ベクトル検索ツールの統合

const ragTools = [
  {
    name: "search_knowledge_base",
    description: "社内ドキュメントから関連情報を検索",
    input_schema: {
      type: "object",
      properties: {
        query: { type: "string", description: "検索クエリ" },
        top_k: {
          type: "number",
          description: "取得する上位結果の数",
          default: 5,
        },
      },
      required: ["query"],
    },
  },
  {
    name: "get_document_metadata",
    description: "ドキュメントのメタデータ(作成日、著者など)を取得",
    input_schema: {
      type: "object",
      properties: {
        document_id: { type: "string" },
      },
      required: ["document_id"],
    },
  },
];

// RAGワークフロー
const ragResponse = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 2048,
  tools: ragTools,
  messages: [
    {
      role: "user",
      content: "2025年度の売上目標について教えて",
    },
  ],
});

// Claude が自動的に search_knowledge_base("2025年度 売上目標") を呼び出し
const searchTool = ragResponse.content.find(
  (b) => b.type === "tool_use" && b.name === "search_knowledge_base"
);

if (searchTool && searchTool.type === "tool_use") {
  // ベクトル検索実行(Pinecone / Weaviate / Qdrant など)
  const searchResults = await vectorDB.search(searchTool.input.query, {
    top_k: searchTool.input.top_k || 5,
  });

  // 検索結果をClaudeに返す
  const finalResponse = await client.messages.create({
    model: "claude-3-5-sonnet-20250929",
    max_tokens: 2048,
    tools: ragTools,
    messages: [
      { role: "user", content: "2025年度の売上目標について教えて" },
      { role: "assistant", content: ragResponse.content },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: searchTool.id,
            content: JSON.stringify(searchResults),
          },
        ],
      },
    ],
  });

  console.log(finalResponse.content);
  // Claude: "社内資料によると、2025年度の売上目標は前年比120%の15億円です(出典: 経営計画書2025.pdf, 2024年12月作成)"
}

情報源の引用と信頼性向上

// 検索結果にメタデータを含める
const searchResultWithMetadata = searchResults.map((result) => ({
  content: result.text,
  metadata: {
    document_id: result.id,
    title: result.title,
    created_at: result.created_at,
    author: result.author,
    confidence_score: result.score, // ベクトル検索の類似度スコア
  },
}));

// Claude に引用を促すシステムプロンプト
const response = await client.messages.create({
  model: "claude-3-5-sonnet-20250929",
  max_tokens: 2048,
  system:
    "回答する際は必ず情報源(ドキュメント名、作成日)を明示してください。",
  tools: ragTools,
  messages: [
    /* ... */
  ],
});

// 出力例:
// "2025年度の売上目標は15億円です。
//  **出典**: 経営計画書2025.pdf(2024年12月1日作成、著者: 経営企画部)
//  信頼度スコア: 0.94"

まとめ

Claude API の Function Calling は、2026年4月現在、以下の点で実用的なLLMワークフロー構築の中核技術となっています。

  • 構造化された出力: JSON Schema による厳密なパラメータ検証
  • 並列実行: 独立したツールを同時呼び出しでレイテンシ削減
  • エラーハンドリング: is_error フラグによる明示的なエラー通知
  • コスト最適化: Prompt Caching との併用で最大90%のトークン削減
  • 実用的なユースケース: RAG、マルチステップ分析、外部API統合

実装のポイント:

  1. ツール定義は必要最小限に絞り、動的に選択する
  2. 並列実行可能なツールは Promise.allSettled でまとめて実行
  3. エラー時は is_error: true で Claude に通知し、代替案を提案させる
  4. tool_choice で実行を制御し、不要なAPI呼び出しを防ぐ
  5. Prompt Caching でツール定義をキャッシュし、コストを削減

これらのテクニックを組み合わせることで、信頼性とコスト効率を両立した複雑なAIシステムを構築できます。

参考リンク

#Claude #Function Calling #API #LLM #TypeScript
シェア: