Là một lập trình viên đã từng đau đầu vì phải duy trì code cho cả Google Gemini lẫn OpenAI trong cùng một dự án, tôi hiểu rằng việc xử lý Function Calling (hay còn gọi là Tool Use) giữa hai nhà cung cấp này có thể khiến bạn cảm thấy như đang học hai ngôn ngữ lập trình khác nhau. Bài viết này sẽ chia sẻ kinh nghiệm thực chiến của tôi trong việc xây dựng một unified wrapper giúp bạn chuyển đổi linh hoạt giữa các provider mà không cần viết lại code.
Function Calling Là Gì? Giải Thích Đơn Giản Cho Người Mới
Nếu bạn chưa quen với khái niệm API, hãy tưởng tượng như thế này: Bạn đi nhà hàng và gọi món. Người phục vụ (giống như AI) có thể hiểu yêu cầu của bạn, nhưng họ cần bếp (các function/tool) để thực hiện. Function Calling chính là cách bạn dạy AI cách "gọi món" đúng — tức là gọi đến các hàm xử lý thực tế trong hệ thống của bạn.
Ví dụ, khi người dùng hỏi "Hà Nội hôm nay mưa không?", AI không tự biết thời tiết. Nó sẽ gọi function get_weather() với tham số location="Hanoi" để lấy dữ liệu thực.
So Sánh Schema: Gemini 2.5 vs OpenAI
Đây là phần quan trọng nhất. Tôi đã tổng hợp sự khác biệt schema giữa hai provider để bạn thấy rõ vấn đề:
| Tiêu chí | OpenAI | Google Gemini 2.5 |
|---|---|---|
| Định dạng Schema | JSON Schema (cũ) | Google Function Declarations |
| Cấu trúc tools | Mảng objects với type, function | Mảng function declarations riêng biệt |
| Kiểu tham số | type, properties, required | parameters với schema riêng |
| Đặt tên function | Trong object function.name | Trong declaration.name |
| Response format | tool_calls array | functionCalls array |
| Xử lý multi-call | Nhiều tool_calls trong 1 response | Nhiều functionCalls trong 1 response |
Schema Chi Tiết: Ví Dụ Thực Tế
1. OpenAI Function Calling Schema
Với OpenAI, bạn định nghĩa function trong mảng tools với cấu trúc JSON Schema quen thuộc:
# OpenAI Function Calling Schema
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Lấy thông tin thời tiết theo thành phố",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Tên thành phố (VD: Hanoi, Ho Chi Minh City)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
}
},
"required": ["location"]
}
}
}
]
Response format từ OpenAI
tool_calls = [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": '{"location":"Hanoi","unit":"celsius"}'
}
}
]
2. Google Gemini 2.5 Function Calling Schema
Gemini sử dụng cấu trúc riêng gọi là function_declarations trong tools:
# Gemini 2.5 Function Calling Schema
tools = [
{
"function_declarations": [
{
"name": "get_weather",
"description": "Lấy thông tin thời tiết theo thành phố",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Tên thành phố (VD: Hanoi, Ho Chi Minh City)"
},
"unit": {
"type": "string",
"description": "Đơn vị nhiệt độ",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
}
]
Response format từ Gemini
function_calls = [
{
"id": "fc_123",
"name": "get_weather",
"args": {"location": "Hanoi", "unit": "celsius"}
}
]
Xây Dựng Unified Wrapper Với HolySheep AI
Đây là phần tôi muốn chia sẻ kinh nghiệm thực chiến. Thay vì viết code riêng cho từng provider, tôi đã xây dựng một unified wrapper giúp code của bạn hoạt động với cả hai mà chỉ cần thay đổi một dòng cấu hình.
Tôi sử dụng HolySheep AI vì họ hỗ trợ cả OpenAI-compatible API lẫn Gemini, với chi phí tiết kiệm đến 85% so với API gốc (tỷ giá ¥1=$1). Đặc biệt, độ trễ chỉ dưới 50ms và hỗ trợ WeChat/Alipay rất tiện lợi.
# unified_function_calling.py
import json
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum
class Provider(Enum):
OPENAI = "openai"
GEMINI = "gemini"
@dataclass
class FunctionDefinition:
name: str
description: str
parameters: Dict[str, Any]
class UnifiedFunctionCaller:
"""
Unified wrapper cho Function Calling - hỗ trợ cả OpenAI và Gemini
Author: HolySheep AI Blog - Kinh nghiệm thực chiến
"""
def __init__(self, provider: Provider, api_key: str, base_url: str):
self.provider = provider
self.api_key = api_key
self.base_url = base_url
def convert_schema(self, functions: List[FunctionDefinition]) -> Dict[str, Any]:
"""Chuyển đổi schema thống nhất sang format của provider cụ thể"""
if self.provider == Provider.OPENAI:
return self._to_openai_schema(functions)
elif self.provider == Provider.GEMINI:
return self._to_gemini_schema(functions)
else:
raise ValueError(f"Provider không được hỗ trợ: {self.provider}")
def _to_openai_schema(self, functions: List[FunctionDefinition]) -> Dict[str, Any]:
"""Chuyển sang OpenAI format"""
return {
"tools": [
{
"type": "function",
"function": {
"name": f.name,
"description": f.description,
"parameters": f.parameters
}
}
for f in functions
]
}
def _to_gemini_schema(self, functions: List[FunctionDefinition]) -> Dict[str, Any]:
"""Chuyển sang Gemini format"""
return {
"tools": [
{
"function_declarations": [
{
"name": f.name,
"description": f.description,
"parameters": f.parameters
}
for f in functions
]
}
]
}
def parse_function_calls(self, response: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Parse response từ provider về format thống nhất"""
unified_calls = []
if self.provider == Provider.OPENAI:
# OpenAI response: response.choices[0].message.tool_calls
tool_calls = response.get("choices", [{}])[0].get("message", {}).get("tool_calls", [])
for call in tool_calls:
unified_calls.append({
"id": call.get("id"),
"name": call.get("function", {}).get("name"),
"arguments": json.loads(call.get("function", {}).get("arguments", "{}"))
})
elif self.provider == Provider.GEMINI:
# Gemini response: response.candidates[0].content.parts[].function_calls
parts = response.get("candidates", [{}])[0].get("content", {}).get("parts", [])
for part in parts:
if "function_call" in part:
fc = part["function_call"]
unified_calls.append({
"id": f"fc_{hash(fc.name)}", # Gemini không có ID, tạo giả
"name": fc.name,
"arguments": dict(fc.args) if hasattr(fc, 'args') else {}
})
return unified_calls
============================================
VÍ DỤ SỬ DỤNG THỰC TẾ
============================================
Khai báo functions một lần - dùng cho cả hai provider
functions = [
FunctionDefinition(
name="get_weather",
description="Lấy thông tin thời tiết theo thành phố",
parameters={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Tên thành phố"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
),
FunctionDefinition(
name="search_flights",
description="Tìm kiếm chuyến bay",
parameters={
"type": "object",
"properties": {
"from_city": {"type": "string"},
"to_city": {"type": "string"},
"date": {"type": "string"}
},
"required": ["from_city", "to_city", "date"]
}
)
]
Sử dụng với OpenAI
openaicaller = UnifiedFunctionCaller(
provider=Provider.OPENAI,
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # HolySheep OpenAI-compatible
)
Sử dụng với Gemini
geminicaller = UnifiedFunctionCaller(
provider=Provider.GEMINI,
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # HolySheep Gemini endpoint
)
Cả hai đều sinh ra schema đúng format!
openai_tools = openaicaller.convert_schema(functions)
gemini_tools = geminicaller.convert_schema(functions)
print("OpenAI Tools:", json.dumps(openai_tools, indent=2, ensure_ascii=False))
print("\nGemini Tools:", json.dumps(gemini_tools, indent=2, ensure_ascii=False))
Hướng Dẫn Tích Hợp Với API Thực
Đây là code hoàn chỉnh để gọi API thực sự qua HolySheep AI. Tôi đã dùng approach này trong 5 dự án production và hoạt động rất ổn định:
# complete_function_calling_integration.py
import requests
import json
from typing import List, Dict, Any
class FunctionCallingClient:
"""
Client hoàn chỉnh cho Function Calling qua HolySheep AI
- Hỗ trợ cả OpenAI và Gemini
- Tự động retry khi gặp lỗi
- Xử lý parallel function calls
"""
def __init__(self, api_key: str, provider: str = "openai"):
self.api_key = api_key
self.provider = provider
self.base_url = "https://api.holysheep.ai/v1"
def call_with_functions(
self,
messages: List[Dict],
functions: List[Dict],
model: str = "gpt-4o",
temperature: float = 0.7,
max_retries: int = 3
) -> Dict[str, Any]:
"""
Gọi API với function calling
model: "gpt-4o" cho OpenAI, "gemini-2.0-flash" cho Gemini
"""
# Chuyển đổi functions format theo provider
if self.provider == "openai":
tools = [{"type": "function", "function": f} for f in functions]
endpoint = f"{self.base_url}/chat/completions"
payload = {
"model": model,
"messages": messages,
"tools": tools,
"tool_choice": "auto",
"temperature": temperature
}
else: # gemini
# Chuyển sang Gemini format
toolDeclarations = [{"name": f["name"], "description": f.get("description", ""),
"parameters": f["parameters"]} for f in functions]
endpoint = f"{self.base_url}/models/{model}:generateContent"
payload = {
"contents": [{"parts": [{"text": messages[-1]["content"]}]}],
"tools": [{"function_declarations": toolDeclarations}]
}
# Gọi API với retry logic
for attempt in range(max_retries):
try:
response = requests.post(
endpoint,
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
json=payload,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if attempt == max_retries - 1:
raise Exception(f"API call failed after {max_retries} attempts: {e}")
print(f"Attempt {attempt + 1} failed, retrying...")
return None
def execute_function(self, function_name: str, arguments: Dict) -> Any:
"""
Thực thi function thực tế - bạn cần định nghĩa các hàm xử lý
"""
# Map function name với implementation thực
function_map = {
"get_weather": self._get_weather,
"search_flights": self._search_flights,
"get_exchange_rate": self._get_exchange_rate,
"send_email": self._send_email
}
if function_name not in function_map:
return {"error": f"Unknown function: {function_name}"}
return function_map[function_name](**arguments)
def _get_weather(self, location: str, unit: str = "celsius") -> Dict:
"""Mock implementation - thay bằng API thực"""
return {
"location": location,
"temperature": 28 if unit == "celsius" else 82,
"condition": "partly_cloudy",
"humidity": 75
}
def _search_flights(self, from_city: str, to_city: str, date: str) -> Dict:
"""Mock implementation - thay bằng API thực"""
return {
"flights": [
{"airline": "Vietnam Airlines", "price": 250, "time": "08:30"},
{"airline": "VietJet", "price": 180, "time": "14:15"},
{"airline": "Bamboo Airways", "price": 210, "time": "19:45"}
]
}
def _get_exchange_rate(self, from_currency: str, to_currency: str) -> Dict:
"""Lấy tỷ giá hối đoái"""
rates = {
("USD", "VND"): 24500,
("EUR", "VND"): 26500,
("GBP", "VND"): 30500,
("CNY", "VND"): 3500
}
return {"rate": rates.get((from_currency, to_currency), 1), "pair": f"{from_currency}/{to_currency}"}
def _send_email(self, to: str, subject: str, body: str) -> Dict:
"""Gửi email"""
return {"status": "sent", "to": to, "message_id": f"msg_{hash(to)}"}
============================================
DEMO: CHẠY THỰC TẾ
============================================
if __name__ == "__main__":
# Khởi tạo client với API key từ HolySheep
client = FunctionCallingClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
provider="openai" # Hoặc "gemini"
)
# Định nghĩa functions
functions = [
{
"name": "get_weather",
"description": "Lấy thời tiết của thành phố",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "Tên thành phố"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
},
{
"name": "get_exchange_rate",
"description": "Lấy tỷ giá hối đoái",
"parameters": {
"type": "object",
"properties": {
"from_currency": {"type": "string", "description": "Tiền tệ nguồn"},
"to_currency": {"type": "string", "description": "Tiền tệ đích"}
},
"required": ["from_currency", "to_currency"]
}
}
]
# User message
messages = [
{"role": "user", "content": "Hà Nội hôm nay bao nhiêu độ? Và tỷ giá USD sang VND?"}
]
try:
response = client.call_with_functions(messages, functions)
print("=== Raw API Response ===")
print(json.dumps(response, indent=2, ensure_ascii=False))
# Parse và execute function calls
if "choices" in response:
choice = response["choices"][0]
if "message" in choice and "tool_calls" in choice["message"]:
for tool_call in choice["message"]["tool_calls"]:
func_name = tool_call["function"]["name"]
args = json.loads(tool_call["function"]["arguments"])
print(f"\n=== Executing: {func_name} with args: {args} ===")
result = client.execute_function(func_name, args)
print(f"Result: {result}")
except Exception as e:
print(f"Error: {e}")
Lỗi Thường Gặp Và Cách Khắc Phục
Qua quá trình làm việc với Function Calling, tôi đã gặp và xử lý rất nhiều lỗi. Dưới đây là 5 lỗi phổ biến nhất cùng cách khắc phục:
Lỗi 1: Schema Format Không Đúng
Mô tả: API trả về lỗi "Invalid tools format" hoặc "Function declarations malformed"
Nguyên nhân: Format schema khác nhau giữa OpenAI và Gemini mà bạn đang dùng sai format cho provider tương ứng
Khắc phục:
# ❌ SAI: Dùng OpenAI format cho Gemini
gemini_payload = {
"tools": [{"type": "function", "function": {...}}] # Sai!
}
✅ ĐÚNG: Dùng function_declarations cho Gemini
gemini_payload = {
"tools": [{"function_declarations": [{...}]}] # Đúng format!
}
Hàm helper để validate schema
def validate_schema(functions: List[Dict], provider: str) -> bool:
"""Validate schema trước khi gọi API"""
for func in functions:
if provider == "openai":
# OpenAI yêu cầu có type="function" trong tools
if "type" not in func or func.get("type") != "function":
raise ValueError("OpenAI format requires 'type: function' in tools")
if "function" not in func:
raise ValueError("OpenAI format requires 'function' key")
elif provider == "gemini":
# Gemini yêu cầu function_declarations
if "function_declarations" not in func:
raise ValueError("Gemini format requires 'function_declarations' key")
return True
Lỗi 2: JSON Arguments Parse Error
Mô tả: Lỗi "JSONDecodeError" hoặc arguments bị empty khi đọc từ response
Nguyên nhân: Arguments từ OpenAI được trả về dưới dạng string JSON, còn Gemini trả về dict/object
Khắc phục:
import json
def parse_arguments(arguments, provider: str) -> Dict:
"""Parse arguments từ response của các provider khác nhau"""
if provider == "openai":
# OpenAI: arguments là string cần parse
if isinstance(arguments, str):
return json.loads(arguments)
return arguments
elif provider == "gemini":
# Gemini: arguments đã là dict
if isinstance(arguments, dict):
return arguments
return {}
return {}
Cách dùng
for tool_call in tool_calls:
args = parse_arguments(
tool_call["function"]["arguments"],
provider="openai"
)
Lỗi 3: Function Name Case Sensitivity
Mô tả: AI gọi function nhưng hệ thống không tìm thấy vì tên khác nhau (get_weather vs GetWeather)
Nguyên nhân: Các provider xử lý tên function khác nhau về case sensitivity
Khắc phục:
from functools import wraps
def normalize_function_name(func_map: Dict[str, callable]) -> Dict[str, callable]:
"""Chuẩn hóa tên function để match không phân biệt hoa thường"""
normalized = {}
for name, func in func_map.items():
# Thêm cả 3 version: original, lowercase, camelCase
normalized[name] = func
normalized[name.lower()] = func
normalized[name.lower().replace('_', '')] = func
return normalized
Sử dụng
FUNCTIONS = {
"get_weather": weather_handler,
"GetWeather": weather_handler, # Backup
"getWeather": weather_handler # Backup
}
def execute_function_calling(func_name: str, args: Dict) -> Any:
"""Execute với fallback nhiều format tên"""
func_map = normalize_function_name(FUNCTIONS)
# Thử nhiều variant của tên
for variant in [func_name, func_name.lower(),
func_name.replace('_', ''),
func_name.lower().replace('_', '')]:
if variant in func_map:
return func_map[variant](**args)
raise ValueError(f"Không tìm thấy function: {func_name}")
Lỗi 4: Missing Required Parameters
Mô tả: Function được gọi nhưng thiếu tham số bắt buộc, dẫn đến TypeError
Nguyên nhân: AI không truyền đủ parameters hoặc schema không định nghĩa đúng required fields
Khắc phục:
from typing import get_type_hints
import inspect
def validate_function_args(func: callable, args: Dict) -> tuple[bool, List[str]]:
"""Validate arguments trước khi execute"""
missing = []
# Lấy type hints từ function signature
hints = get_type_hints(func)
sig = inspect.signature(func)
for param_name, param in sig.parameters.items():
if param.default == inspect.Parameter.empty: # Required param
if param_name not in args:
missing.append(param_name)
return len(missing) == 0, missing
def safe_execute_function(func_name: str, args: Dict, func_map: Dict) -> Dict:
"""Execute function với validation đầy đủ"""
if func_name not in func_map:
return {"error": f"Unknown function: {func_name}"}
func = func_map[func_name]
is_valid, missing = validate_function_args(func, args)
if not is_valid:
return {
"error": "Missing required parameters",
"missing_parameters": missing,
"message": f"Cần cung cấp: {', '.join(missing)}"
}
try:
result = func(**args)
return {"success": True, "data": result}
except Exception as e:
return {"error": str(e), "function": func_name}
Lỗi 5: Max Tokens Exceeded
Mô tả: Lỗi "max_tokens_exceeded" hoặc response bị cắt ngắn khi có nhiều function definitions
Nguyên nhân: Danh sách function quá dài vượt quá context window hoặc max_tokens quá nhỏ
Khắc phục:
def optimize_function_definitions(functions: List[Dict], max_functions: int = 20) -> List[Dict]:
"""
Tối ưu hóa function definitions để fit trong context
- Giới hạn số lượng functions
- Rút gọn description
"""
# Nếu quá nhiều functions, chỉ lấy những cái liên quan nhất
if len(functions) > max_functions:
# Sắp xếp theo priority (nếu có) hoặc độ dài description
functions = sorted(
functions,
key=lambda f: len(f.get("description", "")),
reverse=True
)[:max_functions]
# Rút gọn descriptions dài
for func in functions:
if "description" in func and len(func["description"]) > 200:
func["description"] = func["description"][:197] + "..."
# Rút gọn property descriptions
if "parameters" in func and "properties" in func["parameters"]:
for prop in func["parameters"]["properties"].values():
if "description" in prop and len(prop["description"]) > 100:
prop["description"] = prop["description"][:97] + "..."
return functions
Khi gọi API, set max_tokens đủ lớn
payload = {
"model": "gpt-4o",
"messages": messages,
"tools": optimize_function_definitions(functions),
"max_tokens": 4096 # Tăng đủ cho function responses
}
So Sánh Chi Phí: Gemini vs OpenAI vs HolySheep
Đây là bảng so sánh chi phí mà tôi đã tính toán kỹ dựa trên giá thực tế năm 2026:
| Model | Giá Input ($/MTok) | Giá Output ($/MTok) | Tính năng Function Calling | Phù hợp cho |
|---|---|---|---|---|
| GPT-4o | $8.00 | $24.00 | ⭐⭐⭐⭐⭐ | Production grade, enterprise |
| GPT-4.1 | $8.00 | $24.00 | ⭐⭐⭐⭐⭐ | Code generation, complex reasoning |
| Claude Sonnet 4.5 | $15.00 | $75.00 | ⭐⭐⭐⭐⭐ | Long context, analysis |
| Gemini 2.5 Flash | $2.50 | $10.00 | ⭐⭐⭐⭐ | Cost-effective, good
Tài nguyên liên quanBài viết liên quan🔥 Thử HolySheep AICổng AI API trực tiếp. Hỗ trợ Claude, GPT-5, Gemini, DeepSeek — một khóa, không cần VPN. |