Knaph

ابزار دادن به مدل9 از 376 دقیقه مطالعه

تعریفِ یک ابزار

mini تا اینجا می‌تواند گفت‌وگو کند، ولی کاری از دستش برنمی‌آید. لیلا از آن می‌پرسد «در notes.txt چه نوشته است؟» و mini با صداقت جواب می‌دهد که فایل‌های او را نمی‌بیند. از استفاده از ابزار می‌دانید چه چیزی کم است: درخواست باید به مدل بگوید چه کارهایی را می‌تواند بخواهد. این درس همین بخشِ درخواست را با پایتون می‌نویسد.


لیلا پس یک تابع به مدل می‌دهم و مدل صدایش می‌زند؟

امید مدل هیچ وقت چیزی را صدا نمی‌زند. برای اینکه بتواند کاری را بخواهد، واقعاً چه چیزی از ما لازم دارد؟

لیلا اسمش را، تا بتواند بگوید کدام را می‌خواهد. و اینکه چه می‌کند، تا بداند کی سراغش برود.

امید خوب است. وقتی می‌خواهد فایلی را بخواند، چه چیزی را باید به ما بگوید؟

لیلا کدام فایل. پس باید بداند چه ورودی‌هایی هست.

امید و از کجا مطمئن شویم درخواستش شکلی دارد که کدِ ما بتواند با آن کار کند؟ اگر بنویسد «لطفاً فایلِ یادداشت‌ها را بخوان»، با آن چه کنیم؟

لیلا هیچ کار، راستش. چیزی مثلِ یک فرم لازم داریم، با خانه‌هایی که اسم دارند. خانه‌ای به اسمِ path که باید متن باشد.

امید دقیقاً همین را می‌فرستیم: یک نام، یک شرح به زبانِ ساده، و یک فرم برای ورودی‌ها. این فرم با استانداردی به اسمِ JSON Schema نوشته می‌شود.


ابزار، به شکلِ یک دیکشنری

تعریفِ ابزار (tool definition) یک دیکشنری با سه کلید است. این اولین ابزارِ mini است:

read_file_tool = {
    "name": "read_file",
    "description": "Read a text file in the project. Use before changing a file.",
    "input_schema": {
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "path relative to the project folder"},
        },
        "required": ["path"],
    },
}
Python

به این‌ها دقت کنید:

  • name اسمی است که مدل با آن به ابزار اشاره می‌کند. کوتاه، با حروفِ کوچک، و یک فعل همراهِ یک اسم: read_file، run_command.
  • description برای خواندنِ مدل است. می‌گوید ابزار چه می‌کند و کی باید از آن استفاده کرد.
  • input_schema همان فرم است. "type": "object" یعنی «ورودی‌ها مجموعه‌ای از خانه‌های نام‌دار است»؛ properties هر خانه را با نوع و شرحِ خودش فهرست می‌کند؛ و required خانه‌هایی را می‌گوید که همیشه باید پر شوند.

تعریفِ یک ابزار به شکلِ کارتی با سه بخش: نام، read_file؛ شرح، که مدل آن را می‌خواند تا تصمیم بگیرد کی از ابزار استفاده کند؛ و اسکیمای ورودی، فرمی با یک فیلدِ متنیِ الزامی به نامِ path، که برنامه می‌تواند آن را بررسی کند.

اسکیمای ورودی (input schema) با JSON Schema نوشته می‌شود، استانداردی رایج برای توصیفِ شکلِ داده‌ها. فقط چند کلمه از آن لازمتان می‌شود: "string" برای متن، "integer" و "number" برای عدد، "boolean" برای درست یا نادرست، "array" برای فهرست، و "enum" برای خانه‌ای که باید یکی از چند مقدارِ ثابت باشد، مثلِ "enum": ["celsius", "fahrenheit"].

فرستادنِ آن

ابزارها به شکلِ یک فهرست، کنارِ پیام‌ها، در درخواست می‌روند:

response = client.messages.create(
    model=MODEL,
    max_tokens=16000,
    system=SYSTEM,
    tools=[read_file_tool],
    messages=[{"role": "user", "content": "What's in notes.txt?"}],
)
print(response.stop_reason)
for block in response.content:
    print(block.type, getattr(block, "name", ""), getattr(block, "input", ""))
Python

اگر مدل به این نتیجه برسد که ابزار کمک می‌کند، جواب نمی‌دهد. یک بلوکِ tool_use می‌نویسد و می‌ایستد، و stop_reason (دلیلِ ایستادن) می‌گوید چرا ایستاد: "tool_use". چیزی که چاپ می‌شود کم‌وبیش این است:

tool_use
thinking
tool_use read_file {'path': 'notes.txt'}

بلوکِ اول فکر کردنِ مدل است (Opus 5.5 همیشه فکر می‌کند، و متنِ فکرش به‌طور پیش‌فرض نمی‌آید). دومی خودِ درخواست است و سه بخش دارد: block.name ابزار است؛ block.input یک دیکشنریِ پایتون است که از قبل به شکلِ اسکیمای شما درآمده؛ و block.id، رشته‌ای مثلِ "toolu_01A..."، این درخواست را نشانه‌گذاری می‌کند تا پاسخ را بشود به آن ربط داد. این موضوعِ درسِ بعد است.

درخواست با پیام‌ها و فهرستِ ابزارها به مدل می‌رود. پاسخ با دلیلِ ایستادنِ tool_use برمی‌گردد و یک بلوکِ tool_use دارد با یک شناسه، نامِ read_file و ورودیِ path برابر با notes.txt.

اینکه از ابزار استفاده کند یا نه، انتخابِ خودِ مدل است و شما نمی‌توانید مجبورش کنید. (مدل‌های قدیمی‌تر تنظیمی را می‌پذیرفتند که فراخوانیِ ابزار را اجباری می‌کرد؛ تازه‌ترین مدل‌های Claude آن را رد می‌کنند.) اگر کاری حتماً باید از ابزارِ خاصی استفاده کند، در پیام یا پرامپت سیستمی بگویید: «پیش از جواب دادن، با read_file فایل را نگاه کن.»


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

امید و پشتِ هر کدام هم یک تابع هست. این دو را کجا نگه می‌داری که از هم جدا نیفتند؟

لیلا کنارِ هم. یک جدول: نام، تابع، شرح و ورودی‌ها.


جدولِ ابزارهای mini

mini همه‌ی ابزارهایش را در یک دیکشنری به اسمِ TOOLS نگه می‌دارد و تعریف‌ها را از روی آن می‌سازد. هر ابزار یعنی تابعش، شرحش و ورودی‌هایش:

TOOLS = {  # name -> (function, description, inputs)
    "read_file": (read_file, "Read a text file in the project. Use before changing a file.",
                  {"path": "path relative to the project folder"}),
}
 
def tool_schemas(names):  # the descriptions the model reads
    return [{
        "name": name,
        "description": TOOLS[name][1],
        "input_schema": {
            "type": "object",
            "properties": {k: {"type": "string", "description": d} for k, d in TOOLS[name][2].items()},
            "required": list(TOOLS[name][2]),
        },
    } for name in names]
Python

tool_schemas(["read_file"]) دقیقاً همان دیکشنریِ بالا را می‌سازد. همه‌ی ورودی‌های ابزارهای mini متن‌اند و الزامی، و همین جدول را کوتاه نگه می‌دارد؛ یک عاملِ بزرگ‌تر به هر ورودی نوعِ خودش را می‌دهد. حالا اضافه کردنِ یک ابزار فقط یک خط در TOOLS است. بخشِ بعد سه ابزارِ دیگر اضافه می‌کند.

شرح‌هایی که مدل خوب به کار می‌برد

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

  • ابزار چه می‌کند، در یک جمله: «یک فایلِ متنی در پروژه را می‌خواند.»
  • کی از آن استفاده کنیم و کی نه: «پیش از تغییر دادنِ فایل از آن استفاده کن.» «برای پوشه نیست: از list_files استفاده کن.»
  • ورودی‌ها چه شکلی دارند، با یک نمونه اگر کمک می‌کند: «مسیر نسبت به پوشه‌ی پروژه، مثلِ reports/march.md».
  • چه برمی‌گردد، اگر روشن نیست: «کدِ خروج و خروجی را برمی‌گرداند.»

شرحِ همه‌ی ابزارها در هر درخواست فرستاده می‌شود، پس یکی دو جمله برای هر کدام کافی است، و ده ابزارِ دقیق بهتر از چهل ابزارِ هم‌پوشان کار می‌کند.

امتحان کنید

اگر کلیدتان را تنظیم کرده‌اید، درخواستِ بالا را در پوشه‌ای که یک notes.txt دارد اجرا کنید و بلوک‌ها را چاپ کنید. هزینه‌اش خیلی کمتر از یک سنت است. بعد شرح را فقط "Files." کنید و چند بار بپرسید «در notes.txt چه نوشته است؟»: آیا مدل باز هم از ابزار استفاده می‌کند؟ بدونِ کلید: تعریفِ ابزاری را که برای کارِ خودتان می‌خواهید به شکلِ یک دیکشنری بنویسید. آیا شرحش می‌گوید کی باید از آن استفاده کرد؟

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

  1. اسمِ ابزار را یک فعل و یک اسم بگذارید. read_file، send_email، list_orders: مدل بینِ اسم‌ها انتخاب می‌کند، پس اسم باید بگوید ابزار چه می‌کند.
  2. شرح را برای لحظه‌ی انتخاب بنویسید. «وقتی ... از آن استفاده کن» و «برای ... نیست» بیشتر از یک فهرستِ بلندِ قابلیت‌ها کار می‌کنند.
  3. تعریف را کنارِ تابع نگه دارید. یک جدول برای هر دو یعنی اگر اسمِ ورودی‌ای عوض شود، شرح جا نمی‌ماند.
  4. وقتی مهم است، ابزار را در پرامپت بخواهید. نمی‌توانید فراخوانیِ ابزار را اجباری کنید، ولی می‌توانید بگویید کار به کدام ابزار نیاز دارد.

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

  • تعریفِ ابزار (tool definition) - دیکشنری‌ای که ابزاری را به مدل پیشنهاد می‌کند: نام، شرح و اسکیمای ورودی‌اش
  • اسکیمای ورودی (input schema) - بخشی از تعریفِ ابزار که ورودی‌های ابزار را توصیف می‌کند: اسمشان، نوعشان و اینکه کدام‌ها الزامی‌اند
  • JSON Schema - روشی استاندارد برای توصیفِ شکلِ داده‌ها، که برای ورودی‌های ابزار به کار می‌رود
  • stop_reason (دلیلِ ایستادن) - خانه‌ای از پاسخ که می‌گوید مدل چرا از نوشتن ایستاد؛ "tool_use" یعنی منتظر است ابزاری اجرا شود

ایده‌ی اصلی

ابزار از نگاهِ مدل یک نام است، یک شرح و یک فرم برای ورودی‌ها، که با هر درخواست فرستاده می‌شود. وقتی مدل آن را بخواهد، با یک بلوکِ tool_use می‌ایستد: نام، فرمِ پرشده و یک شناسه. بقیه‌اش با کدِ شماست.

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

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