معماری ایجنت
حافظه در ایجنتها — از context تا حافظهی ماندگار
چرا context حافظه نیست، بین جلسهها چه چیزی گم میشود، و سه روش ساخت حافظه ایجنت: خلاصهسازی، vector store و حالت ساختیافته.
ایجنتی که هر بار از صفر شروع میکند
کاربر دیروز گفته بود «همیشه گزارشها را به تومان بده، نه ریال». امروز همان کاربر برمیگردد و ایجنت دوباره ریال مینویسد. هیچ باگی در کد نیست؛ ایجنت فقط چیزی برای بهیاد آوردن ندارد. جلسهی دیروز با همهی پیامهایش وقتی تمام شد، از بین رفت.
اینجا معمولاً یک سوءتفاهم شکل میگیرد: «پنجرهی context مدل یک میلیون توکن است، پس حافظهاش زیاد است». این دو چیز یکی نیستند. context ظرفیت یک درخواست است، نه چیزی که بین درخواستها باقی میماند. حافظه ایجنت چیزی است که شما باید بسازید.
چرا context حافظه نیست
مدل بین دو فراخوانی هیچ چیزی نگه نمیدارد. حالتی وجود ندارد که ذخیره شود. هر بار که مدل را صدا میزنید، تمام چیزی که باید بداند را خودتان دوباره میفرستید. اینکه در یک گفتوگوی چند پیامی به نظر میرسد مدل «یادش هست»، صرفاً به این دلیل است که کتابخانهی سمت شما کل تاریخچه را در هر درخواست دوباره ارسال میکند.
سه پیامد عملی دارد:
- context موقتی است. با پایان جلسه از بین میرود، مگر اینکه شما جایی ذخیرهاش کنید.
- context گران است. هر توکن در هر گام دوباره هزینه دارد. تاریخچهی طولانی یعنی صورتحساب بزرگتر در هر دور.
- context محدود است، حتی وقتی بزرگ است. پر کردن یک پنجرهی میلیونی با متن خام معمولاً کیفیت را بالا نمیبرد؛ نکتهی مهم بین انبوه جزئیات بیربط گم میشود. جزئیات این محدودیت در پنجرهی context آمده است.
پس حافظه یعنی: تصمیم آگاهانه دربارهی اینکه چه چیزی از یک جلسه بیرون کشیده، ذخیره، و در جلسهی بعد دوباره وارد context شود.
چه چیزی بین جلسهها گم میشود
قبل از انتخاب راهحل، باید بدانید دقیقاً چه چیزی را از دست میدهید. این چهار دسته کاملاً متفاوتاند و راهحلهای متفاوتی هم دارند:
- ترجیحات پایدار کاربر. واحد پول، زبان پاسخ، سطح جزئیات. کمحجم، بهندرت تغییر میکند، خیلی ارزشمند.
- واقعیتهای حوزهی کار. اینکه «مشتری الف همیشه با تخفیف قراردادی فاکتور میشود». نه در مدل هست، نه در دیتابیس ساختیافته.
- تاریخچهی کارها. چه چیزی قبلاً امتحان شده و شکست خورده. جلوی تکرار همان اشتباه را میگیرد.
- وضعیت کار ناتمام. ایجنت وسط یک فرایند سهمرحلهای بوده و مرحلهی دو تأیید نشده.
دستهی چهارم اصلاً حافظه به معنای رایج نیست؛ دادهی تراکنشی است و جایش دیتابیس معمولی است، نه هیچکدام از سازوکارهای زیر.
روش اول: خلاصهسازی
سادهترین راه: وقتی گفتوگو طولانی شد یا جلسه تمام شد، از مدل بخواهید خلاصهای بنویسد و آن خلاصه را ذخیره کنید. جلسهی بعد، خلاصه بهجای تاریخچهی کامل وارد context میشود.
خوبیاش این است که پیادهسازیاش یک روز کار است و context را واقعاً کوچک میکند. بدیاش دو چیز است. اول، اتلاف اطلاعات یکطرفه است: چیزی که در خلاصه نیامد، برای همیشه رفت. دوم، خطا انباشته میشود — وقتی خلاصه را خلاصه میکنید، انحراف هر دور روی دور قبل سوار میشود و بعد از چند نوبت به متنی میرسید که با واقعیت فاصله دارد.
توصیهی عملی: بهجای «خلاصهی گفتوگو» یک قالب مشخص بخواهید — تصمیمهای گرفتهشده، ترجیحات اعلامشده، کارهای باز — و همیشه چند پیام آخر را دستنخورده کنار خلاصه نگه دارید.
روش دوم: vector store
تکههای گفتوگوها و اسناد را embedding میکنید و ذخیره میکنید. در جلسهی بعد، پیام کاربر را هم embed میکنید و نزدیکترین تکهها را به context میآورید. همان الگوی RAG، اینبار روی حافظهی خود ایجنت.
جای درست استفادهاش، حجم زیاد و بازیابی مبتنی بر شباهت است: تاریخچهی صدها گفتوگو، اسناد داخلی، سابقهی تیکتها. مقیاسپذیر است و اطلاعات را دور نمیریزد.
اما محدودیتهایش را دستکم نگیرید:
- بازیابی احتمالی است. ممکن است ترجیح مهم کاربر بازیابی نشود چون پیام امروز از نظر معنایی به آن شبیه نبوده. برای چیزی مثل «همیشه به تومان» که باید *همیشه* اعمال شود، این روش ابزار مناسبی نیست.
- تضاد را مدیریت نمیکند. اگر کاربر ماه پیش گفته «ایمیل هفتگی» و هفتهی پیش گفته «ایمیل ماهانه»، هر دو تکه ذخیره شدهاند و هر دو ممکن است بازیابی شوند. مدل نمیداند کدام تازهتر است، مگر اینکه شما زمان را در متادیتا بگذارید و در prompt به آن اشاره کنید.
- برای فارسی به کیفیت embedding حساس است. انتخاب مدل و نحوهی نرمالسازی متن اثر مستقیم دارد؛ چالشهای NLP فارسی اینجا مستقیماً خودشان را نشان میدهند.
روش سوم: حالت ساختیافته
بهجای متن آزاد، چند فیلد مشخص را در یک جدول یا سند JSON نگه دارید:
{
"user_id": "u-1042",
"preferences": {
"currency": "IRT",
"report_language": "fa",
"detail_level": "short"
},
"known_facts": [
{"fact": "مشتری الف تخفیف قراردادی ۱۲٪ دارد", "updated_at": "2026-08-30"}
]
}
این روش کمهزینهترین و قابل اتکاترین گزینه برای ترجیحات است. حجمش ناچیز است، پس میتوانید در هر درخواست بفرستید؛ یعنی هیچوقت «بازیابی نشدن» ندارید. قابل بازبینی و ویرایش توسط کاربر است. و چون شکل ثابتی دارد، بهروزرسانیاش جایگزینی است نه انباشت — مشکل تضاد از بین میرود.
هزینهاش این است که شما باید schema را از قبل تعریف کنید. هر چیزی که در schema جا نشود، نگه داشته نمیشود. برای دانش باز و بیشکل مناسب نیست.
ترکیب درست، و اشتباههای رایج
در عمل هیچکدام از این سه بهتنهایی کافی نیست. تقسیم کاری که معمولاً جواب میدهد:
| نوع اطلاعات | سازوکار | چه زمانی وارد context میشود |
|---|---|---|
| ترجیحات کاربر | حالت ساختیافته | همیشه |
| دانش حوزه و اسناد | vector store | وقتی مرتبط باشد |
| تاریخچهی جلسههای قبل | خلاصهی قالبدار | ابتدای جلسه |
| کار ناتمام | دیتابیس معمولی | وقتی کار از سر گرفته شود |
و چند اشتباهی که زیاد دیده میشود:
- ذخیرهی همهچیز. حافظهای که هر جمله را نگه میدارد، در بازیابی بیفایده میشود. حافظه یعنی انتخاب، نه انباشت.
- نداشتن مسیر فراموشی. اطلاعات کهنه باید منقضی یا بازنویسی شوند. حافظهی فقطافزودنی بهمرور به منبع تناقض تبدیل میشود.
- نداشتن مرز بین کاربران. حافظه باید به کاربر و مستأجر گره بخورد. نشت ترجیحات یا واقعیتهای یک مشتری به گفتوگوی مشتری دیگر، یک رخداد امنیتی است نه یک باگ کوچک.
- ارزیابی نکردن. بعد از افزودن حافظه، همان مجموعهی تست قبلی را دوباره اجرا کنید. حافظهی بد میتواند کیفیت را پایین بیاورد، چون context را با اطلاعات بیربط پر میکند.
جمعبندی
context ظرفیت یک درخواست است؛ حافظه ایجنت چیزی است که شما بین درخواستها میسازید. خلاصهسازی ارزان است ولی اطلاعات را میخورد، vector store مقیاس میگیرد ولی بازیابیاش تضمینی نیست، و حالت ساختیافته قابل اتکاست ولی فقط برای چیزهایی که از قبل میدانید. اول تصمیم بگیرید چه چیزی *باید* یادآوری شود؛ انتخاب سازوکار بعد از آن تقریباً بدیهی میشود.