2. שימוש בכלים
"הוספת כלי פירושה הוספת handler אחד"
חדש בנושא?
מה זה 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 — המודל הופך לכשיר יותר מבלי שארכיטקטורת הליבה הופכת למורכבת יותר.
מדריך קוד אינטראקטיבי
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 path6 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. כל ניסיון בריחה עם '../' מעלה שגיאה לפני שנוגעים במערכת הקבצים.הוסיפו כלי חמישי — list_files — שמציג תוכן תיקייה. צריך רק סכמה ו-handler.
רמז
השתמשו ב-os.listdir() ב-handler והחזירו את שמות הקבצים