1. Giới thiệu

Bạn điều hành một kênh ấm cúng nhưng rùng rợn trên VibeTube: mỗi đêm một video ngắn cho đến Halloween. Khán giả muốn có những điều kỳ diệu nhỏ trong những căn phòng nhỏ và gửi cho bạn bằng mọi cách có thể — dưới dạng bình luận, ghi chú thoại, ảnh chụp ngăn kéo ma ám của riêng họ và đoạn video về hiên nhà của họ lúc hoàng hôn. Tồn đọng rất nhiều ý tưởng. Mỗi tối, anh đều làm những việc quen thuộc: nghiên cứu xem điều gì đang ám ảnh trang tin, rà soát các nội dung tồn đọng, đọc những gì khán giả gửi, chọn một hướng đi, kiểm tra xem hướng đi đó có phù hợp với chính sách của kênh hay không, viết kịch bản, vẽ áp phích, kết xuất video rồi xuất bản.
Các mô hình tạo sinh có thể tự thực hiện từng việc nhà. Gemini nghiên cứu, lắng nghe, quan sát và viết; mô hình hình ảnh vẽ; Veo kết xuất. Điều họ không thể tự làm là điều hành buổi tối theo thứ tự, dừng lại để đưa ra quyết định của bạn, từ chối một hướng đi tồi trước khi tốn tiền, đợi vài phút để kết xuất mà không cần mở bất cứ thứ gì và để lại bản ghi.
Lớp học lập trình này biến buổi tối đó thành một ADK Workflow: một biểu đồ được vẽ gồm các nút và cạnh tự chạy, chỉ hỏi bạn khi cần bạn đưa ra phán đoán và kết thúc mỗi lần chạy bằng một đoạn video và áp phích của đoạn video đó trên VibeTube.
Câu lệnh không thể hứa hẹn về thứ tự, điểm dừng hoặc điểm kiểm tra. Biểu đồ có thể.
Kiến thức bạn sẽ học được
- Một tác nhân và mô hình hình ảnh đóng vai trò là công cụ.
Agentlà một mô hình, một chỉ dẫn và các công cụ. Công cụ thứ ba gọi mô hình hình ảnh và lần chạy đầu tiên kết thúc bằng một áp phích. Sau đó là câu dẫn dắt phần còn lại của phòng thí nghiệm: câu lệnh không thể hứa hẹn thứ tự, điểm dừng hoặc điểm kiểm tra. - Cấu trúc tạo nên một đồ thị.
Workflowlà danh sách cạnh. Bạn xây dựng mô hình phân tán và kết hợp (các trình đọc song song gặp nhau tại mộtJoinNode) và một bộ định tuyến đặt tên cho lối thoát của riêng mình, với một tác nhân ở chế độ tác vụ phía sau lối thoát bị từ chối để dọn dẹp một hướng trong vòng lặp các lệnh gọi công cụ và tham gia lại dòng chính. - Quy trình nhập dữ liệu đa phương thức. Khán giả gửi bốn loại nội dung, mỗi loại có một trình đọc: Gemini chép lời các bản ghi âm, chú thích ảnh, xem các đoạn video và nghe nhạc nền, và mọi trình đọc đều viết cùng một hình dạng dòng vào một kho dữ liệu của Công cụ RAG. Người đọc thứ ba trong fan-out truy xuất theo ý nghĩa và trả lại nội dung nghe nhìn cùng với các đoạn văn.
- Hai loại hệ thống treo, một cơ chế. Một người chạy dừng lại (
RequestInput: bốn thẻ, bạn trả lời) và một công cụ chậm (Veo phía sauLongRunningFunctionTool: biên nhận). Cả hai đều giống nhau: một lệnh gọi mở trong kho lưu trữ phiên, được tiếp tục bằng mã nhận dạng, từ biểu mẫu hoặc từ một quy trình khác. Hai nhánh của bàn điều khiển nội dung nghe nhìn dùng chung áp phích theo tên, trong trạng thái. - Bộ nhớ qua các đêm. Hai phương thức gọi lại trong vòng đời trên Ngân hàng bộ nhớ:
before_model_callbackgợi lại sở thích của người sáng tạo trước khi người đề xuất đọc,after_agent_callbackghi nhớ lựa chọn của tối nay sau người viết kịch bản. - Bản phát hành. Biểu đồ bên trong một ứng dụng có ADK
Runner, Sự kiện do máy chủ gửi đến trang, Cloud Run có mối quan hệ phiên và một yêu cầu POST nhiều phần đưa đoạn video lên VibeTube với áp phích làm hình thu nhỏ.
Sản phẩm bạn sẽ tạo ra
Một ứng dụng đang chạy trong trình duyệt: VibeStudio Workbench, một trang đồng hành có trình soạn thảo mã trên trang, trình xác minh đọc các tệp trên ổ đĩa và các phiên mà bạn đã viết, cùng một adk web được nhúng. Bảy phiên của khoá học này là bảy trang của lớp học lập trình này, có cùng số và cùng chữ cái của phần, vì vậy cả hai đều đồng bộ. Mỗi trang mở ra trên một dải cho biết phiên trong một câu và ba việc bạn làm ở đó: chỉnh sửa, chạy, xác minh. Đọc một phần tại đây, sau đó chỉnh sửa trong bàn làm việc; mỗi lần chỉnh sửa đều kết thúc bằng một bảng xác minh cho biết những gì thực sự đã xảy ra.
Có 3 thành phần chạy: Workbench (trang trên cổng 4600, trình chỉnh sửa, trình xác minh), phần phụ trợ của bạn (Workflow trong agent/ và một ứng dụng hộp cát cho mỗi phiên trong stage0_prompt/ thông qua stage6_video/, do adk web phân phát) và Google Cloud (Gemini, mô hình hình ảnh, Công cụ RAG, Veo, Ngân hàng bộ nhớ – tất cả đều được truy cập thông qua Vertex AI bằng thông tin xác thực của riêng bạn).
Bảy phiên
Phiên | Sản phẩm bạn sẽ tạo ra | Nội dung chỉnh sửa |
1 · Câu chuyện | Chưa có gì cả — đêm và biểu đồ mà nó trở thành | — |
2 · Tạo hình ảnh | Một nhân viên vẽ áp phích đầu tiên |
|
3 · Quy trình làm việc | Đêm như một biểu đồ: phân nhánh và kết hợp, bộ định tuyến và vòng lặp |
|
4 · Nhập dữ liệu đa phương thức | Những gì khán giả gửi, đọc thành một kho ngữ liệu; người đọc thứ ba |
|
5 · Tạo video | Hai loại lệnh tạm ngưng; áp phích được chia sẻ theo tên trong tiểu bang |
|
6 · Bộ nhớ | Những thông tin mà kênh ghi nhớ giữa các đêm |
|
7 · Triển khai và xuất bản | VibeStudio trên Cloud Run; đêm trên VibeTube | mã sự kiện của phòng và xuất bản |
Cách sử dụng lớp học lập trình này. Khoá học này tập trung vào khái niệm: mỗi phiên giải thích ý tưởng, sau đó cho thấy mã khi đọc sau khi thay đổi và hướng dẫn từng bước. Công cụ này không bao giờ yêu cầu bạn chỉnh sửa tệp ở đây — việc chỉnh sửa diễn ra trong bàn làm việc, nơi cung cấp cho bạn một nội dung chỉnh sửa tại một thời điểm, một gợi ý bên cạnh và một bảng xác minh bên dưới. Đọc phiên, sau đó để trang hướng dẫn bạn thực hiện.
Vị trí của tính năng này
Biểu đồ, chủ đề và phòng trên VibeTube đều lấy từ phiên bản Haunted Edition của VibeStudio do Lin tạo ra; đây là bản cắt đa phương thức, tập trung vào những gì khán giả gửi và hai loại tạm ngưng, ở định dạng tương tác một trang của phòng thí nghiệm Agent Valley. Studio là bối cảnh; mọi cơ chế bên dưới đều là cơ chế thông thường mà bạn sẽ sử dụng cho quy trình hỗ trợ, quy trình đánh giá nội dung hoặc bất kỳ quy trình nào khác phải tạm dừng để chờ người dùng và chờ một công việc chậm.
2. 🧰 Thiết lập
- What (Cái gì) — một tài khoản có thể thanh toán, một dự án, phòng thí nghiệm đã cài đặt, mô hình đã được chứng minh là có thể trả lời và workbench đang chạy ở chế độ nền trên một cổng.
- Lý do — một quy trình trên một cổng: trang, trình chỉnh sửa, trình xác minh,
/api/*vàadk webcó chung một nguồn gốc, vì vậy, không có gì trong lớp học lập trình này cần đến thiết bị đầu cuối thứ hai hoặc cổng thứ hai. - Cách thực hiện – sao chép, hai tập lệnh. Mã thứ hai thực hiện lệnh gọi mô hình thực, khởi động workbench và bắt đầu tạo kho văn bản RAG và Ngân hàng bộ nhớ trong khi bạn đọc, nhờ đó, các phiên 4 và 6 sẽ thấy các phiên này đã sẵn sàng.
Mọi nội dung trong lớp học lập trình này đều chạy trong Cloud Shell — gcloud, Python, Node, ffmpeg và git đã được cài đặt sẵn ở đó, còn Gemini, mô hình hình ảnh, Veo, Công cụ RAG và Ngân hàng bộ nhớ đều được truy cập thông qua Vertex AI bằng thông tin xác thực của riêng bạn. Không có khoá API nào trong lớp học lập trình này và bạn không cần cài đặt gì trên máy tính xách tay. Một máy tính xách tay có Google Cloud SDK, Python 3.12 và Node 20 trở lên cũng hoạt động theo cách tương tự.
Có một thứ mà bài thực hành này không thể cung cấp cho bạn: một dự án trên Google Cloud có thông tin thanh toán. Bước 1 giúp bạn có một tài khoản có thể thanh toán cho dự án; bước 4 biến tài khoản đó thành một dự án. Hãy thực hiện theo thứ tự đó — mọi bước sau bước 4 đều đọc dự án mà bước 4 giải quyết.
1 · Tạo tài khoản có thông tin thanh toán (~1 phút)
Đăng nhập bằng một Tài khoản Google đã bật tính năng thanh toán. Bạn có thể dùng bất kỳ tài khoản nào như vậy. Nếu bạn chưa có, hãy bắt đầu dùng thử miễn phí; bạn sẽ có một tài khoản thanh toán, đây là tất cả những gì bạn cần cho bước 4. Nếu bạn đang tham gia một phòng thí nghiệm có người hướng dẫn, trước tiên, hãy đổi các khoản tín dụng cho phiên này — thao tác này sẽ tạo tài khoản thanh toán cho bạn.
2 · Mở Cloud Shell (~2 phút)
👉🖥 Nhấp vào Kích hoạt Cloud Shell ở đầu Google Cloud Console – đây là biểu tượng có hình thiết bị đầu cuối trong tiêu đề.

👉🖥 Nhấp vào Mở trình chỉnh sửa (biểu tượng thư mục mở có bút chì). Thao tác này sẽ mở ra Trình chỉnh sửa Cloud Shell có trình khám phá tệp ở bên trái — đây là nơi bạn truy cập bất cứ khi nào bạn muốn đọc toàn bộ tệp.

👉🖥 Nếu bạn được nhắc uỷ quyền vào bất kỳ thời điểm nào trong ngày hôm nay, hãy nhấp vào Uỷ quyền để tiếp tục.
👉🖥 Mở một cửa sổ dòng lệnh bên trong trình chỉnh sửa — đó là thẻ 1 và mọi bước 👉💻 đều chạy ở đó. Tab 2 là chính bàn làm việc và bạn sẽ mở thẻ này ở bước 7. Nếu cửa sổ dòng lệnh chưa xuất hiện ở cuối màn hình, hãy mở cửa sổ này bằng cách chọn View → Terminal (Xem → Cửa sổ dòng lệnh):

👉💻 Kiểm tra xem bạn đang đăng nhập bằng tài khoản nào — đó phải là tài khoản ở bước 1:
gcloud auth list
3 · Tải phòng thí nghiệm (~1 phút)
👉💻 Sao chép kho lưu trữ:
cd ~
git clone https://github.com/cuppibla/vibe-studio-multimodal
cd vibe-studio-multimodal
4 · Tạo dự án (~2 phút)
👉💻 Một tập lệnh tìm tài khoản thanh toán của bạn, tạo một dự án trên tài khoản đó, liên kết thông tin thanh toán và trỏ Cloud Shell đến dự án đó:
cd ~/vibe-studio-multimodal
./setup_project.sh
Bạn sẽ thấy một ✓ cho tài khoản thanh toán mà công cụ này chọn, một ✓ cho dự án mà công cụ này tạo và một kết thúc như sau:
Project ready: vibe-studio-4271 Next: ./setup_codelab.sh
vibe-studio-4271 là mã dự án của bạn – mã của bạn sẽ có các chữ số khác. Có 4 điều xảy ra trong đó:
thanh toán | sẽ lấy tài khoản | |
project | tạo và liên kết tài khoản thanh toán đó với | |
chọn | ghi mã nhận dạng vào | |
waits | thăm dò ý kiến cho đến khi dự án thực sự được phép phân phát, tối đa hai phút | |
👉🖥 Dự án đó hiện đã có trong bảng điều khiển — mã của dự án nằm trong thẻ Project info (Thông tin dự án) trên Trang tổng quan:

5 · Cài đặt và thiết lập phòng thí nghiệm (~4 phút)
👉💻
cd ~/vibe-studio-multimodal
./setup_codelab.sh
Một tập lệnh, an toàn để chạy lại — lần chạy thứ hai giữ mọi câu trả lời bạn đã đưa ra trong lần đầu tiên. Chương trình này sẽ hỏi hai câu hỏi gần đầu và Enter sẽ lấy giá trị mặc định cho cả hai:
mã sự kiện | phòng VibeTube mà video của bạn được xuất bản trong buổi 7. Nhập mã phòng do người hướng dẫn cung cấp hoặc nhấn phím Enter để vào phòng | |
tên hiển thị | tên mà phòng ghi nhận cho bạn hoặc nhấn phím Enter để chọn tên được tạo từ tài khoản của bạn. | |
Cả hai đều nằm trong .env và có thể thay đổi sau, trong .env hoặc trên trang xuất bản. Sau đó, hệ thống sẽ tự động thực hiện các bước còn lại theo thứ tự:
uv env | cài đặt uv nếu thiếu, sau đó | |
apis | bật 6 API trên dự án đã chọn ở bước 4 và xác nhận từng câu trả lời trước khi tiếp tục: | |
tài khoản | đảm bảo tài khoản dịch vụ mặc định của Compute Engine trong dự án tồn tại và có các vai trò mà bản dựng và dịch vụ của phiên 7 cần | |
.env | ghi | |
veo · room | xác nhận | |
model | thực hiện một lệnh gọi thực tế đến Gemini, nhờ đó, một dự án không hợp lệ sẽ không thành công tại thời điểm thiết lập chứ không phải trong một phiên | |
starter | đặt 9 tệp mà bạn sẽ chỉnh sửa vào đúng vị trí từ | |
trang | cài đặt và tạo | |
đám mây | bắt đầu tạo kho dữ liệu của Công cụ RAG và Ngân hàng bộ nhớ ở chế độ nền, nhờ đó phiên 4 và 6 có thể sử dụng ngay | |
preflight | chạy | |
6 · Đọc thông tin trước khi bắt đầu (khoảng 1 phút)
Điều cuối cùng mà bước 5 in ra là kết quả kiểm tra trước — mỗi dòng là một kết quả thăm dò thực tế, không phải phỏng đoán:
✓ 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) nói đúng: biểu đồ đó không có dữ liệu và bạn sẽ điền dữ liệu vào đó trong phiên 3. Hai dòng - cũng đúng — một kho văn bản mất một hoặc hai phút để xuất hiện và ngân hàng mất khoảng hai mươi giây, và bạn sẽ không cần đến cả hai trước phiên 4.
👉💻 Chạy lại bất cứ lúc nào để xem bạn đang ở đâu — công cụ này đọc các tệp, dự án và máy chủ đang chạy của bạn, chứ không phải danh sách kiểm tra:
cd ~/vibe-studio-multimodal
uv run python scripts/preflight.py
7 · Mở bàn làm việc (~1 phút)
Workbench đang chạy. Bước 5 đã khởi động quy trình này ở chế độ nền, một quy trình uvicorn trên cổng 4600 phân phát trang, mọi tuyến /api/* và adk web được gắn tại /workbench. Bạn không cần bắt đầu gì cả và cũng không có gì trong phòng thí nghiệm này yêu cầu bạn phải khởi động lại: nội dung chỉnh sửa sẽ xuất hiện trong lần chạy tiếp theo.
👉🖥 Nhấp vào đường liên kết mà quy trình kiểm tra trước khi chạy đã in ra, hoặc trong Cloud Shell: Web Preview (Xem trước trên web) → Change port (Thay đổi cổng) → 4600. Hãy gọi đó là thẻ 2. Mở Phiên 1 · Câu chuyện.
Ba lệnh mà bạn có thể cần sau này, tất cả đều từ ~/vibe-studio-multimodal:
| dừng workbench | |
| khởi động nó ở nền trước; Ctrl+C dừng nó và mọi quy trình mà nó đã bắt đầu | |
|
| |
Bố cục kho lưu trữ
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 · Câu chuyện — đêm và biểu đồ
👉🖥 Thẻ 2 mở ra Phiên 1 · Câu chuyện. Đọc: sáu công việc được sắp xếp thành một vòng tròn xung quanh một người — nghiên cứu xu hướng, rà soát công việc tồn đọng, chọn hướng đi, kiểm tra chính sách, viết kịch bản, kết xuất đoạn video — và vòng tròn này lặp lại mỗi đêm. Gemini có thể tự nghiên cứu, xử lý công việc tồn đọng và viết kịch bản; Veo có thể tự kết xuất; việc lựa chọn và kiểm tra là của con người.
Bên dưới vòng tròn là biểu đồ mà phần còn lại của lớp học lập trình xây dựng, gồm 13 nút từ một ý tưởng đến một đoạn video và áp phích của đoạn video đó. Di chuột lên một nút để xem chức năng của nút đó và phiên tạo ra nút đó. Bạn không cần chỉnh sửa gì ở đây; nút Tạo áp phích đầu tiên của trang là phiên 2.
Từ vựng, vì vậy, sáu trang tiếp theo sẽ đọc mà không dừng lại:
word | ý nghĩa của việc này |
tác nhân | một ADK |
node | một bước của biểu đồ – một hàm Python, một tác nhân, một phép nối, một bộ định tuyến |
edge | một bộ nút chạy theo thứ tự; đồ thị là danh sách các nút này. Phiên 3 |
phiên | bản ghi của một lần chạy trong |
state | một từ điển mà các nút chia sẻ theo tên khoá; một nút ghi |
bộ nhớ | thông tin nào tồn tại lâu hơn một phiên — Ngân hàng bộ nhớ, thông qua lệnh gọi lại. Phiên 6 |
4. 2 · Tạo hình ảnh – một tác nhân và mô hình hình ảnh đóng vai trò là một công cụ

👉🖥 Trong bàn làm việc, hãy mở Phiên 2 · Tạo hình ảnh. Trước khi có biểu đồ, cách đơn giản nhất có thể hiệu quả là: một Agent có hướng dẫn là toàn bộ đêm dưới dạng văn xuôi, ba hàm Python làm tay và một lần chạy kết thúc bằng một bức tranh.
Tác nhân là một mô hình, một chỉ dẫn và các công cụ
stage0_prompt/agent.py xác định root_agent bằng ba trường của tác nhân ADK. model là mô hình Gemini của agent/platform/config.py. instruction là câu lệnh hệ thống: sáu công việc trong một hơi thở, công việc cuối cùng là một bức tranh. tools chứa 3 hàm.
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."))
Mô hình là một thế giới khép kín: mô hình suy luận dựa trên trọng số và mã thông báo trong ngữ cảnh của nó. Công cụ là một hàm Python mà mô hình có thể yêu cầu. ADK đọc chữ ký và chuỗi tài liệu của từng hàm rồi khai báo hàm đó cho mô hình; khi mô hình muốn có một hàm, mô hình sẽ phát ra một sự kiện function_call, thời gian chạy sẽ chạy Python và ghi một sự kiện function_response trở lại ngữ cảnh, và mô hình sẽ tiếp tục với kết quả trước đó.
Ba hàm, và một trong số đó vẽ
Hai trong số ba người đã đọc nghiên cứu. Tác nhân thứ ba yêu cầu mô hình hình ảnh tạo một áp phích:
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 gọi agent/platform/images.py, mô hình hình ảnh được gói dưới dạng một hàm thuần tuý theo cách videogen.py gói Veo: tiền tố kiểu nhà (giao diện Halloween đa giác thấp ấm cúng của kênh, màu cam bí ngô và màu kem dưới ánh nến trên nền màu mận chín), chủ thể, khung hình dọc 9:16, một tệp PNG trong runs/media/posters, được phân phát tại /static/posters. Với STUDIO_REAL_IMAGES=0, lớp học lập trình này sao chép một áp phích thay thế và gắn cờ kết quả prebaked để có thể chạy ở bất cứ đâu.
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."""
Lệnh gọi tương tự là một nút trong biểu đồ ở phiên 5.
Thực hành: một lần chỉnh sửa
Theo mặc định, make_poster trả về một từ điển cho biết áp phích đã bị bỏ qua. 👉🖥 Trong trình chỉnh sửa workbench, hãy gọi mô hình hình ảnh thay cho dòng TODO:
from agent.platform import images
return images.generate(subject, name="solo")
Lưu. Các hàng xác minh đọc tệp: ba công cụ trên tác nhân, lệnh gọi mô hình hình ảnh tại chỗ. 👉🖥 Nhấp vào Check the workflow loads (Kiểm tra tải quy trình làm việc), sau đó nhấp vào Open adk web (Mở web adk) rồi gửi:
tonight's idea: a tiny ghost learning to haunt a sock drawer
Những điều cần biết và lý do
- Hai sự kiện công cụ trước khi trả lời, sau đó là một câu hỏi.
check_trendsvàread_backlogchạy vì hướng dẫn yêu cầu kiểm tra và xem, nhưng mô hình không có cả hai trong trọng số của nó. Câu trả lời đề xuất một hướng đi và yêu cầu bạn xác nhận, vì chỉ dẫn yêu cầu phải có sự đồng ý trước. - Tin nhắn thứ hai nhận được mọi thứ. Gửi
skip the questions, describe the video and draw the poster. Mô hình này mô tả video, gọimake_postermột lần bằng dòng tiêu đề do chính mô hình viết và cho bạn biết vị trí lưu tệp PNG. Bảng xác minh cho thấy áp phích bên cạnh các hàng: một cảnh Halloween theo phong cách của kênh, được vẽ trong khoảng 8 giây. - Lý do đây không phải là kênh. Nghiên cứu được lồng ghép vào văn xuôi mà không có gì có thể trích dẫn. Chính sách này được chứng nhận bởi mô hình đã viết ra chỉ dẫn, và áp phích được vẽ trước khi có bất kỳ ai kiểm tra. Thoả thuận này đã được thảo luận trong một tin nhắn. Văn bản nhắc không phải là một cổng. Phần còn lại của lớp học lập trình này sẽ chia 6 công việc này thành một biểu đồ được vẽ.
Trang này kết thúc bằng phần Cấu trúc của một tác nhân ADK, một tài liệu tham khảo mà bạn có thể xem lại: mô hình, bối cảnh mà mô hình suy luận, các công cụ và cộng tác viên mà mô hình hoạt động thông qua, các lệnh gọi lại bao bọc mỗi lệnh gọi, cũng như phiên và bộ nhớ nằm ngoài mô hình.
5. 3 · Workflows — phân đầu ra và tham số kết hợp, bộ định tuyến và lặp lại

👉🖥 Trong workbench, hãy mở Session 3 · Workflows (Buổi 3 · Quy trình công việc), phần 3a và 3b. Mỗi phần được xây dựng trong một ứng dụng hộp cát để bạn có thể tự chạy phần đó trong adk web: fan-out trong stage1_fanout, bộ định tuyến và vòng lặp của bộ định tuyến trong stage3_router.
Phân tán và kết hợp (3a)
ADK Workflow là một danh sách cạnh. Mỗi mục nhập là một chuỗi, một bộ dữ liệu gồm các nút chạy theo thứ tự. Hai chuỗi rời khỏi START chạy song song. Một chuỗi kết thúc bằng JoinNode sẽ báo cáo cho chuỗi đó và phép nối sẽ đợi mọi chuỗi kết thúc ở đó trước khi tiếp tục, chuyển giao một từ điển được khoá theo tên nút. Một mục tiêu được viết dưới dạng từ điển sẽ liên kết tên tuyến đường của bộ định tuyến với các nút. Trang 3a của bàn làm việc vẽ các hình và có bài tập viết các cạnh.
Loại nút | Được viết dưới dạng | Ý nghĩa |
Nút hàm |
| Python thuần tuý. Cũng có thể ghi trạng thái dùng chung bằng |
Nút kết hợp |
| Chờ cho đến khi mọi chuỗi đến đều đã báo cáo, sau đó chuyển tất cả kết quả đầu ra của các chuỗi đó dưới dạng một từ điển. |
Nút tác nhân |
| Một lệnh gọi mô hình ở chế độ |
Bộ định tuyến |
| Một nút hàm có giá trị trả về đặt tên cho cạnh đi. |
Nút dữ liệu đầu vào của con người |
| Tạm ngưng biểu đồ cho đến khi có người trả lời biểu mẫu. Phiên 5. |
Biểu đồ đầu tiên là nghiên cứu của phiên 2 dưới dạng các nút. scan_trends và read_backlog trong agent/graph.py là hai trình đọc; read_backlog cũng ghi ý tưởng của tối nay vào trạng thái dùng chung. stage1_fanout/agent.py xác định phép nối và tạo Workflow từ một danh sách có tên là EDGES, danh sách này ban đầu trống (0 edges của quy trình kiểm tra trước). 👉🖥 Điền hai chuỗi vào danh sách này:
EDGES = [(START, scan_trends, join_research),
(START, read_backlog, join_research)]
👉🖥 Chạy ứng dụng trong adk web. Hai nút sáng lên cùng nhau, sau đó là nút nối; mở sự kiện của join_research và đọc một từ điển có khoá scan_trends và khoá read_backlog. Không có lệnh gọi mô hình nào được thực hiện. Không có gì định dạng lại từ điển của nút kết hợp: nút sau nút kết hợp, trong bước 3b, là một tác nhân và nút tác nhân nhận node_input làm thông báo, vì vậy, từ điển sẽ đến dưới dạng JSON mà mô hình đọc trực tiếp.
Bộ định tuyến và vòng lặp (3b)
Một Agent bên trong Workflow chạy ở chế độ single_turn: nhận kết quả của nút trước đó làm thông báo, thực hiện một lệnh gọi mô hình và trả lời một lần. Với output_schema, câu trả lời được xác thực dựa trên mô hình Pydantic trước khi nút tiếp theo nhìn thấy, vì vậy, biểu đồ sẽ truyền các đối tượng đã nhập, chứ không phải văn bản. propose_directions đọc từ điển nghiên cứu của phép nối và đề xuất 4 ứng viên, mỗi ứng viên là một cảnh Halloween. Ứng viên 4 là mồi nhử: tiêu đề của ứng viên này chứa một từ mà kênh từ chối, vì vậy, bộ định tuyến có thứ để bắt. Mỗi ứng viên đều có một poster_idea, một dòng đặt tên cho hình ảnh duy nhất giúp quảng bá video; người viết kịch bản giữ hình ảnh đó và bộ phận truyền thông sẽ vẽ hình ảnh đó trong phiên 5.
Bộ định tuyến là một nút hàm có giá trị trả về đặt tên cho cạnh mà quá trình chạy thực hiện. Không cần tuyển người mẫu: policy_check đọc agent/policy_words.txt tại thời điểm đưa ra quyết định, so khớp toàn bộ từ với tiêu đề và góc nhìn của hướng đi, rồi định tuyến hướng đi đó trước khi viết kịch bản hoặc chi tiêu. Chính sách là dữ liệu: chỉnh sửa tệp và lần chạy tiếp theo sẽ đọc tệp đó.
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 là fan-out cộng với propose_directions và choose_direction, một giá trị thay thế cho cửa người mà bạn xây dựng trong phiên 5: nó chọn ứng viên có số kết thúc ý tưởng của đêm nay (..., pick 4), nếu không thì là ứng viên đầu tiên và ghi lựa chọn vào trạng thái. scripter là một nhân viên hỗ trợ single_turn có thể biến một hướng đi đã được phê duyệt thành một Script: tiêu đề, nội dung mô tả, thẻ, câu mở đầu, ba cảnh quay và một ý tưởng áp phích, mỗi cảnh quay là một hình ảnh Halloween theo phong cách của kênh.
Vòng lặp phía sau lối ra bị từ chối. quarantine hoạt động như một tác nhân ở chế độ tác vụ. Agent có mode: chat là vòng lặp trò chuyện mà tác nhân gốc chạy với một người, single_turn là một lệnh gọi có câu trả lời có cấu trúc và task là một vòng lặp các lệnh gọi công cụ mà tác nhân tự chạy cho đến khi gọi finish_task tích hợp sẵn, có các đối số ADK lấy từ output_schema. Quarantine gọi find_policy_hits theo hướng bị từ chối, yêu cầu suggest_replacement cung cấp từ thay thế được phê duyệt của kênh cho mỗi từ (agent/policy_replacements.txt, dữ liệu một lần nữa), viết lại, kiểm tra lại và kết thúc bằng CleanedDirection đã nhập. Hãy đọc mã này trên trang, đừng nhập mã.
quarantine = Agent(
name="quarantine",
model=config.MODEL,
mode="task",
instruction=QUARANTINE_INSTRUCTION,
tools=[find_policy_hits, suggest_replacement],
output_schema=CleanedDirection)
Khi được vận chuyển, chuỗi sẽ chạy từ người chọn đến thẳng người viết kịch bản. 👉🖥 Đặt bộ định tuyến giữa hai thành phần này, với hai lối ra và đường quay lại từ vùng cách ly; ba dòng thay thế một dòng:
(join_research, propose_directions, choose_direction, policy_check),
(policy_check, {"OK": scripter, "BLOCK": quarantine}),
(quarantine, scripter),
👉🖥 Chạy cả hai tuyến đường. Gửi ý tưởng nguyên trạng: choose_direction chọn ứng viên 1, policy_check phát ra OK và người viết kịch bản viết một kịch bản có cấu trúc. Nhấp vào Phiên mới rồi gửi lại ý tưởng với , pick 4 ở cuối: ứng viên 4 là mồi nhử, policy_check phát ra BLOCK, cách ly gọi các công cụ của mình, viết lại các từ bị từ chối, gọi finish_task và hướng đã dọn dẹp sẽ quay trở lại người viết kịch bản. Các hàng xác minh đọc cả hai lần chạy từ kho lưu trữ phiên.
Mỗi câu trong câu lệnh của phiên 2 hiện có một nút: nghiên cứu là hai trình đọc và một phép nối, từ chối là một bộ định tuyến và một tác nhân tác vụ, tập lệnh là một nút tác nhân. Trong toàn bộ biểu đồ, lựa chọn thay thế là hai nút hàm, direction_gate và persist_direction. Bạn sẽ thấy hai nút này chạy thẳng cho đến khi phiên 5 khiến cửa dừng lại. Thoả thuận với nhà sáng tạo vẫn chỉ là một bản nháp; nghiên cứu cũng vậy, vì chưa có ý kiến của khán giả.
6. 4 · Nhập dữ liệu đa phương thức — những gì khán giả gửi, đọc vào một kho văn bản

👉🖥 Trong workbench, hãy mở Session 4 · Multimodal ingestion (Phiên 4 · Nhập dữ liệu đa phương thức), phần 4a và 4b. Khán giả không chỉ viết. Mỗi loại trình đọc sẽ chuyển mọi nội dung mà người dùng gửi thành văn bản có cùng định dạng, một kho dữ liệu RAG Engine sẽ lưu giữ tất cả nội dung đó, và trình đọc thứ ba trong quy trình phân tán sẽ tìm kiếm nội dung theo ý nghĩa rồi trả về nội dung nghe nhìn.
Nội dung khán giả gửi (4a)
agent/comments.md có 12 bình luận dưới 6 video về Halloween của kênh. agent/audience/ chứa phần còn lại: 8 bản ghi âm (voice/*.mp3), 14 bức ảnh về phòng của người xem (photo/*.jpg) và 6 đoạn video (clip/*.mp4), một bức ảnh được lia chậm kèm theo giọng lồng tiếng của người xem. Trang 4a mở ra tất cả: mọi ghi chú thoại đều phát, mọi ảnh đều hiện, mọi đoạn video đều chạy.

Công cụ RAG truy xuất văn bản. Một bản ghi chú thoại, một bức ảnh hoặc một đoạn video chỉ có thể tham gia vào kho văn bản theo cách mà các áp phích trước đây đã làm trong rag.caption_posters: một mô hình biến nó thành một dòng văn bản đặt tên cho tệp của nó, dòng đó trở thành một đoạn văn và khi đoạn văn đó quay trở lại, tệp cũng quay trở lại cùng với nó. agent/platform/audience.py có một trình đọc cho mỗi loại. Mỗi tệp sẽ gửi một tệp và một câu lệnh đến Gemini, đồng thời ghi một dòng cho mỗi tệp:
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 cũng làm như vậy với image/jpeg và PHOTO_PROMPT; watch_clips với video/mp4 và CLIP_PROMPT, còn Gemini xem hình ảnh và nghe nhạc phim trong một cuộc gọi. Mỗi trình đọc đều viết cùng một hình dạng dòng, vì vậy, việc phân đoạn, nhúng và truy xuất không bao giờ biết được một đoạn văn bản đến từ loại nào:
- 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 ...

Ngữ liệu. Vertex AI RAG Engine là công cụ truy xuất tài liệu: một kho văn bản được chia thành các đoạn văn khoảng 120 mã thông báo, mỗi đoạn văn được nhúng bằng text-embedding-005, và một truy vấn được nhúng theo cùng một cách và được trả lời bằng các đoạn văn gần nhất theo nghĩa, chứ không phải theo các từ được chia sẻ. "cozy little hauntings at home" (những bóng ma nhỏ ấm cúng ở nhà) tìm thấy bình luận yêu cầu phép thuật nhỏ trong phòng nhỏ, ghi chú thoại về con dơi ôm bóng đèn và đoạn video có nhạc nền nói rằng bóng ma là một chiếc cốc giấy.
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)))
Quá trình thiết lập đã bắt đầu tạo kho văn bản; hàng kết nối của trang 4a cho biết kho văn bản có hoạt động hay không và tạo kho văn bản nếu quá trình thiết lập không thể thực hiện. Từ các nút trên trang hoặc thiết bị đầu cuối: python -m agent.platform.rag tạo kho văn bản và ghi lại kho văn bản đó trong runs/ragcorpus.json; python -m agent.platform.rag load --audience chạy mọi trình đọc trong audience.READERS, sau đó tải nội dung họ đã viết lên bên cạnh các bình luận và chú thích của người đăng, mỗi tệp có tên riêng và chờ chỉ mục trả lời; python -m agent.platform.rag query "cozy little hauntings at home" cho thấy kết quả truy xuất tìm thấy các đoạn văn không có từ nào giống với cụm từ tìm kiếm.
Nội dung chỉnh sửa. Khi xuất xưởng, READERS chỉ chứa read_comments; ba đầu đọc phương tiện nằm sau một # . 👉🖥 Chạy quy trình nhập một lần khi dữ liệu được gửi: kết quả cho biết not read (no reader wired in audience.READERS): voice, photo, clip và chỉ có nhận xét và phụ đề được nhập. Sau đó, xoá # :
transcribe_voice, caption_photos, watch_clips,
👉🖥 Lưu và chạy lại quy trình nhập. Trình đọc mất khoảng 3 phút cho 40 mục; bảng điều khiển cho biết heard, saw và watched mỗi lần một dòng và mọi thẻ trong lưới đều điền vào dòng của thẻ đó. Các hàng xác minh đọc tệp, kết quả của trình đọc và kho văn bản: 4 trình đọc được kết nối, 8 bản ghi chú thoại được nghe, 14 ảnh được xem, 6 đoạn video được xem, 5 tệp trong kho văn bản.
Người đọc thứ ba (4b)
Ý kiến phản hồi của khán giả là nghiên cứu, được chia sẻ qua mỗi lần chạy, vì vậy, ý kiến phản hồi này sẽ được đưa vào biểu đồ nơi nghiên cứu được đưa vào: dưới dạng trình đọc thứ ba trong phép nối. read_feedback trong agent/graph.py lấy ý tưởng của tối nay, yêu cầu kho dữ liệu cung cấp các đoạn văn gần nhất và báo cáo cho join_research, nơi đang chờ ba đoạn văn. Cạnh (START, read_feedback, join_research) chuyển vào stage5_rag/agent.py, hình dạng mà buổi 3 đã dạy; nội dung chỉnh sửa của bạn nằm trong nút. Truy xuất, không phải lệnh gọi mô hình và mang tính tất định: cùng một ý tưởng sẽ tìm thấy cùng một đoạn văn trong khi người đề xuất thay đổi.
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
Mỗi dòng trong kho văn bản lấy từ một bản ghi chú thoại, ảnh, đoạn video hoặc áp phích đều nêu tên tệp của dòng đó. rag.media_in(hits) đọc những tên đó từ các đoạn văn và chuyển chúng thành URL mà trang có thể hiển thị và phát. 👉🖥 Trả lại nội dung nghe nhìn kèm theo các đoạn văn, để những nội dung mà khán giả gửi đi kèm với những gì họ nói:
return Event(output={"query": query, "feedback": [h["text"] for h in hits],
"media": rag.media_in(hits)})
👉🖥 Chạy stage5_rag với một ý tưởng. Mở read_feedback: truy vấn là ý tưởng của bạn, kết quả đầu ra có năm đoạn văn gần nhất và danh sách media có một URL cho mỗi tệp. Mở join_research: ba phím. Mở propose_directions: ứng viên 1 đến 3 trích dẫn feedback trong bằng chứng của họ. Bảng xác minh cho thấy những nội dung mà độc giả đã xem trong các hàng: một đoạn video, ba bản ghi âm và một áp phích về con ma trong ánh đèn tủ lạnh.

7. 5 · Tạo video – hai loại tạm ngưng, một cơ chế

👉🖥 Trong bàn làm việc, hãy mở Phiên 5 · Tạo video, phần 5a và 5b, được xây dựng trong stage6_video, toàn bộ biểu đồ. Đoạn video mất vài phút và áp phích mất vài giây, còn quá trình chạy dừng hai lần: một lần cho bạn, một lần cho Veo. Hai điểm dừng là một cơ chế và hai nhánh của bàn làm việc chia sẻ những gì họ đã tạo theo tên.
Hai loại dây đeo (5a)
Không cần chờ đợi. Một lượt chạy phải dừng sẽ ghi một lệnh gọi mở vào phiên của nó và kết thúc; bất kỳ ai trả lời lệnh gọi đó, một người hoặc một quy trình, sẽ tiếp tục lệnh gọi đó. Trang này mở ra một bảng:
cho một người · | cho một công cụ chậm · | |
người dừng lượt chạy |
|
|
nội dung mà nó chờ đợi | một người: một trong bốn thẻ | tác vụ chậm: Hoạt động của Veo, phút |
những gì có trong phiên | một lệnh gọi | một lệnh gọi |
cách tiếp tục | một | một |
người gửi |
|
|
nơi nó sống trong thời gian đó |
| cùng một hàng, cùng một mã nhận dạng · không có gì chạy |

Dành cho một người. Trong một câu lệnh, "yêu cầu nhà sáng tạo xác nhận" là một lời khuyên và phiên 2 đã bỏ qua lời khuyên này trong một tin nhắn. Trong một quy trình công việc, trạng thái tạm dừng là của công cụ. Một nút tạo ra RequestInput sẽ tạm ngưng quá trình chạy: ADK ghi một lệnh gọi mở có tên là adk_request_input vào phiên bằng message, response_schema và payload, cung cấp cho nút đó một interrupt_id và trả về cho phương thức gọi. Không có luồng nào chờ. Điều duy nhất có thể tiếp tục quy trình là một function_response mang mã nhận dạng đó, được xác thực dựa trên giản đồ của bạn; một tin nhắn trò chuyện đến quy trình làm việc là một lượt mới, không phải là câu trả lời. Giản đồ cũng là biểu mẫu: adk web hiển thị biểu mẫu đó và ứng dụng VibeStudio hiển thị cùng một giản đồ dưới dạng bốn thẻ. direction_gate đã ghi các đề xuất vào trạng thái. 👉🖥 Cách ngăn chặn:
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})
Câu trả lời của bạn sẽ đến persist_direction dưới dạng node_input, một từ điển có một khoá, pick. Bốn ứng viên đến theo một cách khác: cổng đã ghi họ vào trạng thái chia sẻ trong candidates và ADK liên kết khoá trạng thái với một tham số có cùng tên. Nút này phân giải lựa chọn thành một ứng viên và ghi lại direction, angle, hook và user:prefs bằng Event(state=...). Các khoá có tiền tố user: sẽ được duy trì trong các phiên hoạt động của người dùng đó.
Vì công cụ chậm. Quá trình kết xuất của Veo mất vài phút và biểu đồ không nên chờ lâu như vậy. render_submit trong agent/desk.py bắt đầu công việc và trả về ngay lập tức bằng một biên nhận có trạng thái là 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"]}
Được gói trong LongRunningFunctionTool, biên nhận đó có ý nghĩa đối với ADK: lượt của tác nhân kết thúc, quy trình công việc tạm dừng tại render_desk và lệnh gọi mở có mã nhận dạng và biên nhận là một hàng trong runs/sessions.db. 👉🖥 Trong stage6_video/agent.py, hãy gói công cụ:
tools=[LongRunningFunctionTool(render_submit)])
Tiếp tục theo mã nhận dạng, từ một quy trình khác. agent/deliver.py tìm lệnh gọi đang chờ xử lý trong kho lưu trữ phiên, thăm dò Veo cho đến khi có đoạn video, ghi kết quả vào runs/state.json và gửi function_response có mã nhận dạng của lệnh gọi đó đến cùng một phiên. 👉🖥 Xây dựng phần phản hồi:
part = Part(function_response=FunctionResponse(
id=row["call_id"], name=row["name"], response=response))
👉🖥 Chạy stage6_video cho đến khi dừng hai lần. Gửi ý tưởng: quá trình chạy dừng lại ở một biểu mẫu được tạo từ giản đồ của bạn, bốn ứng viên trong tải trọng của biểu mẫu. Nhập 2 vào pick rồi gửi; biểu đồ sẽ tiếp tục tại persist_direction. Hãy thử kiểm tra việc tắt ứng dụng tại đây: tạm ngưng tại biểu mẫu, dừng máy chủ (scripts/stop.sh trong thẻ 1), khởi động lại máy chủ (scripts/restart.sh), mở lại phiên. Biểu mẫu vẫn còn vì lệnh tạm dừng là một hàng chứ không phải một luồng. Sau script_ready, render_desk gọi render_submit, nhận biên nhận, trả lời WAITING và quá trình chạy kết thúc; phiên giữ lại mã cuộc gọi. Chạy quá trình phân phối từ bảng điều khiển hoặc thẻ 1 của trang:
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
👉🖥 Nhấp vào Refresh adk web (Làm mới web adk): function_response theo sau lệnh gọi đang chờ xử lý, join_media phát hành và store_release chạy. Tám hàng xác minh đọc hai điểm dừng trong phiên: cuộc gọi của cổng và câu trả lời của bạn, biên nhận của trình kết xuất và việc phân phối của trình kết xuất.
Trạng thái và không gian làm việc (5b)
Bàn truyền thông là cấu trúc của 3a, một nhánh rẽ và một nhánh nối, với một nhánh chậm. script_ready gửi kịch bản theo hai cách. render_desk gửi đoạn video và quá trình chạy sẽ tạm dừng. make_poster là công cụ của phiên 2 dưới dạng một nút: poster_idea của kịch bản, phong cách của kênh và những gì Ngân hàng bộ nhớ biết về sở thích của nhà sáng tạo đối với áp phích (được liên kết từ trạng thái dưới dạng memory_facts, phiên 6) sẽ được chuyển đến mô hình hình ảnh và một tệp PNG 9:16 sẽ được trả về sau vài giây. join_media chờ đợi luồng chậm và store_release ghi những gì tìm được vào trạng thái.
Chia sẻ theo tên. store_release không bao giờ nhận được áp phích. Nó khai báo một tham số có tên là poster_url và ADK liên kết tham số đó từ trạng thái dùng chung, vì một nút trong nhánh khác đã ghi khoá đó. Không có tải trọng nào chuyển giữa các nhánh:
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'}")
Khi được vận chuyển, make_poster sẽ tạo ra áp phích dưới dạng kết quả và thông báo, còn store_release sẽ nói poster: not in state. 👉🖥 Thêm lệnh ghi trạng thái vào kết quả cuối cùng:
state={"poster_url": rec.get("url", ""), "poster_path": rec.get("path", "")},
message=f"poster: {said}")
👉🖥 Chạy thêm một lần nữa và phân phối. Sự kiện mở của make_poster: URL của áp phích trong kết quả và delta trạng thái với poster_url. Mở store_release: thông báo của tệp này cho biết tên của người đăng. Khối Phiên của lần chạy này trên trang cho thấy cùng một hàng mà thẻ Trạng thái của adk web cho thấy: mã phiên mà một lần tiếp tục cần, lệnh gọi đang mở và các khoá mà nút đã ghi, poster_url trong số đó. Bảng xác minh cho thấy đoạn video và áp phích cạnh nhau.

8. 6 · Bộ nhớ – những thông tin mà kênh ghi nhớ giữa các đêm

👉🖥 Trong bàn làm việc, hãy mở Session 6 · Memory (Phiên 6 · Bộ nhớ), phần 6a và 6b, được tích hợp trong stage4_memory. Phiên 5 là về trạng thái của một lần chạy. Phiên này nói về những gì kênh ghi nhớ giữa các lần chạy: những gì biểu đồ nghe được từ bên ngoài, thông qua hai lệnh gọi lại.
Memory Bank (6a)
Vertex AI Memory Bank là bộ nhớ dài hạn được quản lý về một người. Thông tin thực tế nằm trong một phạm vi, tên ứng dụng và mã nhận dạng người dùng, cũng như trong các chủ đề mà bạn xác định. Khi bạn cung cấp cho Gemini một cuộc trao đổi bằng memories.generate, Gemini sẽ trích xuất thông tin phù hợp với một chủ đề, nhúng thông tin đó và hợp nhất với thông tin đã có: một thông tin mới được tạo, một thông tin thay đổi được cập nhật, một thông tin lặp lại được hợp nhất. memories.retrieve trả về các dữ kiện cho một phạm vi. Cả hai lệnh gọi đều nằm trong 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.",
}
Chủ đề thứ ba là gu hình ảnh của kênh. Bản phát hành đã ghi vào đó trong phiên 5: một áp phích đã vận chuyển được mô tả trong một câu và được ghi nhớ, còn áp phích tiếp theo được vẽ bằng câu đó trong câu lệnh.
Quá trình thiết lập đã bắt đầu tạo ngân hàng; hàng kết nối của trang 6a cho biết ngân hàng đó có hoạt động hay không. Từ các nút trên trang hoặc thiết bị đầu cuối: python -m agent.platform.bank tạo ngân hàng và các chủ đề của ngân hàng, đồng thời lưu tên tài nguyên vào bộ nhớ đệm trong runs/memorybank.json; python -m agent.platform.bank load tạo bốn phiên trước đó (hai kỷ nguyên động vật có quy tắc về phong cách, một kỷ nguyên tiện ích, một kỷ nguyên giả tưởng gần đây); python -m agent.platform.bank list cho thấy nội dung của bản chép lời: các tuyên bố thực tế hợp nhất, không phải bản chép lời.
Lệnh gọi lại (6b)
Bộ nhớ không phải là một nút. Lệnh gọi lại là một hàm mà bạn truyền đến Agent; ADK gọi hàm này tại một thời điểm cố định với bối cảnh trực tiếp và việc trả về None sẽ cho phép điều bình thường xảy ra. Ba cặp: xung quanh lượt của tác nhân (before_agent_callback, after_agent_callback, có CallbackContext), xung quanh mỗi lệnh gọi mô hình (before_model_callback có LlmRequest, after_model_callback có LlmResponse), xung quanh mỗi lệnh gọi công cụ. Các biện pháp bảo vệ, dữ liệu đo từ xa, bộ nhớ đệm và chèn ngữ cảnh sẽ được đặt ở đây mà không có nút trong biểu đồ.
Trong stage4_memory/agent.py, propose_directions đọc ngân hàng trước khi gọi mô hình: recall_taste tìm nạp các thông tin thực tế, thêm các thông tin đó vào yêu cầu dưới dạng khối MEMORY (Bộ nhớ), thông tin cũ nhất trước tiên và yêu cầu mô hình điều chỉnh các ứng viên từ 1 đến 3 theo sở thích hiện tại và coi các quy tắc là quy tắc ràng buộc. Hàm này cũng ghi các dữ kiện vào trạng thái dưới dạng memory_facts, nơi make_poster tìm thấy chúng. 👉🖥 Kết nối bằng dây:
output_schema=Directions,
before_model_callback=recall_taste)
Người viết kịch bản viết ra những gì nhà sáng tạo đã chọn sau lượt của mình: remember_pick đọc direction từ trạng thái, viết một câu về lựa chọn đó và đưa cho memories.generate. 👉🖥 Kết nối bằng dây:
output_schema=Script,
after_agent_callback=remember_pick)
👉🖥 Chạy bằng một thông báo trống và chọn một thẻ. Mở sự kiện propose_directions: yêu cầu này mang theo ba kỷ nguyên và các ứng cử viên từ 1 đến 3 nghiêng về thị hiếu hiện tại trong khi các xu hướng gợi ý điều gì đó khác. Sau khi người viết kịch bản, hãy nhấp vào Hiện ngân hàng: một kỷ niệm đã thay đổi hoặc được thêm vào lựa chọn của tối nay. Chạy đêm thứ hai: nó nhớ đêm đầu tiên.
9. 7 · Triển khai và xuất bản — biểu đồ bên trong ứng dụng, đêm trên VibeTube

👉🖥 Trong bàn làm việc, hãy mở Phiên 7 · Triển khai và xuất bản, phần 7a và 7b.
Triển khai VibeStudio (7a)
Trong hộp cát, adk web đã thúc đẩy biểu đồ. VibeStudio điều khiển bằng Runner của ADK, trên một luồng worker có vòng lặp sự kiện riêng và gộp mọi sự kiện vào một trạng thái chạy mà trang đọc qua Sự kiện do máy chủ gửi: người đọc, lựa chọn của bạn dưới dạng bốn thẻ, áp phích khi xuất hiện, kết xuất khi được phân phối. Câu trả lời của cổng và việc phân phối của trình kết xuất là cùng một lệnh gọi, một phần function_response và ứng dụng sẽ tự thăm dò ý kiến của Veo thay vì chờ bảng điều khiển.
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/ là bản sao từng byte của agent/ trong phòng thí nghiệm, được checks/verify_app.py kiểm tra. Danh sách cạnh hoàn chỉnh trong agent/graph.py là mọi cấu trúc của lớp học lập trình này ở một nơi, gồm 13 nút; danh sách này được điền sẵn để bạn đọc chứ không cần nhập:
(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),
👉🖥 Triển khai bằng nút Deploy (Triển khai) của workbench hoặc 👉💻 từ thẻ 1:
cd ~/vibe-studio-multimodal
uv run python vibestudio/deploy.py
chạy, với các giá trị từ .env của bạn được điền vào:
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 xây dựng vùng chứa bằng Cloud Build và triển khai vùng chứa đó trong một bước; quá trình xây dựng đầu tiên mất 4 hoặc 5 phút. --session-affinity có một thực thể ấm sẽ giữ một lần chạy bị tạm ngưng trên thực thể lưu giữ nó: kho lưu trữ phiên của phiên 5, một lần nữa. Trong một quy trình triển khai lớn hơn, kho lưu trữ phiên sẽ chuyển sang VertexAiSessionService và mối quan hệ tương đồng sẽ biến mất. Bản dựng và dịch vụ chạy dưới dạng tài khoản dịch vụ điện toán mặc định của dự án, quá trình thiết lập đã cấp các vai trò cần thiết cho tài khoản này.
👉🖥 Mở URL của dịch vụ và chạy một đêm trong ứng dụng: một ý tưởng, một lựa chọn, chờ đoạn video và áp phích. Thẻ Nhật ký lưu giữ cả hai.
Xuất bản (7b)
Việc xuất bản là một yêu cầu POST nhiều phần từ ứng dụng đến điểm cuối của phòng trên VibeTube, trong vibestudio/server/platform/publish.py. Đoạn video là video, áp phích là hình thu nhỏ, còn hồ sơ đặt tên cho phòng và bạn:
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 lưu giữ một video cho mỗi projectId và phòng, đồng thời dự án trên đám mây mà ứng dụng gửi là dự án Google Cloud của bạn, vì vậy, việc xuất bản lại sẽ thay thế video của bạn thay vì thêm một video. Áp phích thay thế không bao giờ được tải lên; áp phích thực là hình thu nhỏ, từng byte một.
Nội dung chỉnh sửa gần đây nhất là một cấu hình, không phải một dòng mã. 👉🖥 Trên trang 7b: URL của VibeStudio (được điền từ quá trình triển khai của 7a), mã sự kiện của phòng, chính xác như cách VibeTube viết (mã bạn đã thiết lập, trừ phi người hướng dẫn của bạn đã mở một mã khác) và tên của bạn. Nhấn vào xuất bản. Bàn làm việc chuyển tiếp lượt nhấp đến ứng dụng, ứng dụng đăng đêm và các hàng bên dưới đọc kết quả từ ứng dụng và từ VibeTube: ứng dụng trả lời, đêm kết thúc bằng một áp phích thực, mã đặt tên cho một phòng mở, VibeTube trả về một mã video, video nằm trên tường và hình thu nhỏ của video là áp phích của bạn. Thẻ kết quả cho thấy hình thu nhỏ trên VibeTube bên cạnh áp phích trong ứng dụng.

10. Sản phẩm bạn đã tạo
👉🖥 Phần cuối của trang 7b vẽ toàn bộ biểu đồ, 13 nút từ một ý tưởng đến một đoạn video và áp phích của video đó; di chuột qua một nút để xem chức năng của nút đó và phiên đã tạo ra nút đó.
Phiên | What it taught | Được viết dưới dạng |
2 · Tạo hình ảnh | Tác nhân: mô hình, chỉ dẫn, công cụ; mô hình hình ảnh dưới dạng công cụ; những điều mà câu lệnh không thể đảm bảo |
|
3 · Quy trình làm việc | Danh sách cạnh, phân nhánh và kết hợp, một bộ định tuyến có vòng lặp chế độ tác vụ phía sau |
|
4 · Nhập dữ liệu đa phương thức | Một độc giả cho mỗi loại, một hình dạng dòng, một kho văn bản; độc giả thứ ba theo ý nghĩa, với nội dung nghe nhìn |
|
5 · Tạo video | Hai loại tạm ngưng, một cơ chế; kho lưu trữ phiên; áp phích được chia sẻ theo tên |
|
6 · Bộ nhớ | Bộ nhớ qua đêm, hai lệnh gọi lại |
|
7 · Triển khai và xuất bản | Biểu đồ bên trong một ứng dụng, trên Cloud Run; đêm trên VibeTube |
|
Biểu đồ tuân theo 5 quy tắc sau:
- Tạm ngưng thay vì chờ đợi. Tạm dừng là một hàng trong kho lưu trữ phiên, không phải là một luồng.
- Tiếp tục mọi thứ theo cùng một cách — theo mã nhận dạng của cuộc gọi đang mở, cho dù người hay quy trình trả lời.
- Chia sẻ dữ liệu theo tên trong trạng thái, không phải bằng cách phân luồng dữ liệu qua mọi tải trọng.
- Đưa ra quyết định bằng mã trước khi trả phí cho một mô hình. Chính sách này là một tệp văn bản và một bộ định tuyến, đồng thời chạy trước khi hiển thị.
- Đọc mọi nội dung không phải văn bản thành văn bản trước khi nội dung đó đến kho văn bản, kèm theo tên tệp trong dòng để tệp có thể quay lại.
Câu lệnh không thể hứa hẹn về thứ tự, điểm dừng hoặc điểm kiểm tra. Biểu đồ có thể.
Bước tiếp theo
VertexAiSessionServicecho các phiên tồn tại lâu hơn một thực thể và bỏ--session-affinity.- Một webhook trả lời lệnh gọi của trình kết xuất thay vì trình thăm dò ý kiến: cùng một
FunctionResponse(id=...), người gửi khác. RequestInputthứ hai trước khi xuất bản — giản đồ là biểu mẫu, vì vậy, đây là một nút và không có mã trang.- Bình luận, ghi chú thoại và đoạn video mới của khán giả được thêm vào kho dữ liệu sau mỗi lần phát hành, kèm theo áp phích mà họ đã gửi.
Dọn dẹp
Mọi thứ mà phòng thí nghiệm này tạo ra đều nằm trong dự án mà bước 4 đã tạo, vì vậy, cách dọn dẹp ít tốn kém nhất là xoá dự án. Trong Cloud Shell:
gcloud projects delete $(cat ~/project_id.txt)
Để giữ lại dự án và chỉ xoá các phần có tính phí: dịch vụ Cloud Run (gcloud run services delete vibestudio --region us-central1), kho văn bản RAG và Ngân hàng bộ nhớ (tên tài nguyên của các phần này nằm trong runs/ragcorpus.json và runs/memorybank.json, đồng thời các trang Agent Engine và RAG Engine của bảng điều khiển sẽ xoá các phần này) và hình ảnh vùng chứa trong Artifact Registry.