1. Giới thiệu

Tìm hiểu cách xây dựng một công cụ đa tác nhân đa phương thức, có thể lấy một bản tóm tắt thô về chủ đề và điều phối việc tạo ra những thành phần hấp dẫn trên mạng xã hội. Bằng cách sử dụng Bộ công cụ phát triển tác nhân (ADK) và giao thức Agent2Agent (A2A) của Google, bạn sẽ tạo các tác nhân vi mô tự động, đóng gói các quy tắc về thương hiệu dưới dạng một kỹ năng tác nhân có thể dùng lại, triển khai dịch vụ tác nhân cho Cloud Run và chấm điểm các hình ảnh chính được tạo để đảm bảo tuân thủ thương hiệu bằng các hàm AI của BigQuery.
Bạn sẽ thực hiện
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách thực hiện các thao tác sau:
- Điều phối nhiều tác nhân ADK bằng quy trình công việc đồ thị xác định.
- Đóng gói nguyên tắc sử dụng thương hiệu dưới dạng một kỹ năng của tác nhân chỉ tải khi cần.
- Trang bị cho một tác nhân công cụ tạo hình ảnh giúp lưu kết quả đầu ra dưới dạng một cấu phần phần mềm.
- Hiển thị một tác nhân dưới dạng dịch vụ A2A và gọi tác nhân đó từ một tác nhân khác dưới dạng một nút từ xa.
- Triển khai Visual Director lên Cloud Run và gọi quy trình công việc qua A2A.
- Chấm điểm hình ảnh chính được tạo để phù hợp với thương hiệu trong BigQuery bằng các hàm AI.
Bạn cần có
- Một trình duyệt web (chẳng hạn như Chrome).
- Một tài khoản Google Cloud và một Dự án trên Google Cloud đã bật tính năng thanh toán.
2. Trước khi bắt đầu
Tạo một dự án trên Google Cloud
- Trong Google Cloud Console, trên trang chọn dự án, hãy chọn hoặc tạo một dự án trên Google Cloud.
- Đảm bảo bạn đã bật tính năng thanh toán cho dự án trên Cloud. Tìm hiểu cách kiểm tra xem tính năng thanh toán có được bật trên một dự án hay không.
Khởi động Cloud Shell
Bạn sẽ sử dụng Google Cloud Shell để tải mã xuống, chạy tập lệnh thiết lập và triển khai ứng dụng.
- Mở Google Cloud Shell trong trình duyệt (hoặc nhấp vào Kích hoạt Cloud Shell ở đầu Google Cloud Console):
Nhấp vào Tiếp tục nếu bạn nhận được thông báo bổ sung.Đợi cho đến khi cả thiết bị đầu cuối và trình chỉnh sửa được tải đầy đủ. Quá trình này có thể mất một hoặc hai phút.https://shell.cloud.google.com/ - Định cấu hình mã dự án và khu vực trên Google Cloud (ví dụ:
us-central1) trong môi trường của bạn bằng cách dán mã này vào thiết bị đầu cuối (thay thếvàbằng các giá trị của bạn):export PROJECT_ID="<YOUR_PROJECT_ID>" export REGION="<YOUR_REGION>" gcloud config set project $PROJECT_ID
Bạn sẽ thấy một thông báo tương tự như sau:Your active configuration is: [cloudshell-####] Updated property [core/project] - Bật các Cloud APIs của Google Cloud bắt buộc cho dự án của bạn:
gcloud services enable \ compute.googleapis.com \ cloudresourcemanager.googleapis.com \ aiplatform.googleapis.com \ storage.googleapis.com \ storage-component.googleapis.com \ bigqueryconnection.googleapis.com \ bigquery.googleapis.com \ run.googleapis.com \ secretmanager.googleapis.com \ cloudbuild.googleapis.com \ artifactregistry.googleapis.com \ iap.googleapis.com \ iam.googleapis.com
Sao chép kho lưu trữ
Chúng tôi có một mẫu mã để giúp bạn bắt đầu với lớp học lập trình này. Mặc dù tương đối nhỏ, nhưng kho lưu trữ GitHub nơi lưu trữ lại khá lớn. Để tăng tốc quá trình sao chép và tiết kiệm dung lượng ổ đĩa, chúng ta thường dùng git clone với tuỳ chọn --filter=blob:none, git sparse-checkout và một số lệnh khác.
Nghe có vẻ rất phức tạp, nhưng Antigravity có thể làm tất cả cho chúng ta!
Trước tiên, hãy định cấu hình Antigravity CLI để có thể giải quyết ngay vấn đề của chúng ta và khởi chạy nó.
Trong Cloud Shell, hãy chạy lệnh sau:
source <(curl -fsSL https://github.com/GoogleCloudPlatform/devrel-demos/raw/refs/heads/vladkol/cloud-shell-configure-agy/codelabs/labs-scripts/bwg-multi-agent-pitch-generator/cloud_shell_configure_agy.sh)
Lệnh này sẽ định cấu hình Antigravity CLI và khởi chạy lệnh đó trong cùng một thiết bị đầu cuối. Khi thoát Antigravity CLI, bạn sẽ quay lại dấu nhắc Cloud Shell.

Chúng ta chỉ cần thư mục codelabs/bwg-multi-agent-pitch-generator trong kho lưu trữ. Hãy yêu cầu Antigravity tải xuống thư mục đó và lưu trữ trên thiết bị trong ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/.
Giao cho Antigravity CLI nhiệm vụ sau:
Download files from under `https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/codelabs/bwg-multi-agent-pitch-generator`
to folder `~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/`.
If `devrel-demos` folder already exists, rename it with a `.backup` suffix.

Antigravity sẽ thực hiện tác vụ này. Công cụ này sẽ yêu cầu bạn cấp quyền để chạy một số lệnh. Sau khi tải mã xuống, hãy thoát Antigravity CLI bằng cách thực thi lệnh /exit:
/exit

Mở không gian làm việc của kho lưu trữ
Bây giờ, hãy mở không gian làm việc mã trong Cloud Shell. Chạy lệnh này trong cửa sổ dòng lệnh Cloud Shell:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
cloudshell workspace .
Chạy tập lệnh thiết lập
Để có trải nghiệm mượt mà hơn, hãy chạy tập lệnh thiết lập từ kho lưu trữ. Thao tác này định cấu hình các biến môi trường, cài đặt agents-cli, tạo bộ chứa Cloud Storage cho các cấu phần phần mềm, tạo kết nối Tài nguyên đám mây BigQuery và cấp các quyền IAM cần thiết cho tài khoản dịch vụ của bộ chứa này bằng tính năng tự động thử lại.
Tập lệnh thiết lập cũng gọi setenv.sh, một trình trợ giúp nhỏ giúp lấy các biến mà cả hai tác nhân đều cần (GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_REGION, GOOGLE_CLOUD_LOCATION và GOOGLE_GENAI_USE_VERTEXAI) từ PROJECT_ID và REGION mà bạn đã xuất ở trên. Thao tác này cũng lưu 2 giá trị đó vào tệp .env trong thư mục gốc để bạn có thể khôi phục sau này.
- Trong Cloud Shell, hãy thực thi tập lệnh thiết lập và kích hoạt tập lệnh đó:
chmod +x codelab-setup.sh setenv.sh ./codelab-setup.sh - Quá trình này sẽ mất khoảng một phút để chạy. Bạn sẽ thấy thông báo này cho biết đã thực hiện thành công: "🎉 🦄 Script execution complete."
(Đã hoàn tất việc thực thi tập lệnh). - Tập lệnh thiết lập chạy trong một shell riêng, vì vậy các biến của tập lệnh này không đến được thiết bị đầu cuối của bạn. Tải các tệp đó vào phiên của bạn:
Bạn sẽ thấy dự án, vùng và vị trí của mình được phản hồi.source ./setenv.sh
3. Điều phối các tác nhân bằng quy trình công việc dạng biểu đồ
Trong nhiệm vụ này, bạn sẽ tạo dự án trình tạo bản đề cử và xây dựng một quy trình công việc dạng biểu đồ chạy hai tác nhân và kết hợp đầu ra của chúng.
Một bản đề cử cần có một khái niệm, tức là ý tưởng và các thành phần bán khái niệm đó. Trong trường hợp sử dụng này, chúng ta sẽ thiết kế các tác nhân có trách nhiệm khác nhau trong phạm vi công việc, minh hoạ cách bạn có thể tách các tác vụ phức tạp và uỷ quyền cho các tác nhân khác nhau có trách nhiệm riêng biệt, giúp bạn dễ dàng quản lý các tác vụ đó.
Việc phân chia loại công việc này giữa các chuyên gia đòi hỏi phải có một cơ chế phối hợp khi mỗi tác nhân chạy. Bạn có thể chọn để tác nhân của mình quyết định thời điểm gọi từng tác nhân phụ hoặc áp dụng một phương pháp mang tính xác định hơn. Trong lớp học lập trình này, bạn sẽ sử dụng một phương pháp xác định với quy trình làm việc đồ thị.
Tạo giàn giáo cho dự án tác nhân đầu tiên
- Trong Cloud Shell, hãy tạo dự án bằng cách dán lệnh này vào thiết bị đầu cuối:
Lệnh này sẽ ghi một dự án hoàn chỉnh, có thể chạy: một tác nhân, mộtcd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator agents-cli create pitch-generator \ --agent adk \ --agent-directory pitch_generator \ --deployment-target cloud_run \ --session-type in_memory \ --cicd-runner skip \ --region <YOUR_REGION> \ --auto-approveDockerfile, các thử nghiệm, Terraform cho sau này và một tệp.envcó mẫu.env.example. Có 2 phần quan trọng trong lớp học lập trình này:pitch_generator/agent.pylà tác nhân và tệp mà bạn viết lại.pitch_generator/là tên gói, do--agent-directoryđặt.
setenv.sh, vì vậy, bạn không cần định cấu hình gì ở đây. - Cài đặt các phần phụ thuộc:
Lưu ý: Khung này cũng kết nối dự án để phân phát A2A, giao thức được dùng sau này trong lớp học lập trình này. Tạm thời không động đến những tệp đó.cd pitch-generator agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
Tìm hiểu biểu đồ
ADK phiên bản 2.x hỗ trợ quy trình làm việc bằng biểu đồ. Biểu đồ được tạo từ nút (đơn vị công việc) và cạnh (kết nối các nút). Nút thường là một tác nhân, mặc dù một hàm Python đơn giản cũng hoạt động. Một cạnh cho biết khi một nút kết thúc, nút khác sẽ chạy tiếp theo.
Các cạnh được viết dưới dạng danh sách và ADK sẽ xác định thứ tự:
edges=[
("START", creative_director), ## the graph begins here
(creative_director, copywriter), ## then this
]
Việc lồng ghép này mang lại ý nghĩa cho một cạnh:
Edge | Ý nghĩa |
| Một chuỗi. a, rồi đến b, rồi đến c, lần lượt |
| Một phân đầu ra. a, sau đó là b và c cùng một lúc |
| Một fan-in. c chờ cả a và b |
Đối với một fan-in, bạn sử dụng JoinNode, chờ mọi tiền nhiệm và truyền các đầu ra của chúng cùng nhau.
Trong nhiệm vụ này, chúng ta sẽ tạo quy trình sau:

Xây dựng quy trình
- Mở tệp tác nhân bằng cách dán nội dung này vào dòng lệnh:
Bạn sẽ thấycd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.pyagent.pyđược giàn giáo trong trình chỉnh sửa Cloud Shell.💡 Lưu ý: Bất cứ lúc nào, bạn cũng có thể chọn mã và dùng biểu tượng bóng đèn để yêu cầu Gemini giải thích mã đó. Bạn cũng có thể hỏi Antigravity CLI.
- Thay thế toàn bộ tệp trong
pitch_generator/agent.pytrong trình chỉnh sửa bằng nội dung sau:import mimetypes import os from dotenv import load_dotenv from google.adk.agents import Agent from google.adk.agents.context import Context from google.adk.agents.remote_a2a_agent import ( AGENT_CARD_WELL_KNOWN_PATH, RemoteA2aAgent, ) from google.adk.apps import App from google.adk.events.event import Event from google.adk.models import Gemini from google.adk.workflow import JoinNode, Workflow from google.genai import types load_dotenv() MODEL = "gemini-3.8-flash" def _model() -> Gemini: return Gemini( model=MODEL, client_kwargs={"location": "global"}, retry_options=types.HttpRetryOptions(attempts=3), ) """ ⬇️ Configure visual-director RemoteA2aAgent agent below this comment """ creative_director = Agent( name="creative_director", model=_model(), description="Turns a raw idea into a campaign concept.", instruction=( "You are the Creative Director. Turn the idea you are given into ONE " "punchy campaign concept line with the rationale explanation behind it." ), output_key="creative_director", ) copywriter = Agent( name="copywriter", model=Gemini(model=MODEL, retry_options=types.HttpRetryOptions(attempts=3)), description="Writes social copy for a campaign concept.", instruction=( "You are the Copywriter. Write ONE short social caption for the " "campaign concept you are given. Under 25 words." ), ) """ Waits for every node wired into it, then hands their outputs on together.""" assemble = JoinNode(name="assemble") def package(node_input: dict): """Render the joined branches as the finished pitch. """ pitch = "\n\n".join( [ f"CONCEPT\n{node_input['creative_director']}", f"COPY\n{node_input['copywriter']}", ] ) """ The first event is what the user sees. The second is the node's output, which any downstream node would receive.""" yield Event(content=types.Content(role="model", parts=[types.Part(text=pitch)])) yield Event(output=pitch) """ we put creative_director before `assemble` so that we can gather the `Concept` output at assemble""" root_agent = Workflow( name="pitch_generator", edges=[ ("START", creative_director), (creative_director, copywriter), ((creative_director, copywriter), assemble), (assemble, package), ], ) app = App( root_agent=root_agent, name="pitch_generator", ) agents-clibao gồm một giao diện người dùng phát triển web tích hợp có tên làplaygroundđể kiểm tra, tương tự như ADK. Cách bắt đầu sử dụng Playground: Bạn sẽ được hỏi xem bạn có muốn bật tính năng đo từ xa hay không. Nhậpagents-cli playground --port 8080nrồi nhấn Enter.- Nhấp vào Web Preview (Xem trước trên web), biểu tượng con mắt ở trên cùng bên phải của thiết bị đầu cuối, rồi chọn Preview on port 8080 (Xem trước trên cổng 8080).

- Bạn sẽ thấy giao diện sau đây để trò chuyện với nhân viên hỗ trợ. Hãy thử gửi câu lệnh "Ván trượt bay cho mèo". Bạn sẽ thấy một câu trả lời như bên dưới, trong đó bạn cũng có thể kiểm tra toàn bộ biểu đồ quy trình làm việc.
Kiểm tra cách câu trả lời đó được tạo ra. Sân chơi cho thấy từng bước mà biểu đồ đã thực hiện theo thứ tự: creative_director, sau đó làcopywriter, rồiassemble, sau đó làpackage. Trình tự đó là biểu đồ mà bạn đã kết nối, nhờ đó, bạn có thể nhanh chóng xác nhận biểu đồ mà bạn vừa tạo. - Quay lại thiết bị đầu cuối và dừng quy trình
agents-cli playgroundđang chạy bằng cách nhấn tổ hợp phím Ctrl+C hai lần.
4. Giao một kỹ năng cho tác nhân
Trong nhiệm vụ này, bạn sẽ tạo một tác nhân thứ hai và cung cấp cho tác nhân đó một bộ nguyên tắc sử dụng thương hiệu được đóng gói dưới dạng một kỹ năng.
Quy trình này ghi các từ. Một bức ảnh cần có phong cách riêng: bảng màu, cách chiếu sáng, bố cục bạn sử dụng và bố cục bạn không bao giờ sử dụng. Đó là một trang quy tắc và trang quy tắc cần có một cơ chế quản lý hiệu quả hơn. Một cách để thực hiện việc này là đóng gói nó dưới dạng "kỹ năng của tác nhân".
Kỹ năng là một thư mục chứa tệp SKILL.md. Cấu trúc của thư mục kỹ năng có dạng như ví dụ sau (sử dụng visual_director, tác nhân tiếp theo mà bạn sẽ tạo):
visual_director/
...
agent.py
skills/
brand-guidelines/
SKILL.md ## required: frontmatter + instructions
references/ ## optional: longer supporting documents
assets/ ## optional: templates, images, data
scripts/ ## optional: executable helpers
...
Điểm truy cập của kỹ năng tác nhân là tệp SKILL.md, bao gồm một phần đầu trang YAML như phần được minh hoạ dưới đây:
---
name: brand-guidelines
description: The house brand rules for a key visual. Load this before writing art direction, so the visual looks like it came from this brand.
---
## Brand guidelines
Deep indigo and slate as the ground. One warm accent, amber or terracotta...
frontmatter là nhãn. Đó là tất cả những gì mà tác nhân thấy ngay từ đầu, đó là lý do nội dung mô tả phải cho biết thời điểm kỹ năng đáng được tải. body là nội dung và nội dung này sẽ nằm ngoài ngữ cảnh cho đến khi tác nhân gọi load_skill.
Đó chính là điểm khác biệt. Câu lệnh vẫn ngắn gọn, tác nhân chỉ lấy các quy tắc khi cần và việc thay đổi kiểu có nghĩa là chỉnh sửa một tệp thay vì tìm kiếm trong một chuỗi hướng dẫn.
Chúng ta sẽ bắt đầu xây dựng tác nhân thứ hai, visual-director, sử dụng các kỹ năng của tác nhân như minh hoạ trong sơ đồ bên dưới:

Tạo giàn giáo cho Trình chỉ đạo hình ảnh
Bạn sẽ tạo một tác nhân chuyên biệt để xử lý thành phần trực quan, visual-director. Đây là một dự án riêng biệt vì sau này dự án này sẽ trở thành dịch vụ riêng để minh hoạ cách nhiều tác nhân giao tiếp với nhau bằng giao thức A2A. Bạn sẽ có 2 dự án song song:
bwg-multi-agent-pitch-generator/
├── pitch-generator/ ## built in the last task
│ ├── pitch_generator/
│ │ ├── agent.py ## the graph you wrote
│ │ ├── app_utils/ ## generated, A2A wiring
│ │ └── fast_api_app.py ## generated, serves the agent
│ └── pyproject.toml
└── visual-director/ ## created below
└── ... ## the same shape again
- Tạo dự án và cài đặt các phần phụ thuộc của dự án bằng cách dán vào thiết bị đầu cuối:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator agents-cli create visual-director \ --agent adk \ --agent-directory visual_director \ --deployment-target cloud_run \ --session-type in_memory \ --cicd-runner skip \ --region <YOUR_REGION> \ --auto-approve cd visual-director agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
Viết nguyên tắc sử dụng thương hiệu
- Tạo thư mục kỹ năng và mở tệp:
mkdir -p visual_director/skills/brand-guidelines cloudshell edit visual_director/skills/brand-guidelines/SKILL.md - Dán nội dung sau vào tệp
SKILL.mdđang mở trong Trình chỉnh sửa rồi lưu tệp đó:
--- name: brand-guidelines description: The house brand rules for a key visual — palette, light, composition, subject treatment, and what never appears. Load this before writing art direction or rendering an image, so the visual looks like it came from this brand rather than from a generic image model. --- ## Brand guidelines House style for campaign key visuals. Every rule here beats a preference in the brief. If the brief asks for something forbidden below, follow the brief for the subject and the guidelines for the treatment. ## Palette Deep indigo and slate as the ground. One warm accent — amber or terracotta — carrying the eye to the subject. Nothing neon, nothing fluorescent. At most three colors with any weight. A fourth reads as clutter. ## Light One dominant source, low and raking. Long shadows are wanted. Golden hour or a single hard studio key, never flat overhead light and never a ring light. Rim light is allowed on the subject only, and only to separate it from the ground. ## Composition Subject off-center, roughly a third in. Generous negative space on the opposite side — copy lands there later, so leave it genuinely empty. Eye level or slightly below. No top-down flat lays. No fisheye, no Dutch tilt. ## Subject One subject. If the idea implies a crowd, pick the one that carries it. Photographic realism with a shallow depth of field. Not illustration, not 3D render, not clay, not pixel art. ## Never - Text, logos, watermarks, or signage anywhere in the frame - Collage or split-screen layouts - Chrome, lens flare, bokeh sparkle - Cool-toned neon cyan or magenta ## Rendering the prompt When you turn art direction into an image prompt, state the palette, the light, and the composition explicitly. An image model will not infer them from the subject alone. Put the "Never" items in as plain description of what the frame does contain, not as negatives — image models handle a positive description far better than a list of prohibitions.
Cập nhật visual_director/agent.py
Cập nhật tệp agent.py và định cấu hình tệp này để có kỹ năng đã xác định trước đó.
- Mở tệp tác nhân:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - Thay thế toàn bộ nội dung tệp
visual_director/agent.pybằng đoạn mã sau: Giờ đây, tác nhânimport mimetypes from pathlib import Path from google import genai from google.adk.agents import Agent from google.adk.apps import App from google.adk.models import Gemini from google.adk.skills import load_skill_from_dir from google.adk.tools import ToolContext from google.adk.tools.skill_toolset import SkillToolset from google.genai import types MODEL = "gemini-3.8-flash" ## Configure the `brand-guidelines` skill for the agent BRAND_SKILL = load_skill_from_dir(Path(__file__).parent / "skills" / "brand-guidelines") ## Add definition of `generate_key_visual` tool below this comment root_agent = Agent( name="visual_director", model=Gemini( model=MODEL, client_kwargs={"location": "global"}, retry_options=types.HttpRetryOptions(attempts=3), ), description="Turns a campaign concept into art direction and a key visual.", instruction="""You are the Visual Director on a campaign pitch team. Call `load_skill` for `brand-guidelines` before you write anything. The house style is not optional and it is not in this prompt. You are given a campaign concept. Then, in order: 1. Write art direction for ONE key visual that sells it, obeying the brand guidelines: subject, composition, lighting, color, mood. Three or four sentences. 2. Call `generate_key_visual` with exactly that art direction. Your final reply is the art direction itself and nothing else. No preamble, no alternatives, no questions back. Do not mention the skill, the tool, or the image file — the image travels on its own.""", tools=[ SkillToolset(skills=[BRAND_SKILL]), ## Supply the `generate_key_visual` tool below this comment ], ) app = App( root_agent=root_agent, name="visual_director", )visual-directorđã được định cấu hình đúng cách bằng một kỹ năng của tác nhân. Trước khi kiểm thử, bạn sẽ trang bị cho tác nhân này một công cụ để tạo thành phần hình ảnh như một trong những chức năng chính của tác nhân.
5. Trang bị cho tác nhân một công cụ tạo hình ảnh
Trong nhiệm vụ này, bạn sẽ cung cấp cho Giám đốc hình ảnh một công cụ kết xuất hướng nghệ thuật dưới dạng hình ảnh và lưu hình ảnh đó dưới dạng một cấu phần phần mềm. Hiện vật là một phần dữ liệu nhị phân hoặc nội dung tệp được đặt tên và có phiên bản mà một tác nhân AI tạo, sửa đổi hoặc sử dụng trong một nhiệm vụ.

Triển khai và thêm công cụ vào tác nhân
- Mở tệp tác nhân:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - Sao chép công cụ
generate_key_visualsau đây trước phần định nghĩaroot_agent:async def generate_key_visual(art_direction: str, tool_context: ToolContext) -> dict: """Render the art direction as the campaign's key visual. Args: art_direction: The art direction to render, in full. Returns: The saved artifact's filename, version, size and media type. """ IMAGE_MODEL = "gemini-3.1-flash-lite-image" response = await genai.Client(location="global").aio.models.generate_content( model=IMAGE_MODEL, contents=art_direction ) ## The image arrives as inline bytes on one of the response parts. image = next( ( part.inline_data for part in response.candidates[0].content.parts or [] if part.inline_data ), None, ) if image is None: raise ValueError(f"{IMAGE_MODEL} returned no image for: {art_direction[:120]}") filename = "key_visual" + ( mimetypes.guess_extension(image.mime_type or "") or ".bin" ) version = await tool_context.save_artifact(filename, types.Part(inline_data=image)) ## Return the artifact filename and version return { "filename": filename, "version": version, "bytes": len(image.data), "mime_type": image.mime_type, } - Thêm
generate_key_visualbên dướiSkillToolsetđể trang bị công cụ cho tác nhân:tools=[ SkillToolset(skills=[BRAND_SKILL]), ## Supply the `generate_key_visual` tool below this comment generate_key_visual, ], agents-clibao gồm một giao diện người dùng phát triển web tích hợp có tên làplaygroundđể kiểm tra, tương tự như ADK. Cách bắt đầu sử dụng Playground:agents-cli playground --port 8080- Nhấp vào Web Preview (Xem trước trên web), biểu tượng con mắt ở trên cùng bên phải của thiết bị đầu cuối, rồi chọn Preview on port 8080 (Xem trước trên cổng 8080).

- Bạn sẽ thấy giao diện sau đây để trò chuyện với nhân viên hỗ trợ. Hãy thử gửi câu lệnh "Chín mạng. Không trọng lực".
Bạn có thể kiểm tra từ các lượt tương tác cách tác nhân visual_directorthực thiload_skill("brand-guidelines")để đọc kỹ năng nguyên tắc trình bày thương hiệu, sau đó thực thigenerate_key_visualđể bắt đầu tạo hình ảnh. Hình ảnh thu được sẽ được lưu trữ dưới dạng một cấu phần phần mềm, đồng thời xuất hiện trong giao diện người dùng và bạn có thể dễ dàng kiểm tra. - Quay lại thiết bị đầu cuối và dừng quy trình
agents-cli playgroundđang chạy bằng cách nhấn tổ hợp phím Ctrl+C hai lần.
6. Kiến trúc nhiều tác nhân có giao thức A2A
Trong nhiệm vụ này, bạn sẽ cung cấp Visual Director dưới dạng một dịch vụ mà các tác nhân khác có thể gọi và định cấu hình dịch vụ này để phản hồi bằng hình ảnh đã tạo.
Agent2Agent (A2A) là một giao thức mở để một tác nhân gọi một tác nhân khác qua mạng. Phần trung tâm là thẻ tác nhân, một tài liệu JSON mà tác nhân xuất bản về chính nó thông qua một URL nổi tiếng, cung cấp tên, nội dung mô tả và các chức năng của tác nhân. Người gọi tìm nạp thẻ và biết cách trao đổi với tác nhân đằng sau thẻ đó mà không cần mã nguồn, các phần phụ thuộc hoặc ngôn ngữ dùng chung.

agents-cli đã kết nối sẵn tính năng này theo mặc định. Khung được tạo visual_director/app_utils/a2a.py, tạo thẻ từ tác nhân của bạn và gắn các tuyến đường, và visual_director/fast_api_app.py, cung cấp các tuyến đường đó. Bạn chỉ cần sửa đổi một chút để cho phép nó gửi các cấu phần phần mềm trở lại máy khách.
Cấu trúc sẽ có dạng như sau. Xin lưu ý rằng tác nhân này chưa được tích hợp vào quy trình công việc ban đầu của chúng tôi:

Khung agents-cli máy chủ A2A
Mã và mô-đun sau đây đã được cung cấp khi chúng ta khởi chạy tác nhân visual-director bằng agents-cli. Mở các tệp này bằng cách dán các lệnh sau vào dòng lệnh:
cloudshell edit visual_director/fast_api_app.py
cloudshell edit visual_director/app_utils/a2a.py
visual_director/fast_api_app.py sẽ khởi động tác nhân và gắn các tuyến A2A trong một đường dẫn được tạo từ tên ứng dụng:
await attach_a2a_routes(
app,
agent=root_agent,
runner=runner,
rpc_path=f"/a2a/{adk_app.name}", ## becomes /a2a/visual_director
)
visual_director/app_utils/a2a.py sẽ làm những việc còn lại trong 3 nước đi.
Trước tiên, nó tạo thẻ tác nhân từ chính tác nhân đó. Tên, nội dung mô tả và mọi công cụ đều trở thành các mục mà người gọi có thể đọc:
agent_card = await AgentCardBuilder(
agent=agent,
rpc_url=f"{resolved_app_url}{rpc_path}",
).build()
Thứ hai, nó đặt một trình thực thi giữa giao thức và nhân viên hỗ trợ của bạn. Các yêu cầu A2A đến được chuyển thành các lần chạy ADK và kết quả được chuyển đổi ngược lại:
request_handler = DefaultRequestHandler(
agent_executor=A2aAgentExecutor(runner=runner, force_new_version=True),
task_store=task_store,
agent_card=agent_card,
)
Thứ ba, nó gắn 2 tuyến đường: một tuyến đường phục vụ thẻ, một tuyến đường chấp nhận các cuộc gọi.
Tuyến đường | Mục đích |
| Thẻ. Người gọi sẽ đọc thông tin này trước tiên để biết những việc mà tác nhân có thể làm |
| Điểm cuối JSON-RPC. Nơi các yêu cầu thực tế được gửi đến |
Cho phép cấu phần phần mềm vượt qua ranh giới
Theo mặc định, phản hồi A2A sẽ có văn bản. Hình ảnh là một cấu phần phần mềm và cấu phần phần mềm sẽ nằm trong kho lưu trữ của tác nhân phân phát, trừ phi bạn định cấu hình để đưa cấu phần phần mềm đó vào.
- Mở tệp A2A đã tạo:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director cloudshell edit visual_director/app_utils/a2a.py - Tìm mục nhập
AgentCardBuilderrồi thêm các mục nhậpA2aAgentExecutorConfigvàinclude_artifacts_in_a2a_event_interceptorbên dưới:## ... ## from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder ## Add the following two below `AgentCardBuilder` import from google.adk.a2a.executor.config import A2aAgentExecutorConfig from google.adk.a2a.executor.interceptors.include_artifacts_in_a2a_event import ( include_artifacts_in_a2a_event_interceptor, ) - Tìm dòng
agent_executor=A2aAgentExecutor(runner=runner),rồi thay thế bằng dòng sau (thêmA2aAgentExecutorConfig):agent_executor=A2aAgentExecutor( runner=runner, force_new_version=True, config=A2aAgentExecutorConfig( execute_interceptors=[include_artifacts_in_a2a_event_interceptor] ), ),
Khởi động dịch vụ và đọc thẻ A2A đã xuất bản
- Khởi động máy chủ ở chế độ nền. Bạn cần khai báo
APP_URL, vì đây là địa chỉ mà thẻ quảng cáo là của riêng mình. Dán nội dung này vào cửa sổ dòng lệnh:APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 & - Chờ vài giây để chiến dịch bắt đầu phân phát, sau đó xác nhận rằng chiến dịch đang phân phát:
Bạn sẽ thấy:sleep 10 tail -2 /tmp/visual-director.logINFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8801 (Press CTRL+C to quit) - Đọc thẻ mà nhân viên hỗ trợ hiện xuất bản:
Thẻ này liệt kê tên, nội dung mô tả, URL và các kỹ năng của trợ lý, bao gồm cảcurl -s http://localhost:8801/a2a/visual_director/.well-known/agent-card.json | python3 -m json.toolgenerate_key_visual. Nội dung này được tạo trực tiếp từ định nghĩa tác nhân trong mã. Bạn đã sẵn sàng nhúng tác nhân{ "name": "visual_director", "description": "Turns a campaign concept into art direction and a key visual.", "skills": [ { "id": "visual_director", "name": "model", "tags": [ "llm" ] }, { "id": "visual_director-generate_key_visual", "name": "generate_key_visual", "description": "Render the art direction as the campaign's key visual.\n\nArgs:\n art_direction: The art direction to render, in full.\n\nReturns:\n The saved artifact's filename, version, size and media type.", "tags": [ "llm", "tools" ] } ], "preferredTransport": "JSONRPC", "protocolVersion": "0.3", "url": "http://localhost:8801/a2a/visual_director" }visual-directorvào quy trìnhpitch-generatorchính.
7. Nhúng tác nhân từ xa A2A vào quy trình công việc
Trong nhiệm vụ này, bạn sẽ thêm tác nhân từ xa vào biểu đồ dưới dạng nhánh thứ ba.
Tác nhân từ xa được bao bọc trong RemoteA2aAgent và được đặt trong biểu đồ như mọi nút khác. Các cạnh vẫn giữ nguyên hình dạng và trình điều phối không biết nút nào là nút cục bộ.
Hướng dẫn điều phối viên về dịch vụ và cập nhật quy trình làm việc trên biểu đồ
- Mở tệp tác nhân:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.py - Thêm các nội dung nhập này vào đầu tệp, cùng với các nội dung nhập hiện có:
import asyncio import subprocess import google.auth.transport.requests import google.oauth2.id_token import httpx from google.adk.a2a.converters.part_converter import convert_genai_part_to_a2a_part - Thêm định nghĩa
RemoteA2aAgentsau đây phía trên phần khai báocreative_director:VISUAL_DIRECTOR_URL = os.getenv("VISUAL_DIRECTOR_URL", "http://localhost:8801") ## For deployment on Cloud Run def _cloud_run_client() -> httpx.AsyncClient | None: """An httpx client that signs each request with a Cloud Run ID token. Cloud Run deploys private, so both the agent-card fetch and the A2A calls need one. """ if not VISUAL_DIRECTOR_URL.startswith("https://"): return None async def sign(request: httpx.Request) -> None: token = None try: token = await asyncio.to_thread( google.oauth2.id_token.fetch_id_token, google.auth.transport.requests.Request(), VISUAL_DIRECTOR_URL, ) except Exception: token = subprocess.check_output( ["gcloud", "auth", "print-identity-token", "-q"], stderr=subprocess.PIPE ).decode().strip() if not token: raise Exception("Failed to fetch ID token") request.headers["Authorization"] = f"Bearer {token}" return httpx.AsyncClient(event_hooks={"request": [sign]}, timeout=600) ## Keep the approval's tool traffic out of the A2A request def _pitch_parts_only(part: types.Part): """Sends the Visual Director text and media only. RemoteA2aAgent replays the session on every call, and the Visual Director runs downstream of the human approval, so the approval's function_response would travel alongside the concept text. An ADK server reads a function response as "resume the paused invocation" and rejects a message that also carries text, so drop the tool parts here. """ if part.function_call or part.function_response: return None return convert_genai_part_to_a2a_part(part) visual_director = RemoteA2aAgent( name="visual_director", description="Turns a campaign concept into art direction for one key visual.", agent_card=( f"{VISUAL_DIRECTOR_URL}/a2a/visual_director{AGENT_CARD_WELL_KNOWN_PATH}" ), httpx_client=_cloud_run_client(), genai_part_converter=_pitch_parts_only, ) - Ghi đè hoặc thay thế hàm Python của nút
packagebằng định nghĩa sau:async def package(ctx: Context, node_input: dict): """Render the joined branches as the finished pitch.""" empty = [name for name, value in node_input.items() if not value] if empty: raise ValueError(f"nothing reached the join from: {', '.join(empty)}") def _key_visual(ctx: Context) -> types.Blob | None: """The image the Visual Director sent back, or None if it sent none.""" for event in reversed(ctx.session.events): if event.author != "visual_director" or not event.content: continue for part in event.content.parts or []: if part.inline_data and part.inline_data.data: return part.inline_data return None image = _key_visual(ctx) if image is None: raise ValueError("the Visual Director returned art direction but no image") suffix = mimetypes.guess_extension(image.mime_type or "") or ".bin" filename = f"key_visual{suffix}" await ctx.save_artifact(filename, types.Part(inline_data=image)) pitch = "\n\n".join( [ f"CONCEPT\n{node_input['creative_director']}", f"COPY\n{node_input['copywriter']}", f"ART DIRECTION\n{node_input['visual_director']}", f"KEY VISUAL\n{filename}, {len(image.data)} bytes, {image.mime_type}", ] ) yield Event( content=types.Content( role="model", parts=[types.Part(text=pitch), types.Part(inline_data=image)], ) ) yield Event(output=pitch) - Thêm Cổng phê duyệt của con người trước khi quá trình tạo nội dung tiếp theo diễn ra. Trong
pitch_generator/agent.py, hãy xác định các nút phê duyệt ở trênroot_agent:@node(rerun_on_resume=False) async def approve_concept(ctx: Context): yield RequestInput( message="Please approve the campaign concept (yes/no).", response_schema=str ) @node(rerun_on_resume=True) async def user_approval(ctx: Context): user_response = await ctx.run_node(approve_concept) if str(user_response).lower() in ("yes", "y"): approved_concept = f"## Approved Concept\n\n{ctx.session.state['creative_director']}" yield Event( content=types.Content( role="model", parts=[types.Part(text=approved_concept)] ) ) else: raise ValueError("User rejected the concept") - Cuối cùng, hãy cập nhật các nội dung nhập ở trên cùng để thêm
RequestInputvànodetừgoogle.adk.eventsvàgoogle.adk.workflow: và sửa đổi quy trìnhfrom google.adk.events import Event, RequestInput from google.adk.workflow import JoinNode, Workflow, noderoot_agentedgesđể định tuyến quauser_approval: Một số thông tin chi tiết đáng chú ý:edges=[ ("START", creative_director), (creative_director, user_approval, (copywriter, visual_director)), ((creative_director, copywriter, visual_director), assemble), (assemble, package), ],- Phê duyệt có sự tham gia của con người (HITL):
approve_conceptsẽ tạo raRequestInput. Khi quy trình công việc chạm đến nút này, quá trình thực thi sẽ tạm dừng và phát ra một tín hiệu ngắt cho người gọi. Các nhánh hạ lưu (copywritervàvisual_director) sẽ không chạy cho đến khi người dùng xác nhận phê duyệt. visual_directorxuất hiện ở các cạnh giống hệt nhưcopywriter, mặc dù một cạnh là lệnh gọi hàm và cạnh kia nằm trên mạng.- Hình ảnh được trả về sẽ được đọc từ các sự kiện của phiên thay vì từ
node_input, vì hiện tại chỉ có văn bản được chuyển thành đầu ra của một nút. Hình ảnh đã vượt qua ranh giới thông qua trình chặn A2A, nhưng được đính kèm vào các sự kiện của tác nhân từ xa, vì vậy hàm trợ giúp_key_visualsẽ truy xuất hình ảnh từ đó.
- Phê duyệt có sự tham gia của con người (HITL):
Kiểm thử quá trình tích hợp
Bây giờ, bạn sẽ kiểm thử để đảm bảo Visual Director (chạy trong nền) và Pitch Generator có thể giao tiếp thông qua giao thức A2A.
- Xác nhận rằng Visual Director vẫn đang chạy ở chế độ nền:
Bạn sẽ nhận được kết quảcurl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.jsonvisual director: 200tại đây.Trong trường hợp bạn nhận được trạng thái khác, hãy chạy lệnh sau để khởi động lại:fuser -k 8801/tcp 2>/dev/null || true cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 & cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator sleep 10 curl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.json - Bắt đầu xem trước:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator/pitch_generator agents-cli playground --port 8080 - Nhấp vào Web Preview (Xem trước trên web), biểu tượng con mắt ở trên cùng bên phải của thiết bị đầu cuối, rồi chọn Preview on port 8080 (Xem trước trên cổng 8080).

- Bạn sẽ thấy giao diện để trò chuyện với tác nhân. Hãy thử gửi câu lệnh "Ván trượt bay cho mèo".

Bạn sẽ thấy toàn bộ hoạt động tương tác, trong đó quy trình công việc bắt đầu và mỗi tác nhân chạy. Sau khi creative_directorphác thảo ý tưởng, quy trình làm việc sẽ tạm dừng ở bước phê duyệt và yêu cầu bạn xác nhận: "Vui lòng phê duyệt ý tưởng về chiến dịch (có/không)." Nhập "yes" vào trường nhập dữ liệu để tiếp tục. Sau khi được phê duyệt, biểu đồ sẽ mở rộng: bạn có thể thấy thời điểm tác nhânvisual-directorbắt đầu tải kỹ năng và ở giữa quá trình tạo hình ảnh, tác nhâncopywriterhoàn tất việc tạo chú thích trên mạng xã hội. Điều này cho thấy cả hai tác nhân đều chạy song song mà không cần chờ đợi lẫn nhau. Sau khi cả hai hoàn tất, nútassemblesẽ kết hợp các kết quả với khái niệm đã được phê duyệt docreative_directortạo ra, hoàn tất việc phân phối nội dung đề cử trong hàmpackage.
8. Lưu trữ hình ảnh chính trong Cloud Storage
Cho đến nay, mọi hình ảnh chính đều nằm trong bộ nhớ. Khi quá trình này dừng lại, hình ảnh sẽ biến mất. Điều này phù hợp với một sân chơi và vô ích cho một chiến dịch. Trong nhiệm vụ này, bạn sẽ cung cấp cho quy trình một bộ chứa để ghi vào và tạo một tập dữ liệu BigQuery mà bạn sẽ sử dụng trong nhiệm vụ cuối cùng.
ADK quyết định vị trí của các cấu phần phần mềm khi khởi động. app_utils/services.py được tạo sẽ đọc một biến môi trường duy nhất:
def get_artifact_service():
"""Process-wide artifact service: GCS when a bucket is set, else in-memory."""
if bucket := os.environ.get("LOGS_BUCKET_NAME"):
return GcsArtifactService(bucket_name=bucket)
return InMemoryArtifactService()
Nếu bạn đặt LOGS_BUCKET_NAME, mọi lệnh gọi save_artifact sẽ được chuyển vào Cloud Storage.

Tạo tập dữ liệu BigQuery
- Trong Cloud Shell, hãy chạy lệnh sau để tạo một tập dữ liệu BigQuery:
bq mk --dataset --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> bwg - Trỏ kho lưu trữ cấu phần phần mềm của trình điều phối vào nhóm:
export LOGS_BUCKET_NAME="<YOUR_PROJECT_ID>-bwg"GcsArtifactServicelấy tên vùng chứa trần, không có tiền tốgs://. Giống như các biến khác, biến này nằm trong shell của bạn và tác vụ triển khai sẽ truyền biến này đến Cloud Run. - Kiểm tra nội dung trong vùng lưu trữ. Trong một thẻ trình duyệt mới, hãy mở URL sau:
Nhấp vào vùng chứa. Tuỳ thuộc vào các thử nghiệm trước đây của bạn, phần này có thể chứa những hình ảnh đã được tạo trước đó.https://console.cloud.google.com/storage/browser?project=<YOUR_PROJECT_ID>
9. Triển khai tác nhân Visual Director lên Cloud Run
Cho đến nay, mọi thứ đều chạy trên một máy, với Giám đốc hình ảnh có thể liên hệ tại localhost:8801. Trong nhiệm vụ này, bạn sẽ triển khai tác nhân Visual Director dưới dạng dịch vụ Cloud Run riêng và cho phép trình tạo bản đề cử giao tiếp với tác nhân này trên mạng bằng giao thức Agent2Agent (A2A).
Visual Director nhận được một URL https:// thực, vì vậy _cloud_run_client mà bạn đã viết trước đó sẽ bắt đầu ký các yêu cầu thay vì trả về None. Ngoài ra, Cloud Run triển khai ở chế độ riêng tư theo mặc định.

Triển khai Visual Director
- Dừng dịch vụ nền cục bộ và hộp cát nếu chúng vẫn đang chạy:
fuser -k 8801/tcp 2>/dev/null || true fuser -k 8000/tcp 2>/dev/null || true - Triển khai tác nhân. Thao tác này sẽ tạo một vùng chứa và đẩy vùng chứa đó.⏰ Quá trình này sẽ mất vài phút.
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director source .env agents-cli deploy \ --service-name visual-director \ --project <YOUR_PROJECT_ID> \ --region <YOUR_REGION> \ --no-confirm-project \ --labels dev-tutorial=bwg001-devlab-1 \ --update-env-vars "LOGS_BUCKET_NAME=$LOGS_BUCKET_NAME,GOOGLE_GENAI_USE_VERTEXAI=true,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_CLOUD_REGION=$GOOGLE_CLOUD_REGION"
Chạy Trình tạo bản đề cử sử dụng Đạo diễn hình ảnh được triển khai trên Cloud Run
- Đọc URL của Visual Director và chạy Pitch Generator sử dụng URL đó:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator source .env export VISUAL_DIRECTOR_URL=$(gcloud run services list \ --project <YOUR_PROJECT_ID> \ --region <YOUR_REGION> \ --flatten='spec.template.spec.containers[0].env' \ --filter='metadata.name=visual-director AND spec.template.spec.containers[0].env.name=APP_URL' \ --format='value(spec.template.spec.containers[0].env.value)') echo "Visual Director is at $VISUAL_DIRECTOR_URL" APP_URL=http://localhost:8000 nohup uv run uvicorn pitch_generator.fast_api_app:app --port 8000 > /tmp/visual-pitch_generator.log 2>&1 &
Gọi quy trình công việc đã triển khai
Cả hai tác nhân đều hỗ trợ giao thức A2A. Kho lưu trữ này cung cấp một ứng dụng nhỏ để gọi Trình tạo bản đề cử thông qua A2A.
Bạn cần đặt PITCH_GENERATOR_URL thành URL của Pitch Generator đang chạy cục bộ. Trong khi đó, Visual Director chạy trong Cloud Run và Pitch Generator giao tiếp với Visual Director qua A2A.
- Sao chép ứng dụng vào dự án của bạn rồi kích hoạt ứng dụng đó:
Vì quy trình này có Cổng phê duyệt của con người, nên tập lệnh sẽ tạm dừng sau khi tạo khái niệm nháp:cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cp ../solution/pitch-generator/call_agent.py . PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "Flying skateboards for cats" --save-image ./key_visual.png Nhập=== HUMAN APPROVAL REQUIRED === [DRAFT CONCEPT] Concept Line: "Gravity is a Suggestion." ... Please approve the campaign concept (yes/no):yesrồi nhấn Enter để phê duyệt và tiếp tục quy trình. (Bạn cũng có thể truyền-yhoặc--auto-approveđể tự động phê duyệt). - Mở
call_agent.pyvà kiểm tra cách hoạt động này tương tác với tác nhân bằng cách sử dụnga2a-sdk:ClientFactoryxây dựng một A2A chính thứcClientnhắm đếnhttps://SERVICE_URL/a2a/pitch_generator.generate_pitch_asyncgửi mộtSendMessageRequestban đầu. Khi phản hồi trả về vớiTaskState.TASK_STATE_INPUT_REQUIRED, phản hồi sẽ phát hiện phầnadk_request_input, hiển thị khái niệm nháp, thu thập sự phê duyệt của bạn và gửi yêu cầu tiếp tục kèm theo phản hồi của bạn trở lại nhiệm vụ.- Vòng lặp phân tích cú pháp sẽ đọc các phần
CONCEPT,COPYvàART DIRECTIONtừ các cấu phần phần mềm của tác vụ đã hoàn tất, đồng thời lưu hình ảnh nhị phân vàokey_visual.png.
key_visual.png. - Tải hình ảnh chính lên bộ chứa của bạn, trong một tiền tố mà bạn có thể trỏ BigQuery đến:
gcloud storage cp ./key_visual.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png - Tạo một bản đề cử thứ hai để bạn có thể so sánh và tải bản đề cử đó lên:
PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "A commuter bike built for rainy cities" --save-image ./bike.png gcloud storage cp ./bike.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png
10. Chấm điểm hình ảnh chính để phù hợp với thương hiệu trong BigQuery
Giờ đây, bạn đã có hình ảnh trong một nhóm và các nguyên tắc sử dụng thương hiệu được cho là sẽ định hình những hình ảnh đó. Trong nhiệm vụ này, bạn sẽ kết hợp cả hai: tham chiếu hình ảnh từ BigQuery, sau đó yêu cầu một mô hình Gemini chấm điểm từng hình ảnh dựa trên các nguyên tắc mà không di chuyển một byte nào ra khỏi Cloud Storage.
Hai tính năng của BigQuery sẽ thực hiện việc này:
ObjectReflà một giá trị trỏ đến một đối tượng Cloud Storage.OBJ.MAKE_REFtạo một đối tượng từ URI và một kết nối cấp cho BigQuery quyền truy cập uỷ quyền vào nhóm.pitch-connectiondo tập lệnh thiết lập tạo ra là kết nối đó và đã có Storage Object Viewer và Agent Platform User.AI.SCOREnhận một câu lệnh và trả về mộtFLOAT64. Câu lệnh có thể kết hợp các chuỗi với giá trịObjectRef, vì vậy mô hình sẽ thấy cả tiêu chí chấm điểm và hình ảnh.

Tạo bảng hình ảnh chính
- Mở BigQuery Studio trong một thẻ trình duyệt mới:
https://console.cloud.google.com/bigquery?project=<YOUR_PROJECT_ID> - Nhấp vào Tạo truy vấn mới (biểu tượng +), sau đó dán và Chạy truy vấn sau (thay thế
vàbằng mã dự án và khu vực của bạn). Thao tác này sẽ tạo một hàng cho mỗi hình ảnh chính, kết hợp ý tưởng của chiến dịch với thông tin tham khảo về hình ảnh:SET @@location = '<YOUR_REGION>'; CREATE OR REPLACE TABLE bwg.key_visuals AS SELECT campaign, concept, OBJ.FETCH_METADATA( OBJ.MAKE_REF(uri, '<YOUR_REGION>.pitch-connection') ) AS key_visual FROM UNNEST([ STRUCT( 'cats' AS campaign, 'Flying skateboards for cats' AS concept, 'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png' AS uri ), STRUCT( 'bike', 'A commuter bike built for rainy cities', 'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png' ) ]);
OBJ.MAKE_REFtạo mối tham chiếu từ URI và kết nối.OBJ.FETCH_METADATAsau đó liên hệ với Bộ nhớ đám mây và điền kích thước, loại nội dung và thế hệ của đối tượng. Lệnh gọi thứ hai đó cũng là cách nhanh nhất để chứng minh rằng kết nối của bạn hoạt động: nếu quyền không chính xác, trườngdetailssẽ trả về lỗi thay vì siêu dữ liệu. - Kiểm tra những nội dung bạn đã lưu trữ. Thay thế truy vấn trước đó bằng truy vấn này rồi chạy truy vấn:
Bảng này chứa con trỏ và siêu dữ liệu của con trỏ, chứ không chứa hình ảnh. Các byte chưa bao giờ rời khỏi nhóm.SELECT campaign, concept, key_visual.uri, key_visual.details FROM bwg.key_visuals;
Đánh giá từng hình ảnh dựa trên nguyên tắc sử dụng thương hiệu
Đạo diễn hình ảnh được yêu cầu tuân thủ bảng màu, quy tắc ánh sáng và quy tắc bố cục. Việc có thực sự làm như vậy hay không là một quyết định mang tính phán đoán và đó là mục đích của AI.SCORE.
- Chạy truy vấn sau (thay
bằng khu vực của bạn). Câu lệnh là một bộ ba: tiêu chí chấm điểm, ý tưởng chiến dịch vàObjectRefcho hình ảnh:SELECT campaign, concept, AI.SCORE( ('''You are the brand guardian reviewing a campaign key visual. Score the attached image from 1 to 10 on how closely it follows these house rules: - Palette: deep indigo and slate, with one warm accent of amber or terracotta. Nothing neon. - Light: one dominant low, raking source with long shadows. No flat overhead light. - Composition: subject off-center with generous empty negative space on the opposite side. - Subject: one subject only, photographic realism, shallow depth of field. - Never: text, logos, or watermarks anywhere in the frame. A 10 obeys every rule. A 1 ignores them. The campaign concept is: ''', concept, key_visual), connection_id => '<YOUR_REGION>.pitch-connection', endpoint => 'gemini-3.5-flash' ) AS brand_fit FROM bwg.key_visuals ORDER BY brand_fit DESC; - Chuyển điểm số thành kết quả mà nhóm của bạn có thể hành động:
CREATE OR REPLACE TABLE bwg.brand_review AS SELECT campaign, concept, brand_fit, IF(brand_fit >= 7, 'on brand', 'needs another pass') AS verdict FROM ( SELECT campaign, concept, AI.SCORE( ('''Score this campaign key visual from 1 to 10 on how closely it follows the house brand rules: deep indigo and slate palette with a single warm accent and nothing neon; one low raking light source with long shadows; the subject off-center with empty negative space opposite; a single photographic subject; and no text, logos, or watermarks in the frame. The campaign concept is: ''', concept, key_visual), connection_id => '<YOUR_REGION>.pitch-connection', endpoint => 'gemini-3.5-flash' ) AS brand_fit FROM bwg.key_visuals ); SELECT * FROM bwg.brand_review ORDER BY brand_fit DESC;AI.SCOREchỉ trả vềFLOAT64, vì vậy ngưỡng nằm trong SQL thuần tuý, nơi bạn có thể điều chỉnh ngưỡng mà không cần chỉnh sửa câu lệnh. Chấm điểm theo từng hàng, tức là cùng một tiêu chí chấm điểm được áp dụng cho mọi thành phần mà một chiến dịch tạo ra, chứ không chỉ hai thành phần mà bạn đã tạo ở đây.
11. Các bước tiếp theo
Tạo bản đánh giá
Đánh giá dựa trên tác nhân hay còn gọi là "đánh giá" là các kiểm thử tự động được dùng để đo lường mức độ hiệu quả của các tác nhân AI tự động trong việc suy luận, lập kế hoạch, sử dụng công cụ và hoàn thành quy trình công việc nhiều bước. Các kiểm thử này tương tự như kiểm thử đơn vị cho các tác nhân, nhưng có tính đến bản chất không xác định của chúng.
Một trong các tác nhân của chúng tôi có chứa Human in the loop, tức là tác nhân đó sẽ chờ dữ liệu đầu vào. Bạn sẽ cần cho phép Antigravity sửa đổi mã hiện tại để hành vi này không cản trở các quy trình đánh giá tự động.
Một trong những cách tốt nhất để bắt đầu là hỏi Antigravity CLI.
Nhập nội dung sau vào Cloud Shell để khởi động CLI:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
agy
Sau đây là một câu lệnh bắt đầu:
Explore this repository (`pitch-generator` and `visual-director`), including their agent instructions, tools, and `SKILL.md` files. Design and implement a creative `agents-cli eval` evaluation suite under `tests/eval/` that stress-tests the agent's behavior.
Bạn cũng có thể tìm thấy ví dụ về các quy trình đánh giá và hướng dẫn chạy các quy trình này trong kho lưu trữ được sao chép.
12. Dọn dẹp
Để tránh bị tính phí cho tài khoản Google Cloud của bạn đối với các tài nguyên được dùng trong lớp học lập trình này, hãy xoá dự án (nên dùng) hoặc xoá từng tài nguyên được tạo trong lớp học lập trình:
gcloud run services delete visual-director --region=<YOUR_REGION> --project=<YOUR_PROJECT_ID> --quiet
bq rm -r -f -d <YOUR_PROJECT_ID>:bwg
bq rm --connection --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> pitch-connection
gcloud storage rm -r gs://<YOUR_PROJECT_ID>-bwg
13. Xin chúc mừng!
Trong lớp học lập trình này, bạn đã xây dựng một trình tạo bản đề xuất chiến dịch đa tác nhân bằng Bộ công cụ phát triển tác nhân. Bạn đã kết nối 3 tác nhân vào một quy trình công việc đồ thị xác định, đóng gói nguyên tắc sử dụng thương hiệu dưới dạng một kỹ năng của tác nhân tải theo yêu cầu và cung cấp cho tác nhân một công cụ tạo hình ảnh lưu kết quả đầu ra dưới dạng một cấu phần phần mềm. Sau đó, bạn chia Visual Director thành dịch vụ A2A riêng, nhúng lại vào biểu đồ dưới dạng một nút từ xa, triển khai nút đó vào Cloud Run và gọi toàn bộ quy trình công việc qua A2A từ một trình điều phối đang chạy cục bộ. Cuối cùng, bạn đã tham chiếu các hình ảnh chính được tạo từ BigQuery bằng ObjectRef và sử dụng AI.SCORE để đánh giá các hình ảnh đó dựa trên nguyên tắc sử dụng thương hiệu của riêng bạn.