ท้าทาย GPT Function calling ด้วย Gorilla OpenFunctions ว่าจะดีจริงไหม

Chanintorn Asavavichairoj
5 min readNov 25, 2023

--

Generated from Bing DALL-E 3

ท่ามกลางความวุ่นวายที่เกิดขึ้นกับ OpenAI หลังจากที่ปล่อยของชุดใหญ่ในงาน OpenAI DevDay 2023 ก็มีเรื่องราวให้เราได้เสพมากมายแทบทุกคืนไม่ว่าจะเป็นของใหม่ที่ปล่อยออกมา หรือกระทั่งดราม่าของบอร์ดบริหาร OpenAI กับนายแซม ที่จบไปแล้วก็ตามแบบที่เรียกว่าสนุกยิ่งกว่าละครหลังข่าวไปอีก ทั้งหมดทั้งมวลที่เกิดขึ้นเราคงยอมรับเลยว่ามันจะไม่สนุกเลยถ้า OpenAI ไม่ได้เป็นผู้เขย่าวงการ AI ในประวัติศาสตร์วงการคอมพิวเตอร์ แต่เรียกได้ว่าเค้าคือผู้นำอยู่ ณ จังหวะนี้จริงๆ

กลับมาสู่เรื่องของเราในวันนี้คือหนึ่งในฟีเจอร์ที่ OpenAI ได้ปล่อยออกมาใน DevDay และเป็นหนึ่งในเรื่องที่ผมคิดว่ามีความน่าสนใจอยู่ไม่น้อย แต่จริงๆก็เป็นความสามารถของ GPT ที่ทำมาได้ตั้งแต่เปิดตัวเจ้า Model GPT-3.5 แล้วแหละ นั่นก็คือการเพิ่มความแม่นยำและเพิ่มศักยภาพกับ Function Calling ให้มีความอัจฉริยะขึ้นไปอีกขั้น และความน่าสนใจนั้นก็คือ Parallel Function Calling นั่นเองงง

GPT Function Calling 101 แบบรวดรัดสำหรับผู้ที่ยังไม่เคยใช้มาก่อน Function Calling มันคือการที่ใช้ความสามารถ Text generation Model ของ LLM เพื่อที่จะให้มันสังเคราะห์ออกมาเป็น JSON Schema เพื่อแยกแยะว่า LLM App ของเราควรจะต้องไปทำงานอะไรเพื่อตอบสนองกับผู้ใช้งาน (หรือจริงๆแล้วมันก็คือความสามารถในการ Classification ภายใน LLM Model นั่นแหละฮ่ะ) ซึ่ง Format ของการที่ Model สังเคราะห์ออกมาขึ้นอยู่กับการ Trained ของแต่ละ Model ด้วย หากเราใช้ความสามารถแบบนี้กับ Google PaLM2 หรือว่า Anthropic Claude2 ก็อาจจะมีลักษณะที่แตกต่างกันไปตาม Model ด้วยนะค้าบบบ

แล้ว Parallel Function Calling มันดียังไงนะ .. ในความคิดผมไม่รู้เป็นกันไหมนะทุกคน Pain point นึงของการใช้ Assistant Platforms ต่างๆ ไม่ว่าจะเป็น Google Home (Google Assistant) หรือ HomePod (Siri) ทุกคนจะพบว่ามันทำงานให้เราได้ทีละอย่าง เช่นเราสั่งให้มัน ‘เปิดทีวี’, ‘เปิดไฟ’, ‘ปิดม่าน’ เราต้องสั่งมันทีละประโยค ยกเว้นแต่เราไปทำ automation workflow ให้กับเราเป็นพิเศษเพื่อร้อยเรียง Steps การทำงานที่เราอยากให้มันทำ แต่ถ้าเราสั่งมันว่า ‘เปิดทีวีแล้วปิดไฟด้วยนะ’ มันกลับที่จะเลือกทำอย่างใดอย่างหนึ่งเพียงเท่านั้น หรือไม่ก็ไม่ทำเลยเพราะไม่เข้าใจ ฮ่าๆ

การเพิ่มความสามารถของ Model ให้มีความสามารถในการทำ Parallel Function ได้เป็นการปลดแอกข้อจำกัดเดิมๆด้วย LLM Model ที่ถูก Trained มาในเรื่องนี้โดยเฉพาะ และทำทุกอย่างพร้อมกันได้อันนี้ผมว่าเป็นจุดเปลี่ยนสำคัญที่ทำให้ไม่ว่าจะเป็นวงการ Iot เอง Chatbot หรือว่า Assistant ต่างๆให้สามารถทำงานหลายๆอย่างพร้อมๆกันได้ และลด Hop ของการปิงปองถามไปมาอยู่ไม่น้อย ซึ่งตอนนี้ OpenAI ก็ได้รองรับ Parallel Function Calling เรียบร้อยแล้ว เพียงแต่มีสอง Model เท่านั้นในตอนนี้ (As of Nov 2023) คือ gpt-4-1106-previewและ gpt-3.5-turbo-1106 (https://platform.openai.com/docs/guides/function-calling/supported-models)

อย่างไรก็ดีด้วยดราม่าของ OpenAI ที่แม้ว่าจะจบไปแล้วด้วยดี แต่ปัญหา OpenAI Platform Outage ก็พบเจอกันได้อยู่บ่อยๆ (https://status.openai.com/) ถึงขนาดต้องขอไป Subscribe เลยดีกว่าเพราะพี่เป็นถี่เหลือเกิ๊นน และอีกทั้งความสบายกระเป๋าสตางค์ในการใช้ Model อัจฉริยะนี้ วันนี้เราก็มีทางเลือกจาก University of California, Berkeley ที่ได้ปล่อย Gorilla Model มาให้เราได้ใช้งานกันฟรีๆและสบายใจด้วย Licence แบบ Apache-2.0! ว่าแล้วก็ตามไปตำกันเลยดีกว่า

ทำความรู้จักกับน้องกอลิล่า 🦍

https://gorilla.cs.berkeley.edu/blogs/4_open_functions.html

การทำงานของ Gorilla OpenFunctions ถูกต่อยอดมาจาก Chat Completion model (Trained on top จาก 7B LLaMA-v2 Model) เพียงเราให้ข้อความในสิ่งที่เราต้องการไปด้วย Natural language นี่แหละฮ่ะ น้องกอลิล่าก็จะทำงานวิเคราะห์ว่าเราต้องการใช้งาน Tools ใดบ้างเพื่อทำการ Execute ซึ่งในตัว Model gorilla-openfunctions-v1(สดๆร้อนๆหลังจาก OpenAI ประกาศในงาน DevDay) นั้นได้รองรับ Parallel Function Calling เป็นที่เรียบร้อยแล้ว สุดแจ่มมม กระดิ่งแมวว

ตัว Gorilla Model ได้ถูก Trained ด้วย Documents ของ Service ระดับโลกต่างๆมาเรียบร้อยในเบื้องต้นแล้วไม่ว่าจะเป็น AWS, GCP, Azure, หรือ RapidAPI แต่ว่าหากเราต้องการ Customize Function ของเราเองก็เพียงแต่ Provide JSON เพื่อเป็น Document ให้กับน้องกอริล่าด้วยรูปแบบประมาณนี้

function_documentaion = {
"name": "Order Food on Uber",
"api_call": "uber.eat.order",
"description": "Order food on uber eat given a list of items and the quantity of items respectively",
"parameters": [
{
"name": "restaurants",
"description": "The restaurants user wants to order from"
},
{
"name": "items",
"description": "A list of order user wants to order from restaurants"
},
{
"name": "quantities",
"description": "A list of quantities corresponding to the items ordered"
}
]
}

เมื่อมีประโยคสั่งน้องกอลิล่ามาทำนองว่า ..
"I want to order five burgers and six chicken wings from McDonlad."
น้องก็จะตีความหมายว่าเราโปรแกรมของเราต้องทำงานตามนี้นะ

uber.eat.order(restaurants="McDonald", item=["chicken wings", "burgers"], quantity=[6,5])

และหากคุณยังไม่มั่นใจในน้องกอลิล่าแล้วละก็ ทาง UC Berkeley ก็ใจดีได้ทำ Benchmark มาให้เรียบร้อยแล้ว ซึ่งดูแล้วน้องไม่ขี้เหร่เลยนะเทียบเคียงกับ GPT Model ได้สบายๆเลย แต่ถ้าไปให้น้อง Llama ทำงานนี้แทนดูสิ น้องน่าจะทำคะแนนได้ไม่ได้เท่าไร

GPT-4 and GPT-3.5 function calling perform better than the state-of-the-art GPT-4-Turbo and GPT-4 model

ที่น่าแปลกใจของเอกสารนี้คือ GPT-4-Turbo ที่ OpenAI พึ่งปล่อยออกมากลับทำคะแนนได้น้อยกว่า GPT-3.5 และ GPT-4 ที่ปล่อยออกมาตั้งแต่ต้นปี ซึ่งน่าสนใจมากๆสำหรับการพิจรณาการเลือกใช้ Model ของพวกเราในอนาคตอีกด้วย

น้องกอลิล่าขอปะทะรุ่นพี่ GPT

โดยในวันนี้เราจะมาเริ่มชาเลนจ์นี้ด้วยการเตรียมความพร้อมของด้าน GPT จาก OpenAI ก่อนดังนี้ ..

import openai
import urllib.parse
import json

def get_gpt_response(prompt="", model="gpt-3.5-turbo-1106", functions=[]):
openai.api_key = "<OPENAI_APIKEY>"
openai.api_base = "https://api.openai.com/v1"
try:
completion = openai.ChatCompletion.create(
model=model,
temperature=0.0,
messages=[{"role": "user", "content": prompt}],
tools=functions,
)
return completion.choices[0]
except Exception as e:
print(e, model, prompt)

และในฝั่งของผู้ท้างชิงน้องกอลิล่า (ใช้งานน้องผ่าน OpenAI Library ได้นะค้าบและ Custom ไปที่ Inference Host ของทาง UC Berkeley ที่ให้เราทดลองใช้งานกันอยู่ — As of Nov 2023)

import openai
import urllib.parse
import json

def get_gorilla_response(prompt="", model="gorilla-openfunctions-v1", functions=[]):
openai.api_key = "EMPTY" # Hosted for free with ❤️ from UC Berkeley
openai.api_base = "http://luigi.millennium.berkeley.edu:8000/v1"
try:
completion = openai.ChatCompletion.create(
model=model,
temperature=0.0,
messages=[{"role": "user", "content": prompt}],
functions=functions,
)
return completion.choices[0]
except Exception as e:
print(e, model, prompt)

หลังจากนั้นโจทย์ของเราในวันนี้คือ Parallel Function ทั้งหมดที่ควรจะต้อง Call จาก Natural Language ในเรื่อง Home Automation (จะได้เอาไปใช้กับ Iot ในบ้านซะเลย)

ด้านของ GPT Function Calling ขอออมมือให้โดยจะใช้แค่ GPT-3.5 พอ โดยเราจะปั้น Request เพื่อ Input ตามนี้นะครับ

functions = [
{
"type": "function",
"function": {
"name": "home_tv_switch",
"description": "Using this tool for turn on or turn off TV",
"parameters": {
"type": "object",
"properties": {
"state": {
"type": "boolean",
"description": "state of TV between turn on or turn off"
},
"unit": {"type": "string", "enum": ["turn on", "turn off"]}
},
"required": ["state"]
}
}
},
{
"type": "function",
"function": {
"name": "home_tv_volume",
"description": "Using this tool for set TV volume",
"parameters": {
"type": "object",
"properties": {
"level": {
"type": "integer",
"description": "level of TV's volume"
},
"unit": {"type": "integer"}
},
"required": ["level"]
}
}
},
{
"type": "function",
"function": {
"name": "home_light_switch",
"description": "Using this tool for turn on or turn off ceiling light",
"parameters": {
"type": "object",
"properties": {
"state": {
"type": "boolean",
"description": "state of ceiling light between turn on or turn off"
},
"unit": {"type": "string", "enum": ["turn on", "turn off"]}
},
"required": ["state"]
}
}
},
{
"type": "function",
"function": {
"name": "home_light_color",
"description": "Using this tool for change color of ceiling light",
"parameters": {
"type": "object",
"properties": {
"color": {
"type": "boolean",
"description": "change color of ceiling light"
},
"unit": {"type": "string", "enum": ["red", "green", "blue"]}
},
"required": ["color"]
}
}
}
]

query = "Please turn on TV and also turn off ceiling light"
get_gpt_response(query, model="gpt-3.5-turbo-1106", functions=functions)

ส่วนน้องกอลิล่าก็จะตามนี้ครับ

functions = [
{
"name": "home_tv_switch",
"api_name": "home_tv_switch",
"description": "Using this tool for turn on or turn off TV",
"parameters": [{"name": "state", "type": "boolean", "description": "state of TV between turn on or turn off"}]
},
{
"name": "home_tv_volume",
"api_name": "home_tv_volume",
"description": "Using this tool for set TV volume",
"parameters": [{"name": "level", "type": "integer", "description": "level of TV's volume"}]
},
{
"name": "home_light_switch",
"api_name": "home_light_switch",
"description": "Using this tool for turn on or turn off ceiling light",
"parameters": [{"name": "state", "type": "boolean", "description": "state of ceiling light between turn on or turn off"}]
},
{
"name": "home_light_color",
"api_name": "home_light_color",
"description": "Using this tool for change color of ceiling light",
"parameters": [{"name": "color", "type": "string", "description": "color of ceiling light support only \"red\", \"green\", \"blue\""}]
},
]

query = "Please turn on TV and also turn off ceiling light"
get_gorilla_response(query, model="gorilla-openfunctions-v1", functions=functions)

และผลลัพท์ที่ได้ก็คือ “รุ่นพี่จีพีที” สามารถทำความเข้าใจได้ง่ายดายโดยสามารถให้ Tool Calls ที่ได้ตามความต้องการเป๊ะ

{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_VucKeXQ60CkqTSRb677FsBRN",
"type": "function",
"function": {
"name": "home_tv_switch",
"arguments": "{\"state\": true, \"unit\": \"turn on\"}"
}
},
{
"id": "call_Cxb6Q825nA5OkdV0I43IcS7y",
"type": "function",
"function": {
"name": "home_light_switch",
"arguments": "{\"state\": false, \"unit\": \"turn off\"}"
}
}
]
},
"finish_reason": "tool_calls"
}

ในขณะที่น้องกอลิล่ายังมีความงงงวยอยู่เล่นให้เรา Call แทบทุก Functions ไปเลย และยังมีการกาว (Hallucinations) ไปอีก ขนาดใส่ temperature เป็น 0 แล้วนะเนี่ย

{
"index": 0,
"message": {
"role": "assistant",
"content": "[\"home_tv_switch(state=true)\", \"home_tv_volume(level=10)\", \"home_light_switch(state=true)\", \"home_light_color(color=red)\"]"
},
"finish_reason": "stop"
}

เด๋วจะหาว่าไม่แฟร์ ลองอีกทีก็ได้ด้วยโจทย์
"Please turn on TV and set volume to 50% and also change the ceiling light color with red color"

และน้องกอลิล่ากับพี่จีพีทีก็ตอบออกมาตามนี้ ไม่ต้องบอกละกันว่าของใครเป็นของใคร ฮ่าๆ

{
"index": 0,
"message": {
"role": "assistant",
"content": "home_tv_switch(state=True, level=50, home_light_color=\"red\")"
},
"finish_reason": "stop"
}
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_OGcfA5cTBiYZdj5Mutzn206G",
"type": "function",
"function": {
"name": "home_tv_switch",
"arguments": "{\"state\": true}"
}
},
{
"id": "call_IOCrBDDn8OQehYt50FI2zn6Y",
"type": "function",
"function": {
"name": "home_tv_volume",
"arguments": "{\"level\": 50}"
}
},
{
"id": "call_A63n96qmPGRBljPGPjVLA6sE",
"type": "function",
"function": {
"name": "home_light_color",
"arguments": "{\"color\": true, \"unit\": \"red\"}"
}
}
]
},
"finish_reason": "tool_calls"
}

ก็เป็นอันสรุปได้ว่าน้องกอลิล่าก็ถือเป็น Model OpenSource ทางเลือกหนึ่งสำหรับคนที่กำลังมองๆหา LLM ที่ใช้ในการทำ workflow บางอย่างด้วยภาษามนุษย์ แต่อย่างไรก็ตาม น้องยังอาจจะต้องกลับไปซ้อมมืออยู่อีกซักพักกว่าน้องจะทำได้ดีอย่างรุ่นพี่ และผมเชื่อว่าน้องน่าจะพัฒนาตามมาอีกหลายเวอร์ชั่นเลยทีเดียว

จริงๆบล็อกนี้ไม่ได้มาบูลี่น้องนะฮ่ะ เพียงแต่ว่าน้องกอลิล่าได้ออกมาประจวบเหมาะมากกับความคันอยากทดลองเรื่อง Parallel Function Calling พอดิบพอดี ซึ่งจริงๆแล้วมี Model ทางเลือกอีกหลายตัวที่ Trained เรื่อง Function calling มาอยู่ อาทิเช่น ToolLLaMA, Llama-2–7b-chat-hf-function-calling-v2 ซึ่งหากใครสนใจก็ตามไปลองใช้กันได้นะค้าบบบ และสำหรับใครอยากตามน้องกอลิล่าไปใช้งานแบบ Local local แล้วละก็เชิญกันได้ที่พี่ HF 🤗 คนดีคนเดิมได้เลยจร้า

--

--