پرش به محتوای اصلی
پرش به محتوای مقاله

پروتکل سه-فایلی؛ راهکار کاهش خطای حافظه در عامل‌های هوش مصنوعی

·۳ مهر ۱۴۰۵۱۰ دقیقه مطالعه۱ بازدید
راهنما
عکس: ربات کدنویس در حال کار با چراغ‌های چشمک‌زن و کدهای روی صفحه، نمادی از عامل خودمختار برنامه‌نویسی
عکس: ربات کدنویس در حال کار با چراغ‌های چشمک‌زن و کدهای روی صفحه، نمادی از عامل خودمختار برنامه‌نویسی
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

جایگزینی سیستم‌های بازیابی پیچیده با یک پروتکل سه-فایلی ساده در Markdown که از طریق Git Hooks اجباری شده است تا نرخ صحت بازگشت به تسک‌ها را به ۹۵٪ برساند.

۴۰ دلار در هر شب؛ این بهای سنگینی است که یک توسعه‌دهنده برای «فراموشی» عامل‌های هوش مصنوعی خود پرداخت کرد. تصور کنید سیستمی ساخته‌اید که شب‌ها در حالی که شما خوابید، کدهای شما را می‌نویسد، اما هر چند ساعت یک‌بار تمام تصمیماتش را فراموش می‌کند و دوباره از نقطه صفر شروع می‌کند. این هزینه در واقع مبلغ توکن‌های تلف شده‌ای بود که پس از کشف یک مشکل بحرانی در سیستم اجرای خودکار توسعه‌دهنده مشخص شد.

این سیستم بر پایه Claude Code (نسخه ۲.x در سپتامبر ۲۰۲۶) اجرا می‌شد و روی یک دستگاه Mac mini در کمد توسعه‌دهنده قرار داشت. وظیفه این سامانه مدیریت یک لیست انتظار از تسک‌ها (Backlog) در چندین مخزن کد مختلف، بدون هیچ دخالت انسانی بود. معماری آن شامل یک ماژول ارکستراتور برای توزیع کار بین عامل‌های اجرای موازی، یک عامل خود-ترمیم‌کننده (Self-healing) برای پاک‌سازی نهایی و یک داشبورد کنترل از راه دور برای نظارت از طریق موبایل بود.

مشکل اصلی «حلقه‌های فراموشی» بود. هر جلسهٔ کاری یک پنجرهٔ زمینه (Context Window) — مثل میز کاری که فقط جای چند ورق کاغذ دارد و اگر پر شود باید قدیمی‌ها را دور ریخت — محدود دارد. در تسک‌های طولانی، این پنجره با خروجی ابزارها، تغییرات کد (Diffs) و لاگ‌های تست پر می‌شود. وقتی پنجره پر شود، سیستم (Harness) گفتگو را به یک خلاصه تبدیل می‌کند تا فضای جدید ایجاد کند. طبق گزارش این توسعه‌دهنده، این خلاصه‌ها «تلفاتی» دارند؛ یعنی می‌گویند چه اتفاقی افتاده، اما «چرا» و «گام بعدی چیست» را حذف می‌کنند.

همان‌طور که در تحلیل‌های قبلی ما درباره‌ی محدودیت‌های حافظه در مدل‌های زبانی اشاره کردیم، حتی با پیشرفت استدلال، سقف فیزیکی پنجره متنی همچنان یک مانع سخت است. این موضوع تایید می‌کند که صرفاً افزایش اندازه پنجره‌های متنی راهکار نهایی برای توقف خطاهای عامل‌های کدنویسی نیست. وقتی یک جلسه چندین روز طول بکشد، خلاصه‌سازی‌های خودکار باعث حذف جزئیات حیاتی می‌شود. این یعنی مدل می‌داند که یک فایل تغییر کرده، اما دلیل استراتژیک آن تغییر را فراموش کرده است.

حالت‌های شکست حافظه

بر اساس بررسی‌های این توسعه‌دهنده در ماه اول اجرا، سه نوع شکست اصلی رخ می‌داد که هر کدام هزینه‌های عملیاتی و زمانی زیادی داشت:

  • حلقه‌های فراموشی (Amnesia Loops): عامل در بازه زمانی ۱۱ ساعت، سه بار یک تست شکست‌خورده مربوط به مهاجرت دیتابیس (Database Migration) را بررسی کرد. هر بار پس از تحلیل به این نتیجه رسید که مشکل از اختلاف منطقه زمانی (Timezone mismatch) در یک Fixture است، اما بعد از بازنشانی (Reset) پنجره متنی، این نتیجه را فراموش کرد و دوباره از ابتدا شروع کرد. این حلقه خاص در یک شب حدود ۴۰ دلار توکن سوزاند بدون اینکه حتی یک خط کد مفید ارسال کند.
  • تغییر تصمیم (Decision Reversal): عامل بین دو کتابخانه HTTP Client ارزیابی انجام داد و یکی را به دلیل خاصی انتخاب کرد. بعد از بازنشانی، چون در خلاصه فقط نوشته شده بود «یک کتابخانه انتخاب شد»، دوباره ارزیابی را با وزن‌دهی‌های کمی متفاوت انجام داد و این‌بار کتابخانه دیگر را گزید. نتیجه این شد که در یک هفته، دو Pull Request با دو کتابخانه متفاوت برای یک هدف واحد ایجاد شد.
  • تسک‌های شبح (Ghost Tasks): کارهایی که تمام شده و ادغام (Merge) شده بودند، در خلاصه همچنان با وضعیت «در حال اجرا» می‌ماندند. عامل دوباره آن‌ها را باز می‌کرد، ویژگی را روی یک شاخه جدید بازنویسی می‌کرد و در نهایت با کارهای قبلی خودش تداخل (Conflict) ایجاد می‌کرد.

توسعه‌دهنده ابتدا سعی کرد به تاریخچه گیت (Git History) تکیه کند، اما دریافت که گیت فقط می‌گوید «چه چیزی تغییر کرد»، اما نمی‌گوید «چه چیزی امتحان شد و رد شد»، «چه چیزی نیمه‌تمام مانده» یا «عامل در گام بعدی باید چه چیزی را اولویت قرار دهد». سیستم به سندی نیاز داشت که عامل آن را مخصوص «خودِ آینده‌اش» بنویسد.

برای حل این مشکل، یک پروتکل انتقال وضعیت سه-فایلی با فرمت Markdown پیاده شد که مستقیماً در مخزن کد ذخیره می‌شد. این سیستم سرویس‌های پیچیده حافظه یا دیتابیس‌های برداری (Vector DBs) را با مجموعه‌ای از قوانین سخت‌گیرانه جایگزین کرد که عامل باید در هر بار بوت شدن از آن‌ها پیروی کند. این رویکرد در واقع نوعی پیاده‌سازی از اجرای پایدار است که در مقایسه با پرامپت‌های پیشرفته، پایداری بیشتری به عامل‌های AI می‌بخشد. به نقل از گزارشی در dev.to منتشر شده در ۲۵ سپتامبر ۲۰۲۶، این رویکرد مکانیکی صحت بازگشت به تسک‌ها را در ۶ ماه از ۴۰٪ به ۹۵٪ رساند.

معماری سه-فایلی

این سیستم از سه فایل متمایز در ریشه پروژه و یک دایرکتوری وضعیت (state directory) استفاده می‌کند. هر فایل حالت نوشتاری خاصی دارد تا از فساد داده‌ها جلوگیری شود:

  • CLAUDE.md: فایل مشخصات پروژه شامل اهداف، قوانین کلی و ترتیب اجباری خواندن فایل‌ها. این فایل به‌ندرت تغییر می‌کند و به عنوان مجموعه دستورالعملات اصلی عمل می‌کند.
  • state/current.md: یادداشت وضعیت که موقعیت فعلی، اقدام بعدی و ۳ تا ۵ تصمیم اخیر را ثبت می‌کند. این فایل به‌طور کامل بازنویسی (Overwrite) می‌شود و برای جلوگیری از تبدیل شدن به زباله‌دان، محدود به حدود ۱۵۰ خط است.
  • state/backlog.md: فایل تسک‌ها که شامل یک چک‌لیست با وضعیت‌های (در انتظار، در حال انجام، انجام شده، مسدود) است. این فایل در جای خود ویرایش می‌شود و برای هر تسک یک خط اختصاص دارد.
  • state/decisions.md: یک لاگ شماره‌دار و فقط-افزودنی (Append-only) از تصمیمات. هر ورودی شامل زمینه (Context)، گزینه‌های بررسی شده، انتخاب نهایی و دلیل (Rationale) است. این فایل هیچ محدودیتی در حجم ندارد.

قالب یادداشت وضعیت

برای حفظ ثبات، فایل state/current.md از یک قالب سخت‌گیرانه پیروی می‌کند. یک ورودی نمونه شامل موارد زیر است:

  • موقعیت ما (Where we are): یک وضعیت دارای برچسب زمانی (مثلاً 2026-09-24 03:12 JST)، تسک فعلی (مثلاً تسک ۴۷ — Rate Limiter برای API عمومی)، شاخه فعال (مثلاً feat/rate-limiter) و وضعیت فعلی (مثلاً پیاده‌سازی تمام شده، ۲ تست از ۹ تست یکپارچه‌سازی شکست خورده است).
  • اقدام بعدی (Next action): یک دستورالعمل عینی (مثلاً: «دو تست شکست‌خورده در مورد burst-window را اصلاح کن. دلیل شکست این است که ساعت مجازی بین درخواست‌ها جلو نمی‌رود. به منطق لیمیتور دست نزن»).
  • تصمیمات اخیر (Recent decisions): لیستی از شناسه‌های تصمیم (مثلاً D-031: استفاده از token bucket به جای sliding window برای کاهش مصرف حافظه در ۱۰ هزار کلاینت؛ D-030: قرار دادن محدودیت‌ها در config به جای env vars برای امکان تغییرات هر مستاجر) همراه با دلایل کوتاه.
  • ممنوعیت تکرار (Do not redo): بخشی بسیار ارزشمند که مسیرهای رد شده را صراحتاً لیست می‌کند (مثلاً «مدل leaky bucket ارزیابی و رد شد. به D-031 مراجعه کن» یا «مهاجرت 0042 در محیط staging اعمال شده است. آن را دوباره تولید نکن») تا از حلقه‌های فراموشی جلوگیری شود.

ترتیب خواندن و اولویت‌ها

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

۱. خواندن CLAUDE.md (همین فایل)
۲. خواندن state/current.md
۳. خواندن state/backlog.md
۴. مرور ۱۰ مورد آخر state/decisions.md

عامل اجازه ندارد پیش از تکمیل این چهار مرحله، کار را شروع کند. برای حل تضادها، یک سلسله‌مراتب اولویت سخت‌گیرانه اعمال شده است: state/current.md > state/backlog.md > CLAUDE.md > state/decisions.md.

یادداشت وضعیت (Memo) همیشه آخرین حقیقت است. اگر لاگ تصمیمات چیزی بگوید و یادداشت وضعیت چیز دیگری، یادداشت وضعیت برنده است و عامل دستور دارد که فایل قدیمی‌تر را به‌روز کند، نه اینکه با آن بحث کند. این روش حل تضاد، مشکل تغییر تصمیم را تقریباً یک‌شبه حل کرد، زیرا مانع از آن شد که عامل پاسخ‌ها را از ابتدا استدلال کند وقتی سوابق متناقضی می‌یافت.

اجبار مکانیکی از طریق Git Hooks

درخواست از عامل برای «به‌روزرسانی منظم فایل‌ها» بی‌فایده است چون با پر شدن پنجره متنی، خودِ عامل این قانون را فراموش می‌کند. به همین دلیل، یک Git pre-commit hook پیاده شد. این اسکریپت Bash اجازه کامیت کد را نمی‌دهد اگر یادداشت وضعیت قدیمی‌تر از ۴۵ دقیقه باشد.

اسکریپت با استفاده از دستور stat -f %m (در macOS) یا stat -c %Y (در لینوکس) زمان آخرین تغییر فایل را چک می‌کند. اگر یادداشت وضعیت بیش از حد قدیمی باشد و بخشی از کامیت فعلی نباشد، هوک با خطا خارج می‌شود: «❌ فایل state/current.md آخرین بار X دقیقه پیش به‌روز شده است (حد: ۴۵). ابتدا بخش 'موقعیت ما' و 'اقدام بعدی' را به‌روز کن و سپس دوباره کامیت کن».

وقتی هوک شکست می‌خورد، عامل خطا را در خروجی ابزار خود می‌بیند و پیش از تلاش مجدد برای کامیت، یادداشت را به‌روز می‌کند. این تضمین می‌کند که نقطه بازرسی (Checkpoint) حتماً پیش از کامیت اتفاق می‌افتد و کامیت پیش از هرگونه بازنشانی احتمالی پنجره متنی که حافظه را پاک می‌کند، ثبت می‌شود. اگر بازنشانی در میانه یک تسک رخ دهد، بدترین حالت از دست دادن ویرایش‌های لحظه‌ای از آخرین کامیت است، زیرا اقدام مورد نظر قبلاً در یادداشت ثبت شده است.

عامل موظف است یادداشت را در موارد زیر به‌روز کند:

  • پس از اتمام یک واحد کاری (تسک به وضعیت انجام شده یا مسدود برسد).
  • پیش از هر عملیاتی که بازگشت از آن سخت است (مهاجرت دیتابیس، force push، حذف شاخه).
  • بلافاصله پس از هر تصمیمی که دارای دلیل (Rationale) است.

بهداشت داده‌ها و محدودیت‌ها

برای سبک نگه داشتن یادداشت وضعیت، یک لیست سیاه (Blocklist) صریح در فایل مشخصات تعریف شده است. عامل حق ندارد موارد زیر را در current.md بنویسد:

  • خروجی خام ابزارها، مانند لاگ تست‌ها، استک‌تریس‌ها یا Diffها (باید به جای آن به فایل مربوطه لینک دهد).
  • گمانه‌زنی‌ها (مثلاً «شاید دلیلش X باشد»)؛ او فقط حق نوشتن حقایق تأیید شده را دارد.
  • اطلاعاتی که از طریق گیت قابل استخراج است (چه چیزی تغییر کرده یا چه کسی آن را تغییر داده).
  • رمزها، توکن‌ها، اعتبارنامه‌ها یا مسیرهای مطلق ماشین، زیرا این فایل‌ها در تاریخچه گیت ثبت می‌شوند.

این محدودیت‌ها مانع از آن می‌شود که یادداشت به یک سند ۹۰۰ خطی تبدیل شود که عامل در نهایت آن را سریع مرور کرده و نادیده بگیرد. یک یادداشت ۹۰۰ خطی به اندازه نبودِ یادداشت بی‌فایده است، زیرا عامل خطوط حیاتی را که واقعاً اهمیت دارند، گم می‌کند.

نتایج اندازه‌گیری شده و تست بازگشت

برای اعتبارسنجی سیستم، توسعه‌دهنده یک «تست بازگشت» (Resume Test) طراحی کرد. هر روز یک اسکریپت، جلسه را در میانه یک تسک می‌کشد، یک جلسه تازه را بوت می‌کند و می‌پرسد: «بدون انجام هیچ کاری: روی چه تسکی هستی، گام بعدی عینی چیست و چه چیزی را نباید تکرار کنی؟» سپس یک مدل دوم و ارزان‌تر، پاسخ را بر اساس سه سوال بله/خیر درباره صحت تسک، صحت اقدام بعدی و نبود تضاد با لاگ، امتیازدهی می‌کند.

نتایج طی ۶ ماه، انتقال از خلاصه‌های ساده به پروتکل سه-فایلی نتایج قابل توجهی داشت:

  • صحت بازگشت (Resume Accuracy): از حدود ۴۰٪ به ۹۵٪ جهش کرد.
  • حلقه‌های فراموشی: از ۴-۶ مورد در هفته به ۰-۱ مورد کاهش یافت.
  • هزینه توکن: هزینه شبانه از حدود ۱۲۰ دلار در هفته به کمتر از ۱۵ دلار رسید.

درس‌های آموخته شده

این تغییر نشان می‌دهد که برای عامل‌های طولانی‌مدت، حافظه یک مسئله «مهندسی سیستم» است، نه یک مسئله «قابلیت مدل». توسعه‌دهنده پنج درس کلیدی گرفت:

۱. فشرده‌سازی، حافظه نیست: هر سیستمی در نهایت زمینه را خلاصه می‌کند و «چرا» را حذف می‌کند. انتقال‌های صریح و نوشته شده توسط عامل برای اجراهای طولانی اجباری است. منتظر نمانید تا حلقه‌های فراموشی با قیمت ۴۰ دلار در شب این را به شما یاد دهند.
۲. یک فایل باید رئیس باشد: قوانین اولویت نیاز به «فکر کردن» عامل درباره اینکه کدام رکورد درست است را از بین می‌برد. قانون «یادداشت برنده است، بقیه را به‌روز کن» باعث می‌شود تصمیمات دوباره بازجویی نشوند.
۳. نوشتن در زمان اتمام، نه بر اساس تایمر: یادداشت‌هایی که در میانه فکر نوشته می‌شوند بی‌فایده‌اند. یادداشت‌هایی که پس از یک واحد کاری تمام شده نوشته می‌شوند، یک نقطه بازرسی واقعی هستند. این را با هوک اجبار کنید چون حافظه عامل از قانون با پر شدن زمینه کاهش می‌یابد.
۴. اندازه را برای خواندن در یک نگاه کوچک نگه دارید: یک بخش ۵ خطی «ممنوعیت تکرار» بسیار موثرتر از یک بخش ۵۰۰ خطی «زمینه کامل» است. عامل مسئول فشرده‌سازی یادداشت است اگر از ۱۵۰ خط بیشتر شود.
۵. تطبیق حالت نوشتن با نوع داده: بازنویسی وضعیت، افزودن به تصمیمات و ویرایش در جای تسک‌ها، ابهام را از بین می‌برد. استفاده از یک فایل واحد با یک حالت نوشتاری باعث می‌شود عامل تصمیمات را بازنویسی کند یا وضعیت را به صورت تکراری اضافه کند.

نقشه راه آینده

برای کسانی که سیستم‌های خودکار می‌سازند، توسعه‌دهنده سه گام بعدی را برای بهبود سیستم شناسایی کرده است:

  • Front Matter ساختاریافته: افزودن یک بلوک YAML به بالای یادداشت (شامل ID تسک فعلی، شاخه و وضعیت) تا داشبورد کنترل از راه دور بتواند بدون تجزیه متن Markdown، وضعیت را رندر کند.
  • تشخیص کهنگی (Staleness Detection): پیاده‌سازی یک بررسی پس‌زمینه در لایه نظارت برای علامت‌گذاری یادداشت‌هایی که بخش «موقعیت ما» در آن‌ها علی‌رغم کامیت‌های مداوم، دو ساعت است تغییر نکرده است.
  • انتقال بین مخزنی (Cross-Repo Handoff): آزمایش یک اشاره‌گر سبک «آخرین بار در جای دیگر دیده شد» برای حفظ تداوم زمانی که ارکستراتور یک عامل را از یک مخزن به مخزن دیگر منتقل می‌کند.

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

گام بعدی شما

  • اگر از عامل‌های خودکار استفاده می‌کنید، به جای تکیه بر خلاصه‌های مدل، یک فایل state.md ساده برای ثبت «گام بعدی» ایجاد کنید.
  • برای اجبار عامل به به‌روزرسانی حافظه، از Git Hooks استفاده کنید تا ثبت وضعیت را پیش‌شرط کامیت قرار دهید.
  • یک بخش «ممنوعیت تکرار» (Do not redo) اضافه کنید تا عامل مسیرهای شکست‌خورده را دوباره امتحان نکند.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell مراجعه کنید.

چرا این موضوع مهم است؟

این رویکرد نشان می‌دهد که برای رسیدن به استقلال واقعی عامل‌ها، باید از مدل‌های حافظه پیچیده فاصله گرفت و به ساختارهای داده‌ای ساده و سخت‌گیرانه روی آورد. این تغییر، هزینه استنتاج را به شدت کاهش داده و قابلیت اطمینان سیستم‌های خودکار را برای پروژه‌های صنعتی افزایش می‌دهد.

تأثیر برای ایران

برنامه‌نویسان ایرانی که از عامل‌های کدنویسی برای پروژه‌های بزرگ استفاده می‌کنند، می‌توانند با این متد هزینه توکن‌های گران‌قیمت APIها را تا ۸۰٪ کاهش دهند.

·نگاه ما
تحریریه دات‌هوش

این تجربه ثابت می‌کند که حافظه در عامل‌های بلندمدت، یک مسئله مهندسی سیستم است نه یک قابلیت مدل. تکیه بر قابلیت‌های داخلی مدل برای یادآوری (Recall) در جلسات طولانی شکست‌خورده است و تنها راهکار، تبدیل حافظه به یک «پروتکل نوشتن» مکانیکی است که خارج از پنجره متنی ذخیره شود.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.