• בלוג
  • יום 5 - שימוש בכלים

יום 5 - שימוש בכלים

04/10/2025

זוכרים את השורה-

result = await Runner.run(agent, user_message)

דיברנו על זה שהיא שולחת רשימת הודעות ממשתמש למודל AI, אבל בעצם מדובר בלולאה - לולאת הפעלת הכלים.

1. מה זה הפעלת כלים

אנחנו יודעים איך להעביר מידע בדחיפה ל AI במערכת משולבת AI - פשוט מוסיפים את המידע הזה לרשימת ההודעות. זה מה שעשינו כבר עם ההודעה מהמשתמש, וקל לראות איך זה עובד עם מידע נוסף, לדוגמה תוכן של קובץ:

async def main():
    agent = Agent(
        name="Assistant",
        instructions="Answer questions about the file /etc/shells",
    )

    file_content = open('/etc/shells', 'r').read()
    result = await Runner.run(agent, [
        {"role": "user", "content": f"File Contents: {file_content}"},
        {"role": "user", "content": "What shells are installed?"}
    ])
    print(result.final_output)

אבל מה לגבי הכיוון ההפוך? מה אם התשובה לשאלה מוחבאת בתוך קובץ וה AI מצליח לזהות את זה? פה נכנס המנגנון של הפעלת כלים והוא עובד כך:

  1. אנחנו שולחים סידרת הודעות לסוכן ומבקשים השלמה. סידרת ההודעות כוללת הודעה שמפרטת רשימה של "פונקציות" שהסוכן יכול לבקש להפעיל.

  2. הסוכן יודע לשלוח רק תשובה אחת. בגלל ששלחנו רשימה של פונקציות התשובה עשויה להיות בקשה להפעלת אחת או יותר מפונקציות אלה - זוהי בקשה להפעלת כלים.

  3. הקוד שקרא ל API צריך לזהות את הבקשה, להפעיל את הפונקציה ולפנות שוב לסוכן עם התשובה. הפעם רשימת ההודעות תכלול את התשובה הקודמת של הסוכן שמבקשת להפעיל כלי, ואז הודעה עם תוצאת הכלי.

  4. עכשיו הסוכן צריך להוסיף הודעה. זו יכולה להיות הודעת סיכום עם תשובה או הודעה עם בקשה להפעלת כלי נוסף.

ביצירת הסוכן אנחנו יכולים לבחור את מדיניות הפעלת הכלים, עד מתי להמשיך את הלולאה וגם איזה כלים לאפשר לסוכן להריץ. בואו נראה איך זה עובד באמצעות היפוך הסדר, במקום שאני אעביר מראש את תוכן הקובץ אני אתן לסוכן את האפשרות לקרוא אותו.

2. העברת כלי לסוכן

כל סוכן יודע לקבל בבנאי מערך של כלים. כלי הוא בסך הכל פונקציה שמסומנת בתור "כלי" בעזרת דקורטור בשם function_tool מה SDK. התיעוד של הפונקציה עובר לסוכן בתור תיאור הכלי וכך הסוכן יודע באיזה מצבים להפעיל את אותה פונקציה.

קוד? בטח:

import asyncio
from agents import Agent, Runner, function_tool

@function_tool()
def read_shells_file():
    """
    Reads the file /etc/shells and returns the full content
    """
    print("Tool Call: read_shells_file")
    with open('/etc/shells', 'r', encoding='utf8') as f:
        return f.read()

async def main():
    agent = Agent(
        name="Assistant",
        tools=[read_shells_file],
        instructions="Answer questions about the file /etc/shells",
    )

    result = await Runner.run(agent, "What shells are installed?")
    print(result.to_input_list())
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

הפונקציה בדוגמה לא מקבלת פרמטרים. התיעוד שלה מבהיר לסוכן שצריך להפעיל אותה כשרוצים לקבל את התוכן של הקובץ /etc/shells. מאחר וביקשנו ממנו לענות על שאלות על הקובץ ושלחנו שאלה על הקובץ הסוכן מבין שכדאי מאוד לקרוא את הקובץ כדי שיוכל לענות על השאלה.

כשמפעילים את התוכנית מקבלים את הפלט הבא:

/Users/ynonp/work/projects/ai/leanagentssdk/.venv/bin/python /Users/ynonp/work/projects/ai/leanagentssdk/explain_file.py 
Tool Call: read_shells_file
[{'role': 'user', 'content': 'What shells are installed?'}, {'arguments': '{}', 'call_id': 'call_LLe5SmhcYkLyouuo9lGwkgch', 'name': 'read_shells_file', 'type': 'function_call', 'id': 'fc_688e2ba2fe94819b990373d3ad78a90709782c5a467ec704', 'status': 'completed'}, {'call_id': 'call_LLe5SmhcYkLyouuo9lGwkgch', 'output': '# List of acceptable shells for chpass(1).\n# Ftpd will not allow users to connect who are not using\n# one of these shells.\n\n/bin/bash\n/bin/csh\n/bin/dash\n/bin/ksh\n/bin/sh\n/bin/tcsh\n/bin/zsh\n/usr/local/bin/pwsh\n/usr/local/bin/bash\n/usr/local/bin/zsh\n', 'type': 'function_call_output'}, {'id': 'msg_688e2ba416c4819b94d498d53c029cbd09782c5a467ec704', 'content': [{'annotations': [], 'text': 'The installed shells listed in the `/etc/shells` file are:\n\n1. `/bin/bash`\n2. `/bin/csh`\n3. `/bin/dash`\n4. `/bin/ksh`\n5. `/bin/sh`\n6. `/bin/tcsh`\n7. `/bin/zsh`\n8. `/usr/local/bin/pwsh`\n9. `/usr/local/bin/bash`\n10. `/usr/local/bin/zsh`', 'type': 'output_text', 'logprobs': []}], 'role': 'assistant', 'status': 'completed', 'type': 'message'}]
The installed shells listed in the `/etc/shells` file are:

1. `/bin/bash`
2. `/bin/csh`
3. `/bin/dash`
4. `/bin/ksh`
5. `/bin/sh`
6. `/bin/tcsh`
7. `/bin/zsh`
8. `/usr/local/bin/pwsh`
9. `/usr/local/bin/bash`
10. `/usr/local/bin/zsh`

Process finished with exit code 0

תחילה אני רואה את שורת ההדפסה Tool Call שמבהירה לי שהפונקציה שלי נקראה. אחר כך מודפסת רשימת כל ההודעות שמבהירה לי מה קרה. אני יכול לזהות שם:

  1. הודעה מהמשתמש עם השאלה שלי.

  2. הודעה מהסוכן שמבקש להפעיל את הפונקציה.

  3. הודעה אוטומטית מה SDK עם התוצאה של הפונקציה.

  4. ההודעה האחרונה של הסוכן שמסכמת את התשובה לשאלה.

בהמשך הסידרה נראה עוד דוגמאות להפעלת כלים וגם נכיר את הכלים המובנים ב SDK. בינתיים כמה תרגילים.

3. עכשיו אתם

  1. הוסיפו פרמטרים לפונקציה. נסו לתת לסוכן כלי שיוכל לקרוא כל קובץ על הדיסק שלכם. חשבו: האם יש בעיית אבטחה בכלי כזה? איזה כלים לא כדאי לכתוב?

  2. העבירו לסוכן שני כלים, אחד שיוצר קובץ ריק ואחד שיוצר תיקייה. בקשו מהסוכן ליצור תיקייה ובדקו שהיא נוצרה.

  3. החליפו את התיאור של שני הסוכנים ובקשו מהסוכן ליצור תיקייה. שימו לב שהפעם הופעל הכלי שיוצר קובץ (כי התיאור שלו אמר שהוא יוצר תיקייה) ונוצר קובץ.

  4. הפעילו את הקוד הבא:

import asyncio
from agents import Agent, Runner, function_tool

@function_tool()
def create_empty_file(filename: str):
    """
    Creates a new empty file
    :param filename: the filename to check
    """
    print("Tool Call: create_empty_file")

@function_tool()
def check_file_exists(filename: str) -> bool:
    """
    Check if a file exists
    :param filename: the filename to check
    :return: True if exists
    """
    print("Tool Call: check_file_exists")
    return False

async def main():
    agent = Agent(
        name="Assistant",
        tools=[create_empty_file, check_file_exists],
        instructions="You are a mission critical filesystem agent. Some operations may fail. You need to verify every action worked and retry failed operations",
    )

    result = await Runner.run(agent, "Create an empty file named test.txt")
    print(result.to_input_list())
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

מה אתם מצפים שיקרה? מה קרה? נסו את זה עם מודלים שונים ושימו לב אם ההתנהגות שונה ביניהם.