۱. مقدمه

هفته اول پرسیده شد چه کسی میگوید چه چیزی اجرا میشود . هفته دوم پرسیده شد چه کسی تصمیم میگیرد چه چیزی اجرا شود . هفته سوم پرسیده شد که یک اجرا چه چیزی را به جا میگذارد . این هفته مکالمه به پایان میرسد و سوال این است که چه چیزی از آن باقی میماند - و معلوم میشود که نوع آن، تعیین میکند که به کجا برود.
بایگانی جایی است که وقتی مشکلی پیش میآید، دره به آنجا میرسد. برجی که همه چیز را مینویسد، و وسپر ، مسئول بایگانی، پشت میزی در طبقه همکف. فانوس شما شبها خاموش میشود. شما میروید و میپرسید.
او همراه خوبی است. او همچنین، یک ماه بعد، از ملاقات شما خوشحال است.
یک رکورد، خاطره نیست.
این جمله کل آزمایشگاه است. شما از چهار پله بالا خواهید رفت و هر پله یک طبقه از برج است:
زنگوله | کف | آنچه را به یاد میآورد | چه چیزی آن را نگه میدارد |
۱ | میز تحریر، و طبقه اول | این بازدید | |
۲ | طبقه دوم، کشوی شما | این بازدیدکننده | |
۳ | طبقه سوم، برج | چه گفته شد؟ | بانک حافظه هوش مصنوعی ورتکس |
۴ | طبقه چهارم، فصل | تمام دره را | بیگکوئری |
هر پلهای که بالاتر میرود به این دلیل است که چیزی از پلهی پایینی افتاده است.

شما از قبل با پنج روش فراموش کردن توسط مسئول بایگانی آشنا هستید، زیرا هر یک از آنها در یک تماس پشتیبانی برای شما اتفاق افتاده است:
آنچه او میگوید | چه اشتباهی رخ داد؟ | |
~~1~~ | میشه دوباره اون مرجع رو بهم بدی؟ ~~ | ~~در عرض یک مکالمه فراموش کردم~~ — هفته سوم این مشکل را حل کردم |
~~2~~ | ~~ من شما را منتقل کردم. لطفا از اول شروع کنید. ~~ | ~~تاریخ با این روند از بین رفت~~ — هفته سوم این مشکل را هم حل کرد |
۳ | خوش آمدید! چطور میتوانم امروز به شما کمک کنم؟ | به کسی که از ماه گذشته پروندهاش باز است |
۴ | میشه آسیب رو توصیف کنید؟ | آخرین بار به فانوس نگاه کرد |
۵ | ما قبلاً هرگز این را ندیدهایم. | سی و یک نفر در این فصل سوال پرسیده اند |
خط سوم مربوط به این هفته است. این خط به سه روش مختلف با شکست مواجه میشود - یک بازدید جدید (فصل ۲)، یک جمله بدون خط روی برگه (فصل ۳) و یک بایگانی که فقط درون فرآیند وجود دارد (فصل ۴) - و هر روش یک فصل است. خط چهارم به شما واگذار میشود: look() در archive/agent.py جایی است که او علامتی را از روی یک عکس میخواند. خط پنجم مربوط به فصل ۵ است و اختیاری است.
شما به هفتههای اول تا سوم نیازی ندارید. یک نماینده، مدلی با یک دستورالعمل و چند ابزار است. هر چیز دیگری، در این هفته خودش را توضیح میدهد.
شما سه خط خواهید نوشت، و هر کدام از آنها یک مفهوم حافظه است :
خط | آنچه هست |
| وضعیت با کد نوشته میشود، نه با مکالمه |
| یک پیشوند یک عمر است |
| سیاست فراخوان - ثبت شده به خاطر سپرده نمیشود |
یک خط دیگر از قبل برای خواندن شما وجود دارد - سیاست نوشتن، await ctx.add_events_to_memory(...) - و فصل ۳ جایی است که قبل از نوشتن خطی که از آن استفاده میکند، عملکرد آن را میخوانید.
دو پله دیگر اصلاً ویرایش نیستند: شما یک برج (یک موتور عامل که میزبان بانک حافظه است) میسازید و همان کد به تنهایی به آن متصل میشود، و شما یک انبار (BigQuery) را بارگذاری میکنید ، یک گراف روی آن قرار میدهید و به همان عامل دو پرسوجوی کنترلشده میدهید - یکی بر اساس معنی، یکی بر اساس اتصال.
هر فصل یک حلقهی یکسان را اجرا میکند، و این حلقهای است که شما در ایجنتهای خودتان استفاده خواهید کرد: کد را بخوانید → خط را بنویسید → آن را در برج تماشا کنید. هیچ چیز ابتدا خراب اجرا نمیشود، هیچ چیز دوباره شروع نمیشود و هیچ چیز Ctrl+C نمیشود: هر ویرایش در پیام بعدی دریافت میشود و اتصال برج یک خط در فایلی است که بایگانی در هر درخواست دوباره میخواند. برج یک نقاشی است که دوباره ترسیم میکند: همان فایلهایی را که ویرایش میکنید میخواند و سه دکمه دارد - 🌙 روز را تمام میکند، 🗓 بازدید بعدی را شروع میکند، 🔥 شما را فراموش میکند. هر کار دیگری که در کد و ترمینال انجام میدهید.
دو مکان، و آنها هرگز کار را تغییر نمیدهند: تب ۱ ترمینال است، تب ۲ بایگانی. میز کار ( adk web ) دقیقاً یک بار، در فصل ۱، برای یک نگاه به وضعیت خام جلسه ظاهر میشود، و سپس کار شما با آن تمام میشود. هر 👉 زیر نشان میدهد که کدام. 👉💻 تب ۱ است، 👉🖥 تب ۲ است، 👉🔬 میز کار است. 👉📖 باز کردن یک فایل و خواندن است - چیزی برای تغییر نیست - و 👉✏️ نوعی از مرحله است که ویرایش میکند. هر زمان که یک مرحله به شما میگوید چیزی به Vesper بگویید، پیام دقیق در بلوک درست زیر آن قرار دارد - آن را همانطور که هست کپی کنید. و بخشی که عنوان آن به - فقط خواندن ختم میشود، چیزی برای تایپ، چسباندن یا کلیک ندارد: کد موجود در آن از قبل در مخزن وجود دارد و در حال اجرا است، برای خواندن شما چاپ شده است.
جملهی بالای درِ وسپر، جملهی اول است. جملهای که این هفته تمام میشود، جملهی پایانی هفتهی چهارم کل فصل است:
حقایق و ترجیحات به فروشگاههای متفاوتی تعلق دارند.
۲. 🧰 تنظیمات
- چه چیزی - به یک پروژه اشاره کنید یا یک کلید را جایگذاری کنید، نصب کنید و ثابت کنید که زنجیره ابزار سبز است.
- چرا - هیچ چیز اینجا بوت نمیشود. هر سرور درست قبل از مرحلهای که به آن نیاز دارد شروع میشود، بنابراین وقتی صفحهای باز میشود، میدانید که کدام دستور آن را دارد.
- چگونه - یک اسکریپت که پروژه را میسازد، یکی که نصب میکند و شما را به Vertex AI هدایت میکند، و یک پیشپرده که کد شما را میخواند و به شما میگوید کجا هستید.
همه چیز در این آزمایشگاه در Cloud Shell اجرا میشود - پایتون، uv و gcloud از قبل در آنجا نصب شدهاند و Gemini از طریق Vertex AI با استفاده از اعتبارنامههای شخصی شما قابل دسترسی است. هیچ کلید API در هیچ کجای این آزمایشگاه وجود ندارد و هیچ چیزی برای نصب روی لپتاپ شما وجود ندارد.
یک چیز هست که این آزمایشگاه نمیتواند به شما بدهد: یک پروژه گوگل کلود به همراه صورتحساب. مرحله ۱ حسابی برای شما ایجاد میکند که میتواند هزینه آن را پرداخت کند؛ مرحله ۴ آن حساب را به یک پروژه تبدیل میکند - یا پروژه Agent Valley را که قبلاً در هفته اول، هفته دوم یا هفته سوم انجام دادهاید، برمیدارد. آنها را به همین ترتیب انجام دهید - هر مرحله بعد از مرحله ۴، پروژهای را که مرحله ۴ به آن اختصاص داده است، میخواند.
۱ · یک حساب کاربری با قابلیت پرداخت دریافت کنید (تقریباً ۱ دقیقه)
🌐 قبل از هر کاری این کار را انجام دهید. با یک حساب گوگل که قابلیت پرداخت صورتحساب در آن فعال است، وارد شوید. هر حسابی از این نوع کار میکند. اگر حساب شما فعال نیست، یک دوره آزمایشی رایگان را شروع کنید؛ این کار به شما یک حساب پرداخت صورتحساب میدهد که تمام چیزی است که در مرحله ۴ به آن نیاز دارید.
۲ · باز کردن پوسته ابری (تقریباً ۱ دقیقه)
🌐 روی گزینهی «فعال کردن پوستهی ابری» در بالای کنسول گوگل کلود کلیک کنید - این آیکون به شکل ترمینال در سربرگ قرار دارد.

🌐 روی گزینه Open Editor (آیکون باز کردن پوشه با مداد) کلیک کنید. این کار ویرایشگر Cloud Shell را به همراه یک فایل اکسپلورر در سمت چپ باز میکند - هر زمان که یک مرحله علامتگذاری شده باشد، در آنجا کار میکنید 👉✏️.

🌐 اگر امروز در هر مرحلهای از شما خواسته شد که مجوز دهید، برای ادامه روی «مجوز دادن» کلیک کنید.
🌐 یک ترمینال درون ویرایشگر باز کنید - این تب ۱ است و هر 👉💻 مرحله در آنجا اجرا میشود، مگر اینکه خلاف آن ذکر شده باشد. تب ۲ بایگانی است و شما آن را در فصل ۱، درست قبل از مرحلهای که به آن نیاز دارید، باز میکنید. اگر ترمینال از قبل در پایین صفحه نیست، آن را با View → Terminal باز کنید:

🌐 بررسی کنید که با کدام حساب کاربری وارد سیستم شدهاید - باید همان حسابی باشد که در مرحله ۱ ذکر شد:
gcloud auth list
۳ · آزمایشگاه را آماده کنید (تقریباً ۱ دقیقه)
💻 مخزن را کپی کنید — این مخزن آرشیو است، نه مخزن هفته اول، هفته دوم یا هفته سوم:
cd ~
git clone https://github.com/cuppibla/agent-valley-archive
cd agent-valley-archive
۴ · پروژه خود را ایجاد کنید — یا از پروژه هفته قبل دوباره استفاده کنید (حدود ۲ دقیقه)
💻 یک اسکریپت حساب صورتحساب شما را پیدا میکند، یک پروژه روی آن ایجاد میکند و Cloud Shell را به آن پروژه ارجاع میدهد. اگر هفته اول، هفته دوم یا هفته سوم را انجام داده باشید، ابتدا پروژه Agent Valley را پیدا میکند و پیشنهاد استفاده مجدد از آن را میدهد - بله بگویید و هیچ پروژه قابل پرداخت دومی برای فکر کردن وجود ندارد:
cd ~/agent-valley-archive
./setup_project.sh
👀 انتظار یک ✓ برای حساب پرداختی که انتخاب کرده، یک ✓ برای پروژهای که ایجاد یا دوباره استفاده کرده، و یک پایان مانند این را داشته باشید:
cd ~/agent-valley-archive
Project ready: agent-valley-4271
Next: ./setup_codelab.sh
agent-valley-4271 شناسه پروژه شماست - شماره شما ارقام متفاوتی خواهد داشت. چهار اتفاق در آنجا رخ میدهد:
صورتحساب | جدیدترین | |
پروژه | به دنبال پروژه Agent Valley که از قبل دارید میگردد - هفته اول، هفته دوم یا هفته سوم - و پیشنهاد استفاده مجدد از آن را میدهد؛ در غیر این صورت | |
انتخاب میکند | شناسه را در | |
منتظر می ماند | نظرسنجیها تا زمانی که پروژه واقعاً اجازه ارائه خدمات داشته باشد، تا دو دقیقه | |
🌐 همان پروژه اکنون در کنسول است - شناسه آن در کارت اطلاعات پروژه در داشبورد قرار دارد:

۵ · نصب و نشانهگیری به سمت Vertex AI (حدود ۳ دقیقه)
💻 uv را نصب کنید، سه متغیری که ADK را به Vertex AI ارسال میکنند را اکسپورت کنید و تنظیمات را اجرا کنید:
cd ~/agent-valley-archive
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_CLOUD_LOCATION=global
./setup_codelab.sh
$(gcloud config get project) چیزی برای برگرداندن دارد زیرا مرحله ۴ آن را تنظیم کرده است. به همین دلیل است که مرحله ۴ اول میآید.
setup_codelab.sh فایل خود-توان (idempotent) است - اجرای مجدد آن به هر تعداد بار که بخواهید، بیخطر است. این فایل به ترتیب پنج کار انجام میدهد:
API | پروژه مرحله ۴ انتخاب شده را میخواند و | |
محیط UV | | |
سایت | | |
.env | متغیرهایی را که آزمایشگاه میخواند مینویسد - | |
بررسی مدل | یک تماس واقعی با Gemini برقرار میکند، بنابراین یک پروژه بد همینجا در زمان راهاندازی شکست میخورد، نه بعداً در یک فصل | |
۶ · ثابت کنید سبز است (~۱ دقیقه)
💻 پیشپرواز را اجرا کنید - هر تیک یک کاوش واقعی است، نه یک حدس:
cd ~/agent-valley-archive
uv run python scripts/preflight.py
👀 پیشپرواز از مدل یک سوال میپرسد تا صحت اعتبارنامههای شما را تأیید کند، بررسی میکند که بستههای آرشیو در جای خود قرار دارند، سپس کد شما را میخواند و نوار پیشرفت این هفته را چاپ میکند:
cd ~/agent-valley-archive
desk ▢ the slip · write_down writes nothing chapter 1 · EDIT ONE
floor 1 ✓ the file · SqliteSessionService chapter 1, read only
floor 2 ▢ the drawer · CASE = "case" chapter 2 · EDIT TWO
floor 3 ✓ what was said · closing time files the day chapter 3, read only
floor 3 ▢ the asking · recall never looks chapter 3 · EDIT THREE
floor 3 ▢ the tower · InMemoryMemoryService chapter 4 · built, not edited
floor 3 ✓ the rules · USER_PREFERENCES, LANTERN_CONTEXT chapter 4, read only
سه مورد از قبل تیک خوردهاند. اینها مواردی هستند که شما فقط میخوانید. سه مورد خطوطی هستند که مینویسید؛ یکی برجی است که میسازید. دوباره پیشپرواز را در هر نقطهای از آزمایشگاه اجرا کنید و به شما خواهد گفت که کجا هستید - فایلهای شما را میخواند، نه یک چکلیست.
۳. جلسه، وضعیت، سرویس SqliteSession
- چی؟ - یک خط بنویس؛ یک بار میز کار را روشن کن و پر شدن برگه را تماشا کن؛ بعد همین کار را در برج انجام بده؛ بعد بخوان که چرا تا شب دوام آورده است.
- چرا - اولین چیزی که در مورد حافظه باید دید این است که هیچ چیز به خودی خود به خاطر سپرده نمیشود. کسی آن را مینویسد.
- چگونه - یک خط در
agent.py، سپس هیچ چیز دیگری را تغییر ندهید.
ویرایش یک
👉📖 archive/agent.py را باز کنید و write_down پیدا کنید. 👉 را بخوانید:
case = dict(tool_context.state.get(CASE, {}))
if case.get(field):
return {"already_written": {field: case[field]}}
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
# tool_context.state[CASE] = case
return {"written": {field: value}}
این تابع، فایل لغزش را در یک dict محلی ایجاد میکند و سپس آن را برمیگرداند - و یک dict محلی با فراخوانی از بین میرود. این را همانطور که هست اجرا کنید و Vesper کاملاً صدای شما را میشنود: چهار فراخوانی write_down اجرا میشوند، هر کدام به "written" پاسخ میدهند و حالت جلسه خالی میماند، زیرا هیچ چیز به آن نرسیده است. او صدای شما را شنید. هیچ چیزی نوشته نشده است.
👉✏️ هنوز در archive/agent.py ، داخل write_down : علامت # را از قبل از یک خط حذف کنید - علامت # و فاصلهی بعد از آن. قبل از:
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
# tool_context.state[CASE] = case
return {"written": {field: value}}
بعد از:
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
tool_context.state[CASE] = case
return {"written": {field: value}}
ذخیره کنید.
لغزش پر میشود
👉💻 میز کار را شروع کنید. این تنها باری است که در آزمایشگاه آن را باز میکنید - این تنها پنجرهای است که وضعیت جلسه را به صورت JSON خام نشان میدهد:
cd ~/agent-valley-archive
uv run adk web . --allow_origins="*"
👉🔬 میز کار را با پیشنمایش وب → پیشنمایش روی پورت ۸۰۰۰ باز کنید (دکمه پیشنمایش وب، آیکون چشم روی پنجره در سمت راست بالای Cloud Shell است). بایگانی را انتخاب کنید و قبل از تایپ هر چیزی، روی وضعیت در پنل سمت چپ کلیک کنید.
👀 خالی. هنوز کسی چیزی نگفته است.
👉🔬 حالا اینو بگو، همهش تو یه پیام:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.
👀 چهار تراشه write_down فعال میشوند - روی یکی از آنها کلیک کنید و JSON را بخوانید: "written": {"no": "k7f2"} . حالا دوباره روی State کلیک کنید.

case {"no": "k7f2", "item": "lantern", "mark": "q7",
"symptom": "keeps going out at night"}
👀 خالی، سپس پر، درون یک پیام. این چیزی است که او با آن به خاطر میآورد، و به خودی خود ظاهر نشده است. شما خطی را که آن را مینویسد نوشتهاید. کل مکانیسم همین است: یک تابع ساده که در tool_context.state مینویسد، و یک دستورالعمل که به او میگوید هر زمان که بازدیدکننده چیزی میگوید که با خط روی برگه مطابقت دارد، از آن استفاده کند.
👉🔬 از او بپرسید:
What's my case number?
👀 k7f2. او آن را از روی برگه میخواند. بدون ویرایش اول، او از شما درخواست میکرد - در یک مکالمه، آن را در لیست پشتیبانی خط بزنید.
دو نگهبان، برگه را به شکل یک فرم کاغذی نشان میدهند: چهار خط کوتاه که هر کدام یک بار نوشته شدهاند. آنها بیش از آنچه به نظر میرسند، اهمیت دارند. بدون آنها، مدل متوجه میشود که میتواند کل مکالمه را در قالب symptom بریزد و فصل ۳ - فصلی که تمام نکته آن برگه است و هیچ خطی برای آن ندارد - بیسروصدا دیگر واقعی نیست. شکل یک فروشگاه توسط کدی که آن را مینویسد، اعمال میشود، یا اعمال نمیشود.
حالا برو به برج
👉💻 کار شما با میز کار تمام شد — آن را در حال اجرا بگذارید یا تب آن را ببندید؛ بقیه آزمایشگاه در بایگانی قرار دارد. یک تب ترمینال دوم (تب ۲ — بایگانی) باز کنید:
cd ~/agent-valley-archive
bash valley.sh
👉🖥 بایگانی را با پیشنمایش وب باز کنید → تغییر پورت ، ۳۴۴۰ را تایپ کنید و روی تغییر و پیشنمایش کلیک کنید. ۳۴۴۰ در فهرست سریع پیشنمایش وب نیست - ۸۰۸۰ پیشفرض آن است - بنابراین باید این بار آن را دستی تنظیم کنید. ▶ شروع ، سپس بایگانی را فشار دهید. شما پشت در هستید. هر کسی را که آمده تا بپرسد - یک نام و یک گونه - انتخاب کنید و وارد شوید.

👀 یک ساختمان. وسپر پشت میز طبقه همکف است و شما آنجا صحبت میکنید. بالای سر او: طبقه اول، طبقه دوم، طبقه سوم و یک اتاق زیرشیروانی تاریک. چهار پله، چهار طبقه هستند. آشنای شما روی طبقه همکف نشسته است، چون تا اینجا بالا رفتهاید. ستون کنار نقاشی، محتویات هر طبقه را در حال حاضر مشخص میکند - همان چهار مغازهای که روی میز مقدمه بود، به همان ترتیب. هیچ چیز در سمت راست دکمه نیست.
👉🖥 همان چهار مورد را در کادر زیر مکالمه - دقیقاً همین پیام - جایگذاری کنید و Enter را فشار دهید:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.

👀 به میز نگاه کنید. برگه خودش را مینویسد، یک خط در هر زمان - روی نقاشی، و به طور کامل زیر برگه کنارش. همان ابزار، همان حالت: برج فقط همان چیزی را که write_down نوشته است، نقاشی میکند و این کار را تا آخر آزمایش ادامه خواهد داد.
او یک شبه به یاد میآورد — فقط خواندنی
آنچه نوشتید و آنچه هست
خط | آنچه هست | کجا زندگی میکند |
| وضعیت جلسه - نوشته شده توسط کد، خوانده شده توسط دستورالعمل | میز · |
| محل ذخیرهسازی جلسه - بازدیدی در یک فایل، نه در یک فرآیند | طبقه اول · |
این پله به یک بازدید میرسد و تا زمان بسته شدن در باقی میماند. اما در بازدید بعدی دوام نمیآورد.
۴. وضعیت کاربر
- چی ؟ - یه ماه بعد برگرد؛ اون برگه دیگه نیست. پنج تا کاراکتر داره، و دنبالت میاد.
- چرا - یک جلسه، یک بازدید است. بازدید بعدی یک جلسه جدید است و هرگز جلسه قبلی را ندیده است.
- چطور — پنج کاراکتر در
state.py.


👉🖥 یک ماه بعد دکمه 🗓 را فشار دهید. این یک جلسه جدید برای همان بازدیدکننده است - روشی که برای شروع یک مکالمه جدید در هفته سوم استفاده شد.
به آرشیو خوش آمدید. امروز چطور میتوانم به شما کمک کنم؟
👉🖥 به برج نگاه کن. طبقه اول یک کتاب دیگر روی آن دارد - این بازدید. کشوی طبقه دوم خالی است. برگه روی میز هم خالی است.

👀 چرا: این رسید در session state نوشته شده است، و یک جلسه همان این بازدید است. بازدیدهای تغییر، جلسات تغییر. هیچ چیز منتقل نمیشود زیرا چیزی به آن گفته نشده است. آخرین بازدید هنوز در فایل است - این اولین دفتر طبقه اول است - اما رسید تحت کلیدی نوشته شده است که برای یک بازدید معتبر است.
ویرایش دو
👉✏️ archive/state.py را باز کنید. علامت 👉 را پیدا کنید. قبل از:
# 👉 EDIT TWO — chapter 2. Five characters. A key with no prefix lives for one
# visit; `user:` follows the visitor into every visit after this.
CASE = "case"
بعد از:
CASE = "user:case"
ذخیره کنید. نیازی به راهاندازی مجدد نیست — بایگانی کد شما را در هر پیام دوباره وارد میکند.
👉🖥 دوباره همان چهار مورد را پیست کنید — دقیقاً همین پیام:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.
👉🖥 سپس یک ماه بعد دوباره 🗓 را فشار دهید - همان دکمه قبلی.

👀 اسم شما روی کشوی طبقه دوم است و چهار فیلد داخل آن قرار دارند. برگه روی میز همان چهار خط است - وقتی وارد شدید از کشو بیرون کشیده شد. فامیل شما یک طبقه بالاتر رفته است. همان کلید، هر بار مراجعه، یک مقدار: این پیشوند است که کار خودش را میکند.
پیشوند یک عمر است.
پیشوند | چقدر عمر میکند | کدام طبقه |
(هیچکدام) | این بازدید | میز تحریر |
| هر بازدیدی که این بازدیدکننده انجام میدهد | طبقه دوم، کشوی شما |
| همه، برای همیشه | بخشی از طبقه چهارم |
| فقط این نوبت — هرگز حتی از پلهها بالا نمیرود | — |
یک میز، چهار کشو. این فصل به یکی از آنها میپردازد.
الان تو رو میشناسه
👉🖥 این ملاقات کاملاً جدید است و شما هنوز چیزی در آن نگفتهاید. از او بپرسید:
What did I bring in last time?
👀 من یادداشت کردهام که شما یک فانوس، مورد k7f2، آوردهاید. با q7 مشخص شده است و شما اشاره کردید که شبها مدام خاموش میشود. هر کلمه از آن از کشو بیرون آمده است، نه مکالمه - این بازدید فقط یک پیام قدیمی است و پیام هیچ چیزی را نامگذاری نکرده است. این وضعیت سطح کاربر است که کار خود را انجام میدهد: یک کلید user: قبل از اینکه بازدیدکننده کلمهای بگوید، در هر بازدیدی که انجام میدهد، آنجاست.
کشو چهار خطی که روی برگه نوشته شده را نگه میدارد، و نه چیز دیگری. آنچه میگویید و روی برگه خط ندارد - چه تلاشی کردهاید، چرا برایتان مهم است، آیا برگه جدیدی میخواهید یا نه - جایی برای رفتن ندارد، و یک ماه بعد از بین میرود. کشو از ماه عبور میکند. یک جمله عبور نمیکند. فصل ۳ در مورد همین است.
آنچه نوشتید و آنچه هست
خط | آنچه هست | کجا زندگی میکند |
| یک پیشوند یک طول عمر است - همان کلید، که به جای بازدید، به بازدیدکننده محدود شده است. | طبقه دوم · |
این پله به یکی از بازدیدکنندگان میرسد. کشو به دنبال شما میآید؛ اما مکالمه همچنان ادامه دارد.
۵. سرویس حافظه داخلی
- چه چیزی - سطری را که روز را ثبت میکند بخوانید، سطری را که میپرسد بنویسید؛ سپس یک زمان پایانی و یک سوال.
- چرا ؟ - این برگه یک فرم است. یک فرم شامل خطوطی است که شما به آن دادهاید. مردم به صورت خطی صحبت نمیکنند - و بایگانی کردن آنچه گفته شده تنها نیمی از به خاطر سپردن آن است.
- چگونه — خواندن
file؛ یک خط درrecall؛ سپس یکی 🌙، یکی 🗓، یک سوال.

کاری که او در راه خانه انجام میدهد - فقط برای خواندن
ثبت شده به خاطر سپرده نمیشود
آن خط روز را روی قفسه میگذارد. چیزی از آن کم نمیکند. یک قفسه پر از کارت دقیقاً به اندازه یک قفسه خالی مفید است اگر کسی نگاه نکند - و همانطور که کد ارسال میشود، هیچ کس نگاه نمیکند: recall ، گرهای که قبل از Vesper در هر سوال اجرا میشود، از برج چیزی نمیپرسد. ذخیرهسازی که نمیتوانید در لحظه مناسب به خاطر بیاورید، حافظه نیست؛ بلکه یک گزارش است. نگاه کردن نیمه دیگر است و نوشتن آن بر عهده شماست.
ویرایش سوم - پرسیدن
👉📖 در archive/agent.py ، تابع find recall پیدا کنید - تابعی که درست بالای file قرار دارد. این یک گره تابع ساده است که قبل از Vesper روی هر سوال اجرا میشود - نه اینکه مدل تصمیم بگیرد چه زمانی جستجو کند، به این معنی که زمانهایی که چیزی پیدا نمیکند نیز قابل مشاهده هستند. برای 👉 بخوانید:
if query.strip():
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
# found = await ctx.search_memory(query)
pass
except ValueError:
# no memory service wired on this runner — degrade, do not crash
pass
👉✏️ هنوز در archive/agent.py هستید، داخل recall : علامت # را از قبل از یک خط حذف کنید - علامت # و فاصلهی بعد از آن. قبل از:
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
# found = await ctx.search_memory(query)
pass
except ValueError:
بعد از:
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
found = await ctx.search_memory(query)
pass
except ValueError:
pass را همان جایی که هست بگذارید - الان هیچ کاری نمیکند، زیرا found از قبل در بالا None است.
ذخیره کنید. بدون نیاز به راهاندازی مجدد.
دو خط، دو سیاست - و شما یکی از آنها را نوشتید. سیاست نوشتن تصمیم میگیرد که چه چیزی و چه زمانی نگه داشته شود (زمان بسته شدن، کل روز). سیاست فراخوانی تصمیم میگیرد که چه زمانی و با چه چیزی نگاه کنید (هر سوال، خود سوال). ADK افعال را به شما میدهد - add_events_to_memory ، search_memory - و از سیاست خارج میماند. این تقسیمبندی درست است، و به همین دلیل است که نوشتن درخواست بر عهده شما بود.
زمان بسته شدن
👉🖥 سه چیزی که در برگه قید نشده را بگویید. یک یا سه پیام:
I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.

👉🖥 پس دکمه 🌙 رو فشار بدید، همین برای امروز کافیه .

👀 به میز نگاه کن. سه چیزی که گفتی از روی تخته بلند میشن و بالا میان و روی طبقه سوم فرود میان. زیر کارتهای کنار برج، لیستشون نوشته شده - یک کارت برای هر پیامی که فرستادی، یه quote علامتگذاری شده، یه جمله کلمه به کلمه، و در این فرآیند بالای اونها. چراغ روی میز تا وقتی که فرود بیان، روشن میمونه.
یک ماه بعد، او میداند
👉🖥 یک ماه بعد دکمه 🗓 را فشار دهید و بپرسید:
What have I already tried?
👀 سعی کردی آن را بالاتر آویزان کنی - کمکی نکرد. او میداند. برگه هیچ خطی برای آن ندارد و هرگز هم نداشته، بنابراین کشو نمیتوانسته به این سوال پاسخ دهد؛ recall کارت را پیدا کرد، آن را در ctx.state["recalled"] نوشت، و دستورالعمل او این را میخواند. یک خط تفاوت است.
و همه چیز در ذهن اوست
زیر کارتهای کنار برج را نگاه کنید: در این فرآیند . در حال حاضر حافظه InMemoryMemoryService است - یک دیکشنری درون bash valley.sh ، و دقیقاً تا زمانی که آن فرآیند وجود دارد، زنده میماند. امشب برج را ببندید و فردا آن را باز کنید: کتابها در طبقه اول هستند، کشو در طبقه دوم است و طبقه سوم تاریک است. پرونده در فایل باقی مانده است. آنچه شما گفتید در فرآیند زنده مانده است و فرآیند از بین رفته است.
ماندگاری، حافظه نیست.
دو چیز در مورد طبقه سوم به صورت فعلی اشتباه است: در حال انجام است، و به قول خودتان - یک نقل قول است، نه یک واقعیت. فصل بعدی هر دو را بدون هیچ کدی اصلاح میکند.
آنچه خواندید، آنچه نوشتید و آنچه هست
خط | آنچه هست | کجا زندگی میکند |
| سیاست نوشتن - یک روز کامل، در زمان بسته شدن | طبقه سوم · سرویس حافظه |
| سیاست فراخوان - هر سوال، قبل از اینکه او پاسخ دهد | |
این پله به آنچه گفته شد میرسد - و آن را در یک دیکشنری درون یک فرآیند، به قول خودتان، نگه میدارد. هر دو نیمهی آن اشتباه هستند و فصل بعدی هر دو را بدون هیچ کدی اصلاح میکند.
6. بانک حافظه
- چه چیزی - یک اسکریپت یک بانک حافظه میسازد؛ دو خطی که از قبل دارید شروع به نوشتن حقایق در آن و خواندن مجدد آنها میکنند.
- چرا - یک سرویس حافظه، یک سرویس است. محل قرارگیری آرشیو، یک تصمیم استقرار است، نه یک عامل - شما آن را بدون دست زدن به عامل تغییر میدهید.
- چطور —
make_tower.pyاجرا کنید، سه جمله بگویید، 🌙 را فشار دهید، 🗓 را فشار دهید، یک سوال بپرسید. سپس نگاهی فقط خواندنی به نوشتن و خواندن بیندازید.
برج را بسازید
طبقه سوم فصل سوم یک دیکشنری پایتون درون bash valley.sh بود. Memory Bank نسخه مدیریتشده آن طبقه است: این طبقه در یک نمونه Agent Engine در پروژه شما قرار دارد، حقایق را به جای جملات شما، از روز استخراج میکند و آنها را به جای کلمات مشترک، بر اساس معنی پیدا میکند. برای استفاده از Vesper نیازی به استقرار آن در یک Agent Engine ندارید - نمونهای که هیچ چیزی روی آن مستقر نشده باشد، همچنان میزبان یک Memory Bank است و این چیزی است که اسکریپت میسازد: برجی که هیچکس در آن زندگی نمیکند و تنها وظیفهاش نگهداری کارتها است.
👉💻 در تب ۱ (حدود یک دقیقه):
cd ~/agent-valley-archive
uv run python scripts/make_tower.py
👀 انتظار:
building the tower in YOUR_PROJECT / us-central1 — about a minute...
built, with the two topics in archive/topics.py configured on it.
resource: projects/.../locations/us-central1/reasoningEngines/6792...
AGENT_ENGINE written to .env
Next — nothing to restart. The Archive reads .env on every message:
look at floor three — under the cards it now says Memory Bank.
(adk web, if you use it: --memory_service_uri "agentengine://projects/.../reasoningEngines/6792...")
یک فراخوانی SDK، agent_engines.create() و یک خط نوشته شده در .env . بایگانی آن فایل را در هر درخواست دوباره میخواند، بنابراین چیزی برای راهاندازی مجدد وجود ندارد.
عملکردش را تماشا کنید
👉🖥 به طبقه سوم نگاه کنید - برج ظرف چند ثانیه خود به خود دوباره ترسیم میشود. حالا زیر کارتها نوشته شده است بانک حافظه ، و قفسه خالی است: سه نقل قول از فصل ۳ در حال انجام بودند، و این روند نیست. سه جمله را دوباره بگویید، یک یا سه پیام:
I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.
👉🖥 دکمه 🌙 رو فشار بدید، همین برای امروز کافیه . الان حدود هشت ثانیه طول میکشه تا بسته بشه - برج داره آب میشه. بعد به طبقه سوم نگاه کنید.

👀 کارتها جملات شما نیستند. چیزهایی میگویند مثل اینکه من از فانوسم برای مطالعه در شب استفاده میکنم، که همان زمانی است که مدام خاموش میشود و من قبلاً سعی کردهام فانوسم را بالاتر آویزان کنم ، و هر fact با تاریخ مشخص کردهام. خلاصه. آنچه وارد شد یک مکالمه بود. آنچه بیرون آمد واقعیت است.
👉🖥 یک ماه بعد - یک ملاقات جدید، بنابراین دیگر هیچ چیز از آنچه که گفتید در مکالمه نیست - دکمه 🗓 را فشار دهید و این را از او بپرسید، سوالی که حتی یک کلمه هم با کارتها مشترک نیست:
Should I just get a different lamp?
👀 از آنجایی که هدیه بود، اشاره کردید که نمیخواهید فانوس خود را تعویض کنید - و قبلاً سعی کردهاید آن را بالاتر آویزان کنید. هر کلمه از این حرف از کارت آمده است، و فقط کارت میتوانسته به او بگوید: این بازدید یک پیام قدیمی است. جستجوی شباهت. برج با معنی مطابقت دارد، نه با کلمات - سوال گفت: لامپ و متفاوت ، کارت میگوید فانوس و جایگزین . و اکنون خارج از فرآیند است: امشب برج را ببندید و کارتها فردا آنجا هستند، زیرا آنها در موتور عامل زندگی میکنند، نه در bash valley.sh .
نوشتن و خواندن - فقط خواندنی
The right to be forgotten

👉🖥 Press 🔥 forget me , under the message box.
👀 Three floors go dark and one does not:
what it holds | after 🔥 | |
floor three, the cards | what she remembered — the facts Memory Bank distilled from your visits | gone — |
floor two, the drawer | | cleared |
the desk, the slip | this visit's copy of the case | cleared |
floor one, the books | the visits themselves — one session in | untouched — the count does not move |
The books are the record : that a visit happened, on which day, and what was said in it. The cards and the drawer are the memory : what she made of those visits and carries into the next one. A request to be forgotten is about the memory — the facts derived about a person, and the state that follows them around. Whether the record of a visit also goes is a separate decision, made for separate reasons — an audit trail, a support history — and this lab keeps it on purpose, so you can watch the two come apart in front of you: the record and the memory are not the same thing, and forgetting one does not touch the other. That is the sentence over Vesper's door, seen from the other side.
The ADK memory abstraction has add_events_to_memory and search_memory . It has no delete. Forgetting is a governance action, not an agent action, so it lives in the application — archive/tower.py , memories.delete on the raw client — where the model cannot reach it by accident. Whether an agent can forget is something you design, exactly like whether it can remember.
What you built and read, and what it is
the thing | آنچه هست | where it lives |
| the memory service, as a service: a deployment decision, outside the agent | Vertex AI Memory Bank on an Agent Engine |
| the same | |
| what may be kept, decided at write time, not in the prompt | |
🔥 → | forgetting is an application action; ADK's memory API has no delete | |
This rung reaches what was said. A day goes in; a fact comes out — distilled to what the topics allow — and the book on floor one never moved.
7. GraphRAG
Optional.
- What — one script loads the valley's past into BigQuery, generates embeddings inside the warehouse and lays a graph over the tables without moving a row; then two questions to Vesper, who has two governed queries for it: one for what sounds alike, one for what is connected.
- Why — sounds alike is not the same as is connected. A vector finds where to look; a graph finds what is attached; and the answer has to carry the path it walked.
- How —
bash scripts/season.sh, once. Everything else in this chapter is read only.
Vesper's tower is scoped to you — (app_name, user_id) — by construction. Other people's visits are not her memory, and searching it harder would not change that. They are the valley's, and the valley keeps them in a warehouse. Thirty-one visitors this season came about a q7 lantern going out at night, and one of them found the cause. This chapter loads that warehouse and hands her two fixed queries into it.
Graph RAG
RAG is retrieve, then generate : find the right records, hand them to the model, let it answer from them. Plain RAG retrieves by similarity — embed the question, pull the nearest records. That finds things that sound like the question, and it is where chapter 4's tower stopped. Graph RAG adds a second retrieval step that follows edges — keys, which a warehouse already has — from whatever similarity found, to whatever is attached to it. The two steps answer different questions, and this chapter makes you ask both:
گام | the question it answers | چگونه | در این فصل |
شباهت | where should I look? | embed the question, | |
traversal | what is connected to that? | | |
نسل | what do I tell the visitor? | the model, with the path as its citation | her answer |
اجراش کن
The season ships as plain CSVs in season/ — visitors, items, asks, fixes, seasons — the way a support system would export them. One script takes them all the way into the warehouse, and it is the only thing you run in this chapter.
👉💻 In tab 1 (about three minutes; it prints each step as it goes, and stops on the first one that fails):
cd ~/agent-valley-archive
bash scripts/season.sh
👀 When it ends, floor four on the tower lights up, and the legend beside it says open .
What it built — read only
How she reads it — read only
Ask her
👉🖥 Floor four is open on the tower. Ask her the way any visitor would:
Has anyone else had a q7 lantern go out at night?

👀 She answers — thirty-one others... most said the flame sat low and went out after an hour... Marlow the otter found the wicks were dipped too shallow — and the last line of her answer is the path. This is her output — read it, do not send it back to her:
mark q7 → 31 visits this season → 10 different things said → the wicks in the q7 batch were dipped too shallow — they drown in the oil and starve in an hour
The path is the citation. Every hop in it is an edge in the graph, and an edge is a key, so anyone can walk it again and get the same thirty-one. A similarity score cannot be walked again by anyone. If she says, just before the path, that the rows came back through three joins because the project has no graph reservation, that is your project's edition refusing GRAPH_TABLE — the rows and the path are the same.
👉🖥 Now leave the mark out — send this:
Has anyone else had a lantern that keeps dying once the sun is down?
👀 She gets to the same thirty-one, and the same path. What changed is the road: season_search ran first, found five visitors who sound alike — each carrying q7 — took that mark, and only then walked. Similar is not connected ; similar is where to look.
What was loaded, and what it is
the thing | آنچه هست | where it lives |
| a warehouse: columns with names and types | |
| the similarity step — where to look | |
| the traversal step — what is connected; tables become nodes and arrows | |
| the only walk she may take; the path is the citation | |
A few dozen visitors is not a reason to buy a warehouse. What this chapter teaches is the shape of the query — not scale. Grow this graph a thousand times and the MATCH does not change by a character.
8. Close
Three lines, one tower, one warehouse — four floors. Nothing restarted.
the rung | کاری که شما انجام دادید | what it bought |
یکی | | a slip that exists at all: state is written by code |
TWO | | a drawer that follows the visitor |
the filing | | what was said, kept |
سه | | what was kept, asked for — filed is not remembered |
برج | | a tower that distills, and survives the night |
the rules | | only what should be kept |
the season | | everyone else's visits, by meaning and by connection |
What each piece replaced
ADK 2 | what you would write instead | what that costs you |
| a dict beside the chat loop, pickled to disk yourself | the model cannot read it, and you write the resume and the locking |
the | a users table and a join on every turn | deciding where a key belongs, every single time |
| a cron that dumps transcripts, and the model deciding when to look | it dumps, nothing distills — and you never see the times it didn't look |
| a vector store, an extraction prompt, a consolidation job, and "please don't remember trivia" in the prompt | three services, and the rule holds until the model feels otherwise |
| export, embed elsewhere, a graph database, a sync job, and the model writing SQL | the data leaves the warehouse to be understood, and you find out which joins it guessed when a customer does |
Three questions, stop at the first yes
- Will it matter tomorrow? No → keep nothing. It lives and dies in the conversation.
- Can a function check it? Yes → the drawer. Code writes it, every turn reads it, and no sentence can change it.
- Otherwise → the tower. Distilled from what was said, looked up when it is asked for, revised by what is said next.
The one sentence
A record is not a memory. A memory is a record plus two decisions — what to keep, and when to look — and what kind of thing it is decides which floor it goes on.
Facts and preferences belong in different stores.
👀 Every floor is lit. Your familiar is at the top. The Archive stamp is in the passport, and week five opens the Night Market — where Nix runs the stage out loud, in real time, and everything the tower knows comes with you.