Knaph

دانش و زیرعامل‌ها29 از 376 دقیقه مطالعه

مهارت‌ها در عاملِ شما

لیلا روالِ گزارشِ ماهانه‌اش را به یک مهارت تبدیل کرده است: پوشه‌ای به نامِ skills/report/ که یک SKILL.md در آن است، با نام و توضیح در بالا و دوازده گامِ شماره‌دار در پایین. وقتی از Claude Code «گزارشِ اسفند» را می‌خواهد، Claude Code سراغِ همین مهارت می‌رود. mini البته نمی‌رود. حالا که mini می‌تواند فایلِ دستورها را بارگذاری کند، لیلا فکر می‌کند شاید بشود همان کار را دوباره کرد: همه‌ی SKILL.mdها را بخواند و همه را به پرامپت سیستمی اضافه کند.


لیلا بارگذاریِ فایلِ دستورها پنج خط بود. نمی‌شود مهارت‌ها را هم همان‌طور بارگذاری کنم؟

امید می‌شود. همه‌شان روی هم چقدر بلندند؟

لیلا هفت مهارت، شاید ۳۰٬۰۰۰ توکن. آه. اصلاً مهارت برای همین وجود دارد: برای چیزهایی که بلندتر از آن‌اند که در هر درخواست همراه باشند.

امید پس mini چه چیزی را در پرامپت سیستمی می‌گذارد؟

لیلا فقط آن بخشی را که مدل برای تصمیم گرفتن لازم دارد: نام و توضیحِ هر مهارت. برای هر کدام یک خط.

امید و وقتی کاری با یکی از این خط‌ها جور درآمد؟

لیلا آن وقت مدل بقیه‌اش را لازم دارد. اگر مسیرِ فایل را بداند، می‌تواند خودش با read_file بخواندش.

امید می‌تواند. حالا اگر به‌جایش ابزاری به نامِ load_skill به آن بدهیم، چه فرقی دارد؟

لیلا مهارت را با نامش می‌خواهد، پس نمی‌تواند مسیر را اشتباه بنویسد. در ردِ کار هم روشن‌تر است: «load_skill report» خودش می‌گوید چه اتفاقی افتاد. اجازه هم لازم ندارد، مثلِ یک فایلِ معمولی، چون فقط مهارت‌ها را می‌تواند بخواند.

امید یک پرسشِ دیگر. وقتی load_skill آن دوازده گام را برمی‌گرداند، این گام‌ها در کجای درخواست قرار می‌گیرند؟

لیلا ابزار است، پس... در یک نتیجه‌ی ابزار. در آخرِ پیام‌ها، بعد از همه‌چیز.

امید یعنی چه؟

لیلا یعنی آغازِ درخواست عوض نمی‌شود و کش سرِ جایش می‌ماند. بارگذاریِ یک مهارت فقط به اندازه‌ی توکن‌های خودِ مهارت هزینه دارد، نه بیشتر.


سه بخشِ یک SKILL.md

فایلِ مهارت یک سربرگ (header) دارد، میانِ دو خطِ ---، و یک بدنه (body) زیرِ آن. mini سه چیز از آن لازم دارد: نام و توضیح از سربرگ، و خودِ بدنه.

فایل SKILL.md و بخش‌هایش: سربرگ میان دو خطِ سه‌خط‌تیره نام و توضیح را دارد که به فهرست مهارت‌ها می‌روند؛ بدنه‌ی زیر آن گام‌ها را دارد که load_skill برمی‌گرداند.

read_skill یک فایل را می‌خواند:

def read_skill(path):
    text = path.read_text()
    _, header, body = text.split("---", 2)
    fields = dict(line.split(":", 1) for line in header.strip().splitlines() if ":" in line)
    return fields["name"].strip(), fields["description"].strip(), body.strip()
Python

خط به خط: split("---", 2) متن را در دو --- اول می‌بُرد و سه تکه می‌دهد: آنچه پیش از سربرگ است (هیچ)، خودِ سربرگ، و بدنه. آن 2 مهم است: یک --- پایین‌تر، درونِ بدنه، دست‌نخورده می‌ماند. سربرگ به یک دیکشنری تبدیل می‌شود: هر خطِ key: value در اولین دونقطه‌اش بریده می‌شود، تا توضیحی که خودش دونقطه دارد آن را نگه دارد. در آخر سه بخش، با فاصله‌های اضافه‌ی بریده‌شده، برمی‌گردند.

این عمداً یک خواننده‌ی کوچک است، نه یک تجزیه‌گرِ کاملِ YAML. انتظار دارد description: یک‌خطی باشد، و اگر فایلی نام یا توضیح نداشته باشد با صدای بلند می‌شکند (KeyError). تمرینِ بعد از این درس آن را مهربان‌تر می‌کند.

فهرست

skill_index خط‌هایی را می‌سازد که به پرامپت سیستمی می‌روند:

def skill_index():
    skills = sorted((ROOT / "skills").glob("*/SKILL.md"))
    if not skills:
        return ""
    lines = [f"- {name}: {description}" for name, description, _ in map(read_skill, skills)]
    return "\n\nSkills you can load with load_skill when a task matches:\n" + "\n".join(lines)
Python

glob("*/SKILL.md") فایلِ هر پوشه‌ی مهارت را پیدا می‌کند. sorted مهم‌تر از آن است که به نظر می‌رسد: بدونِ آن ممکن است ترتیب از یک اجرا به اجرای دیگر فرق کند، و ترتیبِ دیگر یعنی پرامپت سیستمیِ دیگر، که کش را بی‌دلیل می‌شکند. هر مهارت یک خط می‌شود، - name: description، زیرِ عنوانی که به مدل می‌گوید این فهرست برای چیست و با کدام ابزار باید کار کرد. اگر مهارتی نباشد، چیزی اضافه نمی‌شود.

همان‌طور که در درسِ قبل دیدید، این فهرست یک بار، وقتی mini راه می‌افتد، بعد از دستورهای پروژه به پرامپت سیستمی اضافه می‌شود.

ابزار

def load_skill(name):
    for path in (ROOT / "skills").glob("*/SKILL.md"):
        if read_skill(path)[0] == name:
            return read_skill(path)[2]
    return f"no skill called {name}"
 
 
TOOLS["load_skill"] = (load_skill, "Load the full instructions of a skill from the skills list.",
                       {"name": "the skill's name"})
Python

تابع دنبالِ مهارتی با آن نام می‌گردد و بدنه‌اش را برمی‌گرداند. نامِ ناشناخته خطایی نیست که mini را متوقف کند. پیامِ کوتاهی است که مدل می‌خواند و خودش را درست می‌کند، مثلِ هر نتیجه‌ی ابزارِ دیگری. خط‌های آخر آن را دقیقاً مثلِ ابزارهای فایل از درس «تعریفِ یک ابزار» در TOOLS ثبت می‌کنند: یک تابع، یک توضیح و یک ورودی. load_skill در مجموعه‌ی ALLOW در mini هم هست، پس هیچ‌وقت برای اجازه نمی‌ایستد: خواندنِ یک مهارت چیزی را تغییر نمی‌دهد.

یک مهارت، گام به گام

وقتی لیلا از mini گزارشِ اسفند را می‌خواهد، این اتفاق می‌افتد:

  1. شروعِ جلسه: پرامپت سیستمی دستورهای mini، AGENTS.md پروژه و توضیحِ یک‌خطیِ هفت مهارت را دارد.
  2. نوبتِ ۱: مدل درخواست را می‌خواند، می‌بیند خطِ «report: Writes the monthly sales report from the template. Use when asked for the monthly report.» با کار جور است، و load_skill را با name: report درخواست می‌کند.
  3. mini آن را اجرا می‌کند و دوازده گام به شکلِ یک نتیجه‌ی ابزار، در آخرِ پیام‌ها، برمی‌گردد.
  4. از نوبتِ ۲ به بعد: مدل گام‌ها را دنبال می‌کند و مثلِ همیشه ابزارهای فایل را به کار می‌برد. گام‌ها تا آخرِ جلسه در گفت‌وگو می‌مانند، مثلِ هر چیزِ دیگری که خوانده است.

مهارت در دو مرحله به مدل می‌رسد: در شروع جلسه فقط توضیحِ یک‌خطیِ آن در پرامپت سیستمی است؛ وقتی کاری با آن جور درمی‌آید، مدل load_skill را صدا می‌زند و گام‌های کامل به شکل یک نتیجه‌ی ابزار در آخر پیام‌ها می‌رسند.

این همان آشکارسازیِ تدریجی (progressive disclosure) است که پشتِ مهارت‌های عامل قرار دارد، حالا به شکلِ کدی که می‌توانید بخوانید: روی هم حدود ۲۵ خط.

mini یک چیز را کنار گذاشته است: پوشه‌ی یک مهارت می‌تواند فایل‌های دیگری هم داشته باشد (یک قالب، یک برگه‌ی راهنما، یک اسکریپت). mini برای آن‌ها چیزِ تازه‌ای لازم ندارد. گام‌ها می‌گویند «templates/report.md را کپی کن» یا «check_report.py را اجرا کن»، و مدل read_file و run_command را همان‌طور به کار می‌برد که برای هر فایلِ دیگری.

امتحان کنید

پوشه‌ای بسازید با skills/haiku/SKILL.md: سربرگی با name: haiku و description: Writes a haiku about a file's contents. Use when asked for a poem about a file.، و بدنه‌ای با سه گام. mini را آنجا اجرا کنید و بپرسید «write a poem about AGENTS.md». ببینید پیش از هر کاری load_skill را صدا می‌زند. بعد چیزِ بی‌ربطی بپرسید و ببینید مهارت را بارگذاری نمی‌کند. کلید ندارید؟ در پوشه‌ی mini دستورِ print(skill_index()) را اجرا کنید.

درست به کار بردن

  1. فقط توضیح‌ها به پرامپت سیستمی می‌روند. بدنه‌ها بعداً، به شکلِ نتیجه‌ی ابزار، می‌رسند، پس هزینه‌ی هر مهارت تا وقتی به کار نرفته یک خط است.
  2. آنچه را بارگذاری می‌کنید مرتب کنید. ترتیبِ ثابت پرامپت سیستمی را در همه‌ی اجراها یکسان نگه می‌دارد، و کش را هم.
  3. با نام بارگذاری کنید، نه با مسیر. اشتباه کردن با ابزارِ load_skill سخت‌تر از اشتباه کردن با یک مسیر است، در ردِ کار روشن دیده می‌شود، و می‌شود آن را بی‌پرسش مجاز کرد.
  4. مهارتِ خراب باید سروصدا کند. مهارتی که توضیح ندارد بی‌صدا هیچ‌وقت به کار نمی‌رود. وقتی mini راه می‌افتد فایل‌ها را بررسی کنید و بگویید کدام خراب است.

واژه‌های این درس

  • فهرستِ مهارت‌ها (skill index): فهرستِ نام و توضیحِ مهارت‌ها که هارنس در پرامپت سیستمی می‌گذارد، تا مدل بداند چه چیزهایی را می‌تواند بارگذاری کند
  • load_skill: ابزارِ mini که دستورهای کاملِ یک مهارت را با نامش، به شکلِ یک نتیجه‌ی ابزار در آخرِ گفت‌وگو، برمی‌گرداند
  • سربرگ (header): بخشی از SKILL.md که میانِ دو خطِ --- است و نام و توضیح را دارد

ایده‌ی اصلی

سامانه‌ی مهارت در کد یعنی یک فهرست و یک ابزار. فهرستِ نام‌ها و توضیح‌ها در پرامپت سیستمی می‌رود و در کش می‌ماند. دستورها فقط وقتی کاری به آن‌ها نیاز دارد از راهِ یک ابزار برمی‌گردند، در آخرِ گفت‌وگو، جایی که فقط به اندازه‌ی توکن‌های خودشان هزینه دارند.

دانش خود را بسنجید

ورود برای سنجش دانش خود از این درس و پیگیری پیشرفتتان.