با ADK، AlloyDB و Vertex AI Memory Bank یک هماهنگ‌کننده زنجیره تأمین بسازید

۱. مرور کلی

در این آزمایشگاه کد، شما یک عامل هماهنگ‌کننده زنجیره تأمین خواهید ساخت. این برنامه به کاربران امکان می‌دهد تا با استفاده از زبان طبیعی، موجودی را تجزیه و تحلیل کنند، لجستیک را پیگیری کنند و خطرات زنجیره تأمین را مدیریت کنند.

ما از کیت توسعه عامل (ADK) گوگل برای ساخت یک معماری چندعامله استفاده خواهیم کرد که زمینه را حفظ می‌کند، تنظیمات کاربر را از طریق بانک حافظه هوش مصنوعی Vertex به خاطر می‌سپارد و از طریق جعبه ابزار MCP با مجموعه داده‌های عظیم ذخیره شده در AlloyDB تعامل دارد.

آنچه خواهید ساخت

۹۱e۸e۵۳۵۵۶ac۱۹۶۶.jpeg

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

عامل هماهنگ‌کننده سراسری: عامل ریشه‌ای که جریان گفتگو و واگذاری اختیار را مدیریت می‌کند.

نمایندگان متخصص: یک "متخصص موجودی" و "مدیر لجستیک" برای وظایف خاص حوزه.

یکپارچه‌سازی حافظه: حافظه کوتاه‌مدت جلسه و حافظه بلندمدت با استفاده از بانک حافظه هوش مصنوعی Vertex.

رابط کاربری روایتی: یک رابط وب که فرآیند استدلال عامل را تجسم می‌کند (Trace Context).

آنچه یاد خواهید گرفت

  • نحوه استفاده از Google ADK برای ایجاد نمایندگان و زیر نمایندگان تخصصی.
  • نحوه ادغام بانک حافظه هوش مصنوعی Vertex برای حافظه بلندمدت عامل.
  • نحوه استفاده از جعبه ابزار MCP برای اتصال عامل‌ها به ابزارهای داده AlloyDB.
  • نحوه پیاده‌سازی ADK Callbacks برای ردیابی و تجسم استدلال عامل.
  • نحوه استقرار راهکار با استفاده از Cloud Run یا اجرای آن به صورت محلی.

معماری

پشته فناوری

  1. AlloyDB برای PostgreSQL: به عنوان پایگاه داده عملیاتی با کارایی بالا که بیش از ۵۰،۰۰۰ رکورد زنجیره تأمین را در خود جای داده است، عمل می‌کند. این پایگاه داده، جستجو و بازیابی برداری را پشتیبانی می‌کند.
  2. جعبه ابزار MCP برای پایگاه‌های داده: به عنوان "Orchestration Maestro" عمل می‌کند و داده‌های AlloyDB را به عنوان ابزارهای اجرایی که عامل‌ها می‌توانند فراخوانی کنند، در معرض نمایش قرار می‌دهد.
  3. کیت توسعه عامل (ADK): چارچوبی که برای تعریف عامل‌ها، دستورالعمل‌ها و ابزارها استفاده می‌شود.
  4. بانک حافظه هوش مصنوعی ورتکس: حافظه بلندمدت را فراهم می‌کند و به عامل اجازه می‌دهد تا تنظیمات کاربر و تعاملات گذشته را در طول جلسات به خاطر بیاورد.
  5. سرویس جلسه هوش مصنوعی ورتکس: زمینه مکالمه کوتاه مدت را مدیریت می‌کند.

جریان

  1. پرسش کاربر: کاربر سوالی می‌پرسد (مثلاً «موجودی بستنی ممتاز را بررسی کنید»).
  2. بررسی حافظه: هماهنگ‌کننده، بانک حافظه را برای اطلاعات مرتبط گذشته بررسی می‌کند (مثلاً «کاربر مدیر منطقه‌ای برای EMEA است»).
  3. تفویض اختیار: هماهنگ‌کننده وظیفه را به متخصص موجودی واگذار می‌کند.
  4. اجرای ابزار: متخصص از ابزارهای ارائه شده توسط جعبه ابزار MCP برای پرس و جو از AlloyDB استفاده می‌کند.
  5. پاسخ: عامل داده‌ها را پردازش می‌کند و یک جدول با فرمت Markdown برمی‌گرداند.
  6. ذخیره‌سازی حافظه: تعاملات مهم در بانک حافظه ذخیره می‌شوند.

الزامات

  • یک مرورگر، مانند کروم یا فایرفاکس .
  • یک پروژه گوگل کلود با قابلیت پرداخت.
  • آشنایی اولیه با SQL و پایتون.

۲. قبل از شروع

ایجاد یک پروژه

  1. در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید.
  2. مطمئن شوید که صورتحساب برای پروژه ابری شما فعال است. یاد بگیرید که چگونه بررسی کنید که آیا صورتحساب در یک پروژه فعال است یا خیر .
  1. شما از Cloud Shell ، یک محیط خط فرمان که در Google Cloud اجرا می‌شود، استفاده خواهید کرد. روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید.

تصویر دکمه فعال کردن Cloud Shell

  1. پس از اتصال به Cloud Shell، با استفاده از دستور زیر بررسی می‌کنید که آیا از قبل احراز هویت شده‌اید و پروژه روی شناسه پروژه شما تنظیم شده است یا خیر:
gcloud auth list
  1. دستور زیر را در Cloud Shell اجرا کنید تا تأیید شود که دستور gcloud از پروژه شما اطلاع دارد.
gcloud config list project
  1. اگر پروژه شما تنظیم نشده است، از دستور زیر برای تنظیم آن استفاده کنید:
gcloud config set project <YOUR_PROJECT_ID>
  1. فعال کردن API های مورد نیاز: روی لینک کلیک کنید و API ها را فعال کنید.

به عنوان یک روش جایگزین، می‌توانید از دستور gcloud برای این کار استفاده کنید. برای مشاهده دستورات و نحوه استفاده از gcloud به مستندات آن مراجعه کنید.

اشکالات و عیب‌یابی

سندرم «پروژه ارواح»

شما gcloud config set project اجرا کردید، اما در واقع در رابط کاربری کنسول به پروژه‌ی دیگری نگاه می‌کنید. شناسه‌ی پروژه را در منوی کشویی بالا سمت چپ بررسی کنید!

سنگر بیلینگ

شما پروژه را فعال کردید، اما حساب صورتحساب را فراموش کردید. AlloyDB یک موتور با کارایی بالا است؛ اگر "مخزن بنزین" (صورتحساب) خالی باشد، روشن نمی‌شود.

تأخیر انتشار API

شما روی «فعال کردن APIها» کلیک کرده‌اید، اما خط فرمان هنوز می‌گوید Service Not Enabled . ۶۰ ثانیه به آن فرصت دهید. ابر به یک لحظه نیاز دارد تا نورون‌های خود را بیدار کند.

کواگ‌های سهمیه‌ای

اگر از یک حساب آزمایشی کاملاً جدید استفاده می‌کنید، ممکن است به سهمیه منطقه‌ای برای نمونه‌های AlloyDB برسید. اگر us-central1 با شکست مواجه شد، us-east1 امتحان کنید.

نماینده خدمات "پنهان"

گاهی اوقات به طور خودکار نقش aiplatform.user به عامل سرویس AlloyDB اعطا نمی‌شود . اگر کوئری‌های SQL شما بعداً نتوانند با Gemini ارتباط برقرار کنند، معمولاً مشکل از همین است.

۳. راه‌اندازی پایگاه داده

در قلب برنامه ما ، AlloyDB برای PostgreSQL قرار دارد. ما از قابلیت‌های برداری قدرتمند و موتور ستونی یکپارچه آن برای ایجاد جاسازی‌ها برای بیش از ۵۰،۰۰۰ رکورد SCM استفاده کردیم. این امر امکان تجزیه و تحلیل برداری تقریباً بلادرنگ را فراهم می‌کند و به نمایندگان ما اجازه می‌دهد تا ناهنجاری‌های موجودی یا خطرات لجستیکی را در مجموعه داده‌های عظیم در عرض چند میلی‌ثانیه شناسایی کنند.

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

بیایید یک کلاستر، نمونه و جدول AlloyDB ایجاد کنیم که مجموعه داده‌های آزمایشی در آن بارگذاری شوند.

  1. روی دکمه کلیک کنید یا لینک زیر را در مرورگر خود که کاربر Google Cloud Console در آن وارد شده است، کپی کنید.

روش دیگر این است که می‌توانید از پروژه خود که در آن حساب صورتحساب را فعال کرده‌اید، به Cloud Shell Terminal بروید و مخزن github را کلون کنید و با استفاده از دستورات زیر به پروژه بروید:

git clone https://github.com/AbiramiSukumaran/easy-alloydb-setup

cd easy-alloydb-setup
  1. پس از اتمام این مرحله، مخزن در ویرایشگر پوسته ابری محلی شما کلون می‌شود و می‌توانید دستور زیر را از پوشه پروژه اجرا کنید (مهم است که مطمئن شوید در دایرکتوری پروژه هستید):
sh run.sh
  1. حالا از رابط کاربری استفاده کنید (با کلیک روی لینک در ترمینال یا کلیک روی لینک «پیش‌نمایش در وب» در ترمینال).
  2. برای شروع، اطلاعات مربوط به شناسه پروژه، نام کلاستر و نمونه را وارد کنید.
  3. در حالی که کنده‌ها در حال حرکت هستند، یک قهوه بنوشید و می‌توانید در اینجا در مورد چگونگی انجام این کار در پشت صحنه بخوانید.

اشکالات و عیب‌یابی

مشکل «صبر»

خوشه‌های پایگاه داده زیرساخت‌های سنگینی هستند. اگر صفحه را رفرش کنید یا جلسه Cloud Shell را به دلیل «گیر کردن» از بین ببرید، ممکن است در نهایت با یک نمونه «شبح» مواجه شوید که تا حدی آماده شده و حذف آن بدون مداخله دستی غیرممکن است.

عدم تطابق منطقه

اگر APIهای خود را در us-central1 فعال کرده‌اید اما سعی می‌کنید کلاستر را در asia-south1 آماده‌سازی کنید، ممکن است با مشکلات سهمیه‌بندی یا تأخیر در مجوز حساب سرویس مواجه شوید. در کل آزمایش به یک منطقه پایبند باشید!

خوشه‌های زامبی

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

مهلت زمانی پوسته ابری

اگر زمان استراحت قهوه شما 30 دقیقه طول بکشد، ممکن است Cloud Shell به حالت خواب برود و فرآیند sh run.sh قطع کند. تب را فعال نگه دارید!

۴. تأمین طرحواره

پس از اجرای کلاستر و نمونه AlloyDB، به ویرایشگر SQL در AlloyDB Studio بروید تا افزونه‌های هوش مصنوعی را فعال کرده و طرحواره را آماده کنید.

1e3ac974b18a8113.png

ممکن است لازم باشد منتظر بمانید تا نمونه شما به طور کامل ایجاد شود. پس از اتمام این کار، با استفاده از اعتبارنامه‌هایی که هنگام ایجاد خوشه ایجاد کرده‌اید، وارد AlloyDB شوید. از داده‌های زیر برای تأیید اعتبار در PostgreSQL استفاده کنید:

  • نام کاربری: " postgres "
  • پایگاه داده: " postgres "
  • رمز عبور: " alloydb " (یا هر چیزی که در زمان ایجاد تعیین کرده‌اید)

پس از اینکه با موفقیت در AlloyDB Studio احراز هویت شدید، دستورات SQL در ویرایشگر وارد می‌شوند. می‌توانید با استفاده از علامت + در سمت راست آخرین پنجره، چندین پنجره ویرایشگر اضافه کنید.

۲۸cb9a8b6aa0789f.png

شما می‌توانید دستورات AlloyDB را در پنجره‌های ویرایشگر وارد کنید و در صورت لزوم از گزینه‌های Run، Format و Clear استفاده کنید.

فعال کردن افزونه‌ها

برای ساخت این برنامه، از افزونه‌های pgvector و google_ml_integration استفاده خواهیم کرد. افزونه pgvector به شما امکان ذخیره و جستجوی جاسازی‌های برداری را می‌دهد. افزونه google_ml_integration توابعی را ارائه می‌دهد که برای دسترسی به نقاط پایانی پیش‌بینی هوش مصنوعی Vertex برای دریافت پیش‌بینی‌ها در SQL استفاده می‌کنید. این افزونه‌ها را با اجرای DDL های زیر فعال کنید :

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

ایجاد یک جدول

شما می‌توانید با استفاده از دستور DDL زیر در AlloyDB Studio یک جدول ایجاد کنید:

DROP TABLE IF EXISTS shipments;
DROP TABLE IF EXISTS products;

-- 1. Product Inventory Table

CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
category VARCHAR(100),
stock_level INTEGER,
distribution_center VARCHAR(100),
region VARCHAR(50),
embedding vector(768),
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 2. Logistics & Shipments
CREATE TABLE shipments (
shipment_id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES products(id),
status VARCHAR(50), -- 'In Transit', 'Delayed', 'Delivered', 'Pending'
estimated_arrival TIMESTAMP,
route_efficiency_score DECIMAL(3, 2)
);

ستون embedding امکان ذخیره‌سازی مقادیر برداری برخی از فیلدهای متنی را فراهم می‌کند.

دریافت داده

برای درج گروهی ۵۰۰۰۰ رکورد در جدول products، مجموعه دستورات SQL زیر را اجرا کنید:

-- We use a CROSS JOIN pattern with realistic naming segments to create meaningful variety
DO $$
DECLARE
brand_names TEXT[] := ARRAY['Artisan', 'Nature', 'Elite', 'Pure', 'Global', 'Eco', 'Velocity', 'Heritage', 'Aura', 'Summit'];
product_types TEXT[] := ARRAY['Ice Cream', 'Body Wash', 'Laundry Detergent', 'Shampoo', 'Mayonnaise', 'Deodorant', 'Tea', 'Soup', 'Face Cream', 'Soap'];
variants TEXT[] := ARRAY['Classic', 'Gold', 'Premium', 'Eco-Friendly', 'Organic', 'Night-Repair', 'Extra-Fresh', 'Zero-Sugar', 'Sensitive', 'Maximum-Strength'];
regions TEXT[] := ARRAY['EMEA', 'APAC', 'LATAM', 'NAMER'];
dcs TEXT[] := ARRAY['London-Hub', 'Mumbai-Central', 'Sao-Paulo-Logistics', 'Singapore-Port', 'Rotterdam-Gate', 'New-York-DC'];
BEGIN
INSERT INTO products (name, category, stock_level, distribution_center, region)
SELECT
b || ' ' || v || ' ' || t as name,
CASE
WHEN t IN ('Ice Cream', 'Mayonnaise', 'Tea', 'Soup') THEN 'Food & Refreshment'
WHEN t IN ('Body Wash', 'Shampoo', 'Deodorant', 'Face Cream', 'Soap') THEN 'Personal Care'
ELSE 'Home Care'
END as category,
floor(random() * 20000 + 100)::int as stock_level,
dcs[floor(random() * 6 + 1)] as distribution_center,
regions[floor(random() * 4 + 1)] as region
FROM
unnest(brand_names) b,
unnest(variants) v,
unnest(product_types) t,
generate_series(1, 50); -- 10 * 10 * 10 * 50 = 50,000 records
END $$;

بیایید رکوردهای خاص نسخه آزمایشی را وارد کنیم تا از پاسخ‌های قابل پیش‌بینی برای سوالات مربوط به سبک اجرایی اطمینان حاصل شود.

-- These ensure you have predictable answers for specific "Executive" questions
INSERT INTO products (name, category, stock_level, distribution_center, region) VALUES
('Magnum Ultra Gold Limited Edition', 'Food & Refreshment', 45, 'Rotterdam-Gate', 'EMEA'),
('Dove Pro-Health Deep Moisture', 'Personal Care', 12000, 'Mumbai-Central', 'APAC'),
('Hellmanns Real Organic Mayonnaise', 'Food & Refreshment', 8000, 'London-Hub', 'EMEA');

درج داده‌های حمل و نقل

-- Shipments Generation (More shipments than products)
INSERT INTO shipments (product_id, status, estimated_arrival, route_efficiency_score)
SELECT
id,
CASE
WHEN random() > 0.8 THEN 'Delayed'
WHEN random() > 0.4 THEN 'In Transit'
ELSE 'Delivered'
END,
NOW() + (random() * 10 || ' days')::interval,
(random() * 0.5 + 0.5)::decimal(3,2)
FROM products
WHERE random() > 0.3; -- Create shipments for ~70% of products


-- Add duplicate shipments for some products to show complex logistics
INSERT INTO shipments (product_id, status, estimated_arrival, route_efficiency_score)
SELECT id, 'In Transit', NOW() + INTERVAL '12 days', 0.88
FROM products
LIMIT 5000;

اعطای مجوز

برای اعطای مجوز اجرا به تابع "embedding"، دستور زیر را اجرا کنید:

GRANT EXECUTE ON FUNCTION embedding TO postgres;

اعطای نقش کاربری Vertex AI به حساب سرویس AlloyDB

از کنسول Google Cloud IAM ، به حساب سرویس AlloyDB (که به این شکل است: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) دسترسی به نقش "Vertex AI User" را بدهید. PROJECT_NUMBER شماره پروژه شما را خواهد داشت.

همچنین می‌توانید دستور زیر را از ترمینال Cloud Shell اجرا کنید:

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

ایجاد جاسازی‌ها

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

WITH
 rows_to_update AS (
 SELECT
   id
 FROM
   products
 WHERE
   embedding IS NULL
 LIMIT
   5000 )
UPDATE
 products
SET
 embedding = ai.embedding('text-embedding-005', name || ' ' || category || ' ' || distribution_center || ' ' || region)::vector
FROM
 rows_to_update
WHERE
 products.id = rows_to_update.id
 AND embedding IS null;

در این دستور بالا، ما محدودیت را روی ۵۰۰۰ تنظیم کرده‌ایم، بنابراین مطمئن شوید که آن را بارها و بارها اجرا می‌کنید تا هیچ ردیفی در جدول با ستون NULL وجود نداشته باشد.

اشکالات و عیب‌یابی

حلقه‌ی «فراموشی رمز عبور»

اگر از تنظیمات «یک کلیک» استفاده کرده‌اید و رمز عبور خود را به خاطر نمی‌آورید، به صفحه اطلاعات اولیه Instance در کنسول بروید و برای تنظیم مجدد رمز عبور postgres روی «ویرایش» کلیک کنید.

خطای "افزونه یافت نشد"

اگر CREATE EXTENSION با شکست مواجه شود، اغلب به این دلیل است که نمونه هنوز از زمان آماده‌سازی اولیه در حالت "Maintenance" یا "Updating" است. بررسی کنید که آیا مرحله ایجاد نمونه کامل شده است یا خیر و در صورت نیاز چند ثانیه صبر کنید.

شکاف انتشار IAM

شما دستور gcloud IAM را اجرا کردید، اما CALL SQL همچنان با خطای مجوز مواجه می‌شود. تغییرات IAM ممکن است کمی طول بکشد تا در ستون فقرات گوگل منتشر شوند . نفس عمیقی بکشید.

عدم تطابق ابعاد برداری

جدول items روی VECTOR(768) تنظیم شده است. اگر بعداً سعی کنید از مدل متفاوتی (مانند مدل 1536-dim) استفاده کنید، درج‌های شما از بین می‌روند. به text-embedding-005 پایبند باشید.

شناسه پروژه، غلط املایی

در فراخوانی create_model ، اگر براکت‌ها را « » بگذارید یا شناسه پروژه خود را اشتباه تایپ کنید، ثبت مدل موفقیت‌آمیز به نظر می‌رسد اما در اولین پرس‌وجوی واقعی با شکست مواجه می‌شود. رشته خود را دوباره بررسی کنید!

۵. ابزارها و تنظیمات جعبه ابزار

جعبه ابزار MCP برای پایگاه‌های داده، یک سرور MCP متن‌باز برای پایگاه‌های داده است. این جعبه ابزار با مدیریت پیچیدگی‌هایی مانند ادغام اتصال، احراز هویت و موارد دیگر ، شما را قادر می‌سازد تا ابزارها را آسان‌تر، سریع‌تر و ایمن‌تر توسعه دهید. جعبه ابزار به شما کمک می‌کند تا ابزارهای Gen AI بسازید که به عامل‌های شما اجازه می‌دهد به داده‌های پایگاه داده شما دسترسی داشته باشند.

ما از جعبه ابزار Model Context Protocol (MCP) برای پایگاه‌های داده به عنوان "رسانا" استفاده می‌کنیم. این جعبه ابزار به عنوان یک میان‌افزار استاندارد بین عامل‌های ما و AlloyDB عمل می‌کند. با تعریف پیکربندی tools.yaml ، این جعبه ابزار به طور خودکار عملیات پیچیده پایگاه داده را به عنوان ابزارهای تمیز و قابل اجرا مانند search_products_by_context یا check_inventory_levels نمایش می‌دهد. این امر نیاز به ادغام دستی اتصالات یا SQL تکراری در منطق عامل را از بین می‌برد.

نصب سرور جعبه ابزار

از ترمینال Cloud Shell خود، پوشه‌ای برای ذخیره فایل yaml ابزارهای جدید و فایل باینری جعبه ابزار ایجاد کنید:

mkdir scm-agent-toolbox

cd scm-agent-toolbox

از داخل آن پوشه جدید، مجموعه دستورات زیر را اجرا کنید:

# see releases page for other versions
export VERSION=0.27.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox

سپس با رفتن به ویرایشگر Cloud Shell، فایل tools.yaml را درون آن پوشه جدید ایجاد کنید و محتویات این فایل repo را در فایل tools.yaml کپی کنید.

sources:
    supply_chain_db:
        kind: "alloydb-postgres"
        project: "YOUR_PROJECT_ID"
        region: "us-central1"
        cluster: "YOUR_CLUSTER"
        instance: "YOUR_INSTANCE"
        database: "postgres"
        user: "postgres"
        password: "YOUR_PASSWORD"

tools:
  search_products_by_context:
    kind: postgres-sql
    source: supply_chain_db
    description: Find products in the inventory using natural language search and vector embeddings.
    parameters:
      - name: search_text
        type: string
        description: Description of the product or category the user is looking for.
    statement: |
     SELECT name, category, stock_level, distribution_center, region
      FROM products
      ORDER BY embedding <=> ai.embedding('text-embedding-005', $1)::vector
      LIMIT 5;

  check_inventory_levels:
    kind: postgres-sql
    source: supply_chain_db
    description: Get precise stock levels for a specific product name.
    parameters:
      - name: product_name
        type: string
        description: The exact or partial name of the product.
    statement: |
     SELECT name, stock_level, distribution_center, last_updated
      FROM products
      WHERE name ILIKE '%' || $1 || '%'
      ORDER BY stock_level DESC;

  track_shipment_status:
    kind: postgres-sql
    source: supply_chain_db
    description: Retrieve real-time logistics and shipping status for a specific region or product.
    parameters:
      - name: region
        type: string
        description: The geographical region to filter shipments (e.g., EMEA, APAC).
    statement: |
     SELECT p.name, s.status, s.estimated_arrival, s.route_efficiency_score
      FROM shipments s
      JOIN products p ON s.product_id = p.id
      WHERE p.region = $1
      ORDER BY s.estimated_arrival ASC;

  analyze_supply_chain_risk:
    kind: postgres-sql
    source: supply_chain_db
    description: Rerank and filter shipments based on risk profiles and efficiency scores using Google ML reranker.
    parameters:
      - name: risk_context
        type: string
        description: The business context for risk analysis (e.g., 'heatwave impact' or 'port strike').
    statement: |
     WITH initial_ranking AS (
      SELECT s.shipment_id, p.name, s.status, p.distribution_center,
      ROW_NUMBER() OVER () AS ref_number
      FROM shipments s
      JOIN products p ON s.product_id = p.id
      WHERE s.status != 'Delivered'
      LIMIT 10
      ),
      reranked_results AS (
      SELECT index, score FROM
      ai.rank(
      model_id => 'semantic-ranker-default-003',
      search_string => $1,
      documents => (SELECT ARRAY_AGG(name || ' at ' || distribution_center ORDER BY ref_number) FROM initial_ranking)
      )
      )
      SELECT i.name, i.status, i.distribution_center, r.score
      FROM initial_ranking i, reranked_results r
      WHERE i.ref_number = r.index
      ORDER BY r.score DESC;

toolsets:
   supply_chain_toolset:
     - search_products_by_context
     - check_inventory_levels
     - track_shipment_status
     - analyze_supply_chain_risk

اکنون فایل tools.yaml را در سرور محلی آزمایش کنید:

./toolbox --tools-file "tools.yaml"

شما می‌توانید آن را در رابط کاربری (UI) نیز آزمایش کنید.

./toolbox --ui

عالی!! وقتی مطمئن شدید که همه این‌ها کار می‌کنند، آن را در Cloud Run به صورت زیر مستقر کنید.

استقرار ابری

  1. متغیر محیطی PROJECT_ID را تنظیم کنید:
export PROJECT_ID="my-project-id"
  1. مقداردهی اولیه خط فرمان gcloud:
gcloud init
gcloud config set project $PROJECT_ID
  1. شما باید API های زیر را فعال کرده باشید:
gcloud services enable run.googleapis.com \
                       cloudbuild.googleapis.com \
                       artifactregistry.googleapis.com \
                       iam.googleapis.com \
                       secretmanager.googleapis.com
  1. اگر از قبل حساب کاربری سرویس backend ندارید، یک حساب کاربری ایجاد کنید:
gcloud iam service-accounts create toolbox-identity
  1. مجوزهای لازم برای استفاده از مدیر مخفی را اعطا کنید:
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor
  1. مجوزهای اضافی را به حساب سرویس که مختص منبع AlloyDB ما هستند (roles/alloydb.client و roles/serviceusage.serviceUsageConsumer) اعطا کنید.
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/alloydb.client


gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role serviceusage.serviceUsageConsumer
  1. tools.yaml را به عنوان یک فایل مخفی آپلود کنید:
gcloud secrets create tools-scm-agent --data-file=tools.yaml
  1. اگر از قبل یک نسخه مخفی دارید و می‌خواهید نسخه مخفی را به‌روزرسانی کنید، دستور زیر را اجرا کنید:
gcloud secrets versions add tools-scm-agent --data-file=tools.yaml
  1. یک متغیر محیطی را روی تصویر کانتینری که می‌خواهید برای اجرای ابری استفاده کنید، تنظیم کنید:
export IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest
  1. با استفاده از دستور زیر، Toolbox را روی Cloud Run مستقر کنید:

اگر دسترسی عمومی را در نمونه AlloyDB خود فعال کرده‌اید (توصیه نمی‌شود)، برای استقرار در Cloud Run، دستور زیر را دنبال کنید:

gcloud run deploy toolbox-scm-agent \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-scm-agent:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated

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

gcloud run deploy toolbox-scm-agent \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-scm-agent:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    # TODO(dev): update the following to match your VPC details
    --network <<YOUR_NETWORK_NAME>> \
    --subnet <<YOUR_SUBNET_NAME>> \
    --allow-unauthenticated

۶. راه‌اندازی عامل

با استفاده از کیت توسعه عامل (ADK) ، ما از دستورالعمل‌های یکپارچه به سمت یک معماری تخصصی و چندعاملی حرکت کرده‌ایم:

  • متخصص موجودی : بر موجودی محصول و معیارهای انبار تمرکز دارد.
  • مدیر لجستیک : متخصص در مسیرهای حمل و نقل جهانی و تحلیل ریسک.
  • GlobalOrchestrator : «مغزی» که از استدلال برای واگذاری وظایف و ترکیب یافته‌ها استفاده می‌کند.

این مخزن را در پروژه خود کلون کنید و بیایید آن را بررسی کنیم.

برای کلون کردن این، از ترمینال Cloud Shell خود (در دایرکتوری ریشه یا از هر جایی که می‌خواهید این پروژه را ایجاد کنید)، دستور زیر را اجرا کنید:

git clone https://github.com/AbiramiSukumaran/scm-memory-agent
  1. این باید پروژه را ایجاد کند و شما می‌توانید آن را در ویرایشگر Cloud Shell تأیید کنید.

53a398aff6ba7d5b.png

  1. مطمئن شوید که فایل .env را با مقادیر مربوط به پروژه و نمونه خود به‌روزرسانی می‌کنید.

راهنمای کد

نگاهی گذرا به عامل ارکستراتور

    Go to app.py and you should be able to see the following snippet:
orchestrator = adk.Agent(
    name="GlobalOrchestrator",
    model="gemini-2.5-flash",
    description="Global Supply Chain Orchestrator root agent.",
    instruction="""
    You are the Global Supply Chain Brain. You are responsible for products, inventory and logistics.
    You also have access to the memory tool, remember to include all the information that the tool can provide you with about the user before you respond.
    1. Understand intent and delegate to specialists. As the Global Orchestrator, you have access to the full conversation history with the user.
    When you transfer a query to a specialist agent, sub agent or tool, share the important facts and information from your memory to them so they can operate with the full context. 
    2. Ensure the final response is professional and uses Markdown tables for data.
    3. If a specialist provides a long list, ensure only the top 10 items are shown initially.
    4. Conclude with a brief, high-level executive summary of what the data implies.
    """,
    tools=[adk.tools.preload_memory_tool.PreloadMemoryTool()],
    sub_agents=[inventory_agent, logistics_agent],
    
    #after_agent_callback=auto_save_session_to_memory_callback,
)

این قطعه کد، تعریفی برای ریشه است که عامل هماهنگ‌کننده‌ای است که مکالمه یا درخواست را از کاربر دریافت می‌کند و ابزارهای مربوطه را بر اساس وظیفه به زیرعامل یا کاربر مربوطه هدایت می‌کند.

  1. بیایید به نماینده موجودی نگاه کنیم
inventory_agent = adk.Agent(
    name="InventorySpecialist",
    model="gemini-2.5-flash",
    description="Specialist in product stock and warehouse data.",
    instruction="""
    Analyze inventory levels.
    1. Use 'search_products_by_context' or 'check_inventory_levels'.
    2. ALWAYS format results as a clean Markdown table.
    3. If there are many results, display only the TOP 10 most relevant ones.
    4. At the end, state: 'There are additional records available. Would you like to see more?'
    """,
    tools=tools
)

این نماینده فرعی خاص در فعالیت‌های مربوط به موجودی کالا مانند جستجوی محصولات بر اساس زمینه و همچنین بررسی سطح موجودی کالا تخصص دارد.

  1. سپس نوبت به نماینده فرعی لجستیک می‌رسد:
logistics_agent = adk.Agent(
    name="LogisticsManager",
    model="gemini-2.5-flash",
    description="Expert in global shipping routes and logistics tracking.",
    instruction="""
    Check shipment statuses.
    1. Use 'track_shipment_status' or 'analyze_supply_chain_risk'.
    2. ALWAYS format results as a clean Markdown table.
    3. Limit initial output to the top 10 shipments.
    4. Ask if the user needs the full manifest if more results exist.
    """,
    tools=tools
)

این زیر-عامل خاص در فعالیت‌های لجستیکی مانند ردیابی محموله‌ها و تجزیه و تحلیل خطرات در زنجیره تأمین تخصص دارد.

  1. هر سه عاملی که تاکنون مورد بحث قرار دادیم از ابزارها استفاده می‌کنند و ابزارها از طریق سرور Toolbox ما که قبلاً در بخش قبلی مستقر کرده‌ایم، ارجاع داده می‌شوند. به قطعه کد زیر مراجعه کنید:
from toolbox_core import ToolboxSyncClient

TOOLBOX_SERVER = os.environ["TOOLBOX_SERVER"]
TOOLBOX_TOOLSET = os.environ["TOOLBOX_TOOLSET"]

# --- ADK TOOLBOX CONFIGURATION ---
toolbox = ToolboxSyncClient(TOOLBOX_SERVER)
tools = toolbox.load_toolset(TOOLBOX_TOOLSET)

این زیر-عامل خاص در فعالیت‌های لجستیکی مانند ردیابی محموله‌ها و تجزیه و تحلیل خطرات در زنجیره تأمین تخصص دارد.

۷. موتور عامل

در اجرای اولیه، موتور عامل (Agent Engine) را ایجاد کنید

import vertexai

GOOGLE_CLOUD_PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
GOOGLE_CLOUD_LOCATION = os.environ["GOOGLE_CLOUD_LOCATION"]

client = vertexai.Client(
  project=GOOGLE_CLOUD_PROJECT,
  location=GOOGLE_CLOUD_LOCATION
)

agent_engine = client.agent_engines.create()
  1. برای اجرای بعدی، موتور عامل را با پیکربندی بانک حافظه به‌روزرسانی کنید:
agent_engine = client.agent_engines.update(
    name=APP_NAME,
    config={
        "context_spec": {
            "memory_bank_config": {
                "generation_config": {
                    "model": f"projects/{PROJECT_ID}/locations/{GOOGLE_CLOUD_LOCATION}/publishers/google/models/gemini-2.5-flash"
                }
            }
        }
    })

۸. زمینه، اجرا و حافظه

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

حافظه کوتاه‌مدت (جلسات) : از طریق VertexAiSessionService مدیریت می‌شود و تاریخچه رویدادهای فوری (پیام‌های کاربر، پاسخ‌های ابزار) را در یک تعامل واحد ردیابی می‌کند.

حافظه بلندمدت (بانک حافظه) : توسط بانک حافظه هوش مصنوعی Vertex از طریق adk.memorybankservice پشتیبانی می‌شود. این لایه اطلاعات «معنی‌دار» - مانند ترجیح کاربر برای حامل‌های حمل و نقل خاص یا تأخیرهای مکرر انبار - را استخراج کرده و آنها را در طول جلسات حفظ می‌کند.

مقداردهی اولیه جلسه برای حافظه جلسه در محدوده مکالمه

این بخشی از قطعه کد است که جلسه (session) را برای برنامه فعلی و برای کاربر فعلی ایجاد می‌کند.

from google.adk.sessions import VertexAiSessionService

...

session_service = VertexAiSessionService(
    project=PROJECT_ID,
    location=GOOGLE_CLOUD_LOCATION,
)

...

# Initialize the session *outside* of the route handler to avoid repeated creation
session = None
session_lock = threading.Lock()

async def initialize_session():
    global session
    try:
        session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID)
        print(f"Session {session.id} created successfully.")  # Add a log
    except Exception as e:
        print(f"Error creating session: {e}")
        session = None  # Ensure session is None in case of error

# Create the session on app startup
asyncio.run(initialize_session())

مقداردهی اولیه بانک حافظه هوش مصنوعی Vertex برای حافظه بلندمدت

این بخشی از قطعه کد است که شیء سرویس بانک حافظه هوش مصنوعی Vertex را برای موتور عامل نمونه‌سازی می‌کند.

from google.adk.memory import InMemoryMemoryService
from google.adk.memory import VertexAiMemoryBankService

...

try:
    memory_bank_service = adk.memory.VertexAiMemoryBankService(
        agent_engine_id=AGENT_ENGINE_ID,
        project=PROJECT_ID,
        location=GOOGLE_CLOUD_LOCATION,
    )
    #in_memory_service = InMemoryMemoryService()
    print("Memory Bank Service initialized successfully.")
except Exception as e:
    print(f"Error initializing Memory Bank Service: {e}")
    memory_bank_service = None

runner = adk.Runner(
    agent=orchestrator,
    app_name=APP_NAME,
    session_service=session_service,
    memory_service=memory_bank_service,
)

...

چه چیزی پیکربندی شده است؟

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

چه چیزی به عنوان بخشی از اجرای عامل اجرا می‌شود؟

   async def run_and_collect():
        final_text = ""
        try:
            async for event in runner.run_async(
                new_message=content,
                user_id=user_id,
                session_id=session_id
            ):
                if hasattr(event, 'author') and event.author:
                    if not any(log['agent'] == event.author for log in execution_logs):
                        execution_logs.append({
                            "agent": event.author,
                            "action": "Analyzing data requirements...",
                            "type": "orchestration_event"
                        })
                if hasattr(event, 'text') and event.text:
                    final_text = event.text
                elif hasattr(event, 'content') and hasattr(event.content, 'parts'):
                    for part in event.content.parts:
                        if hasattr(part, 'text') and part.text:
                            final_text = part.text
        except Exception as e:
            print(f"Error during runner.run_async: {e}")
            raise  # Re-raise the exception to signal failure
        finally:
            gc.collect()
            return final_text

این تابع، محتوای ورودی کاربر را در شیء new_message با شناسه کاربر و شناسه نشست در محدوده پردازش می‌کند. سپس عامل (agent) کنترل را به دست می‌گیرد و پاسخ عامل (agent) پردازش و بازگردانده می‌شود.

چه چیزهایی در حافظه بلند مدت ذخیره می شود؟

جزئیات جلسه در محدوده برنامه و کاربر در متغیر جلسه استخراج می‌شود.

سپس این جلسه با استفاده از متد " add_session_to_memory " به عنوان حافظه برای کاربر فعلی برای برنامه فعلی شیء Vertex AI Memory Bank اضافه می‌شود.

session = asyncio.run(session_service.get_session(app_name=APP_NAME, user_id=USER_ID, session_id=session.id))

if memory_bank_service and session:  # Check memory service AND session
                try:
                    #asyncio.run(in_memory_service.add_session_to_memory(session))
                    asyncio.run(memory_bank_service.add_session_to_memory(session))
                    '''
                    client.agent_engines.memories.generate(
                        scope={"app_name": APP_NAME, "user_id": USER_ID},
                        name=APP_NAME,
                        direct_contents_source={
                            "events": [
                                {"content": content}
                            ]
                        },
                        config={"wait_for_completion": True},
                    )   
                    '''

                    print("Successfully added session to memory.******")
                    print(session.id)

                except Exception as e:
                    print(f"Error adding session to memory: {e}")

بازیابی حافظه

ما باید حافظه بلندمدت ذخیره‌شده را با استفاده از نام برنامه و نام کاربری به عنوان دامنه (از آنجایی که این دامنه‌ای است که خاطرات را برای آن ذخیره کرده‌ایم) بازیابی کنیم تا بتوانیم آن را به عنوان بخشی از متن به هماهنگ‌کننده و سایر عامل‌ها در صورت لزوم منتقل کنیم.

    results = client.agent_engines.memories.retrieve(
    name=APP_NAME,
    scope={"app_name": APP_NAME, "user_id": USER_ID}
    )
    # RetrieveMemories returns a pager. You can use `list` to retrieve all pages' memories.
    list(results)
    print(list(results))

چگونه حافظه بازیابی شده به عنوان بخشی از متن بارگذاری می‌شود؟

ما از ویژگی زیر در تعریف عامل Orchestrator استفاده می‌کنیم که به عامل ریشه اجازه می‌دهد زمینه را از بانک حافظه پیش‌بارگذاری کند. این علاوه بر ابزارهایی است که ما از سرور جعبه ابزار برای عامل‌های فرعی به آنها دسترسی داریم.

tools=[adk.tools.preload_memory_tool.PreloadMemoryTool()],

زمینه فراخوانی مجدد

در یک زنجیره تأمین سازمانی، شما نمی‌توانید یک «جعبه سیاه» داشته باشید. ما از CallbackContext شرکت ADK برای ایجاد یک موتور روایت استفاده می‌کنیم. با اتصال به اجرای عامل، هر فرآیند فکری و فراخوانی ابزار را ضبط می‌کنیم و آنها را به یک نوار کناری رابط کاربری منتقل می‌کنیم.

  • رویداد ردیابی : "GlobalOrchestrator در حال تجزیه و تحلیل الزامات داده است..."
  • رویداد ردیابی : "واگذاری مسئولیت سطوح موجودی به متخصص موجودی..."
  • رویداد ردیابی : "بازیابی الگوهای تأخیر تأمین‌کننده تاریخی از بانک حافظه..."

این ردیابی حسابرسی برای اشکال‌زدایی بسیار ارزشمند است و تضمین می‌کند که اپراتورهای انسانی می‌توانند به تصمیمات خودمختار عامل اعتماد کنند.

from google.adk.agents.callback_context import CallbackContext

...

# --- ADK CALLBACKS (Narrative Engine) ---
execution_logs = []

async def trace_callback(context: CallbackContext):
    """
    Captures agent and tool invocation flow for the UI narrative.
    """
    agent_name = context.agent.name
    event = {
        "agent": agent_name,
        "action": "Processing request steps...",
        "type": "orchestration_event"
    }
    execution_logs.append(event)
    return None

...

همین!!! ما با موفقیت پروژه را کلون کردیم و جزئیات عامل، حافظه و زمینه را بررسی کردیم.

می‌توانید با رفتن به پوشه پروژه مخزن کلون شده و اجرای دستورات زیر، آن را آزمایش کنید:

>> pip install -r requirements.txt

>> python app.py

این باید عامل شما را به صورت محلی شروع کند و شما باید بتوانید آن را آزمایش کنید.

۹. بیایید آن را در Cloud Run مستقر کنیم

  1. با اجرای دستور زیر از ترمینال Cloud Shell که پروژه در آن کلون شده است، آن را روی Cloud Run مستقر کنید و مطمئن شوید که در پوشه ریشه پروژه هستید .

این را در ترمینال Cloud Shell خود اجرا کنید:

gcloud run deploy supply-chain-agent --source . --platform managed   --region us-central1 --allow-unauthenticated --set-env-vars GOOGLE_CLOUD_PROJECT=<<YOUR_PROJECT>>,GOOGLE_CLOUD_LOCATION=us-central1,GOOGLE_GENAI_USE_VERTEXAI=TRUE,REASONING_ENGINE_APP_NAME=<<YOUR_APP_ENGINE_URL>>,TOOLBOX_SERVER=<<YOUR_TOOLBOX_SERVER>>,TOOLBOX_TOOLSET=supply_chain_toolset,AGENT_ENGINE_ID=<<YOUR_AGENT_ENGINE_ID>>

مقادیر مربوط به متغیرهای <<YOUR_PROJECT>>, <<YOUR_APP_ENGINE_URL>>, <<YOUR_TOOLBOX_SERVER>> و <<YOUR_AGENT_ENGINE_ID>> را جایگزین کنید.

پس از اتمام دستور، یک URL سرویس نمایش داده می‌شود. آن را کپی کنید.

  1. نقش AlloyDB Client را به حساب سرویس Cloud Run اعطا کنید. این به برنامه بدون سرور شما اجازه می‌دهد تا به طور ایمن به پایگاه داده تونل بزند.

این را در ترمینال Cloud Shell خود اجرا کنید:

# 1. Get your Project ID and Project Number
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

# 2. Grant the AlloyDB Client role
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/alloydb.client"

حالا از آدرس اینترنتی سرویس (نقطه پایانی Cloud Run که قبلاً کپی کرده‌اید) استفاده کنید و برنامه را آزمایش کنید.

توجه: اگر با مشکل سرویس مواجه شدید و دلیل آن را حافظه اعلام کرد، برای آزمایش، محدودیت حافظه اختصاص داده شده را به ۱ گیگابایت افزایش دهید.

3e4d36ed99b39325.png

d6b337f79a1f1d82.png

5e781a193a4aa903.png

۱۰. تمیز کردن

پس از انجام این آزمایش، فراموش نکنید که کلاستر و نمونه alloyDB را حذف کنید.

باید کلاستر را به همراه نمونه(های) آن پاکسازی کند.

۱۱. تبریک

با ترکیب سرعت AlloyDB ، کارایی هماهنگ‌سازی MCP Toolbox و «حافظه سازمانی» Vertex AI Memory Bank ، ما یک سیستم زنجیره تأمین تکامل‌یافته ساخته‌ایم. این سیستم فقط به سؤالات پاسخ نمی‌دهد؛ بلکه به خاطر می‌سپارد که انبار شما در سنگاپور همیشه با تأخیرهای مربوط به بارندگی‌های موسمی دست و پنجه نرم می‌کند و قبل از اینکه حتی درخواست کنید، به طور پیشگیرانه پیشنهاد تغییر مسیر محموله‌ها را می‌دهد.