ایمن نگه داشتنِ عامل20 از 37
در درسِ «[هوکها در عاملِ خودتان](hooks-in-your-agent)» به mini دو فهرست هوک دادید: هوکهای پیش از اجرا که میتوانند جلوی یک فراخوانیِ ابزار را بگیرند، و هوکهای پس از اجرا که میتوانند خروجیاش را تغییر دهند. اینجا تابعی را مینویسید که آنها را اجرا میکند، یعنی run_tool، همانطور که mini مینویسد. در این تمرین هیچ مدلی صدا زده نمیشود: فراخوانیهای ابزار بهصورتِ ورودی میآیند، انگار مدل نوشته باشدشان.
## آنچه از پیش آماده است
کدِ آغاز یک پوشهی خیالی از فایلها و دو ابزار به شما میدهد:
- read_file(path) متنِ یک فایل را برمیگرداند، یا برای فایلی که وجود ندارد FileNotFoundError میدهد؛
- run_command(command) فقط ran: <command> را برمیگرداند.
شش هوک هم در آن هست. هوکهای پیش از اجرا: no_secrets (جلوی هر مسیری را که .env در آن باشد میگیرد)، no_rm (جلوی دستورهایی را که rm دارند میگیرد) و only_txt (جلوی خواندنِ فایلی را که به .txt ختم نمیشود میگیرد). هوکهای پس از اجرا: cut_long (۴۰ نویسهی اول را نگه میدارد و [cut] را اضافه میکند)، hide_keys (کلیدهای sk-... را به sk-*** تبدیل میکند) و tag ([tool name] را جلوی خروجی میگذارد).
## ورودی
دو خط میگویند کدام هوکها روشناند، به ترتیب. بعد یک خط با ---، و بعد در هر خط یک فراخوانیِ ابزار: شناسهاش، نامِ ابزار، و ورودیهایش بهصورتِ JSON:
``text
before: no_secrets
after: cut_long
---
t1 read_file {"path": "notes.txt"}
t2 read_file {"path": ".env"}
t3 run_command {"command": "ls"}
`
## آنچه باید بنویسید
run_tool(block, before, after) را کامل کنید. این تابع یک دیکشنریِ tool_result برمیگرداند و main آن را بهصورتِ JSON چاپ میکند. قاعدهها، به ترتیب:
1. **ابزارِ ناشناخته** با دلیلِ no tool called <name> رد میشود.
2. **هوکهای پیش از اجرا به ترتیب اجرا میشوند.** به محضِ اینکه یکی دلیلی برگرداند، بایستید: جلوی فراخوانی گرفته شده و هوکهای بعدی اجرا نمیشوند.
3. **فراخوانیِ ردشده** "content": "Blocked: <reason>" را همراهِ "is_error": True برمیگرداند و هیچ اتفاقِ دیگری نمیافتد.
4. **وگرنه ابزار اجرا میشود**، با ورودیها بهصورتِ آرگومانهای نامدار: TOOLS[name](args)`. اگر خطا داد، `"content": "Error: <the error's message>"` را همراهِ `"is_error": True` برگردانید. هوکهای پس از اجرا اجرا نمیشوند.
5. بعد هوکهای پس از اجرا به ترتیب اجرا میشوند، هر کدام روی خروجیِ قبلی.
6. نتیجهای که کار کرده** اصلاً کلیدِ is_error ندارد.
برای نمونهی بالا، خروجی این است:
``text
{"content": "Buy milk.\nCall Sara about the March repo [cut]", "tool_use_id": "t1", "type": "tool_result"}
{"content": "Blocked: never read .env files", "is_error": true, "tool_use_id": "t2", "type": "tool_result"}
{"content": "ran: ls", "tool_use_id": "t3", "type": "tool_result"}
`
<details>
<summary>یک راهنمایی، اگر گیر کردید</summary>
run_tool خودِ mini در یک حلقه reason = reason or hook(name, args) را به کار میبرد. این هر هوک را فقط تا وقتی صدا میزند که هنوز چیزی جلوی فراخوانی را نگرفته باشد. با reason = None if name in TOOLS else f"no tool called {name}"` شروع کنید.
</details>
وقتی کار کرد، به چیزی که ساختهاید نگاه کنید: قاعدههای mini دیگر در ابزارهایش پخش نیستند. هر فراخوانی، از هر ابزاری، هر بار از همین چند خط و به همین ترتیب میگذرد.
ورود برای امتحان این تمرین.