AI Agentがユーザーの意図を理解し、外部ツールやAPIと連携して自律的にタスクを実行する——この中核技術がFunction Callingです。本稿では、ECサイトのAI客服システムという具体的なユースケースを通じて、HolySheep AI APIを活用した実践的な実装パターンを丁寧に解説します。

今すぐ登録して始めましょう。

Function Callingとは:Agentの「手」と「目」を作る技術

Function Callingとは、LLM(大規模言語モデル)に外部関数を呼び出す能力を与える仕組みです。従来のプロンプトエンジニアリングでは「画像を生成してください」と指示出すだけで、実際の処理は人間の手に委ねられていました。Function Callingを活用すれば、LLMは以下の一連の処理を自律的に実行できます:

私は以前、企業のRAG(検索拡張生成)システムを構築する際、パラメータ検証の甘さで何度も痛い目に遭いました。特にECサイトのAI客服では、取りこぼしゼロと即座の応答が命綱となるため、堅牢な実装が不可欠です。

実践的なユースケース:ECサイトのAIカスタマーサービスBot

假设这样一个AI客服系统:

実装:Function Callingの基本構造

まずはHolySheep AI APIを使った基本的な実装を見てみましょう。レートは¥1=$1(公式¥7.3=$1的比で85%節約)で、企業ユースにも個人開発にも非常に経済的です。

import requests
import json
from typing import Optional, Dict, Any

class HolySheepAIClient:
    """HolySheep AI APIクライアント - Function Calling対応"""
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def chat_completions(
        self,
        messages: list,
        functions: Optional[list] = None,
        model: str = "gpt-4o"
    ) -> Dict[str, Any]:
        """
        Function Calling可能なチャット補完リクエスト
        
        レイテンシ: <50ms(HolySheep独自最適化)
        """
        payload = {
            "model": model,
            "messages": messages,
            "temperature": 0.7
        }
        
        if functions:
            payload["tools"] = functions
            payload["tool_choice"] = "auto"
        
        response = requests.post(
            f"{self.base_url}/chat/completions",
            headers=self.headers,
            json=payload,
            timeout=30
        )
        
        if response.status_code != 200:
            raise APIError(
                f"API Error: {response.status_code} - {response.text}"
            )
        
        return response.json()


class APIError(Exception):
    """カスタムAPIエラー"""
    pass

Functionスキーマの定義:arta引数検証の要

Function Callingの精度を左右するのは、関数スキーマの定義品質です。以下に、実践的なEC客服システムの3つの関数を定義します:

# ECサイトAI客服 - Function Calling定義
FUNCTIONS = [
    {
        "name": "get_product_stock",
        "description": "商品の在庫数を確認する関数。SKUコードまたは商品名で検索可能。",
        "parameters": {
            "type": "object",
            "properties": {
                "product_id": {
                    "type": "string",
                    "description": "商品IDまたはSKUコード(例: SKU-12345)"
                },
                "size": {
                    "type": "string", 
                    "description": "サイズ(S/M/L/XLまたは数値)",
                    "enum": ["S", "M", "L", "XL", "XXL"]
                },
                "color": {
                    "type": "string",
                    "description": "カラー(任意)"
                }
            },
            "required": ["product_id"]
        }
    },
    {
        "name": "check_order_status",
        "description": "注文の状況を確認する関数。",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "注文ID(例: ORD-20240101-001)",
                    "pattern": "^ORD-\\d{8}-\\d{3}$"
                },
                "email": {
                    "type": "string",
                    "description": "注文時に入力したメールアドレス"
                }
            },
            "required": ["order_id"],
            "additionalProperties": False
        }
    },
    {
        "name": "process_return",
        "description": "退货・返金の申請を処理する関数。",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "退货対象注文ID"
                },
                "reason": {
                    "type": "string",
                    "description": "退货理由",
                    "enum": ["サイズ不合い", "品質問題", "誤配送", "不要になった", "その他"]
                },
                "items": {
                    "type": "array",
                    "description": "退货する商品的リスト",
                    "items": {
                        "type": "object",
                        "properties": {
                            "product_id": {"type": "string"},
                            "quantity": {"type": "integer", "minimum": 1}
                        },
                        "required": ["product_id", "quantity"]
                    }
                }
            },
            "required": ["order_id", "reason", "items"]
        }
    }
]


def validate_function_args(func_name: str, args: dict) -> tuple[bool, Optional[str]]:
    """
    関数引数の追加バリデーション
    
    Returns:
        (is_valid, error_message)
    """
    if func_name == "get_product_stock":
        if "size" in args and args["size"] not in ["S", "M", "L", "XL", "XXL"]:
            return False, f"サイズが不正です: {args['size']}"
    
    elif func_name == "check_order_status":
        if not args.get("order_id", "").startswith("ORD-"):
            return False, "注文IDはORD-から始まる形式で入力してください"
    
    elif func_name == "process_return":
        if len(args.get("items", [])) == 0:
            return False, "退货する商品を1点以上選択してください"
    
    return True, None

AI Agentの実装:会話ループとTool Call処理

ここが核心です。LLMがfunction_callを返してきた際の处理ループを実装します:

import time
from dataclasses import dataclass
from enum import Enum

class OrderStatus(Enum):
    PENDING = "pending"
    SHIPPED = "shipped"
    DELIVERED = "delivered"
    RETURNED = "returned"


@dataclass
class ToolResult:
    """ツール実行結果"""
    success: bool
    result: Any = None
    error: Optional[str] = None
    latency_ms: float = 0.0


class ECommerceFunctionExecutor:
    """ECサイトの各関数を 실제로実行するクラス"""
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    def execute(self, func_name: str, args: dict) -> ToolResult:
        """関数を実行し、結果とレイテンシを返す"""
        start_time = time.perf_counter()
        
        try:
            # 追加バリデーション
            is_valid, error_msg = validate_function_args(func_name, args)
            if not is_valid:
                return ToolResult(
                    success=False,
                    error=error_msg,
                    latency_ms=(time.perf_counter() - start_time) * 1000
                )
            
            # 関数実行
            if func_name == "get_product_stock":
                result = self._get_stock(args)
            elif func_name == "check_order_status":
                result = self._check_order(args)
            elif func_name == "process_return":
                result = self._process_return(args)
            else:
                result = {"error": f"不明な関数: {func_name}"}
            
            return ToolResult(
                success=True,
                result=result,
                latency_ms=(time.perf_counter() - start_time) * 1000
            )
            
        except Exception as e:
            return ToolResult(
                success=False,
                error=str(e),
                latency_ms=(time.perf_counter() - start_time) * 1000
            )
    
    def _get_stock(self, args: dict) -> dict:
        """在庫確認の実装"""
        # 實際にはDBクエリを実行
        return {
            "product_id": args["product_id"],
            "stock": 42,
            "size": args.get("size", "ALL"),
            "estimated_delivery": "2-3日"
        }
    
    def _check_order(self, args: dict) -> dict:
        """注文状況確認の実装"""
        return {
            "order_id": args["order_id"],
            "status": OrderStatus.SHIPPED.value,
            "tracking_number": "JP123456789",
            "estimated_arrival": "2024-01-20"
        }
    
    def _process_return(self, args: dict) -> dict:
        """退货処理の実装"""
        return {
            "return_id": f"RET-{int(time.time())}",
            "order_id": args["order_id"],
            "status": "approved",
            "refund_amount": 9800,
            "refund_method": "original_payment"
        }


class AgentLoop:
    """Function Calling実行ループ"""
    
    MAX_TURNS = 10  # 無限ループ防止
    
    def __init__(self, client: HolySheepAIClient, executor: ECommerceFunctionExecutor):
        self.client = client
        self.executor = executor
    
    def run(self, user_message: str) -> str:
        messages = [{"role": "user", "content": user_message}]
        
        for turn in range(self.MAX_TURNS):
            response = self.client.chat_completions(
                messages=messages,
                functions=FUNCTIONS
            )
            
            assistant_message = response["choices"][0]["message"]
            messages.append(assistant_message)
            
            # Tool Callsの確認
            if "tool_calls" not in assistant_message:
                # これ以上関数呼び出しなし = 最終回答
                return assistant_message["content"]
            
            # 各ツール呼び出しを実行
            for tool_call in assistant_message["tool_calls"]:
                func_name = tool_call["function"]["name"]
                args = json.loads(tool_call["function"]["arguments"])
                
                print(f"[DEBUG] Calling {func_name} with {args}")
                
                # 関数実行
                result = self.executor.execute(func_name, args)
                
                # 結果をmessagesに追加
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call["id"],
                    "content": json.dumps(result.__dict__, ensure_ascii=False)
                })
                
                print(f"[DEBUG] Result: {result.latency_ms:.2f}ms - Success: {result.success}")
        
        return "申し訳ありません。処理が複雑過ぎました。人間が対応します。"

料金試算:HolySheep AI的经济的なメリット

このEC客服Botを例に、月間コストを算出してみます:

HolySheep AIの料金体系中、GPT-4oのoutput价格为$8/MTokです。1对话平均500トークンの場合:

# 月間コスト計算
conversations = 10000
avg_function_calls = 2
output_tokens_per_call = 300  # 関数結果の報告

total_output_tokens = conversations * avg_function_calls * output_tokens_per_call
print(f"月間Outputトークン数: {total_output_tokens:,}")

HolySheep AI

holysheep_cost = (total_output_tokens / 1_000_000) * 8 # $8/MTok print(f"HolySheep AI費用: ${holysheep_cost:.2f}")

日本円換算(¥1=$1のレート)

print(f"日本円換算: ¥{holysheep_cost:.0f}")

比較:他社(¥7.3=$1)

other_cost_yen = holysheep_cost * 7.3 print(f"他社使用時(¥7.3=$1): ¥{other_cost_yen:.0f}") print(f"節約額: ¥{other_cost_yen - holysheep_cost:.0f}({((other_cost_yen - holysheep_cost) / other_cost_yen * 100):.1f}%OFF)")

出力結果(実測):

月間Outputトークン数: 6,000,000
HolySheep AI費用: $48.00
日本円換算: ¥48
他社使用時(¥7.3=$1): ¥350
節約額: ¥302(86%OFF)

HolySheep AIでは¥1=$1のレートが適用されるため、公式价比で85%以上の節約になります。WeChat PayやAlipayにも対応しており中国企业でも 쉽게 결제 가능합니다。

よくあるエラーと対処法

エラー1:Invalid API Key - 認証エラー

エラー内容:

AuthenticationError: Invalid API key provided
{"error": {"message": "Invalid API key", "type": "invalid_request_error"}}

原因と解決:APIキーが未設定または正しく渡されていない場合に発生します。環境変数からの読み込みを確認しましょう:

import os

❌ よくある間違い

api_key = "YOUR_HOLYSHEEP_API_KEY" # 文字列リテラルをそのまま使用

✅ 正しい実装

api_key = os.environ.get("HOLYSHEEP_API_KEY") if not api_key: raise ValueError("HOLYSHEEP_API_KEY環境変数を設定してください")

またはフォールバックとして

api_key = os.environ.get("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") if api_key == "YOUR_HOLYSHEEP_API_KEY": print("⚠️ APIキーを設定してください: https://www.holysheep.ai/register")

エラー2:Function引数の型不一致

エラー内容:

TypeError: Object of type int64 is not JSON serializable
JSONDecodeError: Expecting value: line 1 column 1 (char 0)

原因と解決:Pythonのint64型やnumpy型はJSONに変換できません。DBからの結果を受け取る前に、必ず基本型に変換します:

from decimal import Decimal
import numpy as np

def sanitize_for_json(obj):
    """JSONシリアライズ可能な型に変換"""
    if isinstance(obj, np.integer):
        return int(obj)
    elif isinstance(obj, np.floating):
        return float(obj)
    elif isinstance(obj, np.ndarray):
        return obj.tolist()
    elif isinstance(obj, Decimal):
        return float(obj)
    elif isinstance(obj, dict):
        return {k: sanitize_for_json(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [sanitize_for_json(i) for i in obj]
    return obj

使用例

stock_result = db.execute("SELECT stock FROM products WHERE id = ?", [product_id]) clean_result = sanitize_for_json(stock_result) json_str = json.dumps(clean_result)

エラー3:Tool Call配列の순서不正

エラー内容:

ValueError: tool_calls must be provided in the same order as function_call indices

原因と解決:複数のTool Callを返す場合、assistant_messageのtool_callsと、それに対応するrole=toolのメッセージを同じ顺序で追加する必要があります:

# ❌ 間違い:順序を狂わせる
messages.append(assistant_message)

途中で他のmessageを追加...

messages.append({"role": "user", "content": "追加質問"}) messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": tool_result })

✅ 正しい実装

messages.append(assistant_message)

Tool Call結果を元の順序で追加

for tool_call in assistant_message["tool_calls"]: # 対応する結果を検索して追加 result = tool_results[tool_call["id"]] messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": json.dumps(result) })

追加質問は最後に

if follow_up_question: messages.append({"role": "user", "content": follow_up_question})

エラー4:タイムアウトとリトライ処理の欠如

エラー内容:

requests.exceptions.Timeout: HTTPTimeoutError
ConnectionError: HTTPSConnectionPool Max retries exceeded

原因と解決:ネットワーク不安定やAPI一時的停止に備え、指数バックオフ方式のリトライを実装します:

import time
from functools import wraps

def retry_with_exponential_backoff(
    max_retries=3,
    initial_delay=1,
    max_delay=60,
    exponential_base=2
):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            delay = initial_delay
            last_exception = None
            
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except (requests.exceptions.Timeout, 
                        requests.exceptions.ConnectionError) as e:
                    last_exception = e
                    
                    if attempt < max_retries - 1:
                        wait_time = min(delay * (exponential_base ** attempt), max_delay)
                        print(f"[RETRY] {attempt + 1}/{max_retries} - {wait_time}s後に再試行...")
                        time.sleep(wait_time)
                    else:
                        print(f"[ERROR] 最大リトライ回数を超過: {e}")
                        raise
            
            raise last_exception
        return wrapper
    return decorator

使用例

@retry_with_exponential_backoff(max_retries=3, initial_delay=1) def call_api_with_retry(client, messages, functions): return client.chat_completions(messages=messages, functions=functions)

エラー5:JSON解析エラー(関数引数の不完整)

エラー内容:

json.decoder.JSONDecodeError: Expecting ',' delimiter
{"function":{"name":"get_product_stock","arguments":"{\"product_id\":}"}

原因と解決:LLMが不完全なJSONを生成する場合があります。堅牢な解析処理を実装します:

import re

def parse_function_arguments(tool_call) -> dict:
    """堅牢な関数引数解析"""
    raw_args = tool_call["function"]["arguments"]
    func_name = tool_call["function"]["name"]
    
    try:
        return json.loads(raw_args)
    except json.JSONDecodeError:
        # 不完全なJSONを修正を試みる
        print(f"[WARN] JSON解析エラー、修復を試みる: {func_name}")
        
        # 中括弧を閉じる
        fixed_args = raw_args
        open_braces = fixed_args.count('{')
        close_braces = fixed_args.count('}')
        
        if open_braces > close_braces:
            fixed_args += '}' * (open_braces - close_braces)
        
        try:
            return json.loads(fixed_args)
        except json.JSONDecodeError:
            # 完全に修復できない場合はデフォルト値を使用
            print(f"[ERROR] 修復不可、デフォルト値を使用: {func_name}")
            return {"error": "引数解析失敗", "raw": raw_args}

Tool Call処理で使用

for tool_call in assistant_message["tool_calls"]: args = parse_function_arguments(tool_call) if "error" in args: # エラー內容をLLMにフィードバック messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": json.dumps({ "success": False, "error": "引数の形式が不正です。もう一度引数を指定してください。" }) })

まとめ:堅牢なFunction Calling実装のポイント

本稿では、ECサイトのAI客服システムを例に、Function Callingの実装における重要ポイントを解説しました:

HolySheep AIは<50msのレイテンシ¥1=$1のレートで、本番環境のAI Agentにも最適な選択です。DeepSeek V3.2のような低成本モデル($0.42/MTok)もラインナップしており、ログ_only функция呼び出しにも экономично対応できます。

👉 HolySheep AI に登録して無料クレジットを獲得