۱. مقدمه

شما یک کانال دنج و ترسناک در VibeTube دارید: هر شب یک فیلم کوتاه تا هالووین. مخاطبان جادوی کوچک را در اتاقهای کوچک میخواهند و آن را به هر طریقی که میتوانند برای شما ارسال میکنند — بهعنوان نظر، یادداشت صوتی، عکسهایی از کشوهای جنزده خودشان، و کلیپهایی از ایوانشان در تاریکوروشن. کارهای معوقه پر از ایده است. هر شب به کارهای تکراری میگذرد: تحقیق درباره آنچه در فید پرطرفدار است، بررسی کارهای عقبافتاده، خواندن آنچه مخاطبان ارسال کردهاند، انتخاب مسیر، بررسی آن با خطمشی کانال، نوشتن فیلمنامه، طراحی پوستر، رندر کردن کلیپ، و انتشار.
مدلهای زایا میتوانند هر کار را بهتنهایی انجام دهند. Gemini پژوهش میکند، گوش میدهد، نگاه میکند، و مینویسد؛ مدل تصویر طراحی میکند؛ Veo رندر میکند. آنچه آنها نمیتوانند بهتنهایی انجام دهند، اجرای شب بهترتیب، توقف برای تصمیمی که متعلق به شماست، رد کردن یک جهت بد قبلاز اینکه هزینه داشته باشد، انتظار چند دقیقهای برای رندر بدون باز نگه داشتن چیزی، و گذاشتن یک رکورد است.
این کارگاه کدنویسی آن عصر را به ADK Workflow تبدیل میکند: نمودار ترسیمشدهای از گرهها و لبهها که خودش اجرا میشود، فقط در جایی که به قضاوت شما نیاز است از شما سؤال میکند، و هر اجرا را با یک کلیپ و پوستر آن در VibeTube به پایان میرساند.
پیامواره نمیتواند ترتیب، توقف، یا بررسی را تضمین کند. گراف میتواند.
آنچه خواهید آموخت
- یک عامل، و مدل تصویر بهعنوان ابزار.
Agentعبارت است از یک مدل، یک دستورالعمل، و ابزارها. ابزار سوم مدل تصویر را فراخوانی میکند و اجرای اول با یک پوستر بهپایان میرسد. سپس جملهای که بقیه آزمایشگاه را هدایت میکند: پیامواره نمیتواند نظم، توقف، یا بررسی را تضمین کند. - ساختارهایی که گراف از آنها ساخته شده است.
Workflowیک فهرست لبه است. شما فناوت و اتصال (خوانندگان موازی که درJoinNodeبه هم میرسند) و مسیریابی را میسازید که خروجی خود را نامگذاری میکند، با یک عامل حالت تکلیف پشت خروجی ردشده که جهتی را در حلقهای از فراخوانیهای ابزار پاک میکند و دوباره به خط اصلی میپیوندد. - خط لوله دریافت چندحالته. چهار نوع چیزی که مخاطب ارسال کرده است، یک خواننده برای هر نوع: Gemini یادداشتهای صوتی را ترانویسی میکند، برای عکسها زیرنویس مینویسد، کلیپها را تماشا میکند و موسیقی متن آنها را میشنود، و هر خواننده همان شکل خط را در یک مجموعه «موتور RAG» مینویسد. خواننده سوم در فناوت براساس معنی بازیابی میکند و رسانه را همراه با بخشها برمیگرداند.
- دو نوع تعلیق، یک سازوکار. یک اجرا برای یک شخص (
RequestInput: چهار کارت، شما پاسخ میدهید) و برای یک ابزار کند (Veo پشتLongRunningFunctionTool: یک رسید) متوقف میشود. هر دو یک چیز هستند: یک تماس باز در فروشگاه جلسه، که با شناسه آن، از فرم یا از فرایند دیگری ازسر گرفته شده است. دو شعبه میز رسانه پوستر را براساس نام، در ایالت همرسانی میکنند. - حافظه درطول شبها. Two lifecycle callbacks on Memory Bank:
before_model_callbackrecalls the creator's taste before the proposer reads,after_agent_callbackremembers tonight's pick after the scripter. - نسخه پخش. نمودار داخل برنامهای با ADK
Runner، «رویدادهای ارسالشده از سرور» به صفحه، Cloud Run با وابستگی جلسه، و یک POST چندبخشی که کلیپ را با پوستر بهعنوان ریزعکس آن در VibeTube قرار میدهد.
آنچه خواهید ساخت
برنامهای که در مرورگر شما اجرا میشود: VibeStudio Workbench، صفحه همراهی با ویرایشگر کد درونصفحهای، تأییدکنندههایی که فایلهای روی دیسک و جلسههایی را که اجراهای شما نوشتهاند میخوانند، و adk web جاسازیشده. هفت جلسه آن هفت صفحه این codelab است، با همان اعداد و همان حروف بخش، بنابراین این دو در یک مرحله باقی میمانند. هر صفحه در نواری باز میشود که جلسه را در یک جمله و سه کاری را که در آنجا انجام میدهید بیان میکند: ویرایش، اجرا، درستیسنجی. بخشی را در اینجا بخوانید، سپس ویرایش را در میزکار انجام دهید؛ هر ویرایش با پانل درستیسنجیای بهپایان میرسد که آنچه را واقعاً اتفاق افتاده است میخواند.
سه چیز اجرا میشود: Workbench (صفحه در درگاه ۴۶۰۰، ویرایشگر، تأییدکنندهها)، بخش باطنی شما (Workflow در agent/ و یک برنامه محیط امن برای هر جلسه در stage0_prompt/ تا stage6_video/، که adk web ارائه میدهد)، و Google Cloud (Gemini، مدل تصویر، RAG Engine، Veo، Memory Bank — همه ازطریق Vertex AI با اطلاعات کاربری خودتان قابلدسترسی هستند).
هفت جلسه
جلسه | آنچه میسازید | ویرایشهای شما |
۱ · داستان | هنوز هیچچیز — شب، و گرافی که میشود | — |
۲ · تولید تصویر | یک عامل اولین پوستر را میکشد |
|
۳ · گردش کار | شب بهصورت نمودار: فناوت و اتصال، رهیاب و حلقهاش | |
۴ · دریافت چندحالته | آنچه مخاطب ارسال کرد، در یک مجموعه خوانده شد؛ خواننده سوم | |
۵ · تولید ویدیو | دو نوع تعلیق؛ پوستر همرسانیشده با نام در ایالت |
|
۶ · خاطره | آنچه کانال بین شبها بهخاطر میسپارد | |
۷ · مستقر و منتشر کردن | VibeStudio در Cloud Run؛ شب در VibeTube | کد رویداد اتاق، و انتشار |
نحوه استفاده از این codelab. این دوره مفهوممحور است: هر جلسه ایده را توضیح میدهد، سپس کد را همانطور که پساز اعمال تغییر خوانده میشود نشان میدهد و آن را مرور میکند. هرگز از شما نمیخواهد فایلی را در اینجا ویرایش کنید — ویرایش در میزکار انجام میشود که هربار یک ویرایش را به شما میدهد، با یک راهنمایی در کنار آن، و یک پانل درستیسنجی در زیر آن. جلسه را بخوانید، سپس اجازه دهید صفحه شما را در آن راهنمایی کند.
محل قرارگیری
نمودار، زمینه، و اتاق در VibeTube از Lin's Haunted Edition از VibeStudio میآید؛ این برش چندحالتی است که دوباره بر آنچه مخاطب ارسال کرده و بر دو نوع تعلیق، در قالب تعاملی یکصفحهای آزمایشگاههای Agent Valley متمرکز شده است. استودیو یک صحنه است؛ هر مکانیزمی که در زیر آن قرار دارد، همان مکانیزم معمولی است که برای خط لوله پشتیبانی، جریان بازبینی محتوا، یا هر چیز دیگری که باید برای یک شخص متوقف شود و منتظر یک کار کند انجام شود، استفاده میکنید.
۲. 🧰 راهاندازی
- چه چیزهایی — حسابی که بتواند پرداخت کند، پروژه، آزمایشگاه نصبشده، مدلی که ثابت شده است پاسخ میدهد، و میز کار که در پسزمینه روی یک درگاه اجرا میشود.
- چرا — یک فرایند در یک درگاه: صفحه، ویرایشگر، درستیسنجها،
/api/*وadk webدارای یک مبدأ هستند، بنابراین هیچچیز در این آزمایشگاه به پایانه دوم یا درگاه دوم نیاز ندارد. - چگونه — همسانسازی، دو متن. دومی یک فراخوانی مدل واقعی انجام میدهد، میز کار را راهاندازی میکند، و درحالیکه شما مشغول خواندن هستید، شروع به ایجاد پیکره RAG و «بانک حافظه» میکند تا جلسههای ۴ و ۶ آنها را آماده پیدا کنند.
همه چیز در این آزمایشگاه در Cloud Shell اجرا میشود — gcloud، Python، Node، ffmpeg، و git ازقبل در آنجا نصب شدهاند و Gemini، مدل تصویر، Veo، RAG Engine، و Memory Bank همگی ازطریق Vertex AI بااستفاده از اطلاعات کاربری خودتان قابلدسترسی هستند. هیچ کلید میانای برنامهسازی کاربردی در هیچ کجای این آزمایشگاه وجود ندارد و چیزی برای نصب در رایانه کیفی شما وجود ندارد. رایانه کیفی با Google Cloud SDK، Python 3.12، و Node 20+ نیز به همین روش کار میکند.
یک چیز وجود دارد که این آزمایشگاه نمیتواند به شما ارائه دهد: پروژه Google Cloud با صورتحساب. مرحله ۱ حسابی را دراختیارتان قرار میدهد که میتواند هزینه آن را پرداخت کند؛ مرحله ۴ آن حساب را به پروژه تبدیل میکند. این کارها را بهترتیب انجام دهید — هر مرحله بعداز مرحله ۴، پروژهای را میخواند که مرحله ۴ روی آن مستقر میشود.
۱ · دریافت حساب با صورتحساب (حدود ۱ دقیقه)
با «حساب Google» که صورتحساب آن فعال است به سیستم وارد شوید. هر حسابی از این نوع کار میکند. اگر پروژه شما اینگونه نیست، دوره آزمایشی رایگان را شروع کنید؛ این دوره به شما حساب صورتحساب میدهد که تنها چیزی است که مرحله ۴ نیاز دارد. اگر در آزمایشگاه تحت هدایت مربی هستید، ابتدا اعتبارات این جلسه را پسخرید کنید — این کار حساب صورتحساب را برای شما ایجاد میکند.
۲ · باز کردن Cloud Shell (~۲ دقیقه)
👉🖥 روی فعالسازی Cloud Shell در بالای کنسول Google Cloud کلیک کنید — این نماد بهشکل ترمینال در سرصفحه است.

👉🖥 روی باز کردن ویرایشگر (نماد پوشه باز با مداد) کلیک کنید. با این کار، «ویرایشگر Cloud Shell» با کاوشگر فایل در سمت راست باز میشود — هرگاه بخواهید کل فایل را بخوانید، به آنجا میروید.

👉🖥 اگر امروز در هر مرحلهای از شما خواسته شد که مجوز بدهید، روی مجوز دادن کلیک کنید تا ادامه دهید.
👉🖥 پایانهای را در ویرایشگر باز کنید — یعنی برگه ۱، و هر 👉💻 مرحله در آنجا اجرا میشود. برگه ۲ خود میزکار است و آن را در مرحله ۷ باز میکنید. اگر پایانه ازقبل در پایین صفحه قرار ندارد، آن را با View (مشاهده) ← Terminal (پایانه) باز کنید:

👉💻 بررسی کنید با کدام حساب به سیستم وارد شدهاید — باید همان حساب مرحله ۱ باشد:
gcloud auth list
۳ · دریافت آزمایشگاه (حدود ۱ دقیقه)
👉💻 مخزن را همسانهسازی کنید:
cd ~
git clone https://github.com/cuppibla/vibe-studio-multimodal
cd vibe-studio-multimodal
۴ · پروژه خود را ایجاد کنید (~۲ دقیقه)
👉💻 یک اسکریپت حساب صورتحساب شما را پیدا میکند، پروژهای در آن ایجاد میکند، صورتحساب را پیوند میدهد، و Cloud Shell را به آن پروژه هدایت میکند:
cd ~/vibe-studio-multimodal
./setup_project.sh
انتظار میرود ✓ برای حساب صورتحساب انتخابشده، ✓ برای پروژه ایجادشده، و پایانی مانند این داشته باشید:
Project ready: vibe-studio-4271 Next: ./setup_codelab.sh
vibe-studio-4271 شناسه پروژه شما است — شناسه شما ارقام متفاوتی خواهد داشت. چهار اتفاق در آنجا میافتد:
صدور صورتحساب | جدیدترین حساب | |
پروژه |
| |
انتخاب میکند | شناسه را در | |
منتظر میماند | تا زمانی که پروژه واقعاً مجاز به ارائه شود، حداکثر تا دو دقیقه نظرسنجی میکند | |
👉🖥 همان پروژه اکنون در کنسول است — شناسه آن در کارت اطلاعات پروژه در «داشبورد» قرار دارد:

۵ · نصب و راهاندازی آزمایشگاه (~۴ دقیقه)
👉💻
cd ~/vibe-studio-multimodal
./setup_codelab.sh
یک اسکریپت، ایمن برای اجرای مجدد — اجرای دوم همه پاسخهایی را که در اولین اجرا دادهاید حفظ میکند. در نزدیکی شروع، دو سؤال میپرسد و Enter پیشفرض را برای هر دو انتخاب میکند:
کد رویداد | اتاق VibeTube که ویدیو شما در جلسه ۷ در آن منتشر شده است. کد اتاق را که از مدرستان دریافت کردهاید تایپ کنید، یا برای | |
نام نمایشی | نامی که اتاق شما را با آن معرفی میکند، یا Enter را برای نامی که از حسابتان ساخته شده است فشار دهید. | |
هر دو در .env قرار میگیرند و بعداً در .env یا در صفحه انتشار قابلتغییر هستند. سپس بقیه کارها را خودش بهترتیب انجام میدهد:
uv env | اگر uv وجود نداشته باشد، آن را نصب میکند، سپس | |
apis | شش میانای برنامهسازی کاربردی را در پروژه انتخابشده در مرحله ۴ فعال میکند و قبلاز ادامه دادن، پاسخ هرکدام را تأیید میکند: | |
حساب | مطمئن میشود که حساب سرویس محاسباتی پیشفرض پروژه وجود دارد و نقشهای موردنیاز ساخت و سرویس جلسه ۷ را دارد | |
.env |
| |
veo · room | تأیید میکند که | |
مدل | یک تماس واقعی با Gemini برقرار میکند، بنابراین پروژه بد در اینجا در زمان راهاندازی ناموفق میشود و نه در داخل جلسه | |
آغازگر | نه فایلی را که ویرایش خواهید کرد از | |
صفحه |
| |
ابر | ایجاد کردن پیکره «موتور RAG» و «بانک حافظه» را در پسزمینه شروع میکند تا جلسههای ۴ و ۶ آنها را آماده ببینند | |
پیشپرواز |
| |
۶ · خواندن پیشپرواز (حدود ۱ دقیقه)
آخرین چیزی که مرحله ۵ چاپ کرد، پیشپرواز است — هر خط یک کاوش واقعی است، نه یک حدس:
✓ python 3.12 ✓ auth path A: Vertex via ADC (STUDIO_VERTEX=1) ✓ Google Cloud ADC (project vibe-studio-4271) ✓ Memory Bank SDK ✓ stage0_prompt loads ✓ stage1_fanout loads (0 edges) ✓ stage3_router loads (... edges) ✓ stage4_memory loads (... edges) ✓ stage5_rag loads (... edges) ✓ stage6_video loads (... edges) ✓ node and npm (build the learning center's page) ✓ learning center page built (web/dist) ✓ aiplatform.googleapis.com enabled (Gemini, Veo, Memory Bank, RAG Engine) ... - Memory Bank: still being created in the background - RAG corpus: still being created in the background ✓ session 7 runtime account 123456789-compute@developer.gserviceaccount.com has its roles ✓ learning center running on port 4600 PREFLIGHT GREEN Open session 1 here: https://4600-cs-...cloudshell.dev/step/story
stage1_fanout loads (0 edges) درست میگوید: آن گراف خالی ارسال میشود و در جلسه ۳ آن را پر میکنید. - خط بعدی هم درست است — یک دقیقه یا دو دقیقه طول میکشد تا پیکره بالا بیاید و حدود بیست ثانیه برای بانک، و تا جلسه ۴ به هیچکدام نیاز نخواهید داشت.
👉💻 هر زمان خواستید آن را دوباره اجرا کنید تا ببینید در چه مرحلهای هستید — این ابزار فایلها، پروژه، و سرور درحال اجرا را میخواند، نه یک بازبینه:
cd ~/vibe-studio-multimodal
uv run python scripts/preflight.py
۷ · باز کردن میزکار (حدود ۱ دقیقه)
میزکار ازقبل درحال اجرا است. مرحله ۵ آن را در پسزمینه شروع کرد، یک فرایند uvicorn در درگاه ۴۶۰۰ که صفحه را ارائه میدهد، هر مسیر /api/*، و adk web سوارشده در /workbench. چیزی برای شروع وجود ندارد و هیچچیز در این آزمایشگاه از شما نمیخواهد آن را بازراهاندازی کنید: ویرایش در اجرای بعدی شما اعمال میشود.
👉🖥 روی پیوندی که پیشپرواز چاپ کرده است کلیک کنید، یا در Cloud Shell: پیشنمایش وب ← تغییر درگاه ← ۴۶۰۰. با برگه ۲ تماس بگیرید. این ویدیو با جلسه ۱ · داستان باز میشود.
سه فرمانی که ممکن است بعداً بخواهید، همگی از ~/vibe-studio-multimodal:
| میز کار را متوقف میکند | |
| آن را در پیشنما شروع میکند؛ Ctrl+C آن و هر فرایندی را که شروع کرده است متوقف میکند | |
| | |
چیدمان مخزن
vibe-studio-multimodal/
├── agent/ # the workflow and the platform clients
│ ├── graph.py # the graph: three readers, the proposer, the gate, the router, the script, the desk
│ ├── desk.py # render_desk and render_submit, the LongRunningFunctionTool
│ ├── deliver.py # the delivery console: find the pending call, wait for Veo, answer by id
│ ├── schemas.py # Direction, Directions, CleanedDirection, Script, each with a poster idea
│ ├── cleanup_tools.py # find_policy_hits, suggest_replacement: the task agent's tools
│ ├── policy_words.txt # the policy, as data; policy_replacements.txt, the approved stand-ins
│ ├── backlog.txt # the creator's Halloween ideas; comments.md, the audience's comments
│ ├── audience/ # what else the audience sent: voice/*.mp3, photo/*.jpg, clip/*.mp4
│ ├── media/past/ # six past posters and captions.md, what Gemini sees in each
│ └── platform/ # config, audience (the readers), rag (RAG Engine), images, videogen (Veo), memory (Memory Bank)
├── stage0_prompt/ ... stage6_video/ # one sandbox app per session, runnable in adk web
├── server/ & web/ # the VibeStudio Workbench (FastAPI + React, port 4600)
├── vibestudio/ # the app deployed in session 7, with its own copy of agent/
├── checks/ & scripts/ # the hole registry, the verifiers, preflight, start/stop/restart
└── starter/ # the nine files you edit, as students receive them
۳. ۱ · داستان — شب، و نموداری که به آن تبدیل میشود
👉🖥 برگه ۲ در جلسه ۱ · داستان باز میشود. بخوانید: شش کار که بهصورت حلقهای دور یک نفر چیده شدهاند — تحقیق درباره گرایش، بررسی کارهای عقبافتاده، انتخاب جهت، بررسی خطمشی، نوشتن فیلمنامه، رندر کردن کلیپ — و این حلقه هر شب تکرار میشود. Gemini میتواند بهتنهایی تحقیق، کارهای معوقه، و فیلمنامه را انجام دهد؛ Veo میتواند بهتنهایی رندر کند؛ انتخاب و بررسی برعهده فرد است.
زیر حلقه، نموداری است که بقیه آزمایشگاه میسازد، سیزده گره از یک ایده تا یک کلیپ و پوستر آن. نشانگر را روی گره نگه دارید تا ببینید چه کاری انجام میدهد و کدام جلسه آن را میسازد. چیزی برای ویرایش در اینجا وجود ندارد؛ دکمه تولید اولین پوستر صفحه، جلسه ۲ است.
واژگان، بنابراین شش صفحه بعدی بدون توقف خوانده میشود:
کلمه | معنی آن در اینجا |
عامل | یک ADK |
گره | یک مرحله از گراف — یک تابع پایتون، یک عامل، یک اتصال، یک مسیریاب |
edge | چندتایی از گرهها که بهترتیب اجرا میشوند؛ نمودار فهرستی از آنها است. جلسه ۳ |
جلسه | سابقه یک اجرا در |
state | یک dict که گرهها براساس نام کلید همرسانی میکنند؛ یک گره |
خاطره | آنچه پساز پایان جلسه باقی میماند — «بانک حافظه»، ازطریق فراخوانیهای برگشتی. جلسه ۶ |
۴. ۲ · تولید تصویر — یک عامل، و مدل تصویر بهعنوان ابزار

👉🖥 در میزکار، جلسه ۲ · تولید تصویر را باز کنید. قبلاز گراف، سادهترین چیزی که میتوانست کار کند: یک Agent که دستورالعمل آن کل شب بهصورت نثر است، سه تابع Python بهعنوان دستهای آن، و اجرایی که با یک تصویر پایان مییابد.
عامل عبارت است از یک مدل، یک دستورالعمل، و ابزارها
stage0_prompt/agent.py، root_agent را با سه فیلد از عامل ADK تعریف میکند. model مدل Gemini از agent/platform/config.py است. instruction پیامواره سیستم است: شش شغل در یک نفس، آخرین آنها یک تصویر است. tools سه عملکرد دارد.
root_agent = Agent(
name="solo_channel", model=config.MODEL,
tools=[check_trends, read_backlog, make_poster],
instruction=(
"You run the creator's cozy-spooky short-video channel, alone, every "
"night until Halloween.\n"
"When the creator gives you an idea (or nothing), do ALL of this:\n"
"check what is trending. look at your backlog of ideas. propose a "
"direction and agree on it with the creator. refuse blacklisted "
"subjects (competitor, hateful, gore). then describe the video you "
"would make: a title (<=60 chars) and 3 shots, one visual sentence "
"each. then draw its poster: call make_poster once with one line that "
"names the single image that sells the video, and tell the creator "
"where the poster is.\n"
"Ask the creator to confirm the direction before describing the "
"video."))
مدل یک دنیای بسته است: مدل براساس وزنهای خود و نشانههای موجود در بافت خود استدلال میکند. ابزار یک تابع Python است که مدل ممکن است درخواست کند. ADK امضا و رشته مستندات هر تابع را میخواند و آن را به مدل اعلام میکند؛ وقتی مدل به تابعی نیاز دارد، رویداد function_call را صادر میکند، زمان اجرا کد پایتون را اجرا میکند و رویداد function_response را به بافت برمیگرداند، و مدل با نتیجهای که دراختیار دارد ادامه میدهد.
سه تابع، و یکی از آنها نقاشی میکشد
دو نفر از این سه نفر پژوهش را خواندند. شخص سوم از مدل تصویر میخواهد پوستری بسازد:
def check_trends() -> dict:
"""Ten formats trending on the platform right now, with a heat score each."""
from agent.trends import sample_trends
return {"trends": sample_trends()}
def read_backlog() -> dict:
"""The creator's backlog: ideas they noted down to make someday."""
from agent.graph import backlog_notes
return {"backlog": backlog_notes()}
def make_poster(subject: str) -> dict:
"""Draw tonight's poster: one image of `subject` in the channel's cozy
low-poly style. Returns where the PNG landed (path, url) in a few seconds."""
...
make_poster با agent/platform/images.py تماس میگیرد، مدل تصویر بهعنوان یک تابع ساده پیچیده شده است، همانطور که videogen.py Veo را میپیچد: یک پیشوند به سبک خانه (ظاهر دنج هالووین با چندضلعی کم کانال، نارنجی کدو تنبل و کرم نور شمع در مقابل آلو نیمهشب)، سوژه، یک قاب عمودی ۹:۱۶، یک PNG زیر runs/media/posters، که در /static/posters ارائه میشود. با STUDIO_REAL_IMAGES=0، پوستر جایگزین را کپی میکند و نتیجه prebaked را پرچمگذاری میکند تا آزمایشگاه در هر جایی اجرا شود.
MODEL = os.environ.get("STUDIO_IMAGE_MODEL", "gemini-2.5-flash-image")
ASPECT = "9:16"
def generate(subject: str, visual_style: str = "", name: str | None = None) -> dict:
"""One poster for `subject`. Returns where it landed: status, path, url,
prompt, and prebaked=True when the stand-in was used instead of the model."""
همان تماس گرهی در نمودار جلسه ۵ است.
عملی: ویرایش بینظیر
همانطور که ارسال شده است، make_poster یک dict برمیگرداند که میگوید پوستر رد شده است. 👉🖥 در ویرایشگر میزکار، آن را بهجای خط TODO، مدل تصویر را فراخوانی کنید:
from agent.platform import images
return images.generate(subject, name="solo")
ذخیره کلیک کنید. ردیفهای درستیسنجی فایل را میخوانند: سه ابزار در نماینده، فراخوانی مدل تصویر در محل. 👉🖥 روی بررسی بار شدن گردش کار کلیک کنید، سپس باز کردن وب adk را انتخاب کنید و این را ارسال کنید:
tonight's idea: a tiny ghost learning to haunt a sock drawer
چه انتظاری باید داشته باشید و چرا
- دو رویداد ابزار قبلاز پاسخ، سپس یک سؤال.
check_trendsوread_backlogاجرا میشوند چون دستورالعمل گفته بود که بررسی و نگاه کند، و مدل هیچکدام را در وزنهایش نداشت. پاسخ جهتی را پیشنهاد میکند و از شما میخواهد آن را تأیید کنید، زیرا دستورالعمل ابتدا درخواست موافقت کرده است. - پیام دوم همه چیز را دریافت میکند.
skip the questions, describe the video and draw the posterرا ارسال کنید. مدل ویدیو را توصیف میکند،make_posterرا یکبار با خط موضوعی که خودش نوشته است فراخوانی میکند، و به شما میگوید PNG کجا قرار گرفته است. پانل درستیسنجی پوستر را در کنار ردیفها نشان میدهد: صحنهای از هالووین با ظاهر کانال که در حدود هشت ثانیه ترسیم شده است. - چرا این یک کانال نیست. پژوهش در نثری گنجانده شده است که هیچچیز نمیتواند به آن استناد کند. این خطمشی توسط مدلی که دستورالعمل را نوشته بود تأیید شد و پوستر قبلاز اینکه چیزی آن را بررسی کند کشیده شد. در یک پیام، توافق نادیده گرفته شد. نوشتار پیامواره دروازه نیست. بقیه آزمایشگاه این شش شغل را به یک نمودار ترسیمشده تقسیم میکند.
صفحه با عامل ADK از چه چیزی ساخته شده است به پایان میرسد، مرجعی که میتوانید به آن بازگردید: مدل، زمینهای که با آن استدلال میکند، ابزارها و همکارانی که ازطریق آنها عمل میکند، فراخوانیهایی که هر تماس را دربرمیگیرند، و جلسه و حافظهای که خارج از آن زندگی میکنند.
۵. ۳ · گردش کار — فناوت و اتصال، رهیاب و حلقه

👉🖥 در میزکار، جلسه ۳ · گردشهای کاری، بخشهای ۳الف و ۳ب را باز کنید. هر بخش در یک برنامه جعبه شنی ساخته شده است تا بتوانید آن را بهتنهایی در adk web اجرا کنید: fan-out در stage1_fanout، و مسیریاب و حلقه آن در stage3_router.
Fan-out and join (3a)
ADK Workflow یک فهرست لبه است. هر ورودی یک زنجیره است، چندتایی از گرهها که بهترتیب اجرا میشوند. دو زنجیره که از START خارج میشوند بهصورت موازی اجرا میشوند. زنجیرهای که به JoinNode ختم میشود به آن گزارش میدهد و پیوستن برای هر زنجیرهای که در آنجا ختم میشود منتظر میماند تا ادامه دهد و یک دیکته را که با نام گره کلید خورده است تحویل میدهد. هدفی که بهصورت dict نوشته شده است، نامهای مسیر رهیاب را به گرهها نگاشت میکند. صفحه 3a میزکار اشکال را میکشد و تمرین نوشتن لبهها را دارد.
نوع گره | نوشتهشده بهصورت | کارکرد |
گره تابع |
| پایتون ساده. همچنین ممکن است وضعیت همرسانیشده را با |
گره پیوستن |
| منتظر میماند تا همه زنجیرههای ورودی گزارش دهند، سپس همه خروجیهای آنها را بهعنوان یک دیکشنری ارسال میکند. |
گره عامل |
| یک فراخوانی مدل در حالت |
رهیاب |
| گره تابعی که بازگشت آن لبه خروجی را نامگذاری میکند. |
گره ورودی انسانی |
| نمودار را تا زمانی که فردی به فرم پاسخ دهد تعلیق میکند. جلسه ۵. |
نمودار اول پژوهش جلسه ۲ را بهصورت گره نشان میدهد. scan_trends و read_backlog در agent/graph.py دو خواننده هستند؛ read_backlog ایده امشب را نیز در وضعیت مشترک مینویسد. stage1_fanout/agent.py پیوستن را تعریف میکند و Workflow خود را از فهرستی بهنام EDGES میسازد که خالی ارسال میشود (0 edges پیشپرواز). 👉🖥 آن را با دو زنجیره پر کنید:
EDGES = [(START, scan_trends, join_research),
(START, read_backlog, join_research)]
👉🖥 آن را در adk web اجرا کنید. Two nodes light together, then the join; open join_research's event and read one dict with a scan_trends key and a read_backlog key. هیچ فراخوانی مدلی انجام نشد. هیچچیز قالب dict اتصال را تغییر نمیدهد: گره بعداز آن، در 3b، یک عامل است و گره عامل node_input خود را بهعنوان پیام دریافت میکند، بنابراین dict بهصورت JSON میرسد که مدل مستقیماً آن را میخواند.
رهیاب و حلقه (3b)
یک Agent درون یک Workflow در حالت single_turn اجرا میشود: برونداد گره قبلی را بهعنوان پیام خود دریافت میکند، یک فراخوانی مدل انجام میدهد، و یکبار پاسخ میدهد. با output_schema، پاسخ قبلاز اینکه گره بعدی آن را ببیند دربرابر مدل Pydantic اعتبارسنجی میشود، بنابراین نمودار اشیای تایپشده را ارسال میکند، نه نثر. propose_directions دیکشنری پژوهشی پیوستن را میخواند و چهار نامزد را پیشنهاد میدهد که هرکدام صحنهای از هالووین است. نامزد ۴ طعمه است: عنوان آن حاوی کلمهای است که کانال آن را رد میکند، بنابراین مسیریاب چیزی برای گرفتن دارد. هر نامزد یک poster_idea دارد، یک خط که نام تصویر واحدی را که ویدیو را میفروشد ذکر میکند؛ فیلمنامهنویس آن را نگه میدارد و میز رسانه آن را در جلسه ۵ ترسیم میکند.
مسیریاب گره تابعی است که بازگشت آن نام لبهای را که اجرا میگیرد مشخص میکند. بدون فراخوانی مدل: policy_check در زمان تصمیمگیری agent/policy_words.txt را میخواند، کلمات کامل را با عنوان و زاویه جهت مطابقت میدهد، و آن را قبلاز نوشته شدن هرگونه متن یا صرف هرگونه هزینه هدایت میکند. خطمشی داده است: فایل را ویرایش کنید و اجرای بعدی آن را میخواند.
def policy_check(node_input):
"""The policy gate: a router. Refused words in the direction send it to
quarantine; a clean one goes to the scripter. Code, not a model."""
chosen = _as_dict(node_input)
bad = policy_hits(f"{chosen.get('title', '')} {chosen.get('angle', '')}")
return Event(output=node_input, route="BLOCK" if bad else "OK")
stage3_router/agent.py همان fan-out بهعلاوه propose_directions و choose_direction است، جایگزینی برای در انسانی که در جلسه ۵ میسازید: نامزدی را انتخاب میکند که شمارهاش با ایده امشب (..., pick 4) تمام میشود، درغیراینصورت اولین نامزد را انتخاب میکند و انتخاب را در وضعیت مینویسد. scripter یک عامل single_turn است که یک جهت تأییدشده را به یک Script تبدیل میکند: یک عنوان، یک توضیح، برچسبها، یک خط آغازین، سه عکس و یک ایده پوستر، هر عکس یک تصویر هالووین در ظاهر کانال.
حلقه پشت خروجی ردشده. quarantine بهعنوان عاملی در حالت تکلیف عرضه میشود. Agent دارای mode است: chat حلقه مکالمهای است که عامل ریشه با شخص اجرا میکند، single_turn یک تماس با پاسخ ساختاریافته است، و task حلقهای از تماسهای ابزار است که عامل بهتنهایی اجرا میکند تا زمانی که finish_task داخلی را فراخوانی کند، که ADK آرگومانهای آن را از output_schema مشتق میکند. قرنطینه کردن تماسها find_policy_hits در جهت ردشده، از suggest_replacement برای جایگزین تأییدشده کانال برای هر کلمه (agent/policy_replacements.txt، دوباره داده) درخواست میکند، بازنویسی میکند، دوباره بررسی میکند، و با CleanedDirection تایپشده تمام میکند. آن را در صفحه بخوانید؛ آن را تایپ نکنید.
quarantine = Agent(
name="quarantine",
model=config.MODEL,
mode="task",
instruction=QUARANTINE_INSTRUCTION,
tools=[find_policy_hits, suggest_replacement],
output_schema=CleanedDirection)
همانطور که ارسال شده است، زنجیره از انتخاب مستقیماً به اسکریپتنویس میرود. 👉🖥 روتر را بین آنها قرار دهید، با دو خروجی و راه برگشت از قرنطینه؛ سه خط جایگزین یک خط میشود:
(join_research, propose_directions, choose_direction, policy_check),
(policy_check, {"OK": scripter, "BLOCK": quarantine}),
(quarantine, scripter),
👉🖥 هر دو مسیر را اجرا کنید. ایده را همانطور که هست ارسال کنید: choose_direction گزینه ۱ را میگیرد، policy_check OK را صادر میکند، و نویسنده متن ساختاریافتهای مینویسد. روی جلسه جدید کلیک کنید و ایده را دوباره با , pick 4 در انتها ارسال کنید: نامزد ۴ طعمه است، policy_check BLOCK را منتشر میکند، قرنطینه ابزارهای خود را فراخوانی میکند، کلمات ردشده را بازنویسی میکند، finish_task را فراخوانی میکند، و جهت پاکشده به اسکریپتنویس بازمیگردد. ردیفهای درستیسنجی هر دو اجرا را از فضای ذخیرهسازی جلسه میخوانند.
اکنون هر جمله از پیامواره جلسه ۲ یک گره دارد: پژوهش دو خواننده و یک پیوند است، رد کردن یک رهیاب و یک عامل تکلیف است، و متن یک گره عامل است. در کل نمودار، انتخاب جایگزین دو گره تابع است، direction_gate و persist_direction، که خواهید دید مستقیماً اجرا میشوند تا زمانی که جلسه ۵ باعث توقف در شود. توافق با محتواساز هنوز موقتی است؛ پژوهش هم همینطور است، چون هنوز نظر مخاطبان را نشنیده است.
۶. ۴ · دریافت چندحالتی — آنچه مخاطب ارسال کرده است، در یک مجموعه خوانده میشود

👉🖥 در میزکار، جلسه ۴ · دریافت چندحالتی، بخشهای ۴الف و ۴ب را باز کنید. مخاطبان فقط نمینوشتند. یک خواننده برای هر نوع، هر چیزی را که ارسال کردهاند به نوشتاری با همان شکل تبدیل میکند، یک مجموعه RAG Engine همه آن را نگه میدارد، و خواننده سوم در توزیع گسترده آن را براساس معنی جستجو میکند و رسانه را برمیگرداند.
آنچه مخاطب ارسال کرده است (۴الف)
agent/comments.md دوازده نظر را که زیر شش ویدیو هالووین کانال نوشته شده است نگه میدارد. agent/audience/ بقیه را نگه میدارد: هشت یادداشت صوتی (voice/*.mp3)، چهارده عکس از اتاقهای خود بینندگان (photo/*.jpg) و شش کلیپ (clip/*.mp4)، یک حرکت آهسته روی یک عکس با صدای بیننده روی آن. صفحه 4a روی همه آنها باز میشود: هر یادداشت صوتی پخش میشود، هر عکس نشان داده میشود، هر کلیپ اجرا میشود.

موتور RAG نوشتار را بازیابی میکند. یادداشت صوتی، عکس، یا کلیپ فقط به همان روشی که پستکنندگان قبلی در rag.caption_posters انجام میدادند میتواند به مجموعه اضافه شود: یک مدل آن را به خطی از نوشتار تبدیل میکند که نام فایل آن است، این خط به یک متن تبدیل میشود، و وقتی متن برمیگردد، فایل هم با آن برمیگردد. agent/platform/audience.py یک خواننده برای هر نوع دارد. هرکدام یک فایل و یک پیامواره به Gemini ارسال میکند و یک خط برای هر فایل مینویسد:
VOICE_PROMPT = ("Transcribe this voice note word for word inside double quotes. Then add: mood: and one word. "
"Plain text on one line, nothing else.")
PHOTO_PROMPT = ("Two sentences: what is in the picture, the palette, the mood. Then: tags: and three short tags. "
"Plain text on one line, no headings, no markdown.")
CLIP_PROMPT = ("Describe the shots in two sentences, then quote what you HEAR on the soundtrack as: heard: \"...\". "
"Plain text on one line, no headings, no markdown.")
def _ask(path: pathlib.Path, mime: str, prompt: str) -> str:
"""One media file in, one line of text out."""
from google.genai import types as gt
client = images._client()
resp = client.models.generate_content(
model=config.MODEL,
contents=[gt.Part.from_bytes(data=path.read_bytes(), mime_type=mime), prompt],
config=gt.GenerateContentConfig(automatic_function_calling=gt.AutomaticFunctionCallingConfig(disable=True)))
return " ".join((resp.text or "").split())
def transcribe_voice() -> pathlib.Path:
"""voice note -> text: Gemini listens."""
files = sorted((AUDIENCE / "voice").glob("*.mp3"))
lines = []
for p in files:
said = _ask(p, "audio/mpeg", VOICE_PROMPT)
lines.append(f"- voice: {p.name} — {said}")
return _write(AUDIENCE / "voice" / "transcripts.md", "voice", "What Gemini heard in the audience's voice notes", lines)
caption_photos همین کار را با image/jpeg و PHOTO_PROMPT انجام میدهد؛ watch_clips با video/mp4 و CLIP_PROMPT، و Gemini تصویر را تماشا میکند و موسیقی متن را در یک تماس میشنود. هر خواننده شکل خط یکسانی را مینویسد، بنابراین تکهتکه کردن، جاسازی، و بازیابی هرگز نمیدانند که یک بخش از کدام نوع آمده است:
- voice: note-03.mp3 — "I want a whole series of the cat judging costumes ..." mood: delighted
- photo: photo-01-ghost-drawer.jpg — A cream wooden drawer, striped socks, a paper-cup ghost ... tags: drawer, socks, cozy
- clip: clip-05.mp4 — A hallway floor, a snail in a sheet and a round vacuum ... heard: "The snail won."
- poster: sock-ghost.png — A sheet ghost one inch above a striped sock ...

پیکره. Vertex AI RAG Engine بازیابی روی اسناد است: یک مجموعه به بخشهایی با حدود ۱۲۰ نشانه تقسیم میشود، هر بخش با text-embedding-005 جاسازی میشود، و پُرسمان به همان روش جاسازی میشود و با نزدیکترین بخشها ازنظر معنایی، نه ازنظر کلمات مشترک، پاسخ داده میشود. «cozy little hauntings at home» (تسخیرهای کوچک دنج در خانه) نظری را پیدا میکند که جادوی کوچک در اتاقهای کوچک را میخواهد، یادداشت صوتی درباره خفاشی که لامپ را بغل کرده است، و کلیپی که موسیقی متن آن میگوید روح یک لیوان کاغذی است.
corpus = rag.create_corpus(
display_name="vibestudio-feedback",
backend_config=rag.RagVectorDbConfig(
rag_embedding_model_config=rag.RagEmbeddingModelConfig(
vertex_prediction_endpoint=rag.VertexPredictionEndpoint(
publisher_model="publishers/google/models/text-embedding-005"))))
rag.upload_file(
corpus_name=corpus.name, path="agent/comments.md", display_name="comments.md",
transformation_config=rag.TransformationConfig(
chunking_config=rag.ChunkingConfig(chunk_size=120, chunk_overlap=20)))
راهاندازی ازقبل ایجاد مجموعه را شروع کرده است؛ ردیف اتصال صفحه 4a میگوید که آیا فعال است یا نه، و اگر راهاندازی نتواند آن را ایجاد کند، آن را ایجاد میکند. از دکمههای صفحه یا پایانه: python -m agent.platform.rag مجموعه را ایجاد میکند و آن را در runs/ragcorpus.json ضبط میکند؛ python -m agent.platform.rag load --audience هر خواننده را در audience.READERS اجرا میکند، سپس آنچه را که در کنار نظرات و زیرنویسهای پوسترها نوشتهاند، هر فایل را با نام خودش، بارگذاری میکند و منتظر پاسخ نمایه میماند؛ python -m agent.platform.rag query "cozy little hauntings at home" بازیابی را نشان میدهد که بخشهایی را پیدا میکند که هیچ کلمهای با پُرسمان مشترک ندارند.
ویرایش. همانطور که ارسال شده است، READERS فقط read_comments را نگه میدارد؛ سه کارتخوان رسانه پشت یک # قرار دارند . 👉🖥 یکبار فرایند انتقال را اجرا کنید: خروجی not read (no reader wired in audience.READERS): voice, photo, clip را نشان میدهد و فقط نظرات و زیرنویسهای ناشنوایان وارد میشوند. سپس # را حذف کنید :
transcribe_voice, caption_photos, watch_clips,
👉🖥 ذخیره کنید و دوباره جذب را اجرا کنید. خوانندگان حدود سه دقیقه برای چهل مورد وقت میگذارند؛ کنسول heard، saw، و watched را هربار یک خط میگوید و هر کارت در جدول با خط خودش پر میشود. ردیفهای تأیید فایل، خروجی خوانندگان، و مجموعه داده را میخوانند: چهار خواننده متصل شدهاند، هشت یادداشت صوتی شنیده شده است، چهارده عکس دیده شده است، شش کلیپ تماشا شده است، پنج فایل در مجموعه داده وجود دارد.
کارتخوان سوم (4b)
بازخورد مخاطب نوعی پژوهش است که در هر اجرا همرسانی میشود، بنابراین در جایی وارد نمودار میشود که پژوهش وارد میشود: بهعنوان خواننده سوم در پیوند. read_feedback در agent/graph.py ایده امشب را میگیرد، از پیکره نزدیکترین بخشها را میپرسد، و به join_research گزارش میدهد که منتظر سه مورد است. The edge (START, read_feedback, join_research) ships in stage5_rag/agent.py, the shape session 3 taught; your edit is inside the node. بازیابی، نه فراخوانی مدل، و قطعی: ایده یکسان گذرگاههای یکسانی را پیدا میکند درحالیکه پیشنهاددهنده متفاوت است.
def read_feedback(node_input):
"""The third reader: what the audience sent - comments, and what Gemini
heard and saw in their voice notes, photos and clips - the passages
nearest to tonight's idea, and the media those passages came with.
Retrieval, not a model call."""
from .platform import rag
idea = idea_text(node_input)
query = idea or "what viewers liked and what they complained about"
try:
hits = rag.retrieve(query)
except Exception as e:
print(f" [rag] feedback unavailable ({str(e)[:80]})")
return Event(output={"query": query, "feedback": [], "media": [],
"note": "no corpus connected - run: python -m agent.platform.rag"})
return Event(output={"query": query, "feedback": [h["text"] for h in hits]}) # TODO: RAG_MEDIA
هر خط در مجموعه که از یادداشت صوتی، عکس، کلیپ، یا پوستر آمده باشد، نام فایل آن را ذکر میکند. rag.media_in(hits) این نامها را از متنها میخواند و آنها را به نشانیهای وب تبدیل میکند که صفحه میتواند نشان دهد و پخش کند. 👉🖥 رسانه را همراه با بخشهای متن برگردانید تا چیزهایی که مخاطب ارسال کرده است همراه با آنچه گفته است منتقل شود:
return Event(output={"query": query, "feedback": [h["text"] for h in hits],
"media": rag.media_in(hits)})
👉🖥 stage5_rag را با یک ایده اجرا کنید. read_feedback را باز کنید: پرسمان ایده شما است، برونداد دارای پنج نزدیکترین بخش و یک فهرست media با یک نشانی وب برای هر فایل است. باز کردن join_research: سه کلید. propose_directions را باز کنید: نامزدهای ۱ تا ۳ در شواهدشان به feedback استناد میکنند. پانل درستیسنجی نشان میدهد که خواننده به چه چیزی در زیر ردیفها نگاه کرده است: یک کلیپ، سه یادداشت صوتی، و یک پوستر برای روح در نور یخچال.

۷. ۵ · تولید ویدیو — دو نوع تعلیق، یک سازوکار

👉🖥 در میزکار، جلسه ۵ · تولید ویدیو، بخشهای ۵الف و ۵ب، ساختهشده در stage6_video، کل نمودار را باز کنید. کلیپ چند دقیقه و پوستر چند ثانیه طول میکشد و اجرا دو بار در مسیر متوقف میشود: یک بار برای شما، یک بار برای Veo. این دو توقف یک سازوکار هستند و دو شعبه این میزکار آنچه را که ساختهاند با نام همرسانی میکنند.
دو نوع تعلیق (5a)
هیچچیز منتظر نمیماند. اجرایی که باید متوقف شود، یک تماس باز در جلسه خود مینویسد و پایان مییابد؛ هر کسی که به آن تماس پاسخ دهد، چه شخص باشد و چه فرایند، آن را ادامه میدهد. صفحه در یک جدول باز میشود:
برای یک نفر · | برای ابزار کند · | |
کسی که دویدن را متوقف میکند | |
|
منتظر چه چیزی است | یک نفر: یکی از چهار کارت | یک کار آهسته: عملکرد Veo، چند دقیقه |
آنچه در جلسه قرار دارد | یک تماس | یک تماس |
نحوه ازسرگیری آن | یک | یک |
کسی که آن را ارسال میکند | | |
جایی که در این مدت زندگی میکند |
| the same row, the same id · nothing runs |

برای یک نفر. در یک پیامواره، «از محتواساز بخواهید تأیید کند» توصیهای است و جلسه ۲ در یک پیام از آن گذشت. در گردش کار، مکث مربوط به موتور است. گرهای که RequestInput را تولید میکند، اجرا را تعلیق میکند: ADK یک فراخوانی باز به نام adk_request_input را با message، response_schema، و payload شما در جلسه مینویسد، به آن یک interrupt_id میدهد، و به فراخواننده برمیگردد. هیچ رشتهای منتظر نیست. تنها چیزی که آن را ازسر میگیرد function_response است که آن شناسه را حمل میکند و با طرحواره شما اعتبارسنجی شده است؛ پیام گپ به گردش کار یک نوبت جدید است، نه پاسخ. طرحواره همان فرم است: adk web آن را ارائه میکند و برنامه VibeStudio همان طرحواره را بهصورت چهار کارت ارائه میکند. direction_gate ازقبل نامزدها را در وضعیت مینویسد. 👉🖥 متوقف کردن:
yield RequestInput(
message="Pick tonight's direction: 1, 2, 3 or 4.",
response_schema={
"type": "object",
"properties": {
"pick": {"type": "string", "enum": ["1", "2", "3", "4"]}}},
payload={"candidates": cands})
پاسخ شما به persist_direction بهعنوان node_input آن میرسد، یک دیکته با یک کلید، pick. چهار نامزد به روش دیگری میرسند: دروازه آنها را در وضعیت مشترک تحت candidates نوشت و ADK کلید وضعیت را به پارامتری با همان نام متصل میکند. گره انتخاب را به یک نامزد تبدیل میکند و direction، angle، hook و user:prefs را با Event(state=...) بازنویسی میکند. کلیدهای دارای پیشوند user: در جلسههای آن کاربر باقی میمانند.
برای ابزار کند. پرداخت Veo چند دقیقه طول میکشد و گراف نباید اینقدر منتظر بماند. render_submit در agent/desk.py کار را شروع میکند و بلافاصله با رسیدی که وضعیت آن pending است برمیگردد:
def render_submit(prompt: str) -> dict:
"""Submit one Veo render of `prompt`. Returns at once with a pending
receipt; the clip is delivered later, to this call, by id."""
receipt = videogen.start(f"{prompt} {videogen.NO_TEXT}")
return {"status": "pending", "operation": receipt["operation"], "prompt": receipt["prompt"]}
این رسید که در LongRunningFunctionTool پیچیده شده است، برای ADK معنایی دارد: نوبت عامل به پایان میرسد، گردش کار در render_desk تعلیق میشود، و تماس باز با شناسه و رسید آن ردیفی در runs/sessions.db است. 👉🖥 در stage6_video/agent.py، ابزار را بپیچید:
tools=[LongRunningFunctionTool(render_submit)])
ازسرگیری با شناسه، از فرایندی دیگر. agent/deliver.py تماس معلقه را در مخزن جلسه پیدا میکند، Veo را تا زمانی که کلیپ وجود داشته باشد نظرسنجی میکند، نتیجه را در runs/state.json مینویسد، و همان جلسه را با شناسه آن تماس به function_response ارسال میکند. 👉🖥 بخش پاسخ را بسازید:
part = Part(function_response=FunctionResponse(
id=row["call_id"], name=row["name"], response=response))
👉🖥 stage6_video را اجرا کنید تا دو بار متوقف شود. ایدهای ارسال کنید: اجرا روی فرمی که از طرحواره شما ساخته شده است متوقف میشود، چهار نامزد در محموله آن. 2 را در pick تایپ کنید و ارسال کنید؛ نمودار در persist_direction ازسر گرفته میشود. آزمایش توقف را اینجا امتحان کنید: در فرم تعلیق کنید، سرور را متوقف کنید (scripts/stop.sh در برگه ۱)، آن را دوباره راهاندازی کنید (scripts/restart.sh)، جلسه را دوباره باز کنید. فرم هنوز پابرجا است، زیرا مکث یک ردیف است، نه یک رشته. پساز script_ready، render_desk با render_submit تماس میگیرد، رسید را دریافت میکند، به WAITING پاسخ میدهد، و اجرا بهپایان میرسد؛ جلسه شناسه تماس را نگه میدارد. ارسال را از کنسول یا برگه ۱ صفحه اجرا کنید:
cd ~/vibe-studio-multimodal
uv run python -m agent.deliver status # the pending renders, by session, call id and operation
uv run python -m agent.deliver # wait for Veo, answer the newest pending call by id
👉🖥 روی بازآوری وب adk کلیک کنید: function_response تماس معلقه را دنبال میکند، join_media منتشر میشود، و store_release اجرا میشود. هشت ردیف تأیید دو توقف را از جلسه میخوانند: تماس دروازه و پاسخ شما، رسید رندر و تحویل آن.
وضعیت و میزکار (5b)
میز رسانه دوباره ساختار 3a است، یک فناوت و یک اتصال، با یک شاخه کند. script_ready متن را به دو روش ارسال میکند. render_desk کلیپ را ارسال میکند و اجرا بهحالت تعلیق درمیآید. make_poster ابزار جلسه ۲ بهعنوان گره است: poster_idea نوشتار، سبک کانال، و آنچه «بانک حافظه» درباره سلیقه محتواساز در پوسترها میداند (که از وضعیت بهعنوان memory_facts، جلسه ۶ محدود شده است) به مدل تصویر میرود و یک PNG با نسبت ابعاد ۹:۱۶ در چند ثانیه برمیگردد. join_media منتظر پاسخ کندتر میماند و store_release آنچه را که پیدا کرده است در وضعیت مینویسد.
همرسانی براساس نام. store_release هرگز پوستر را دریافت نمیکند. پارامتری بهنام poster_url را اعلام میکند و ADK آن را از وضعیت مشترک مقید میکند، زیرا گرهای در شاخه دیگر آن کلید را نوشته است. هیچ بار دادهای بین شاخهها جابهجا نمیشود:
def store_release(node_input, poster_url: str = "", poster_path: str = ""):
"""`poster_url` and `poster_path` are not passed by anyone: ADK binds them
from shared STATE because the parameter names match the keys make_poster
wrote (the STATE_SHARE edit). The two branches of the desk never hand each
other payloads; they share what they made by name."""
render = state.load().get("render") or {} # the delivered clip, written by deliver
url, status = render.get("url") or "", render.get("status", "")
...
if not poster_url:
said += " · poster: not in state (did make_poster write poster_url to state? the STATE_SHARE edit)"
yield Event(output={"status": status, "url": url, "poster_url": poster_url},
state={"render_url": url, "render_status": status, "poster_url": poster_url},
message=f"video: {said} · poster: {poster_url or 'none'}")
همانطور که ارسال شده است، make_poster پوستر را بهعنوان برونداد و پیام ارائه میدهد و store_release میگوید poster: not in state. 👉🖥 نوشتن وضعیت را به بازده نهایی آن اضافه کنید:
state={"poster_url": rec.get("url", ""), "poster_path": rec.get("path", "")},
message=f"poster: {said}")
👉🖥 یکبار دیگر اجرا کنید و تحویل دهید. رویداد make_poster را باز کنید: نشانی وب پوستر در خروجی آن و دلتای وضعیت با poster_url. store_release را باز کنید: پیام آن نام پوستر را نشان میدهد. بلوک جلسه این اجرا در صفحه همان چیزی را نشان میدهد که برگه «وضعیت» ردیف adk web نشان میدهد: شناسه جلسهای که ازسرگیری نیاز دارد، تماسی که باز است، و کلیدهایی که گرهها نوشتهاند، poster_url ازجمله آنها. پانل درستیسنجی کلیپ و پوستر را کنار هم نشان میدهد.

۸. ۶ · حافظه — آنچه کانال بین شبها بهخاطر میآورد

👉🖥 در میزکار، جلسه ۶ · حافظه، بخشهای ۶الف و ۶ب، ساختهشده در stage4_memory را باز کنید. جلسه ۵ درباره وضعیت یک اجرا بود. این جلسه درباره چیزهایی است که کانال بین اجراها بهخاطر میآورد: آنچه نمودار از بیرون، ازطریق دو فراخوانی برگشتی، میشنود.
Memory Bank (6a)
«بانک حافظه Vertex AI» حافظه بلندمدت مدیریتشده درباره یک شخص است. حقایق در یک محدوده، نام برنامه و شناسه کاربر، و تحت موضوعاتی که شما تعریف میکنید، قرار دارند. وقتی مکالمهای با memories.generate را به آن میدهید، آنچه را که با موضوع مطابقت دارد استخراج میکند، آن را جاسازی میکند، و آن را با آنچه ازقبل دارد ادغام میکند: یک واقعیت جدید ایجاد میشود، واقعیت تغییریافته بهروز میشود، و واقعیت تکراری ادغام میشود. memories.retrieve حقایق مربوط به یک دامنه را برمیگرداند. هر دو تماس در agent/platform/memory.py قرار دارند.
SCOPE = {"app_name": config.APP, "user_id": config.USER}
TOPICS = {
"CREATOR_TASTE": "Which video directions this creator picks and passes on, "
"and how that preference changes over time.",
"CHANNEL_RULES": "Standing instructions the creator states for every video "
"(style, subjects to avoid, format rules).",
"VISUAL_STYLE": "What this creator approves and rejects in a poster: palettes, "
"framing, props and mood of the posters they shipped.",
}
موضوع سوم سلیقه کانال در انتخاب تصاویر است. این نسخه در جلسه ۵ به آن نوشت: یک پوستر ارسالشده در یک جمله توصیف و بهخاطر سپرده میشود، و پوستر بعدی با آن جمله در پیاموارهاش ترسیم میشود.
راهاندازی قبلاً ایجاد بانک را شروع کرده است؛ ردیف اتصال صفحه 6a میگوید که آیا فعال است یا نه. از دکمههای صفحه یا پایانه: python -m agent.platform.bank بانک و موضوعات آن را ایجاد میکند و نام منبع را در runs/memorybank.json ذخیره میکند؛ python -m agent.platform.bank load چهار جلسه گذشته را تغذیه میکند (دو دوره حیوانات با قانون سبک، یک دوره ابزار، یک دوره فانتزی اخیر)؛ python -m agent.platform.bank list نشان میدهد که ترانویسیها به چه چیزی تبدیل شدهاند: اظهارات واقعیت تلفیقی، نه ترانویسیها.
تماسهای برگشتی (۶ب)
حافظه گره نیست. فراخوانی تابعی است که به Agent ارسال میکنید؛ ADK آن را در لحظهای ثابت با بافت زنده فراخوانی میکند و برگرداندن None اجازه میدهد اتفاق عادی رخ دهد. سه جفت: در اطراف نوبت نماینده (before_agent_callback، after_agent_callback، با CallbackContext)، در اطراف هر تماس مدل (before_model_callback با LlmRequest، after_model_callback با LlmResponse)، در اطراف هر تماس ابزار. حفاظها، دورسنجی، حافظههای پنهان، و تزریق بافت در اینجا قرار میگیرند، بدون اینکه گرهی در نمودار داشته باشند.
در stage4_memory/agent.py، propose_directions قبلاز فراخوانی مدل، بانک را میخواند: recall_taste حقایق را واکشی میکند، آنها را بهعنوان بلوک «حافظه» به درخواست اضافه میکند (قدیمیترینها در ابتدا)، و به مدل میگوید که گزینههای ۱ تا ۳ را به سمت سلیقه فعلی متمایل کند و با قوانین بهعنوان محدودیت برخورد کند. همچنین حقایق را بهصورت memory_facts مینویسد تا make_poster آنها را پیدا کند. 👉🖥 آن را سیمکشی کنید:
output_schema=Directions,
before_model_callback=recall_taste)
فیلمنامهنویس آنچه را که محتواساز پساز نوبتش انتخاب کرده است مینویسد: remember_pick direction را از وضعیت میخواند، یک جمله درباره انتخاب مینویسد، و آن را به memories.generate میدهد. 👉🖥 آن را سیمکشی کنید:
output_schema=Script,
after_agent_callback=remember_pick)
👉🖥 با پیام خالی اجرا کنید و کارتی را انتخاب کنید. رویداد propose_directions را باز کنید: درخواست شامل سه دوره است و نامزدهای ۱ تا ۳ به سلیقه فعلی گرایش دارند، درحالیکه گرایشها چیز دیگری را نشان میدهند. بعداز اسکریپتنویس، روی نمایش بانک کلیک کنید: یک خاطره برای انتخاب امشب تغییر کرد یا اضافه شد. شب دوم را اجرا کنید: شب اول را بهخاطر میآورد.
۹. ۷ · استقرار و انتشار — نمودار داخل برنامه، شب در VibeTube

👉🖥 در میزکار، جلسه ۷ · استقرار و انتشار، بخشهای ۷الف و ۷ب را باز کنید.
Deploy VibeStudio (7a)
در محیطهای آزمایشی، adk web نمودار را هدایت کرد. VibeStudio آن را با Runner ADK در یک رشته عامل با حلقه رویداد خودش هدایت میکند و هر رویداد را در یک حالت اجرا که صفحه ازطریق «رویدادهای ارسالشده از سرور» میخواند ادغام میکند: خوانندگان، انتخاب شما بهعنوان چهار کارت، پوستر هنگام فرود، رندر هنگام تحویل. پاسخ دروازه و تحویل رندر یک تماس هستند، یک بخش function_response، و برنامه بهجای منتظر ماندن برای کنسول، خودش Veo را نظرسنجی میکند.
self._svc = DatabaseSessionService(db_url=config.DB_URL)
self._runner = Runner(app_name=config.APP, agent=wf, session_service=self._svc)
async for ev in self._runner.run_async(user_id=config.USER, session_id=run_id, new_message=message):
self._absorb(ev) # fold the ADK event into the run state, publish one app event
part = Part(function_response=FunctionResponse(id=call_id, name=name, response=response))
vibestudio/server/agent/ کپی بایتبهبایت از agent/ آزمایشگاه است که توسط checks/verify_app.py بررسی شده است. فهرست کامل لبهها در agent/graph.py شامل همه ساختارهای این codelab در یک مکان است، سیزده گره؛ این فهرست بهصورت پرشده ارائه میشود تا شما آن را بخوانید، نه اینکه تایپ کنید:
(START, scan_trends, join_research),
(START, read_backlog, join_research),
(START, read_feedback, join_research),
(join_research, propose_directions, direction_gate,
persist_direction, policy_check),
(policy_check, {"OK": scripter, "BLOCK": quarantine}),
(quarantine, scripter),
(scripter, script_ready),
(script_ready, render_desk, join_media),
(script_ready, make_poster, join_media),
(join_media, store_release),
👉🖥 با دکمه استقرار میزکار استقرار دهید، یا 👉💻 از برگه ۱:
cd ~/vibe-studio-multimodal
uv run python vibestudio/deploy.py
که با مقادیر پرشده از .env شما اجرا میشود:
gcloud run deploy vibestudio --source vibestudio \
--project $GOOGLE_CLOUD_PROJECT --region us-central1 \
--labels dev-tutorial-codelab=vibetube --allow-unauthenticated \
--memory 2Gi --cpu 2 --timeout 3600 --concurrency 40 \
--max-instances 1 --min-instances 1 --session-affinity \
--set-env-vars GOOGLE_CLOUD_PROJECT=...,STUDIO_VERTEX=1,STUDIO_MEMORY_BANK=...,STUDIO_RAG_CORPUS=...,VIBETUBE_URL=...,VIBETUBE_EVENT=...,VIBETUBE_NAME=...,VIBETUBE_PROJECT=...
--source محتوی را با Cloud Build میسازد و آن را در یک مرحله مستقر میکند؛ ساخت اولیه چهار یا پنج دقیقه طول میکشد. --session-affinity با یک نمونه گرم، اجرای تعلیقشده را در نمونهای که آن را نگه میدارد حفظ میکند: ذخیرهسازی جلسه از جلسه ۵، یک بار دیگر. در استقرار بزرگتر، فضای ذخیرهسازی جلسه به VertexAiSessionService منتقل میشود و وابستگی ازبین میرود. ساخت و سرویس بهعنوان حساب سرویس محاسباتی پیشفرض پروژه اجرا میشوند که راهاندازی نقشهای موردنیاز را به آن اعطا کرده است.
👉🖥 نشانی وب سرویس را باز کنید و یک شب را در برنامه اجرا کنید: یک ایده، یک انتخاب، منتظر کلیپ و پوستر بمانید. برگه «سابقه» هر دو را نگه میدارد.
منتشر کردن (7b)
انتشار یک POST چندبخشی از برنامه به نقطه پایانی اتاق در VibeTube، در vibestudio/server/platform/publish.py است. کلیپ همان ویدیو است، پوستر همان ریزعکس است، و نمایه نام اتاق و شما را نشان میدهد:
url = f"{prof['platform_url']}/api/events/{prof['event_code']}/videos"
data = {"title": state.script["title"], "description": state.script["description"],
"duration": "0:08", "displayName": prof["display_name"],
"projectId": prof["project_id"]} # one video per project and room: publishing again replaces it
files = {"videoFile": (clip.name, clip.open("rb"), "video/mp4"),
"thumbnailFile": (poster.name, poster.open("rb"), "image/png")} # the poster, not a frame
r = httpx.post(url, data=data, files=files, timeout=120)
video_id = r.json()["id"] # watch it at {platform}/e/{event}?v={video_id}
VibeTube یک ویدیو برای هر projectId و اتاق نگه میدارد و پروژهای که برنامه ارسال میکند پروژه Google Cloud شما است، بنابراین انتشار مجدد ویدیو شما را جایگزین میکند بهجای اینکه ویدیو جدیدی اضافه کند. پوستر جایگزین هرگز بارگذاری نمیشود؛ پوستر واقعی همان ریزعکس است، بایت به بایت.
آخرین ویرایش یک پیکربندی است، نه یک خط کد. 👉🖥 در صفحه 7b: نشانی وب VibeStudio (که از استقرار 7a پر شده است)، کد رویداد اتاق، دقیقاً همانطور که VibeTube آن را هجی میکند (کدی که به راهاندازی دادید، مگراینکه مربیتان کد دیگری را باز کرده باشد)، و نام شما. انتشار را فشار دهید. میز کار کلیک را به برنامه منتقل میکند، برنامه شب را پست میکند، و ردیفهای زیر نتیجه را از برنامه و از VibeTube میخوانند: برنامه پاسخ میدهد، شب با یک پوستر واقعی تمام میشود، کد نام یک اتاق باز را نشان میدهد، VibeTube شناسه ویدیو را برمیگرداند، ویدیو روی دیوار است، و ریزعکس آن پوستر شما است. کارت نتیجه ریزعکس را در VibeTube در کنار پوستر در برنامه نشان میدهد.

۱۰. آنچه ساختهاید
👉🖥 بخش پایانی صفحه 7b کل نمودار را ترسیم میکند، سیزده گره از یک ایده تا یک کلیپ و پوستر آن؛ برای دیدن عملکرد هر گره و جلسهای که آن را ساخته است، نشانگر را روی آن نگه دارید.
جلسه | آنچه آموزش داد | نوشتهشده بهصورت |
۲ · تولید تصویر | عامل: مدل، دستورالعمل، ابزارها؛ مدل تصویر بهعنوان ابزار؛ آنچه پیامواره نمیتواند تضمین کند |
|
۳ · گردش کار | فهرست لبه، فناوت و اتصال، رهیاب با حلقه حالت تکلیف در پشت آن | |
۴ · دریافت چندحالته | یک خواننده برای هر نوع، یک شکل خط، یک پیکره؛ خواننده سوم براساس معنی، با رسانه | |
۵ · تولید ویدیو | Two kinds of suspension, one mechanism; the session store; the poster shared by name | |
۶ · خاطره | Memory across nights, two callbacks |
|
۷ · مستقر و منتشر کردن | نمودار داخل برنامه، در Cloud Run؛ شب در VibeTube | |
پنج قانونی که نمودار از آنها پیروی میکند:
- بهجای منتظر ماندن، تعلیق کنید. مکث یک ردیف در فضای ذخیرهسازی جلسه است، نه یک رشته.
- همه چیز را به همان روش ازسر بگیرید — با شناسه تماس باز، چه شخص پاسخ دهد چه فرایند.
- دادهها را براساس نام در وضعیت همرسانی کنید، نه با رشته کردن آن در هر بار داده.
- قبلاز پرداخت هزینه مدل، با کد تصمیم بگیرید. خطمشی یک فایل نوشتاری و یک مسیریاب است و قبلاز رندر اجرا میشود.
- هر چیزی را که نوشتار نیست قبلاز رسیدن به پیکره به نوشتار تبدیل کنید، بهطوریکه نام فایل در خط باشد تا فایل بتواند برگردد.
پیامواره نمیتواند ترتیب، توقف، یا بررسی را تضمین کند. گراف میتواند.
مرحله بعدی چیست
VertexAiSessionServiceبرای جلسههایی که بیشتر از یک نمونه دوام میآورند، و--session-affinityرا رها کنید.- یک وبقلاب که بهجای نظرسنجی به تماس رندر پاسخ میدهد: همان
FunctionResponse(id=...)، فرستنده متفاوت. - یک
RequestInputدوم قبلاز انتشار — طرحواره همان فرم است، بنابراین یک گره است و کد صفحه ندارد. - نظرات جدید، یادداشتهای صوتی، و کلیپهای مخاطبان پساز هر انتشار به مجموعه اضافه میشود و نام کاربری ارسالکننده نیز همراه آنها خواهد بود.
مرتبسازی
هر چیزی که این آزمایشگاه ایجاد کرده است در پروژه مرحله ۴ ساخته شده است، بنابراین ارزانترین پاکسازی حذف پروژه است. در Cloud Shell:
gcloud projects delete $(cat ~/project_id.txt)
برای حفظ پروژه و برداشتن فقط بخشهای پولی: سرویس Cloud Run (gcloud run services delete vibestudio --region us-central1)، پیکره RAG و Memory Bank (نام منابع آنها در runs/ragcorpus.json و runs/memorybank.json است و صفحههای Agent Engine و RAG Engine در کنسول آنها را حذف میکنند)، و تصویر محفظه در Artifact Registry.