1. Pengantar

Anda mengelola channel bertema seram tapi nyaman di VibeTube: satu video Shorts setiap malam hingga Halloween. Audiens menginginkan keajaiban kecil di ruangan kecil dan mengirimkannya kepada Anda dengan segala cara yang mereka bisa — sebagai komentar, catatan suara, foto laci berhantu mereka sendiri, dan klip teras mereka saat senja. Backlog penuh dengan ide. Setiap malam, ia melakukan rutinitas yang sama: mencari tahu apa yang sedang ramai di feed, menyisir backlog, membaca kiriman audiens, memilih arah, memeriksa kesesuaiannya dengan kebijakan channel, menulis skrip, menggambar poster, merender klip, dan memublikasikannya.
Model generatif dapat melakukan setiap tugas sendirian. Gemini melakukan riset, mendengarkan, melihat, dan menulis; model gambar menggambar; Veo merender. Hal yang tidak dapat mereka lakukan sendiri adalah menjalankan malam secara berurutan, berhenti untuk satu keputusan yang menjadi milik Anda, menolak arahan yang buruk sebelum menghabiskan uang, menunggu beberapa menit untuk rendering tanpa membiarkan apa pun terbuka, dan meninggalkan catatan.
Codelab ini mengubah malam itu menjadi ADK Workflow: grafik node dan edge yang digambar dan berjalan sendiri, hanya menanyakan di mana penilaian Anda diperlukan, dan mengakhiri setiap proses dengan klip dan posternya di VibeTube.
Perintah tidak dapat menjanjikan urutan, penghentian, atau pemeriksaan. Grafik bisa.
Yang akan Anda pelajari
- Satu agen, dan model gambar sebagai alat.
Agentadalah model, petunjuk, dan alat. Alat ketiga memanggil model gambar, dan operasi pertama berakhir dengan poster. Kemudian, kalimat yang mendorong sisa lab: perintah tidak dapat menjanjikan urutan, penghentian, atau pemeriksaan. - Struktur yang membentuk grafik.
Workflowadalah daftar edge. Anda membangun fan-out dan join (pembaca paralel yang bertemu diJoinNode) dan router yang memberi nama keluarannya sendiri, dengan agen mode tugas di belakang keluaran yang ditolak yang membersihkan arah dalam loop panggilan alat dan bergabung kembali ke jalur utama. - Pipeline penyerapan multimodal. Empat jenis hal yang dikirim audiens, satu pembaca per jenis: Gemini mentranskripsikan catatan suara, memberi teks pada foto, menonton klip dan mendengarkan soundtrack-nya, dan setiap pembaca menulis bentuk baris yang sama ke dalam satu korpus RAG Engine. Pembaca ketiga dalam fan-out mengambil berdasarkan makna dan mengembalikan media beserta bagiannya.
- Dua jenis penangguhan, satu mekanisme. Proses berhenti untuk seseorang (
RequestInput: empat kartu, Anda menjawab) dan untuk alat yang lambat (Veo di belakangLongRunningFunctionTool: tanda terima). Keduanya adalah hal yang sama: panggilan terbuka di penyimpanan sesi, yang dilanjutkan dengan ID-nya, dari formulir atau dari proses lain. Dua cabang meja media membagikan poster berdasarkan nama, dalam status. - Memori sepanjang malam. Dua callback siklus proses di Memory Bank:
before_model_callbackmengingat selera kreator sebelum pengusul membaca,after_agent_callbackmengingat pilihan malam ini setelah penulis skrip. - Rilis. Grafik di dalam aplikasi dengan ADK
Runner, Peristiwa yang Dikirim Server ke halaman, Cloud Run dengan afinitas sesi, dan satu POST multibagian yang menempatkan klip di VibeTube dengan poster sebagai thumbnail-nya.
Yang akan Anda buat
Aplikasi yang berjalan di browser Anda: VibeStudio Workbench, halaman pendamping dengan editor kode dalam halaman, pemverifikasi yang membaca file di disk dan sesi yang ditulis oleh operasi Anda, serta adk web yang disematkan. Tujuh sesinya adalah tujuh halaman codelab ini, dengan nomor dan huruf bagian yang sama, sehingga keduanya tetap selaras. Setiap halaman akan terbuka di panel yang menjelaskan sesi dalam satu kalimat dan tiga hal yang Anda lakukan di sana: mengedit, menjalankan, memverifikasi. Baca bagian di sini, lalu lakukan pengeditan di workbench; setiap pengeditan diakhiri dengan panel verifikasi yang menunjukkan apa yang sebenarnya terjadi.
Tiga hal yang berjalan: Workbench (halaman di port 4600, editor, pemverifikasi), backend Anda (Workflow di agent/ dan satu aplikasi sandbox per sesi di stage0_prompt/ hingga stage6_video/, yang disajikan oleh adk web), dan Google Cloud (Gemini, model gambar, RAG Engine, Veo, Memory Bank — semuanya dijangkau melalui Vertex AI dengan kredensial Anda sendiri).
Tujuh sesi
Sesi | Yang Anda bangun | Hasil edit Anda |
1 · Cerita | Belum ada — malam, dan grafik yang terbentuk | — |
2 · Pembuatan gambar | Satu agen menggambar poster pertama |
|
3 · Alur kerja | Malam sebagai grafik: fan-out dan join, router dan loop-nya |
|
4 · Penyerapan multimodal | Apa yang dikirim audiens, dibaca menjadi satu korpus; pembaca ketiga |
|
5 · Pembuatan video | Dua jenis penangguhan; poster yang dibagikan berdasarkan nama di negara bagian |
|
6 · Memori | Informasi yang diingat saluran dari malam ke malam |
|
7 · Men-deploy dan memublikasikan | VibeStudio di Cloud Run; malam di VibeTube | kode acara ruang, dan publikasikan |
Cara menggunakan codelab ini. Codelab ini mengutamakan konsep: setiap sesi menjelaskan ide, lalu menampilkan kode sebagaimana dibaca setelah perubahan diterapkan dan membahasnya. Alat ini tidak pernah meminta Anda mengedit file di sini — pengeditan terjadi di workbench, yang memberi Anda satu pengeditan dalam satu waktu, petunjuk di sampingnya, dan panel verifikasi di bawahnya. Baca sesi, lalu biarkan halaman memandu Anda.
Tempat penempatannya
Grafik, tema, dan ruang di VibeTube berasal dari VibeStudio Haunted Edition milik Lin; ini adalah potongan multimodal, yang berfokus kembali pada apa yang dikirim audiens dan pada dua jenis suspensi, dalam format interaktif satu halaman dari lab Agent Valley. Studio adalah pemandangan; setiap mekanisme di bawahnya adalah mekanisme biasa yang akan Anda gunakan untuk pipeline dukungan, alur peninjauan konten, atau hal lain yang harus dijeda untuk seseorang dan menunggu tugas yang lambat.
2. 🧰 Penyiapan
- Apa — akun yang dapat membayar, project, lab yang diinstal, model yang terbukti dapat menjawab, dan workbench yang berjalan di latar belakang pada satu port.
- Alasan — satu proses di satu port: halaman, editor, pemverifikasi,
/api/*, danadk webmemiliki origin yang sama, sehingga tidak ada yang memerlukan terminal kedua atau port kedua di lab ini. - Cara — clone, dua skrip. Notebook kedua melakukan panggilan model yang sebenarnya, memulai workbench, dan mulai membuat korpus RAG dan Memory Bank saat Anda membaca, sehingga sesi 4 dan 6 menemukannya siap.
Semua hal dalam lab ini berjalan di Cloud Shell — gcloud, Python, Node, ffmpeg, dan git sudah diinstal di sana, dan Gemini, model gambar, Veo, RAG Engine, dan Memory Bank semuanya dijangkau melalui Vertex AI menggunakan kredensial Anda sendiri. Tidak ada kunci API di mana pun dalam lab ini, dan tidak ada yang perlu diinstal di laptop Anda. Laptop dengan Google Cloud SDK, Python 3.12, dan Node 20+ berfungsi dengan cara yang sama.
Ada satu hal yang tidak dapat diberikan lab ini kepada Anda: project Google Cloud dengan penagihan yang diaktifkan. Langkah 1 memberi Anda akun yang dapat membayar project; langkah 4 mengubah akun tersebut menjadi project. Lakukan langkah-langkah tersebut sesuai urutan — setiap langkah setelah langkah 4 akan membaca project yang ditetapkan pada langkah 4.
1 · Dapatkan akun dengan penagihan (~1 mnt)
Login dengan Akun Google yang penagihannya diaktifkan. Akun apa pun dapat digunakan. Jika tidak, mulai uji coba gratis; uji coba ini akan memberi Anda akun penagihan, yang merupakan satu-satunya hal yang diperlukan di langkah 4. Jika Anda mengikuti lab yang dipandu instruktur, tukarkan kredit untuk sesi ini terlebih dahulu — tindakan ini akan membuat akun penagihan untuk Anda.
2 · Buka Cloud Shell(~2 mnt)
👉🖥 Klik Activate Cloud Shell di bagian atas Konsol Google Cloud — ikon berbentuk terminal di header.

👉🖥 Klik Open Editor (ikon folder terbuka dengan pensil). Tindakan ini akan menampilkan Cloud Shell Editor dengan file explorer di sebelah kiri — tempat Anda membuka setiap kali ingin membaca seluruh file.

👉🖥 Jika Anda diminta untuk memberikan otorisasi kapan saja hari ini, klik Authorize untuk melanjutkan.
👉🖥 Buka terminal di dalam editor — yaitu tab 1, dan setiap langkah 👉💻 dijalankan di sana. Tab 2 adalah workbench itu sendiri, dan Anda membukanya di langkah 7. Jika terminal belum berada di bagian bawah layar, buka dengan View → Terminal:

👉💻 Periksa akun yang Anda gunakan untuk login — akun tersebut harus sama dengan akun dari langkah 1:
gcloud auth list
3 · Dapatkan lab (~1 mnt)
👉💻 Buat clone repo:
cd ~
git clone https://github.com/cuppibla/vibe-studio-multimodal
cd vibe-studio-multimodal
4 · Membuat project (~2 mnt)
👉💻 Satu skrip menemukan akun penagihan Anda, membuat project di dalamnya, menautkan penagihan, dan mengarahkan Cloud Shell ke project tersebut:
cd ~/vibe-studio-multimodal
./setup_project.sh
Anda akan melihat ✓ untuk akun penagihan yang dipilih, ✓ untuk project yang dibuat, dan akhiran seperti:
Project ready: vibe-studio-4271 Next: ./setup_codelab.sh
vibe-studio-4271 adalah project ID Anda — project ID Anda akan memiliki digit yang berbeda. Empat hal terjadi di sana:
billing | mengambil akun | |
project | membuat | |
selects | menulis ID ke | |
waits | melakukan polling hingga project benar-benar diizinkan untuk ditayangkan, hingga dua menit | |
👉🖥 Project yang sama kini ada di konsol — ID-nya ada di kartu Project info di Dashboard:

5 · Menginstal dan menyiapkan lab (~4 mnt)
👉💻
cd ~/vibe-studio-multimodal
./setup_codelab.sh
Satu skrip, aman untuk dijalankan lagi — eksekusi kedua akan mempertahankan setiap jawaban yang Anda berikan pada eksekusi pertama. Perintah ini akan mengajukan dua pertanyaan di awal, dan Enter akan mengambil nilai default untuk keduanya:
kode acara | ruang VibeTube tempat video Anda dipublikasikan di sesi 7. Ketik kode ruang dari instruktur Anda, atau tekan Enter untuk | |
nama tampilan | nama yang digunakan ruang untuk memberi Anda kredit, atau Enter untuk nama yang dibuat dari akun Anda. | |
Keduanya akan ditampilkan di .env dan dapat diubah nanti, di .env atau di halaman publikasi. Kemudian, Gemini akan melakukan sisanya sendiri, secara berurutan:
uv env | menginstal uv jika tidak ada, lalu | |
apis | mengaktifkan enam API pada project yang dipilih di langkah 4 dan mengonfirmasi bahwa setiap API merespons sebelum melanjutkan: | |
account | memastikan akun layanan komputasi default project ada dan memiliki peran yang diperlukan build dan layanan sesi 7 | |
.env | menulis | |
veo · room | mengonfirmasi bahwa | |
model | melakukan satu panggilan nyata ke Gemini, sehingga project yang buruk akan gagal di sini pada waktu penyiapan, bukan di dalam sesi | |
starter | menempatkan sembilan file yang akan Anda edit di tempatnya dari | |
page | menginstal dan membangun | |
cloud | mulai membuat korpus RAG Engine dan Memory Bank di latar belakang, sehingga sesi 4 dan 6 menemukannya dalam keadaan siap | |
preflight | menjalankan | |
6 · Baca preflight (~1 mnt)
Hal terakhir yang dicetak langkah 5 adalah preflight — setiap baris adalah pemeriksaan nyata, bukan tebakan:
✓ 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) benar: grafik tersebut dikirim dalam keadaan kosong, dan sesi 3 adalah tempat Anda mengisinya. Dua baris - juga benar — korpus memerlukan waktu satu atau dua menit untuk muncul dan bank sekitar dua puluh detik, dan Anda tidak akan memerlukan keduanya sebelum sesi 4.
👉💻 Jalankan lagi kapan saja untuk melihat posisi Anda — alat ini membaca file, project, dan server yang berjalan, bukan checklist:
cd ~/vibe-studio-multimodal
uv run python scripts/preflight.py
7 · Membuka workbench (~1 mnt)
Workbench sudah berjalan. Langkah 5 memulainya di latar belakang, satu proses uvicorn di port 4600 yang menyajikan halaman, setiap rute /api/*, dan adk web yang dipasang di /workbench. Tidak ada yang perlu dimulai, dan tidak ada yang meminta Anda untuk memulai ulang di lab ini: hasil edit akan diterapkan pada operasi berikutnya.
👉🖥 Klik link yang dicetak preflight, atau di Cloud Shell: Web Preview → Change port → 4600. Sebut saja tab 2. Halaman akan terbuka di Sesi 1 · Cerita.
Tiga perintah yang mungkin Anda perlukan nanti, semuanya dari ~/vibe-studio-multimodal:
| menghentikan workbench | |
| memulainya di latar depan; Ctrl+C menghentikannya dan setiap proses yang dimulai | |
|
| |
Tata letak repositori
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
3. 1 · Cerita — malam, dan grafik yang dihasilkan
👉🖥 Tab 2 akan terbuka di Sesi 1 · Cerita. Baca: enam tugas yang disusun dalam lingkaran di sekitar satu orang — riset tren, sisir backlog, pilih arah, periksa kebijakan, tulis naskah, render klip — dan lingkaran itu berulang setiap malam. Gemini dapat melakukan riset, backlog, dan skrip sendiri; Veo dapat merender sendiri; memilih dan memeriksa adalah tugas orang tersebut.
Di bawah cincin tersebut terdapat grafik yang dibuat oleh lab lainnya, tiga belas node dari ide hingga klip dan posternya. Arahkan kursor ke node untuk mengetahui fungsinya dan sesi yang membuatnya. Tidak ada yang perlu diedit di sini; tombol Generate the first poster di halaman ini adalah sesi 2.
Kosakata, sehingga enam halaman berikutnya dibaca tanpa berhenti:
kata | apa artinya di sini |
agent | ADK |
node | satu langkah grafik — fungsi Python, agen, join, router |
edge | tuple node yang berjalan secara berurutan; grafik adalah daftar node tersebut. Sesi 3 |
session | data satu operasi di |
dengan status tersembunyi akhir | dict yang dibagikan node berdasarkan nama kunci; satu node menulis |
memori | apa yang bertahan setelah sesi berakhir — Memory Bank, melalui callback. Sesi 6 |
4. 2 · Pembuatan gambar — satu agen, dan model gambar sebagai alat

👉🖥 Di workbench, buka Sesi 2 · Pembuatan gambar. Sebelum grafik, hal paling sederhana yang dapat dilakukan: satu Agent yang petunjuknya adalah seluruh malam sebagai prosa, tiga fungsi Python sebagai tangannya, dan eksekusi yang diakhiri dengan gambar.
Agen adalah model, petunjuk, dan alat
stage0_prompt/agent.py menentukan root_agent dengan tiga kolom agen ADK. model adalah model Gemini dari agent/platform/config.py. instruction adalah perintah sistem: enam tugas dalam satu waktu, yang terakhir adalah gambar. tools memiliki tiga fungsi.
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."))
Model adalah dunia tertutup: model melakukan penalaran berdasarkan bobotnya dan token dalam konteksnya. Alat adalah fungsi Python yang mungkin diminta oleh model. ADK membaca tanda tangan dan docstring setiap fungsi dan mendeklarasikannya ke model; saat model menginginkannya, model akan memunculkan peristiwa function_call, runtime akan menjalankan Python dan menulis peristiwa function_response kembali ke konteks, dan model akan melanjutkan dengan hasil di depannya.
Tiga fungsi, dan salah satunya menggambar
Dua dari tiga orang membaca riset. Perintah ketiga meminta model gambar untuk membuat poster:
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 memanggil agent/platform/images.py, model gambar yang digabungkan sebagai fungsi biasa seperti cara videogen.py menggabungkan Veo: awalan gaya rumah (tampilan Halloween low-poly yang nyaman di channel, warna oranye labu dan krem cahaya lilin dengan latar belakang warna plum tengah malam), subjek, bingkai vertikal 9:16, satu PNG di bawah runs/media/posters, disajikan di /static/posters. Dengan STUDIO_REAL_IMAGES=0, model ini akan menyalin poster pengganti dan menandai hasilnya prebaked, sehingga lab dapat berjalan di mana saja.
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."""
Panggilan yang sama adalah node dalam grafik di sesi 5.
Praktik langsung: satu pengeditan
Saat dikirim, make_poster menampilkan dict yang menyatakan bahwa poster dilewati. 👉🖥 Di editor workbench, buat editor memanggil model gambar, sebagai pengganti baris TODO:
from agent.platform import images
return images.generate(subject, name="solo")
Simpan. Baris verifikasi membaca file: tiga alat di agen, panggilan model gambar sudah siap. 👉🖥 Klik Check the workflow loads, lalu Open adk web dan kirim:
tonight's idea: a tiny ghost learning to haunt a sock drawer
Yang akan terjadi dan alasannya
- Dua peristiwa alat sebelum balasan, lalu pertanyaan.
check_trendsdanread_backlogberjalan karena petunjuknya adalah memeriksa dan melihat, dan model tidak memiliki keduanya dalam bobotnya. Balasan tersebut mengusulkan arah dan meminta Anda untuk mengonfirmasi, karena petunjuknya meminta persetujuan terlebih dahulu. - Pesan kedua mendapatkan semuanya. Kirim
skip the questions, describe the video and draw the poster. Model ini mendeskripsikan video, memanggilmake_postersekali dengan baris subjek yang ditulisnya sendiri, dan memberi tahu Anda tempat PNG disimpan. Panel verifikasi menampilkan poster di samping baris: adegan Halloween dalam tampilan channel, yang digambar dalam waktu sekitar delapan detik. - Mengapa ini bukan channel. Riset tersebut diringkas menjadi prosa yang tidak dapat dikutip. Kebijakan ini disertifikasi oleh model yang menulis petunjuk, dan poster digambar sebelum diperiksa. Perjanjian tersebut dibahas dalam satu pesan. Teks perintah bukan gerbang. Bagian selanjutnya dari lab ini akan menguraikan keenam tugas ini menjadi grafik yang digambar.
Halaman ini diakhiri dengan Komponen agen ADK, referensi yang dapat Anda baca kembali: model, konteks yang digunakan untuk penalaran, alat dan kolaborator yang digunakan untuk bertindak, callback yang menggabungkan setiap panggilan, serta sesi dan memori yang berada di luar agen.
5. 3 · Alur kerja — fan-out dan join, router, dan loop

👉🖥 Di workbench, buka Session 3 · Workflows, bagian 3a dan 3b. Setiap bagian dibuat dalam aplikasi sandbox sehingga Anda dapat menjalankannya di adk web secara mandiri: fan-out di stage1_fanout, router dan loop-nya di stage3_router.
Fan-out dan join (3a)
Workflow ADK adalah daftar edge. Setiap entri adalah rantai, yaitu tuple node yang berjalan secara berurutan. Dua rantai yang keluar dari START berjalan secara paralel. Rantai yang berakhir di JoinNode akan melaporkan ke sana, dan join akan menunggu setiap rantai yang berakhir di sana sebelum melanjutkan, dengan menyerahkan satu dict yang diberi kunci berdasarkan nama node. Target yang ditulis sebagai dict memetakan nama rute router ke node. Halaman 3a workbench menggambar bentuk dan memiliki latihan menulis tepi.
Jenis node | Ditulis sebagai | Fungsinya |
Node fungsi |
| Python biasa. Dapat juga menulis status bersama dengan |
Node gabungan |
| Menunggu hingga setiap rantai yang masuk melaporkan, lalu meneruskan semua output-nya sebagai satu dict. |
Node agen |
| Satu panggilan model dalam mode |
Router |
| Node fungsi yang menampilkan nama edge keluar. |
Node input manusia |
| Menangguhkan grafik hingga seseorang menjawab formulir. Sesi 5. |
Grafik pertama adalah riset sesi 2 sebagai node. scan_trends dan read_backlog di agent/graph.py adalah dua pembaca; read_backlog juga menulis ide malam ini ke status bersama. stage1_fanout/agent.py menentukan join dan membangun Workflow-nya dari daftar bernama EDGES, yang dikirimkan kosong (0 edges preflight). 👉🖥 Isi dengan dua rantai:
EDGES = [(START, scan_trends, join_research),
(START, read_backlog, join_research)]
👉🖥 Jalankan di adk web. Dua node menyala bersama, lalu bergabung; buka peristiwa join_research dan baca satu dict dengan kunci scan_trends dan kunci read_backlog. Tidak ada panggilan model yang terjadi. Tidak ada yang memformat ulang dict join: node setelahnya, di 3b, adalah agen, dan node agen menerima node_input sebagai pesannya, sehingga dict tiba sebagai JSON yang dibaca langsung oleh model.
Router dan loop (3b)
Agent di dalam Workflow berjalan dalam mode single_turn: menerima output node sebelumnya sebagai pesannya, melakukan satu panggilan model, dan menjawab sekali. Dengan output_schema, jawaban divalidasi terhadap model Pydantic sebelum node berikutnya melihatnya, sehingga grafik meneruskan objek yang diketik, bukan prosa. propose_directions membaca kamus riset gabungan dan mengajukan empat kandidat, yang masing-masing merupakan adegan Halloween. Kandidat 4 adalah umpan: judulnya berisi kata yang ditolak channel, sehingga router memiliki sesuatu untuk ditangkap. Setiap kandidat membawa poster_idea, satu baris yang menyebutkan satu gambar yang menjual video; penulis naskah menyimpannya dan meja media menggambarnya di sesi 5.
Router adalah node fungsi yang menampilkan nama edge yang diambil oleh operasi. Tanpa panggilan model: policy_check membaca agent/policy_words.txt pada saat pengambilan keputusan, mencocokkan seluruh kata dengan judul dan sudut arahan, serta merutekannya sebelum skrip ditulis atau uang dibelanjakan. Kebijakan adalah data: edit file, dan eksekusi berikutnya akan membacanya.
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 adalah fan-out ditambah propose_directions dan choose_direction, pengganti pintu manusia yang Anda buat di sesi 5: memilih kandidat yang nomornya mengakhiri ide malam ini (..., pick 4), jika tidak, yang pertama, dan menulis pilihan ke status. scripter adalah agen single_turn yang mengubah arahan yang disetujui menjadi Script: judul, deskripsi, tag, kalimat pembuka, tiga shot, dan ide poster, dengan setiap shot berupa gambar Halloween dalam tampilan channel.
Loop di balik penolakan keluar. quarantine dikirim sebagai agen mode tugas. Agent memiliki mode: chat adalah loop percakapan yang dijalankan agen root dengan seseorang, single_turn adalah satu panggilan dengan jawaban terstruktur, dan task adalah loop panggilan alat yang dijalankan agen sendiri hingga memanggil finish_task bawaan, yang argumennya berasal dari output_schema. Karantina memanggil find_policy_hits pada arah yang ditolak, meminta suggest_replacement untuk pengganti yang disetujui channel untuk setiap kata (agent/policy_replacements.txt, data lagi), menulis ulang, memeriksa lagi, dan diakhiri dengan CleanedDirection yang diketik. Baca di halaman; Anda tidak mengetiknya.
quarantine = Agent(
name="quarantine",
model=config.MODEL,
mode="task",
instruction=QUARANTINE_INSTRUCTION,
tools=[find_policy_hits, suggest_replacement],
output_schema=CleanedDirection)
Saat dikirim, rantai berjalan dari pick langsung ke scripter. 👉🖥 Tempatkan router di antara keduanya, dengan dua pintu keluar dan jalur kembali dari karantina; tiga baris menggantikan satu baris:
(join_research, propose_directions, choose_direction, policy_check),
(policy_check, {"OK": scripter, "BLOCK": quarantine}),
(quarantine, scripter),
👉🖥 Jalankan kedua rute. Kirim ide apa adanya: choose_direction mengambil kandidat 1, policy_check memunculkan OK, dan penulis skrip menulis skrip terstruktur. Klik Sesi baru dan kirim ide lagi dengan , pick 4 di akhir: kandidat 4 adalah umpan, policy_check memunculkan BLOCK, karantina memanggil alatnya, menulis ulang kata-kata yang ditolak, memanggil finish_task, dan arahan yang telah dibersihkan dikirim kembali ke penulis skrip. Baris verifikasi membaca kedua operasi dari penyimpanan sesi.
Setiap kalimat perintah sesi 2 kini memiliki node: riset adalah dua pembaca dan gabungan, penolakan adalah router dan agen tugas, skrip adalah node agen. Di seluruh grafik, pilihan pengganti adalah dua node fungsi, direction_gate dan persist_direction, yang akan Anda lihat berjalan terus hingga sesi 5 membuat pintu berhenti. Perjanjian dengan kreator masih berupa pengganti; begitu pula risetnya, yang belum mendengar pendapat audiens.
6. 4 · Penyerapan multimodal — apa yang dikirim audiens, dibaca ke dalam satu korpus

👉🖥 Di workbench, buka Session 4 · Multimodal ingestion, bagian 4a dan 4b. Audiens tidak hanya menulis. Satu pembaca per jenis mengubah semua yang dikirim menjadi teks dengan bentuk yang sama, satu korpus RAG Engine menyimpan semuanya, dan pembaca ketiga dalam fan-out menelusurinya berdasarkan makna dan mengembalikan media.
Yang dikirim audiens (4a)
agent/comments.md menyimpan dua belas komentar yang ditulis di bawah enam video Halloween channel tersebut. agent/audience/ menyimpan sisanya: delapan catatan suara (voice/*.mp3), empat belas foto kamar penonton (photo/*.jpg), dan enam klip (clip/*.mp4), yaitu panning lambat pada foto dengan voiceover penonton. Halaman 4a terbuka di semua perangkat: setiap catatan suara diputar, setiap foto ditampilkan, setiap klip berjalan.

Mesin RAG mengambil teks. Catatan suara, foto, atau klip hanya dapat bergabung dengan korpus seperti yang dilakukan poster sebelumnya di rag.caption_posters: model mengubahnya menjadi baris teks yang memberi nama filenya, baris tersebut menjadi bagian, dan saat bagian tersebut kembali, file tersebut akan kembali bersamanya. agent/platform/audience.py memiliki satu pembaca per jenis. Setiap baris mengirimkan satu file dan satu perintah ke Gemini dan menulis satu baris per file:
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 melakukan hal yang sama dengan image/jpeg dan PHOTO_PROMPT; watch_clips dengan video/mp4 dan CLIP_PROMPT, dan Gemini menonton gambar serta mendengarkan soundtrack dalam satu panggilan. Setiap pembaca menulis bentuk baris yang sama, sehingga chunking, embedding, dan pengambilan tidak pernah tahu dari jenis mana suatu bagian berasal:
- 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 ...

Kumpulan data. Vertex AI RAG Engine adalah pengambilan dokumen: korpus dibagi menjadi bagian-bagian yang masing-masing berisi sekitar 120 token, setiap bagian disematkan dengan text-embedding-005, dan kueri disematkan dengan cara yang sama dan dijawab dengan bagian terdekat berdasarkan makna, bukan berdasarkan kata yang sama. "hantu kecil yang nyaman di rumah" menemukan komentar yang meminta keajaiban kecil di ruangan kecil, pesan suara tentang kelelawar yang memeluk bola lampu, dan klip yang soundtrack-nya mengatakan bahwa hantu itu adalah gelas kertas.
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)))
Penyiapan sudah mulai membuat korpus; baris koneksi halaman 4a menunjukkan apakah korpus sudah aktif, dan membuatnya jika penyiapan tidak dapat melakukannya. Dari tombol halaman atau terminal: python -m agent.platform.rag membuat korpus dan mencatatnya di runs/ragcorpus.json; python -m agent.platform.rag load --audience menjalankan setiap pembaca di audience.READERS, lalu mengupload apa yang mereka tulis di samping komentar dan teks poster, setiap file dengan namanya sendiri, dan menunggu indeks menjawab; python -m agent.platform.rag query "cozy little hauntings at home" menampilkan pengambilan yang menemukan bagian yang tidak memiliki kata yang sama dengan kueri.
Pengeditan. Saat dikirim, READERS hanya menampung read_comments; tiga pembaca media berada di belakang satu # . 👉🖥 Jalankan penyerapan sekali seperti yang dikirimkan: outputnya akan menampilkan not read (no reader wired in audience.READERS): voice, photo, clip, dan hanya komentar serta teks yang masuk. Kemudian, hapus # :
transcribe_voice, caption_photos, watch_clips,
👉🖥 Simpan dan jalankan penyerapan lagi. Pembaca memerlukan waktu sekitar tiga menit untuk empat puluh item; konsol menampilkan heard, saw, dan watched satu baris per baris, dan setiap kartu dalam petak diisi dengan barisnya. Baris verifikasi membaca file, output pembaca, dan korpus: empat pembaca terhubung, delapan catatan suara didengar, empat belas foto dilihat, enam klip ditonton, lima file dalam korpus.
Pembaca ketiga (4b)
Masukan audiens adalah riset, yang dibagikan oleh setiap operasi, sehingga masuk ke grafik tempat riset berada: sebagai pembaca ketiga ke dalam penggabungan. read_feedback di agent/graph.py mengambil ide malam ini, meminta korpus untuk memberikan bagian terdekat, dan melaporkannya ke join_research, yang menunggu tiga bagian. Edge (START, read_feedback, join_research) dikirimkan dalam stage5_rag/agent.py, sesi bentuk 3 yang diajarkan; hasil edit Anda ada di dalam node. Pengambilan, bukan panggilan model, dan deterministik: ide yang sama menemukan bagian yang sama sementara pengusulnya bervariasi.
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
Setiap baris dalam korpus yang berasal dari catatan suara, foto, klip, atau poster menyebutkan nama filenya. rag.media_in(hits) membaca nama-nama tersebut dari teks dan mengubahnya menjadi URL yang dapat ditampilkan dan diputar oleh halaman. 👉🖥 Serahkan kembali media beserta teksnya, sehingga hal-hal yang dikirim audiens akan disertakan bersama apa yang mereka katakan:
return Event(output={"query": query, "feedback": [h["text"] for h in hits],
"media": rag.media_in(hits)})
👉🖥 Jalankan stage5_rag dengan sebuah ide. Buka read_feedback: kuerinya adalah ide Anda, outputnya memiliki lima bagian terdekat dan daftar media dengan URL per file. Buka join_research: tiga kunci. Buka propose_directions: kandidat 1 hingga 3 mengutip feedback dalam bukti mereka. Panel verifikasi menampilkan apa yang dilihat pembaca di bawah baris: klip, tiga catatan suara, dan poster hantu di lampu kulkas.

7. 5 · Pembuatan video — dua jenis penangguhan, satu mekanisme

👉🖥 Di workbench, buka Sesi 5 · Pembuatan video, bagian 5a dan 5b, yang dibuat di stage6_video, seluruh grafik. Klip memerlukan waktu beberapa menit dan poster memerlukan waktu beberapa detik, dan prosesnya berhenti dua kali: sekali untuk Anda, sekali untuk Veo. Kedua perhentian tersebut adalah satu mekanisme, dan kedua cabang desk berbagi apa yang mereka buat berdasarkan nama.
Dua jenis penangguhan (5a)
Tidak ada yang menunggu. Lari yang harus berhenti menulis panggilan terbuka ke dalam sesinya dan berakhir; siapa pun yang menjawab panggilan itu, baik orang maupun proses, akan melanjutkannya. Halaman akan terbuka di satu tabel:
untuk seseorang · | untuk alat yang lambat · | |
yang menghentikan operasi |
|
|
apa yang ditunggunya | seseorang: satu dari empat kartu | tugas yang lambat: Operasi Veo, menit |
apa yang ada dalam sesi | panggilan | panggilan |
cara melanjutkan |
|
|
siapa yang mengirimnya |
|
|
tempatnya berada sementara itu |
| baris yang sama, ID yang sama · tidak ada yang berjalan |

Untuk seseorang. Dalam satu perintah, "minta kreator untuk mengonfirmasi" bersifat saran, dan sesi 2 membahasnya dalam satu pesan. Dalam alur kerja, jeda adalah milik mesin. Node yang menghasilkan RequestInput akan menangguhkan operasi: ADK menulis panggilan terbuka bernama adk_request_input ke dalam sesi dengan message, response_schema, dan payload, memberinya interrupt_id, dan kembali ke pemanggil. Tidak ada thread yang menunggu. Satu-satunya hal yang melanjutkannya adalah function_response yang membawa ID tersebut, yang divalidasi terhadap skema Anda; pesan chat ke alur kerja adalah giliran baru, bukan jawaban. Skema ini juga merupakan formulir: adk web merendernya, dan aplikasi VibeStudio merender skema yang sama sebagai empat kartu. direction_gate sudah menulis kandidat ke status. 👉🖥 Hentikan:
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})
Jawaban Anda akan sampai di persist_direction sebagai node_input, yaitu dict dengan satu kunci, pick. Keempat kandidat tiba dengan cara lain: gerbang menulisnya ke status bersama di bagian candidates, dan ADK mengikat kunci status ke parameter dengan nama yang sama. Node menyelesaikan pilihan menjadi satu kandidat dan menulis direction, angle, hook, dan user:prefs kembali dengan Event(state=...). Kunci dengan awalan user: tetap ada di seluruh sesi untuk pengguna tersebut.
Untuk alat yang lambat. Rendering Veo memerlukan waktu beberapa menit, dan grafik tidak boleh menunggu selama itu. render_submit di agent/desk.py memulai tugas dan langsung menampilkan tanda terima dengan status 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"]}
Dibungkus dalam LongRunningFunctionTool, tanda terima tersebut berarti sesuatu bagi ADK: giliran agen berakhir, alur kerja ditangguhkan di render_desk, dan panggilan terbuka dengan ID dan tanda terimanya adalah baris di runs/sessions.db. 👉🖥 Di stage6_video/agent.py, gabungkan alat:
tools=[LongRunningFunctionTool(render_submit)])
Melanjutkan berdasarkan ID, dari proses lain. agent/deliver.py menemukan panggilan yang tertunda di penyimpanan sesi, melakukan polling pada Veo hingga klip tersedia, menulis hasilnya ke runs/state.json, dan mengirimkan function_response dengan ID panggilan tersebut ke sesi yang sama. 👉🖥 Buat bagian respons:
part = Part(function_response=FunctionResponse(
id=row["call_id"], name=row["name"], response=response))
👉🖥 Jalankan stage6_video hingga berhenti dua kali. Kirim ide: proses berhenti pada formulir yang dibuat dari skema Anda, dengan empat kandidat dalam payload-nya. Ketik 2 di pick dan kirim; grafik akan dilanjutkan pada persist_direction. Coba uji penghentian di sini: tangguhkan di formulir, hentikan server (scripts/stop.sh di tab 1), mulai lagi (scripts/restart.sh), buka kembali sesi. Formulir masih ada, karena jeda adalah baris, bukan rangkaian pesan. Setelah script_ready, render_desk memanggil render_submit, mendapatkan tanda terima, membalas WAITING, dan operasi berakhir; sesi menyimpan ID panggilan. Jalankan pengiriman dari konsol halaman atau tab 1:
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
👉🖥 Klik Refresh adk web: function_response mengikuti panggilan yang tertunda, join_media dirilis, dan store_release berjalan. Delapan baris verifikasi membaca dua perhentian dari sesi: panggilan gerbang dan jawaban Anda, tanda terima render dan pengirimannya.
State dan desk (5b)
Media desk adalah struktur 3a lagi, fan-out dan join, dengan satu cabang lambat. script_ready mengirimkan skrip dengan dua cara. render_desk mengirimkan klip dan operasi ditangguhkan. make_poster adalah alat sesi 2 sebagai node: poster_idea skrip, gaya channel, dan apa yang diketahui Memory Bank tentang selera kreator dalam poster (terikat dari status sebagai memory_facts, sesi 6) dikirim ke model gambar, dan PNG 9:16 akan ditampilkan dalam hitungan detik. join_media menunggu yang lambat, dan store_release menulis apa yang ditemukannya ke status.
Bagikan berdasarkan nama. store_release tidak pernah menerima poster. Node ini mendeklarasikan parameter bernama poster_url, dan ADK mengikatnya dari status bersama, karena node di cabang lain menulis kunci tersebut. Tidak ada payload yang melintasi cabang:
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'}")
Saat dikirim, make_poster menghasilkan poster sebagai output dan pesan, dan store_release mengatakan poster: not in state. 👉🖥 Tambahkan penulisan status ke hasil akhirnya:
state={"poster_url": rec.get("url", ""), "poster_path": rec.get("path", "")},
message=f"poster: {said}")
👉🖥 Jalankan sekali lagi dan kirimkan. Buka peristiwa make_poster: URL poster dalam outputnya dan delta status dengan poster_url. Buka store_release: pesannya menyebutkan nama poster. Blok This run's session di halaman ini menampilkan baris yang sama dengan yang ditampilkan tab State adk web: ID sesi yang diperlukan untuk melanjutkan, panggilan yang terbuka, dan kunci yang ditulis node, termasuk poster_url. Panel verifikasi menampilkan klip dan poster secara berdampingan.

8. 6 · Memori — hal yang diingat saluran di antara malam hari

👉🖥 Di workbench, buka Session 6 · Memory, bagian 6a dan 6b, yang dibuat di stage4_memory. Sesi 5 membahas status satu operasi. Sesi ini membahas hal yang diingat saluran di antara operasi: hal yang didengar grafik dari luar, melalui dua callback.
Memory Bank (6a)
Vertex AI Memory Bank adalah memori jangka panjang terkelola tentang seseorang. Fakta berada dalam cakupan, nama aplikasi, dan ID pengguna, serta dalam topik yang Anda tentukan. Saat Anda memberikan pertukaran dengan memories.generate, model akan mengekstrak apa yang sesuai dengan topik, menyematkannya, dan menggabungkannya dengan apa yang sudah dimilikinya: fakta baru dibuat, fakta yang berubah diperbarui, fakta yang berulang digabungkan. memories.retrieve menampilkan fakta untuk cakupan. Kedua panggilan berada di 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.",
}
Topik ketiga adalah selera channel dalam gambar. Rilis ini menulisnya di sesi 5: poster yang dikirim dijelaskan dalam satu kalimat dan diingat, dan poster berikutnya digambar dengan kalimat tersebut dalam perintahnya.
Penyiapan sudah mulai membuat bank; baris connect di halaman 6a menunjukkan apakah bank sudah aktif. Dari tombol halaman atau terminal: python -m agent.platform.bank membuat bank dan topiknya serta meng-cache nama resource di runs/memorybank.json; python -m agent.platform.bank load menyisipkan empat sesi sebelumnya (dua era hewan dengan aturan gaya, satu era gadget, satu era fantasi terbaru); python -m agent.platform.bank list menunjukkan hasil transkrip: pernyataan fakta yang digabungkan, bukan transkrip.
Callback (6b)
Memori bukan node. Callback adalah fungsi yang Anda teruskan ke Agent; ADK memanggilnya pada momen tetap dengan konteks langsung, dan menampilkan None memungkinkan hal normal terjadi. Tiga pasangan: di sekitar giliran agen (before_agent_callback, after_agent_callback, dengan CallbackContext), di sekitar setiap panggilan model (before_model_callback dengan LlmRequest, after_model_callback dengan LlmResponse), di sekitar setiap panggilan alat. Guardrail, telemetri, cache, dan injeksi konteks ditempatkan di sini, tanpa node dalam grafik.
Di stage4_memory/agent.py, propose_directions membaca bank sebelum panggilan modelnya: recall_taste mengambil fakta, menambahkannya ke permintaan sebagai blok MEMORY, yang terlama terlebih dahulu, dan memberi tahu model untuk mengarahkan kandidat 1 hingga 3 ke selera saat ini dan memperlakukan aturan sebagai batasan. Fakta juga ditulis ke status sebagai memory_facts, tempat make_poster menemukannya. 👉🖥 Hubungkan:
output_schema=Directions,
before_model_callback=recall_taste)
Penulis naskah menulis apa yang dipilih kreator setelah gilirannya: remember_pick membaca direction dari status, menulis satu kalimat tentang pilihan tersebut, dan menyerahkannya kepada memories.generate. 👉🖥 Hubungkan:
output_schema=Script,
after_agent_callback=remember_pick)
👉🖥 Jalankan dengan pesan kosong dan pilih kartu. Buka acara propose_directions: permintaan ini membawa tiga era, dan kandidat 1 hingga 3 cenderung sesuai dengan selera saat ini, sementara tren menunjukkan hal lain. Setelah skrip, klik Tampilkan bank: memori diubah atau ditambahkan untuk pilihan malam ini. Jalankan malam kedua: sistem akan mengingat malam pertama.
9. 7 · Men-deploy dan memublikasikan — grafik di dalam aplikasi, malam di VibeTube

👉🖥 Di workbench, buka Sesi 7 · Deploy dan publikasikan, bagian 7a dan 7b.
Men-deploy VibeStudio (7a)
Di sandbox, adk web menggerakkan grafik. VibeStudio menggerakkannya dengan Runner ADK, di thread pekerja dengan loop peristiwanya sendiri, dan menggabungkan setiap peristiwa ke dalam satu status run yang dibaca halaman melalui Server-Sent Events: pembaca, pilihan Anda sebagai empat kartu, poster saat mendarat, render saat dikirimkan. Jawaban gate dan pengiriman render adalah panggilan yang sama, bagian function_response, dan aplikasi melakukan polling pada Veo, bukan menunggu konsol.
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/ adalah salinan byte-for-byte dari agent/ lab, yang diperiksa oleh checks/verify_app.py. Daftar edge lengkap di agent/graph.py adalah setiap struktur codelab ini di satu tempat, tiga belas node; daftar ini dikirimkan dalam keadaan terisi, untuk Anda baca, bukan untuk diketik:
(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),
👉🖥 Deploy dengan tombol Deploy workbench, atau 👉💻 dari tab 1:
cd ~/vibe-studio-multimodal
uv run python vibestudio/deploy.py
yang berjalan, dengan nilai dari .env Anda yang diisi:
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 membangun container dengan Cloud Build dan men-deploy-nya dalam satu langkah; build pertama memerlukan waktu empat atau lima menit. --session-affinity dengan satu warm instance akan mempertahankan eksekusi yang ditangguhkan pada instance yang menampungnya: penyimpanan sesi dari sesi 5, sekali lagi. Dalam deployment yang lebih besar, penyimpanan sesi akan dipindahkan ke VertexAiSessionService dan afinitas akan hilang. Build dan layanan berjalan sebagai akun layanan komputasi default project, yang penyiapannya memberikan peran yang diperlukan.
👉🖥 Buka URL layanan dan jalankan malam di aplikasi: ide, pilihan, tunggu klip dan posternya. Tab Histori menyimpan keduanya.
Publikasikan (7b)
Publikasi adalah satu POST multibagian dari aplikasi ke endpoint ruang di VibeTube, dalam vibestudio/server/platform/publish.py. Klip adalah video, poster adalah thumbnail, dan profil memberi nama ruang dan Anda:
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 menyimpan satu video per projectId dan ruang, dan project yang dikirim aplikasi adalah project Google Cloud Anda, sehingga memublikasikan lagi akan menggantikan video Anda, bukan menambahkan video. Poster pengganti tidak pernah diupload; poster yang sebenarnya adalah thumbnail, byte demi byte.
Pengeditan terakhir adalah konfigurasi, bukan baris kode. 👉🖥 Di halaman 7b: URL VibeStudio (diisi dari deployment 7a), kode acara ruangan, persis seperti yang dieja VibeTube (yang Anda berikan saat penyiapan, kecuali jika instruktur Anda telah membuka yang lain), dan nama Anda. Tekan publikasikan. Workbench meneruskan klik ke aplikasi, aplikasi memposting malam, dan baris di bawah membaca hasil dari aplikasi dan dari VibeTube: aplikasi menjawab, malam selesai dengan poster asli, kode memberi nama ruang terbuka, VibeTube menampilkan ID video, video ada di dinding, dan thumbnail-nya adalah poster Anda. Kartu hasil menampilkan thumbnail di VibeTube di samping poster di aplikasi.

10. Yang telah Anda bangun
👉🖥 Bagian akhir halaman 7b menampilkan seluruh grafik, tiga belas node dari ide hingga klip dan posternya; arahkan kursor ke node untuk mengetahui fungsinya dan sesi yang membuatnya.
Sesi | Yang diajarkan | Ditulis sebagai |
2 · Pembuatan gambar | Agen: model, petunjuk, alat; model gambar sebagai alat; hal yang tidak dapat dijanjikan oleh perintah |
|
3 · Alur kerja | Daftar edge, fan-out dan join, router dengan loop mode tugas di belakangnya |
|
4 · Penyerapan multimodal | Satu pembaca per jenis, satu bentuk baris, satu korpus; pembaca ketiga berdasarkan makna, dengan media |
|
5 · Pembuatan video | Dua jenis penangguhan, satu mekanisme; penyimpanan sesi; poster yang dibagikan berdasarkan nama |
|
6 · Memori | Memori di beberapa malam, dua callback |
|
7 · Men-deploy dan memublikasikan | Grafik di dalam aplikasi, di Cloud Run; malam di VibeTube |
|
Lima aturan yang diikuti grafik:
- Tangguhkan, bukan menunggu. Jeda adalah baris di penyimpanan sesi, bukan thread.
- Lanjutkan semuanya dengan cara yang sama — berdasarkan ID panggilan terbuka, baik dijawab oleh orang maupun proses.
- Bagikan data berdasarkan nama dalam status, bukan dengan memasukkannya ke dalam setiap payload.
- Memutuskan dengan kode sebelum membayar model. Kebijakan ini adalah file teks dan router, dan berjalan sebelum render.
- Baca semua yang bukan teks menjadi teks sebelum mencapai korpus, dengan nama file di dalam baris sehingga file dapat kembali.
Perintah tidak dapat menjanjikan urutan, penghentian, atau pemeriksaan. Grafik bisa.
Langkah berikutnya
VertexAiSessionServiceuntuk sesi yang bertahan lebih lama dari instance, dan hapus--session-affinity.- Webhook yang menjawab panggilan render, bukan poller:
FunctionResponse(id=...)yang sama, pengirim yang berbeda. RequestInputkedua sebelum publikasi — skemanya adalah formulir, jadi ini adalah satu node dan tidak ada kode halaman.- Komentar, pesan suara, dan klip baru dari audiens ditambahkan ke korpus setelah setiap rilis, dengan poster yang dikirimkan bersama komentar tersebut.
Pembersihan
Semua yang dibuat lab ini berada dalam project yang dibuat pada langkah 4, jadi pembersihan termurah adalah dengan menghapus project. Dalam Cloud Shell:
gcloud projects delete $(cat ~/project_id.txt)
Untuk mempertahankan project dan hanya menghapus bagian berbayar: layanan Cloud Run (gcloud run services delete vibestudio --region us-central1), korpus RAG dan Memory Bank (nama resource-nya ada di runs/ragcorpus.json dan runs/memorybank.json, serta halaman Agent Engine dan RAG Engine di konsol akan menghapusnya), dan image container di Artifact Registry.