ในโปรเจกต์ล่าสุดของผม ผมเจอปัญหา Function Calling ที่ใช้งานไม่ได้ในรอบที่สองของการสนทนา — ระบบจะคืนค่า tool_calls กลับมาถูกต้อง แต่พอส่ง response กลับไป กลับได้รับข้อผิดพลาดว่า Invalid response format ในที่สุด หลังจาก debug เกือบ 3 ชั่วโมง ผมค้นพบว่า ปัญหาอยู่ที่การจัดการ conversation history และ tool_calls ID
บทความนี้จะสอนทุกอย่างตั้งแต่พื้นฐานจนถึงเทคนิคขั้นสูงสำหรับการทำ Multi-Turn Function Calling กับ Gemini 2.5 Flash ผ่าน HolySheep AI ซึ่งมีราคาเพียง $2.50/MTok เท่านั้น
ทำความรู้จัก Gemini 2.5 Flash Function Calling
Function Calling คือความสามารถของ LLM ในการเรียกใช้ฟังก์ชันภายนอกเมื่อต้องการข้อมูลหรือดำเนินการบางอย่าง แทนที่จะตอบด้วยข้อความธรรมดา ซึ่งแตกต่างจาก GPT-4 ตรงที่ Gemini ใช้โครงสร้าง tool_calls ที่มี ID ติดตามทุกการเรียก
// โครงสร้าง Tool Call ของ Gemini
{
"id": "toolu_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"กรุงเทพฯ\",\"unit\":\"celsius\"}"
}
}
การตั้งค่าโปรเจกต์และการเชื่อมต่อ HolySheep API
ก่อนเริ่มต้น คุณต้องสมัครสมาชิกที่ HolySheep AI ก่อน ซึ่งให้เครดิตฟรีเมื่อลงทะเบียน และราคาถูกกว่า API ทั่วไปถึง 85% ขึ้นไป วิธีตั้งค่า:
import openai
import json
import requests
from typing import List, Dict, Any, Optional
class GeminiFunctionCaller:
"""Multi-turn Function Calling สำหรับ Gemini 2.5 Flash"""
def __init__(self, api_key: str):
self.client = openai.OpenAI(
api_key=api_key,
base_url="https://api.holysheep.ai/v1"
)
self.conversation_history: List[Dict] = []
self.tools_used: List[Dict] = []
def add_tool(self, name: str, description: str, parameters: dict):
"""เพิ่ม function tool ที่จะใช้งาน"""
if not hasattr(self, 'tools'):
self.tools = []
self.tools.append({
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": parameters
}
})
def execute_function(self, func_name: str, arguments: str) -> dict:
"""จำลองการทำงานของ function ที่เรียก"""
try:
args = json.loads(arguments)
except:
return {"error": "Invalid JSON arguments"}
# ตัวอย่าง function สำหรับค้นหาข้อมูล
if func_name == "search_product":
return {
"found": True,
"product": "iPhone 15 Pro",
"price": 45900,
"stock": 25
}
elif func_name == "calculate_discount":
price = args.get("price", 0)
discount = args.get("discount_percent", 0)
return {
"original_price": price,
"discounted_price": price * (1 - discount/100),
"savings": price * (discount/100)
}
elif func_name == "get_order_status":
order_id = args.get("order_id", "")
return {
"order_id": order_id,
"status": "shipped",
"eta": "2-3 วันทำการ"
}
return {"error": f"Unknown function: {func_name}"}
def send_message(self, user_message: str) -> str:
"""ส่งข้อความและรับคำตอบ พร้อมจัดการ function calling"""
# เพิ่มข้อความผู้ใช้เข้า history
self.conversation_history.append({
"role": "user",
"content": user_message
})
while True:
# ส่ง request ไปยัง API
response = self.client.chat.completions.create(
model="gemini-2.0-flash",
messages=self.conversation_history,
tools=self.tools if hasattr(self, 'tools') else None,
tool_choice="auto"
)
assistant_message = response.choices[0].message
# กรณีไม่มี function call ให้ตอบกลับได้เลย
if not assistant_message.tool_calls:
self.conversation_history.append({
"role": "assistant",
"content": assistant_message.content
})
return assistant_message.content
# มี function call — ประมวลผลทีละตัว
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name
arguments = tool_call.function.arguments
# บันทึกการใช้ tool
tool_result = self.execute_function(function_name, arguments)
# เพิ่ม tool call และผลลัพธ์เข้า history
self.conversation_history.append({
"role": "assistant",
"content": None,
"tool_calls": [{
"id": tool_call.id,
"type": "function",
"function": {
"name": function_name,
"arguments": arguments
}
}]
})
self.conversation_history.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(tool_result, ensure_ascii=False)
})
# วนกลับไปถาม LLM อีกครั้งด้วยผลลัพธ์ใหม่
ตัวอย่างการใช้งาน
api_key = "YOUR_HOLYSHEEP_API_KEY"
caller = GeminiFunctionCaller(api_key)
กำหนด tools
caller.add_tool(
name="search_product",
description="ค้นหาข้อมูลสินค้าตามชื่อหรือหมวดหมู่",
parameters={
"type": "object",
"properties": {
"product_name": {"type": "string", "description": "ชื่อสินค้า"},
"category": {"type": "string", "description": "หมวดหมู่สินค้า"}
},
"required": ["product_name"]
}
)
caller.add_tool(
name="calculate_discount",
description="คำนวณส่วนลดและราคาหลังหักส่วนลด",
parameters={
"type": "object",
"properties": {
"price": {"type": "number", "description": "ราคาเดิม"},
"discount_percent": {"type": "number", "description": "เปอร์เซ็นต์ส่วนลด"}
},
"required": ["price", "discount_percent"]
}
)
caller.add_tool(
name="get_order_status",
description="ตรวจสอบสถานะการจัดส่งสินค้า",
parameters={
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "หมายเลขคำสั่งซื้อ"}
},
"required": ["order_id"]
}
)
ทดสอบ Multi-Turn Conversation
print("=== รอบที่ 1: ค้นหาสินค้า ===")
result1 = caller.send_message("iPhone 15 Pro ราคาเท่าไหร่?")
print(result1)
print("\n=== รอบที่ 2: ขอส่วนลด ===")
result2 = caller.send_message("ลด 15% ได้ไหม?")
print(result2)
print("\n=== รอบที่ 3: ตรวจสอบคำสั่งซื้อ ===")
result3 = caller.send_message("ตรวจสอบ Order #12345")
print(result3)
โครงสร้าง Tool Definition ที่ถูกต้องสำหรับ Gemini
สิ่งสำคัญที่ผมเรียนรู้จากข้อผิดพลาดคือ โครงสร้าง parameters ต้องเป็นไปตาม JSON Schema อย่างเคร่งครัด ต่อไปนี้คือตัวอย่างที่ใช้งานได้จริง:
import openai
กำหนดค่าเริ่มต้น
client = openai.OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
Tools สำหรับระบบจองตั๋วเครื่องบิน
flight_tools = [
{
"type": "function",
"function": {
"name": "search_flights",
"description": "ค้นหาเที่ยวบินที่พร้อมให้บริการ",
"parameters": {
"type": "object",
"properties": {
"origin": {
"type": "string",
"description": "สนามบินต้นทาง (รหัส IATA เช่น BKK, DMK)",
"example": "BKK"
},
"destination": {
"type": "string",
"description": "สนามบินปลายทาง (รหัส IATA)",
"example": "CNX"
},
"departure_date": {
"type": "string",
"description": "วันที่เดินทาง (รูปแบบ YYYY-MM-DD)",
"example": "2025-07-15"
},
"passengers": {
"type": "integer",
"description": "จำนวนผู้โดยสาร",
"minimum": 1,
"maximum": 9,
"default": 1
},
"cabin_class": {
"type": "string",
"description": "ชั้นที่นั่ง (economy, business, first)",
"enum": ["economy", "business", "first"]
}
},
"required": ["origin", "destination", "departure_date"]
}
}
},
{
"type": "function",
"function": {
"name": "book_flight",
"description": "จองตั๋วเครื่องบินหลังจากค้นหาเที่ยวบินแล้ว",
"parameters": {
"type": "object",
"properties": {
"flight_id": {
"type": "string",
"description": "หมายเลขเที่ยวบินที่ได้จากการค้นหา"
},
"passenger_info": {
"type": "object",
"description": "ข้อมูลผู้โดยสาร",
"properties": {
"first_name": {"type": "string"},
"last_name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"phone": {"type": "string"}
},
"required": ["first_name", "last_name", "email"]
}
},
"required": ["flight_id", "passenger_info"]
}
}
}
]
ตัวอย่างการสนทนา Multi-Turn
messages = [
{
"role": "system",
"content": "คุณเป็นผู้ช่วยจองตั๋วเครื่องบิน ใช้ภาษาไทยในการสื่อสาร"
},
{
"role": "user",
"content": "อยากไปเชียงใหม่วันที่ 20 กรกฎาคม 2568 มีเที่ยวบินอะไรบ้าง?"
}
]
รอบที่ 1: ค้นหาเที่ยวบิน
response1 = client.chat.completions.create(
model="gemini-2.0-flash",
messages=messages,
tools=flight_tools,
tool_choice="auto"
)
print("รอบที่ 1 - Tool Calls:")
for tool in response1.choices[0].message.tool_calls:
print(f" Function: {tool.function.name}")
print(f" Arguments: {tool.function.arguments}")
จำลองผลลัพธ์การค้นหา
search_result = {
"flights": [
{"id": "TH001", "time": "08:30", "price": 2500, "airline": "Thai Air"},
{"id": "TH002", "time": "14:15", "price": 2200, "airline": "Thai Air"}
]
}
เพิ่ม tool call และผลลัพธ์เข้า messages
messages.append({
"role": "assistant",
"tool_calls": response1.choices[0].message.tool_calls
})
messages.append({
"role": "tool",
"tool_call_id": response1.choices[0].message.tool_calls[0].id,
"content": json.dumps(search_result, ensure_ascii=False)
})
รอบที่ 2: ถามต่อ (LLM จะใช้ context จากรอบก่อน)
messages.append({
"role": "user",
"content": "เลือก TH002 แล้วจองให้ฉัน"
})
response2 = client.chat.completions.create(
model="gemini-2.0-flash",
messages=messages,
tools=flight_tools,
tool_choice="auto"
)
print("\nรอบที่ 2 - Tool Calls:")
for tool in response2.choices[0].message.tool_calls:
print(f" Function: {tool.function.name}")
print(f" Arguments: {tool.function.arguments}")
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ข้อผิดพลาด "401 Unauthorized" หรือ "Invalid API Key"
สาเหตุ: API Key ไม่ถูกต้องหรือยังไม่ได้เปลี่ยน base_url เป็นของ HolySheep
# ❌ วิธีที่ผิด - จะได้ 401 Unauthorized
client = openai.OpenAI(
api_key="YOUR_KEY",
base_url="https://api.openai.com/v1" # ผิด!
)
✅ วิธีที่ถูกต้อง
client = openai.OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # ถูกต้อง!
)