Function calling
הפיצ'ר של Function calling נועד לשתי מטרות:
- אחזור מידע ל-
LLM
כדי שיוכל לענות על ה-prompt (מה שנקרא RAG) - הפעלת פעולות
למה צריך לאחזר מידע❓
מודל LLM
מאומן על מאגר נתונים שבאמצעותו הוא בונה את הידע והמוח שלו. כל אימון מייצר גירסה של המודל כשמכאן והלאה המודל קופא על שמריו ולא לומד דברים חדשים.
לכן, כאשר המודל נשאל שאלה שדורשת ידע שלא קיים אצלו הוא לא יכול לענות עליה.
ולא, המודל לא מסוגל ללכת לחפש בעצמו את התשובה באינטרנט. מודל הוא יישום שיודע לעשות דבר אחד - completion, כלומר להשלים את ה-token הבא בהקשר הנוכחי.
מה קורה כשה-LLM
לא יודע מה לענות❓
במקרה הטוב הוא יודה בכנות שאין לו את המידע הדרוש, ובמקרה הפחות טוב נקבל את תופעת ההזיות הידועה. המודל פשוט ימציא תשובה וידבר שטויות במיץ 🤡
מתי זה קורה? בעיקר כשהמידע הנדרש הוא דינמי ומשתנה מדי פעם.
דוגמה
תחזית מזג האויר להיום בירושלים
אין סיכוי בעולם שהמודל יידע מה התחזית להיום.
גם אם הוא יצא היום לשוק, תהליך האימון לוקח הרבה זמן, כך שבכל מקרה המידע שיש לו הוא כבר היסטוריה.
אומנם למעשה, כששואלים את ChatGPT מה התחזית להיום הוא כן יודע לענות על כך, הנה דוגמא:
אבל אם נשאל אותו באמצעות ה-API, נקבל תשובה כזו:
מה ההבדל❓
זו המחשה מעולה מה זה Agent 🤖
GPT הוא מודל LLM
שיודע לג'נרט completion לפי קונטקסט (הקשר). כאשר אנחנו שולחים קריאה ל-API של OpenAI אנחנו פונים ישירות ל-LLM
ולכן הוא לא יודע לספק לנו תשובה. (ויפה מצידו שלא ניסה להמציא משהו על סמך מידע היסטורי...).
לעומת זאת, ChatGPT הוא Agent שהליבה שלו אומנם היא ה-LLM
, אבל הוא מוסיף על זה כלים נוספים כמו חיפוש באינטרנט ועוד.
במקרה הזה מתברר ש-OpenAI תכנתו את ChatGPT כך שעבור שאלות על תחזית מזג האויר יבצע חיפוש באינטרנט כדי להביא את המידע העדכני הרלוונטי.
הנה מה שנקבל בלחיצה על כפתור ה-Sources, נוכל לצפות במקורות המידע שמהם ChatGPT שאב את המידע שעליו מתבססת התשובה שקיבלנו.
עכשיו נשתמש בפיצ'ר של Function calling כדי לעשות בדיוק את מה ש-OpenAI עשו ב-ChatGPT.
נפתח Agent
שצורך את GPT באמצעות ה-API ויודע לספק תחזית מזג אויר עדכנית.
Function calling
Function calling זהו פיצ'ר שמאפשר לנו לתת ל-LLM
תיאור של פונקציה (או פונקציות) שיש לנו בארסנל ולבקש ממנו שבמידת הצורך יתן לנו הוראה להפעיל את הפונקציה.
התהליך עובד כך:
1️⃣ המשתמש מזין שאילתה, לדוגמא: מה התחזית להיום בירושלים.
2️⃣ ה-Agent
שולח ל-LLM
את השאלה ומוסיף תיאור של פונקצית get_weather שיודעת להחזיר תחזית לפי קואורדינטות של מיקום מסוים על פני כדור הארץ.
3️⃣ ה-LLM
מנתח את השאלה באופן כזה:
מה התחזית להיום אין לו דרך לדעת, בשביל זה הוא צריך כלי עזר חיצוני. ה-LLM מסיק לפי תיאור הפונקציה שלנו שהיא מתאימה לבעיה ויש להפעיל אותה כדי לקבל את התחזית. הוא לומד מתיאור הפונקציה שהיא דורשת נקודות ציון של אורך ורוחב. את המידע הזה אין לו בעיה לדעת, שהרי המיקום על פני כדור הארץ הוא קבוע ולא השתנה מאז שאומן המודל.
4️⃣ ה-LLM
מחזיר ל-Agent
תשובה עם הוראה להפעיל את הפונקציה get_weather עם הערכים עבור הפרמטרים אורך ורוחב.
5️⃣ ה-Agent
מפעיל את הפונקציה get_weather ומקבל תוצאה של מספר שמייצג את התחזית במעלות צלסיוס.
6️⃣ ה-Agent
שולח ל-LLM
שוב את השאלה של המשתמש, מוסיף את התשובה שקיבל מה-LLM
קודם עם ההוראה להפעלת הפונקציה, ולסיום מוסיף את התשובה של הפונקציה - התחזית.
7️⃣ ה-LLM
מג'נרט תשובה מנוסחת בשפת בני אדם בסגנון: "תחזית מזג האויר ליום זה וזה בירושלים היא כך וכך מעלות צלסיוס"
🎉 👏
לסיכום,
יש פה סוג של שילוב ידים 🤝
ה-LLM
מביא את יכולת ההבנה של שפה טבעית וממיר אותה למידע שהוא structured - מובנה (במקרה הזה json שמתאר איזו פונקציה להפעיל עם איזה פרמטרים).
ה-Agent
, שהוא תוכנית קוד קלאסית, מביא את היכולת להפעיל פונקציות ומנהל את התהליך השלם של קריאה ל-LLM
, הפעלת הפונקציות וניצוח על כל המקהלה.
התוצאה, ללא ספק, מרשימה ביותר ✨ 🥳
איך זה נראה בפועל❓
🔗 קישור לדמו עם קריאות מוכנות ב-Postman
להלן מבנה ה-body
של הבקשות שנשלחות ל-API של OpenAI של התגובות המתקבלות ממנו.
זו הבקשה הראשונה.
יש לנו כאן מערך של messages
עם השאלה של המשתמש "מה התחזית להיום בירושלים"
בנוסף יש לנו מערך של tools
עם אוביקט מסוג function
שמתאר פונקציה בשם get_weather
שמחזירה טמפרטורת צלסיוס לפי קואורדינטות.
מוגדרים גם הפרמטרים שהפונקציה דורשת: אורך ורוחב כמספרים.
המאפיין tool_choice
מגדיר האם מחיבים את ה-LLM
להחזיר הוראת הפעלה לפונקציה או שזה לגיטימי אם יחליט שלא נדרשת הפעלה של פונקציה (לפי שאילתת המשתמש כמובן, ובהתאם לצורך העסקי של האפליקציה)
במקרה הזה מוגדר כ-required
כדי להקשיח אותו שיחזיר תמיד הוראת הפעלה לפונקציה.
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "מה התחזית להיום בירושלים"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current temperature for provided coordinates in celsius.",
"parameters": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
},
"required": [
"latitude",
"longitude"
],
"additionalProperties": false
},
"strict": true
}
}
],
"tool_choice": "required"
}
זו התגובה של ה-LLM
.
נשים לב שהוא החזיר לנו message
עם content
ריק, מכיון שאין לו תשובה לשאלה אלא הוראה להפעלת פונקציה.
tool_calls
מגדיר לנו בצורה מובנית איזו פונקציה להפעיל ומה יהיו ערכי הפרמטרים.
{
"id": "chatcmpl-ArwhyboyFpBwbKfVQ1N0TsEa6zXZt",
"object": "chat.completion",
"created": 1737420422,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_hH7LRv5VEFHOjCCeVOS2TPIs",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"latitude\":31.7683,\"longitude\":35.2137}"
}
}
],
"refusal": null
},
"logprobs": null,
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 59,
"completion_tokens": 25,
"total_tokens": 84,
"prompt_tokens_details": {
"cached_tokens": 0,
"audio_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
},
"service_tier": "default",
"system_fingerprint": "fp_72ed7ab54c"
}
רק כדי לוודא, נלך שניה לגוגל לבדוק האם הקואורדינטות שהוא נותן לנו אכן נכונות.
לא מפתיע, אבל הוא צודק 😌
נניח שהפעלנו את הפונקציה get_weather
וקיבלנו את המספר 10.
זו הבקשה השניה שנשלחת ל-LLM
.
נשים לב לשרשור במערך של ה-messages
. נוסף אוביקט עם התגובה של ה-LLM
שאומרת להפעיל את הפונקציה וכן אוביקט עם תוצאת הפונקציה (שהופעלה או שלא, לא משנה...)
בנוסף, חשוב מאוד! tool_choice
הושמט מהבקשה, אחרת תגובת ה-LLM
תהיה שוב הוראת הפעלה לפונקציה...
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "מה התחזית להיום בירושלים"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_hH7LRv5VEFHOjCCeVOS2TPIs",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"latitude\":31.7683,\"longitude\":35.2137}"
}
}
],
"refusal": null
},
{
"role": "tool",
"tool_call_id": "call_hH7LRv5VEFHOjCCeVOS2TPIs",
"content": "10"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current temperature for provided coordinates in celsius.",
"parameters": {
"type": "object",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
},
"required": [
"latitude",
"longitude"
],
"additionalProperties": false
},
"strict": true
}
}
]
}
וכעת, לתוצאה הסופית!
זו תשובת ה-LLM
עם המסקנה העולה מכל התהליך.
{
"id": "chatcmpl-ArwvINYR7gzz635qcaCSrzbGCH2UE",
"object": "chat.completion",
"created": 1737421248,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "הטמפרטורה בירושלים להיום היא 10 מעלות צלזיוס.",
"refusal": null
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 92,
"completion_tokens": 23,
"total_tokens": 115,
"prompt_tokens_details": {
"cached_tokens": 0,
"audio_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
},
"service_tier": "default",
"system_fingerprint": "fp_72ed7ab54c"
}
👏👏👏 (מחיאות כפיים סוערות!)
דוגמת קוד בפייתון
import requests
import json
# OpenAI API key configuration
api_key = "sk-xxx" # Put your API Key here
url = "https://api.openai.com/v1/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
async def openai_chat_completion(message: str):
messages = [{"role": "user", "content": message}]
try:
response = requests.post(url, headers=headers, json={
"model": "gpt-4o-mini",
"messages": messages,
"tools": tools_weather,
"tool_choice": "required"
})
response.raise_for_status()
except requests.exceptions.HTTPError as http_err:
return None
completion = response.json()
tool_call = completion['choices'][0]['message']['tool_calls'][0]
args = json.loads(tool_call['function']['arguments'])
result = get_weather(args["latitude"], args["longitude"])
messages.append(completion['choices'][0]['message']) # append model's function call message
messages.append({ # append result message
"role": "tool",
"tool_call_id": tool_call['id'],
"content": result
})
try:
response2 = requests.post(url, headers=headers, json={
"model": "gpt-4o-mini",
"messages": messages,
"tools": tools_weather
})
response2.raise_for_status()
except requests.exceptions.HTTPError as http_err:
return None
return response2.json()
tools_weather = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current temperature for provided coordinates in celsius.",
"parameters": {
"type": "object",
"properties": {
"latitude": {"type": "number"},
"longitude": {"type": "number"}
},
"required": ["latitude", "longitude"],
"additionalProperties": False
},
"strict": True
}
}]
לסיום
ראינו דוגמה בסיסית לשימוש ב-Function calling
כדי לבצע RAG
- העשרה של ה-LLM
במידע מבחוץ על מנת לאפשר לו לצאת מעצמו ולתת מענה גם לשאלות שאין לו מידע לגביהן.
מכאן צפונה - האפשרויות הן בלי סוף 🙂↕️
דוגמית? נסי להוסיף ל-Agent
פיצ'ר המלצה מה ללבוש היום בהתאם לתחזית 🧥 🌂
בהנאה ובהצלחה 🤗