ในโปรเจกต์ล่าสุดของผม ผมเจอปัญหา 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" # ถูกต้อง! )

2. ข้อผิดพลาด "No tool choices