AI Agentがユーザーの意図を理解し、外部ツールやAPIと連携して自律的にタスクを実行する——この中核技術がFunction Callingです。本稿では、ECサイトのAI客服システムという具体的なユースケースを通じて、HolySheep AI APIを活用した実践的な実装パターンを丁寧に解説します。
今すぐ登録して始めましょう。
Function Callingとは:Agentの「手」と「目」を作る技術
Function Callingとは、LLM(大規模言語モデル)に外部関数を呼び出す能力を与える仕組みです。従来のプロンプトエンジニアリングでは「画像を生成してください」と指示出すだけで、実際の処理は人間の手に委ねられていました。Function Callingを活用すれば、LLMは以下の一連の処理を自律的に実行できます:
- 意図の理解:ユーザー発言から「どの関数を呼ぶべきか」を判断
- 引数の抽出:関数に渡すべきパラメータをJSON形式で生成
- 結果の統合:関数実行結果を踏まえて最終回答を生成
私は以前、企業のRAG(検索拡張生成)システムを構築する際、パラメータ検証の甘さで何度も痛い目に遭いました。特にECサイトのAI客服では、取りこぼしゼロと即座の応答が命綱となるため、堅牢な実装が不可欠です。
実践的なユースケース:ECサイトのAIカスタマーサービスBot
假设这样一个AI客服系统:
- 在庫確認(get_product_stock)
- 注文状況查询(check_order_status)
- 退货处理(process_return)
実装: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を例に、月間コストを算出してみます:
- 月間对话数:10,000件
- 平均Function Call数/对话:2回
- 使用モデル:GPT-4o(output: $8/MTok)
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の実装における重要ポイントを解説しました:
- スキーマ設計:厳密な型定義とenum制約で、LLMの解釈错误を防止
- 追加バリデーション:API側でもLLMが生成した引数を検証
- 型変換:numpyやDecimalをJSON対応型に確実に変換
- リトライ機構:指数バックオフで一時的エラーに対応
- ログと監視:各関数呼び出しのレイテンシと成功率を追跡
HolySheep AIは<50msのレイテンシと¥1=$1のレートで、本番環境のAI Agentにも最適な選択です。DeepSeek V3.2のような低成本モデル($0.42/MTok)もラインナップしており、ログ_only функция呼び出しにも экономично対応できます。