Megumin-chat / docs /Google-ADK.md
Junhoee's picture
Upload 3 files
eae04fe verified
# 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)