تصور کنید یک برنامهنویس بخواهد ابزاری بسازد که همزمان بتواند فایل بخواند، درخواست HTTP بفرستد و کد اجرا کند، اما نگران باشد که افزودن هر قابلیت جدید، مدل را گیج کند یا سیستم را ناپایدار سازد. CodeSmith با طراحی یک «اسلات» (Slot) واحد برای مدیریت بیش از ۵۰ ابزار داخلی، این چالش را حل کرده است تا حاکمیت سیستم روی تمام ابزارها یکسان باشد. این طراحی مانع از شکستهای رایج عاملهای هوش مصنوعی میشود؛ جایی که افزودن قابلیتهای بیشتر منجر به سردرگمی مدل یا بیثباتی سیستم میگردد.
در نسخه v0.5.0 (کامیت 3a74c82f)، CodeSmith حدود ۵۰ پیادهسازی ToolSpec را در مسیر crates/tool-impls و ۶۲ مورد دیگر را در سمت میزبان TUI مدیریت میکند. علاوه بر این، ابزارهای خارجی MCP، افزونههای کتابخانه دینامیک (dynamic-library) و ابزارهای مربوط به زیر-عاملها (subagent tools) نیز در این ساختار جای میگیرند.
بسیاری از عاملهای هوش مصنوعی با گسترش مجموعه ابزارهایشان دچار مشکل میشوند؛ وقتی مدل با گزینههای زیاد روبهرو میشود، دقت پاسخها افت میکند و هزینه توکنها به شدت بالا میرود. CodeSmith برای مقابله با این وضعیت، منطق تجاری ابزار را از حاکمیت آن جدا کرده است و از یک سیستم لایهبندی شده از Traitها برای مدیریت همه چیز، از تاییدیه های امنیتی گرفته تا حافظه کش (Caching)، استفاده میکند.
ساختار حاکمیتی سهلایه
طبق مستندات فنی CodeSmith، این سیستم برای اطمینان از اینکه هر لایه فقط اطلاعات مورد نیاز خود را بداند، به سه لایه مجزا از تبار (Lineage) تقسیم شده است که هر کدام در یک Crate جداگانه قرار دارند:
- لایه چارچوب (Framework Layer) (
crates/agent/src/tools/mod.rs:46-78): این لایه یک قرارداد حداقلی و مستقل از میزبان است که میتوان آن را مشابه «BaseTool در LangChain» دانست. این لایه تنها نام، شرح و طرح ورودی (input_schema) را تعریف میکند و یک متدrunدارد که یک boxed future برمیگرداند. این لایه را تعمداً طوری طراحی کردهاند که هیچ اطلاعی از محیطهای ایزوله (Sandbox) یا تاییدیه ها نداشته باشد؛ قابلیتهای آن صرفاً «متادیتای راهنما برای میزبانها» است. این رویکرد بهینهسازی لایههای زیرساختی یادآور بهبودهای اخیر در لایهی Harness است که منجر به ارتقای قابل توجه عملکرد عاملهای کدنویس شد. - لایه تولید (Production Layer - ToolSpec) (
crates/agent-runtime/src/tools/spec.rs:809-886): این لایه مسئول مدیریت عملیات واقعی و امنیت است. جسورانهترین بخش طراحی آن، تبدیل قابلیتها به سیاستهای تاییدیه است. متدapproval_requirementجایگاه گیت تایید را بر اساس مجموعه قابلیتها تعیین میکند: قابلیتToolCapability::ExecutesCodeباعث فعال شدنApprovalRequirement::Requiredمیشود، قابلیتToolCapability::WritesFilesمنجر بهApprovalRequirement::Suggestمیگردد و سایر موارد در حالتAutoقرار میگیرند. همچنین متدapproval_requirement_for_inputبرای کنترل دقیقتر (مثلاً تفاوت بین دستورlsوrm) و متدvalidate_inputبرای اعتبارسنجی معنایی پیش از تایید، در این لایه وجود دارد. - لایه درز (Seam Layer - ToolDispatcher) (
crates/agent-runtime/src/tool_dispatch.rs:53-105): این لایه مانند یک ضربهگیر بین موتور و میزبان عمل میکند. موتور تنها یکBox<dyn ...>را نگه میدارد و ابزارها را از طریق یک اسنپشاتToolMetadataفراخوانی میکند. این جداسازی ضروری است زیراToolContextحاوی حدود سی فیلد سرویس وابسته به میزبان (مانند ترمینال، اعلانها و مدیریت وظایف) است که نمیتوانند بدون آلوده کردن هسته سیستم به جزئیات ترمینال، در Crate مربوط به Runtime قرار گیرند.
ثبت یکپارچه و نقطه بازرسی امن
بر اساس بررسی کدها، تمام ابزارها، فارغ از منشأ آنها، در نهایت به شکل Arc<dyn ToolSpec> در یک ثبت (Registry) واحد قرار میگیرند. ابزارهای داخلی مستقیماً این Trait را پیاده میکنند، در حالی که ابزارهای MCP از یک آداپتور و ابزارهای افزونه از پل ExtensionToolSpecAdapter استفاده میکنند. زیر-عاملها از همان سازندهای (Builder) استفاده میکنند که والدینشان استفاده کردهاند تا سطح ابزارهای آنها «همگام» (in lockstep) باقی بماند و امکان ایجاد بازگشتی زیر-عاملها فراهم شود.
احکام مربوط به قابلیتهای MCP از یک خط پایه محافظهکارانه شروع میشوند. تنها ابزارهای کمکی اکتشاف که نامهای list_mcp یا read_mcp دارند، به عنوان «فقط-خواندنی» شناخته میشوند. همانطور که در کامنتهای ماژول در crates/tui/src/tools/registry.rs:867-925 آمده است: «با ابزارهای MCP به صورت محافظهکارانه برخورد کنید و فرض کنید به تاییدیه و دسترسی شبکه نیاز دارند، مگر اینکه ابزارهای اکتشاف شناخته شده باشند.»
برای حفظ پایداری سیستم، ورودی ثبت ابزارها (crates/agent-runtime/src/tools/registry.rs:484-642) از یک نقطه بازرسی «بسته-در-صورت-خطا» (fail-closed) استفاده میکند:
- اعتبارسنجی: نام ابزارها باید با عبارت منظم
^[a-zA-Z0-9_-]{1,64}مطابقت داشته باشد و ریشه طرح (Schema) باید یک Object باشد. - ایزولهسازی: ابزارهای غیرمنطبق با یک
FailClosedToolجایگزین میشوند. شرح این ابزار چنین است: «غیرقابل دسترس: این ابزار در مقداردهی اولیه شکست خورد و در حالت بسته قرار گرفت. آن را فراخوانی نکنید.» - پیشگیری: این مکانیسم تضمین میکند که یک پلاگین معیوب باعث ایجاد Panic یا خطای ۴۰۰ در میانه یک نوبت (Turn) نشود، زیرا چنین اتفاقی تمام ابزارهای سیستم را از کار میانداخت. جرم یک ابزار بد نباید منجر به مجازات دستهجمعی تمام ابزارها شود.
مدیریت حجم ابزارها و بهینهسازی حافظه
برای جلوگیری از «بیماری ابزارهای زیاد» — وضعیتی که مدل در میان ۱۰۰ ابزار مختلف سردرگم میشود و دقتش در پاسخ به سوالات چندگزینهای افت میکند — CodeSmith از بارگذاری در لحظه (On-demand loading) استفاده میکند. ریختن تمام ابزارها در پنجره متنی، توکنها را میبلعد و KV Cache را تخریب میکند. این چالش مدیریت توکنها در مقیاس بالا، مشابه اهدافی است که پروژه Caveman برای کاهش هزینههای ورودی دنبال میکرد.
به نقل از الگوهای صنعتی، این سیستم از روشهای زیر بهره میبرد:
- بارگذاری تعویقی (Deferred Loading): با استفاده از
defer_loadingابزارها ثبت میشوند اما به مدل ارسال نمیشوند. فعالسازی در دو مرحله انجام میشود: ابزارهای مقیم (Resident) در ابتدا ارائه میشوند و ابزارهایی که در میانه مسیر فعال میشوند، به انتهای کاتالوگ اضافه میگردند تا پیشوند کششده (Cached byte prefix) دستنخورده باقی بماند. - فیلتر معنایی: این سیستم از الگوهای صنعتی بهره میبرد. برای مثال، پیشفیلترینگ مبتنی بر بازیابی (Retrieval-based) در Anthropic نشان داده است که دقت استفاده از ابزار را در محیطهایی با بیش از ۱۰۰ ابزار، از ۴۹٪ به ۷۴٪ میرساند. به همین ترتیب، همگامسازی پوشه MCP در Cursor، مصرف توکن در وظایف مرتبط را ۴۶.۹٪ کاهش داده است.
- بومیسازی API: با پیروی از منطق
tool_searchدر OpenAI و بازیابی BM25 در Codex CLI، سیستم یک پیشوند استاتیک از نام ابزارها و شرحهای تکخطی را نگه میدارد. طرح کامل (Full Schema) تنها پس از درخواست مدل اضافه میشود تا تغییرناپذیری پیشوند (Prefix immutability) حفظ شود. - اکتشاف فعال: در حالی که CodeSmith بین لایه اول و دوم قرار دارد، لایه سوم (مانند MCP-Zero) را میشناسد که هیچ طرحی را فراهم نمیکند و مسیریابی را بر اساس معنا در دو گام (ابتدا سرور و سپس ابزار) انجام میدهد و صرفهجویی در توکن را تا حدود ۹۸٪ گزارش کرده است.
خط لوله اجرا و شروع گمانهزنانه
وقتی مدل یک بلوک tool_use صادر میکند، CodeSmith آن را از یک مسیر سختگیرانه در engine/turn/stream.rs و engine/turn/batches.rs عبور میدهد:
۱. استریمینگ: بلوک ToolUse شروع میشود، شناسه و نام ثبت میگردد و در همین حال InputJsonDelta بافر ورودی را جمعآوری میکند. JSON آرگومانها هرگز به کاربر نشان داده نمیشود.
۲. برنامهریزی: سیستم یک گیت قطعکننده (Truncation gate) را اعمال میکند (اگر نوبت به دلیل محدودیت خروجی قطع شده باشد، فراخوانی رد میشود) و یک loop_guard را فعال میکند (اگر یک نام و آرگومان یکسان سه بار تکرار شود، مسدود میگردد).
۳. دستهبندی (Batching): ابزارهای فقط-خواندنی متوالی که برای اجرای موازی ایمن هستند، در یک دسته همزمان (Concurrent batch) قرار میگیرند؛ سایرین به صورت متوالی (Serial) اجرا میشوند. این نوع مدیریت جریان عملیات، شباهتهای ساختاری با سازوکار ارکستراسیون در Codemode دارد که برای مدیریت گردشکارهای پیچیده طراحی شده است.
۴. اجرا: دستههای همزمان از FuturesUnordered استفاده میکنند (به ترتیب شروع شده و به ترتیب معکوس LIFO پایان مییابند). ابزارهای متوالی با یک گیت تاییدیه مواجه میشوند. یک RwLock دسترسی را مدیریت میکند: ابزارهای ایمن-موازی قفل خواندن (Read lock) و ابزارهای متوالی قفل نوشتن (Write lock) میگیرند. برای جلوگیری از بنبست (Deadlock)، در حالتهایی که ابزاری ابزار دیگری را فراخوانی میکند، قفل نادیده گرفته میشود (crates/tools/src/lib.rs:321-388).
۵. پسپردازش: نتایج جمعآوری شده، تشخیصهای LSP گردآوری میگردند و نتایج حاصل از شبکه در delimitرهای <external_content> قرار میگیرند (batches.rs:642).
یک بهینهسازی جالب، «شروع زودهنگام گمانهزنانه» (Speculative early start) است. ابزارهای فقط-خواندنی میتوانند در حالی که مدل هنوز در حال تولید آرگومانهاست، اجرا شوند. اما ابزارهای شبکه از این «کازینو» حذف شدهاند (stream.rs:154-160) زیرا سطح تاییدیه آنها بسته به میزبان متفاوت است. از آنجایی که اعزام گمانهزنانه پیش از تصمیم تاییدیه رخ میدهد، باید در حالت «بسته» (fail-closed) باشد تا سیاستهای امنیتی دور زده نشوند. حساب و کتاب ساده است: میزبانهایی که سیاستشان اجازه میدهد چیزی را از دست نمیدهند؛ فراخوانی صرفاً از «زودهنگام گمانهزنانه» به «شروع عادی در زمان اجرا» تغییر میکند.
اثر انگشت تاییدیه و همزمانی
تاییدیه از طریق دو اثر انگشت (Fingerprint) مجزا در crates/agent-runtime/src/tools/approval_cache.rs:141,170 مدیریت میشود. رد کردنها (Rejections) از یک اثر انگشت دقیق استفاده میکنند؛ رد کردن rm -rf /tmp/x نباید باعث مسدود شدن یک فراخوانی متفاوت شود. اما تاییدها (Approvals) از یک اثر انگشت منعطفتر و آگاه از تعداد آرگومانها (Arity-aware) استفاده میکنند: وقتی cargo build تایید شد، cargo build --release دیگر کاربر را متوقف نمیکند. این سیستمی میسازد که در برابر دور زدن سختگیر و در برابر مزاحمت برای کاربر، منعطف است.
همزمانی توسط یک اصل ایمن مدیریت میشود: هر ابزار به طور پیشفرض اجازه اجرای همزمان ندارد. صلاحیت ابزار توسط tool_plan_is_parallel_safe (engine/dispatch.rs:335) تعیین میشود که نیازمند read_only && supports_parallel بودن، عدم نیاز به تاییدیه و غیر-تعاملی بودن است.
مرزهای شکست ساختاری هستند. دستههای همزمان نتایج را از طریق اسلاتهای پیشتعیین شده بازیابی میکنند. همانطور که در کامنت batches.rs آمده است، این یک «تخلیه حفظکننده ایندکس» است و ترتیب تکمیل اهمیتی ندارد. شکست یک Future تنها اسلات خودش را آلوده میکند و نه همدستههایش و نه کل نوبت را مجازات میکند.
تحمل خطا و اصلاح «دستهای لرزان»
CodeSmith خطاهای مدل را یک امر دائمی میپندارد و از یک «نردبان تعمیر» (crates/agent-runtime/src/tools/arg_repair.rs) برای اصلاح آرگومانهای JSON خراب که بر اثر مرزهای تکههای SSE یا کاراکترهای کنترلی ایجاد شدهاند، استفاده میکند. این نردبان پنج پله دارد:
۱. پارس سختگیرانه (Strict parse)
۲. حذف کاراکترهای کنترلی از رشتهها
۳. حذف کاماهای انتهایی پیش از }]
۴. بستن براکتهای باز
۵. حذف بستههای اضافی
اگر ورودی بیش از ۱ مگابایت باشد، تعمیر رد میشود. اگر تمام پلهها شکست بخورند، یک شیء خالی {} برگردانده میشود تا اعزام ادامه یابد. اما اگر نوبت به دلیل محدودیت خروجی قطع شده باشد، نردبان بسته شده (ArgSalvagePolicy::StrictOnly, engine/dispatch.rs:167) و فراخوانی رد شده و درخواست ارسال مجدد میگردد.
اگر نام ابزاری غلط نوشته شود، یک نردبان تفکیک (tools/registry.rs:394-448) تلاش میکند آن را از طریق روشهای زیر مطابقت دهد:
- حروف کوچک دقیق
- تبدیل خط تیره به زیرخط (Hyphen to underscore)
- تبدیل CamelCase به snake_case
- حذف پسوند
_tool - تطبیق پیشوند فازی (Fuzzy prefix match)
اگر همه شکست بخورند، سیستم پیشنهاد «آیا منظورتان این بود؟» را همراه با نگاشت صحیح نام فیلد و retry_required: true برمیگرداند (engine/tool_catalog.rs:469-502). علاوه بر این، tools/schema_sanitize.rs:1-33 تغییراتی را برای حفظ معنا انجام میدهد (مانند تبدیل anyOf:[X,{type:"null"}] به یک X قابل نال) تا از رد شدن طرحهای کثیف MCP توسط API در حالت سختگیرانه جلوگیری شود.
امنیت و «قانون آهنین» جعل
در حالی که سیستم با «دستهای لرزان» منعطف است، با «جعل» (Forgeries) خصومت دارد. موتور به شدت بین یک خطای پروتکل و یک فراخوانی ابزار جعلی که در قالب Markdown، XML یا pseudo-calls مدل Replit پیچیده شده است، تفاوت قائل میشود.
همانطور که در crates/agent-runtime/tests/protocol_recovery.rs ذکر شده، tool_parser قدیمی بازنشسته شده است. سیستم تضمین میکند که Wrapperهای جدیدتر <function_calls> به عنوان فراخوانیهای واقعی ابزار تلقی نشوند. هر فراخوانی که خارج از پروتکل رسمی باشد، حذف شده و به عنوان یک اعلان وضعیت (Status notice) صادر میشود. تعمیر یک جعل، کانالی برای مهاجمان باز میکند؛ بنابراین، آرگومانهای کج و معوج تعمیر میشوند (آسیب داخل پروتکل)، اما فرمتهای غلط حذف میشوند (جعل خارج از پروتکل).
اقتصاد خروجی و مدیریت حافظه
برای مدیریت پنجرههای متنی، خروجی شل به ۳۰,۰۰۰ بایت محدود شده است (crates/agent-runtime/src/tools/shell_output.rs). CodeSmith از رویکرد بودجه تفکیکی استفاده میکند: ۲۲ کیلوبایت در ابتدا و ۸ کیلوبایت در انتها حفظ میشود تا پیامهای خطا و خلاصههای تست باقی بمانند.
بخش میانی از طریق spill_full_shell_output در یک پاکت روی دیسک در مسیر ~/.codesmith/tool_outputs/ ذخیره میشود. به مدل یک اشارهگر بازیابی داده میشود: «خروجی کامل ذخیره شد؛ برای مشاهده آن retrieve_tool_result ref=<id> را فراخوانی کنید.»
این مکانیسم تخلیه (Spilling) شامل چهار گاردریل است:
- نظارت (Polls): ابزارهایی که در حالت
Runningهستند تخلیه نمیشوند تا از چرخش شدید دیسک ($O(polls \times bytes)$) جلوگیری شود. - وظایف منقضی: پس از اخراج یک وظیفه، تخلیهای رخ نمیدهد تا دنباله ذخیره شده تخریب نشود.
- تداخل (Collision): شناسه تخلیه شامل PID است تا از تداخل بین فرآیندهایی که در یک میلیثانیه شروع شدهاند جلوگیری شود.
- قابلیت مشاهده: شکست در نوشتن باعث ایجاد یک
warn!میشود و نه شکست خاموش، زیرا اشارهگر بازیابی یک «قول» به مدل است.
نتایج کلی ابزارها دارای آستانه تخلیه ۱۰۰ کیلوبایتی هستند (truncate.rs:56). نتایج بیش از حد بزرگ به توکنهای var_handle تبدیل یا خلاصه میشوند تا از دفتر کل متنی (Context ledger) محافظت شود. نتایج سریالشده کاتالوگ ابزارها در یک OnceLock کش شده و بر اساس نام مرتب میشوند تا کش پیشوند به حداکثر برسد.
نتیجهگیری: همه چیز متصل به اسلات
این معماری ثابت میکند که انتزاع (Abstraction) به معنای نوشتن کد کمتر نیست، بلکه به معنای یکبار نوشتن حاکمیت است. با جداسازی «اسلات» از «سلاح»، CodeSmith تضمین میکند که تاییدیه ها، قفلهای همزمانی، مرتبسازی کش و تحمل خطا توسط رابط (Interface) مدیریت شوند و پیادهسازان ابزار فقط منطق تجاری را بنویسند. چه read_file باشد که مانع از استفاده بیرویه از cat میشود و چه fetch_url که دسترسی به 169.254.169.254 را مسدود میکند، هنر ساخت هر سلاح از حاکمیت اسلات جدا میماند.




گفتگو