2. שימוש בכלים

"הוספת כלי פירושה הוספת handler אחד"

15 דקות קריאה
💡חדש בנושא?

מה זה dispatch map?

מילון שממפה שמות כלים לפונקציות שמטפלות בהם. כשהמודל קורא ל-'read_file', מפת ה-dispatch מחפשת איזו פונקציית Python מטפלת בזה — כמו ספר טלפונים לכלים.

למה אנחנו עושים sandbox לנתיבי קבצים?

כדי למנוע מהסוכן לקרוא או לכתוב קבצים מחוץ לתיקיית הפרויקט. הפונקציה safe_path() בודקת שכל נתיב מבוקש נשאר בתוך סביבת העבודה — גבול אבטחה בסיסי.

מה זה path traversal?

טריק שבו מישהו משתמש ב-'../' בנתיב קובץ כדי להימלט מהתיקייה המיועדת. לדוגמה, '../../etc/passwd' מנסה לקרוא קבצי מערכת. ה-sandbox שלנו חוסם זאת.

הבעיה

לולאת הסוכן נתנה לסוכן כלי אחד: bash. זה עובד, אבל זה כלי גס. כל קריאת קובץ דורשת cat, כל כתיבה דורשת echo >, כל עריכה דורשת sed. המודל מבזבז טוקנים על תחביר shell כשהוא יכול להשתמש בכלים ייעודיים.

הפתרון

הוסיפו כלים למערך. הוסיפו handlers למפת ה-dispatch. הלולאה לא משתנה.

TOOL_HANDLERS = {
    "bash":       run_bash,
    "read_file":  run_read,
    "write_file": run_write,
    "edit_file":  run_edit,
}

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

מפת ה-Dispatch

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

def run_read(path: str, limit: int = None) -> str:
    text = safe_path(path).read_text()
    lines = text.splitlines()
    if limit and limit < len(lines):
        lines = lines[:limit]
    return "\n".join(lines)[:50000]

def run_write(path: str, content: str) -> str:
    fp = safe_path(path)
    fp.parent.mkdir(parents=True, exist_ok=True)
    fp.write_text(content)
    return f"Wrote {len(content)} bytes to {path}"

def run_edit(path: str, old_text: str, new_text: str) -> str:
    fp = safe_path(path)
    content = fp.read_text()
    if old_text not in content:
        return f"Error: Text not found in {path}"
    fp.write_text(content.replace(old_text, new_text, 1))
    return f"Edited {path}"

TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

מה השתנה מ-לולאת הסוכן

רכיבלולאת הסוכןשימוש בכלים
לולאהwhile True + stop_reasonזהה
כלים1 (bash)4 (bash, read, write, edit)
Dispatchקריאה ישירהמפה: {name: handler}
אבטחהרשימת פקודות חסומות+ Sandbox לנתיבים

הלולאה זהה. הצמיחה היחידה היא במערך הכלים ובמפת ה-dispatch. התבנית הזו מתרחבת ללא הגבלה — הסשנים הבאים ממשיכים להוסיף כלים מבלי לגעת בלולאה.

מסקנה מרכזית

הוספת כלי לסוכן פירושה שני דברים: (1) סכמת JSON שהמודל רואה, (2) פונקציית handler שה-harness קורא לה. הלולאה לעולם לא משתנה. זו היסוד של הנדסת ה-harness — המודל הופך לכשיר יותר מבלי שארכיטקטורת הליבה הופכת למורכבת יותר.

מדריך קוד אינטראקטיבי

מפת ה-Dispatch
1def safe_path(p: str) -> Path:
2 path = (WORKDIR / p).resolve()
3 if not path.is_relative_to(WORKDIR):
4 raise ValueError(f"Path escapes workspace: {p}")
5 return path
6 
7def run_read(path: str, limit: int = None) -> str:
8 text = safe_path(path).read_text()
9 lines = text.splitlines()
10 if limit and limit < len(lines):
11 lines = lines[:limit]
12 return "\n".join(lines)[:50000]
13 
14def run_write(path: str, content: str) -> str:
15 fp = safe_path(path)
16 fp.parent.mkdir(parents=True, exist_ok=True)
17 fp.write_text(content)
18 return f"Wrote {len(content)} bytes to {path}"
19 
20def run_edit(path: str, old_text: str, new_text: str) -> str:
21 fp = safe_path(path)
22 content = fp.read_text()
23 if old_text not in content:
24 return f"Error: Text not found in {path}"
25 fp.write_text(content.replace(old_text, new_text, 1))
26 return f"Edited {path}"
27 
28TOOL_HANDLERS = {
29 "bash": lambda **kw: run_bash(kw["command"]),
30 "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
31 "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
32 "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
33}
34 
safe_path() היא גבול האבטחה. היא פותרת את הנתיב ובודקת שהוא נשאר בתוך WORKDIR. כל ניסיון בריחה עם '../' מעלה שגיאה לפני שנוגעים במערכת הקבצים.
שלב 1 מתוך 5
🧪 נסו בעצמכם

הוסיפו כלי חמישי — list_files — שמציג תוכן תיקייה. צריך רק סכמה ו-handler.

רמז

השתמשו ב-os.listdir() ב-handler והחזירו את שמות הקבצים

מצאתם טעות? דווחו ←