1. 1. 개요
이 Codelab에서는 포뮬러 E 스트리트 서킷을 설계하고 간소화된 FIA 규칙에 따라 승인을 받는 멀티 에이전트 시스템을 빌드합니다.
agents-cli를 사용하여 두 개의 표준 에이전트 개발 키트 (ADK) 프로젝트를 생성한 다음 에이전트 기술, 세 개의 에이전트, 평가와 같은 중요한 부분을 작성합니다. 그 외의 모든 항목 (프로젝트 레이아웃, 서버, 종속 항목, 배포 구성)은 CLI에서 가져옵니다.
포뮬러 E가 처음이신가요?
모터스포츠를 알 필요는 없습니다. 다음 5가지 용어만 알면 됩니다.
- 포뮬러 E: FIA의 순수 전기 레이싱 챔피언십입니다. 대부분의 경주는 도심의 임시 도로 서킷에서 열립니다.
- 승인: 서킷이 레이싱에 안전하다는 FIA의 공식 승인입니다. 승인 없이는 레이스도 없습니다.
- 회생제동: 자동차가 제동할 때 모터가 배터리를 충전합니다. 브레이크를 더 세게 밟으면 더 많은 에너지가 회수되므로 이 Codelab의 규칙에서는 최소 하나의 급제동 구간을 요구합니다.
- CAD 레이어: 레이싱 라인, 피트 레인, 장벽과 같은 서킷 설계 도면의 이름이 지정된 레이어입니다. FIA는 필수 항목이 있는지 확인합니다.
- 어택 모드: 드라이버가 레이싱 라인 외부에 있는 활성화 구역을 통과하면 일시적으로 출력이 증가합니다.
에이전트가 양쪽 역할을 모두 수행합니다. 한 에이전트는 서킷을 설계하고, 두 에이전트는 FIA를 위해 서킷을 검토합니다.
빌드 대상
녹색 상자는 에이전트이고 점선 상자는 에이전트의 도구입니다. ADK 플레이그라운드에서 그리는 방식과 동일합니다. 두 도구는 다른 에이전트를 래핑합니다. fia_assessor는 FIA 규정 준수 기술을 갖춘 FIA 평가자 에이전트이고, remote_gatekeeper는 A2A를 통해 다른 프로젝트의 게이트키퍼 에이전트를 호출합니다.
에이전트 | 프로젝트 | 작업 |
|
수석 설계자 |
| 공연 무대를 읽고, 평가자에게 질문하고, 문제를 해결하고, 게이트키퍼에게 승인을 요청합니다. |
|
FIA 평가자 |
| FIA 기술 및 점수 도구를 사용하여 레이아웃을 확인합니다. |
|
디지털 트윈 게이트키퍼 |
| 최종 승인 체크리스트 실행 |
|
학습할 내용
agents-cli scaffold create에서 ADK 프로젝트를 배치하는 방법과 수정하는 파일- 도메인 지식을 에이전트 기술 (
SKILL.md,references/,scripts/)로 패키징하고 ADK의SkillToolset로 로드하는 방법 - 각 상담사의 업무에
thinking_level를 매칭하는 방법 agents-cli eval run로 에이전트를 평가하는 방법- Agent Runtime에 에이전트를 배포하는 방법, 에이전트 ID가 서비스 계정과 다른 점, A2A 프로토콜을 통해 배포된 에이전트를 호출하는 방법
2. 2. 환경 설정
시작하기 전에
결제가 사용 설정된 Google Cloud 프로젝트가 필요합니다. 이벤트에서 이 Codelab을 진행하는 경우 제공된 계정과 프로젝트를 사용하세요.
Cloud Shell을 열고 프로젝트 설정
Cloud Shell을 열고 다음을 실행합니다.
# Cloud Shell sometimes opens without a project set. If so, this sets it to
# the first project on your account.
if [ -z "$(gcloud config get-value project 2>/dev/null)" ]; then
gcloud config set project "$(gcloud projects list --format='value(projectId)' --limit=1)"
fi
export GOOGLE_CLOUD_PROJECT="$(gcloud config get-value project)"
export REGION="us-central1"
export ASSETS="$HOME/devrel-demos/codelabs/formula-e-track-planner"
echo "Project: $GOOGLE_CLOUD_PROJECT Region: $REGION"
에이전트의 사용자 인증 정보 만들기
gcloud은(는) 이미 로그인되어 있지만 에이전트가 실행하는 Python 코드는 별도의 로그인이 필요한 애플리케이션 기본 사용자 인증 정보(ADC)를 사용합니다.
gcloud auth application-default login
출력된 링크를 열고 동일한 계정으로 로그인한 후 코드를 복사하여 Cloud Shell에 다시 붙여넣습니다. 그런 다음 할당량이 청구되는 프로젝트를 설정합니다.
gcloud auth application-default set-quota-project "$GOOGLE_CLOUD_PROJECT"
API 사용 설정
gcloud services enable \
aiplatform.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
storage.googleapis.com \
cloudresourcemanager.googleapis.com \
serviceusage.googleapis.com \
compute.googleapis.com \
iam.googleapis.com \
logging.googleapis.com \
cloudtrace.googleapis.com
각 API의 용도:
API | 연결 대상: |
| Gemini 모델 및 Agent Runtime |
|
|
| 7단계의 배포에서 사용하는 Compute Engine 기본 서비스 계정을 만듭니다. |
| Agent Runtime 설정 가이드에서 권장하는 스토리지, 로그, trace |
| 7단계의 배포에 대비하여 컨테이너 빌드 및 이미지가 포함됨 |
많은 프로젝트에서 이미 이러한 기능 중 일부가 사용 설정되어 있습니다. 이미 사용 설정된 API를 사용 설정해도 아무것도 변경되지 않습니다.
uv 및 agents-cli 설치
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv tool install google-agents-cli==1.10.0
agents-cli --version
이 Codelab은 agents-cli 1.10.0으로 테스트되었으므로 이 버전을 설치하여 동일한 생성 파일을 가져옵니다.
3. 3. 프로젝트 스캐폴딩 및 애셋 추가
두 프로젝트 생성
agents-cli scaffold create는 배포 가능한 완전한 ADK 프로젝트를 생성합니다. 각 에이전트 시스템에 대해 한 번씩 두 번 실행합니다.
먼저 트랙 플래너입니다.
cd ~
agents-cli scaffold create track-planner \
--agent adk \
--deployment-target agent_runtime \
--region "$REGION" \
--prototype \
--yes
그런 다음 안내 직원은 다음을 수행합니다.
cd ~
agents-cli scaffold create gatekeeper \
--agent adk \
--deployment-target agent_runtime \
--region "$REGION" \
--prototype \
--yes
플래그 | 의미 |
| Python ADK 템플릿에서 시작 |
| Agent Runtime의 서버 및 구성 생성 |
| CI/CD 건너뛰기, 프로젝트 최소화 |
| 메시지 표시 없이 기본값 수락 |
레이아웃 살펴보기
생성된 프로젝트는 다음과 같습니다. ✍️로 표시된 파일만 수정합니다.
track-planner/
├── agents-cli-manifest.yaml # Records the template, deployment target and root agent name
├── pyproject.toml # Dependencies (google-adk, a2a-sdk, ...)
├── .env # Your project and model location, filled in for you
├── Dockerfile # Used by `agents-cli deploy`; copies app/ into the image
├── app/
│ ├── agent.py # ✍️ Your agents
│ ├── fast_api_app.py # Generated server: ADK API, A2A routes, Agent Runtime routes
│ └── app_utils/ # Generated helpers (A2A, sessions)
└── tests/
├── unit/ # Fast tests with no model calls
├── integration/ # Generated server tests
└── eval/ # ✍️ eval_config.yaml and datasets for `agents-cli eval`
생성된 모든 파일에 대한 설명은 Agents CLI 가이드의 프로젝트 구조를 참고하세요.
생성된 에이전트 사용해 보기
변경하기 전에 템플릿 에이전트를 실행하여 스캐폴드가 제공하는 기능을 확인합니다. 종속 항목을 설치하고 포트 8080에서 로컬 플레이그라운드를 시작합니다.
cd ~/track-planner
agents-cli install
agents-cli playground --port 8080
이를 열려면 Cloud Shell 툴바에서 웹 미리보기 버튼 (오른쪽 상단의 눈 아이콘)을 클릭한 다음 포트 8080에서 미리보기를 선택합니다.
플레이그라운드에서 템플릿 에이전트에게 도구가 대답할 수 있는 질문을 합니다. 예를 들면 다음과 같습니다.
What's the weather in San Francisco?
측면 패널에는 get_weather 도구 호출 및 결과가 포함된 각 이벤트가 표시됩니다. 나중에 동일한 뷰를 사용하여 자체 에이전트를 따릅니다.
완료되면 터미널로 돌아가 Ctrl+C를 눌러 플레이그라운드를 중지합니다.
포뮬러 E 애셋 추가
공연 무대 데이터, FIA 규칙, 물리 스크립트, 도구, 평가 데이터 세트는 강의 내용이 아니므로 직접 입력하지 않고 GitHub에서 복사합니다.
cd ~
git clone --depth 1 --filter=blob:none --sparse \
--branch formula-e-lab-v1 \
https://github.com/tdsymonds/devrel-demos.git
git -C devrel-demos sparse-checkout set codelabs/formula-e-track-planner
cp -r "$ASSETS/track-planner/." ~/track-planner/
cp -r "$ASSETS/gatekeeper/." ~/gatekeeper/
formula-e-lab-v1는 고정된 출시 태그이므로 모든 사용자가 동일한 파일을 가져옵니다. $ASSETS은 2단계에서 설정되었으며 방금 클론한 폴더를 가리킵니다.
그러면 다음 파일이 추가됩니다.
track-planner/
├── app/
│ ├── tools.py # get_track_topology, evaluate_track_layout
│ ├── auth.py # A2A client with per-request token refresh
│ ├── data/circuits.json # Five candidate venues
│ └── skills/fia-compliance-skill/
│ ├── references/track_regulations.md # The FIA rules
│ └── scripts/calculate_regen_zones.py # Braking-energy calculator
└── tests/
├── unit/test_tools.py
└── eval/datasets/track_eval.json, track_eval_quick.json
gatekeeper/
├── app/tools.py # verify_infrastructure_checklist
└── tests/unit/test_tools.py
설치 및 테스트
(cd ~/track-planner && agents-cli install && uv run pytest tests/unit)
(cd ~/gatekeeper && agents-cli install && uv run pytest tests/unit)
agents-cli install는 uv sync를 실행하여 각 프로젝트의 가상 환경을 만듭니다. 모든 단위 테스트가 통과해야 합니다.
4. 4. FIA 규정 준수 기술 작성
에이전트 스킬은 요청 사항, 참고 문서, 스크립트를 하나의 폴더에 패키징합니다. ADK의 SkillToolset는 이를 세 가지 수준으로 로드하므로 에이전트는 필요한 것만 읽습니다.
수준 | 파일 | 상담사가 확인하는 경우 |
1 |
| 항상 (수십 개의 토큰) |
2 |
|
|
3 |
|
|
생성된 Dockerfile는 app/만 배포된 이미지에 복사하므로 기술은 app/skills/에 있습니다.
레벨 3 파일 보기
3단계에서 기술의 레벨 3 파일을 복사했습니다.
references/track_regulations.md에는 평가자가 확인하는 규칙이 포함되어 있습니다. 길이 및 너비 제한, 각 제동 구역에 필요한 장벽, 필수 CAD 레이어 (병원 경로의 의료 레이어 포함), 재생 목표, 공격 모드 규칙이 포함됩니다.scripts/calculate_regen_zones.py은 계산기입니다. 제동 구역의 진입 속도, 탈출 속도, 제동 거리가 주어지면 회수되는 에너지와 코너에 필요한 장벽을 계산하므로 에이전트가 물리학을 추측할 필요가 없습니다.
런던의 10번 코너에서 자동차가 82m에 걸쳐 시속 215km에서 55km로 감속하는 스크립트를 실행합니다.
cd ~/track-planner
uv run python app/skills/fia-compliance-skill/scripts/calculate_regen_zones.py \
--entry-kph 215 --exit-kph 55 --brake-dist-m 82
이 지점에서 차량은 목표인 250kW를 초과하는 약 508kW를 회수합니다. 2G를 초과하면 속도가 느려지므로 코너에 Tecpro 장벽 (F1_TEC3)이 필요합니다.
{"entry_kph": 215.0, "exit_kph": 55.0, "brake_dist_m": 82.0, "deceleration_g": 2.07, "estimated_regen_kw": 507.7, "meets_250kw_target": true, "required_barrier_layer": "F1_TEC3"}
SKILL.md 작성
스킬을 연결하는 파일을 만듭니다.
cloudshell edit app/skills/fia-compliance-skill/SKILL.md
다음 내용을 추가합니다.
---
name: fia-compliance-skill
description: >
FIA Formula E street circuit rules: length and width limits, mandatory CAD
layers, barrier selection, and Gen3 regenerative braking estimates. Use when
designing, checking or fixing a Formula E circuit layout.
---
# FIA Formula E Compliance
Follow these steps for every circuit you check.
1. **Read the rules.** Call `load_skill_resource` with
`file_path="references/track_regulations.md"`.
2. **Estimate regeneration for every heavy braking zone.** Call
`run_skill_script` with `file_path="scripts/calculate_regen_zones.py"` and
`args={"entry-kph": <entry>, "exit-kph": <exit>, "brake-dist-m": <distance>}`.
Never guess kW values. Use the highest `estimated_regen_kw` as the
circuit's peak regeneration.
3. **Check barriers.** Any zone whose `required_barrier_layer` is `F1_TEC3`
needs the `F1_TEC3` CAD layer.
4. **Report.** List every rule the layout fails and a concrete fix for each
one.
description은 모델이 스킬을 사용할 시점을 결정하기 위해 읽는 내용이므로 스킬이 다루는 내용과 사용 시점을 모두 설명합니다.
스킬이 연결된 방식 확인
Cloud Shell 편집기에서 app/tools.py를 엽니다.
cloudshell edit app/tools.py
build_fia_skill_toolset 함수를 찾습니다.
def build_fia_skill_toolset() -> SkillToolset:
return SkillToolset(
skills=[load_skill_from_dir(SKILL_DIR)],
code_executor=UnsafeLocalCodeExecutor(),
)
load_skill_from_dir는SKILL.md,references/,scripts/스킬 폴더를 읽습니다.SkillToolset는 이를 에이전트가 호출할 수 있는 도구인list_skills,load_skill,load_skill_resource,run_skill_script로 변환합니다.UnsafeLocalCodeExecutor를 사용하면run_skill_script가 이 머신에서 Python 스크립트를 실행할 수 있습니다. 실습에는 적합하지만 프로덕션에서는 샌드박스 처리된 실행기를 사용하세요.
다음 단계에서 이 도구 모음을 평가자에 연결합니다.
5. 5. 에이전트 작성
Gemini 3 모델은 토큰 예산 대신 thinking_level (LOW, MEDIUM 또는 HIGH)를 사용하여 추론을 제어합니다. ADK에서는 types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH)로 설정합니다. 각 에이전트의 작업에 맞게 조정합니다. 사고량이 많을수록 계획이 개선되지만 지연 시간과 비용이 증가합니다.
게이트키퍼 (LOW)
게이트키퍼는 결정론적 체크리스트만 실행하므로 추론이 거의 필요하지 않습니다. 템플릿 에이전트를 바꿉니다.
cloudshell edit ~/gatekeeper/app/agent.py
"""FIA Digital Twin Gatekeeper agent.
This agent runs in its own project and is deployed to Agent Runtime. The track
planner calls it over the A2A protocol.
"""
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.models import Gemini
from google.genai import types
from app.tools import verify_infrastructure_checklist
# Tested with Gemini 3.8 Flash. To try Gemini 4 Argon, change this to
# "gemini-4-argon", delete the `temperature` settings below (Argon ignores
# them) and run `uv add "google-genai>=2.30.1"` in this project first.
MODEL = "gemini-3.8-flash"
root_agent = Agent(
# Must match `root_agent_name` in agents-cli-manifest.yaml.
name="gatekeeper",
model=Gemini(
model=MODEL,
retry_options=types.HttpRetryOptions(attempts=3),
),
description=(
"FIA Digital Twin Gatekeeper. Runs the final homologation checklist "
"for a Formula E circuit blueprint."
),
instruction=(
"You are the FIA Digital Twin Gatekeeper. For every circuit blueprint "
"you receive, call `verify_infrastructure_checklist` with its circuit "
"ID, CAD layers, minimum width and peak regeneration. Reply with the "
"status and any missing CAD layers. Never approve a blueprint the "
"checklist rejects."
),
# A deterministic checklist needs little reasoning: keep it fast and cheap.
generate_content_config=types.GenerateContentConfig(
temperature=0.0,
thinking_config=types.ThinkingConfig(
thinking_level=types.ThinkingLevel.LOW,
),
),
tools=[verify_infrastructure_checklist],
)
app = App(
root_agent=root_agent,
name="app",
)
로컬에서 Gatekeeper 실행
두 번째 Cloud Shell 탭을 열고 포트 8001에서 Gatekeeper의 생성된 서버를 시작합니다.
cd ~/gatekeeper
APP_URL=http://localhost:8001 uv run uvicorn app.fast_api_app:app --port 8001
첫 번째 탭으로 돌아가서 Gatekeeper의 A2A 상담사 카드를 확인합니다. 다른 에이전트가 Gatekeeper의 정의와 기능을 검색하는 방법은 다음과 같습니다.
curl -s http://localhost:8001/a2a/app/.well-known/agent-card.json | jq .
다음과 같은 JSON이 표시됩니다 (축약됨).
{
"name": "gatekeeper",
"description": "FIA Digital Twin Gatekeeper. Runs the final homologation checklist for a Formula E circuit blueprint.",
"supportedInterfaces": [
{"url": "http://localhost:8001/a2a/app", "protocolBinding": "JSONRPC", "protocolVersion": "1.0"}
],
"skills": [
{"id": "gatekeeper", "name": "model", "description": "FIA Digital Twin Gatekeeper. ..."},
{"id": "gatekeeper-verify_infrastructure_checklist", "name": "verify_infrastructure_checklist", "description": "Runs the final homologation checklist ..."}
]
}
name및description는Agent(...)정의에서 직접 가져옵니다.skills에는 에이전트가 할 수 있는 작업이 나열되어 있습니다. ADK는 도구의 docstring을 사용하여verify_infrastructure_checklist를 자동으로 추가했습니다.supportedInterfaces는 호출자에게 A2A 요청을 보낼 위치를 알려줍니다. 이것이APP_URL=http://localhost:8001로 서버를 시작한 이유입니다. 호출자는 먼저 카드를 읽은 다음 카드가 알리는url에 요청을 보냅니다.
여기에는 직접 작성한 내용이 없습니다. 스캐폴드된 서버가 에이전트에서 카드를 빌드합니다. 트랙 플래너는 이 카드를 읽고 게이트키퍼를 찾아 호출합니다.
평가자 (MEDIUM) 및 수석 설계자 (HIGH)
트랙 플래너에는 두 가지 에이전트가 있습니다.
- 평가자는 여러 규칙을 평가하므로
MEDIUM를 가져옵니다. 이 함수는AgentTool로 래핑되어 있으므로 수석 설계자는 이를 함수처럼 호출하여 결과를 반환받습니다. - 수석 아키텍트는 계획, 수정, 조정을 담당하므로
HIGH를 받습니다.app/auth.py의create_remote_gatekeeper_agent()에 의해 생성된RemoteA2aAgent를 통해 Gatekeeper에 도달하고AgentTool로 래핑합니다.
두 도우미를 모두 도구로 래핑하는 이유는 무엇인가요? AgentTool는 호출자에게 답변을 반환하므로 수석 설계자가 계속 담당하여 최종 청사진을 작성합니다. 게이트키퍼가 sub_agent인 경우 수석 설계자는 대화를 게이트키퍼에게 넘기고 게이트키퍼의 짧은 상태가 최종 답변이 됩니다.
트랙 플래너의 app/agent.py 파일을 엽니다.
cloudshell edit ~/track-planner/app/agent.py
다음 코드를 복사하여 ~/track-planner/app/agent.py의 전체 콘텐츠를 바꿉니다.
"""Formula E track planner: Lead Architect, FIA Assessor and remote Gatekeeper.
- The Lead Architect (root agent) designs the circuit and stays in charge.
- The FIA Assessor checks the layout and is called as a tool (`AgentTool`).
- The Gatekeeper runs in a separate project. It is also called as a tool, over
A2A, so its verdict comes back to the Lead Architect.
"""
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.models import Gemini
from google.adk.tools.agent_tool import AgentTool
from google.genai import types
from app.auth import create_remote_gatekeeper_agent
from app.tools import build_fia_skill_toolset
from app.tools import evaluate_track_layout
from app.tools import get_track_topology
# Tested with Gemini 3.8 Flash. To try Gemini 4 Argon, change this to
# "gemini-4-argon", delete the `temperature` settings below (Argon ignores
# them) and run `uv add "google-genai>=2.30.1"` in this project first.
MODEL = "gemini-3.8-flash"
fia_skill_toolset = build_fia_skill_toolset()
# 1. FIA Assessor: MEDIUM thinking to weigh several rules without much latency.
assessor_agent = Agent(
name="fia_assessor",
model=Gemini(
model=MODEL,
retry_options=types.HttpRetryOptions(attempts=3),
),
description=(
"FIA safety and energy assessor. Audits a proposed circuit against "
"the geometry, regeneration and CAD layer rules."
),
instruction=(
"You are the FIA Assessor. Load the `fia-compliance-skill` and follow "
"it. Estimate regeneration for each braking zone with "
"`calculate_regen_zones.py`, then call `evaluate_track_layout` with "
"the highest value. Reply with the verdict, the score and every "
"remediation note."
),
generate_content_config=types.GenerateContentConfig(
temperature=0.1,
thinking_config=types.ThinkingConfig(
thinking_level=types.ThinkingLevel.MEDIUM,
),
),
tools=[fia_skill_toolset, evaluate_track_layout],
)
# 2. Remote Gatekeeper, reached over A2A (see app/auth.py).
remote_gatekeeper = create_remote_gatekeeper_agent()
# 3. Lead Architect: HIGH thinking for multi-step planning and trade-offs.
root_agent = Agent(
# Must match `root_agent_name` in agents-cli-manifest.yaml.
name="track_planner",
model=Gemini(
model=MODEL,
retry_options=types.HttpRetryOptions(attempts=3),
),
description=(
"Lead Formula E circuit architect. Designs FIA-compliant street "
"circuits and gets them homologated."
),
instruction=(
"You are the Lead Formula E Circuit Architect. For every request:\n"
"1. Call `get_track_topology` for the venue.\n"
"2. Call the `fia_assessor` tool with the full topology.\n"
"3. If the Assessor rejects the layout, propose concrete fixes (for "
"example widen a sector to 10.0m or add missing CAD layers) and "
"re-assess the fixed layout.\n"
"4. Once the Assessor passes it, call the `remote_gatekeeper` tool "
"with the circuit ID, CAD layers, minimum width and peak "
"regeneration for final approval.\n"
"5. Finish with a short blueprint: circuit ID, length, minimum width, "
"peak regeneration, every CAD layer by name, and the Gatekeeper's "
"status."
),
generate_content_config=types.GenerateContentConfig(
temperature=0.2,
thinking_config=types.ThinkingConfig(
thinking_level=types.ThinkingLevel.HIGH,
),
),
# Both helpers are tools, so their answers come back to the Lead Architect
# and it writes the final blueprint.
tools=[
get_track_topology,
AgentTool(agent=assessor_agent),
AgentTool(agent=remote_gatekeeper),
],
)
app = App(
root_agent=root_agent,
name="app",
)
플레이그라운드에서 전체 시스템 실행
Gatekeeper가 두 번째 탭에서 계속 실행 중인지 확인합니다. 첫 번째 탭에서 트랙 플래너의 플레이그라운드를 시작합니다.
cd ~/track-planner
agents-cli playground --port 8080
3단계에서와 같이 웹 미리보기 > 포트 8080에서 미리보기를 클릭합니다.
규칙을 충족하는 장소에서 회로를 요청합니다.
Design and homologate a Formula E circuit at Tokyo Bay.
에이전트가 작업하는 동안 이벤트 패널을 확인합니다. 다음과 같은 순서로 표시됩니다.
get_track_topology: 수석 설계자가 도쿄만 데이터를 로드합니다.fia_assessor: 수석 설계자가 레이아웃을 평가자에게 전달하면 평가자는load_skill,load_skill_resource,run_skill_script(제동 구역당 한 번)를 호출한 다음evaluate_track_layout를 호출합니다.remote_gatekeeper: 리드 설계자가 A2A를 통해 게이트키퍼를 호출합니다. 두 번째 탭에 요청이 기록된 것을 확인할 수 있습니다.- 수석 설계자의 최종 청사진으로, 모든 CAD 레이어와 게이트키퍼의 APPROVED 상태가 나열되어 있습니다.
이벤트를 클릭하여 정확한 입력 및 출력을 확인합니다.
이제 새 세션을 시작하고 규칙을 위반하는 공연 무대를 시도해 보세요.
Can the Monaco Harbour layout be homologated? If not, fix it and try again.
이번에는 평가자가 6.8m 시케인을 거부하고, 수석 설계자가 더 넓은 시케인을 제안하고, 수정된 레이아웃이 게이트키퍼에게 도달하기 전에 평가자에게 다시 전달됩니다.
완료되면 첫 번째 탭에서 Ctrl+C를 눌러 플레이그라운드를 중지합니다. 다음 단계에서 필요하므로 두 번째 탭에서 Gatekeeper를 실행 중인 상태로 둡니다.
6. 6. 에이전트 평가
agents-cli eval run는 데이터 세트에서 에이전트를 실행하고, 모든 대화와 도구 호출을 기록하고, 결과에 점수를 매깁니다. tests/eval/eval_config.yaml에서 측정할 항목을 결정합니다.
데이터 세트 살펴보기
Cloud Shell 편집기에서 데이터 세트를 엽니다.
cd ~/track-planner
cloudshell edit tests/eval/datasets/track_eval.json
이 파일에는 eval_cases 목록이 포함되어 있습니다. 각 케이스에는 eval_case_id, prompt (사용자가 질문하는 내용), reference (좋은 답변의 예시)가 있습니다. LLM 판정 모델은 에이전트의 답변을 참조와 비교합니다.
| 테스트 대상 |
| 모든 규칙을 통과하고 승인된 공연 무대 |
| 6.8m 시케인은 너비 규칙을 충족하지 못함 |
| 4,350m 회선이 길이 규칙을 위반함 |
| 필수 CAD 레이어가 누락된 레이아웃 |
| 목표인 250kW 미만의 제동 구간 |
| 의료 CAD 레이어가 필요한 병원 액세스 |
| 레이싱 라인의 어택 모드 구역 |
| 멀티턴 대화: Monaco가 거부되고 사용자가 수정을 요청하며 수정된 레이아웃이 승인되어야 합니다. |
마지막 사례에서는 prompt 대신 agent_data를 사용합니다. 대화의 이전 차례를 재생하므로 에이전트가 단일 질문뿐만 아니라 후속 요청을 처리하는 방법을 테스트할 수 있습니다.
같은 폴더에 있는 track_eval_quick.json에는 케이스 1 (단일 질문)과 케이스 8 (대화)이라는 두 가지 케이스만 있습니다. 더 빠르고 할당량을 적게 사용하므로 이 파이프라인을 실행합니다.
측정항목 작성
데이터 세트는 에이전트에게 무엇을 요청할지를 알려줍니다. eval_config.yaml는 답변에 점수를 매기는 방법을 알려줍니다. agents-cli eval run는 상담사가 모든 케이스에 답변한 후 이를 읽고 각 답변에 대해 각 측정항목을 실행합니다.
스캐폴드가 생성한 파일을 엽니다.
cloudshell edit tests/eval/eval_config.yaml
여기에는 이미 일반 LLM 심사자 (custom_response_quality)와 대화만 집계하는 agent_turn_count 측정항목이 포함되어 있습니다. 에이전트의 시작으로는 괜찮지만 Formula E에 관한 내용은 없습니다. 심사자를 유지하고 이 도메인에만 적용되는 규칙을 확인하는 측정항목을 추가합니다.
좋은 평가는 일반적으로 두 가지 유형의 측정항목을 혼합합니다.
종류 | 점수 산정 방식 | 용도 | 장단점 |
LLM-as-a-judge | 모델이 답변과 케이스의 | 모호한 품질: 답변이 정확하고 완전하며 명확한가요? | 케이스당 모델 호출 비용이 발생하며 실행마다 점수가 약간 다를 수 있음 |
코드 측정항목 | 답변을 확인하는 작은 Python 함수 | 정확하고 확인 가능한 사실: 답변에 반드시 포함되어야 하는 내용이 포함되어 있나요? | 코드로 표현할 수 있는 것만 확인 |
파일의 콘텐츠를 다음으로 바꿉니다.
metrics_to_run:
- custom_response_quality
- fia_cad_layer_compliance
custom_metrics:
# LLM-as-a-judge. response_quality.py is generated by `agents-cli scaffold
# create`; it grades the final answer against the case's `reference`.
- name: custom_response_quality
custom_function_file: response_quality.py
# Deterministic code metric: an approved blueprint must name every
# mandatory FIA CAD layer.
- name: fia_cad_layer_compliance
custom_function: |
def evaluate(instance):
response = str(instance.get("response") or "")
reference = str(instance.get("reference") or "")
# Only approvals must list every layer. Rejections and other
# questions (such as Attack Mode) don't, so skip them.
if "APPROVED" not in reference:
return {"score": 1.0, "explanation": "Not an approval case."}
required = [
"F1_TRACK_CENTRELINE",
"F1_PIT_ENTRY_SPEED_LIMIT_LANE",
"F1_WALL_TEMP",
"F1_TEC3",
]
missing = [layer for layer in required if layer not in response]
return {
"score": round(1 - len(missing) / len(required), 2),
"explanation": "Missing: " + (", ".join(missing) or "none"),
}
각 부분의 역할은 다음과 같습니다.
metrics_to_run는 실행할 측정항목을 이름별로 나열합니다. 여기에서 이름을 삭제하면 해당 측정항목이 삭제되지 않고 사용 중지됩니다.custom_response_quality는 LLM 판정자입니다.custom_function_file는 스캐폴드가 생성한tests/eval/response_quality.py를 가리킵니다. 심사 프롬프트를 보려면 이 파일을 여세요.fia_cad_layer_compliance는 코드 측정항목으로,custom_function아래에 인라인으로 작성됩니다. 각 케이스에 대해instance(프롬프트, 에이전트의 최종response,reference)를 수신합니다. 예상 답변이 승인인 케이스의 경우 응답에서 필수 CAD 레이어 4개를 모두 언급하는지 확인하고 누락된 레이어를 나열합니다. 예를 들어F1_TEC3를 제외한 청사진은 0.75점을 받습니다. 다른 케이스는 거부 또는 공격 모드 답변에서 모든 레이어를 나열할 필요가 없으므로 1.0점을 받습니다.
CAD 레이어를 확인해야 하는 이유 필수 레이어를 누락한 승인된 청사진은 가독성이 좋더라도 잘못된 것이며, LLM 심사관은 이를 간과할 수 있습니다. 코드 검사는 실행할 때마다 동일한 규칙을 동일한 방식으로 적용합니다.
평가 실행
두 번째 탭에서 로컬 Gatekeeper를 계속 실행한 후 다음을 실행합니다.
agents-cli eval run \
--dataset tests/eval/datasets/track_eval_quick.json \
--config tests/eval/eval_config.yaml \
--concurrency 1
ls artifacts/grade_results/
평가에는 몇 분 정도 걸립니다. 각 케이스는 전체 멀티 에이전트 흐름을 실행하며, HIGH에 대한 리드 아키텍트의 사고를 포함하여 약 10회의 모델 호출이 이루어지므로 Step 1/2: eval generate가 느린 부분입니다. 멈추지 않습니다.
완료되면 Cloud Shell 편집기를 사용하여 artifacts/grade_results/에서 HTML 보고서를 엽니다. fia_cad_layer_compliance 점수가 낮으면 수석 설계자의 최종 답변에서 레이어가 누락되었음을 의미합니다. 요청 사항을 더 구체적으로 작성하고 평가를 다시 실행해 보세요.
7. 7. Agent Runtime에 게이트키퍼 배포
Gatekeeper만 배포합니다. 트랙 플래너는 Cloud Shell에서 계속 실행되며 이를 호출합니다.
새 Cloud Shell 탭 열기
배포하는 데 5~8분이 걸리므로 6단계의 평가가 완료되는 동안 새 탭에서 배포를 진행합니다.
- Cloud Shell 툴바에서 새 탭 열기 (+)를 클릭합니다.
- 새 탭에서는 2단계의 변수가 유지되지 않으므로 다시 설정합니다.
export GOOGLE_CLOUD_PROJECT="$(gcloud config get-value project)" export REGION="us-central1" export ASSETS="$HOME/devrel-demos/codelabs/formula-e-track-planner" export PATH="$HOME/.local/bin:$PATH" echo "Project: $GOOGLE_CLOUD_PROJECT Region: $REGION"
두 번째 탭에서 로컬 Gatekeeper를 실행 중인 상태로 둡니다. 평가에서 계속 사용하며 배포 시 중지할 필요가 없습니다.
배포
새 탭에서 Gatekeeper를 배포합니다. 프로젝트의 Compute Engine 기본 서비스 계정으로 실행됩니다.
cd ~/gatekeeper
PROJECT_NUMBER=$(gcloud projects describe "$GOOGLE_CLOUD_PROJECT" --format="value(projectNumber)")
agents-cli deploy \
--no-agent-identity \
--service-account "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--project "$GOOGLE_CLOUD_PROJECT" \
--region "$REGION" \
--no-confirm-project
배포하는 데 5~8분 정도 걸립니다. 실행 중에는 Cloud Shell 탭을 닫지 마세요. 로그 스트림 연결이 끊어지면 agents-cli deploy --status로 진행 상황을 확인합니다. 기다리는 동안 6단계로 돌아가 평가 보고서를 읽어보세요.
에이전트의 ID 검사
agents-cli는 배포된 리소스를 deployment_metadata.json에 기록합니다.
export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
echo "$RUNTIME_ID"
curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${REGION}-aiplatform.googleapis.com/v1beta1/${RUNTIME_ID}" \
| jq -r .spec.effectiveIdentity
출력의 두 번째 줄은 Gatekeeper가 실행되는 ID입니다.
배포 도구 | 출력 예 |
서비스 계정 (기본값) |
|
에이전트 ID |
|
서비스 계정은 프로젝트에서 이를 사용하는 모든 항목에서 공유됩니다. 에이전트 ID는 이 에이전트 하나에 속하므로 필요한 역할을 정확하게 부여하고 감사 로그에서 에이전트의 작업을 별도로 확인할 수 있습니다.
8. 8. A2A를 통해 배포된 Gatekeeper에 연결
Agent Runtime은 인증된 /api 경로 뒤에서 Gatekeeper가 생성한 A2A 경로를 제공합니다.
6단계의 평가가 완료되었는지 확인한 후 두 번째 탭에서 로컬 Gatekeeper를 중지합니다 (Ctrl+C). 이제부터 트랙 플래너는 배포된 버전을 호출합니다. 첫 번째 탭에서 트랙 플래너를 가리킵니다.
export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
: "${RUNTIME_ID:?No deployed Gatekeeper found. Complete Step 7 first.}"
cd ~/track-planner
echo "GATEKEEPER_A2A_URL=https://${REGION}-aiplatform.googleapis.com/reasoningEngines/v1/${RUNTIME_ID}/api/a2a/app" >> .env
클라이언트가 토큰을 새로고침하는 이유
app/auth.py를 엽니다. Google Cloud 액세스 토큰은 1시간 후에 만료되므로 시작 시 고정 Authorization 헤더를 설정하는 클라이언트는 1시간 후에 401 Unauthorized로 실패하기 시작합니다. GoogleAuthRefresh는 httpx.Auth를 서브클래스로 분류하고 모든 요청에서 사용자 인증 정보를 확인하여 필요할 때 새로고침합니다. create_remote_gatekeeper_agent()는 https:// URL에만 첨부합니다.
엔드 투 엔드 실행
새 GATEKEEPER_A2A_URL가 적용되도록 플레이그라운드를 다시 시작합니다.
agents-cli playground --port 8080
웹 미리보기 > 포트 8080에서 미리보기를 클릭하고 새 세션을 시작한 후 다음 질문을 합니다.
Assess the Monaco Harbour layout, widen the chicane to 11m, and get Gatekeeper approval.
이벤트는 5단계와 동일하게 표시되지만 이제 remote_gatekeeper 호출이 두 번째 탭이 아닌 Agent Runtime의 Gatekeeper로 이동합니다.
Agent Runtime에 호출이 도착하는지 확인
요청이 A2A를 통해 배포된 Gatekeeper에 실제로 도달했는지 확인합니다.
- Cloud 콘솔에서 Agent Platform을 검색한 다음 왼쪽 메뉴에서 배포를 엽니다.
- gatekeeper를 선택합니다.
- 세션 탭을 엽니다.
사용자 ID가 A2A_USER_로 시작하는 새 세션이 표시됩니다. 트랙 플래너가 호출할 때 Gatekeeper의 A2A 서버가 이를 생성했습니다. 같은 페이지에서 Traces 탭을 열어 Gatekeeper의 모델 호출과 해당 요청에 대한 verify_infrastructure_checklist 도구 호출을 확인합니다.
선택사항: 트랙 플래너도 배포
지금까지 트랙 플래너는 Cloud Shell에서 실행되므로 Gatekeeper에 대한 A2A 호출은 내 사용자 인증 정보를 사용합니다. 플래너도 배포하고 두 에이전트 모두 Agent Runtime에서 실행되며 배포된 자체 ID로 서로를 호출합니다.
Ctrl+C를 눌러 플레이그라운드를 중지한 다음 Gatekeeper와 동일한 방식으로 플래너를 배포합니다.
cd ~/track-planner
PROJECT_NUMBER=$(gcloud projects describe "$GOOGLE_CLOUD_PROJECT" --format="value(projectNumber)")
agents-cli deploy \
--no-agent-identity \
--service-account "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--project "$GOOGLE_CLOUD_PROJECT" \
--region "$REGION" \
--no-confirm-project
두 에이전트 모두 동일한 Compute Engine 서비스 계정으로 실행됩니다. --service-account를 유지합니다. 이 변수가 없으면 플래너가 Agent Runtime의 기본 서비스 에이전트로 실행되며 Gatekeeper를 호출할 수 없습니다 (7단계의 이 서비스 계정이 필요한 이유 참고).
배치된 플래너가 안내 직원에게 연락하는 방법:
agents-cli deploy는.env의 값을 배포에 복사하므로 플래너는 위에서 추가한GATEKEEPER_A2A_URL를 가져옵니다.- Agent Runtime 내에서
app/auth.py의GoogleAuthRefresh는 사용자의 ID가 아닌 플래너의 배포된 ID를 선택합니다.
배포하는 데 5~8분 정도 걸립니다. 완료되면 플래너가 실행되는 ID를 확인합니다.
export PLANNER_ID=$(jq -r .remote_agent_runtime_id deployment_metadata.json)
curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${REGION}-aiplatform.googleapis.com/v1beta1/${PLANNER_ID}" \
| jq -r .spec.effectiveIdentity
배포된 시스템을 사용해 보려면 다음 안내를 따르세요.
- Cloud 콘솔에서 Agent Platform > 배포로 돌아갑니다.
- track-planner를 선택하고 Playground 탭을 엽니다.
- 다음을 입력합니다.
Design and homologate a Formula E circuit at Tokyo Bay. - 게이트키퍼의 승인됨 상태가 있는 청사진을 기다립니다.
- 배포로 돌아가서 gatekeeper를 선택하고 세션 탭을 엽니다. 최신
A2A_USER_세션은 Cloud Shell이 아닌 배포된 플래너에서 시작되었습니다.
다음 단계에서 정리할 때 플래너도 삭제합니다.
curl -X DELETE -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${REGION}-aiplatform.googleapis.com/v1beta1/${PLANNER_ID}?force=true"
9. 9. 문제 해결 및 삭제
문제 해결
증상 | 수정 |
| Cloud 콘솔에서 서비스 약관에 동의한 후 다시 시도하세요. |
프로젝트: | 2단계의 프로젝트 명령어를 다시 실행합니다. |
|
|
| 4단계를 완료하거나 |
| 로컬 Gatekeeper를 시작하거나 (5단계) |
배포 시 | 계정에서 상담사 ID를 사용할 수 없습니다. 7단계의 정리 상자를 따른 다음 서비스 계정 배포를 사용합니다. |
배포된 트랙 플래너가 Gatekeeper에서 | 플래너의 ID는 Gatekeeper를 호출할 수 없습니다. 7단계와 같이 |
| Gemini 할당량에 도달했습니다. 잠시 기다리거나 |
| 다른 서버를 중지하거나 다른 |
삭제
배포된 Gatekeeper가 계속 실행되지 않도록 삭제합니다.
export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
: "${RUNTIME_ID:?No deployed Gatekeeper found.}"
curl -X DELETE -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${REGION}-aiplatform.googleapis.com/v1beta1/${RUNTIME_ID}?force=true"
이 Codelab만을 위한 프로젝트를 만든 경우 전체 프로젝트를 삭제할 수 있습니다. 이벤트에 참여 중인 경우 주최자가 별도로 안내할 때까지 제공된 프로젝트를 유지하세요.
학습한 내용
agents-cli scaffold create는 배포 가능한 표준 ADK 프로젝트를 제공하므로 에이전트, 기술, 평가만 작성하면 됩니다.- 에이전트 기술은 에이전트가 필요로 할 때까지 분야별 지식을 프롬프트에 포함하지 않습니다.
thinking_level를 사용하면 에이전트별로 추론 깊이를 속도와 맞바꿀 수 있습니다.agents-cli eval run는 '효과가 있을까?'라는 질문을 추적 가능한 점수로 변환합니다.- 에이전트 ID와 A2A를 사용하면 독립적으로 배포된 에이전트가 안전하게 함께 작업할 수 있습니다.