5. מיומנויות

"טענו ידע כשצריך, לא מראש"

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

מה זה מיומנות (skill) בהקשר זה?

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

מה זה הגישה הדו-שכבתית?

שכבה 1: תיאורי מיומנות קצרים בפרומפט המערכת (זול, תמיד גלוי). שכבה 2: תוכן מיומנות מלא שנטען דרך tool_result כשהמודל מבקש אותו (יקר, לפי דרישה). זה חוסך טוקנים.

למה לא לשים הכל בפרומפט המערכת?

10 מיומנויות של 2000 טוקן כל אחת = 20,000 טוקן של הוראות, רובם לא רלוונטים לכל משימה נתונה. הגישה הדו-שכבתית עולה ~100 טוקן למיומנות בפרומפט המערכת. תוכן מלא נטען רק כשצריך.

איך המודל יודע איזה מיומנות לטעון?

פרומפט המערכת מפרט שמות מיומנויות זמינות עם תיאורים קצרים. המודל קורא אותם ומחליט איזו מיומנות רלוונטית למשימה הנוכחית, ואז קורא ל-load_skill('name') כדי לקבל את ההוראות המלאות.

הבעיה

אתם רוצים שהסוכן יעקוב אחר תהליכי עבודה ספציפיים לתחום: מוסכמות git, תבניות בדיקות, רשימות סקירת קוד. הכנסת הכל לפרומפט המערכת מבזבזת טוקנים על מיומנויות שלא בשימוש. 10 מיומנויות של 2000 טוקן כל אחת = 20,000 טוקן, רובם לא רלוונטים לכל משימה נתונה.

הפתרון

System prompt (Layer 1 -- always present):
+--------------------------------------+
| You are a coding agent.              |
| Skills available:                    |
|   - git: Git workflow helpers        |  ~100 tokens/skill
|   - test: Testing best practices     |
+--------------------------------------+

When model calls load_skill("git"):
+--------------------------------------+
| tool_result (Layer 2 -- on demand):  |
| <skill name="git">                   |
|   Full git workflow instructions...  |  ~2000 tokens
|   Step 1: ...                        |
| </skill>                             |
+--------------------------------------+

שכבה 1: שמות מיומנויות בפרומפט המערכת (זול). שכבה 2: גוף מלא דרך tool_result (לפי דרישה).

איך זה עובד

  1. כל מיומנות היא תיקייה המכילה SKILL.md עם frontmatter מסוג YAML.
skills/
  pdf/
    SKILL.md       # ---\n name: pdf\n description: Process PDF files\n ---\n ...
  code-review/
    SKILL.md       # ---\n name: code-review\n description: Review code\n ---\n ...
  1. SkillLoader סורק קבצי SKILL.md, משתמש בשם התיקייה כמזהה המיומנות.
class SkillLoader:
    def __init__(self, skills_dir: Path):
        self.skills = {}
        for f in sorted(skills_dir.rglob("SKILL.md")):
            text = f.read_text()
            meta, body = self._parse_frontmatter(text)
            name = meta.get("name", f.parent.name)
            self.skills[name] = {"meta": meta, "body": body}

    def get_descriptions(self) -> str:
        lines = []
        for name, skill in self.skills.items():
            desc = skill["meta"].get("description", "")
            lines.append(f"  - {name}: {desc}")
        return "\n".join(lines)

    def get_content(self, name: str) -> str:
        skill = self.skills.get(name)
        if not skill:
            return f"Error: Unknown skill '{name}'."
        return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"
  1. שכבה 1 נכנסת לפרומפט המערכת. שכבה 2 היא רק handler נוסף לכלי.
SYSTEM = f"""You are a coding agent at {WORKDIR}.
Skills available:
{SKILL_LOADER.get_descriptions()}"""

TOOL_HANDLERS = {
    # ...base tools...
    "load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}

המודל לומד אילו מיומנויות קיימות (זול) וטוען אותן כשרלוונטי (יקר).

מה השתנה מ-תת-סוכנים

רכיבלפני (תת-סוכנים)אחרי (מיומנויות)
כלים5 (base + task)5 (base + load_skill)
פרומפט מערכתמחרוזת סטטית+ תיאורי מיומנויות
ידעאיןקבצי skills/*/SKILL.md
הזרקהאיןדו-שכבתי (system + result)

מסקנה מרכזית

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

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

מנגנון טעינת המיומנויות
1class SkillLoader:
2 def __init__(self, skills_dir: Path):
3 self.skills = {}
4 for f in sorted(skills_dir.rglob("SKILL.md")):
5 text = f.read_text()
6 meta, body = self._parse_frontmatter(text)
7 name = meta.get("name", f.parent.name)
8 self.skills[name] = {"meta": meta, "body": body}
9 
10 def get_descriptions(self) -> str:
11 lines = []
12 for name, skill in self.skills.items():
13 desc = skill["meta"].get("description", "")
14 lines.append(f" - {name}: {desc}")
15 return "\n".join(lines)
16 
17 def get_content(self, name: str) -> str:
18 skill = self.skills.get(name)
19 if not skill:
20 return f"Error: Unknown skill '{name}'."
21 return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"
22 
23TOOL_HANDLERS = {
24 "load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
25}
26 
__init__ סורקת את תיקיית המיומנויות רקורסיבית לחיפוש קבצי SKILL.md. היא מנתחת frontmatter של YAML כדי לקבל מטא-נתונים (שם, תיאור) ושומרת את הגוף בנפרד. שם התיקייה משמש כחלופה אם 'name' חסר ב-frontmatter.
שלב 1 מתוך 4
🧪 נסו בעצמכם

כתבו קובץ SKILL.md משלכם לתחום שאתם מכירים — בישול, מוזיקה, או תחום העבודה שלכם. טענו אותו לסוכן.

רמז

שימו אותו בתיקיית skills/ והסוכן ימצא אותו

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