আর্কাইভ — এজেন্ট ভ্যালিতে স্মৃতি

১. ভূমিকা

নিচতলার ডেস্কে আর্কাইভিস্ট ভেসপার।

প্রথম সপ্তাহে প্রশ্ন করা হয়েছিল , কী চলবে তা কে ঠিক করে । দ্বিতীয় সপ্তাহে প্রশ্ন করা হয়েছিল , এরপর কী চলবে তা কে ঠিক করে । তৃতীয় সপ্তাহে প্রশ্ন করা হয়েছিল , একটি দৌড় পেছনে কী রেখে যায় । এই সপ্তাহে আলোচনাটি শেষ হয়, এবং প্রশ্ন হলো এর পরেও কী টিকে থাকে — আর দেখা যায় যে , জিনিসটি কী ধরনের, সেটাই ঠিক করে দেয় এটি কোথায় যাবে।

যখন কিছু ভুল হয়ে যায়, তখন উপত্যকাটা আর্কাইভে এসে মেশে। একটা টাওয়ার যা সবকিছু লিখে রাখে, আর নিচতলার ডেস্কে বসে আছেন আর্কাইভিস্ট ভেসপার । রাতে আপনার লণ্ঠনটা নিভে যায়। আপনি গিয়ে জিজ্ঞাসা করেন।

সে একজন চমৎকার সঙ্গী। এক মাস পর, আপনার সাথে দেখা হয়ে সেও আনন্দিত।

রেকর্ড স্মৃতি নয়।

ওই বাক্যটাই হলো পুরো পরীক্ষাগার। তুমি এটাতে চারটি ধাপে উঠবে, এবং প্রতিটি ধাপ হলো টাওয়ারের এক একটি তলা:

ধাপ

মেঝে

এটা যা মনে রাখে

কি এটাকে রাখে

১

ডেস্ক, এবং মেঝে এক

এই সফর

session.state · SqliteSessionService

২

দ্বিতীয় তলা, তোমার ড্রয়ার

এই পরিদর্শক

user: উপসর্গ

৩

তৃতীয় তলা, টাওয়ার

যা বলা হয়েছিল

ভার্টেক্স এআই মেমরি ব্যাংক

৪

চতুর্থ তলা, ঋতু

পুরো উপত্যকা

বিগকোয়েরি

প্রতিটি উপরের ধাপ তৈরি হয় কারণ নিচের ধাপ থেকে কিছু একটা পড়ে গেছে।

আর্কাইভটি খুলে দেখা গেল—পাঁচটি তলা, এবং একটি ছোট দল তার প্রতিটি তলা রক্ষণাবেক্ষণ করছে।

আর্কাইভিস্টের ভুলে যাওয়ার পাঁচটি উপায় আপনার আগে থেকেই জানা আছে, কারণ সাপোর্ট কলের সময় এর প্রত্যেকটিই আপনার সাথে ঘটেছে:

সে যা বলে

কী ভুল হয়েছে

~~১~~

আপনি কি আমাকে সেই সূত্রটি আবার দিতে পারবেন?

এক কথোপকথনের মধ্যেই ভুলে গিয়েছিলাম — তৃতীয় সপ্তাহে এটা ঠিক করেছি।

~~২~~

আপনাকে স্থানান্তর করা হয়েছে। অনুগ্রহ করে শুরু থেকে শুরু করুন।

প্রক্রিয়াটির সাথে ইতিহাসও মরে গেল — তৃতীয় সপ্তাহে এটাও ঠিক হয়ে গেল।

৩

স্বাগতম! আজ আমি আপনাকে কীভাবে সাহায্য করতে পারি?

গত মাস থেকে যার একটি চলমান মামলা রয়েছে এমন কারো কাছে

৪

আপনি কি ক্ষতির বিবরণ দিতে পারবেন?

সে গতবার লণ্ঠনটির দিকে তাকিয়েছিল।

৫

আমরা আগে কখনো এটা দেখিনি।

এই মৌসুমে একত্রিশ জন জিজ্ঞাসা করেছেন

তৃতীয় লাইনটি হলো এই সপ্তাহের। এটি তিনভাবে ব্যর্থ হয় — একটি নতুন ভিজিট (অধ্যায় ২), স্লিপে কোনো লাইন ছাড়া একটি বাক্য (অধ্যায় ৩), এবং একটি আর্কাইভ যা শুধুমাত্র প্রসেসের ভেতরেই থাকে (অধ্যায় ৪) — এবং প্রতিটি উপায়ই একটি অধ্যায়। চতুর্থ লাইনটি আপনার জন্য রাখা হলো: archive/agent.py এর look() হলো সেই জায়গা যেখানে সে একটি ছবি থেকে একটি চিহ্ন পড়ে। পঞ্চম লাইনটি হলো অধ্যায় ৫, এবং এটি ঐচ্ছিক।

আপনার প্রথম থেকে তৃতীয় সপ্তাহের প্রয়োজন নেই। একজন এজেন্ট হলো একটি মডেল, যার সাথে একটি নির্দেশিকা এবং কয়েকটি সরঞ্জাম থাকে। বাকি সবকিছু, এই সপ্তাহেই পরিষ্কার হয়ে যাবে।

আপনি তিনটি লাইন লিখবেন, এবং সেগুলোর প্রতিটিই একটি স্মৃতি ধারণা:

লাইন

এটা কি

tool_context.state[CASE] = case

স্টেট কোড দ্বারা লেখা হয়, কথোপকথন দ্বারা নয়।

CASE = "user:case"

একটি উপসর্গ হলো আজীবন

found = await ctx.search_memory(query)

প্রত্যাহার নীতি — দাখিল করা হয়েছে মনে রাখা হয়নি

আপনার পড়ার জন্য আরও একটি লাইন ইতিমধ্যেই দেওয়া আছে — রাইট পলিসি, await ctx.add_events_to_memory(...) — এবং যে লাইনে এটি ব্যবহার করা হবে, সেটি লেখার আগে এটি কী কাজ করে তা অধ্যায় ৩-এ পড়ে নিতে হবে।

অন্য দুটি ধাপ মোটেও কোনো সম্পাদনা নয়: আপনি একটি টাওয়ার (একটি এজেন্ট ইঞ্জিন যা মেমোরি ব্যাংক হোস্ট করে) তৈরি করেন এবং একই কোড নিজে থেকেই এর সাথে সংযুক্ত হয়, এবং আপনি একটি ডেটা ওয়্যারহাউস (বিগকোয়েরি) লোড করে তার উপর একটি গ্রাফ স্থাপন করেন, এবং একই এজেন্টকে দুটি নিয়ন্ত্রিত কোয়েরি দেন — একটি অর্থ অনুসারে, অন্যটি সংযোগ অনুসারে।

প্রতিটি অধ্যায় একই লুপে চলে, এবং এই লুপটিই আপনি আপনার নিজের এজেন্টদের উপর ব্যবহার করবেন: কোড পড়ুন → লাইনটি লিখুন → টাওয়ারে তা দেখুন। প্রথমে কোনো কিছুই ত্রুটিপূর্ণভাবে চলে না, কিছুই রিস্টার্ট করা হয় না, এবং কিছুই Ctrl+C দিয়ে বন্ধ করা হয় না: প্রতিটি সম্পাদনা পরবর্তী মেসেজে গৃহীত হয়, এবং টাওয়ার সংযোগ করা হলো একটি ফাইলের একটি মাত্র লাইন, যা আর্কাইভ প্রতিটি অনুরোধে পুনরায় পড়ে। টাওয়ার হলো এমন একটি অঙ্কন যা পুনরায় আঁকা হয়: এটি আপনার সম্পাদনা করা ফাইলগুলোই পড়ে, এবং এর তিনটি বাটন আছে — 🌙 দিন শেষ করে, 🗓 পরবর্তী ভিজিট শুরু করে, 🔥 আপনাকে ভুলে যায়। বাকি সবকিছু আপনি কোড এবং টার্মিনালে করেন।

দুটি জায়গা, এবং তাদের কাজ কখনো বদলায় না: ট্যাব ১ হলো টার্মিনাল, ট্যাব ২ হলো আর্কাইভ। ওয়ার্কবেঞ্চ ( adk web ) ঠিক একবার আসে, প্রথম অধ্যায়ে, সেশনের মূল অবস্থা একবার দেখার জন্য, এবং তারপর আপনার কাজ শেষ। নিচের প্রতিটি 👉 চিহ্ন নির্দেশ করে কোনটি। 👉💻 হলো ট্যাব ১, 👉🖥 হলো ট্যাব ২, 👉🔬 হলো ওয়ার্কবেঞ্চ। 👉📖 হলো একটি ফাইল খুলে পড়া — কোনো কিছু পরিবর্তন করার নেই — এবং 👉✏️ হলো সেই ধরনের ধাপ যা সম্পাদনা করে। যখনই কোনো ধাপে আপনাকে ভেসপারকে কিছু বলতে বলা হয়, ঠিক সেই বার্তাটি তার নিচের ব্লকেই থাকে — হুবহু কপি করে নিন। আর যে সেকশনের শিরোনামের শেষে — ‘রিড অনলি’ লেখা থাকে, সেখানে টাইপ, পেস্ট বা ক্লিক করার কিছুই নেই: এর ভেতরের কোড ইতিমধ্যেই রিপো-তে আছে এবং চলছে, যা আপনার পড়ার জন্য প্রিন্ট করা হয়েছে।

ভেসপারের দরজার উপরের বাক্যটি হলো একদম উপরেরটি। এই সপ্তাহটি যে বাক্য দিয়ে শেষ হচ্ছে, তা হলো পুরো সিজনের চতুর্থ সপ্তাহের সমাপ্তি বাক্য:

তথ্য ও পছন্দ ভিন্ন ভিন্ন জায়গায় থাকে।

২. 🧰 সেটআপ

  • কী — কোনো প্রজেক্ট দেখান বা একটি কী পেস্ট করুন, ইনস্টল করুন এবং প্রমাণ করুন যে টুলচেইনটি গ্রিন।
  • কেন — এখানে কিছুই বুট করা হয় না। প্রতিটি সার্ভার ঠিক সেই ধাপের আগেই চালু হয় যেখানে এটির প্রয়োজন, তাই যখন কোনো স্ক্রিন খোলে, আপনি জানতে পারেন কোন কমান্ডটি সেটির দায়িত্বে আছে।
  • কীভাবে — একটি স্ক্রিপ্ট যা প্রজেক্টটি তৈরি করে, একটি যা ভার্টেক্স এআই (Vertex AI) ইনস্টল করে এবং আপনাকে সেদিকে নির্দেশ করে, এবং একটি প্রিফ্লাইট যা আপনার কোড পড়ে আপনাকে বলে দেয় আপনি কোথায় আছেন।

এই ল্যাবের সবকিছু ক্লাউড শেল -এ চলে — সেখানে পাইথন, uv এবং gcloud আগে থেকেই ইনস্টল করা আছে, এবং আপনার নিজের ক্রেডেনশিয়াল ব্যবহার করে ভার্টেক্স এআই-এর মাধ্যমে জেমিনিতে প্রবেশ করা যায়। এই ল্যাবে কোথাও কোনো এপিআই কী নেই, এবং আপনার ল্যাপটপেও কিছু ইনস্টল করার প্রয়োজন নেই।

এই ল্যাব আপনাকে একটি জিনিস দিতে পারবে না: বিলিং সহ একটি গুগল ক্লাউড প্রজেক্ট। ধাপ ১ আপনাকে এমন একটি অ্যাকাউন্ট দেবে যা দিয়ে এর জন্য অর্থ প্রদান করা যাবে; ধাপ ৪ সেই অ্যাকাউন্টটিকে একটি প্রজেক্টে পরিণত করবে — অথবা প্রথম, দ্বিতীয় বা তৃতীয় সপ্তাহে আপনার তৈরি করা এজেন্ট ভ্যালি প্রজেক্টটি গ্রহণ করবে। এই ক্রমেই কাজগুলো করুন — ধাপ ৪-এর পরের প্রতিটি ধাপ সেই প্রজেক্টটি পড়বে, যেটি ধাপ ৪-এ স্থির করা হয়েছে।

১ · বিলিং সহ একটি অ্যাকাউন্ট তৈরি করুন (~১ মিনিট)

🌐 অন্য কিছু করার আগে এটি করুন। বিলিং চালু আছে এমন একটি গুগল অ্যাকাউন্ট দিয়ে সাইন ইন করুন। এই ধরনের যেকোনো অ্যাকাউন্টই চলবে। যদি আপনার অ্যাকাউন্টে তা না থাকে, তাহলে একটি ফ্রি ট্রায়াল শুরু করুন; এটি আপনাকে একটি বিলিং অ্যাকাউন্ট দেবে, যা ধাপ ৪-এর জন্য প্রয়োজন।

২ · ক্লাউড শেল খুলুন (~১ মিনিট)

🌐 গুগল ক্লাউড কনসোলের উপরে থাকা ‘Activate Cloud Shell’- এ ক্লিক করুন — এটি হেডারে থাকা টার্মিনালের মতো দেখতে আইকনটি।

Google Cloud কনসোল হেডার থেকে ক্লাউড শেল সক্রিয় করুন

🌐 ওপেন এডিটর (পেন্সিলসহ ফোল্ডার খোলার আইকন)-এ ক্লিক করুন। এটি ক্লাউড শেল এডিটরটি খুলে দেবে, যার বাম দিকে একটি ফাইল এক্সপ্লোরার থাকবে — যখনই কোনো ধাপ 👉✏️ চিহ্নিত করা থাকবে, সেখানেই আপনাকে কাজ করতে হবে।

ক্লাউড শেল এডিটর খুলুন

🌐 আজ যেকোনো সময়ে আপনাকে অনুমোদন করতে বলা হলে, চালিয়ে যাওয়ার জন্য 'অনুমোদন করুন' (Authorize) বোতামে ক্লিক করুন।

🌐 এডিটরের ভিতরে একটি টার্মিনাল খুলুন — সেটি হলো ট্যাব ১ , এবং অন্যথা বলা না থাকলে প্রতিটি 👉💻 ধাপ সেখানেই চলবে। ট্যাব ২ হলো আর্কাইভ, এবং আপনি এটি অধ্যায় ১-এ, যে ধাপটির জন্য এটি প্রয়োজন তার ঠিক আগে খুলবেন। যদি টার্মিনালটি আগে থেকেই স্ক্রিনের নীচে না থাকে, তাহলে View → Terminal দিয়ে এটি খুলুন:

ক্লাউড শেল এডিটরে একটি নতুন টার্মিনাল খুলুন।

🌐 আপনি কোন অ্যাকাউন্ট দিয়ে সাইন ইন করেছেন তা পরীক্ষা করুন — এটি ধাপ ১-এর অ্যাকাউন্টটিই হওয়া উচিত:

gcloud auth list

৩ · ল্যাবটি খুলুন (~১ মিনিট)

💻 রিপোটি ক্লোন করুন — এটি আর্কাইভ রিপো, প্রথম, দ্বিতীয় বা তৃতীয় সপ্তাহের নয়:

cd ~
git clone https://github.com/cuppibla/agent-valley-archive
cd agent-valley-archive

৪ · আপনার প্রজেক্ট তৈরি করুন — অথবা আগের সপ্তাহের প্রজেক্টটি পুনরায় ব্যবহার করুন (~২ মিনিট)

💻 একটি স্ক্রিপ্ট আপনার বিলিং অ্যাকাউন্ট খুঁজে বের করে, সেটিতে একটি প্রজেক্ট তৈরি করে এবং ক্লাউড শেলকে সেই প্রজেক্টটির দিকে নির্দেশ করে। আপনি যদি প্রথম, দ্বিতীয় বা তৃতীয় সপ্তাহের কাজ করে থাকেন, তবে এটি প্রথমে সেই এজেন্ট ভ্যালি প্রজেক্টটি খুঁজে পায় এবং সেটি পুনরায় ব্যবহার করার প্রস্তাব দেয় — আপনি হ্যাঁ বললে, দ্বিতীয় কোনো বিলযোগ্য প্রজেক্ট নিয়ে আর চিন্তা করতে হবে না:

cd ~/agent-valley-archive
./setup_project.sh

👀 আশা করুন , এটি যে বিলিং অ্যাকাউন্টটি বেছে নিয়েছে তার জন্য একটি ✓ চিহ্ন, এটি যে প্রজেক্টটি তৈরি বা পুনঃব্যবহার করেছে তার জন্য একটি ✓ এবং শেষে এইরকম কিছু থাকবে:

cd ~/agent-valley-archive
Project ready: agent-valley-4271
  Next:  ./setup_codelab.sh

agent-valley-4271 হলো আপনার প্রজেক্ট আইডি — আপনারটির সংখ্যাগুলো ভিন্ন হবে। সেখানে চারটি কাজ হয়:

বিলিং

আপনার যদি নতুনতম [YYYY-MM-DD] GDP Credit: অ্যাকাউন্ট থাকে তবে সেটি গ্রহণ করে, অন্যথায় অন্য যেকোনো খোলা বিলিং অ্যাকাউন্ট গ্রহণ করে।

প্রকল্প

আপনার ইতিমধ্যে মালিকানাধীন কোনো এজেন্ট ভ্যালি প্রজেক্ট—প্রথম সপ্তাহ, দ্বিতীয় সপ্তাহ বা তৃতীয় সপ্তাহের—খুঁজে বের করে এবং সেটি পুনরায় ব্যবহারের প্রস্তাব দেয়; অন্যথায় agent-valley-XXXX তৈরি করে এবং সেই বিলিং অ্যাকাউন্টটিকে এর সাথে লিঙ্ক করে দেয়।

নির্বাচন করে

এটি আইডিটি ~/project_id.txt এ লিখে রাখে এবং gcloud config set project চালায়, তাই এর পরে আর আপনাকে আইডিটি টাইপ করতে বলা হয় না।

অপেক্ষা

প্রকল্পটি প্রকৃতপক্ষে চালু হওয়ার অনুমতি না পাওয়া পর্যন্ত, সর্বোচ্চ দুই মিনিটের জন্য ভোটগ্রহণ চলবে।

🌐 সেই একই প্রজেক্টটি এখন কনসোলে রয়েছে — এর আইডিটি ড্যাশবোর্ডের প্রজেক্ট ইনফো কার্ডে দেখা যাচ্ছে:

কনসোল ড্যাশবোর্ডে আপনার প্রজেক্ট আইডি

৫ · ভার্টেক্স এআই ইনস্টল করুন এবং সেটির দিকে নির্দেশ করুন (~৩ মিনিট)

💻 uv ইনস্টল করুন, ADK থেকে Vertex AI-তে ডেটা পাঠানোর জন্য তিনটি ভেরিয়েবল এক্সপোর্ট করুন, এবং setup চালান:

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 হলো আইডম্পোটেন্ট — অর্থাৎ এটি যতবার খুশি নিরাপদে পুনরায় চালানো যায়। এটি ক্রমানুসারে পাঁচটি কাজ করে:

এপিআই

প্রকল্পের ধাপ ৪ নির্বাচিত হয়েছে কিনা তা পড়ে এবং এতে aiplatform.googleapis.com (Vertex AI, এবং মেমরি ব্যাংক হোস্টকারী এজেন্ট ইঞ্জিন) ও bigquery.googleapis.com (অধ্যায় ৫-এর ডেটা ওয়্যারহাউস) সক্রিয় করে।

ইউভি এনভি

uv venv .venv ফাইল তৈরি করে, অথবা আগে থেকে থাকা ফাইলটিই পুনরায় ব্যবহার করে, তারপর uv sync ঠিক uv.lock পিনগুলোর সবকিছুই ইনস্টল করে — google-adk , google-genai এবং এগুলোর নিচের সবকিছু।

সাইট

এটি site/ ফোল্ডারে npm ci চালায় — আর্কাইভটি নিজেই একটি Next.js অ্যাপ — তাই প্রথম অধ্যায়ের bash valley.sh এর আর কিছু ডাউনলোড করার থাকে না; প্যাকেজগুলো আগে থেকেই থাকলে এটি এড়িয়ে যাওয়া হয়।

.env

ল্যাব যে ভ্যারিয়েবলগুলো পড়ে, সেগুলো লিখে রাখে — GOOGLE_GENAI_USE_VERTEXAI , GOOGLE_CLOUD_PROJECT , GOOGLE_CLOUD_LOCATION , MEMORY_BANK_LOCATION এবং BQ_DATASET , এবং কোথাও কোনো API কী নেই।

মডেল চেক

জেমিনিকে একবারই আসল কল করা হয়, তাই একটি খারাপ প্রজেক্ট এখানেই, সেটআপের সময়েই ব্যর্থ হয়, পরে কোনো অধ্যায়ের ভেতরে নয়।

৬ · এটি যে সবুজ তা প্রমাণ করুন (~১ মিনিট)

💻 প্রিফ্লাইট চালান — প্রতিটি টিক একটি প্রকৃত অনুসন্ধান, কোনো অনুমান নয়:

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

তিনটি ইতিমধ্যেই টিক দেওয়া আছে। ওগুলোই আপনি শুধু পড়েন। তিনটি হলো আপনার লেখা লাইন; একটি হলো আপনার গড়া মিনার। ল্যাবের যেকোনো পর্যায়ে প্রিফ্লাইটটি আবার চালান এবং এটি আপনাকে বলে দেবে আপনি কোথায় আছেন — এটি আপনার ফাইলগুলো পড়ে, কোনো চেকলিস্ট নয়।

৩. সেশন, স্টেট, SqliteSessionService

  • কী —এক লাইন লেখো; ওয়ার্কবেঞ্চটি একবার চালু করে দেখো স্লিপটি কীভাবে ভরে যায়; তারপর টাওয়ারেও একই কাজ করো; এরপর পড়ো কেন এটি রাতভর টিকে থাকে।
  • কেন —স্মৃতি সম্পর্কে প্রথম যে বিষয়টি বুঝতে হবে তা হলো, কোনো কিছুই নিজে থেকে মনে থাকে না। কেউ তা লিখে রাখে।
  • কীভাবে — 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}}

ফাংশনটি একটি লোকাল ডিক্টে স্লিপটি তৈরি করে এবং তারপর সেটি রিটার্ন করে — এবং কলটির সাথে সাথে লোকাল ডিক্টটিও বিলুপ্ত হয়ে যায়। এটিকে যেমন আছে তেমনভাবে চালালে ভেসপার আপনার কথা পুরোপুরি শুনতে পায়: চারটি 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="*"

👉🔬 ওয়েব প্রিভিউ → প্রিভিউ ব্যবহার করে পোর্ট ৮০০০-এ ওয়ার্কবেঞ্চটি খুলুন (ক্লাউড শেলের উপরের ডানদিকে থাকা জানালার ওপর চোখের আইকনটিই হলো ওয়েব প্রিভিউ বাটন)। আর্কাইভ বেছে নিন, এবং কিছু টাইপ করার আগে বাম প্যানেলে থাকা স্টেট- এ ক্লিক করুন।

👀 খালি। এখনো কেউ কিছু বলেনি।

👉🔬 এখন এই কথাগুলো একটি মেসেজেই বলুন:

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. সে স্লিপটা থেকে এটা পড়ে। EDIT ONE ছাড়া সে আপনার কাছে এটা চাইত — সাপোর্ট লিস্টের প্রথম লাইনে, একটা কথোপকথনের ভেতরে।

দুটি রক্ষাকবচ হলো স্লিপটি, যা একটি কাগজের ফর্ম : চারটি ছোট লাইন, প্রতিটি একবার করে লেখা। এগুলো দেখতে যতটা না গুরুত্বপূর্ণ, তার চেয়েও বেশি। এগুলো ছাড়া মডেলটি আবিষ্কার করে যে সে পুরো কথোপকথনটিকে symptom পরিণত করতে পারে, এবং অধ্যায় ৩—যে অধ্যায়ের মূল কথাই হলো স্লিপটিতে তার জন্য কোনো লাইন নেই —নিঃশব্দে সত্য থাকা বন্ধ করে দেয়। একটি দোকানের কাঠামো সেই কোড দ্বারা বলবৎ হয় যা এটিকে লেখে, অথবা তা বলবৎ হয় না।

এখন টাওয়ারে যান

👉💻 আপনার ওয়ার্কবেঞ্চের কাজ শেষ — এটিকে চালু রাখুন অথবা এর ট্যাবটি বন্ধ করে দিন; ল্যাবের বাকি অংশ আর্কাইভে রয়েছে। একটি দ্বিতীয় টার্মিনাল ট্যাব খুলুন (ট্যাব ২ — আর্কাইভ) :

cd ~/agent-valley-archive
bash valley.sh

👉🖥 ওয়েব প্রিভিউ দিয়ে আর্কাইভটি খুলুন → পোর্ট পরিবর্তন করুন , 3440 টাইপ করুন, এবং 'Change and Preview'-এ ক্লিক করুন। 3440 ওয়েব প্রিভিউ-এর কুইক লিস্টে নেই — এর ডিফল্ট হলো 8080 — তাই এই একবার আপনাকে এটি নিজে হাতে সেট করতে হবে। ▶ স্টার্ট চাপুন, তারপর 'The Archive'- এ যান। আপনি এখন দরজার সামনে। যে জিজ্ঞাসা করতে এসেছে তাকে বেছে নিন — একটি নাম এবং একটি প্রজাতি — এবং ভেতরে যান।

টাওয়ারটি কেটে খোলা

👀 একটিই বিল্ডিং। ভেসপার নিচতলার ডেস্কে বসে আছে, এবং সেখানেই আপনাকে কথা বলতে হবে। তার উপরে: প্রথম তলা, দ্বিতীয় তলা, তৃতীয় তলা, এবং একটি অন্ধকার চিলেকোঠা। চারটি ধাপ হলো চারটি তলা। আপনার পরিচিতজন নিচতলায় বসে আছে, কারণ আপনি ততটুকুই উঠেছেন। ড্রয়িংটির পাশের স্তম্ভটি স্পষ্ট করে বলছে এই মুহূর্তে প্রতিটি তলায় কী আছে — সূচনার টেবিলের মতোই একই চারটি দোকান, একই ক্রমে। ডানদিকের কোনোটিই বাটন নয়।

👉🖥 কথোপকথনের নিচের বক্সে ঠিক এই চারটি জিনিস — এই হুবহু বার্তাটি — পেস্ট করুন এবং এন্টার চাপুন:

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 যা লিখেছে তাই আঁকছে, এবং ল্যাবের বাকি সময়টা জুড়ে এটা এটাই করতে থাকবে।

সে রাতারাতি মনে করতে পারে — শুধুমাত্র পঠনযোগ্য

আপনি যা লিখেছেন, এবং তা কী

লাইন

এটা কি

যেখানে এটি বাস করে

tool_context.state[CASE] = case

সেশন স্টেট — কোড দ্বারা লিখিত, নির্দেশনা দ্বারা পঠিত

ডেস্ক · session.state

SqliteSessionService(DB) — আপনি শুধু এটি পড়েছেন

সেশন স্টোর — ফাইলের মধ্যে একটি ভিজিট, কোনো প্রসেসের মধ্যে নয়।

প্রথম তলা · archive.db

এই ধাপটি একবার পরিদর্শনের পর বন্ধ হওয়ার সময় পর্যন্ত টিকে থাকে। পরেরবার এটি আর টিকে থাকে না।

৪. ব্যবহারকারীর অবস্থা

  • কী —এক মাস পরে ফিরে এসে দেখি চিরকুটটা নেই। মাত্র পাঁচটি অক্ষর, আর এটা আপনাকে অনুসরণ করে।
  • কারণ —একটি সেশন হলো একটি সাক্ষাৎ। পরবর্তী সাক্ষাৎটি একটি নতুন সেশন, এবং এটি আগেরটি কখনো দেখেনি।
  • কিভাবে — state.py ফাইলে পাঁচটি অক্ষর ব্যবহার করে।

এক মাস পর। আপনার সাথে পরিচিত হয়ে ভালো লাগলো।

এখানে চাপুন

👉🖥 এক মাস পরে 🗓 চাপুন। এটি একই ভিজিটরের জন্য একটি নতুন সেশন — ঠিক যেভাবে তৃতীয় সপ্তাহে নতুন কথোপকথন শুরু করা হয়েছিল।

আর্কাইভে আপনাকে স্বাগতম। আজ আমি আপনাকে কীভাবে সাহায্য করতে পারি?

👉🖥 টাওয়ারটির দিকে তাকান। প্রথম তলায় আরেকটি বই আছে — এইবারের সফরে। দ্বিতীয় তলার ড্রয়ারটি খালি। ডেস্কের ওপরের চিরকুটটি ফাঁকা।

প্রথম তলায় আরেকটি বই; ড্রয়ারটি খালি; চিরকুটটি ফাঁকা।

👀 কারণ: স্লিপটি সেশন স্টেটে লেখা হয়, এবং একটি সেশন হলো এই ভিজিটটি । ভিজিট পরিবর্তন করলে, সেশনও পরিবর্তিত হয়। কোনো কিছুই স্থানান্তরিত হয়নি কারণ সে ব্যাপারে কিছু বলা হয়নি। শেষ ভিজিটটি এখনও ফাইলে আছে — অর্থাৎ ফ্লোর ওয়ানের প্রথম বইতে — কিন্তু স্লিপটি এমন একটি কী-এর অধীনে লেখা হয়েছিল যা শুধুমাত্র একটি ভিজিটের জন্য কার্যকর থাকে।

দ্বিতীয় সম্পাদনা

👉✏️ 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.

👉🖥 তারপর এক মাস পর আবার 🗓 চাপুন — আগের মতোই একই বাটনটি।

ড্রয়ারটি, যার উপর একটি নাম লেখা আছে

👀 দোতলার ড্রয়ারটিতে আপনার নাম লেখা আছে, এবং চারটি ফিল্ড এর ভেতরে রয়েছে। ডেস্কের ওপরের স্লিপটিতেও সেই একই চারটি লাইন আছে — আপনি ঢোকার সময়ই এটি ড্রয়ার থেকে বের করা হয়েছিল। আপনার পরিচিত সত্তাটি এক তলা ওপরে উঠে গেছে। একই চাবি, প্রতিবার পরিদর্শনে, একটিই মান: এটাই হলো প্রিফিক্সটির কাজ।

উপসর্গটি হলো একটি জীবনকাল।

উপসর্গ

এটি কতক্ষণ বেঁচে থাকে

কোন তলা

(কিছুই না)

এই সফর

ডেস্ক

user:

এই পরিদর্শকের প্রতিটি পরিদর্শন

দ্বিতীয় তলা, তোমার ড্রয়ার

app:

সবাই, চিরকাল

চতুর্থ তলার অংশ

temp:

শুধু এই মোড়েই — সিঁড়ি বেয়ে কখনোই ওঠে না।

—

একটি টেবিল, চারটি ড্রয়ার। এই অধ্যায়ে সেগুলোর একটি নিয়ে আলোচনা করা হয়েছে।

সে এখন তোমাকে চেনে।

👉🖥 এই সাক্ষাৎটি একদম নতুন এবং আপনি এখনও এ বিষয়ে কিছুই বলেননি। তাকে জিজ্ঞাসা করুন:

What did I bring in last time?

👀 আমার কাছে লেখা আছে যে আপনি একটি লণ্ঠন এনেছিলেন, কেস k7f2। এটিতে q7 লেখা আছে এবং আপনি উল্লেখ করেছেন যে এটি রাতে নিভে যায়। ওই কথাগুলোর প্রতিটিই ড্রয়ার থেকে এসেছে, কথোপকথনটি নয় — এই ভিজিটটি মাত্র একটি মেসেজের পুরনো, এবং মেসেজটির কোনো নাম ছিল না। এটাই হলো ইউজার-লেভেল স্টেটের কাজ: ভিজিটর কোনো কথা বলার আগেই, তার প্রতিটি ভিজিটের সময় একটি user: কী সেখানে থাকে।

ড্রয়ারটি স্লিপের চারটি লাইনই ধরে রাখে, আর কিছুই না। আপনি যা বলেন, যার জন্য স্লিপে কোনো লাইন নেই—আপনি কী চেষ্টা করেছিলেন, কেন আপনি পরোয়া করেন, আপনি একটি নতুন চান কি না—তার যাওয়ার কোনো জায়গা নেই, এবং এক মাস পরে তা হারিয়ে যায়। ড্রয়ারটি মাস পার করে যায়। একটি বাক্য তা করে না । অধ্যায় ৩ এই বিষয় নিয়েই।

আপনি যা লিখেছেন, এবং তা কী

লাইন

এটা কি

যেখানে এটি বাস করে

CASE = "user:case"

একটি প্রিফিক্স হলো একটি লাইফটাইম — একই কী, যা ভিজিটের পরিবর্তে ভিজিটরের জন্য স্কোপ করা থাকে।

দ্বিতীয় তলা · user: অবস্থা

এই ধাপটি একজন আগন্তুকের কাছে পৌঁছায়। ড্রয়ারটি আপনাকে অনুসরণ করে; কিন্তু কথোপকথনটি তবুও এগোয় না।

৫. ইনমেমোরিমেমোরিসার্ভিস

  • কী —দিনটিকে নথিভুক্তকারী লাইনটি পড়ুন, প্রশ্নকারী লাইনটি লিখুন; তারপর একটি সমাপ্তির সময় এবং একটি প্রশ্ন।
  • কেন —চিঠিটি একটি ফর্ম। একটি ফর্মে আপনি যে লাইনগুলো দিয়েছেন, সেগুলোই থাকে। মানুষ লাইন ধরে কথা বলে না—এবং তারা যা বলেছে তা লিখে রাখাটা মনে রাখার অর্ধেক মাত্র।
  • কীভাবে — file পড়ুন; recall একটি লাইন; তারপর একটি 🌙, একটি 🗓, একটি প্রশ্ন।

বন্ধ করার সময় হয়ে গেছে। সে তাকগুলোর পাশ দিয়ে হেঁটে ফিরে যায়।

বাড়ি ফেরার পথে সে যা করে — শুধু পড়ুন

ফাইলটি মনে রাখা হয়নি

ওই লাইনটি দিনটিকে তাকের উপর তুলে রাখে। এটি সেখান থেকে কিছুই সরিয়ে নেয় না। যদি কেউ না দেখে, তবে তাসে ভরা একটি তাক ঠিক ততটাই সাহায্য করে যতটা একটি খালি তাক করে — এবং যেভাবে কোডটি পাঠানো হয়, তাতে কেউ দেখেও না: recall , যে নোডটি প্রতিটি প্রশ্নে ভেসপারের আগে চলে, সেটি টাওয়ারকে কিছুই জিজ্ঞাসা করে না। যে স্টোরেজ আপনি সঠিক মুহূর্তে মনে করতে পারেন না, তা মেমরি নয়; এটি একটি লগ। দেখাটা হলো অন্য অর্ধেক, এবং সেটি লেখার দায়িত্ব আপনার।

তৃতীয় সম্পাদনা — জিজ্ঞাসা

👉📖 archive/agent.py ফাইলে, 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 এর ভেতরের একটি ডিকশনারি, এবং এটি ঠিক ততক্ষণই টিকে থাকে যতক্ষণ ওই প্রসেসটি টিকে থাকে। আজ রাতে টাওয়ারটি বন্ধ করুন এবং আগামীকাল খুলুন: বইগুলো এক নম্বর তলায়, ড্রয়ারটি দুই নম্বর তলায়, এবং তিন নম্বর তলা অন্ধকার। কেসটি ফাইলের মধ্যে টিকে ছিল। আপনি যা বলেছিলেন তা প্রসেসের মধ্যে বেঁচে ছিল, এবং সেই প্রসেসটি এখন আর নেই।

অধ্যবসায় স্মৃতি নয়।

বর্তমান অবস্থায় তৃতীয় তলার দুটি সমস্যা রয়েছে: এটি এখনও নির্মাণাধীন, এবং এটি আপনার নিজের কথায় লেখা — একটি উদ্ধৃতি, কোনো বাস্তব ঘটনা নয়। পরবর্তী অধ্যায়ে কোনো কোড ছাড়াই এই দুটি সমস্যারই সমাধান করা হয়েছে।

আপনি যা পড়েছেন, যা লিখেছেন, এবং তা কী

লাইন

এটা কি

যেখানে এটি বাস করে

await ctx.add_events_to_memory(events, custom_metadata=FILING) — আপনি শুধু এটি পড়েন

সঠিক নীতি — পুরো দিন, বন্ধের সময়

তৃতীয় তলা · স্মৃতি পরিষেবা

found = await ctx.search_memory(query) — তৃতীয় সম্পাদনা

প্রত্যাহার নীতি — তার উত্তর দেওয়ার আগে প্রতিটি প্রশ্ন।

recall() → ctx.state["recalled"]

এই ধাপটি বলা কথাটিতে পৌঁছায় — এবং আপনার নিজের ভাষায়, সেটিকে একটি প্রসেসের ভেতরের একটি ডিকশনারিতে সংরক্ষণ করে। এর উভয় অংশই ভুল, এবং পরবর্তী অধ্যায়ে কোনো কোড ছাড়াই উভয়টিই ঠিক করা হয়েছে।

৬. মেমোরি ব্যাংক

  • কী — একটি স্ক্রিপ্ট একটি স্মৃতিভাণ্ডার তৈরি করে; আপনার কাছে আগে থেকে থাকা দুটি লাইন তাতে তথ্য লিখতে শুরু করে এবং সেগুলো আবার পড়ে শোনায়।
  • কারণ —একটি মেমোরি সার্ভিস হলো একটি সার্ভিস । আর্কাইভটি কোথায় থাকবে, তা একটি ডেপ্লয়মেন্ট সংক্রান্ত সিদ্ধান্ত, এজেন্টের নয়—আপনি এজেন্টকে স্পর্শ না করেই এটি পরিবর্তন করতে পারেন।
  • কীভাবে করবেন — make_tower.py চালান, তিনটি বাক্য বলুন, 🌙 চাপুন, 🗓 চাপুন, একটি প্রশ্ন করুন। তারপর লেখা এবং পড়াটি শুধু দেখার জন্য দেখতে পাবেন।

টাওয়ারটি তৈরি করুন

অধ্যায় ৩-এর তৃতীয় তলাটি ছিল bash valley.sh ভেতরে থাকা একটি পাইথন ডিকশনারি। মেমোরি ব্যাংক হলো সেই তলার পরিচালিত সংস্করণ: এটি আপনার প্রোজেক্টের একটি এজেন্ট ইঞ্জিন ইনস্ট্যান্সে থাকে, এটি আপনার বাক্যগুলোর পরিবর্তে দিনের পর দিন থেকে সংগৃহীত তথ্য সংরক্ষণ করে, এবং এটি সাধারণ শব্দের পরিবর্তে অর্থের মাধ্যমে তথ্য খুঁজে বের করে। এটি ব্যবহার করার জন্য আপনাকে কোনো এজেন্ট ইঞ্জিনে ভেসপার ডেপ্লয় করতে হবে না — যে ইনস্ট্যান্সে কিছুই ডেপ্লয় করা নেই, সেটিও একটি মেমোরি ব্যাংক হোস্ট করে, এবং স্ক্রিপ্টটি ঠিক সেটাই তৈরি করে: এমন একটি টাওয়ার যেখানে কেউ বাস করে না, যার একমাত্র কাজ হলো কার্ডগুলো সংরক্ষণ করা।

👉💻 ট্যাব ১- এ (প্রায় এক মিনিট):

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 it here

👉🖥 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 — memories.delete , one card at a time

floor two, the drawer

user:case , the slip that follows this visitor from visit to visit

cleared

the desk, the slip

this visit's copy of the case

cleared

floor one, the books

the visits themselves — one session in archive.db per visit, every message of it

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

make_tower.py → AGENT_ENGINE in .env — built, not edited, nothing restarted

the memory service, as a service: a deployment decision, outside the agent

Vertex AI Memory Bank on an Agent Engine

tower_service(engine) — you only read it

the same Runner(memory_service=...) slot, a different home

archive/service.py

"allowed_topics": TOPICS in FILING — read only

what may be kept, decided at write time, not in the prompt

archive/topics.py + the tower's own config

🔥 → memories.delete — pressed, not written

forgetting is an application action; ADK's memory API has no delete

archive/tower.py

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

ঐচ্ছিক।

  • 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

কীভাবে

in this chapter

সাদৃশ্য

where should I look?

embed the question, VECTOR_SEARCH

season_search — when the visitor brings no mark

traversal

what is connected to that?

MATCH a path through a property graph

season_known_issue — one fixed MATCH

প্রজন্ম

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, and the last line is the path

👀 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

bq load with explicit schemas — run, not written

a warehouse: columns with names and types

archive.visitors / items / asks / fixes / seasons

ML.GENERATE_EMBEDDING + VECTOR_SEARCH

the similarity step — where to look

archive.ask_embeddings , the embedder model

CREATE PROPERTY GRAPH — no row moved

the traversal step — what is connected; tables become nodes and arrows

archive.season_graph

GQL in archive/season/__init__.py — one parameter

the only walk she may take; the path is the citation

season_known_issue(mark)

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

এক

tool_context.state[CASE] = case — an edit

a slip that exists at all: state is written by code

দুই

CASE = "user:case" — an edit

a drawer that follows the visitor

the filing

await ctx.add_events_to_memory(..., custom_metadata=FILING) — read, not written

what was said, kept

তিন

found = await ctx.search_memory(query) — an edit

what was kept, asked for — filed is not remembered

the tower

make_tower.py , then AGENT_ENGINE in .env — built and connected by one line, nothing restarted

a tower that distills, and survives the night

নিয়মগুলো

allowed_topics in FILING — read, not written

only what should be kept

the season

season.sh — loaded, embedded in place, a graph laid over it, walked with one MATCH

everyone else's visits, by meaning and by connection

What each piece replaced

ADK 2

what you would write instead

what that costs you

session.state + one tool, kept by SqliteSessionService

a dict beside the chat loop, pickled to disk yourself

the model cannot read it, and you write the resume and the locking

the user: prefix

a users table and a join on every turn

deciding where a key belongs, every single time

add_events_to_memory + search_memory in a function node

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

VertexAiMemoryBankService + allowed_topics

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

VECTOR_SEARCH + one fixed GQL MATCH

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

  1. Will it matter tomorrow? No → keep nothing. It lives and dies in the conversation.
  2. Can a function check it? Yes → the drawer. Code writes it, every turn reads it, and no sentence can change it.
  3. 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.