# Google ADK 핵심 개념 완전 가이드 이 문서는 Google Agent Development Kit (ADK)의 핵심 개념들을 체계적으로 설명합니다. 샘플 코드에서 사용된 모든 중요 요소들을 이해할 수 있도록 구성했습니다. --- ## 📚 목차 1. [전체 아키텍처 개요](#1-전체-아키텍처-개요) 2. [Session (세션)](#2-session-세션) 3. [State (상태)](#3-state-상태) 4. [Event (이벤트)](#4-event-이벤트) 5. [Context (컨텍스트)](#5-context-컨텍스트) 6. [Runner (러너)](#6-runner-러너) 7. [Memory (메모리)](#7-memory-메모리) 8. [Callbacks (콜백)](#8-callbacks-콜백) 9. [Tools (도구)](#9-tools-도구) 10. [핵심 개념 간의 관계](#10-핵심-개념-간의-관계) --- ## 1. 전체 아키텍처 개요 ### ADK의 핵심 철학 ADK는 **"에이전트 개발을 소프트웨어 개발처럼"** 만드는 것을 목표로 합니다. 복잡한 AI 에이전트 시스템을 모듈화하고, 테스트 가능하며, 유지보수하기 쉽게 만듭니다. ### 핵심 컴포넌트 다이어그램 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 사용자 (User) │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Runner (러너/오케스트레이터) │ │ - 실행 루프 관리 │ │ - 이벤트 처리 및 라우팅 │ │ - 서비스 조율 │ └─────────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ SessionService │ │ MemoryService │ │ ArtifactService │ │ (세션 관리) │ │ (장기 기억) │ │ (파일/데이터) │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Session (세션) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ State │ │ Events │ │ Metadata (ID, User) │ │ │ │ (상태) │ │ (이벤트들) │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Agent (에이전트) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ InvocationContext (호출 컨텍스트) │ │ │ │ - session, state, services 접근 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Tools │ │ LLM │ │Callbacks│ │ │ │ (도구) │ │ (모델) │ │ (콜백) │ │ │ └─────────┘ └─────────┘ └─────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 2. Session (세션) ### 개념 **Session**은 사용자와 에이전트 간의 **단일 대화 스레드**를 나타냅니다. 전화 통화에 비유하면, 하나의 통화가 하나의 세션입니다. ### 주요 속성 ```python from google.adk.sessions import Session # Session의 주요 속성 session.id # 세션 고유 ID session.app_name # 애플리케이션 이름 session.user_id # 사용자 ID session.state # 상태 딕셔너리 (State) session.events # 이벤트 히스토리 리스트 session.last_update_time # 마지막 업데이트 시간 ``` ### SessionService 세션의 생명주기를 관리하는 서비스입니다. ```python from google.adk.sessions import InMemorySessionService # 세션 서비스 생성 session_service = InMemorySessionService() # 새 세션 생성 session = await session_service.create_session( app_name="my_app", user_id="user_123", state={"initial_key": "initial_value"} # 초기 상태 설정 가능 ) # 기존 세션 조회 session = await session_service.get_session( app_name="my_app", user_id="user_123", session_id="session_abc" ) # 세션 목록 조회 sessions = await session_service.list_sessions( app_name="my_app", user_id="user_123" ) # 세션 삭제 await session_service.delete_session( app_name="my_app", user_id="user_123", session_id="session_abc" ) ``` ### SessionService 구현체 종류 | 구현체 | 저장 위치 | 영속성 | 용도 | | ------------------------ | ------------ | ---------------------- | ------------- | | `InMemorySessionService` | 메모리 | ❌ (앱 재시작 시 소멸) | 개발/테스트 | | `DatabaseSessionService` | SQL DB | ✅ | 프로덕션 | | `VertexAISessionService` | Google Cloud | ✅ | 클라우드 배포 | ### 세션 생명주기 ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ CREATE │ ──▶ │ UPDATE │ ──▶ │ DELETE │ │ (생성) │ │ (이벤트 추가)│ │ (종료) │ └─────────────┘ └─────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ State │ │ 변경 │ └─────────────┘ ``` --- ## 3. State (상태) ### 개념 **State**는 세션 내에서 데이터를 저장하는 **키-값 저장소**입니다. 에이전트의 "메모장" 역할을 하며, 대화 중 필요한 정보를 기억합니다. ### State의 범위 (Prefix) ADK에서 State는 **prefix**로 범위를 구분합니다: | Prefix | 범위 | 설명 | | ------- | ------------ | --------------------------------------------- | | (없음) | 세션 전체 | 기본값, 세션 동안 유지 | | `app:` | 애플리케이션 | 같은 앱의 모든 세션에서 공유 | | `user:` | 사용자 | 같은 사용자의 모든 세션에서 공유 | | `temp:` | 현재 호출 | 현재 invocation에서만 유지, 호출 종료 시 삭제 | ```python # State 사용 예시 ctx.session.state["user_name"] = "홍길동" # 세션 범위 ctx.session.state["app:settings"] = {"theme": "dark"} # 앱 전체 공유 ctx.session.state["user:preferences"] = {"lang": "ko"} # 사용자 공유 ctx.session.state["temp:current_step"] = 3 # 현재 호출만 ``` ### State 읽기/쓰기 #### 1. output_key를 통한 자동 저장 (LlmAgent) ```python from google.adk.agents import LlmAgent # 에이전트 응답이 자동으로 state['analysis_result']에 저장됨 agent = LlmAgent( name="Analyzer", instruction="데이터를 분석하세요.", output_key="analysis_result" # ← 자동 저장 ) ``` #### 2. Instruction 템플릿에서 읽기 ```python agent = LlmAgent( name="Reporter", instruction=""" 이전 분석 결과: {analysis_result} 위 결과를 바탕으로 보고서를 작성하세요. """ # ← {key} 형식으로 자동 주입 ) ``` #### 3. Tool/Callback에서 직접 접근 ```python from google.adk.tools import ToolContext def my_tool(context: ToolContext, query: str) -> dict: # 읽기 user_name = context.state.get("user_name", "Unknown") # 쓰기 (자동으로 state_delta에 추적됨) context.state["last_query"] = query context.state["query_count"] = context.state.get("query_count", 0) + 1 return {"result": f"Hello {user_name}, processed: {query}"} ``` ### ⚠️ State 수정 시 주의사항 ```python # ❌ 잘못된 방법: 세션 객체 직접 수정 session = await session_service.get_session(...) session.state["key"] = "value" # 추적되지 않음! # ✅ 올바른 방법: Context를 통해 수정 def my_callback(context: CallbackContext): context.state["key"] = "value" # 자동 추적됨 ``` --- ## 4. Event (이벤트) ### 개념 **Event**는 세션 내에서 발생하는 **모든 활동의 기록 단위**입니다. 사용자 메시지, 에이전트 응답, 도구 호출, 상태 변경 등 모든 것이 이벤트입니다. ### Event 구조 ```python from google.adk.events import Event, EventActions event = Event( # 식별 정보 id="event_123", # 고유 ID invocation_id="inv_456", # 소속 invocation ID author="MyAgent", # 생성자 (에이전트명) # 내용 content=types.Content( # 실제 메시지/데이터 parts=[types.Part(text="안녕하세요!")] ), # 액션/변경사항 actions=EventActions( state_delta={"key": "value"}, # 상태 변경 artifact_delta={}, # 아티팩트 변경 escalate=False, # 루프 종료 신호 transfer_to_agent="OtherAgent", # 에이전트 전환 ), # 메타데이터 timestamp=1234567890.0, partial=False, # 스트리밍 중간 결과 여부 ) ``` ### Event 유형 | 유형 | 설명 | 식별 방법 | | ------------- | -------------------- | -------------------------------- | | 사용자 메시지 | 사용자 입력 | `event.content.role == "user"` | | 텍스트 응답 | 에이전트 텍스트 응답 | `event.content.parts[0].text` | | 도구 호출 | LLM이 도구 호출 요청 | `event.get_function_calls()` | | 도구 결과 | 도구 실행 결과 | `event.get_function_responses()` | | 상태 변경 | State 업데이트 | `event.actions.state_delta` | | 스트리밍 청크 | 부분 응답 | `event.partial == True` | ### EventActions 이벤트에 연결된 **부수 효과(side effects)**를 정의합니다: ```python from google.adk.events import EventActions actions = EventActions( # 상태 변경 state_delta={"new_key": "new_value"}, # 루프 종료 신호 (LoopAgent에서 사용) escalate=True, # 에이전트 전환 (LLM Transfer) transfer_to_agent="TargetAgentName", # 아티팩트(파일) 변경 artifact_delta={"file.txt": "content"}, ) ``` ### Event 처리 흐름 ``` ┌──────────────────────────────────────────────────────────────┐ │ Event 생성 및 처리 흐름 │ └──────────────────────────────────────────────────────────────┘ 1. Event 생성 Agent/Tool/Callback → yield Event(...) 2. Runner 수신 Runner가 이벤트를 받음 3. SessionService 처리 - state_delta를 session.state에 병합 - artifact_delta 처리 - events 히스토리에 추가 4. 클라이언트 전달 - UI/API로 이벤트 스트리밍 ``` --- ## 5. Context (컨텍스트) ### 개념 **Context**는 현재 실행 상황에 대한 **모든 정보를 담은 컨테이너**입니다. 에이전트, 도구, 콜백이 실행될 때 필요한 모든 정보에 접근할 수 있게 합니다. ### Context 유형 #### 1. InvocationContext (호출 컨텍스트) 가장 상위 레벨의 컨텍스트로, 하나의 사용자 요청 처리 전체를 관장합니다. ```python from google.adk.agents import BaseAgent from google.adk.agents.invocation_context import InvocationContext class MyAgent(BaseAgent): async def _run_async_impl(self, ctx: InvocationContext): # 세션 정보 접근 session_id = ctx.session.id user_id = ctx.session.user_id # 상태 접근 state_value = ctx.session.state.get("key") # 서비스 접근 memory_service = ctx.memory_service artifact_service = ctx.artifact_service # 현재 에이전트 정보 agent_name = ctx.agent.name # 호출 제어 ctx.end_invocation = True # 호출 종료 신호 yield Event(author=self.name, content=...) ``` #### 2. CallbackContext (콜백 컨텍스트) 콜백 함수에서 사용되며, InvocationContext의 서브셋입니다. ```python from google.adk.agents.callback_context import CallbackContext def my_before_agent_callback(ctx: CallbackContext): # 에이전트 정보 agent_name = ctx.agent_name invocation_id = ctx.invocation_id # 상태 읽기/쓰기 ctx.state["processed"] = True # 사용자 입력 확인 user_content = ctx.user_content return None # 계속 진행 (또는 Content 반환 시 에이전트 건너뜀) ``` #### 3. ToolContext (도구 컨텍스트) 도구(Tool) 함수에서 사용되며, 도구 실행에 필요한 추가 기능을 제공합니다. ```python from google.adk.tools import ToolContext def my_tool(context: ToolContext, query: str) -> dict: # 상태 접근 (CallbackContext와 동일) context.state["last_query"] = query # 도구 전용 기능 function_call_id = context.function_call_id # 현재 함수 호출 ID # 메모리 검색 results = await context.search_memory("검색어") # 아티팩트 목록 artifacts = context.list_artifacts() # 인증 요청 (OAuth 등) credential = await context.get_auth_response(auth_config) return {"result": "success"} ``` ### Context 계층 구조 ``` InvocationContext (최상위) ├── session │ ├── state │ └── events ├── agent (현재 에이전트) ├── services (memory, artifact, session) └── invocation_id ▼ 파생 CallbackContext (콜백용) ├── state (읽기/쓰기) ├── agent_name ├── user_content └── invocation_id ▼ 확장 ToolContext (도구용) ├── (CallbackContext의 모든 것) ├── function_call_id ├── search_memory() ├── list_artifacts() └── request_credential() ``` --- ## 6. Runner (러너) ### 개념 **Runner**는 에이전트 실행의 **중앙 오케스트레이터**입니다. 사용자 입력을 받아 에이전트를 실행하고, 이벤트를 처리하며, 서비스들을 조율합니다. ### 기본 사용법 ```python from google.adk.runners import InMemoryRunner, Runner from google.adk.sessions import InMemorySessionService # 방법 1: InMemoryRunner (간단한 테스트용) runner = InMemoryRunner( agent=my_agent, app_name="my_app" ) # 방법 2: Runner (커스터마이징 필요 시) session_service = InMemorySessionService() runner = Runner( agent=my_agent, app_name="my_app", session_service=session_service, # memory_service=my_memory_service, # 옵션 # artifact_service=my_artifact_service, # 옵션 ) ``` ### 에이전트 실행 ```python # 비동기 실행 (일반적인 방법) async for event in runner.run_async( user_id="user_123", session_id="session_456", # 새 세션이면 자동 생성 new_message="안녕하세요!" ): if event.content and event.content.parts: for part in event.content.parts: if hasattr(part, 'text') and part.text: print(f"[{event.author}]: {part.text}") # 실시간 스트리밍 (음성/영상) async for event in runner.run_live(...): ... ``` ### Runner 실행 흐름 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Runner.run_async() 흐름 │ └─────────────────────────────────────────────────────────────────┘ 1. 세션 준비 ┌─────────────────┐ │ get/create │ ← 세션 조회 또는 생성 │ Session │ └─────────────────┘ │ ▼ 2. 사용자 메시지를 이벤트로 추가 ┌─────────────────┐ │ append_event │ ← 사용자 입력을 세션 히스토리에 추가 │ (user message) │ └─────────────────┘ │ ▼ 3. InvocationContext 생성 ┌─────────────────┐ │ Create │ ← 실행에 필요한 모든 정보 준비 │ InvocationContext│ └─────────────────┘ │ ▼ 4. 에이전트 실행 루프 ┌─────────────────────────────────────────────┐ │ ┌─────────────────┐ │ │ │ Agent.run() │ ← 에이전트 실행 │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ yield Event │ ← 이벤트 생성 │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Process Event │ ← 이벤트 처리 │ │ │ - state_delta │ (상태 업데이트 등) │ │ │ - append to │ │ │ │ session │ │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ yield to caller │ ← 호출자에게 전달 │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ [더 많은 이벤트?] ──Yes──▶ (반복) │ │ │ │ │ No │ │ │ │ └──────────┼───────────────────────────────────┘ │ ▼ 5. 실행 완료 ``` ### Invocation vs Agent Call vs Step ``` Invocation (호출) │ 사용자 메시지 → 최종 응답까지의 전체 사이클 │ ├── Agent Call 1 (에이전트 호출) │ │ 하나의 에이전트가 실행되는 단위 │ │ │ ├── Step 1 (스텝) │ │ └── LLM 호출 1회 + 도구 실행 │ │ │ ├── Step 2 │ │ └── LLM 호출 1회 + 도구 실행 │ │ │ └── (transfer_to_agent 발생) │ ├── Agent Call 2 (다른 에이전트로 전환) │ │ │ ├── Step 1 │ │ └── LLM 호출 + 최종 응답 │ │ │ └── (완료) │ └── Invocation 종료 ``` --- ## 7. Memory (메모리) ### 개념 **Memory**는 **장기 기억 저장소**입니다. 세션이 종료된 후에도 유지되며, 여러 세션에 걸쳐 정보를 검색할 수 있습니다. ### Session State vs Memory 비교 | 특성 | Session State | Memory | | --------- | ------------------------ | --------------- | | 범위 | 현재 세션 내 | 여러 세션 걸쳐 | | 수명 | 세션 종료 시 (보통) 삭제 | 영구 저장 | | 접근 방식 | Key-Value 직접 접근 | 검색 쿼리 | | 용도 | 대화 중 임시 데이터 | 과거 정보 참조 | | 비유 | 대화 중 메모장 | 도서관 아카이브 | ### MemoryService 사용 ```python from google.adk.memory import InMemoryMemoryService # 메모리 서비스 생성 memory_service = InMemoryMemoryService() # 세션을 메모리에 저장 (대화 종료 후) await memory_service.add_session_to_memory(session) # 메모리 검색 results = await memory_service.search_memory( app_name="my_app", user_id="user_123", query="이전에 추천받은 음식점" ) for result in results.memories: print(result.content) ``` ### MemoryService 구현체 | 구현체 | 저장 방식 | 검색 방식 | 용도 | | --------------------------- | --------- | ----------- | ----------- | | `InMemoryMemoryService` | 메모리 | 키워드 매칭 | 개발/테스트 | | `VertexAIMemoryBankService` | Vertex AI | 시맨틱 검색 | 프로덕션 | ### 메모리 워크플로우 ``` ┌──────────────────────────────────────────────────────────────┐ │ Memory 워크플로우 │ └──────────────────────────────────────────────────────────────┘ 1. 세션 대화 진행 User ↔ Agent (Session에 Events 축적) 2. 세션 종료 시 메모리 저장 memory_service.add_session_to_memory(session) └── 세션의 주요 정보를 메모리에 저장 3. 이후 세션에서 메모리 검색 results = memory_service.search_memory(query) └── 과거 대화에서 관련 정보 검색 4. 검색 결과를 현재 대화에 활용 Agent가 검색 결과를 참고하여 응답 ``` ### 에이전트에서 메모리 사용 ```python from google.adk.tools import preload_memory_tool # 메모리 검색 도구 추가 agent = LlmAgent( name="MemoryAgent", instruction="사용자의 과거 대화를 참고하여 답변하세요.", tools=[preload_memory_tool.PreloadMemoryTool()] ) # 세션 종료 시 자동 저장 콜백 async def auto_save_to_memory(callback_context): await callback_context._invocation_context.memory_service.add_session_to_memory( callback_context._invocation_context.session ) agent = LlmAgent( name="AutoSaveAgent", after_agent_callback=auto_save_to_memory ) ``` --- ## 8. Callbacks (콜백) ### 개념 **Callback**은 에이전트 실행 중 **특정 시점에 개입**할 수 있는 훅(hook)입니다. 가드레일, 로깅, 수정 등 다양한 용도로 사용됩니다. ### 콜백 종류 및 실행 시점 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 콜백 실행 시점 │ └─────────────────────────────────────────────────────────────────┘ 사용자 입력 │ ▼ ┌───────────────────────┐ │ before_agent_callback │ ← 에이전트 실행 전 └───────────────────────┘ │ ▼ ┌───────────────────────┐ │ before_model_callback │ ← LLM 호출 전 └───────────────────────┘ │ ▼ [LLM 호출] │ ▼ ┌───────────────────────┐ │ after_model_callback │ ← LLM 응답 후 └───────────────────────┘ │ ▼ ┌───────────────────────┐ │ before_tool_callback │ ← 도구 실행 전 └───────────────────────┘ │ ▼ [도구 실행] │ ▼ ┌───────────────────────┐ │ after_tool_callback │ ← 도구 실행 후 └───────────────────────┘ │ ▼ ┌───────────────────────┐ │ after_agent_callback │ ← 에이전트 실행 후 └───────────────────────┘ │ ▼ 최종 응답 ``` ### 콜백 정의 및 사용 #### before_agent_callback ```python from google.adk.agents import LlmAgent from google.adk.agents.callback_context import CallbackContext from google.genai import types def check_access(ctx: CallbackContext) -> types.Content | None: """에이전트 실행 전 접근 제어""" user_id = ctx.session.user_id # 차단할 사용자 체크 if user_id in BLOCKED_USERS: # Content 반환 시 에이전트 실행 건너뜀 return types.Content( parts=[types.Part(text="접근이 거부되었습니다.")] ) # None 반환 시 정상 진행 return None agent = LlmAgent( name="SecureAgent", before_agent_callback=check_access ) ``` #### before_model_callback (가드레일) ```python from google.adk.models import LlmRequest, LlmResponse def content_guardrail(ctx: CallbackContext, request: LlmRequest) -> LlmResponse | None: """LLM 호출 전 입력 검사""" user_text = ctx.user_content.parts[0].text # 금지어 체크 if any(word in user_text for word in FORBIDDEN_WORDS): return LlmResponse( content=types.Content( parts=[types.Part(text="부적절한 내용이 감지되었습니다.")] ) ) # 프롬프트 수정 # request.contents를 수정하여 시스템 지시 추가 가능 return None # 정상 진행 agent = LlmAgent( name="GuardedAgent", before_model_callback=content_guardrail ) ``` #### after_model_callback ```python def post_process_response(ctx: CallbackContext, response: LlmResponse) -> LlmResponse | None: """LLM 응답 후처리""" # 응답 로깅 print(f"LLM Response: {response.content}") # 응답 수정 (필요시) # return modified_response return None # 원본 응답 유지 agent = LlmAgent( name="LoggingAgent", after_model_callback=post_process_response ) ``` #### before_tool_callback / after_tool_callback ```python def tool_guardrail(ctx: ToolContext, tool_name: str, args: dict) -> dict | None: """도구 실행 전 검사""" if tool_name == "delete_file" and not ctx.state.get("admin"): return {"error": "권한이 없습니다."} # 도구 실행 건너뜀 return None def log_tool_result(ctx: ToolContext, tool_name: str, result: dict) -> dict | None: """도구 실행 후 로깅""" print(f"Tool {tool_name} returned: {result}") return None # 결과 유지 agent = LlmAgent( name="ToolAuditAgent", before_tool_callback=tool_guardrail, after_tool_callback=log_tool_result ) ``` ### 콜백 반환값의 의미 | 콜백 | 반환값 | 효과 | | ------------ | ------------- | ------------------------------------ | | before_agent | `None` | 에이전트 정상 실행 | | before_agent | `Content` | 에이전트 건너뜀, 반환된 Content 사용 | | before_model | `None` | LLM 정상 호출 | | before_model | `LlmResponse` | LLM 호출 건너뜀, 반환된 응답 사용 | | before_tool | `None` | 도구 정상 실행 | | before_tool | `dict` | 도구 실행 건너뜀, 반환된 결과 사용 | --- ## 9. Tools (도구) ### 개념 **Tool**은 에이전트에게 **외부 기능을 제공**하는 컴포넌트입니다. API 호출, 계산, 데이터베이스 조회 등을 수행할 수 있습니다. ### Tool 유형 #### 1. FunctionTool (함수 도구) Python 함수를 도구로 변환합니다. ```python from google.adk.tools import FunctionTool def get_weather(city: str, unit: str = "celsius") -> dict: """ 특정 도시의 날씨를 조회합니다. Args: city: 조회할 도시 이름 unit: 온도 단위 (celsius 또는 fahrenheit) Returns: 날씨 정보 딕셔너리 """ # 실제 API 호출 또는 시뮬레이션 return {"city": city, "temp": 20, "unit": unit} # 함수를 도구로 변환 weather_tool = FunctionTool(func=get_weather) # 에이전트에 도구 추가 agent = LlmAgent( name="WeatherAgent", tools=[weather_tool] ) ``` #### 2. AgentTool (에이전트를 도구로) 다른 에이전트를 도구처럼 호출할 수 있습니다. ```python from google.adk.tools import agent_tool # 전문 에이전트 정의 translator = LlmAgent( name="Translator", description="텍스트를 번역합니다." ) # 에이전트를 도구로 래핑 translator_tool = agent_tool.AgentTool(agent=translator) # 다른 에이전트에서 도구로 사용 main_agent = LlmAgent( name="MainAgent", tools=[translator_tool] ) ``` ### ToolContext 활용 도구 함수 내에서 Context에 접근하여 다양한 기능을 사용할 수 있습니다. ```python from google.adk.tools import ToolContext def advanced_tool(context: ToolContext, query: str) -> dict: # 1. 상태 읽기/쓰기 context.state["last_query"] = query previous = context.state.get("history", []) # 2. 메모리 검색 memory_results = await context.search_memory(query) # 3. 아티팩트 목록 조회 artifacts = context.list_artifacts() # 4. 인증 처리 if needs_auth: cred = await context.get_auth_response(oauth_config) return {"result": "processed"} ``` ### 도구 실행 흐름 ``` ┌──────────────────────────────────────────────────────────────┐ │ 도구 실행 흐름 │ └──────────────────────────────────────────────────────────────┘ 1. LLM이 도구 호출 결정 LLM Response: FunctionCall(name="get_weather", args={"city": "서울"}) 2. ADK가 도구 찾기 tools 목록에서 "get_weather" 찾음 3. before_tool_callback 실행 (있으면) 가드레일, 로깅 등 4. 도구 함수 실행 result = get_weather(city="서울") 5. after_tool_callback 실행 (있으면) 결과 후처리, 로깅 등 6. FunctionResponse 이벤트 생성 Event(content=FunctionResponse(name="get_weather", response=result)) 7. LLM에게 결과 전달 LLM이 결과를 보고 다음 응답 생성 ``` --- ## 10. 핵심 개념 간의 관계 ### 전체 관계도 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ ADK 핵심 개념 관계도 │ └─────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────┐ │ Runner │ │ (오케스트레이터) │ └─────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ SessionSvc │ │ MemorySvc │ │ ArtifactSvc │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ ▼ │ ┌─────────────────┐ │ │ Session │ │ │ ┌───────────┐ │ │ │ │ State │◀─┼──────┼──── 에이전트가 읽고/씀 │ └───────────┘ │ │ │ ┌───────────┐ │ │ │ │ Events │ │ │ │ └───────────┘ │ │ └─────────────────┘ │ │ │ ▼ ▼ ┌─────────────────────────────────────────┐ │ InvocationContext │ │ ┌─────────────────────────────────┐ │ │ │ session, state, services, agent │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌──────────┐ ┌──────────┐ │ Callbacks │ │ Agent │ │ Tools │ │ │ │ │ │ │ │ Callback │ │ │ │ Tool │ │ Context │ │ │ │ Context │ └────────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────┼─────────────┘ ▼ ┌────────────┐ │ Event │ │ ┌──────┐ │ │ │Actions│ ──▶ state_delta │ └──────┘ │ escalate └────────────┘ transfer ``` ### 데이터 흐름 요약 ``` 1. 사용자 입력 └──▶ Runner.run_async(message) 2. 세션 준비 └──▶ SessionService.get_session() → Session 3. 컨텍스트 생성 └──▶ InvocationContext(session, services, ...) 4. 콜백 실행 └──▶ before_agent_callback(CallbackContext) 5. 에이전트 실행 └──▶ Agent.run(InvocationContext) │ ├──▶ before_model_callback ├──▶ LLM 호출 ├──▶ after_model_callback │ ├──▶ [도구 호출 시] │ ├──▶ before_tool_callback │ ├──▶ Tool 실행(ToolContext) │ └──▶ after_tool_callback │ └──▶ yield Event 6. 이벤트 처리 └──▶ SessionService.append_event(event) └──▶ state_delta 적용 └──▶ events 히스토리 추가 7. 콜백 실행 └──▶ after_agent_callback(CallbackContext) 8. 결과 반환 └──▶ yield event to caller ``` ### 핵심 포인트 정리 | 개념 | 역할 | 비유 | | ------------ | -------------- | ------------------- | | **Session** | 대화 컨테이너 | 전화 통화 한 건 | | **State** | 대화 중 메모 | 메모장/스크래치패드 | | **Event** | 활동 기록 단위 | 대화 기록의 한 줄 | | **Context** | 실행 정보 묶음 | 업무 문맥/배경 정보 | | **Runner** | 실행 조율자 | 지휘자 | | **Memory** | 장기 기억 | 도서관 아카이브 | | **Callback** | 실행 중 개입점 | 체크포인트/관문 | | **Tool** | 외부 기능 | 도구 상자 | --- ## 부록: 자주 묻는 질문 (FAQ) ### Q1: State와 Memory의 차이는? - **State**: 현재 세션 내에서만 유효, 키-값 직접 접근 - **Memory**: 여러 세션에 걸쳐 유지, 검색 쿼리로 접근 ### Q2: output_key가 없으면 어떻게 되나요? 에이전트의 응답이 state에 자동 저장되지 않습니다. 다음 에이전트에서 이전 응답을 참조하려면 output_key를 설정하거나, 직접 콜백/도구에서 state를 업데이트해야 합니다. ### Q3: InMemorySessionService는 프로덕션에서 쓸 수 있나요? 권장하지 않습니다. 앱 재시작 시 모든 데이터가 손실됩니다. 프로덕션에서는 Database-backed 또는 Cloud-based 구현체를 사용하세요. ### Q4: 콜백에서 Content를 반환하면 무슨 일이 일어나나요? before_agent_callback에서 Content를 반환하면 에이전트 실행이 건너뛰어지고, 반환된 Content가 에이전트의 응답으로 사용됩니다. 이를 통해 조건부 단축(short-circuit)을 구현할 수 있습니다. ### Q5: LoopAgent의 escalate는 어떻게 동작하나요? sub_agent가 Event를 생성할 때 `actions=EventActions(escalate=True)`를 설정하면, LoopAgent가 이를 감지하고 즉시 루프를 종료합니다. max_iterations에 도달하기 전에 조건부로 종료할 때 사용합니다. --- ## 참고 자료 - [ADK 공식 문서](https://google.github.io/adk-docs/) - [ADK GitHub (Python)](https://github.com/google/adk-python) - [Vertex AI Agent Engine](https://cloud.google.com/vertex-ai/docs/agents)