1. 개요
ADK 2의 주요 내용은 세 가지 오케스트레이션 패턴입니다. 이 Codelab에서는 마라톤 레이스 데이 코치라는 하나의 앱을 실행 가능한 링 하나씩 빌드하여 세 가지를 모두 학습합니다. 각 레벨은 하나의 질문에 답하고, 하나의 아이디어를 추가하며, 자체적으로 실행됩니다.
학습할 내용
- 그래프 워크플로 (필라 1) - 입력이 도착하기 전에 흐름을 그릴 수 있는 경우
- 협업 에이전트 (필라 2) - 팀은 알지만 요청에서 하위 집합을 선택하는 경우, 세 가지 협업 모드 (
chat/task/single_turn)가 모두 실시간으로 실행됩니다. - 동적 워크플로 (필러 3) - 작업의 형태 자체가 입력에 따라 달라지는 경우
- 선택 방법 - 질문이 하나인 결정 트리와 패턴 구성 방법
주제 간의 연결점
알려진 구조 → 알려진 팀 / 변수 하위 집합 → 알 수 없는 모양 → 올바른 모양 선택

빌드할 항목
한 앱(마라톤 레이스 데이 코치)은 실행 가능한 레벨을 한 번에 하나씩 조립했습니다. 모든 레벨은 터미널에서 실행하는 일반 Python 모듈입니다. L5에서는 아래 조각을 모두 사용할 수 있습니다.
그림은 실행 중인 코드에서 가져온 것입니다. 모든 실선은 Workflow.graph.edges에서 읽어왔습니다. 이것이 첫 번째 교훈입니다. 미리 그릴 수 있는 부분은 정확히 Pillar 1이고, 그릴 수 없는 부분은 Pillar 2와 3이 존재하는 이유입니다.

필요한 항목
- Google 계정 (Colab용) - 로컬 설정이 필요하지 않음
- 약 50분 (두 개의 L4 레벨이 긴 편이므로 예산을 책정하세요).
- Gemini 모델에 액세스하는 두 가지 방법 중 하나입니다. 레인 선택: 설정 단계를 하나만 실행하고 나머지는 건너뜁니다.
🎓 워크숍 | 🏠 테이크아웃 | |
소개 | 라이브 워크숍에 참석 중이며 강사가 크레딧 청구 링크를 제공한 경우 | 워크숍 참석자를 포함한 기타 모든 사용자(나중에) |
필요한 항목 | 클레임 링크 및 Cloud 프로젝트를 만들 수 있는 Google 계정 | |
실행 | 워크숍 크레딧으로 청구되는 프로젝트의 Vertex AI | Google AI Studio |
비용 | 크레딧 적용 대상 | 무료 등급 |
설정 단계 | 워크숍 설정 (다음 단계) | 집에서 설정 (다음 단계) |
프롤로그 이후의 모든 것은 어느 쪽이든 동일합니다. 레인은 노트북이 통신하는 모델 엔드포인트만 결정합니다.
두 가지 방법으로 따라하기
아래의 각 단계는 Colab 노트북의 셀 하나와 GitHub 저장소의 폴더 하나에 매핑됩니다. 다음 중 하나를 선택합니다.
2. 워크숍 설정 · 크레딧을 사용하고 Vertex AI로 전환하기
워크숍에서 Google Cloud 크레딧이 제공됩니다. 이를 소유권 주장하고, 이에 청구되는 프로젝트를 만들고, 노트북이 AI Studio 대신 Vertex AI를 가리키도록 합니다. 한 셀이 클레임 이후의 모든 작업을 처리합니다.
1 · 크레딧 받기 (약 1분)
- 강사가 공유한 소유권 주장 링크를 엽니다.
https://me.developers.google.com/benefits/claim/your-workshop-name과 같이 표시됩니다. - 로그인하고 페이지를 따라 크레딧을 수락합니다.
- 사용된 Google 계정을 확인합니다. 아래의 모든 단계는 동일한 계정으로 실행해야 합니다.
2. 노트북을 열고 ADK 2 설치 (~1분)
Colab에서 열기 ▶를 클릭한 다음 첫 번째 코드 셀을 실행합니다. 이 Codelab이 인증된 정확한 ADK 2 버전을 고정하고 ✓ installed를 출력합니다.
3. '워크숍 설정' 셀 실행 (약 3분)
🎓 경로 A · 워크숍이라는 제목의 셀입니다. 실행하면 Colab에서 승인을 요청합니다. 방금 크레임을 신청한 것과 동일한 Google 계정을 선택하고 액세스를 허용합니다.
이 셀은 다음 네 가지 작업을 실행합니다. 크레딧으로 adk-2-tutorial-XXXX라는 프로젝트를 만들고, Vertex AI API를 사용 설정하고, 이후 셀에서 읽는 네 가지 환경 변수를 설정한 다음 Vertex에 테스트 호출을 하고 응답을 기다립니다. 따라서 설정이 완료되거나 이유를 알려주며, 나중에 레벨 내에서 실패하지 않습니다.
예상 출력 - 마지막 줄이 중요합니다.
Signed in as: you@example.com
...
Successfully created GCP project 'adk-2-tutorial-4817'.
Successfully linked 'adk-2-tutorial-4817' to billing account '01ABCD-...'.
waiting for Vertex AI to come up on the new project... (10s)
waiting for Vertex AI to come up on the new project... (20s)
✅ Vertex AI on adk-2-tutorial-4817 · us-central1 · gemini-2.5-flash — answered a test call
4 · 'Take-home setup' 단계 건너뛰기
AI Studio 키 셀을 실행하면 노트북이 AI Studio로 다시 전환되고 방금 실행한 작업이 실행취소되므로 실행하지 마세요. 셀이 이를 방지하고 실행을 거부하지만 더 깔끔한 방법은 건너뛰는 것입니다. 여기에서 바로 공유 빌딩 블록 셀로 이동합니다.
5 · '공유 템플릿' 셀 실행
한 번 실행합니다. L2 이상의 모든 수준에서 재사용하는 Pydantic 스키마와 미리 준비된 마라톤 시나리오를 정의합니다. ✓ schemas + scenarios ready이 표시됩니다.
워크숍 후
크레딧과 크레딧으로 만든 프로젝트는 영구적이지 않습니다. 워크숍이 끝난 후에도 이러한 수준을 무료로 계속 실행하려면 Take-home 설정 단계를 대신 실행하세요. 무료 AI Studio 키, Cloud 프로젝트 없음, 결제 없음 이 셀만 변경됩니다.
더 빨리 정리하려면 Cloud 콘솔을 열고 adk-2-tutorial-XXXX를 선택한 후 삭제합니다. 이 Codelab의 다른 항목은 유료 리소스를 만들지 않습니다.
3. 집에서 설정 · AI Studio API 키
이 경로의 모든 항목은 무료 Google AI Studio API 키에서 실행됩니다. Google Cloud 프로젝트, 결제, 로컬 설치가 필요하지 않습니다. 이 전체 단계는 약 3분입니다.
1 · 노트북 열기
Colab에서 열기 ▶를 클릭합니다. 노트북이 열립니다. 마크다운 소개와 각 수준별로 실행 가능한 셀이 하나씩 있습니다. 셀을 위에서 아래로 실행하면 각 셀의 출력이 바로 아래에 인쇄됩니다.
2 · ADK 2 설치(~1분)
첫 번째 코드 셀을 실행합니다. 이 Codelab에서 확인된 정확한 버전을 고정합니다.
%pip install -q "google-adk==2.3.0" python-dotenv pydantic nest_asyncio
완료될 때까지 기다립니다. ✓ installed이 표시됩니다. (처음 설치하는 데는 30~60초가 걸리며 이후에는 캐시됩니다.)
3 · AI Studio에서 Gemini API 키 가져오기 (약 1분)
- 새 브라우저 탭에서 aistudio.google.com/app/apikey를 엽니다.
- Google 계정으로 로그인합니다.
- API 키 만들기 (오른쪽 상단)를 클릭합니다.
- 기존 Google 프로젝트를 선택하거나 새 프로젝트를 만듭니다.
- 키를 복사합니다. 키는
AIza...로 시작하고 약 40자입니다.
4 · Colab에 키 추가 (약 1분)
옵션 A — Colab 보안 비밀 (권장, 키가 숨겨진 상태로 유지됨):
- Colab 왼쪽 사이드바에서 🔑 키 아이콘을 클릭합니다.
- + 새 보안 비밀 추가를 클릭합니다.
- 이름을 정확히
GOOGLE_API_KEY로 설정합니다. - 키를 값에 붙여넣습니다.
- 노트북 액세스를 사용으로 전환합니다.
옵션 B - 메시지가 표시되면 붙여넣기 (빠름): 보안 비밀을 건너뜁니다. 다음 셀을 실행하면 숨겨진 메시지 🔑 Enter your Google AI Studio API key:이 표시됩니다. 붙여넣고 Enter 키를 누릅니다.
5 · 키 셀 실행
비밀번호를 읽거나 붙여넣기 프롬프트로 대체한 다음 ADK가 Vertex AI가 아닌 AI Studio를 가리킵니다.
import os
# 🏠 TAKE-HOME ONLY — if you ran the Workshop setup cell, skip this one.
if os.environ.get("GOOGLE_GENAI_USE_VERTEXAI") == "True":
raise SystemExit("✋ You're set up on the workshop path (Vertex AI). Skip this cell.")
# Google AI Studio API key — add GOOGLE_API_KEY in the 🔑 Secrets panel (or paste when prompted).
try:
from google.colab import userdata
key = userdata.get("GOOGLE_API_KEY")
except Exception:
import getpass
key = getpass.getpass("Enter your Google AI Studio API key: ")
os.environ["GOOGLE_API_KEY"] = "".join(key.split()) # drop any stray whitespace/newlines
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False" # use AI Studio, not Vertex AI
print("✅ API key set — using Google AI Studio.")
예상 출력: ✅ API key set — using Google AI Studio.
6. '공유 템플릿' 셀 실행
공유 빌딩 블록 셀을 한 번 실행합니다. L2 이상의 모든 수준에서 재사용하는 Pydantic 스키마와 미리 준비된 마라톤 시나리오를 정의합니다. ✓ schemas + scenarios ready이 표시됩니다.
설정이 완료되었습니다. 🎽 L0(모든 사용자가 처음 빌드하는 버전)으로 가기 전에 간단히 우회해 보겠습니다.
4. 프롤로그 · 하나의 큰 프롬프트를 사용하지 않는 이유
⚡ 실행하기 전에 시청할 한 가지를 정하세요. 모든 구체적인 숫자는 어디에서 가져온 것인가요? 이것이 전체 연습입니다. 그 외 모든 것은 장식입니다.
사다리 전에 사다리가 대체하는 항목을 실행합니다. 프롬프트에서 모든 것을 약속하는 에이전트 하나는 날씨를 가져오고, 강의를 분석하고, 교육 로그를 읽고, 조건별로 라우팅하고, 계획을 출력합니다.
표시되는 내용: 자신감 있고 구체적이며 형식이 잘 지정된 전략... 이지만 숫자는 가짜입니다. 한 라이브 실행에서는 '오늘의 날씨 측정항목을 가져왔습니다'라는 말로 시작하여 52°F, 9mph 바람, 본 적이 없는 트레이닝 로그 분석을 보고했습니다. 여기에는 날씨 API도, 강의 데이터도, 로그도 없습니다. 불투명한 모델 호출 하나가 입력을 조작하거나 쓸모없는 것으로 회피합니다.
이것이 질병이며, 이름이 지정된 네 가지 증상이 있습니다.
- 신뢰할 수 없음: 데이터가 유창하게 만들어집니다.
- 테스트할 수 없음: 4단계의 라우팅은 설명 내에 있습니다. 단위 테스트를 위한
if가 없습니다. - 단계를 바꿀 수 없음: 실제 날씨 API를 연결할 수 있는 이음새가 없습니다.
- 매번 모든 항목에 대해 비용을 지불합니다. 5단계, 하나의 대규모 호출, 결정적인 부분은 캐시되지 않습니다.
그 감정을 유지하세요. 다음 9개 레벨에서는 함수 가져오기 (L1~L2a), if 문 라우팅 (L2b), 전문가가 작업 나누기 (L3a~L3b), 코드 바운드가 모양 지정 (L4a~L4b)과 같이 프롬프트에서 한 번에 한 단계씩 단계를 수행합니다.

💻 로컬: python -m shared.prologue
5. L0 · 첫 번째 ADK 2 에이전트

⚡ 요약: 에이전트는 모델 + 요청 사항 + 호출할 수 있는 도구로 구성되며, Runner가 이를 실행합니다. 이 수준 이후의 모든 것은 더 나은 모양으로 배열된 더 많은 에이전트일 뿐입니다.
질문: 산술이 중요한 경우 모델이 대답하고 실제 코드를 사용할 수 있나요?
하나의 아이디어 - 세 부분:
Agent: 추론하는 항목 (Gemini 모델 + 명령)Runner: 세션 내에서 에이전트를 실행하고 이벤트를 스트리밍하는 항목입니다.- 도구: 모델이 호출하기로 결정한 일반 Python 함수 (
pace_splits) ADK는 서명과 독스트링을 읽고 모델에 선언을 전달합니다. 스키마 작성은 없습니다.
프롤로그 이후 첫 번째 수정 사항은 LLM이 머릿속으로 페이스 산술을 수행하면 잘못될 수 있다는 것입니다. pace_splits는 결정론적 Python이므로 대답의 숫자는 즉흥적으로 만들어진 것이 아니라 계산된 것입니다.
▶ Colab: L0 셀 실행 · 📁 GitHub: L0_first_agent/ · 💻 로컬: python -m L0_first_agent.agent

def pace_splits(target_finish: str) -> dict:
"""Convert a goal time like '3:30:00' into exact per-mile / per-km paces."""
... # deterministic Python — no LLM
pace_coach = Agent(
name="pace_coach", model=MODEL,
tools=[pace_splits], # the model may call it; ADK reads the signature
instruction="You are a friendly, concise marathon coach. ... If the runner "
"mentions a goal time, call pace_splits — never do arithmetic yourself.",
)
runner = Runner(node=pace_coach, session_service=InMemorySessionService(), auto_create_session=True)
async for event in runner.run_async(user_id="u1", session_id="s1", new_message=msg):
... # events carry the model's text
🔍 마커: Agent(...), tools=[pace_splits], Runner(...) 출력에서 🔧 줄은 모델이 대답 중간에 코드를 호출하기로 결정한 것입니다.
표시되는 내용:
🔧 model called tool → pace_splits({'target_finish': '3:30:00'})
🔧 tool returned → {'per_mile': '8:00', 'per_km': '4:58', ...}
🧠 Coach: To finish in 3:30:00, you need an average pace of 8:00 per mile...
🔧 줄은 교훈입니다. 대답 중간에 모델이 함수를 호출하기로 선택했으며 대답의 정확한 8:00/mile는 토큰 통계가 아닌 코드에서 가져왔습니다.
❓ 모델이 항상 도구를 호출하는지 궁금할 수 있습니다. 아니요. 질문별로 결정합니다. 숫자가 없는 내용을 질문하면 🔧 선이 사라집니다 (Playground에서 정확히 이 작업을 시도해 볼 수 있음).
👀 읽기: pace_splits (일반 함수) 및 tools=[pace_splits] 줄 · ▶ 실행합니다. · ✏️ 변경: 일반 질문 (목표 시간 없음)을 요청합니다. 🔧 줄이 사라진 것을 확인할 수 있습니다. 모델이 도구를 호출할 가치가 있는 시점을 결정합니다. 그런 다음 instruction를 다시 작성하고 다시 실행합니다. 명령은 프로그램의 나머지 부분입니다.
6. L1 · 첫 번째 워크플로

⚡ 요약: 일반 함수와 LLM 에이전트는 동일한 종류의 노드입니다. 예측 가능한 작업 → 함수 (LLM 0, 결정적), 추론 → 에이전트
질문: 코드인 부분에 대한 모델 호출 비용을 지불하지 않고 하나의 흐름에서 일반 코드와 LLM을 혼합하려면 어떻게 해야 할까요?
하나의 아이디어: Workflow에서 일반 Python 함수와 LLM 에이전트는 모두 동일한 edges 목록의 노드일 뿐입니다.
START ──► fetch_conditions (function, 0 LLM) ──► advise (agent, 1 LLM)
▶ Colab: L1 셀 실행 · 📁 GitHub: L1_graph_basics/ · 💻 로컬: python -m L1_graph_basics.workflow

함수 노드는 생성된 데이터를 출력하고 (모델 호출 없음) 에이전트는 수신한 실제 온도와 바람을 참조하는 조언을 제공합니다.
def fetch_conditions(node_input): # function node — 0 LLM
return Event(output=Conditions(temp_f=78, wind_mph=12, conditions="sunny").model_dump())
advise = Agent(name="advise", model=MODEL, mode="single_turn",
input_schema=Conditions, instruction="...give pacing + gear advice...")
workflow = Workflow(edges=[(START, fetch_conditions, advise)])
🔍 마커: 가운데에 일반 Python 함수가 있는 한쪽 가장자리 튜플((START, fetch_conditions, advise))과 핸드오프를 검증하는 input_schema=
L0과 비교한 새로운 기능: Workflow(edges=[...]), START(입력이 들어가는 곳), Event(output=...)를 반환하는 함수 노드, input_schema=Conditions. 따라서 에이전트가 보기 전에(JSON 텍스트로) 함수의 출력이 해당 스키마에 대해 검증됩니다(input_schema는 경계를 검증하며 에이전트에게 Python 객체를 전달하지는 않음).
❓ 다음과 같은 의문이 들 수 있습니다. 함수-에이전트 순서가 필수인가요? 아니요. 순서, 혼합, 개수와 관계없이 가능합니다. advise은 fetch_conditions의 데이터가 필요하기 때문에 두 번째로 실행됩니다. 수업은 작위이지 순서가 아닙니다.
👀 읽기: fetch_conditions는 모델 호출 없이 데이터를 반환하고 advise에는 input_schema=Conditions이 있습니다. · ▶ 실행합니다. · ✏️ 변경: 함수에서 temp_f=30를 설정하고 다시 실행하면 추천이 바뀌고 함수는 여전히 LLM 호출 비용이 0입니다.
7. L2a: 병렬 팬아웃 + JoinNode (핵심 요소 1a)

⚡ 요약: 병렬로 팬아웃 (무료), 모두 대기, 번들링, 한 에이전트에게 전체 그림 전달
질문: 입력이 도착하기 전에 흐름을 그릴 수 있습니다. 스켈레톤부터 시작하세요. 데이터를 병렬로 수집하고, 번들로 묶고, 하나의 에이전트에게 전달합니다.
도형:
START ──► fetch_weather ──┐
START ──► analyze_course ─┼─► JoinNode ─► strategy (1 agent)
START ──► pull_fitness ───┘ (bundles)
▶ Colab: L2a 셀 실행 · 📁 GitHub: L2a_parallel_join/ · 💻 로컬: python -m L2a_parallel_join.workflow

🔍 마커: 모두 START(팬아웃)에서 시작하는 세 개의 모서리와 회의 지점인 JoinNode입니다.
- 세 번의 가져오기는 함수입니다. 병렬로 실행되며 LLM 호출은 0입니다.
JoinNode는 세 가지를 모두 기다리고 함수 이름으로 키가 지정된 하나의 유형이 지정된 페이로드 (BundledRunData)로 번들링합니다.- 한
strategy에이전트가 번들을 읽고RaceStrategy를 작성합니다.
표시되는 항목: 각 가져오기는 started / finished 타임스탬프를 출력합니다. 세 가지 모두 0.0초에 시작하고 팬아웃은 2.0초에 종료됩니다. 지속 시간을 합한 4.5초가 아닌 가장 느린 가져오기입니다. 이 중복이 병렬 처리입니다. (마지막에 출력되는 총 실제 시간은 전략 에이전트의 LLM 호출도 포함되어 있으므로 약 8초입니다. 총 시간이 아닌 병렬 요청의 가져오기 타임스탬프를 읽으세요.)
💡 프롤로그 콜백: 메가 프롬프트가 날씨를 발명했습니다. 여기서 온도는 가져오기 함수에서 나옵니다. 실제 코드, 실제 이음새입니다. 미리 정의된 사전(dict)을 실제 날씨 API로 바꾸면 다른 사항은 변경되지 않습니다.
❓ 다음과 같은 궁금증이 있을 수 있습니다. 얼마나 많은
JoinNode
알아두어야 할 사항은 무엇인가요? 한 문장으로 설명하면 모든 병렬 브랜치가 도착할 때까지 기다리고, 출력을 업스트림 함수의 이름으로 키가 지정된 하나의 dict에 패킹하고, 자체적으로는 아무것도 계산하지 않습니다. 이 사전 덕분에 L2b의 라우터가 node_input["fetch_weather"]["temp_f"]를 쓸 수 있습니다.
👀 읽기: START에서 세 개의 에지가 펼쳐집니다. JoinNode는 하나의 에이전트를 위해 이를 번들로 묶습니다. · ▶ 실행하고 전체가 아닌 타임스탬프를 읽습니다. · ✏️ 변경: 하나의 가져오기 절전 모드 3.0를 만듭니다. 먼저 새 팬아웃 종료 시간을 예측한 다음 확인합니다.
8. L2b · 결정적 라우터 추가 (필러 1b)

⚡ 요약: L2a는 그대로 유지되고 일반 if가 실행할 하나의 에이전트를 결정합니다. 모델에 묻지 않고 분기하기
질문: 더운 날씨와 추운 날씨에 따라 계획이 달라야 합니다. 모델에 결정을 요청하지 않고 어떻게 분기할 수 있나요?
모양 (L2a + 라우터):
... JoinNode ─► route_by_weather ─► hot_strategy
(if-statement) ─► normal_strategy
─► cold_strategy
▶ Colab: L2b 셀 실행 — run("NORMAL") / run("COLD") 시도 · 📁 GitHub: L2b_router/ · 💻 로컬: python -m L2b_router.workflow COLD

def route_by_weather(node_input): # an if-statement, 0 LLM
temp = node_input["fetch_weather"]["temp_f"]
route = "HOT" if temp >= 70 else "COLD" if temp <= 40 else "NORMAL"
return Event(output=node_input, route=route)
(route_by_weather, {"HOT": hot_strategy, "NORMAL": normal_strategy, "COLD": cold_strategy})
🔍 마커: 경로를 이름 지정하는 함수 노드 Event(output=..., route=...)와 이름을 노드에 매핑하는 dict-edge {"HOT": ..., "NORMAL": ..., "COLD": ...}.
결론:세 가지 종류의 일, 세 가지 집
- 예측 가능한 작업 → 함수 (3개의 병렬 가져오기)
- 명확한 규칙 → 명시적 라우팅 (
route_by_weather는 모델 결정이 아닌if문임) - Reasoning → 모델 (정확히 하나의 전략 에이전트가 실행됨)
표시되는 항목: temp=78F -> route=HOT, 구조화된 RaceStrategy 순 비용: 1 LLM 호출
⚠️ 네 번째 브랜치를 추가하는 경우 경로 사전에도 DEFAULT_ROUTE 항목을 지정하세요. 딕셔너리와 일치하지 않는 경로는 오류가 아닙니다. 브랜치가 종료되고 프로그램이 출력 없이 0으로 종료되므로 디버그하기 혼란스러운 막다른 길입니다.
❓ 궁금한 점이 있으신가요? L2b는 말 그대로 L2a에 라우터를 더한 것인가요? 예. 가져오기 및 조인은 그대로 유지되며 여전히 정확히 1개의 LLM 호출입니다. 변경사항: '항상 동일한 상담사'가 '데이터에 따라 선택되는 3명의 상담사 중 한 명'으로 변경되었습니다.
👀 읽기: route_by_weather — 라우터는 에이전트가 아닌 if 문입니다. · ▶ 실행 run("COLD")도 실행합니다. · ✏️ 변경: 네 번째 상담사가 있는 WINDY 브랜치를 추가하고 그 전에 위의 DEFAULT_ROUTE 경고를 읽으세요.
9. L3a · 협업 에이전트: 하나의 플래그, 두 개의 세계 - 2번 기둥

⚡ 요약: 동일한 팀, 하나의 플래그 chat 한 전문가에게 전체 대화를 넘겨주고 다시 돌아오지 않습니다. single_turn 각 전문가를 도구로 전환합니다(병렬 하위 집합, 자동 반환, 하나의 합성).
질문: 팀은 알고 있지만 요청에 따라 답변할 구성원이 결정됩니다. LLM이 하위 집합을 선택하고 동시에 실행하도록 하려면 어떻게 해야 할까요?
모양: 6명의 전문가 (의료, 날씨, 페이싱, 장비, 영양, 정신)를 관리하는 코디네이터 이 레벨에서는 동일한 팀을 두 번 실행합니다. 동일한 코디네이터 프롬프트, 동일한 6명의 전문가가 사용됩니다. 유일한 차이점은 하위 에이전트의 플래그 하나입니다. 대조는 교훈입니다.
▶ Colab: L3a 셀 실행 · 📁 GitHub: L3a_collaborative/ · 💻 로컬: python -m L3a_collaborative.concierge --mode chat "What about fueling?"

🔍 마커: 팩토리의 mode="single_turn"와 출력의 TRANSFER → (비트 1)과 하나의 타임스탬프를 공유하는 DISPATCH → 라인 버스트 (비트 2)
비트 1: 기본값을 먼저 실행하고 작업이 실패하는지 확인합니다.
mode=이 작성되지 않음 → 하위 에이전트가 기본적으로 chat로 설정됩니다. 표시되는 내용:
TRANSFER → nutrition_specialist (transfer_to_agent — the only tool chat subagents provide)
Final speaker: nutrition_specialist
코디네이터에게는 위임 도구가 없습니다. 채팅 하위 에이전트는 transfer_to_agent만 제공하며, 이는 전체 대화를 한 명의 전문가에게 순차적으로 전달하는 것입니다. 전문가가 사용자에게 직접 답변하고 실행이 종료됩니다. 병렬 디스패치가 없습니다. 반품 불가 합성되지 않습니다. 광범위한 질문을 하면 상황이 더 악화됩니다. 6명의 전문가, 1번의 트랜스퍼가 발생합니다.
이는 버그가 아니라 채팅 모드가 작동하는 방식입니다. 대화는 명시적으로 이전될 때까지 대화를 보유한 사용자에게 속합니다. 개방형 어시스턴트에는 적합하지만 파이프라인 단계에는 적합하지 않습니다.
Beat 2 · 하나의 플래그, 두 개의 세계
유일한 차이점은 각 전문가의 mode="single_turn"입니다. 동일한 질문을 다시 실행합니다.
[t= 7.8s] DISPATCH → medical_specialist ← same timestamp =
[t= 7.8s] DISPATCH → weather_specialist one turn, many calls
[t=14.5s] ↩ medical_specialist replied ← replies land inside
[t=14.5s] ↩ weather_specialist replied one short window
🧠 Concierge (synthesized): <one answer>
이제 ADK는 하위 에이전트의 이름을 따서 지정되고 description=로 설명되는 전문가당 하나의 위임 도구를 삽입합니다 (이 텍스트는 코디네이터가 하위 집합을 선택할 때 읽는 텍스트임. 이 텍스트를 건너뛰면 이름만으로 라우팅됨). 코디네이터는 한 번에 여러 호출을 내보내고 ADK는 이를 병렬로 실행하며 각 호출은 결과를 자동으로 반환하고 코디네이터는 이를 종합합니다.
질문 | 발사하는 전문가 |
'연료는 어떻게 해야 해?' | 영양만 |
'18마일 지점에서 무릎이 아파' | 의료만 |
'오늘 경주해야 하나?' | 의료 + 날씨 + 페이싱 |
'걱정해야 할 게 있어?' | 모두 6 |
각 전문가에게 전체 브리핑이 전달되는 이유: 각 single_turn 하위 에이전트는 자체 격리된 세션 브랜치에서 실행되므로 대화나 동료를 볼 수 없습니다. 앰비언트가 없습니다. 코디네이터는 전체 SpecialistInput (질문 + 전략 + 러너 데이터)을 모든 병렬 호출에 별도로 전달해야 합니다.
💡 ADK 2에서 직접 홈을 제공하는 경우: LLM이 요청별 하위 집합을 선택하고 sub_agents + mode="single_turn"을 통해 선언하여 병렬로 실행합니다. AgentTool로 각 전문가를 래핑하여 1.x에서 동일한 모양을 어셈블할 수 있습니다. 변경된 점은 이제 배관이 아닌 선언이라는 것입니다. (ParallelAgent은 항상 all이고 transfer_to_agent은 serial임)
⚠️ 두 가지 솔직한 주의사항이 있습니다. (1) 모델이 하위 집합을 선택하므로 L2의 하드 코딩된 라우터보다 결정성이 떨어집니다. 정확한 하위 집합은 실행마다 다를 수 있습니다. (2) 가끔 한 전문가에 대한 Error validating input: ... 줄이 표시됩니다. 전문가의 출력은 거의 없습니다. output_schema를 사용하면 Gemini가 서버 측에서 이를 적용합니다. 입력입니다. 코디네이터는 병렬 호출마다 전체 중첩 SpecialistInput를 그대로 재현해야 하며 때로는 하나를 놓치기도 합니다. ADK는 해당 도구의 결과로 오류를 반환하고 코디네이터는 복구되며 합성도 계속됩니다.
❓ 다음과 같은 질문이 있을 수 있습니다.
chat
1.x 스타일 위임(한 번에 하나의 에이전트)만 지원되나요? 기본적으로는 그렇습니다. 이제 이름이 지정된 1.x 기본 동작입니다. single_turn와의 차이는 3차원입니다. 코디네이터가 보유하는 항목 (transfer_to_agent 1개 대 전문가 당 도구 1개), 작업할 수 있는 수 (대화를 소유하는 1개 대 병렬 N개), 제어가 반환되는지 여부 (결과와 함께 자동으로 대 없음) 코드에 관해: 팩토리의 if mode == 브랜치는 한 팀이 이 대비를 위해 두 가지 방식으로 빌드할 수 있도록 만 존재합니다. 실제 앱은 한 모드를 하드코딩하고 if는 사라집니다.
👀 읽기: _specialist 팩토리. mode 매개변수는 전체 수준입니다. · ▶ 두 비트를 모두 실행합니다. · ✏️ 변경: '18마일 지점에서 무릎이 아파'라고 질문하는 경우 먼저 하위 집합을 예측한 다음 디스패치 라인을 확인합니다.
# The factory's mode parameter is THE variable this level teaches:
def _specialist(name, domain, focus, mode):
kwargs = {}
if mode == "single_turn": # the structured contract only makes sense for a TOOL
kwargs = dict(mode="single_turn",
input_schema=SpecialistInput, output_schema=SpecialistResponse)
return Agent(name=name, model=MODEL,
description=f"Marathon {domain} specialist. Consult for: {focus}.",
instruction=..., **kwargs)
race_concierge = Agent(name="race_concierge", model=MODEL,
sub_agents=[...six specialists...], # NOTE: no `mode` on the coordinator
instruction="...DECIDE which specialists are relevant... call them IN PARALLEL... SYNTHESIZE...")
10. L3b · 작업 모드: 결승선이 있는 대화 — 2번 기둥

⚡ 요약: 중간 모드에서는 필드가 수집될 때까지 사용자와 대화한 다음 검증된 객체와 함께 자동으로 반환됩니다.
질문: L3a에 간격이 있습니다. chat가 전체 대화를 소유하고 single_turn는 사용자에게 전혀 말하지 않습니다. 하지만 실제 접수 작업은 그 사이에 있습니다. 'X를 수집할 때까지 사용자에게 말을 걸고, 그런 다음 검증된 객체를 가지고 돌아오세요.' 어떤 모드인가요?
도형:
race_desk (coordinator)
└─ gear_fitter (mode="task", output_schema=GearOrder)
▶ Colab: L3b 셀 실행 · 📁 GitHub: L3b_task_desk/ · 💻 로컬: python -m L3b_task_desk.desk


🔍 마커: 동일한 상담사의 mode="task" + output_schema= 및 출력의 ⏸ 일시중지 및 finish_task 통화
표시되는 내용:
━━ TURN 1 ━━ user: 'I need shoes for the marathon.'
race_desk → delegate: gear_fitter
gear_fitter: What is your shoe size?
⏸ The run ENDED — but nothing failed. This is a PAUSED task.
━━ TURN 2 ━━ user: 'Size 9, wide.' (same session → resumes the task)
gear_fitter → finish_task (payload validates as GearOrder)
race_desk: Your order ... in size 9 Wide has been confirmed.
L3a 모드에서는 할 수 없는 세 가지 작업이 발생했습니다.
- 작업 중에 러닝이 실제로 중지됨 - 일시중지됨 작업, 멈춤이나 실패가 아님 상담사가 명확히 확인하기 위한 질문을 했으며 작업이 열려 있습니다. (
adk web에서는 답변을 입력하기만 하면 됩니다. 하니스에서 동일한 세션의 두 번째 메시지로 스크립팅합니다.) - 다음 메시지에서 동일한 작업 에이전트가 재개됨 — 재라우팅이나 재위임이 없습니다. 세션에서 대기한 사용자를 알고 있습니다.
finish_task이 종료됨:mode="task"때문에 ADK가 삽입한 도구입니다. 에이전트가 이를 호출하여 완료해야 하며 페이로드는output_schema에 대해 검증되어야 합니다. 입력된 마무리 문구가 포함된 대화 - 그러면 컨트롤이 코디네이터에게 자동으로 반환되고 결과가 첨부됩니다.
모드 선택을 위한 질문 하나 규칙
💡 '사용자가 언제까지 대화해야 하나요?' chat = 무기한 · task = 필드가 수집될 때까지 · single_turn = 없음
모드 | 인간 참여형(Human In The Loop) | 병렬? | 상위로 돌아감 |
| 전체 대화 | 아니요 | 수동 (전송을 통해) |
| 확인 질문만 | 아니요 | 자동 ( |
| 없음 | 예 | 자동 (결과 포함) |
mode는 하위 에이전트에만 적용되며 코디네이터에는 적용되지 않습니다. 워크플로 노드는 기본적으로 single_turn로 설정되어 있으며 (L1~L2b가 이를 작성하지 않은 이유) 하위 에이전트는 기본적으로 chat로 설정되어 있습니다 (L3a가 이를 작성해야 한 이유).
⚠️ 이 내용을 기반으로 빌드하기 전에 두 가지 버전 참고사항을 확인하세요. (1) task
정적 그래프 노드로 사용하면 버전에 따라 달라짐 — 2.0.0b1~2.3.0 (이 Codelab의 핀)에서는 Workflow(...)가 생성 시 발생합니다. 이 수준에서 하는 것 (작업 하위 에이전트가 있는 채팅 코디네이터)을 정확히 사용하거나 ctx.run_node를 통해 디스패치하세요. 2.5.0에서 삭제됨 (2) '작업 에이전트는 리프 에이전트여야 합니다' (자체 하위 에이전트 없음)는 문서화된 ADK 제한이지만 계약이지 런타임 가드는 아닙니다. 2.3.0과 2.5.0 모두 중단되지 않습니다. 오류가 없다고 해서 권한이 있는 것으로 간주하지 마세요.
💡 자세히 알아보기: 그래프 워크플로 (2.5.0 이상 모양)에 삽입된 task 에이전트, 대화를 다시 시도하기 위해 다시 루프할 수 있는 라우팅: 동반 리포지토리 22_agent_in_workflow · 전체 모드 가이드: docs/agent-modes.md
❓ 다음과 같은 질문이 있을 수 있습니다.
task
다른 두 개는 안 되고 이것만 되는 이유가 뭐야? 세 가지가 있습니다. 자동 반환 (채팅이 대화를 대신 진행함), 입력된 종료선 (finish_task의 페이로드가 스키마에 대해 검증되어야 함. 스크립트가 아닌 데이터를 다시 받음), 일시중지/재개 (⏸는 중단이 아닌 사람을 기다리는 보류된 작업임).
👀 읽음: gear_fitter — mode="task" + output_schema이 전체 계약입니다. · ▶ 실행합니다. · ✏️ 변경: run_desk("I need a hydration vest", "2 liters, medium") - 명확히 확인하기 위한 질문이 조정되고, 마지막 줄은 입력된 상태로 유지됩니다.
11. L4a · 런타임 크기 조정 병렬 팬아웃 (필라 3a)

⚡ 요약: 스켈레톤은 여전히 세 개의 정적 단계로 구성됩니다. 동적 단계는 중간 단계 내부에 숨겨져 있으며, 너비는 런타임에 데이터에 의해 결정됩니다.
⚠️ 주의: 이 단계가 가장 어렵습니다. 이전 수준은 44줄이었지만 이번에는 120줄입니다. 에이전트 3개와 워크플로 노드 2개가 있으며 패딩은 없습니다. 15분 정도의 시간을 할애하고 마지막에 있는 읽기/실행/변경 줄에 집중하세요. 처음부터 모든 줄을 흡수할 필요는 없습니다.
질문: 작업의 모양은 입력에 따라 달라집니다. 그래프를 미리 그릴 수는 없습니다. 런타임 너비로 시작합니다. LLM이 하위 질문의 개수를 결정하도록 합니다.
모양 (한 단계 깊이):
START ─► decompose ─► research_topic (parallel_worker) ─► synthesize
│ │ │
└──┴──┴─ (flat: no children yet)
개방형 질문은 N개의 하위 질문으로 분해됩니다. N은 런타임에 LLM에 의해 선택되며 (3~7) 각 질문은 병렬로 조사된 후 하나의 브리핑으로 합성됩니다.
▶ Colab: L4a 셀 실행 · 📁 GitHub: L4a_flat_research/ · 💻 로컬: python -m L4a_flat_research.deep_research

🔍 마커가 없습니다.
dynamic=True
전환 동적은 구성이 아닌 작성 방식입니다. 두 개의 마커만 있습니다. @node(parallel_worker=True) (런타임 크기 목록을 가져와 항목당 하나의 작업자를 실행함)와 ctx.run_node(...) (코드 예약 노드를 직접 실행함)입니다. 둘 중 하나가 표시되면 동적입니다.
표시되는 내용: 디컴포저가 5개의 하위 질문을 출력하고, 병렬로 조사한 다음, 합성된 브리핑을 출력합니다. 숫자는 실행할 때마다 달라집니다. 고정된 그래프에서는 이렇게 할 수 없습니다.
작업자에서 이해해야 할 두 가지 플래그:
rerun_on_resume=True은ctx.run_node를 호출하는 모든 노드에서 필수입니다. 그렇지 않으면 ADK에서ValueError를 발생시킵니다. 재개 시에는 정적 그래프에 없으므로 생성된 하위 요소를 다시 빌드하기 위해 디스패치 노드를 다시 실행해야 합니다.retry_config=는 이 실패의 범위를 제한합니다. 병렬 작업자는 모든 형제 작업을 취소하고 하나의 하위 요소가 실패하면 즉시 다시 발생시킵니다. 따라서 재시도가 없으면 일시적인 429 오류 하나로 이미 비용을 지불한 모든 호출을 포함한 전체 실행이 삭제됩니다. 재시도는 내부 항목별 노드에서 이루어지므로 각 브랜치가 독립적으로 재시도됩니다.
❓ 다음과 같은 의문이 들 수 있습니다. ADK는 이것이 동적이라는 것을 어디에서 '알'까요? 선언된 것이 없으므로 그럴 필요가 없습니다. 분해기는 런타임에 목록을 생성하고 병렬 작업자는 도착하는 항목에 맞게 크기를 조정합니다. 동적성은 사용 설정한 모드가 아닌 작성한 데이터 흐름의 속성입니다.
👀 읽기: research_topic의 두 플래그(parallel_worker 및 rerun_on_resume) · ▶ 실행합니다. · ✏️ 변경: 자체적인 열린 질문으로 바꿉니다. 입력에서 너비를 결정하므로 N이 변경됩니다.
12. L4b · 재귀적 스폰 추가 (필라 3b)

⚡ 요약: 재귀는 주어지는 것이 아니라 작성되는 것입니다. 작업자는 일반 Python인 ctx.run_node를 통해 자체를 호출하므로 브레이크도 작성해야 합니다. MAX_DEPTH입니다.
질문: 한 연구 결과에서 자체 조사가 필요한 좁은 하위 주제가 드러나는 경우가 있습니다. 브랜치가 더 많은 병렬 작업을 생성하도록 허용하면서도 제한을 유지하려면 어떻게 해야 할까요?
모양 (이제 재귀적임):
START ─► decompose ─► research_topic (parallel_worker, recursive) ─► synthesize
│ │ │
│ │ └─ research(q3) ─► maybe spawn children
│ └─── research(q2) ─► maybe spawn children
└────── research(q1) ─► maybe spawn children
▶ Colab: L4b 셀 실행 · 📁 GitHub: L4b_recursion/ · 💻 로컬: python -m L4b_recursion.deep_research

@node(parallel_worker=True, rerun_on_resume=True)
async def research_topic(ctx, node_input):
finding = coerce(await ctx.run_node(research_agent, node_input=...), ResearchFinding)
if finding.needs_deeper and finding.deeper_questions and depth < MAX_DEPTH: # boundary in CODE
children = await ctx.run_node(research_topic, node_input=deeper) # recursive fan-out
yield Event(output={..., "children": children})
🔍 마커: ctx.run_node(research_topic, ...) 내부research_topic 자체(자체 참조는 재귀임) 및 그 위의 한 줄에 있는 가드 depth < MAX_DEPTH
표시되는 내용: 연구 노드에서 spawning N deeper가 출력됩니다(실시간 재귀 발생). 그런 다음 런타임 트리 모양(예: 5 top-level + 10 recursive children)이 표시됩니다. 트리는 실행마다 다릅니다.
⚠️ 노브를 올리기 전: 상한이 빠르게 증가합니다. MAX_DEPTH=3는 최악의 경우를 ~30에서 ~93으로 가져옵니다. 실행이 끝날 때 cancelling N leftover tasks 로그 줄이 표시될 수 있습니다. 이는 결과가 이미 완료된 후 ADK가 병렬 작업 그룹을 해체하는 것입니다. 무해하며 로깅 구성에 따라 표시되지 않을 수도 있습니다.
❓ 다음과 같은 의문이 들 수 있습니다. 동적은 기본적으로 재귀적이지 않나요? 아니요. L4a는 재귀가 0인 완전한 동적입니다. 동적은 일반적인 Python 제어 흐름만 제공합니다. L4b는 이를 사용하여 재귀를 작성하기로 선택합니다. 사용자가 재귀를 작성했으므로 사용자가 경계를 작성해야 합니다. 여기서 'LLM이 작업을 형성하도록 하고 경계는 코드에 유지'라는 슬로건은 더 이상 적용되지 않습니다.
👀 읽기: 가드: if finding.needs_deeper and depth < MAX_DEPTH · ▶ 실행합니다. · ✏️ 변경: MAX_DEPTH = 1을 설정하고 다시 실행하면 트리가 평탄해지고 실행 비용이 저렴해집니다. 경계는 코드에 있습니다.
13. L5 · 어떤 패턴을 사용해야 할까요?

⚡ 요약: 한 축이 모든 것을 결정합니다.다음 단계를 선택하는 주체(내가 그린 그래프, LLM, 코드)
세 가지를 모두 구축했습니다. 이러한 패턴이 유용한 이유는 문제의 형태에 패턴을 맞추기 때문입니다.
축: 다음에 실행할 항목을 누가 결정하나요?
기둥 | 다음에 실행할 항목을 누가 결정하나요? | 기본 제공 정보 유형 |
1 · 그래프 | 그린 그래프 | L2a / L2b |
2 · 공동작업 | LLM | L3a / L3b |
3 · Dynamic | 런타임에 Python 코드를 | L4a / L4b |
0단계: 그래프가 필요한가요?
ADK는 사전 빌드된 워크플로 에이전트(SequentialAgent, ParallelAgent, LoopAgent)를 제공합니다. 일반적인 에이전트 체인의 경우 가장 저렴한 정답이며 조립할 그래프가 없습니다. 명시적 라우팅 (L2b 라우터), 조인 (L2a의 JoinNode) 또는 에이전트가 아닌 노드 (일반 함수, LLM 호출 없음)가 필요한 경우 이를 사용하세요. 마지막이 일반적으로 이유입니다.
Would a prebuilt SequentialAgent / ParallelAgent / LoopAgent do?
│
├─ YES ──────────────────────────────► use it; stop here
│
└─ NO — I need routing, a join, or non-agent nodes
│
Can you draw the workflow before the input arrives?
│
├─ YES ───────────────────────────► Pillar 1 · Graph workflow (L2a/L2b)
│
└─ NO
├─ Known team, request picks the subset? ─► Pillar 2 · Collaborative (L3a/L3b)
└─ Does the shape depend on the input? ──► Pillar 3 · Dynamic (L4a/L4b)

솔직한 1.x 대 2 프레임
'2.0은 1.x에서 할 수 없는 작업을 할 수 있습니다'가 아닙니다. 1.x에서 모든 것을 빌드할 수 있습니다. 2.0에서는 각 도형에 더 직접적인 홈이 제공되므로 알려진 제어 흐름이 프롬프트를 벗어나 보고 테스트할 수 있는 구조가 됩니다.
패턴 | 1.x 비용 | ADK 2 홈 |
그래프 | 일반 빌드에서 LLM 호출 4개, 프롬프트에 숨겨진 라우팅 | 함수 + 에이전트 노드를 피어로 사용 → 1회 호출, |
공동작업 |
| 선언된 팀: |
동적 | 재귀로 인해 프레임워크에서 벗어남 | 프레임워크 내 |
전체 앱 및 그래프로 표시할 수 없는 항목
이제 아래의 모든 부분을 빌드했습니다. Workflow는 graph.edges에서 구조를 노출하므로 이 그림은 손으로 그린 것이 아니라 코드에서 생성됩니다. 인트로스펙션에서 찾은 내용은 이 실습의 요약입니다.
기둥 |
| 이유 |
1 · 그래프 (L2b) | 10개의 가장자리, 경로 등 | 입력이 도착하기 전에 그렸습니다. |
2 · 협업 (L3a) | 0개 모서리 - | LLM이 요청별로 하위 집합을 선택합니다. |
3 · Dynamic (L4a/L4b) | 3개의 가장자리 - 두 경우 모두 동일 | 재귀는 그래프에 연결되지 않고 Python으로 작성됩니다. |
마지막 행은 질문 L4b에 대한 증거입니다. L4a와 L4b의 그래프는 동일하며 둘 중 하나만 재귀됩니다.
지금 빌드할 수 있는 항목
방금 실행한 각 패턴은 실제 제품 모양입니다.
연습한 내용 | 실제로는 | 출발지: |
그래프 + 라우터 (L2a/L2b) | 파이프라인, ETL-with-LLM-steps, 검토/승인 체인, 평가 하네스 | 이 저장소의 L2b |
코디네이터 + | 전문가 팀, 분류 데스크, 다중 렌즈 검토를 갖춘 지원 부조종사 | 마라톤 데모 모드 2 |
| 접수 양식, 예약 흐름, 온보딩, KYC 등 '수집 후 조치' | |
동적 너비/깊이 (L4a/L4b) | 연구 에이전트, 보고서 생성기, 알 수 없는 크기의 입력에 대한 감사 스윕 | 마라톤 데모 모드 3 |
이러한 모델을 구성하고
세 가지 패턴은 상호 배타적이지 않습니다. 그래프 노드는 공동작업 코디네이터를 호출할 수 있고, 전문가는 동적 워크플로를 실행할 수 있습니다. 문제의 부분별로 적절한 패턴을 선택하세요. 이렇게 하면 모든 에이전트 시스템이 하나의 거대한 프롬프트로 바뀌는 것을 방지할 수 있습니다.

💡 자체 워크플로에서 사용해 보세요. 이 그림을 그린 스크립트는 scripts/graph_dump.py입니다. Workflow를 가리키면 실제 가장자리가 출력됩니다. 즉, 빌드하는 모든 항목의 무료 구조 다이어그램이 출력됩니다.
14. 축하합니다

마라톤 레이스 당일 코치를 빌드하면서 ADK 2의 오케스트레이션 패턴 세 가지를 모두 사용했습니다.
학습한 내용
- 프롤로그: 자체 날씨를 발명한 메가 프롬프트로, 구조가 존재하는 이유를 설명합니다.
- L0~L1 -
Agent,Runner, 모델이 호출하도록 선택한 실제 도구, 첫 번째Workflow(함수 노드 + 에이전트 노드(피어)) - L2a / L2b - 그래프 워크플로: 병렬 팬아웃 +
JoinNode, 결정적 라우팅 - LLM 호출 1회 - L3a - 협업 에이전트: 동일한 팀이
chat(고립됨)에서 실행된 후single_turn(병렬 하위 집합 + 합성)에서 실행됩니다. 하나의 플래그, 두 개의 세계입니다. - L3b -
task모드: 일시중지된 확인 질문, 스크립트된 재개, 검증된 객체를 반환하는finish_task - L4a / L4b - 동적 워크플로: 런타임 너비 (팬아웃), 코드의 경계를 사용한 런타임 깊이 (재귀)
- L5: 결정 트리와 패턴 구성 방식
보관할 가치가 있는 대사
함수는 컨텍스트를 준비합니다. 가장자리는 워크플로를 정의합니다. 라우터가 경로를 선택합니다. 모델이 대답을 작성합니다.
LLM이 작업을 형성하도록 하되 코드에서 경계를 유지하세요.
문제의 형태에 맞게 패턴을 지정합니다.
다음 단계
- 이러한 수준이 추출된 전체 앱인 Marathon Race Day Coach를 실행합니다. 이 앱은 브라우저 UI가 있는 FastAPI + SSE 빌드로, github.com/cuppibla/adk-2-marathon-demo에서 세 가지 모드를 모두 실시간으로 보여줍니다.
- 더 넓은 범위: adk-workflows-compared - 23개의 공식 ADK 2 워크플로 샘플이 모두 포함되어 있으며, 각 샘플에는 1.x 포트와 사용 시기 안내가 포함되어 있습니다.
docs/three-pillars.md부터 시작한 다음 이 Codelab에서 건너뛴07_loop,17_request_input,22_agent_in_workflow을 살펴봅니다. - 자체 문제 포팅: 알려진 구조 (L2), 알려진 팀 (L3a/L3b), 알 수 없는 모양 (L4)은 어느 부분인가요?
- 코드 살펴보기: github.com/cuppibla/adk2-tutorial
- 워크숍을 통해 오셨나요? 크레딧과 크레딧으로 만든 프로젝트는 영구적이지 않습니다. 이러한 수준을 무료로 계속 다시 실행하려면 대신 테이크홈 설정 단계를 따르세요. 무료 AI Studio 키, Cloud 프로젝트 없음, 결제 없음. 이 셀 하나를 바꾸는 것이 유일한 변경사항입니다.