יום 12 - לולאת הפעלת הכלים
הפעלת כלים היא מצד אחד היכולת הכי משמעותית של מודלים, היא מהווה בסיס להרבה תבניות עבודה עם מודלים אבל מצד שני בגלל האופי הלא אמין של מודלים היא גם מבלבלת ומקור בלתי נדלה לבאגים. היום נבין יותר לעומק איך מנגנון הפעלת הכלים עובד, מה נקודות הבקרה שלנו במנגנון ולאיזה מטרות נרצה להשתמש בו.
1. איך זה עובד - לולאת הפעלת כלים
בואו נתחיל עם תוכנית ראשונה כמעט העתקה מהדוגמה בתיעוד ששואלת מודל מה מזג האוויר ונותנת לו כלי באמצעותו יוכל לענות:
from typing_extensions import TypedDict
from agents import Agent, function_tool, Runner
from agents.extensions.models.litellm_model import LitellmModel
import requests
import os
import asyncio
weather_api_key = os.getenv("OPENWEATHER_API_KEY")
if not weather_api_key:
raise "Error: OpenWeather API key not found in environment variables"
class Location(TypedDict):
lat: float
long: float
@function_tool
async def fetch_weather(location: Location) -> str:
base_url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"lat": location["lat"],
"lon": location["long"],
"appid": weather_api_key,
"units": "metric"
}
# Run the synchronous requests call in a thread pool
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, requests.get, base_url, params)
response.raise_for_status()
data = response.json()
weather_desc = data["weather"][0]["description"]
temp = data["main"]["temp"]
feels_like = data["main"]["feels_like"]
humidity = data["main"]["humidity"]
return f"Weather: {weather_desc.title()}, Temperature: {temp}°C (feels like {feels_like}°C), Humidity: {humidity}%"
agent = Agent(
name="Assistant",
model=LitellmModel(model="github/gpt-4.1", api_key=os.environ["GITHUB_TOKEN"]),
tools=[fetch_weather],
)
async def main():
result = await Runner.run(agent, "What is the weather in Tel Aviv?")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
הפעלה של התוכנית אצלי מדפיסה את הפלט:
The weather in Tel Aviv is currently clear with a temperature of 37.05°C (feels like 40.16°C) and humidity at 37%. It's quite hot outside!
אבל בואו נתמקד בכלי ובהפעלת כלי - מה זה אומר שהמודל ״מפעיל כלי״? איך מודל יכול להפעיל כלי? ואיך המודל יודע איזה כלים צריך להפעיל?
התשובה היא שהמודל מתקשר עם הקוד שלנו בממשק טקסטואלי בלבד - אנחנו שולחים הודעת טקסט ומקבלים בחזרה טקסט. הטקסט שהמודל מחזיר הוא ההמשך הסביר ביותר ל"שיחה" שאת תחילתה אנחנו שולחים. כך עובדים מודלי שפה גדולים.
כשמדובר בכלים יש פה שיתוף פעולה בין כמה רכיבים במערכת:
המודל עצמו מאומן בצורה שהודעות מסוג מסוים יובילו לתשובות מסוג מסוים. בפרט בנושא של כלים אם ההודעה מגיעה במבנה מסוים שמכיל שם של כלי ורשימת פרמטרים המודל מאומן לענות בפורמט מתאים מסוים - שנראה כמו JSON עם פרטים על הכלי אותו רוצים להפעיל ורשימת הפרמטרים שלו.
ספריית הסוכנים OpenAI Agents מזהה את פורמט התשובה של מודל שמעוניין להפעיל כלי ומפעילה את הפונקציה שהעברתי, בדוגמה שראינו זו פונקציית חישוב מזג האוויר. את התוצאה היא מעבירה חזרה למודל. בממשק העברת כלים המודל מבקש הפעלת כלי ומצרף לבקשה מזהה אקראי, וספריית הסוכנים מצרפת את אותו מזהה בדיוק לתשובה.
המודל יכול להחליף להפעיל את הכלי שוב, כלומר להחזיר שוב פעם טקסט שמייצג הפעלת כלי, או לעצור ולהחזיר הודעת סיום שמסכמת את התוצאה של הכלי.
נמשיך לחקור - אני מוסיף SQLite Session לתוכנית כדי שיהיה תיעוד לכל ההודעות ומפעיל את הקוד המעודכן:
async def main():
session = SQLiteSession("weather", "weather.db")
result = await Runner.run(agent, "What is the weather in Tel Aviv?", session=session)
print(result.final_output)
עכשיו נוצר לי קובץ חדש בתיקייה בשם weather.db עם כל ההודעות. זה התוכן:
sqlite> select * from agent_messages ;
1|weather|{"content": "What is the weather in Tel Aviv?", "role": "user"}|2025-08-14 09:54:56
2|weather|{"arguments": "{\"location\":{\"lat\":32.0853,\"long\":34.7818}}", "call_id": "call_obLi5Lbz93fzOIDRvC195udg", "name": "fetch_weather", "type": "function_call", "id": "__fake_id__"}|2025-08-14 09:54:56
3|weather|{"call_id": "call_obLi5Lbz93fzOIDRvC195udg", "output": "Weather: Clear Sky, Temperature: 36.9\u00b0C (feels like 40.23\u00b0C), Humidity: 38%", "type": "function_call_output"}|2025-08-14 09:54:56
4|weather|{"id": "__fake_id__", "content": [{"annotations": [], "text": "The weather in Tel Aviv is currently clear sky with a temperature of 36.9\u00b0C (feels like 40.23\u00b0C) and humidity at 38%. It's quite hot today!", "type": "output_text"}], "role": "assistant", "status": "completed", "type": "message"}|2025-08-14 09:54:56
נשים לב ששלחנו הודעה מהמשתמש, בתגובה המודל ענה בהודעה עם בקשה להפעלת כלי ונתן לה מזהה call_obLi5Lbz93fzOIDRvC195udg, לאחר מכן ספריית הסוכנים הפעילה את הפונקציה והוסיפה תגובה חדשה עם JSON שכולל את אותו מזהה ובסוף המודל הוסיף הודעה שעונה למשתמש בעזרת התשובה שהגיעה מהכלי. סך הכל הפקודה:
result = await Runner.run(agent, "What is the weather in Tel Aviv?", session=session)
פנתה למודל פעמיים - בפעם הראשונה קיבלה בקשה להפעלת כלי, ובפעם השניה קיבלה את הטקסט שיחזור ל result.final_output.
בואו נבעט בו עוד קצת ונבקש את מזג האוויר במספר מקומות:
async def main():
session = SQLiteSession("weather", "weather.db")
result = await Runner.run(agent, "I'm planning a trip to Israel, what is the weather in Tel Aviv, Jerusalem, Haifa and Eilat today?", session=session)
print(result.final_output)
אלה ההודעות בבסיס הנתונים:
5|weather|{"content": "I'm planning a trip to Israel, what is the weather in Tel Aviv, Jerusalem, Haifa and Eilat today?", "role": "user"}|2025-08-14 09:59:42
6|weather|{"arguments": "{\"location\": {\"lat\": 32.0853, \"long\": 34.7818}}", "call_id": "call_la2QHswRUH0Xa80wZNLQ6dOv", "name": "fetch_weather", "type": "function_call", "id": "__fake_id__"}|2025-08-14 09:59:42
7|weather|{"arguments": "{\"location\": {\"lat\": 31.7683, \"long\": 35.2137}}", "call_id": "call_AEGdktcxYfR2MzGS7BzhA9AY", "name": "fetch_weather", "type": "function_call", "id": "__fake_id__"}|2025-08-14 09:59:42
8|weather|{"arguments": "{\"location\": {\"lat\": 32.794, \"long\": 34.9896}}", "call_id": "call_CFb2TkCz6M6eL19aT7c69Zae", "name": "fetch_weather", "type": "function_call", "id": "__fake_id__"}|2025-08-14 09:59:42
9|weather|{"arguments": "{\"location\": {\"lat\": 29.5577, \"long\": 34.9519}}", "call_id": "call_DWgN1qFAru8ScClVHj1jgqIz", "name": "fetch_weather", "type": "function_call", "id": "__fake_id__"}|2025-08-14 09:59:42
10|weather|{"call_id": "call_la2QHswRUH0Xa80wZNLQ6dOv", "output": "Weather: Clear Sky, Temperature: 36.9\u00b0C (feels like 40.23\u00b0C), Humidity: 38%", "type": "function_call_output"}|2025-08-14 09:59:42
11|weather|{"call_id": "call_AEGdktcxYfR2MzGS7BzhA9AY", "output": "Weather: Clear Sky, Temperature: 38.34\u00b0C (feels like 42.39\u00b0C), Humidity: 36%", "type": "function_call_output"}|2025-08-14 09:59:42
12|weather|{"call_id": "call_CFb2TkCz6M6eL19aT7c69Zae", "output": "Weather: Clear Sky, Temperature: 33.39\u00b0C (feels like 34.41\u00b0C), Humidity: 40%", "type": "function_call_output"}|2025-08-14 09:59:42
13|weather|{"call_id": "call_DWgN1qFAru8ScClVHj1jgqIz", "output": "Weather: Clear Sky, Temperature: 42.91\u00b0C (feels like 45.23\u00b0C), Humidity: 23%", "type": "function_call_output"}|2025-08-14 09:59:42
14|weather|{"id": "__fake_id__", "content": [{"annotations": [], "text": "Here is today's weather in major Israeli cities:\n\n- Tel Aviv: Clear sky, 36.9\u00b0C (feels like 40.23\u00b0C), 38% humidity\n- Jerusalem: Clear sky, 38.34\u00b0C (feels like 42.39\u00b0C), 36% humidity\n- Haifa: Clear sky, 33.39\u00b0C (feels like 34.41\u00b0C), 40% humidity\n- Eilat: Clear sky, 42.91\u00b0C (feels like 45.23\u00b0C), 23% humidity\n\nIt\u2019s a very hot and sunny day across the country\u2014plan accordingly!", "type": "output_text"}], "role": "assistant", "status": "completed", "type": "message"}|2025-08-14 09:59:42
הפעם הודעת המודל להפעלת כלי כללה בקשה להפעלת ארבעה כלים. ספריית הסוכנים רשמה את זה בתור ארבע הודעות בטבלה, הוסיפה 4 תשובות ואז פנתה שוב למודל כדי לייצר את התשובה הסופית.
2. לולאת הפעלת כלים
מנגנון הפעלת הכלים שראינו נקרא React שזה קיצור של Reason + Act. סוכני ריאקט עובדים בלולאה ויכולים להפעיל כלים שוב ושוב עד שמגיעים לתשובה סופית. נראה דוגמה שכוללת הפעלת מספר כלים אחד אחרי השני - אני מייצר שלוש קופסאות עם מזהים אקראיים ומבקש מהמודל לפתוח קופסאות עד שהוא מגיע לאוצר:
from typing_extensions import TypedDict
from agents import Agent, function_tool, Runner, SQLiteSession
from agents.extensions.models.litellm_model import LitellmModel
from uuid import uuid4
import requests
import os
import asyncio
boxes = [
{
"id": str(uuid4()),
"has_treasure": False
},
{
"id": str(uuid4()),
"has_treasure": True
},
{
"id": str(uuid4()),
"has_treasure": False
}
]
@function_tool
async def open_box(id: str) -> bool:
"""Open a box and search for the treasure inside. Returns True if found"""
return next(b['has_treasure'] for b in boxes if b['id'] == id)
@function_tool
async def get_box_ids() -> list[str]:
return [b['id'] for b in boxes]
agent = Agent(
name="Assistant",
model=LitellmModel(model="github/gpt-4.1", api_key=os.environ["GITHUB_TOKEN"]),
tools=[open_box, get_box_ids],
)
async def main():
session = SQLiteSession("boxes", "boxes.db")
result = await Runner.run(agent, """
Let's play a game. You are given 3 boxes and you need to find the treasure hidden in one of them. You can open any box you want
to look inside.
Use open_box tool to open boxes and find the treasure.
Try to find it by opening the miniumum number of boxes.
""", session=session)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
אלה ההודעות שנוצרו הפעם:
sqlite> select * from agent_messages ;
1|boxes|{"content": "\n Let's play a game. You are given 3 boxes and you need to find the treasure hidden in one of them. You can open any box you want\n to look inside.\n Use open_box tool to open boxes and find the treasure. \n Try to find it by opening the miniumum number of boxes.\n ", "role": "user"}|2025-08-14 10:13:28
2|boxes|{"arguments": "{}", "call_id": "call_Rjqjj5dQyxCISwpPMR3hrrai", "name": "get_box_ids", "type": "function_call", "id": "__fake_id__"}|2025-08-14 10:13:28
3|boxes|{"call_id": "call_Rjqjj5dQyxCISwpPMR3hrrai", "output": "['c77fa56d-0375-4e8d-9470-3f494b16706c', '1e499625-705d-40f7-bae4-df71bf97c523', 'd3f99c22-33fd-4cce-b7c2-9f7de13c122e']", "type": "function_call_output"}|2025-08-14 10:13:28
4|boxes|{"arguments": "{\"id\":\"c77fa56d-0375-4e8d-9470-3f494b16706c\"}", "call_id": "call_MU2FdBwcGn9ayc0f04UyS1ea", "name": "open_box", "type": "function_call", "id": "__fake_id__"}|2025-08-14 10:13:28
5|boxes|{"call_id": "call_MU2FdBwcGn9ayc0f04UyS1ea", "output": "False", "type": "function_call_output"}|2025-08-14 10:13:28
6|boxes|{"arguments": "{\"id\":\"1e499625-705d-40f7-bae4-df71bf97c523\"}", "call_id": "call_0ukIo397uSCrgRHDNwJugFuC", "name": "open_box", "type": "function_call", "id": "__fake_id__"}|2025-08-14 10:13:28
7|boxes|{"call_id": "call_0ukIo397uSCrgRHDNwJugFuC", "output": "True", "type": "function_call_output"}|2025-08-14 10:13:28
8|boxes|{"id": "__fake_id__", "content": [{"annotations": [], "text": "I opened the first box, but didn't find the treasure. Then I opened the second box and found the treasure inside! \n\nI found the treasure by opening only 2 boxes.", "type": "output_text"}], "role": "assistant", "status": "completed", "type": "message"}|2025-08-14 10:13:28
הפעם אנחנו רואים מבנה שונה - המודל לא יכל להעביר את כל הקריאות במכה אחת בתשובה להודעה הראשונה כי הוא לא ידע מה המזהים של הקופסאות. אפילו אחרי שהוא השתמש בכלי כדי לקבל רשימת מזהים הוא עדיין לא רצה לפתוח את כל שלושת הקופסאות במכה אחת כי ביקשתי לצמצם את מספר הפתיחות, לכן אנחנו רואים את העבודה פעולה אחרי פעולה.
מה אם היו מאות או אלפי קופסאות? מה אם כלל לא היה אוצר?
3. שליטה על לולאת הפעלת הכלים
נשים לב שספריית OpenAI Agents SDK עובדת בלולאה - כל פעם שהמודל מבקש להפעיל כלי הספריה מפעילה את הפונקציה, מוסיפה את ההודעה המתאימה לרשימת ההודעות ופונה שוב למודל. לולאה כזאת יכולה לקחת הרבה זמן מאחר וכל פניה למודל יכולה להיות איטית וגם לעלות בהרבה טוקנים ולכן הרבה כסף, במיוחד אם המודל מתבלבל ומפעיל כלים בלי להגיע לתוצאה.
דרך אחת לשלוט בלולאה היא הפרמטר max_turns שניתן להעביר ל Runner.run. פרמטר זה מגביל את מספר האיטרציות - אם המודל ינסה להפעיל כלים יותר פעמים ממה שמוגדר במשתנה הספריה תפסיק את הלולאה ותסיים עם שגיאה. זה נראה כך:
async def main():
session = SQLiteSession("boxes", "boxes.db")
result = await Runner.run(agent, """
Let's play a game. You are given 3 boxes and you need to find the treasure hidden in one of them. You can open any box you want
to look inside.
Use open_box tool to open boxes and find the treasure.
Try to find it by opening the miniumum number of boxes.
""", session=session, max_turns=3)
print(result.final_output)
והרצה עם שגיאה מסתיימת כך:
agents.exceptions.MaxTurnsExceeded: Max turns (3) exceeded
דרך נוספת לשלוט בלולאה היא המאפיין tool_use_behavior בהגדרת סוכן:
agent = Agent(
name="Assistant",
model=LitellmModel(model="github/gpt-4.1", api_key=os.environ["GITHUB_TOKEN"]),
tools=[open_box, get_box_ids],
tool_use_behavior=custom_tool_use_behavior
)
מאפיין זה יכול לקבל פונקציה שתוכל להחליט אם להמשיך את הלולאה או להפסיק. אני מריץ את הפונקציה הבאה קודם כל בשביל לחקור את הפרמטרים שלה:
def custom_tool_use_behavior(context, tool_results: list[FunctionToolResult]) -> ToolsToFinalOutputResult:
print(context)
for result in tool_results:
print(result)
print("---")
# Otherwise, continue
return ToolsToFinalOutputResult(is_final_output=False)
והפלט לאחר סידור הוא:
RunContextWrapper(
context=None,
usage=Usage(
requests=1,
input_tokens=437,
input_tokens_details=InputTokensDetails(
cached_tokens=0
),
output_tokens=40,
output_tokens_details=OutputTokensDetails(
reasoning_tokens=0
),
total_tokens=477
)
)
FunctionToolResult(
tool=FunctionTool(
name='open_box',
description='Open a box and search for the treasure inside. Returns True if found',
params_json_schema={
'properties': {
'id': {
'title': 'Id',
'type': 'string'
}
},
'required': ['id'],
'title': 'open_box_args',
'type': 'object',
'additionalProperties': False
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x104dd1bc0>,
strict_json_schema=True,
is_enabled=True
),
output='An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
run_item=ToolCallOutputItem(
agent=Agent(
name='Assistant',
handoff_description=None,
tools=[
FunctionTool(
name='open_box',
description='Open a box and search for the treasure inside. Returns True if found',
params_json_schema={
'properties': {
'id': {
'title': 'Id',
'type': 'string'
}
},
'required': ['id'],
'title': 'open_box_args',
'type': 'object',
'additionalProperties': False
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x104dd1bc0>,
strict_json_schema=True,
is_enabled=True
),
FunctionTool(
name='get_box_ids',
description='',
params_json_schema={
'properties': {},
'title': 'get_box_ids_args',
'type': 'object',
'additionalProperties': False,
'required': []
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x110d2a200>,
strict_json_schema=True,
is_enabled=True
)
],
mcp_servers=[],
mcp_config={},
instructions=None,
prompt=None,
handoffs=[],
model=<agents.extensions.models.litellm_model.LitellmModel object at 0x110aa7620>,
model_settings=ModelSettings(
temperature=None,
top_p=None,
frequency_penalty=None,
presence_penalty=None,
tool_choice=None,
parallel_tool_calls=None,
truncation=None,
max_tokens=None,
reasoning=None,
metadata=None,
store=None,
include_usage=None,
response_include=None,
extra_query=None,
extra_body=None,
extra_headers=None,
extra_args=None
),
input_guardrails=[],
output_guardrails=[],
output_type=None,
hooks=None,
tool_use_behavior=<function custom_tool_use_behavior at 0x110d2a0c0>,
reset_tool_choice=True
),
raw_item={
'call_id': 'call_AakkVgr7fikDFcam0VADoa9z',
'output': 'An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
'type': 'function_call_output'
},
output='An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
type='tool_call_output_item'
)
)
---
RunContextWrapper(
context=None,
usage=Usage(
requests=2,
input_tokens=939,
input_tokens_details=InputTokensDetails(
cached_tokens=0
),
output_tokens=78,
output_tokens_details=OutputTokensDetails(
reasoning_tokens=0
),
total_tokens=1017
)
)
FunctionToolResult(
tool=FunctionTool(
name='open_box',
description='Open a box and search for the treasure inside. Returns True if found',
params_json_schema={
'properties': {
'id': {
'title': 'Id',
'type': 'string'
}
},
'required': ['id'],
'title': 'open_box_args',
'type': 'object',
'additionalProperties': False
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x104dd1bc0>,
strict_json_schema=True,
is_enabled=True
),
output='An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
run_item=ToolCallOutputItem(
agent=Agent(
name='Assistant',
handoff_description=None,
tools=[
FunctionTool(
name='open_box',
description='Open a box and search for the treasure inside. Returns True if found',
params_json_schema={
'properties': {
'id': {
'title': 'Id',
'type': 'string'
}
},
'required': ['id'],
'title': 'open_box_args',
'type': 'object',
'additionalProperties': False
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x104dd1bc0>,
strict_json_schema=True,
is_enabled=True
),
FunctionTool(
name='get_box_ids',
description='',
params_json_schema={
'properties': {},
'title': 'get_box_ids_args',
'type': 'object',
'additionalProperties': False,
'required': []
},
on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x110d2a200>,
strict_json_schema=True,
is_enabled=True
)
],
mcp_servers=[],
mcp_config={},
instructions=None,
prompt=None,
handoffs=[],
model=<agents.extensions.models.litellm_model.LitellmModel object at 0x110aa7620>,
model_settings=ModelSettings(
temperature=None,
top_p=None,
frequency_penalty=None,
presence_penalty=None,
tool_choice=None,
parallel_tool_calls=None,
truncation=None,
max_tokens=None,
reasoning=None,
metadata=None,
store=None,
include_usage=None,
response_include=None,
extra_query=None,
extra_body=None,
extra_headers=None,
extra_args=None
),
input_guardrails=[],
output_guardrails=[],
output_type=None,
hooks=None,
tool_use_behavior=<function custom_tool_use_behavior at 0x110d2a0c0>,
reset_tool_choice=True
),
raw_item={
'call_id': 'call_4cPEq87qI3ccfPvCEQKZrelY',
'output': 'An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
'type': 'function_call_output'
},
output='An error occurred while running the tool. Please try again. Error: coroutine raised StopIteration',
type='tool_call_output_item'
)
)
---
אנחנו רואים שהפונקציה נקראה כל פעם שהמודל מפעיל כלי עם התוצאה של הכלי שהופעל ופרטי הכלי. אין לנו את הפרמטרים שהמודל העביר לכלי.
בשביל לעצור את הלולאה אני יכול להחזיר אוביקט ToolsToFinalOutputResult עם is_final_output מוגדר ל True, לדוגמה הקוד הבא עוצר את הלולאה אחרי פתיחת הקופסה הראשונה:
def custom_tool_use_behavior(context, tool_results: list[FunctionToolResult]) -> ToolsToFinalOutputResult:
print(context)
for result in tool_results:
if result.tool.name == "open_box":
return ToolsToFinalOutputResult(is_final_output=True, final_output=result.output)
# Otherwise, continue
return ToolsToFinalOutputResult(is_final_output=False)
4. מתי משתמשים בכל דרך
בפוסט ראינו שתי דרכים לשלוט ולעצור את לולאת הפעלת הכלים. במערכות אמיתיות נרצה לשלב את שתי הדרכים:
נרצה להשתמש ב
max_turnsכדי לוודא שהמודל לא יוצא משליטה בהפעלת הכלים. הפעלה של יותר מדי כלים עלולה להיות סימן שהמודל נכנס ללולאה ולא מצליח לחשב את התשובה הבאה.נרצה להשתמש בפונקציית הבקרה כדי לממש סוכנים שיודעים "לוותר על השליטה", לדוגמה סוכן שאחראי על ביצוע שלב מסוים בתהליך וכשהוא מסיים הוא יוכל להפעיל כלי "סיום" שיעביר את התוצאה לסוכן אחר להמשך עבודה. נוכל להשתמש בפונקציה גם לבקרה שהסוכן לא מנסה להפעיל כלים בהתבסס על מידע שגוי ולהוציא אותו מלולאות של חישובים שגויים.
בהמשך הסידרה נדבר על עוד שני מנגנונים לשליטה על סוכנים - מנגנון Handoff ומנגנון Guardrails, כל אחד מהם בדרכו יעזור לנו לעצור פעולה של סוכן לפני שהסתיימה.
5. עכשיו אתם
כתבו סוכן שמקבל שלושה כלים - הצגת רשימת הקבצים בתיקייה, קריאת טקסט בקובץ וכניסה לתיקיה אחרת. השתמשו בסוכן כדי לחפש קובץ שמכיל טקסט מסוים. תעדו את הפעלת הכלים. לאחר מכן הוסיפו "מוקשים", קבצים שכשפותחים אותם לולאת הפעלת הכלים של הסוכן תיעצר.