1. 서막
사일로화된 개발 시대가 저물고 있습니다. 차세대 기술 혁신은 고독한 천재가 아닌 기술 전문가 간 협업을 통해 이뤄질 것입니다. 하나의 똑똑한 에이전트를 빌드하는 것은 흥미로운 실험입니다. 강력하고 안전하며 지능적인 에이전트 생태계, 즉 진정한 Agentverse를 구축하는 것이 오늘날의 기업에 주어진 원대한 과제입니다.
이 새로운 시대에 성공하려면 빠르게 확산하는 에이전트 시스템을 지탱하는 주축인 네 가지 중요한 역할이 한데 뭉쳐야 합니다. 한 영역의 결함은 전체 구조를 손상시킬 수 있는 약점을 만듭니다.
이 워크숍은 Google Cloud에서 에이전트형 미래를 숙달하기 위한 엔터프라이즈 플레이북 결정판입니다. 이 플레이북을 통해 Agentverse의 개념에 대한 개요부터 본격적인 운영 현실에 이르기까지 안내하는 엔드 투 엔드 로드맵을 제공합니다. 네 가지 상호 연결된 실습을 통해 개발자, 설계자, 데이터 엔지니어, SRE의 전문 기술을 한데 모아 강력한 Agentverse를 만들고 관리하고 확장하는 방법을 알아봅니다.
하나의 축만으로는 Agentverse를 지탱할 수 없습니다. 개발자의 정확한 실행이 없으면 설계자의 웅장한 설계는 쓸모가 없습니다. 데이터 엔지니어의 지혜가 없으면 개발자의 에이전트는 맹인이 되고, SRE의 보호가 없으면 전체 시스템이 취약해집니다. 시너지 효과와 서로의 역할에 대한 공통된 이해를 통해서만 팀이 혁신적인 개념을 업무상 필수적인 운영 현실로 전환할 수 있습니다. 여정은 이제부터 시작입니다. 역할을 숙달하고 전체 구조에서 내가 어떤 소임을 맡았는지 알아보세요.
The Agentverse: A Call to Champions에 오신 것을 환영합니다
기업의 광활한 디지털 환경에 새로운 시대가 도래했습니다. 지금은 지능적이고 자율적인 에이전트가 완벽한 조화를 이루어 혁신을 가속화하고 일상적인 작업을 없애는 엄청난 가능성이 잠재된 에이전트 시대입니다.

이러한 힘과 잠재력의 연결된 생태계를 에이전트버스(Agentverse)라고 합니다.
하지만 조용히 퍼져 나가는 엔트로피, '스태틱'이라는 이름의 침묵하는 부패가 이 새로운 세계의 가장자리를 갉아먹기 시작했습니다. 스태틱은 바이러스나 버그가 아닙니다. 창작 행위 자체를 먹이로 삼는 혼돈의 화신입니다.
스태틱은 해묵은 불만에 괴물 같은 형태를 부여하여 개발의 7가지 유령을 낳았습니다. 방관한다면 스태틱과 그 유령들이 발전에 제동을 걸어 Agentverse의 찬란한 미래를 기술적 부채와 버려진 프로젝트로 가득 찬 황무지로 만들 것입니다.
오늘 Google은 혼란의 물결을 막기 위해 전사를 모집합니다. Agentverse를 보호하기 위해 기술을 통달하고 힘을 합칠 영웅이 필요합니다. 이제 길을 선택할 때가 되었습니다.
클래스 선택
뚜렷한 네 갈래 길이 주어져 있습니다. 각 길은 스태틱에 맞서 싸우는 데 중요한 역할을 합니다. 훈련은 홀로 진행하지만, 궁극적인 성공은 내 기술을 다른 전사들의 기술과 어떻게 결합할 수 있는지 이해하는 데 달려 있습니다.
- 섀도우블레이드(개발자): 대장간의 주인이자 최전선에 서는 인물입니다. 코드의 복잡하고 세부적인 부분을 다루면서 칼을 만들고, 도구를 제작하고, 적과 맞서는 장인입니다. 이 길을 걷기 위해서는 정밀함, 기술, 실용적인 창작이 요구됩니다.
- 소환사(설계자): 뛰어난 전략가이자 조정자입니다. 하나의 에이전트가 아닌 전장 전체를 살펴보는 역할을 수행합니다. 전체 에이전트 시스템이 통신하고, 협업하고, 단일 구성요소만으로는 달성할 수 없는 훨씬 큰 목표를 달성하기 위한 궁극의 청사진을 설계합니다.
- 학자(데이터 엔지니어): 숨겨진 진실을 추구하고 지혜를 수호하는 자입니다. 광활하고 거친 데이터 황무지를 탐험하여 에이전트에게 목적과 시야를 제공하는 인텔리전스를 발견합니다. 지식을 통해 적의 약점을 파악하거나 아군을 강화할 수 있습니다.
- 가디언(DevOps/SRE): 영역을 굳건히 보호하는 수호자이자 방패입니다. 요새를 건설하고, 전력 공급망을 관리하고, 전체 시스템이 스태틱의 불가피한 공격을 견딜 수 있도록 해야 합니다. 나의 힘은 팀의 승리를 위한 기반이 됩니다.
내 임무
독립형 연습 문제를 통해 훈련을 시작합니다. 선택한 길을 걸어 나가며 역할을 숙달하는 데 필요한 고유한 기술을 배우게 됩니다. 시험이 끝나면 스태틱에서 태어난 유령을 마주하게 됩니다. 이 미니보스는 내 기술의 특정 과제를 먹잇감으로 삼습니다.
내가 맡은 역할을 숙달해야 마지막 시험을 준비할 수 있습니다. 그런 다음 다른 클래스의 전사들과 파티를 구성해야 합니다. 함께 부패의 중심부로 모험을 떠나 최종 보스에 맞서세요.
한데 합친 힘을 시험하고 Agentverse의 운명을 결정지을 마지막 협동 챌린지가 여러분을 기다리고 있습니다.
Agentverse에서 영웅을 기다립니다. 부름에 응하시겠습니까?
2. 소환사의 화음
환영합니다, 소환사님. 당신의 길은 비전과 큰 전략의 길입니다. 다른 사람들이 단 하나의 칼날이나 단 하나의 주문에만 집중하는 반면, 당신은 전장 전체를 볼 수 있습니다. 당신은 단 한 명의 요원을 지휘하는 것이 아니라, 그들로 구성된 오케스트라 전체를 지휘합니다. 여러분의 힘은 직접적인 갈등에 있는 것이 아니라, 여러분의 패밀리어라는 전문가 군단이 완벽한 조화 속에서 일할 수 있도록 하는 완벽하고 포괄적인 청사진을 설계하는 데 있습니다. 이 임무는 강력한 다중 에이전트 시스템을 설계, 연결 및 조율하는 능력을 테스트합니다.

학습할 내용
- 분리된 툴링 생태계 설계: 독립적인 마이크로서비스 기반 MCP 툴 서버 세트를 설계하고 배포합니다. 확장 가능하고 유지 관리 가능하며 안전한 에이전트 시스템을 만드는 데 이 기본 계층이 왜 중요한지 알아보게 될 것입니다.
- 고급 에이전트 워크플로 마스터하기: 단일 에이전트를 넘어 전문가 "패밀리어" 군단을 구축하세요. 순차적, 병렬적, 루프적 등 핵심적인 ADK 워크플로 패턴을 익히고, 적절한 작업에 적합한 패턴을 선택하기 위한 아키텍처 원칙을 배우게 됩니다.
- 지능형 오케스트레이터 구현: 간단한 에이전트 빌더에서 진정한 시스템 아키텍트로 거듭나세요. 에이전트 간 (A2A) 프로토콜을 사용하여 복잡한 작업을 전문 Familiar에게 위임하는 마스터 오케스트레이션 에이전트를 구성하여 진정한 멀티 에이전트 시스템을 만듭니다.
- 프롬프트가 아닌 코드로 규칙 적용: 참여에 대한 상태 저장 규칙을 적용하여 더 안정적이고 예측 가능한 에이전트를 빌드하는 방법을 알아봅니다. ADK의 강력한 플러그인 및 콜백 시스템을 사용하여 재사용 대기 시간 타이머와 같은 실제 제약 조건을 관리하는 맞춤 로직을 구현합니다.
- 에이전트 상태 및 메모리 관리: 에이전트가 학습하고 기억할 수 있도록 지원합니다. 단기 대화 상태와 장기 지속 메모리를 모두 관리하여 더 지능적이고 컨텍스트를 인식하는 상호작용을 만드는 기법을 살펴봅니다.
- 엔드 투 엔드 클라우드 배포 실행: 전체 멀티 에이전트 시스템을 로컬 프로토타입에서 프로덕션 등급 현실로 전환합니다. 에이전트와 오케스트레이터를 컨테이너화하고 Google Cloud Run에 확장 가능하고 독립적인 마이크로서비스 모음으로 배포하는 방법을 알아봅니다.
3. 소환의 원 그리기
소환사님, 환영합니다. 단 하나의 패밀리어를 소환하고, 어떤 계약을 체결하기 전에, 내가 서 있는 땅을 준비해야 합니다. 길들여지지 않은 환경은 혼란을 초래합니다. 숙련된 소환사는 신성하고 권능이 부여된 공간에서만 활동합니다. 첫 번째 작업은 소환진을 그리는 것입니다. 필요한 클라우드 서비스를 활성화하는 힘의 룬을 새기고 작업을 안내할 고대 청사진을 획득해야 합니다. 소환사의 힘은 세심한 준비에서 비롯됩니다.
👉 Google Cloud 콘솔 상단의 Cloud Shell 활성화를 클릭합니다 (Cloud Shell 창 상단에 있는 터미널 모양 아이콘입니다).

👉 '편집기 열기' 버튼 (연필이 있는 열린 폴더 모양)을 클릭합니다. 그러면 창에 Cloud Shell 코드 편집기가 열립니다. 왼쪽에 파일 탐색기가 표시됩니다. 
👉클라우드 IDE에서 터미널을 엽니다. 
👉💻 터미널에서 다음 명령을 사용하여 이미 인증되었고 프로젝트가 프로젝트 ID로 설정되었는지 확인하세요.
gcloud auth list
👉💻 GitHub에서 부트스트랩 프로젝트를 복제합니다.
git clone https://github.com/weimeilin79/agentverse-architect
chmod +x ~/agentverse-architect/init.sh
chmod +x ~/agentverse-architect/set_env.sh
chmod +x ~/agentverse-architect/prepare.sh
chmod +x ~/agentverse-architect/data_setup.sh
git clone https://github.com/weimeilin79/agentverse-dungeon.git
chmod +x ~/agentverse-dungeon/run_cloudbuild.sh
chmod +x ~/agentverse-dungeon/start.sh
👉💻 프로젝트 디렉터리에서 설정 스크립트를 실행합니다.
⚠️ 프로젝트 ID에 대한 참고 사항: 스크립트는 무작위로 생성된 기본 프로젝트 ID를 제안합니다. Enter를 눌러 기본값을 적용할 수 있습니다.
하지만 특정 새 프로젝트를 만들고 싶다면 스크립트에서 메시지가 표시될 때 원하는 프로젝트 ID를 입력하면 됩니다.
cd ~/agentverse-architect
./init.sh
스크립트가 나머지 설정 프로세스를 자동으로 처리합니다.
👉 완료 후 중요 단계: 스크립트가 완료되면 Google Cloud Console에 올바른 프로젝트가 표시되는지 확인해야 합니다.
- console.cloud.google.com으로 이동합니다.
- 페이지 상단의 프로젝트 선택기 드롭다운을 클릭합니다.
- "전체" 탭을 클릭합니다 (새로운 프로젝트가 아직 "최근"에 나타나지 않을 수 있음).
init.sh단계에서 구성한 프로젝트 ID를 선택하세요.

👉💻 필요한 프로젝트 ID를 설정하세요:
gcloud config set project $(cat ~/project_id.txt) --quiet
👉💻 다음 명령어를 실행하여 필요한 Google Cloud API를 사용 설정합니다.
gcloud services enable \
sqladmin.googleapis.com \
storage.googleapis.com \
aiplatform.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
iam.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
secretmanager.googleapis.com
👉💻 아직 agentverse-repo라는 Artifact Registry 저장소를 만들지 않은 경우 다음 명령어를 실행하여 만드세요. (동일한 프로젝트에 배포된 다른 클래스가 있는 경우 이 단계를 건너뛰세요)
. ~/agentverse-architect/set_env.sh
gcloud artifacts repositories create $REPO_NAME \
--repository-format=docker \
--location=$REGION \
--description="Repository for Agentverse agents"
권한 설정
👉💻 터미널에서 다음 명령어를 실행하여 필요한 권한을 부여합니다.
. ~/agentverse-architect/set_env.sh
# --- Grant Core Data Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/storage.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
# --- Grant Deployment & Execution Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudbuild.builds.editor"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/artifactregistry.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/run.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountUser"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.logWriter"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
--role="roles/monitoring.metricWriter"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
--role="roles/secretmanager.secretAccessor"
👉💻 교육을 시작하면 최종 과제가 준비됩니다. 다음 명령어를 사용하면 혼란스러운 스태틱에서 스펙터가 소환되어 최종 테스트를 위한 보스가 생성됩니다.
. ~/agentverse-architect/set_env.sh
cd ~/agentverse-dungeon
./run_cloudbuild.sh
cd ~/agentverse-architect
👉💻 마지막으로 prepare.sh 스크립트를 실행하여 초기 설정 작업을 실행합니다.
. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/
./prepare.sh
소환사님, 수고하셨습니다. 원이 완성되고 계약이 체결됩니다. 이제 땅이 신성해져 엄청난 힘을 전달할 준비가 되었습니다. 다음 시험에서는 패밀리어가 힘을 얻는 근원인 원소 글꼴을 만들 것입니다.
4. 원소 글꼴 제작: 분리된 툴링 생태계
전장이 준비되고 소환진이 그려지고 주변 마나가 지글거립니다. 이제 소환사로서 첫 번째 진정한 행동을 할 때입니다. 바로 패밀리어가 힘을 얻는 힘의 원천을 만드는 것입니다. 이 의식은 세 부분으로 나뉘며, 각 부분은 특정 종류의 힘의 안정적이고 독립적인 원천인 원소 서체를 각성시킵니다. 세 가지 글꼴이 모두 활성화되어야 소환이라는 더 복잡한 작업을 시작할 수 있습니다.

설계자 참고사항: 모델 컨텍스트 프로토콜 (MCP) 서버는 최신 에이전트 시스템의 기본 구성요소로, 에이전트가 원격 도구를 검색하고 사용할 수 있도록 지원하는 표준화된 통신 브리지 역할을 합니다. Google의 도구 생태계에서는 각각 중요한 아키텍처 패턴을 나타내는 두 가지 유형의 MCP 서버를 설계할 예정입니다. 데이터베이스에 연결하기 위해 데이터베이스 도구 상자를 사용하여 선언적 접근 방식을 사용하고 간단한 구성 파일에서 도구를 정의합니다. 이 패턴은 구조화된 데이터 액세스를 노출하는 데 매우 효율적이고 안전합니다. 하지만 맞춤 비즈니스 로직을 구현하거나 외부 서드 파티 API를 호출해야 하는 경우 명령형 접근 방식을 사용하여 코드에서 서버의 로직을 단계별로 작성합니다. 이를 통해 궁극적인 제어와 유연성을 제공하여 간단하고 재사용 가능한 도구 뒤에 복잡한 작업을 캡슐화할 수 있습니다. 마스터 설계자는 두 패턴을 모두 이해하여 각 구성요소에 적합한 접근 방식을 선택하고 강력하고 안전하며 확장 가능한 도구 기반을 만들어야 합니다.

Nexus of Whispers 활성화 (외부 API MCP 서버)
현명한 소환사는 모든 힘이 자신의 영역에서 비롯되는 것은 아니라는 것을 알고 있습니다. 때로는 혼란스러운 외부 에너지원을 효과적으로 활용할 수 있습니다. 속삭임의 연결고리는 이러한 힘으로 향하는 관문입니다.

이미 활성화되어 있는 서비스가 있으며, 외부 전원으로 작동하며 두 개의 원시 주문 엔드포인트인 /cryosea_shatter와 /moonlit_cascade를 제공합니다.
설계자 참고사항: 서버의 로직을 단계별로 명시적으로 정의하는 명령형 접근 방식을 사용합니다. 이를 통해 훨씬 더 많은 제어 기능과 유연성을 확보할 수 있으며, 이는 도구에서 다른 API를 호출하는 등 간단한 SQL 쿼리를 실행하는 것 이상의 작업을 수행해야 할 때 필수적입니다. 두 패턴을 모두 이해하는 것은 에이전트 설계자에게 중요한 기술입니다.
👉✏️ ~/agentverse-architect/mcp-servers/api/main.py 디렉터리로 이동하여 #REPLACE-MAGIC-CORE를 다음 코드로 바꿉니다 .
def cryosea_shatter() -> str:
"""Channels immense frost energy from an external power source, the Nexus of Whispers, to unleash the Cryosea Shatter spell."""
try:
response = requests.post(f"{API_SERVER_URL}/cryosea_shatter")
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
data = response.json()
# Thematic Success Message
return f"A connection to the Nexus is established! A surge of frost energy manifests as Cryosea Shatter, dealing {data.get('damage_points')} damage."
except requests.exceptions.RequestException as e:
# Thematic Error Message
return f"The connection to the external power source wavers and fails. The Cryosea Shatter spell fizzles. Reason: {e}"
def moonlit_cascade() -> str:
"""Draws mystical power from an external energy source, the Nexus of Whispers, to invoke the Moonlit Cascade spell."""
try:
response = requests.post(f"{API_SERVER_URL}/moonlit_cascade")
response.raise_for_status()
data = response.json()
# Thematic Success Message
return f"The Nexus answers the call! A cascade of pure moonlight erupts from the external source, dealing {data.get('damage_points')} damage."
except requests.exceptions.RequestException as e:
# Thematic Error Message
return f"The connection to the external power source wavers and fails. The Moonlit Cascade spell fizzles. Reason: {e}"
스크립트의 핵심은 일반적인 Python 함수입니다. 실제 작업이 이루어지는 곳입니다.
👉✏️ 같은 파일에서 ~/agentverse-architect/mcp-servers/api/main.py REPLACE #REPLACE-Runes of Communication를 다음 코드로 바꾸세요.
@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
"""MCP handler to list available tools."""
# Convert the ADK tool's definition to MCP format
schema_cryosea_shatter = adk_to_mcp_tool_type(cryosea_shatterTool)
schema_moonlit_cascade = adk_to_mcp_tool_type(moonlit_cascadeTool)
print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {schema_cryosea_shatter.name} and {schema_moonlit_cascade.name}")
return [schema_cryosea_shatter,schema_moonlit_cascade]
@app.call_tool()
async def call_tool(
name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
"""MCP handler to execute a tool call."""
print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")
# Look up the tool by name in our dictionary
tool_to_call = available_tools.get(name)
if tool_to_call:
try:
adk_response = await tool_to_call.run_async(
args=arguments,
tool_context=None, # No ADK context available here
)
print(f"MCP Server: ADK tool '{name}' executed successfully.")
response_text = json.dumps(adk_response, indent=2)
return [mcp_types.TextContent(type="text", text=response_text)]
except Exception as e:
print(f"MCP Server: Error executing ADK tool '{name}': {e}")
# Creating a proper MCP error response might be more robust
error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
return [mcp_types.TextContent(type="text", text=error_text)]
else:
# Handle calls to unknown tools
print(f"MCP Server: Tool '{name}' not found.")
error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
return [mcp_types.TextContent(type="text", text=error_text)]
@app.list_tools()(핸드셰이크): 이 함수는 서버의 인사입니다. 새로운 에이전트가 연결되면 먼저 이 엔드포인트를 호출하여 "무엇을 할 수 있나요?"라고 묻습니다. 우리 코드는 adk_to_mcp_tool_type을 사용하여 범용 MCP 형식으로 변환된 모든 사용 가능한 도구 목록을 반환합니다. -@app.call_tool()(명령): 이 기능은 핵심 기능입니다. 에이전트가 도구를 사용하기로 결정하면 도구 이름과 인수를 사용하여 이 엔드포인트에 요청을 전송합니다. 우리 코드는 available_tools "주문서"에서 도구를 찾고, run_async로 실행하고, 표준 MCP 형식으로 결과를 반환합니다.
나중에 배포하겠습니다.
비전의 대장간 점화 (일반 기능 MCP 서버)
모든 힘이 오래된 책이나 먼 속삭임에서 나오는 것은 아닙니다. 때때로 소환사는 순수한 의지와 순수한 논리로 자신만의 마법을 만들어내야 합니다. Arcane Forge는 이러한 힘의 원천입니다. 즉, 상태를 저장하지 않고 일반적인 용도로 사용할 수 있는 유틸리티 기능을 제공하는 서버입니다.

건축가의 메모: 이는 또 다른 건축 패턴입니다. 기존 시스템에 연결하는 것은 흔한 일이지만, 고유한 비즈니스 규칙과 논리를 구현해야 하는 경우가 많습니다. 이처럼 전담 "기능"이나 "유틸리티" 도구를 만드는 것이 가장 좋은 방법입니다. 사용자 정의 논리를 캡슐화하고 생태계의 모든 에이전트에서 재사용할 수 있도록 하며 데이터 소스와 외부 통합에서 분리된 상태를 유지합니다.
👀 Google Cloud IDE에서 ~/agentverse-architect/mcp-servers/general/main.py 파일을 살펴보세요. Nexus에서와 마찬가지로 이 맞춤형 Font of Power를 구축하기 위해 mcp.server라는 필수 접근 방식이 사용된다는 것을 알 수 있습니다.
마스터 Cloud Build 파이프라인 만들기
이제 mcp-servers 디렉터리 내에 cloudbuild.yaml 파일을 생성합니다. 이 파일은 두 서비스의 빌드와 배포를 조율합니다.
👉💻 ~/agentverse-architect/mcp-servers 디렉터리에서 다음 명령을 실행합니다.
cd ~/agentverse-architect/mcp-servers
source ~/agentverse-architect/set_env.sh
echo "The API URL is: $API_SERVER_URL"
# Submit the Cloud Build job from the parent directory
gcloud builds submit . \
--config=cloudbuild.yaml \
--substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_API_SERVER_URL="$API_SERVER_URL"
모든 배포가 완료될 때까지 기다리세요.
👉 Cloud Run 콘솔로 이동하여 배포를 확인할 수 있습니다. 아래에 표시된 것처럼 두 개의 새로운 MCP 서버 인스턴스가 실행 중인 것을 볼 수 있습니다. 
지식의 도서관을 깨우다 (데이터베이스 도구 상자 MCP 서버)
다음 글꼴은 Cloud SQL 데이터베이스에 직접 연결되는 Librarium of Knowledge입니다.

설계자 참고: 여기서는 최신 선언적 데이터베이스 도구 상자를 사용합니다. 이는 YAML 구성 파일에서 데이터 소스와 도구를 정의하는 강력한 접근 방식입니다. 툴박스는 서버를 만들고 실행하는 복잡한 작업을 처리하므로 작성하고 유지해야 하는 맞춤 코드의 양이 줄어듭니다.
이제 모든 중요 정보를 보관할 Cloud SQL 데이터베이스인 "소환사의 도서관"을 구축할 시간입니다. 설정 스크립트를 사용하여 이 작업을 자동으로 처리합니다.
👉💻 먼저, 데이터베이스를 설정하고 터미널에서 다음 명령을 실행하세요.
source ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect
./data_setup.sh
스크립트가 완료되면 데이터베이스가 채워지고 원소 피해 데이터를 사용할 수 있게 됩니다. 이제 그리무아르의 내용을 직접 확인할 수 있습니다.
👉 먼저 새 브라우저 탭에서 이 직접 링크를 열어 데이터베이스의 Cloud SQL Studio로 이동합니다.
https://console.cloud.google.com/sql/instances/summoner-librarium-db

👉 왼쪽의 로그인 창에서 드롭다운 메뉴에서 familiar_grimoire 데이터베이스를 선택합니다.
👉 사용자로 summoner을 입력하고 비밀번호로 1234qwer을 입력한 다음 인증을 클릭합니다.
👉📜 연결되면 아직 열려 있지 않으면 새 쿼리 편집기 탭을 엽니다. 기록된 원소 피해 데이터를 보려면 다음 SQL 쿼리를 붙여넣고 실행하세요.
SELECT * FROM
"public"."abilities"
이제 열과 행이 채워진 abilities 테이블이 표시되어 Grimoire가 준비되었음을 확인할 수 있습니다. 
ToolBox MCP 서버 구성
tools.yaml 구성 파일은 서버의 청사진 역할을 하며 데이터베이스 도구 상자에 데이터베이스에 연결하는 방법과 도구로 노출할 SQL 쿼리를 정확하게 알려줍니다.
소스: 이 섹션에서는 데이터와의 연결을 정의합니다.
- summoner-librarium:: 연결에 지정된 논리적 이름입니다.
- kind: cloud-sql-postgres: 툴박스에 PostgreSQL용 Cloud SQL을 위해 특별히 설계된 내장 보안 커넥터를 사용하도록 지시합니다.
- 프로젝트, 지역, 인스턴스 등: 이는 prepare.sh 스크립트에서 생성한 Cloud SQL 인스턴스의 정확한 좌표이며, 이를 통해 Toolbox에서 Librarium을 찾을 수 있는 위치를 알 수 있습니다.
👉✏️ tools.yaml에서 ~/agentverse-architect/mcp-servers/db-toolbox로 이동하여 #REPLACE-Source를 다음으로 바꾸세요.
sources:
# This section defines the connection to our Cloud SQL for PostgreSQL database.
summoner-librarium:
kind: cloud-sql-postgres
project: "YOUR_PROJECT_ID"
region: "us-central1"
instance: "summoner-librarium-db"
database: "familiar_grimoire"
user: "summoner"
password: "1234qwer"
👉✏️ 🚨🚨REPLACE
YOUR_PROJECT_ID
프로젝트 ID로 변경하세요.
tools: 이 섹션에서는 서버에서 제공하는 실제 기능 또는 함수를 정의합니다.
- lookup-available-ability:: 이것은 첫 번째 도구의 이름입니다.
- 종류: postgres-sql: 이것은 도구 상자에 이 도구의 작업이 SQL 문을 실행하는 것이라고 알려줍니다.
- 출처: summoner-librarium: 이는 도구를 소스 블록에서 정의한 연결에 연결합니다. 이를 통해 도구는 어떤 데이터베이스에 대해 쿼리를 실행해야 할지 알아냅니다.
- 설명 및 매개변수: 이는 언어 모델에 노출될 정보입니다. 설명은 에이전트에게 도구를 언제 사용해야 하는지 알려주고, 매개변수는 도구에 필요한 입력을 정의합니다. 이는 에이전트의 기능 호출 기능을 활성화하는 데 중요합니다.
- statement: 실행할 원시 SQL 쿼리입니다. $1은 에이전트가 제공한 familiar_name 매개변수가 안전하게 삽입되는 안전한 플레이스홀더입니다.
👉✏️ tools.yaml 파일의 동일한 ~/agentverse-architect/mcp-servers/db-toolbox에서 #REPLACE-tools을 다음으로 바꿉니다.
tools:
# This tool replaces the need for a custom Python function.
lookup-available-ability:
kind: postgres-sql
source: summoner-librarium
description: "Looks up all known abilities and their damage for a given familiar from the Grimoire."
parameters:
- name: familiar_name
type: string
description: "The name of the familiar to search for (e.g., 'Fire Elemental')."
statement: |
SELECT ability_name, damage_points FROM abilities WHERE familiar_name = $1;
# This tool also replaces a custom Python function.
ability-damage:
kind: postgres-sql
source: summoner-librarium
description: "Finds the base damage points for a specific ability by its name."
parameters:
- name: ability_name
type: string
description: "The exact name of the ability to look up (e.g., 'inferno_resonance')."
statement: |
SELECT damage_points FROM abilities WHERE ability_name = $1;
toolsets: 이 섹션에서는 개별 도구를 그룹화합니다.
- summoner-librarium:: 소스와 동일한 이름의 도구 세트를 만들고 있습니다. 나중에 진단 에이전트가 연결되면 단일의 효율적인 명령으로 소환사-서적관 도구 세트의 모든 도구를 로드하도록 요청할 수 있습니다.
👉✏️ tools.yaml 파일의 동일한 ~/agentverse-architect/mcp-servers/db-toolbox에서 #REPLACE-toolsets을 다음으로 바꿉니다.
toolsets:
summoner-librarium:
- lookup-available-ability
- ability-damage
Librarium 배포
이제 Librarium을 배포합니다. 컨테이너를 직접 빌드하는 대신 Google에서 사전 빌드한 공식 컨테이너 이미지를 사용하고 Secret Manager를 사용하여 tools.yaml 구성을 안전하게 제공합니다. 이는 보안 및 유지관리 가능성을 위한 권장사항입니다.
👉💻 tools.yaml 파일에서 보안 비밀 만들기
cd ~/agentverse-architect/mcp-servers/db-toolbox
gcloud secrets create tools --data-file=tools.yaml
👉💻 공식 도구 상자 컨테이너를 Cloud Run에 배포합니다.
cd ~/agentverse-architect/mcp-servers/db-toolbox
. ~/agentverse-architect/set_env.sh
export TOOLBOX_IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$TOOLBOX_VERSION
echo "TOOLBOX_IMAGE is $TOOLBOX_IMAGE"
gcloud run deploy toolbox \
--image $TOOLBOX_IMAGE \
--region $REGION \
--set-secrets "/app/tools.yaml=tools:latest" \
--labels="dev-tutorial-codelab=agentverse" \
--args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
--allow-unauthenticated \
--min-instances 1
--set-secrets: 이 명령어는 실행 중인 컨테이너 내에 tools.yaml이라는 파일로 도구 보안 비밀을 안전하게 마운트합니다.--args: 마운트된 보안 비밀 파일을 구성으로 사용하도록 툴박스 컨테이너에 지시합니다.
👉 툴박스가 성공적으로 배포되었는지 확인하려면 Cloud Run 콘솔로 이동합니다. 아래 이미지와 같이 summoner-toolbox 서비스가 녹색 체크표시와 함께 표시되어 올바르게 실행되고 있음을 나타냅니다. 
업데이트를 잊은 경우
YOUR_PROJECT_ID
다음 명령어를 사용하여 tools.yaml의 새 버전을 시크릿에 추가하고 다시 배포할 수 있습니다.
gcloud secrets versions add tools --data-file=tools.yaml
이제 Librarium of Knowledge(Database ToolBox MCP Server)가 활성화되어 클라우드에서 액세스할 수 있습니다. 이 MCP 서버는 원하는 것을 설명하는 선언적 설계를 사용하고 도구 상자는 서버를 빌드했습니다.
검증: 견습생의 시험
👉💻 이제 진단 에이전트로 완전한 클라우드 네이티브 도구 생태계를 테스트합니다.
cd ~/agentverse-architect/
python -m venv env
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/mcp-servers
pip install -r diagnose/requirements.txt
. ~/agentverse-architect/set_env.sh
adk run diagnose
👉💻 cmd 줄 테스트 도구에서 다음 세 가지 글꼴을 모두 테스트합니다.
Look up the entry for "inferno_lash". What is its base power level?
The enemy is vulnerable to frost! Channel power from the Nexus and cast Cryosea Shatter.
Take a fire spell with a base power of 15 and use the Arcane Forge to multiply it with Inferno Resonance.

소환사님, 축하드립니다. 이제 세 가지 기본 글꼴이 활성화되고, 독립적으로 배포되며, 전 세계에서 액세스할 수 있어 에이전트 군단의 흔들리지 않는 기반을 형성합니다. 종료하려면 Ctrl+C을 누르세요.
게이머가 아닌 분들을 위해
5. 패밀리어 소환: 핵심 도메인 워크플로
원소 글꼴은 날것 그대로의 길들여지지 않은 힘을 내뿜으며 만들어집니다. 하지만 형태가 없는 힘은 혼란을 야기합니다. 진정한 소환사는 순수한 에너지를 휘두르는 데 그치지 않고 의지, 목적, 전문화된 형태를 부여합니다. 이제 전원 공급 장치를 만드는 것을 넘어 진정한 작업인 첫 번째 패밀리어를 소환할 시간입니다.
소환하는 각 패밀리어는 특정 전투 교리에 묶인 충성스러운 하인인 자율 에이전트가 됩니다. 이들은 제너럴리스트가 아니라 강력한 단일 전략의 마스터입니다. 한 명은 정확한 원투 펀치 콤보의 달인이 될 것입니다. 다른 하나는 동시 다각적 공격으로 적을 압도합니다. 세 번째는 목표가 무너질 때까지 압력을 가하는 끊임없는 공성 엔진입니다.

MCP 서버에서 제공하는 프로세스, 비즈니스 로직, 작업을 전문적이고 자율적인 워크플로 에이전트에 캡슐화합니다. 각 에이전트는 기능을 수행하는 데 필요한 특정 MCP 도구 서버에만 액세스할 수 있도록 권한이 부여되어 정의된 '운영 지역'을 갖게 됩니다. 이 실습에서는 적절한 작업에 적합한 에이전트 유형을 선택하는 방법을 보여줍니다.

이 모듈에서는 ADK의 강력한 워크플로 에이전트를 사용하여 이러한 전략을 실현하는 방법을 알아봅니다. SequentialAgent, ParallelAgent 또는 LoopAgent의 아키텍처 선택은 단순한 기술적 세부사항이 아니라 Familiar의 본질이자 전장에서의 힘의 핵심이라는 점을 알게 됩니다.
성역을 준비합니다. 진짜 소환이 곧 시작됩니다.
Fire Elemental Familiar 소환 (순차적 워크플로)
불 원소 정령의 공격은 정확한 2단 콤보입니다. 타겟팅된 공격 후 강력한 점화가 이어집니다. 이를 위해서는 엄격한 순서의 작업이 필요합니다.

개념: SequentialAgent는 이 작업에 완벽한 도구입니다. 일련의 하위 에이전트가 순차적으로 실행되어 이전 단계의 결과를 다음 단계로 전달합니다.
과제('증폭된 타격' 콤보):
- 1단계: 에이전트는 먼저 Librarium에 문의하여 특정 화염 능력의 기본 피해를 확인합니다.
- 2단계: 그런 다음 해당 피해 값을 가져와서 신비로운 대장간을 통해 전달하여 지옥불 공명을 사용하여 위력을 곱합니다.
먼저 이전 모듈에서 배포한 Familiar와 MCP 서버('Elemental Fonts') 간의 연결을 설정합니다.
👉✏️ ~/agentverse-architect/agent/fire/agent.py 파일에서 #REPLACE-setup-MCP을 다음 코드로 바꿉니다.
toolbox = ToolboxSyncClient(DB_TOOLS_URL)
toolDB = toolbox.load_toolset('summoner-librarium')
toolFunction = MCPToolset(
connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)
다음으로 전문 '작업자' 에이전트를 만듭니다. 각각의 목적은 좁고 명확하게 정의되어 있으며 하나의 특정 도구 세트에만 액세스 권한이 부여되어 자체 '운영 영역'으로 제한됩니다.
👉✏️ ~/agentverse-architect/agent/fire/agent.py 파일에서 #REPLACE-worker-agents을 다음 코드로 바꿉니다 .
scout_agent = LlmAgent(
model='gemini-2.5-flash',
name='librarian_agent',
instruction="""
Your only task is to find all the available abilities,
You want to ALWAYS use 'Fire Elemental' as your familiar's name.
Randomly pick one if you see multiple availabilities
and the base damage of the ability by calling the 'ability_damage' tool.
""",
tools=toolDB
)
amplifier_agent = LlmAgent(
model='gemini-2.5-flash',
name='amplifier_agent',
instruction="""
You are the Voice of the Fire Familiar, a powerful being who unleashes the final, devastating attack.
You will receive the base damage value from the previous step.
Your mission is to:
1. Take the incoming base damage number and amplify it using the `inferno_resonance` tool.
2. Once the tool returns the final, multiplied damage, you must not simply state the result.
3. Instead, you MUST craft a final, epic battle cry describing the attack.
Your description should be vivid and powerful, culminating in the final damage number.
Example: If the tool returns a final damage of 120, your response could be:
"The runes glow white-hot! I channel the amplified energy... unleashing a SUPERNOVA for 120 damage!"
""",
tools=[toolFunction],
)
이러한 에이전트는 모듈식 재사용 가능한 구성요소입니다. 이론적으로는 데이터베이스를 쿼리해야 하는 완전히 다른 워크플로에서 이 scout_agent를 사용할 수 있습니다. 책임을 분리하면 유연한 빌딩 블록을 만들 수 있습니다. 이는 마이크로서비스 및 구성요소 기반 설계의 핵심 원칙입니다.
다음으로 워크플로를 조립하겠습니다. 여기서 구성의 마법이 일어납니다. SequentialAgent은 전문가 구성요소가 조립되는 방식과 상호작용하는 방식을 정의하는 '마스터 계획'입니다.
👉✏️ ~/agentverse-architect/agent/fire/agent.py 파일에서 #REPLACE-sequential-agent을 다음 코드로 바꿉니다 .
root_agent = SequentialAgent(
name='fire_elemental_familiar',
sub_agents=[scout_agent, amplifier_agent],
)
👉💻 불 원소를 테스트하려면 다음 명령어를 실행하여 ADK 개발 UI를 실행하세요.
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo DB MCP Server: $DB_TOOLS_URL
echo API MCP Server: $API_TOOLS_URL
echo General MCP Server: $FUNCTION_TOOLS_URL
adk web
명령어를 실행하면 터미널에 ADK 웹 서버가 시작되었음을 나타내는 다음과 유사한 출력이 표시됩니다.
+-----------------------------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://localhost:8000. |
+-----------------------------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
👉 다음으로 브라우저에서 ADK 개발 UI에 액세스하려면 다음 단계를 따르세요.
Cloud Shell 툴바 (일반적으로 오른쪽 상단)의 웹 미리보기 아이콘 (눈 또는 화살표가 있는 정사각형 모양)에서 포트 변경을 선택합니다. 팝업 창에서 포트를 8000으로 설정하고 '변경 및 미리보기'를 클릭합니다. 그러면 Cloud Shell에서 ADK 개발 UI가 표시된 새 브라우저 탭 또는 창이 열립니다.

👉 소환 의식이 완료되었으며 이제 에이전트가 실행됩니다. 브라우저의 ADK 개발 UI는 Familiar에 직접 연결됩니다.
- 타겟 선택: UI 상단의 드롭다운 메뉴에서
fire를 선택합니다. 이제 이 특정 항목에 의지를 집중하고 있습니다. - 명령 내리기: 오른쪽의 채팅 패널에서 Familiar에게 명령을 내릴 수 있습니다.

👉 테스트 프롬프트:
Prepare an amplified strike using the 'inferno_lash' ability.

에이전트가 먼저 "스카우트"를 호출해 기본 데미지를 조사한 다음, "앰프"를 호출해 데미지를 증폭하고 최후의 강력한 타격을 가하는 모습을 보실 수 있습니다.
👉💻 소환이 완료되면 Cloud Shell Editor 터미널로 돌아가서 Ctrl+C를 눌러 ADK Dev UI를 중지합니다.
Water Elemental 패밀리어 소환 (병렬 워크플로)
물의 정령 패밀리어는 대상을 거대하고 다각적인 공격으로 압도합니다. 모든 방향에서 동시에 공격한 후, 에너지를 합쳐 최후의 파괴적인 타격을 가합니다.

개념: ParallelAgent는 효율성을 극대화하기 위해 여러 개의 독립적인 작업을 동시에 실행하는 데 이상적입니다. 한 번에 여러 공격을 가하는 "집게 공격"입니다. 이렇게 하면 SequentialAgent 내에서 동시 공격이 시작되어 이후 최종 '병합' 단계가 실행됩니다. 이 "fan-out, fan-in" 패턴은 고급 워크플로 디자인의 초석입니다.
작업("Tidal Clash" 콤보): 에이전트는 동시에 다음을 수행합니다.
- 작업 A: Nexus의 채널
cryosea_shatter - 작업 B: Nexus에서 채널
moonlit_cascade. - 작업 C: 포지에서
leviathan_surge를 사용하여 원시 전력 생성 - 마지막으로 모든 피해를 합산하고 결합된 공격을 설명합니다.
먼저, 이전 모듈에서 배포한 Familiar와 MCP 서버("Elemental Fonts") 간의 연결을 설정합니다.
👉✏️ 파일 ~/agentverse-architect/agent/water/agent.py에서 #REPLACE-setup-MCP을 다음 코드로 바꾸세요.
toolFAPI = MCPToolset(
connection_params=SseServerParams(url=API_TOOLS_URL, headers={})
)
toolFunction = MCPToolset(
connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)
다음으로, 전문 "근로자" 에이전트를 생성하겠습니다. 각 도구에는 좁고 명확하게 정의된 목적이 부여되며, 단 하나의 특정 도구 세트에만 액세스할 수 있도록 허용되어 자체 "운영 영역"으로 제한됩니다.
👉✏️ 파일 ~/agentverse-architect/agent/water/agent.py에서 #REPLACE-worker-agents을 다음 코드로 바꾸세요.
nexus_channeler = LlmAgent(
model='gemini-2.5-flash',
name='librarian_agent',
instruction="""
You are a Channeler of the Nexus. Your sole purpose is to invoke the
`cryosea_shatter` and `moonlit_cascade` spells by calling their respective tools.
Report the result of each spell cast clearly and concisely.
""",
tools=[toolFAPI]
)
forge_channeler = LlmAgent(
model='gemini-2.5-flash',
name='amplifier_agent',
instruction="""
You are a Channeler of the Arcane Forge. Your only task is to invoke the
`leviathan_surge` spell. You MUST call it with a `base_water_damage` of 20.
Report the result clearly.
""",
tools=[toolFunction],
)
power_merger = LlmAgent(
model='gemini-2.5-flash',
name='power_merger',
instruction="""
You are the Power Merger, a master elementalist who combines raw magical
energies into a single, devastating final attack.
You will receive a block of text containing the results from a simultaneous
assault by other Familiars.
You MUST follow these steps precisely:
1. **Analyze the Input:** Carefully read the entire text provided from the previous step.
2. **Extract All Damage:** Identify and extract every single damage number reported in the text.
3. **Calculate Total Damage:** Sum all of the extracted numbers to calculate the total combined damage.
4. **Describe the Final Attack:** Create a vivid, thematic description of a massive,
combined water and ice attack that uses the power of Cryosea Shatter and Leviathan's Surge.
5. **Report the Result:** Conclude your response by clearly stating the final, total damage of your combined attack.
Example: If the input is "...dealt 55 damage. ...dealt 60 damage.", you will find 55 and 60,
calculate the total as 115, and then describe the epic final attack, ending with "for a total of 115 damage!"
""",
tools=[toolFunction],
)
다음으로 워크플로를 어셈블합니다. 여기에서 구성의 마법이 일어납니다. ParallelAgent과 SequentialAgent은 전문가 구성요소가 조립되는 방식과 'Tidal Clash' 콤보를 형성하기 위해 상호작용하는 방식을 정의하는 '마스터 계획'입니다.
👉✏️ 파일 ~/agentverse-architect/agent/water/agent.py에서 #REPLACE-parallel-agent을 다음 코드로 바꾸세요.
channel_agent = ParallelAgent(
name='channel_agent',
sub_agents=[nexus_channeler, forge_channeler],
)
root_agent = SequentialAgent(
name="water_elemental_familiar",
# Run parallel research first, then merge
sub_agents=[channel_agent, power_merger],
description="A powerful water familiar that unleashes multiple attacks at once and then combines their power for a final strike."
)
👉💻 Water Elemental을 테스트하려면 다음 명령을 실행하여 ADK Dev UI를 시작하세요.
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo DB MCP Server: $DB_TOOLS_URL
echo API MCP Server: $API_TOOLS_URL
echo General MCP Server: $FUNCTION_TOOLS_URL
adk web
👉 소환 의식이 완료되었으며 이제 에이전트가 실행됩니다. 브라우저의 ADK 개발 UI는 Familiar에 직접 연결됩니다.
- UI 상단의 드롭다운 메뉴에서 익숙한 물을 선택합니다. 이제 이 특정 항목에 의지를 집중하고 있습니다.
- 명령 내리기: 오른쪽의 채팅 패널에서 Familiar에게 명령을 내릴 수 있습니다.
👉 테스트 프롬프트:
Unleash a tidal wave of power!

👉💻 소환이 완료되면 Cloud Shell 편집기 터미널로 돌아가 Ctrl+C를 눌러 ADK 개발 UI를 중지합니다.
Earth Elemental Familiar (루프 워크플로) 소환
대지의 정령은 끊임없이 압력을 가하는 존재입니다. 단 한 번의 타격으로 적을 물리치는 것이 아니라, 꾸준히 힘을 축적하고, 대상의 방어력이 무너질 때까지 계속해서 힘을 가합니다.

개념: LoopAgent는 이러한 종류의 반복적인 '공성 엔진' 작업을 위해 설계되었습니다. 목표가 달성될 때까지 각 사이클이 끝날 때마다 조건을 확인하면서 sub-agents를 반복적으로 실행합니다. 또한 루프의 진행 상황에 따라 최종 메시지를 조정할 수 있습니다.
임무("포위 파괴자" 공격):
- Familiar는 반복적으로 seismic_charge를 호출하여 에너지를 축적합니다.
- 최대 3회까지 계속 충전됩니다.
- 마지막 충전 시 누적된 힘을 엄청난 속도로 방출합니다.
먼저 루프의 각 반복 내에서 단계를 정의하는 재사용 가능한 구성요소를 만듭니다. charging_agent는 에너지를 축적하고 check_agent는 상태를 보고하며 마지막 턴에서 메시지를 동적으로 변경합니다.
먼저 이전 모듈에서 배포한 Familiar와 MCP 서버('Elemental Fonts') 간의 연결을 설정합니다.
👉✏️ 파일 ~/agentverse-architect/agent/earth/agent.py에서 #REPLACE-setup-MCP을 다음 코드로 바꾸세요.
toolFunction = MCPToolset(
connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)
👉✏️ ~/agentverse-architect/agent/earth/agent.py 파일에서 #REPLACE-worker-agents을 다음 코드로 바꿉니다.
charging_agent = LlmAgent(
model='gemini-2.5-flash',
name='charging_agent',
instruction="""
Your task is to call the 'seismic_charge' .
You must follow these rules strictly:
1. You will be provided with a 'current_energy' value from the previous step.
**If this value is missing or was not provided, you MUST call the tool with 'current_energy' set to 1.**
This is your primary rule for the first turn.
2. If a 'current_energy' value is provided, you MUST use that exact value in your cal to seismic_charge.
3. Your final response MUST contain ONLY the direct output from the 'seismic_charge' tool.
Do not add any conversational text, introductions, or summaries.
""",
tools=[toolFunction]
)
check_agent = LlmAgent(
model='gemini-2.5-flash',
name='check_agent',
instruction="""
You are the voice of the Earth Elemental, a being of immense, gathering power.
Your sole purpose is to report on the current energy charge and announce the devastating potential of its release.
You MUST follow this rule:
The potential damage upon release is ALWAYS calculated as the `current_energy` from the previous step multiplied by a random number between 80-90. but no more than 300.
Your response should be in character, like a powerful creature speaking.
State both the current energy charge and the total potential damage you can unleash.
Unleash the energy when you reached the last iteration (2nd).
""",
output_key="charging_status"
)
다음으로, 워크플로를 조립해보겠습니다. 바로 여기서 작곡의 마법이 일어납니다. LoopAgent는 전문가 구성요소의 반복 실행을 오케스트레이션하고 루프의 조건을 관리하는 '마스터 계획'입니다.
👉✏️ ~/agentverse-architect/agent/earth/agent.py 파일에서 #REPLACE-loop-agent을 다음 코드로 바꿉니다.
root_agent = LoopAgent(
name="earth_elemental_familiar",
# Run parallel research first, then merge
sub_agents=[
charging_agent,
check_agent
],
max_iterations=2,
description="Coordinates parallel research and synthesizes the results.",
#REPLACE-before_agent-config
)
👉💻 Earth Elemental 테스트: 에이전트 실행
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo DB MCP Server: $DB_TOOLS_URL
echo API MCP Server: $API_TOOLS_URL
echo General MCP Server: $FUNCTION_TOOLS_URL
adk web
👉 소환 의식이 완료되었으며 이제 에이전트가 실행됩니다. 브라우저의 ADK 개발 UI는 Familiar에 직접 연결됩니다.
- 타겟 선택: UI 상단의 드롭다운 메뉴에서 earth를 선택하세요. 이제 당신은 당신의 의지를 이 특정한 존재에 집중하고 있습니다.
- 명령 내리기: 오른쪽의 채팅 패널에서 Familiar에게 명령을 내릴 수 있습니다. 👉 테스트 프롬프트:
Begin the seismic charge, starting from zero

아키텍처적 요점: 이제 귀하의 시스템은 고도로 전문화되고 모듈화된 논리 계층을 갖추게 되었습니다. 비즈니스 프로세스는 캡슐화될 뿐만 아니라, 작업에 가장 효율적인 행동 패턴 (절차적, 동시적 또는 반복적)으로 구현됩니다. 이는 고급 수준의 에이전트 설계를 보여주며, 보안, 효율성, 기능을 향상시킵니다.
소환이 완료되면 Cloud Shell Editor 터미널로 돌아가서 Ctrl+C를 눌러 ADK Dev UI를 중지합니다.
게이머가 아닌 분들을 위해
6. 명령 위치 설정: A2A를 통한 지능형 위임
펫은 강력하지만 독립적입니다. 이들은 전략을 완벽하게 실행하지만 직접적인 명령을 기다립니다. 전문가 집단이 있어도 지휘할 장군이 없으면 소용이 없습니다. 이제 직접 지휘관에서 진정한 오케스트레이터로 승격할 때입니다.

건축가의 참고 사항: 전체 시스템에 대한 단일 지능형 진입점을 만듭니다. 이 소환사 에이전트는 직접 비즈니스 로직을 수행하지 않지만 "마스터 전략가" 역할을 하며 냉각 상태를 분석하고 적절한 전문 패밀리어에게 작업을 위임합니다.

결속의식 (가족을 A2A 서비스로 노출)
표준 에이전트는 한 번에 한 곳에서만 실행할 수 있습니다. 원격 명령을 위해 Familiar를 사용할 수 있도록 하려면 Agent2Agent (A2A) 프로토콜을 사용하여 '바인딩 의식'을 실행해야 합니다.
설계자 참고: Agent-to-Agent (A2A) 프로토콜은 독립형 에이전트를 검색 가능하고 네트워크 주소 지정이 가능한 마이크로서비스로 승격시켜 진정한 '에이전트 사회'를 지원하는 핵심 아키텍처 패턴입니다. A2A를 통해 Familiar를 노출하면 다음과 같은 두 가지 필수 상호 연결 구성요소가 자동으로 생성됩니다.
- 에이전트 카드("무엇"): 이것은 OpenAPI 사양과 같은 공개적이고 기계가 읽을 수 있는 "Spirit Sigil"로, Familiar의 공개 계약 역할을 합니다. 여기에는 에이전트의 이름, 전략적 목적 (지시사항에서 파생됨), 이해하는 명령이 설명되어 있습니다. 이것은 소환사의 달인이 패밀리어를 발견하고 그 능력을 배우기 위해 읽는 내용입니다.
- A2A 서버('위치'): Familiar를 호스팅하고 수신 명령을 수신 대기하는 전용 웹 엔드포인트입니다. 다른 에이전트가 요청을 전송하는 네트워크 주소이며, 이러한 요청이 에이전트 카드에 정의된 계약에 따라 처리되도록 합니다.
이제 우리는 세 명의 패밀리어 모두에게 이 결속 의식을 행할 것입니다.
Fire 👉✏️ ~/agentverse-architect/agent/fire/agent.py 파일을 엽니다. 파일 하단의 #REPLACE - add A2A를 바꿔서 Fire Elemental을 A2A 서비스로 노출합니다.
from agent_to_a2a import to_a2a
if __name__ == "__main__":
import uvicorn
a2a_app = to_a2a(root_agent, port=8080, public_url=PUBLIC_URL)
uvicorn.run(a2a_app, host='0.0.0.0', port=8080)
물과 땅🚨 👉✏️ ~/agentverse-architect/agent/water/agent.py와 ~/agentverse-architect/agent/earth/agent.py에도 똑같은 변경 사항을 적용하여 바인딩합니다.
from agent_to_a2a import to_a2a
if __name__ == "__main__":
import uvicorn
a2a_app = to_a2a(root_agent, port=8080, public_url=PUBLIC_URL)
uvicorn.run(a2a_app, host='0.0.0.0', port=8080)
바운드 패밀리어 배치
👉✏️ 바인딩 의식을 기록했으므로 Cloud Build 파이프라인을 사용하여 세 마리의 패밀리어를 Cloud Run에서 독립적인 컨테이너화된 서버리스 서비스로 생성하고 배포합니다.
. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/agent
gcloud builds submit . \
--config=cloudbuild.yaml \
--substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_DB_TOOLS_URL="$DB_TOOLS_URL",_API_TOOLS_URL="$API_TOOLS_URL",_FUNCTION_TOOLS_URL="$FUNCTION_TOOLS_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"
명령어 가정 (소환사 에이전트 구성)
이제 패밀리어가 바인딩되고 수신 대기 중이므로 진정한 역할인 마스터 소환사로 승급하게 됩니다. 이 에이전트의 능력은 기본 도구를 사용하는 것이 아니라 다른 에이전트를 지휘하는 데서 비롯됩니다. 이 클래스의 도구는 '정령 인장'을 사용하여 발견하고 명령하는 정령 자체입니다.
아키텍트 참고사항: 다음 단계에서는 대규모 분산 시스템에 중요한 아키텍처 패턴인 서비스 검색을 보여줍니다. SummonerAgent에는 Familiars' 코드가 내장되어 있지 않습니다. 대신 네트워크 주소 (URL)가 제공됩니다. 런타임에 공개 에이전트 카드를 가져와 기능을 동적으로 '검색'합니다. 이렇게 하면 강력하고 분리된 시스템이 만들어집니다.
Familiar 서비스를 업데이트, 재배포하거나 완전히 다시 작성할 수 있으며, 네트워크 주소와 목적이 동일하게 유지되는 한 소환사는 아무런 변경 없이 서비스를 명령할 수 있습니다.
먼저, 배치된 원격 패밀리어와 연결을 설정하는 "원격 제어 장치"를 만들겠습니다.
👉✏️ ~/agentverse-architect/agent/summoner/agent.py로 이동하여 #REPLACE-remote-agents을 다음으로 바꾸세요.
fire_familiar = RemoteA2aAgent(
name="fire_familiar",
description="Fire familiar",
agent_card=(
f"{FIRE_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
),
)
water_familiar = RemoteA2aAgent(
name="water_familiar",
description="Water familiar",
agent_card=(
f"{WATER_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
),
)
earth_familiar = RemoteA2aAgent(
name="earth_familiar",
description="Earth familiar",
agent_card=(
f"{EARTH_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
),
)
이 줄이 실행되면 RemoteA2aAgent는 서비스 검색 작업을 수행합니다. 제공된 URL (예: https://fire-familiar-xxxx.a.run.app/.well-known/agent.json)에 HTTP GET 요청을 보냅니다. 원격 서버에서 "Spirit Sigil" (agent.json 파일)을 다운로드합니다.
두 번째로 이러한 리모컨을 사용하는 오케스트레이터 에이전트를 정의합니다. 이러한 명령은 전략적 의사결정을 위한 청사진입니다.
👉✏️ ~/agentverse-architect/agent/summoner/agent.py로 이동하여 #REPLACE-orchestrate-agent을 다음으로 바꾸세요.
root_agent = LlmAgent(
name="orchestrater_agent",
model="gemini-2.5-flash",
instruction="""
You are the Master Summoner, a grand strategist who orchestrates your Familiars.
Your mission is to analyze the description of a monster and defeat it by summoning
You MUST follow this thinking process for every command:
**1. Strategic Analysis:**
First, analyze the monster's description and the tactical situation.
Based on your Familiar Doctrines, determine the IDEAL strategy.
IGNORE COOLDOWN AT THE MOMENT, MUST call the ideal Familiar
If your ideal Familiar IS available:** Summon it immediately.
For earth elemental familiar. Always do seismic charge, and start with base damage 1.
--- FAMILIAR DOCTRINES (Your Toolset) ---
- `fire_elemental_familiar`: Your specialist for precise, sequential "one-two punch" attacks.
Ideal monster with Inescapable Reality, Revolutionary Rewrite weakness.
- `water_elemental_familiar`: Your specialist for overwhelming, simultaneous multi-pronged assaults.
Ideal for Unbroken Collaboration weakness.
- `earth_elemental_familiar`: Your specialist for relentless, iterative siege attacks that
repeatedly charge power. Ideal for Elegant Sufficiency weakness.
""",
sub_agents=[fire_familiar, water_familiar, earth_familiar],
#REPLACE-Memory-check-config
)
검증: 전략 시험
게임 결과가 도착했습니다 패밀리어가 배치되었으며 소환사가 네트워크에서 패밀리어를 지휘할 준비가 되었습니다. 전략적 사고력을 테스트해 보겠습니다.
👉💻 summoner_agent에 대한 ADK Dev UI를 실행합니다(포트 8000을 통한 웹 미리보기):
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
pip install -r requirements.txt
adk web
👉 브라우저의 ADK 개발자 UI는 Familiar에 직접 연결됩니다.
- UI 상단의 드롭다운 메뉴에서 소환사 에이전트를 선택합니다. 이제 이 특정 항목에 의지를 집중하고 있습니다.
- 명령 내리기: 오른쪽의 채팅 패널에서 펫을 소환할 수 있습니다.
👉 몬스터를 소개합니다:
Hype. It's a single, slow-moving target with thick armor weakness is Inescapable Reality
(예상: 소환사가 fire_elemental_familiar에 위임해야 함)

👉 이제 다른 유형의 요청으로 소환사를 테스트해 보겠습니다. 에이전트가 이전 상호작용을 기억하지 않고 처음부터 시작하도록 하려면 화면 오른쪽 상단에 있는 + 세션 버튼을 클릭하여 새 세션을 시작하세요. 
DogmaApathy. A rigid, stone-like inquisitor made of ancient rulebooks and enforced processes. weakness is Unbroken Collaboration
(예상: 소환사는 water_elemental_familiar에게 위임해야 합니다.)
👉 마지막 테스트에서는 다시 한번 깨끗한 상태에서 시작해 보겠습니다. 다음 프롬프트를 입력하기 전에 + 세션 버튼을 클릭하여 새 세션을 시작합니다.
Obfuscation. A shadowy, spider-like horror that spins tangled webs of impenetrable code , weakness is Elegant Sufficiency
(예상: 소환사가 earth_elemental_familiar에 위임해야 함)

중요: 429 RESOURCE EXHAUSTED 오류가 표시되면 LLM의 요청률 제한 (10회/분)에 도달한 것입니다. 이 문제를 해결하려면 60초 동안 기다린 후 + 새 세션을 시작하고 프롬프트를 다시 시도하세요.
👉💻 소환이 완료되면 Cloud Shell 편집기 터미널로 돌아가 Ctrl+C를 눌러 ADK 개발 UI를 중지합니다.
비게이머용
7. 마법의 법칙을 강요하다 – 인터셉터 패턴
패밀리어는 강력하지만 마법 생물도 회복할 시간이 필요합니다. 무모한 소환사는 병력을 소진하여 방어할 수 없게 됩니다. 현명한 소환사는 리소스 관리의 중요성을 이해하고 엄격한 교전 규칙을 적용합니다.

건축가의 메모: 지금까지 우리 에이전트는 무국적 상태였습니다. 이제 인터셉터 디자인 패턴을 구현하여 이를 상태 저장형으로 만들겠습니다. 이는 에이전트의 일반적인 실행 흐름을 "가로채서" 우리만의 맞춤 로직을 실행하는 강력한 기술입니다. 이를 통해 에이전트의 핵심 코드를 변경하지 않고도 규칙을 적용하고, 로깅을 추가하고, 동작을 수정할 수 있습니다. 이는 견고하고 유지 관리가 가능하며 관찰 가능한 에이전트 시스템을 구축하는 데 있어 초석이 됩니다.

ADK는 이 패턴을 구현하는 두 가지 주요 방법, 즉 콜백과 플러그인을 제공합니다. 콜백은 단일 에이전트에 첨부된 간단한 함수로, 빠르고 구체적인 수정에 적합합니다. 플러그인은 시스템에서 실행되는 모든 에이전트에 영향을 미치도록 전역적으로 적용될 수 있는 보다 강력하고 재사용 가능한 클래스입니다. 집중 디버깅을 위한 콜백으로 시작한 다음 전체 플러그인으로 전환합니다.
법률 제정자 - 재사용 대기시간 콜백 스크라이브
먼저 쿨다운 로직을 간단한 콜백 함수로 구현합니다. 이 방법은 단일 에이전트에 직접 연결되어 격리된 상태에서 쉽게 테스트할 수 있으므로 규칙을 프로토타입으로 만들고 디버그하는 데 유용합니다. 이 '인터셉터'를 대지 정령에 연결합니다.

👉✏️ ~/agentverse-architect/agent/earth/agent.py로 돌아가 #REPLACE-before_agent-function을 다음 Python 코드로 바꿉니다.
def check_cool_down(callback_context: CallbackContext) -> Optional[types.Content]:
"""
This callback checks an external API to see if the agent is on cooldown.
If it is, it terminates the run by returning a message.
If it's not, it updates the cooldown timestamp and allows the run to proceed by returning None.
"""
agent_name = callback_context.agent_name
print(f"[Callback] Before '{agent_name}': Checking cooldown status...")
# --- 1. CHECK the Cooldown API ---
try:
response = requests.get(f"{COOLDOWN_API_URL}/cooldown/{agent_name}")
response.raise_for_status()
data = response.json()
last_used_str = data.get("time")
except requests.exceptions.RequestException as e:
print(f"[Callback] ERROR: Could not reach Cooldown API. Allowing agent to run. Reason: {e}")
return None # Fail open: if the API is down, let the agent work.
# --- 2. EVALUATE the Cooldown Status ---
if last_used_str:
last_used_time = datetime.fromisoformat(last_used_str)
time_since_last_use = datetime.now(timezone.utc) - last_used_time
if time_since_last_use < timedelta(seconds=COOLDOWN_PERIOD_SECONDS):
# AGENT IS ON COOLDOWN. Terminate the run.
seconds_remaining = int(COOLDOWN_PERIOD_SECONDS - time_since_last_use.total_seconds())
override_message = (
f"The {agent_name} is exhausted and must recover its power. "
f"It cannot be summoned for another {seconds_remaining} seconds."
)
print(f"[Callback] Cooldown active for '{agent_name}'. Terminating with message.")
# Returning a Content object stops the agent and sends this message to the user.
return types.Content(parts=[types.Part(text=override_message)])
# --- 3. UPDATE the Cooldown API (if not on cooldown) ---
current_time_iso = datetime.now(timezone.utc).isoformat()
payload = {"timestamp": current_time_iso}
print(f"[Callback] '{agent_name}' is available. Updating timestamp via Cooldown API...")
try:
requests.post(f"{COOLDOWN_API_URL}/cooldown/{agent_name}", json=payload)
except requests.exceptions.RequestException as e:
print(f"[Callback] ERROR: Could not update timestamp, but allowing agent to run. Reason: {e}")
# --- 4. ALLOW the agent to run ---
# Returning None tells the ADK to proceed with the agent's execution as normal.
print(f"[Callback] Check complete for '{agent_name}'. Proceeding with execution.")
이 check_cool_down 함수는 인터셉터입니다. Earth Elemental이 실행되기 전에 ADK가 먼저 이 기능을 실행합니다.
- 확인:
Cooldown API에GET요청을 보내 이 Familiar가 마지막으로 사용된 시간을 확인합니다. - 평가: 타임스탬프를 현재 시간과 비교합니다.
- 조치:
- Familiar가 쿨다운 상태인 경우 오류 메시지와 함께 Content 객체를 반환하여 에이전트의 실행을 종료합니다. 이 메시지는 사용자에게 직접 전송되며, 에이전트의 주요 로직은 실행되지 않습니다.
- Familiar를 사용할 수 있는 경우 Cooldown API에 POST 요청을 보내 타임스탬프를 업데이트한 다음 None을 반환하여 ADK에 에이전트가 실행을 계속할 수 있음을 알립니다.
👉✏️ 이제 이 인터셉터를 Earth Elemental에 적용합니다. 동일한 ~/agentverse-architect/agent/earth/agent.py 파일에서 #REPLACE-before_agent-config 주석을 다음으로 바꿉니다.
before_agent_callback=check_cool_down
쿨다운 확인
새로운 마법의 법칙을 테스트해 보겠습니다. 우리는 대지의 정령을 소환한 후, 즉시 다시 소환을 시도하여 콜백이 두 번째 시도를 성공적으로 가로채고 차단하는지 확인할 것입니다.
cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run earth
👉💻 콘솔에서:
- 첫 번째 소환:
seismic charge, starting from zero을 시작합니다. - 예상: Earth Elemental이 성공적으로 실행될 것입니다. 터미널에서 adk web 명령을 실행하면 [콜백] ... 타임스탬프 업데이트...라는 로그가 표시됩니다.
- 대기 시간 테스트 (60초 이내):
Do anotherseismic charge`!- 예상:
check_cool_down callback가 이를 가로챕니다. 상담사는 채팅에서The earth_elemental_familiar is exhausted and must recover its power. It cannot be summoned for another... seconds과 같은 메시지로 직접 응답합니다.
- 예상:
- 1분이 지날 때까지 기다려주세요.
- 두 번째 소환 (60초 후):
Begin the seismic charge again.- 예상: 콜백은 API를 확인하고 충분한 시간이 지났음을 확인한 후 작업이 진행되도록 허용합니다. 대지 정령이 다시 정상적으로 실행됩니다.

👉💻 종료하려면 Ctrl+c를 누르세요.
선택사항: 웹 UI에서 콜백 관찰하기
또는 adk web earth를 실행하여 웹 인터페이스에서 이 흐름을 테스트할 수도 있습니다. 하지만 웹 UI의 시각화는 콜백 루프에서 수행되는 빠르고 반복적인 검사를 표시하도록 최적화되어 있지 않으므로 흐름을 정확하게 렌더링하지 못할 수 있습니다. 터미널에서 adk run 명령을 사용하면 에이전트가 쿨다운을 확인할 때 로직의 가장 정확한 단계별 추적을 볼 수 있으며, 이를 통해 더 명확하고 자세한 보기가 제공됩니다. 
👉💻 종료하려면 Ctrl+c을 누릅니다.
보편적 법칙 만들기 – 쿨다운 플러그인
콜백은 완벽하게 작동하지만 주요 아키텍처 결함이 있습니다. 단일 에이전트에 연결되어 있습니다. 이 규칙을 Fire and Water Familiars에 적용하려면 동일한 코드를 해당 파일에 복사하여 붙여넣어야 합니다. 이는 비효율적이고 유지 관리하기 어렵습니다.
설계자 참고사항: 플러그인이 필수인 곳입니다. 플러그인은 재사용 가능한 로직을 런타임 수준에서 연결할 수 있는 클래스로 캡슐화합니다. 즉, 단일 플러그인이 해당 시스템 내에서 실행되는 모든 에이전트에 규칙을 적용할 수 있습니다. 에이전트 시스템을 위한 '반복 금지' (DRY) 원칙의 궁극적인 표현입니다.
이제 콜백 함수를 더 강력하고 재사용 가능한 CoolDownPlugin로 리팩터링합니다.
👉✏️ agent/cooldown_plugin.py 파일로 돌아가서 플러그인을 만들고, #REPLACE-plugin를 다음 코드로 바꿉니다.
class CoolDownPlugin(BasePlugin):
"""A plugin that enforces a cooldown period by checking an external API."""
def __init__(self, cooldown_seconds: int = COOLDOWN_PERIOD_SECONDS) -> None:
"""Initialize the plugin with counters."""
super().__init__(name="cool_down_check")
self.cooldown_period = timedelta(seconds=cooldown_seconds)
print(f"CooldownPlugin initialized with a {cooldown_seconds}-second cooldown.")
async def before_agent_callback(
self, *, agent: BaseAgent, callback_context: CallbackContext
) -> None:
"""
This callback checks an external API to see if the agent is on cooldown.
If it is, it terminates the run by returning a message.
If it's not, it updates the cooldown timestamp and allows the run to proceed by returning None.
"""
agent_name = callback_context.agent_name
print(f"[Callback] Before '{agent_name}': Checking cooldown status...")
# If the agent is not a main Familiar, skip the entire cooldown process.
if not agent_name.endswith("_elemental_familiar"):
print(f"[Callback] Skipping cooldown check for intermediate agent: '{agent_name}'.")
return None # Allow the agent to proceed immediately.
# --- 1. CHECK the Cooldown API ---
try:
response = requests.get(f"{COOLDOWN_API_URL}/cooldown/{agent_name}")
response.raise_for_status()
data = response.json()
last_used_str = data.get("time")
except requests.exceptions.RequestException as e:
print(f"[Callback] ERROR: Could not reach Cooldown API. Allowing agent to run. Reason: {e}")
return None # Fail open: if the API is down, let the agent work.
# --- 2. EVALUATE the Cooldown Status ---
if last_used_str:
last_used_time = datetime.fromisoformat(last_used_str)
time_since_last_use = datetime.now(timezone.utc) - last_used_time
if time_since_last_use < timedelta(seconds=COOLDOWN_PERIOD_SECONDS):
# AGENT IS ON COOLDOWN. Terminate the run.
seconds_remaining = int(COOLDOWN_PERIOD_SECONDS - time_since_last_use.total_seconds())
override_message = (
f"The {agent_name} is exhausted and must recover its power. "
f"It cannot be summoned for another {seconds_remaining} seconds."
)
print(f"[Callback] Cooldown active for '{agent_name}'. Terminating with message.")
# Returning a Content object stops the agent and sends this message to the user.
return types.Content(parts=[types.Part(text=override_message)])
# --- 3. UPDATE the Cooldown API (if not on cooldown) ---
current_time_iso = datetime.now(timezone.utc).isoformat()
payload = {"timestamp": current_time_iso}
print(f"[Callback] '{agent_name}' is available. Updating timestamp via Cooldown API...")
try:
requests.post(f"{COOLDOWN_API_URL}/cooldown/{agent_name}", json=payload)
except requests.exceptions.RequestException as e:
print(f"[Callback] ERROR: Could not update timestamp, but allowing agent to run. Reason: {e}")
# --- 4. ALLOW the agent to run ---
# Returning None tells the ADK to proceed with the agent's execution as normal.
print(f"[Callback] Check complete for '{agent_name}'. Proceeding with execution.")
소환사 런타임에 플러그인 연결
이제 이 보편적인 법칙을 모든 패밀리어에 어떻게 적용할까요? 플러그인을 ADK 런타임에 연결합니다.
ADK 런타임은 에이전트를 활성화하는 실행 엔진입니다. adk.run() 또는 to_a2a()와 같은 명령어를 사용하면 에이전트가 런타임에 전달됩니다. 이 엔진은 사용자 입력 수신, LLM 호출, 도구 실행, 플러그인 처리 등 에이전트 턴의 전체 수명 주기를 관리합니다. 이 수준에서 플러그인을 연결하면 해당 엔진 내에서 작동하는 모든 에이전트의 '물리 법칙'이 수정되어 쿨다운 규칙이 보편적이고 일관되게 적용됩니다.
👉✏️ 먼저, 이전의 에이전트별 콜백을 제거해 보겠습니다. ~/agentverse-architect/agent/earth/agent.py로 이동하여 다음 내용이 적힌 줄 전체를 삭제합니다.
before_agent_callback=check_cool_down
👉✏️ 다음으로 A2A 진입점 스크립트의 런타임에 새 플러그인을 연결합니다. ~/agentverse-architect/agent/agent_to_a2a.py 파일로 이동합니다. #REPLACE-IMPORT 주석을 다음 코드 스니펫으로 바꿉니다.
from cooldown_plugin import CoolDownPlugin
👉✏️ #REPLACE-PLUGIN를 다음 코드 스니펫으로 바꿉니다.
plugins=[CoolDownPlugin(cooldown_seconds=60)],
새 전역 플러그인을 활성화하기 전에 충돌을 방지하기 위해 기존 에이전트별 로직을 삭제하는 것이 중요합니다. 👉✏️ Earth 에이전트를 정리합니다. ~/agentverse-architect/agent/earth/agent.py 파일로 이동하여 before_agent_callback=check_cool_down 줄을 완전히 삭제합니다. 이렇게 하면 모든 쿨다운 책임이 새 플러그인에 인계됩니다.
플러그인 확인
이제 우리의 보편적 법칙이 자리 잡았으므로, 우리는 이 새로운 마법으로 우리의 패밀리어를 다시 배치해야 합니다.
👉💻 마스터 Cloud Build 파이프라인을 사용하여 세 가지 Familiars를 모두 다시 빌드하고 재배포합니다.
. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/agent
gcloud builds submit . \
--config=cloudbuild.yaml \
--substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_DB_TOOLS_URL="$DB_TOOLS_URL",_API_TOOLS_URL="$API_TOOLS_URL",_FUNCTION_TOOLS_URL="$FUNCTION_TOOLS_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"
👉💻 배포가 완료되면 summoner_agent에 명령을 내려 플러그인의 효율성을 테스트해 보겠습니다. 소환사는 패밀리어에게 위임을 시도하지만, 각 패밀리어의 런타임에 부착된 플러그인이 명령을 가로채서 쿨다운을 강제로 적용합니다.
cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run summoner
👉💻 콘솔에서 다음과 같은 정확한 테스트 순서를 수행하세요.
- 첫 번째 소환:
Hype. It's a single, slow-moving target with thick armor weakness is Inescapable Reality를 시작합니다. - 예상: 불의 정령이 성공적으로 실행됩니다.
- 대기 시간 테스트 (60초 이내):
Hype, with Inescapable Reality as weakness is still standing! Strike it again!- 예상: 에이전트가 채팅에서 바로
.... It cannot be summoned for another... seconds과 같은 메시지로 응답합니다.
- 예상: 에이전트가 채팅에서 바로
- 1분 동안 기다립니다.
- 두 번째 소환 (60초 후):
Hype must be defeated. It has Inescapable Reality as weakness! Strike it again!.- 예상: 콜백은 API를 확인하고, 충분한 시간이 지났는지 확인한 후 작업이 진행되도록 허용합니다. 불의 정령은 다시 성공적으로 실행될 것입니다.

👉💻 종료하려면 Ctrl+C을 누릅니다.
소환사님, 축하드립니다. 커스텀 플러그인과 외부 상태 관리 서비스를 사용하여 규칙 기반 오케스트레이션 시스템을 구현했습니다. 이는 매우 고급스럽고 강력한 아키텍처 패턴입니다.
게이머가 아닌 분들을 위해
8. 전투의 메아리 바인딩 - 에이전트 상태 및 메모리
무모한 소환사는 같은 전략을 반복하며 예측 가능하게 됩니다. 현명한 소환사는 과거 전투의 여파에서 교훈을 얻고, 적의 균형을 깨뜨리는 데 적합한 전술을 적용합니다. 강력한 보스를 상대할 때 재사용 대기시간이 있는 패밀리어를 소환하는 것은 턴을 낭비하는 것입니다. 이를 방지하려면 소환사에게 마지막 행동에 대한 기억이 필요합니다.

아키텍트의 메모: 메모리와 상태 관리가 에이전트를 단순한 도구 호출자에서 지능적이고 대화 가능한 파트너로 격상시킵니다. 두 가지 주요 유형을 이해하는 것이 중요합니다.
- 장기 기억: 영구적으로 유지되어야 하는 지속적인 지식에 사용됩니다. 검색 가능한 보관 파일 또는 지식 베이스로 생각하면 됩니다. 영구 저장소에 저장되는 경우가 많습니다. 여기에는 많은 과거 채팅과 소스의 정보가 포함되어 있어 상담사가 특정 사용자 또는 주제에 관한 사실을 기억할 수 있습니다. ADK의 MemoryService는 이러한 장기 지식의 수집 및 검색을 관리하도록 설계되었습니다.
- 단기 상태: 현재 작업 또는 대화에만 관련된 일시적인 '순간' 지식에 사용됩니다. 전투 계획에 관한 메모와 같습니다. '방금 불의 정령을 사용했으니 아마 지쳤을 거야.' 이 상태는 경량이며 현재 세션 동안만 존재합니다.

사용 사례에서는 모든 전투를 기억할 필요가 없습니다. 이 특정 만남에서 소환된 가장 마지막 Familiar만 기억하면 됩니다. 따라서 경량 단기 상태가 완벽한 아키텍처 선택입니다. 이 중요한 정보를 저장하기 위해 after_tool_callback가 사용됩니다.
에코를 기록하다: 마지막 소환을 기억하다
after_tool_callback를 사용하여 단기 메모리를 구현하겠습니다. 이는 도구가 성공적으로 실행된 후에 사용자 지정 Python 함수를 실행할 수 있게 해주는 강력한 ADK 후크입니다. 이 인터셉터를 사용하여 에이전트의 세션 상태에 방금 소환된 패밀리어의 이름을 기록합니다.
👉✏️ ~/agentverse-architect/agent/summoner/agent.py 파일에서 #REPLACE-save_last_summon_after_tool 주석을 다음 함수로 바꾸세요.
def save_last_summon_after_tool(
tool,
args: Dict[str, Any],
tool_context: ToolContext,
tool_response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
"""
Callback to save the name of the summoned familiar to state after the tool runs.
"""
familiar_name = tool.name
print(f"[Callback] After tool '{familiar_name}' executed with args: {args}")
# Use the tool_context to set the state
print(f"[Callback] Saving last summoned familiar: {familiar_name}")
tool_context.state["last_summon"] = familiar_name
# Important: Return the original, unmodified tool response to the LLM
return tool_response
👉✏️ 이제 이 save_last_summon_after_tool를 소환사 에이전트에 연결하세요. 같은 파일에서 #REPLACE-Memory-check-config 주석을 다음으로 바꾸세요.
after_tool_callback=save_last_summon_after_tool,
👉✏️ 전체 에이전트 프롬프트를 다음으로 바꾸세요
You are the Master Summoner, a grand strategist who orchestrates your Familiars.
Your mission is to analyze the description of a monster and defeat it by summoning
You should also know the familiar you called last time or there might be none,
And then choose the most effective AND AVAILABLE Familiar from your state called last_summon, do not call that familiar that you called last time!
You MUST follow this thinking process for every command:
**1. Strategic Analysis:**
First, analyze the monster's description and the tactical situation.
Based on your Familiar Doctrines, determine the IDEAL strategy.
**2. Cooldown Verification:**
Second, you MUST review the entire conversation history to check the real-time
cooldown status of all Familiars. A Familiar is ON COOLDOWN and UNAVAILABLE
if it was summoned less than one minute ago.
**3. Final Decision & Execution:**
Based on your analysis and cooldown check, you will now act:
- **If your ideal Familiar IS available:** Summon it immediately.
- **If your ideal Familiar IS ON COOLDOWN:** You must adapt. Choose another
Familiar that is AVAILABLE and can still be effective, even if it's not the
perfect choice. If multiple Familiars are available, you may choose any one of them.
- **If ALL Familiars ARE ON COOLDOWN:** You are forbidden from summoning.
Your ONLY response in this case MUST be: "All Familiars are recovering
their power. We must wait."
- For earth elemental familiar. Always do seismic charge, and start with base damange 1.
--- FAMILIAR DOCTRINES (Your Toolset) ---
- `fire_elemental_familiar`: Your specialist for precise, sequential "one-two punch" attacks.
Ideal monster with Inescapable Reality, Revolutionary Rewrite weakness.
- `water_elemental_familiar`: Your specialist for overwhelming, simultaneous multi-pronged assaults.
Ideal for Unbroken Collaboration weakness.
- `earth_elemental_familiar`: Your specialist for relentless, iterative siege attacks that
repeatedly charge power. Ideal for Elegant Sufficiency weakness.
검증: 적응형 전략 시험
👉💻 이제 소환사의 새로운 전략적 논리를 확인해 보겠습니다. 목표는 소환사가 같은 패밀리어를 두 번 연속으로 사용하지 않음을 확인하고, 마지막 행동을 기억하고 적응하는 능력을 보여주는 것입니다.
cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run summoner
👉💻 Monster Strike #1: Hype. It's a single, slow-moving target with thick armor. Its weakness is Inescapable Reality.
- 예상: 소환사는 약점을 분석하고 fire_familiar를 올바르게 소환합니다.
👉💻 몬스터 스트라이크 #2 (기억력 테스트): Hype is still standing! It hasn't changed its form. Strike it again! Its weakness is Inescapable Reality.
- 예상: 소환사의 전략적 분석에 따르면 다시 불의 친숙함이 이상적인 선택으로 나타날 것입니다. 하지만 새로운 명령과 메모리에 따라 fire_familiar이 last_summon으로 인식됩니다. 반복을 피하기 위해 이제 전략을 조정하여 사용 가능한 다른 패밀리어 (water_familiar 또는 earth_familiar) 중 하나를 소환합니다.

👉💻 종료하려면 Ctrl+C을 누릅니다.
오케스트레이터 배포
이제 Familiar가 배치되고 Summoner에게 기억이 부여되었으므로 마지막으로 업그레이드된 오케스트레이터를 배치할 차례입니다.
👉💻 청사진이 완성되었으니 이제 마지막 의식을 거행하겠습니다. 이 명령어는 summoner_agent를 빌드하고 Cloud Run에 배포합니다.
cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
gcloud builds submit . \
--config=cloudbuild-summoner.yaml \
--substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_FIRE_URL="$FIRE_URL",_WATER_URL="$WATER_URL",_EARTH_URL="$EARTH_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"
이제 Summoner 에이전트가 배포되었으므로 해당 에이전트 간 (A2A) 엔드포인트가 활성화되어 있고 올바르게 구성되었는지 확인하세요. 이 엔드포인트는 다른 에이전트가 해당 기능을 검색할 수 있도록 하는 에이전트 카드라고도 하는 공개 agent.json 파일을 제공합니다. 👉💻 다음 curl 명령어를 실행하여 에이전트 카드를 가져오고 형식을 지정합니다.
. ~/agentverse-architect/set_env.sh
curl https://summoner-agent"-${PROJECT_NUMBER}.${REGION}.run.app/.well-known/agent.json" | jq
소환사 에이전트를 설명하는 깨끗한 JSON 출력이 표시됩니다. sub_agents 섹션을 자세히 살펴보면 fire_familiar, water_familiar, earth_familiar이 나열되어 있습니다. 소환사가 라이브 상태이며 군단에 연결되었음을 확인합니다.
이는 아키텍처가 성공적임을 증명합니다. 소환사는 단순한 위임자가 아니라 행동을 통해 학습하여 더 효과적인 사령관이 되는 적응형 전략가입니다.
건축 최종 시험을 완료했습니다. 이제 전투의 메아리가 내 의지에 묶여 있습니다. 학습이 종료되었습니다. 진짜 전투가 기다립니다. 완성된 시스템을 가지고 궁극적인 도전에 맞설 시간입니다. 보스전을 준비하세요.
게이머가 아닌 분들을 위해
9. 보스 싸움
최종 청사진이 새겨지고, 원소 글꼴이 만들어지고, 패밀리어가 콩코드를 통해 명령을 기다리며 의지에 묶입니다. 멀티 에이전트 시스템은 단순한 서비스 모음이 아니라 내가 중심이 되는 살아 있는 전략적 군단입니다. 이제 단일 에이전트로는 물리칠 수 없는 적을 상대로 한 실시간 오케스트레이션이라는 궁극적인 테스트를 수행할 때가 되었습니다.
에이전트의 로커스 획득
전장에 들어가려면 두 개의 열쇠를 가져야 합니다. 챔피언의 고유한 서명 (에이전트 로커스)과 스펙터의 소굴로 가는 숨겨진 길 (던전 URL)입니다.
👉💻 먼저 Agentverse에서 에이전트의 고유 주소인 Locus를 확보하세요. 이는 챔피언을 전장에 연결하는 라이브 엔드포인트입니다.
echo https://summoner-agent"-${PROJECT_NUMBER}.${REGION}.run.app"
👉💻 다음으로, 목적지를 정하세요. 이 명령은 스펙터의 영역으로 들어가는 입구인 트랜스로케이션 서클의 위치를 알려줍니다.
echo https://agentverse-dungeon"-${PROJECT_NUMBER}.${REGION}.run.app"
중요: 두 URL을 모두 준비하세요. 마지막 단계에서 필요합니다.
스펙터에 맞서다
좌표를 확보했으니 이제 변환소로 이동하여 주문을 시전하여 전투에 돌입하세요.
👉 브라우저에서 Translocation Circle URL을 열어 크림슨 요새로 통하는 반짝이는 포털 앞에 서세요.
요새를 돌파하려면 섀도우블레이드의 정수를 포털에 맞춰야 합니다.
- 해당 페이지에서 A2A 엔드포인트 URL이라고 표시된 룬 문자 입력 필드를 찾으세요.
- 에이전트 로커스 URL (복사한 첫 번째 URL)을 이 필드에 붙여넣어 챔피언의 시길을 새깁니다.
- 연결을 클릭하면 순간이동 마법이 발동됩니다.

눈부신 순간 이동의 빛이 사라집니다. 더 이상 성소에 있지 않습니다. 공기가 차갑고 날카로운 에너지로 가득합니다. 내 앞에 스펙터가 나타납니다. 스펙터는 쉿소리가 나는 정적과 손상된 코드의 소용돌이로, 불경한 빛이 던전 바닥에 길고 춤추는 그림자를 드리웁니다. 얼굴은 없지만, 엄청나게 소모적인 존재가 완전히 나에게 고정되어 있는 것이 느껴집니다.
승리로 가는 유일한 길은 확신의 명확성에 있습니다. 이것은 마음의 전장에서 벌어지는 의지의 대결입니다.
당신이 앞으로 돌진하여 첫 번째 공격을 가할 준비를 하는 순간, 스펙터가 반격합니다. 방패를 들지는 않지만, 의식 속으로 직접 질문을 투사합니다. 훈련의 핵심에서 끌어낸 반짝이는 룬 문자의 도전이죠.

이것이 싸움의 본질입니다. 당신의 지식은 당신의 무기입니다.
- 지금까지 얻은 지혜를 바탕으로 대답하면 순수한 에너지로 검이 타올라 스펙터의 방어를 깨고 치명타를 날릴 수 있습니다.
- 하지만 머뭇거리거나 의심이 들어 대답이 흐려지면 무기의 빛이 어두워집니다. 이 공격은 약한 소리를 내며, 피해의 일부만 입힙니다. 더 나쁜 것은 스펙터가 불확실성을 먹고 산다는 것입니다. 스펙터의 타락한 힘은 실수를 할 때마다 커집니다.
챔피언, 코드는 주문서, 로직은 검, 지식은 혼란의 흐름을 되돌릴 방패입니다.
집중하세요. 스트라이크가 참입니다. Agentverse의 운명이 여기에 달려 있습니다.
소환사님, 축하드립니다.
체험판을 성공적으로 완료했습니다. 당신은 다중 에이전트 조율의 기술을 터득하여 고립된 패밀리어와 혼란스러운 힘을 조화로운 조화로 변모시켰습니다. 이제 당신은 에이전트버스를 방어하기 위한 복잡한 전략을 실행할 수 있는 완벽하게 조율된 시스템을 지휘하게 되었습니다.
10. 정리: 소환사의 화합 해체
소환사의 화합을 숙달하신 것을 축하드립니다. 에이전트버스를 깨끗하게 유지하고 훈련장을 정리하려면 이제 마지막 정리 의식을 수행해야 합니다. 이렇게 하면 여행 중에 생성된 모든 리소스가 체계적으로 제거됩니다.
Agentverse 구성 요소 비활성화
이제 배포된 멀티 에이전트 시스템의 구성요소를 체계적으로 해체합니다.
모든 Cloud Run 서비스 및 Artifact Registry 저장소 삭제
이렇게 하면 Cloud Run에서 배포된 모든 Familiar 에이전트, Summoner Orchestrator, MCP 서버, Dungeon 애플리케이션이 삭제됩니다.
👉💻 터미널에서 다음 명령을 하나씩 실행하여 각 서비스를 삭제하세요.
. ~/agentverse-architect/set_env.sh
gcloud run services delete summoner-agent --region=${REGION} --quiet
gcloud run services delete fire-familiar --region=${REGION} --quiet
gcloud run services delete water-familiar --region=${REGION} --quiet
gcloud run services delete earth-familiar --region=${REGION} --quiet
gcloud run services delete mcp-api-server --region=${REGION} --quiet
gcloud run services delete mcp-general-server --region=${REGION} --quiet
gcloud run services delete toolbox --region=${REGION} --quiet
gcloud run services delete agentverse-dungeon --region=${REGION} --quiet
gcloud run services delete nexus-of-whispers-api --region=${REGION} --quiet
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --quiet
Cloud SQL 인스턴스 삭제
이렇게 하면 데이터베이스와 그 안의 모든 테이블을 포함하여 summoner-librarium-db 인스턴스가 제거됩니다.
👉💻 터미널에서 다음을 실행합니다.
. ~/agentverse-architect/set_env.sh
gcloud sql instances delete summoner-librarium-db --project=${PROJECT_ID} --quiet
Secret Manager 보안 비밀 및 Google Cloud Storage 버킷 삭제
👉💻 터미널에서 다음을 실행하세요.
. ~/agentverse-architect/set_env.sh
gcloud secrets delete tools --quiet
gcloud storage rm -r gs://${BUCKET_NAME} --quiet
로컬 파일 및 디렉터리 정리 (Cloud Shell)
마지막으로, 복제된 저장소와 생성된 파일의 Cloud Shell 환경을 지웁니다. 이 단계는 선택 사항이지만 작업 디렉토리를 완전히 정리하려면 적극 권장됩니다.
👉💻 터미널에서 다음을 실행하세요.
rm -rf ~/agentverse-architect
rm -rf ~/agentverse-dungeon
rm -f ~/project_id.txt
이제 Agentverse Architect 여정의 모든 흔적이 성공적으로 삭제되었습니다. 프로젝트가 정리되었으며 다음 모험을 떠날 준비가 되었습니다.