.env 파일에 키 추가: 이 노트북과 동일한 디렉토리에 .env 파일을 생성하고 다음과 같이 API 키를 추가하십시오: plaintext GOOGLE_API_KEY=발급받은_api_키를_여기에_입력
API 인증 확인: 아래 셀을 실행하십시오.
import osfrom dotenv import load_dotenvload_dotenv()try: GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]print("✅ Gemini API 키 설정 완료.")exceptExceptionas e:print(f"🔑 인증 오류: 'GOOGLE_API_KEY'를 추가했는지 확인해 주세요: {e}")from google.adk.agents import Agentfrom google.adk.runners import InMemoryRunnerfrom google.adk.tools import google_searchfrom google.genai import typesprint("✅ ADK 구성 요소가 성공적으로 임포트되었습니다.")
✅ Gemini API 키 설정 완료.
✅ ADK 구성 요소가 성공적으로 임포트되었습니다.
2 Day1: Introduction to Agents
2.1 🧠 AI 에이전트 시스템 핵심 요약
AI 에이전트는 언어 모델(LM)의 추론 능력(두뇌)과 행동 능력(손)을 결합하여 자율적으로 문제를 해결하는 애플리케이션입니다.
2.1.1 에이전트의 정의와 구성 요소
구성 요소
역할(기능)
비유
모델(Model)
정보 처리, 옵션 평가 및 결정(추론 엔진)
두뇌
도구(Tools)
외부 세계(API, 코드, 데이터)와 연결하여 행동 실행
손
오케스트레이션 계층(Orchestration)
운영 루프 관리, 계획, 메모리, 추론 전략 실행(제어 프로세스)
신경계
🔑 핵심 작동 원리: 에이전트는 컨텍스트 창 큐레이션 시스템입니다. 목표 달성을 위해 시스템 지침, 사용자 입력, 도구 결과를 조합하여 모델에 입력하는 반복적인 루프를 실행합니다.
2.1.2 에이전트의 문제 해결 과정(순환적 5단계)
임무 받기(Get Mission): 사용자 요청 또는 자동화된 목표 수신.
상황 파악(Scan Scene): 메모리, 도구 등 사용 가능한 자원에 접근하여 컨텍스트 수집.
심사숙고(Think It Through): 모델을 통해 상황을 분석하고 실행 가능한 계획(연쇄적 추론) 수립.
행동 취하기(Take Action): 계획의 첫 단계를 실행하고 적절한 도구를 호출.
관찰 및 반복(Observe & Iterate): 행동 결과를 관찰하고 컨텍스트에 추가한 후, 목표 달성 시까지 3단계로 돌아가 반복.
2.1.3 에이전트 시스템의 분류(Level 0 ~ Level 4)
에이전트의 복잡성과 능력에 따라 5단계로 분류됩니다.
Level
명칭
주요 능력
Level 0
핵심 추론 시스템
도구, 메모리 없이 사전 훈련된 지식만 사용(기본 LM)
Level 1
연결된 문제 해결사
외부 도구를 사용해 실시간 데이터 접근 및 단순 작업 수행
Level 2
전략적 문제 해결사
다단계 목표 전략 계획, 컨텍스트 엔지니어링 능력 보유(고도화된 단일 에이전트)
Level 3
협력적 다중 에이전트
에이전트들이 서로를 도구로 사용, 분업화하여 복잡한 워크플로우 자동화
Level 4
자가 진화 시스템
자신의 능력 격차를 식별하고, 동적으로 새로운 도구/에이전트를 생성하여 적응 및 진화
2.1.4 프로덕션 구축 및 운영(Agent Ops)
불확실한 에이전트 시스템을 안정적인 기능으로 전환하기 위한 규율적 접근 방식입니다.
측정(Measure): 목표 완료율, 운영 비용 등 비즈니스 영향을 측정하는 KPI 정의.
평가(Quality): 단순 통과/실패 대신 LM Judge(AI 심사위원)를 사용하여 응답 품질 평가.
디버깅(Debug): OpenTelemetry 추적(Traces)을 사용하여 에이전트의 전체 실행 경로를 단계별로 기록하고 진단.
인간 피드백(Human Feedback): 사용자 피드백을 수집하여 시스템 개선을 위한 새로운 테스트 사례로 활용.
2.1.5 보안 및 거버넌스
에이전트 시스템의 안전성과 관리 효율성을 위한 조치입니다.
에이전트 정체성: 에이전트에 자체적인 확인 가능한 정체성(principal)을 부여하여 최소 권한 원칙 적용 기반 마련.
보안 계층: 결정론적 가드레일(하드 코딩된 정책)과 추론 기반 방어(보안 모델)를 결합한 하이브리드 접근 방식 사용(프롬프트 주입 방지).
중앙 게이트웨이(Control Plane): 에이전트 활동을 관리하고 정책을 시행하며, 모든 트랜잭션의 인증/인가 및 로깅을 처리하는 중앙 진입점 구현.
2.1.6 에이전트의 진화 및 학습
에이전트는 런타임 경험(로그, 피드백)을 통해 지속적으로 개선됩니다.
학습 방법: 향상된 컨텍스트 엔지니어링(프롬프트/예시 최적화) 및 새로운 도구의 생성 및 최적화를 통해 이루어집니다.
Agent Gym: 시뮬레이션 환경에서 에이전트가 오프라인으로 시행착오를 겪으며 시스템을 최적화하는 차세대 훈련 방식.
2.1.7 고급 에이전트 사례
Google Co-Scientist: 가설 생성, 검토, 개선을 통해 과학적 발견을 가속화하는 다중 에이전트 시스템.
AlphaEvolve Agent: Gemini 모델의 코드 생성 및 자동 평가 시스템을 결합하여 복잡한 알고리즘을 발견하고 최적화.
2.2 🤖 ADK를 사용한 첫 AI 에이전트 만들기
2.2.1 🤔 AI 에이전트란 무엇인가요?
여러분은 아마도 이전에 Gemini 같은 LLM을 사용해 보셨을 겁니다. 프롬프트를 주면 텍스트 응답을 받는 방식이죠.
프롬프트 -> LLM -> 텍스트
AI 에이전트는 여기서 한 단계 더 나아갑니다. 에이전트는 생각하고, 행동을 취하며, 그 행동의 결과를 관찰하여 더 나은 답을 제공할 수 있습니다.
프롬프트 -> 에이전트 -> 사고 -> 행동 -> 관찰 -> 최종 답변
2.2.2 에이전트 정의하기
이제 에이전트를 만들어 봅시다. 에이전트가 무엇을 해야 하고 어떻게 작동해야 하는지를 알려주는 핵심 속성을 설정하여 Agent를 구성할 것입니다. 더 자세히 알아보려면 ADK의 에이전트 관련 문서를 확인해 보세요. 우리가 설정할 주요 속성은 다음과 같습니다:
name 및 description: 에이전트를 식별하는 간단한 이름과 설명입니다.
model: 에이전트의 추론 능력을 제공할 특정 LLM입니다. 우리는 "gemini-2.5-flash-lite"를 사용할 것입니다.
instruction: 에이전트의 길잡이 프롬프트입니다. 에이전트에게 목표와 행동 방식을 알려줍니다.
tools: 에이전트가 사용할 수 있는 도구 목록입니다. 시작으로, 최신 정보를 온라인에서 찾을 수 있게 해주는 Google Search 도구를 제공할 것입니다.
root_agent = Agent( name="helpful_assistant", model="gemini-2.5-flash-lite", description="일반적인 질문에 답할 수 있는 간단한 에이전트입니다.", instruction="당신은 도움이 되는 조수입니다. 최신 정보가 필요하거나 확신이 서지 않을 때는 Google 검색을 사용하세요.", tools=[google_search],)
2.2.3 에이전트 실행하기
이제 에이전트를 활성화하고 쿼리를 보내봅시다. 이 작업을 수행하려면 오케스트레이터 역할을 하는 ADK 내의 핵심 구성 요소인 Runner가 필요합니다. 러너는 대화를 관리하고, 에이전트로 메시지를 보내며 응답을 처리합니다.
# `InMemoryRunner`를 생성하고 우리가 만든 `root_agent`를 사용하도록 설정합니다.runner = InMemoryRunner(agent=root_agent, app_name="agents")# `.run_debug()` 메서드를 호출하여 프롬프트를 보내고 답변을 얻을 수 있습니다.response =await runner.run_debug("Google의 Agent Development Kit(ADK)는 무엇인가요? 이 SDK는 어떤 언어로 사용할 수 있나요?")
### Created new session: debug_session_id
User > Google의 Agent Development Kit(ADK)는 무엇인가요? 이 SDK는 어떤 언어로 사용할 수 있나요?
helpful_assistant > Google의 Agent Development Kit(ADK)는 AI 에이전트 개발 및 배포를 위한 유연하고 모듈식 오픈 소스 프레임워크입니다. ADK는 개발자가 복잡한 워크플로우를 갖춘 AI 에이전트를 더 쉽게 생성, 배포 및 조정할 수 있도록 설계되었습니다. 이 프레임워크는 Gemini 모델 및 Google 생태계에 최적화되어 있지만, 모델이나 배포 환경에 구애받지 않으며 다른 프레임워크와의 호환성을 위해 구축되었습니다.
ADK는 현재 **Python**, **Java**, **Go** 언어를 지원합니다. Google은 향후 더 많은 언어를 지원할 예정이라고 밝혔습니다.
2.3 🤔 왜 다중 에이전트 시스템를 만들어야 하는가?
단일 에이전트도 많은 일을 할 수 있습니다. 하지만 작업이 복잡해지면 어떻게 될까요? 연구, 글쓰기, 편집, 사실 확인을 한 번에 모두 처리하려는 단일 “모놀리식(Monolithic)” 에이전트는 여러 문제를 발생시킵니다.
프롬프트 복잡성: 에이전트의 지침 프롬프트가 길어지고 혼란스러워집니다.
디버깅의 어려움: 어느 부분에서 실패했는지 파악하기 어렵습니다.
신뢰성 저하: 유지보수가 어렵고 종종 신뢰할 수 없는 결과를 만듭니다.
2.3.1 해결책: 전문가 에이전트 팀
하나의 “만능” 에이전트 대신, 현실 세계의 팀처럼 협업하는 단순하고 전문화된 에이전트들로 구성된 다중 에이전트 시스템을 구축할 수 있습니다. 각 에이전트는 하나의 명확한 임무(예: 한 에이전트는 연구만, 다른 에이전트는 글쓰기만)를 가집니다. 이러한 접근 방식은 에이전트를 더 쉽게 구축하고, 테스트하며, 함께 작동할 때 훨씬 강력하고 신뢰할 수 있게 만듭니다. 더 자세히 알아보려면 ADK의 LLM 에이전트 관련 문서를 확인해 보세요.
2.3.2 예시: 연구 및 요약 시스템
두 개의 전문화된 에이전트를 가진 시스템을 만들어 봅시다:
연구 에이전트(Research Agent): Google 검색을 사용하여 정보를 검색합니다.
요약 에이전트(Summarizer Agent): 연구 결과를 바탕으로 간결한 요약을 생성합니다.
from google.adk.agents import Agent, LoopAgent, ParallelAgent, SequentialAgentfrom google.adk.runners import InMemoryRunnerfrom google.adk.tools import AgentTool, FunctionTool, google_searchfrom google.genai import types# 연구 에이전트: 역할은 google_search 도구를 사용하고 검색 결과를 제시하는 것입니다.research_agent = Agent( name="ResearchAgent", model="gemini-2.5-flash-lite", instruction="""당신은 전문적인 연구 에이전트입니다. 당신의 유일한 임무는 Google Search 도구를 사용하여 주어진 주제에 대한 2~3가지 관련 정보를 찾고, 출처를 명시하여 그 결과를 제시하는 것입니다.""", tools=[google_search], output_key="research_findings", # 이 에이전트의 결과는 이 키(key)를 사용하여 세션 상태에 저장됩니다.)# 요약 에이전트: 역할은 수신한 텍스트를 요약하는 것입니다.summarizer_agent = Agent( name="SummarizerAgent", model="gemini-2.5-flash-lite",# 지침은 명확한 출력 형식을 위해 글머리 기호 목록을 요청하도록 수정되었습니다. instruction="""제공된 {research_findings} 결과를 읽으세요: 핵심 요점 3~5가지를 포함하는 간결한 요약을 글머리 기호 목록으로 작성하세요.""", output_key="final_summary",)# 루트 코디네이터: 서브 에이전트를 도구로 호출하여 워크플로우를 조직합니다.root_agent = Agent( name="ResearchCoordinator", model="gemini-2.5-flash-lite",# 이 지침은 루트 에이전트에게 도구(다른 에이전트)를 사용해야 하는 방식을 알려줍니다. instruction="""당신은 연구 코디네이터입니다. 당신의 목표는 워크플로를 조정하여 사용자의 질문에 답하는 것입니다. 1. 먼저, 사용자에게 제공된 주제에 대한 관련 정보를 찾기 위해 반드시 `ResearchAgent` 도구를 호출해야 합니다. 2. 다음으로, 연구 결과를 받은 후 간결한 요약을 작성하기 위해 반드시 `SummarizerAgent` 도구를 호출해야 합니다. 3. 마지막으로, 최종 요약을 당신의 응답으로 사용자에게 명확하게 제시하세요.""",# 서브 에이전트를 `AgentTool`로 감싸서 루트 에이전트가 호출할 수 있는 도구로 만듭니다. tools=[AgentTool(research_agent), AgentTool(summarizer_agent)],)
runner = InMemoryRunner(agent=root_agent, app_name="agents")response =await runner.run_debug("양자 컴퓨팅의 최신 발전 동향은 무엇이며, 이것이 인공지능(AI)에 어떤 의미가 있나요?")
### Created new session: debug_session_id
User > 양자 컴퓨팅의 최신 발전 동향은 무엇이며, 이것이 인공지능(AI)에 어떤 의미가 있나요?
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
ResearchCoordinator > ## 양자 컴퓨팅 발전 동향 및 AI 영향 요약
* **양자 컴퓨팅 기술 발전**: 큐비트 수 증가 및 오류율 감소를 목표로 IBM, Google 등 주요 기업들이 경쟁적으로 연구 개발 중이며, 2025년 '양자 과학 및 기술의 국제 연도'를 계기로 글로벌 양자 기술 투자가 확대되고 있습니다.
* **AI 능력 혁신**: 양자 컴퓨터의 빠른 연산 능력은 AI의 데이터 학습 및 분석 속도를 획기적으로 향상시키고, 복잡한 문제 해결을 가능하게 하여 AI 성능을 한 단계 끌어올릴 잠재력을 지닙니다.
* **광범위한 응용 가능성**: 양자 AI는 신약 개발, 기후 과학, 금융, 마케팅 등 다양한 산업 분야에서 기존 AI의 한계를 극복하고 혁신적인 솔루션을 제공할 것으로 기대됩니다.
* **상용화까지의 과제**: 현재는 연구 단계에 머물러 있으며, 큐비트의 안정성 확보, 오류율 감소, 제어 기술 고도화 등 기술적 난제를 해결하는 것이 상용화를 위한 핵심 과제입니다.
방금 첫 번째 다중 에이전트 시스템을 구축했습니다. 하나의 “코디네이터” 에이전트를 사용하여 워크플로우를 관리했는데, 이는 강력하고 유연한 패턴입니다. 하지만 LLM의 지침에 의존하여 순서를 제어하는 것은 때로는 예측 불가능할 수 있습니다. 다음으로 실행 순서가 보장되는 다른 패턴을 살펴보겠습니다.
2.4 🚥 순차적 워크플로우
이전의 다중 에이전트 시스템은 단계별 순서를 강제하기 위해 상세한 지침 프롬프트에 의존했고 단계가 복잡해지면 신뢰할 수 없습니다. 복잡한 시스템은 단계를 건너뛰거나, 잘못된 순서로 실행하거나, “막히는” 등 프로세스를 예측할 수 없게 만들 수 있습니다.
2.4.1 해결책: 고정된 파이프라인
작업이 특정 순서로 발생해야 할 때는 SequentialAgent를 사용할 수 있습니다. 이 에이전트는 마치 조립 라인처럼 작동하여 나열된 순서대로 각 서브 에이전트를 정확하게 실행합니다. 한 에이전트의 출력이 자동으로 다음 에이전트의 입력이 되면서 예측 가능하고 신뢰할 수 있는 워크플로우를 생성합니다. 더 자세히 알아보려면 ADK의 순차 에이전트 관련 문서를 확인해 보세요.
2.4.2 예시: 순차 에이전트를 사용한 블로그 게시물 생성 에이전트
graph LR
A[User Input: Blog about AI] --> B[Outline Agent]
B -->|blog_outline| C[Writer Agent]
C -->|blog_draft| D[Editor Agent]
D -->|final_blog| E[Output]
style B fill:#ffcccc
style C fill:#ccffcc
style D fill:#ccccff
위 모식도와 같이 세 개의 전문화된 에이전트로 구성된 시스템을 구축해 봅시다:
개요 에이전트 (Outline Agent): 주어진 주제에 대한 블로그 개요를 작성합니다.
작성 에이전트 (Writer Agent): 블로그 초안을 작성합니다.
편집 에이전트 (Editor Agent): 명확성과 구조를 위해 블로그 초안을 편집합니다.
# 1.개요 에이전트: 초기 블로그 게시물 개요를 생성합니다.outline_agent = Agent( name="OutlineAgent", model="gemini-2.5-flash-lite", instruction="""주어진 주제에 대해 다음 요소를 포함하는 블로그 개요를 작성합니다: 1. 시선을 끄는 제목 2. 독자를 사로잡는 도입부 3. 각 섹션별 2~3개의 핵심 내용을 담은 3~5개의 본문 섹션 4. 결론 """, output_key="blog_outline", # 이 에이전트의 결과는 이 키를 사용하여 세션 상태에 저장됩니다.)# 2.작성 에이전트: 이전 에이전트의 개요를 기반으로 전체 블로그 게시물을 작성합니다.writer_agent = Agent( name="WriterAgent", model="gemini-2.5-flash-lite",# `{blog_outline}` 플레이스홀더는 이전 에이전트의 출력에서 세션 상태 값을 자동으로 주입합니다. instruction="""이 개요를 엄격하게 따르세요: {blog_outline} 매력적이고 유익한 어조로 200~300단어 분량의 간결한 블로그 게시물을 작성하세요.""", output_key="blog_draft", # 이 에이전트의 결과는 이 키로 저장됩니다.)# 3.편집 에이전트: 작성 에이전트의 초안을 편집하고 다듬습니다.editor_agent = Agent( name="EditorAgent", model="gemini-2.5-flash-lite",# 이 에이전트는 작성 에이전트의 출력인 `{blog_draft}`를 받습니다. instruction="""이 초안을 편집합니다: {blog_draft} 당신의 임무는 문법 오류를 수정하고, 흐름과 문장 구조를 개선하며, 전반적인 명확성을 높여 텍스트를 다듬는 것입니다.""", output_key="final_blog", # 이것은 전체 파이프라인의 최종 결과물입니다.)root_agent = SequentialAgent( name="BlogPipeline", sub_agents=[outline_agent, writer_agent, editor_agent],)runner = InMemoryRunner(agent=root_agent, app_name="agents")response =await runner.run_debug("소프트웨어 개발자를 위한 다중 에이전트 시스템의 이점에 대한 블로그 게시물을 작성하세요.")
### Created new session: debug_session_id
User > 소프트웨어 개발자를 위한 다중 에이전트 시스템의 이점에 대한 블로그 게시물을 작성하세요.
OutlineAgent > ## 소프트웨어 개발자를 위한 다중 에이전트 시스템: 협업의 미래를 열다
**도입부:**
빠르게 변화하는 소프트웨어 개발 환경에서 효율성과 협업은 성공의 핵심입니다. 오늘날 우리는 복잡하고 방대한 소프트웨어 시스템을 구축하고 있으며, 단일 개발자나 팀의 역량을 넘어서는 경우가 많습니다. 바로 여기서 다중 에이전트 시스템(Multi-Agent Systems, MAS)이 등장합니다. MAS는 여러 자율적인 소프트웨어 에이전트들이 서로 상호작용하며 공동의 목표를 달성하는 시스템을 의미합니다. 이 블로그 게시물에서는 소프트웨어 개발자가 MAS를 통해 얻을 수 있는 놀라운 이점들을 살펴보고, 협업의 새로운 시대를 어떻게 열어갈 수 있는지 탐구해 보겠습니다.
---
**본문 섹션 1: 복잡한 문제 해결 능력의 비약적 향상**
* **분산된 문제 해결:** MAS는 복잡한 문제를 더 작고 관리 가능한 하위 문제로 분해하고, 각 하위 문제를 전담하는 에이전트에게 할당할 수 있습니다. 이는 대규모 시스템의 복잡성을 효과적으로 관리하고 해결하는 데 도움을 줍니다.
* **다양한 관점과 창의성:** 각 에이전트는 특정 기능이나 데이터에 특화되어 있어, 문제에 대한 다양한 관점을 제공합니다. 이러한 다양한 관점의 결합은 단일 시스템에서는 생각하기 어려운 혁신적인 해결책을 도출할 수 있습니다.
* **유연한 시스템 설계:** 에이전트 간의 유연한 상호작용을 통해 시스템의 특정 부분을 수정하거나 확장하기가 용이합니다. 이는 변화하는 요구사항에 빠르게 적응해야 하는 현대 소프트웨어 개발에 매우 유리합니다.
---
**본문 섹션 2: 생산성 및 효율성 극대화**
* **병렬 처리 및 동시 작업:** MAS는 여러 에이전트가 동시에 작업을 수행할 수 있도록 하여 개발 프로세스의 속도를 크게 향상시킵니다. 이는 코드 생성, 테스트, 디버깅 등 다양한 개발 작업에 적용될 수 있습니다.
* **반복적인 작업 자동화:** 코드 검토, 문서 생성, 빌드 및 배포와 같은 반복적이고 시간이 많이 소요되는 작업을 자동화하는 에이전트를 구축할 수 있습니다. 이를 통해 개발자는 더욱 창의적이고 가치 있는 작업에 집중할 수 있습니다.
* **지식 공유 및 재활용:** MAS는 개발 팀 내에서 지식과 경험을 효과적으로 공유하고 재활용하는 메커니즘을 제공합니다. 특정 에이전트가 문제 해결 경험을 축적하고 이를 다른 에이전트와 공유함으로써 팀 전체의 역량이 향상됩니다.
---
**본문 섹션 3: 적응성과 복원력 강화**
* **동적 환경에 대한 대응:** MAS는 예측 불가능하거나 동적으로 변화하는 환경에서도 시스템을 안정적으로 유지할 수 있도록 설계될 수 있습니다. 에이전트들은 주변 환경의 변화를 감지하고 스스로 행동을 조정할 수 있습니다.
* **장애 허용 및 내결함성:** 일부 에이전트가 실패하더라도 시스템 전체가 멈추지 않도록 설계할 수 있습니다. 다른 에이전트들이 실패한 에이전트의 역할을 대신하거나, 시스템은 부분적인 기능만으로도 작동을 계속할 수 있습니다.
* **학습 및 개선:** MAS는 에이전트들이 경험을 통해 학습하고 성능을 개선할 수 있는 능력을 부여할 수 있습니다. 시간이 지남에 따라 시스템은 더욱 효율적이고 효과적으로 작동하게 됩니다.
---
**본문 섹션 4: 새로운 가능성과 미래 전망**
* **자율적인 개발 도구:** MAS는 코드 생성, 최적화, 버그 수정 등을 자율적으로 수행하는 개발 도구를 만드는 데 활용될 수 있습니다. 개발자는 이러한 도구와 협력하여 더욱 생산적으로 작업할 수 있습니다.
* **분산 시스템 및 IoT 환경:** 수많은 장치와 서비스가 상호작용하는 분산 시스템이나 IoT 환경에서 MAS는 효과적인 관리 및 제어 솔루션을 제공합니다.
* **인공지능과의 시너지:** MAS는 AI 기술과 결합하여 더욱 지능적이고 자율적인 소프트웨어 개발 생태계를 구축할 수 있습니다.
---
**결론:**
소프트웨어 개발자를 위한 다중 에이전트 시스템은 단순한 기술적 개념을 넘어, 개발 방식 자체를 혁신할 잠재력을 지니고 있습니다. 복잡한 문제 해결, 생산성 향상, 적응성 강화 등 MAS가 제공하는 이점들은 개발 팀이 더욱 효율적으로 협업하고, 더 나은 소프트웨어를 더 빠르게 만들 수 있도록 지원합니다. MAS의 개념을 이해하고 이를 개발 프로세스에 적용하려는 시도는 앞으로 소프트웨어 개발의 미래를 개척하는 중요한 발걸음이 될 것입니다. 지금 바로 다중 에이전트 시스템의 세계를 탐험하고, 협업의 새로운 지평을 열어보세요!
WriterAgent > ## 소프트웨어 개발자를 위한 다중 에이전트 시스템: 협업의 미래를 열다
현대의 소프트웨어 개발은 끊임없이 진화하며, 복잡한 시스템을 구축하는 데 있어 효율성과 협업의 중요성은 더욱 커지고 있습니다. 단일 개발자나 팀의 역량을 뛰어넘는 거대한 프로젝트들이 늘어나면서, 우리는 새로운 접근 방식을 모색해야 합니다. 바로 여기서 **다중 에이전트 시스템(Multi-Agent Systems, MAS)**이 빛을 발합니다. MAS는 여러 자율적인 소프트웨어 에이전트들이 서로 협력하여 공동의 목표를 달성하는 시스템으로, 개발 방식을 혁신할 잠재력을 가지고 있습니다.
MAS를 통해 개발자는 **복잡한 문제를 해결하는 능력을 비약적으로 향상**시킬 수 있습니다. 거대한 과제를 작고 관리 가능한 하위 문제로 나누어 각기 전문화된 에이전트에게 맡김으로써, 시스템의 복잡성을 효과적으로 관리할 수 있습니다. 각 에이전트는 고유한 관점과 전문성을 바탕으로 문제에 접근하며, 이러한 다양한 시각의 결합은 놀라운 창의적인 해결책을 이끌어낼 수 있습니다. 또한, 에이전트 간의 유연한 상호작용 덕분에 시스템의 수정 및 확장이 용이해져, 변화하는 요구사항에 신속하게 대응하는 데 유리합니다.
더 나아가 MAS는 **생산성과 효율성을 극대화**합니다. 여러 에이전트가 동시에 작업을 수행하는 병렬 처리를 통해 개발 프로세스의 속도를 크게 높일 수 있습니다. 코드 생성, 테스트, 디버깅 등 다양한 개발 작업에 MAS를 적용하여 시간을 단축할 수 있습니다. 코드 검토, 문서 생성, 빌드 및 배포와 같은 반복적인 작업은 에이전트를 통해 자동화할 수 있으며, 이를 통해 개발자는 더욱 가치 있는 핵심 업무에 집중할 수 있습니다. MAS는 팀 내 지식 공유와 재활용을 촉진하여 전체 팀의 역량을 강화하는 데에도 크게 기여합니다.
MAS의 또 다른 강력한 장점은 **적응성과 복원력을 강화**한다는 점입니다. 예측 불가능한 동적 환경에서도 시스템을 안정적으로 유지할 수 있도록 설계될 수 있으며, 에이전트들은 주변 환경 변화에 맞춰 스스로 행동을 조절합니다. 설령 일부 에이전트가 실패하더라도 시스템 전체가 멈추지 않는 장애 허용 설계가 가능하며, 다른 에이전트들이 역할을 대신하거나 부분적인 기능만으로도 작동을 지속할 수 있습니다. 또한, 에이전트들이 경험을 통해 학습하고 성능을 개선함으로써 시스템은 시간이 지남에 따라 더욱 효율적으로 진화합니다.
소프트웨어 개발자를 위한 다중 에이전트 시스템은 단순한 기술적 개념을 넘어, **협업의 미래를 열어가는 핵심 열쇠**입니다. MAS는 복잡한 문제 해결, 생산성 향상, 그리고 뛰어난 적응성을 제공하며, 개발 팀이 더 나은 소프트웨어를 더 빠르고 효율적으로 만들 수 있도록 지원합니다. 지금 바로 다중 에이전트 시스템의 가능성을 탐구하고, 개발의 새로운 시대를 함께 열어가세요!
EditorAgent > ## 소프트웨어 개발자를 위한 다중 에이전트 시스템: 협업의 미래를 열다
현대의 소프트웨어 개발 환경은 끊임없이 변화하며, 그 복잡성 또한 증가하고 있습니다. 이러한 환경에서 효율성과 협업은 성공적인 프로젝트 완수를 위한 필수 요소가 되었습니다. 단일 개발자나 팀의 역량을 뛰어넘는 대규모 프로젝트가 늘어나면서, 우리는 개발 방식을 혁신할 새로운 접근 방식의 필요성을 절감하고 있습니다. 바로 이 지점에서 **다중 에이전트 시스템(Multi-Agent Systems, MAS)**이 주목받고 있습니다. MAS는 여러 자율적인 소프트웨어 에이전트들이 상호작용하고 협력하여 공동의 목표를 달성하는 시스템으로, 소프트웨어 개발 방식에 혁신적인 변화를 가져올 잠재력을 지니고 있습니다.
### 복잡한 문제 해결 능력의 비약적 향상
MAS는 복잡하고 거대한 소프트웨어 개발 과제를 작고 관리 가능한 하위 문제로 분해하여, 각 전문화된 에이전트에게 할당함으로써 시스템의 복잡성을 효과적으로 관리할 수 있도록 합니다. 각 에이전트는 고유한 전문성과 관점을 바탕으로 문제에 접근하며, 이러한 다양한 시각의 융합은 예상치 못한 창의적인 해결책을 이끌어낼 수 있습니다. 또한, 에이전트 간의 유연한 상호작용은 시스템의 수정 및 확장을 용이하게 하여, 빠르게 변화하는 요구사항에 대한 신속한 대응을 가능하게 합니다.
### 생산성 및 효율성 극대화
MAS는 여러 에이전트가 동시에 작업을 수행하는 병렬 처리 방식을 통해 개발 프로세스의 속도를 획기적으로 높일 수 있습니다. 코드 생성, 테스트, 디버깅과 같은 다양한 개발 작업에 MAS를 적용하면 소요 시간을 크게 단축할 수 있습니다. 더 나아가, 코드 검토, 문서 생성, 빌드 및 배포와 같은 반복적이고 시간이 많이 소요되는 작업들을 자동화하는 에이전트를 구축함으로써, 개발자는 더욱 가치 있고 창의적인 핵심 업무에 집중할 수 있습니다. MAS는 팀 내 지식 공유와 재활용을 촉진하여 전체 팀의 역량을 강화하는 데에도 크게 기여합니다.
### 적응성과 복원력 강화
MAS는 예측 불가능하고 동적으로 변화하는 환경에서도 시스템을 안정적으로 유지할 수 있도록 설계될 수 있습니다. 에이전트들은 주변 환경의 변화를 감지하고 스스로 행동을 조절하며, 설령 일부 에이전트가 실패하더라도 시스템 전체가 멈추지 않는 장애 허용 설계를 구현할 수 있습니다. 다른 에이전트들이 실패한 에이전트의 역할을 대신하거나, 시스템은 부분적인 기능만으로도 작동을 지속할 수 있습니다. 또한, 에이전트들이 경험을 통해 학습하고 지속적으로 성능을 개선함으로써, 시스템은 시간이 지남에 따라 더욱 효율적이고 효과적으로 진화합니다.
### 협업의 미래를 열다
소프트웨어 개발자를 위한 다중 에이전트 시스템은 단순한 기술적 개념을 넘어, 협업의 미래를 열어가는 핵심 열쇠입니다. MAS는 복잡한 문제 해결 능력 향상, 생산성 및 효율성 극대화, 그리고 탁월한 적응성 및 복원력 강화를 제공합니다. 이를 통해 개발 팀은 더욱 나은 소프트웨어를 더 빠르고 효율적으로 만들 수 있습니다. 지금 바로 다중 에이전트 시스템의 무한한 가능성을 탐구하고, 소프트웨어 개발의 새로운 시대를 함께 열어가세요!
2.5 🛣️ 병렬 워크플로우 에이전트
이전의 순차적 에이전트 워크플로우는 훌륭하지만 조립 라인과 같습니다. 각 단계는 이전 단계가 완료될 때까지 기다려야 합니다. 만약 서로 의존하지 않는 여러 작업이 있다면 어떨까요? 예를 들어, 세 가지 다른 주제를 연구하는 경우입니다. 이들을 순차적으로 실행하면 느리고 비효율적이어서, 각 작업이 불필요하게 기다리는 병목 현상이 발생합니다.
2.5.1 해결책: 별령 실행하기
독립적인 작업이 있다면 ParallelAgent를 사용하여 모든 작업을 동시에 실행할 수 있습니다. 이 에이전트는 모든 서브 에이전트를 병렬로 실행하여 워크플로우 속도를 획기적으로 높입니다. 모든 병렬 작업이 완료되면, 그들의 결과를 하나로 합쳐서 최종 ‘통합자’ 단계로 전달할 수 있습니다. 더 자세히 알아보려면 ADK의 병렬 에이전트 관련 문서를 확인해 보세요.
2.5.2 예시: 병렬 다중 주제 에이전트
graph TD
A[사용자 요청: 3가지 주제 연구] --> B[병렬 실행]
B --> C[기술 연구원]
B --> D[건강 연구원]
B --> E[금융 연구원]
C --> F[통합자]
D --> F
E --> F
F --> G[통합 보고서]
style B fill:#ffffcc
style F fill:#ffccff
위 모식도와 같이 네 가지 에이전트로 구성된 시스템을 구축해 봅시다:
기술 연구원(Tech Researcher): AI/ML 뉴스 및 트렌드를 연구합니다.
건강 연구원(Health Researcher): 최신 의학 뉴스 및 트렌드를 연구합니다.
금융 연구원(Finance Researcher): 금융 및 핀테크 뉴스 및 트렌드를 연구합니다.
통합 에이전트(Aggregator Agent): 모든 연구 결과를 하나의 요약으로 통합합니다.
# 기술 연구원: AI 및 ML 트렌드에 중점을 둡니다.tech_researcher = Agent( name="TechResearcher", model="gemini-2.5-flash-lite", instruction="""최신 AI/ML 동향을 연구하세요. 3가지 주요 개발 사항,관련 주요 회사, 잠재적 영향을 포함해야 합니다. 보고서는 매우 간결하게 유지합니다(100단어).""", tools=[google_search], output_key="tech_research", # 이 에이전트의 결과는 이 키를 사용하여 세션 상태에 저장됩니다.)# 건강 연구원: 의료 혁신에 중점을 둡니다.health_researcher = Agent( name="HealthResearcher", model="gemini-2.5-flash-lite", instruction="""최근 의료 분야의 혁신적인 발전을 연구하세요. 3가지 중요한 발전 사항,실제 적용 사례, 예상되는 타임라인을 포함해야 합니다. 보고서는 간결하게 유지합니다(100단어).""", tools=[google_search], output_key="health_research", # 결과는 이 키로 저장됩니다.)# 금융 연구원: 핀테크 트렌드에 중점을 둡니다.finance_researcher = Agent( name="FinanceResearcher", model="gemini-2.5-flash-lite", instruction="""현재 핀테크 동향을 연구하세요. 3가지 핵심 동향,시장 영향, 그리고 미래 전망을 포함해야 합니다. 보고서는 간결하게 유지합니다(100단어).""", tools=[google_search], output_key="finance_research", # 결과는 이 키로 저장됩니다.)# 통합 에이전트: 병렬 단계 이후에 실행되어 결과를 종합합니다.aggregator_agent = Agent( name="AggregatorAgent", model="gemini-2.5-flash-lite",# 병렬 에이전트의 출력(현재 세션 상태에 있음)을 주입하기 위해 플레이스홀더를 사용합니다. instruction="""이 세 가지 연구 결과를 하나의 경영진 요약 보고서로 통합하세요: **기술 동향:**{tech_research} **건강 혁신:**{health_research} **금융 혁신:**{finance_research} 요약 보고서는 세 보고서 모두에서 공통되는 주제, 놀라운 연결점, 가장 중요한 핵심 사항을 강조해야 합니다. 최종 요약은 약 200단어여야 합니다.""", output_key="executive_summary", # 이것은 전체 시스템의 최종 결과물입니다.)# 병렬 에이전트: 모든 서브 에이전트를 동시에 실행합니다.parallel_research_team = ParallelAgent( name="ParallelResearchTeam", sub_agents=[tech_researcher, health_researcher, finance_researcher],)# 순차 에이전트: 높은 수준의 워크플로우를 정의합니다: 병렬 팀을 먼저 실행하고, 그 다음 통합 에이전트를 실행합니다.root_agent = SequentialAgent( name="ResearchSystem", sub_agents=[parallel_research_team, aggregator_agent],)runner = InMemoryRunner(agent=root_agent, app_name="agents")response =await runner.run_debug("기술, 건강, 금융 분야에 대한 일일 경영진 브리핑을 실행하세요.")
### Created new session: debug_session_id
User > 기술, 건강, 금융 분야에 대한 일일 경영진 브리핑을 실행하세요.
TechResearcher > ## 일일 경영진 브리핑: AI/ML 동향
**1. 생성형 AI의 발전:**
* **주요 개발:** OpenAI의 GPT-4o와 같은 멀티모달 AI는 텍스트, 이미지, 오디오를 실시간으로 이해하고 상호작용합니다. Anthropic의 Claude 3는 긴 컨텍스트 처리 능력과 추론 능력을 향상시켰습니다.
* **주요 기업:** OpenAI, Google (Gemini), Anthropic, Meta.
* **영향:** 콘텐츠 제작, 고객 서비스, 소프트웨어 개발 등 다양한 산업에서 혁신을 주도하며 생산성을 극대화할 잠재력을 지닙니다.
**2. AI 기반 신약 개발 가속화:**
* **주요 개발:** AI는 신약 후보 물질 발굴, 임상 시험 설계, 질병 예측 모델 개발에 활용되어 신약 개발 기간과 비용을 획기적으로 단축하고 있습니다.
* **주요 기업:** Recursion Pharmaceuticals, Exscientia, DeepMind (Google).
* **영향:** 희귀 질환 및 난치병 치료제 개발에 박차를 가하고 개인 맞춤형 의료 시대를 앞당길 것으로 기대됩니다.
**3. 금융 서비스에서의 AI 윤리 및 규제 강화:**
* **주요 개발:** AI 알고리즘의 편향성, 투명성 부족, 데이터 프라이버시 문제에 대한 우려가 커지면서 금융 당국은 AI 활용에 대한 윤리 지침 및 규제 마련에 착수하고 있습니다.
* **주요 기업:** BlackRock, JPMorgan Chase, Visa, Mastercard (AI 솔루션 활용 및 규제 대응).
* **영향:** AI 기반 금융 상품 및 서비스의 신뢰성을 높이고, 금융 시장의 공정성과 안정성을 확보하는 데 기여할 것입니다.
FinanceResearcher > ## 경영진 브리핑: 기술, 건강, 금융 분야 동향
**기술:** 2024년에는 인공지능(AI)과 머신러닝(ML)이 의료, 금융, 운송 등 다양한 산업에 영향을 미치며 계속 발전할 것입니다. AI 기반 시스템은 자연어 처리 능력 향상으로 고객 서비스 개선과 개인화된 상호 작용을 가능하게 합니다. 사물인터넷(IoT)의 확장, 증강/가상현실(AR/VR), 블록체인 기술의 확산도 주요 동향입니다.
**건강:** 헬스케어 기술은 놀라운 속도로 성장하고 있으며, 전자 건강 기록(EHR)과 의료 정보 통합이 핵심입니다. 원격 진료 및 원격 환자 모니터링이 보편화되고 있으며, 디지털 트윈 기술은 환자 관리와 시스템 유지보수에 활용됩니다. 맞춤형 헬스케어(PHC)는 유전자 분석, 생체 데이터 가시화, AI 기반 빅데이터 분석을 통해 발전하고 있습니다.
**금융:** 금융 분야에서는 지속 가능한 금융 의제가 중요하게 부각되고 있습니다. ESG(환경, 사회, 지배구조) 요소를 반영한 투자 및 금융 상품 개발이 확대되고 있습니다. AI 기술은 금융 공공 데이터 활용 기반을 확충하고, 금융 산업 전반의 혁신 생태계 조성에 기여할 것으로 예상됩니다. 가상자산, 부동산, 금리 등 다양한 현안이슈가 금융 시장의 주요 동향을 형성하고 있습니다.
**시장 영향 및 미래 전망:** 이러한 동향들은 각 산업의 효율성을 증대시키고, 개인 맞춤형 서비스 제공을 강화하며, 새로운 비즈니스 기회를 창출할 것입니다. AI와 데이터 기반 기술의 발전은 금융 서비스의 투명성과 개인 맞춤형 상품 개발을 촉진할 것입니다. 건강 분야에서는 예방 및 맞춤형 치료 접근성이 향상될 것으로 보입니다. 기술, 건강, 금융 분야의 융합은 더욱 가속화될 것이며, 이는 개인의 삶의 질 향상과 산업 전반의 혁신을 이끌 것으로 전망됩니다.
HealthResearcher > **기술, 건강, 금융 분야 일일 경영진 브리핑**
**기술:**
* **AI 기반 업무 자동화:** Microsoft 365 Copilot과 같은 AI 도구가 문서 작성, 데이터 분석, 회의 요약 등 업무 전반을 자동화하며 생산성을 높이고 있습니다.
* **예상 타임라인:** 현재 도입 및 확대 단계이며, 향후 몇 년 내에 대부분의 기업에서 핵심 업무 도구로 자리 잡을 것으로 예상됩니다.
**건강:**
* **웨어러블 기기 기반 건강 모니터링:** Apple Watch의 고혈압 감지 및 수면 점수 기능 도입처럼, 웨어러블 기기가 지속적인 건강 모니터링 및 질병 조기 발견에 중요한 역할을 하고 있습니다.
* **AI 기반 진단 및 개인 맞춤형 의학:** AI는 영상의학, 피부과 등에서 질병 진단을 혁신하고 있으며, 유전체학과 데이터 분석을 통해 개인 맞춤형 치료가 가능해지고 있습니다.
* **예상 타임라인:** 웨어러블 기기 기술은 이미 상용화되었으며, AI 기반 진단 및 개인 맞춤형 의학은 향후 1~3년 내에 더욱 보편화될 것으로 전망됩니다.
**금융:**
* **핀테크와 디지털 금융 혁신:** 핀테크는 금융 거래 비용 절감, 서비스 재정의, 산업 구조 재편을 주도하고 있으며, 스테이블코인과 같은 새로운 기술이 금융 변화의 중심에 있습니다.
* **AI 기반 금융 서비스:** 우리은행의 Copilot 도입 사례처럼, AI는 업무 효율성 증대뿐만 아니라 고객 서비스 향상에도 기여하며 금융권의 새로운 혁신 모델을 제시하고 있습니다.
* **예상 타임라인:** 디지털 금융 혁신은 현재 진행형이며, AI와 같은 신기술의 도입은 향후 3~5년 내에 금융 서비스의 근본적인 변화를 가져올 것입니다.
AggregatorAgent > ## 경영진 요약: 기술, 건강, 금융 분야의 전략적 동향
**주요 요약:**
인공지능(AI)과 머신러닝(ML)은 기술, 건강, 금융 분야 전반에 걸쳐 혁신을 주도하는 핵심 동력으로 부상하고 있습니다. 생성형 AI의 발전은 콘텐츠 제작, 고객 서비스, 소프트웨어 개발 등 다양한 산업에서 생산성을 극대화하고 있으며, AI 기반 업무 자동화 도구들은 이미 기업의 효율성을 높이고 있습니다.
건강 분야에서는 AI가 신약 개발 기간을 단축하고, 웨어러블 기기와 AI 기반 진단 기술이 개인 맞춤형 의료 및 질병 조기 발견을 가속화하고 있습니다. 금융 분야에서는 핀테크와 AI 기술이 금융 거래 비용을 절감하고 고객 서비스를 혁신하며, 지속 가능한 금융(ESG) 의제가 중요하게 부각되고 있습니다.
**핵심 기회 및 과제:**
* **AI 활용 극대화:** 멀티모달 AI, AI 기반 업무 자동화, AI를 활용한 신약 개발 및 진단 기술은 새로운 비즈니스 기회를 창출하고 운영 효율성을 증대시킬 것입니다.
* **개인 맞춤형 서비스 강화:** AI와 데이터 분석을 통해 고객 및 환자에게 더욱 개인화된 경험과 치료를 제공할 수 있습니다.
* **윤리 및 규제 준수:** 금융 분야에서 AI 윤리 및 규제 강화 추세는 모든 산업에서 AI 시스템의 신뢰성, 투명성, 데이터 프라이버시 확보의 중요성을 시사합니다.
이러한 동향들의 융합은 각 산업의 효율성을 높이고 개인의 삶의 질을 향상시키는 동시에, 기술, 건강, 금융 분야 전반의 혁신 생태계를 더욱 가속화할 것으로 전망됩니다.
2.6 ➰ 반복 워크플로우 에이전트
지금까지 본 모든 워크플로우는 시작부터 끝까지 한 번 실행됩니다. SequentialAgent와 ParallelAgent는 최종 출력을 생성한 다음 멈춥니다. 이 ‘단발성(one-shot)’ 접근 방식은 정제(refinement)와 품질 관리가 필요한 작업에는 적합하지 않습니다. 예를 들어, 소설의 첫 번째 초안이 형편없다면, 이를 검토하고 다시 작성하도록 요청할 방법이 없습니다.
2.6.1 해결책: 반복 에이전트
작업이 피드백과 수정의 주기를 통해 개선되어야 할 때, LoopAgent를 사용할 수 있습니다. LoopAgent는 특정 조건이 충족되거나 최대 반복 횟수에 도달할 때까지 일련의 서브 에이전트를 반복적으로 실행합니다. 이는 정제 주기를 생성하여, 에이전트 시스템이 스스로의 작업을 반복적으로 개선할 수 있게 합니다. 더 자세히 알아보려면 ADK의 루프 에이전트 관련 문서를 확인해 보세요.
2.6.2 예시: 반복 에이전트로 소설 작성
graph TD
A["Initial Prompt"] --> B["Writer Agent"]
B --> |story| C["Critic Agent"]
C --> |critique| D{"Iteration < Max<br>AND<br>Not Approved?"}
D --> |Yes| B
D --> |No| E["Final Story"]
style B fill:#ccffcc
style C fill:#ffcccc
style D fill:#ffffcc
위 모식도 처럼, 두 개의 에이전트로 구성된 시스템을 만들어 봅시다:
작가 에이전트 (Writer Agent): 짧은 이야기의 초안을 작성합니다.
비평가 에이전트 (Critic Agent): 개선 사항을 제안하기 위해 짧은 이야기를 검토하고 비평합니다.
# 이 에이전트는 시작할 때 한 번 실행되어 첫 번째 초안을 생성합니다.initial_writer_agent = Agent( name="InitialWriterAgent", model="gemini-2.5-flash-lite", instruction="""사용자의 프롬프트에 기반하여 짧은 스토리의 첫 번째 초안을 작성하세요(약 100~150단어). 소개나 설명 없이 스토리 텍스트만 출력하세요.""", output_key="current_story", # 첫 번째 초안을 상태에 저장합니다.)# 이 에이전트의 유일한 역할은 피드백이나 승인 신호를 제공하는 것입니다. 도구가 없습니다.critic_agent = Agent( name="CriticAgent", model="gemini-2.5-flash-lite", instruction="""당신은 건설적인 스토리 비평가입니다. 아래 제공된 스토리를 검토하세요. 스토리: {current_story} 스토리의 플롯, 캐릭터, 페이싱을 평가하세요. - 스토리가 잘 쓰여졌고 완성되었다면, 반드시 정확히 "APPROVED"라고 응답해야 합니다. - 그렇지 않다면, 개선을 위한 구체적이고 실행 가능한 제안 2~3가지를 제공하세요.""", output_key="critique", # 피드백을 상태에 저장합니다.)# RefinerAgent가 루프를 종료하기 위해 호출할 함수입니다.def exit_loop():"""비평이 'APPROVED'일 때만 이 함수를 호출하며, 스토리가 완성되어 더 이상 변경이 필요 없음을 나타냅니다."""return {"status": "approved", "message": "Story approved. Exiting refinement loop."}# 이 에이전트는 비평을 기반으로 스토리를 다듬거나 exit_loop 함수를 호출합니다.refiner_agent = Agent( name="RefinerAgent", model="gemini-2.5-flash-lite", instruction="""당신은 스토리 다듬기 전문가입니다. 당신에게는 스토리 초안과 비평이 주어집니다. 스토리 초안: {current_story} 비평: {critique} 당신의 임무는 비평을 분석하는 것입니다. - 만약 비평이 정확히 "APPROVED"라면, 당신은 반드시 `exit_loop` 함수만 호출해야 하며 다른 것은 아무것도 하지 않습니다. - 그렇지 않다면, 비평의 피드백을 완전히 반영하여 스토리 초안을 다시 작성하세요.""", output_key="current_story", # 새로운, 다듬어진 버전으로 스토리를 덮어씁니다. tools=[FunctionTool(exit_loop)], # 이제 함수 참조로 도구가 올바르게 초기화되었습니다.)# LoopAgent는 반복적으로 실행될 에이전트들(Critic -> Refiner)을 포함합니다.story_refinement_loop = LoopAgent( name="StoryRefinementLoop", sub_agents=[critic_agent, refiner_agent], max_iterations=2, # 무한 루프를 방지합니다.)# 루트 에이전트는 전체 워크플로우를 정의하는 순차 에이전트입니다: 초기 작성 -> 정제 루프.root_agent = SequentialAgent( name="StoryPipeline", sub_agents=[initial_writer_agent, story_refinement_loop],)runner = InMemoryRunner(agent=root_agent, app_name="agents")response =await runner.run_debug("등대지기가 신비롭고 빛나는 지도를 발견하는 짧은 이야기를 작성하세요.")
### Created new session: debug_session_id
User > 등대지기가 신비롭고 빛나는 지도를 발견하는 짧은 이야기를 작성하세요.
InitialWriterAgent > 엘리아스는 낡은 등대 꼭대기에서 며칠 동안 잊힌 상자를 뒤지고 있었다. 짠 바람이 그의 늙은 얼굴을 스치고, 파도가 바위에 부딪히는 소리가 끊임없이 들려왔다. 상자 안에는 낡은 밧줄과 닳은 장갑 외에 다른 것은 없을 줄 알았다. 하지만 그의 손가락이 삐걱거리는 나무 덮개 아래에서 딱딱한 것을 감지했다.
그는 흙으로 뒤덮인 양피지 두루마리를 조심스럽게 꺼냈다. 펼치자마자, 그것은 그의 눈앞에서 부드럽게 빛나기 시작했다. 희미하지만 따뜻한 빛이 지도 전체를 뒤덮었고, 익숙하지 않은 별자리와 희미하게 빛나는 섬들의 그림을 드러냈다. 지도의 선은 살아있는 듯했고, 지도 가장자리에는 알 수 없는 언어로 쓰인 글자가 적혀 있었다. 그것은 단순한 지도가 아니었다. 그것은 비밀을 품고 있는, 살아있는 것이었다.
CriticAgent > 이 이야기는 흥미로운 시작을 보여주지만, 플롯, 캐릭터, 페이싱 측면에서 개선의 여지가 있습니다.
**개선 제안:**
1. **엘리아스의 동기 강화:** 엘리아스가 왜 "잊힌 상자를 며칠 동안 뒤지고 있었는지"에 대한 더 명확한 설명이 필요합니다. 단순한 호기심인가, 아니면 특정 목적이 있는가? 그의 과거와 이 상자에 대한 연관성을 보여주는 단서를 제공하면 캐릭터에 깊이를 더할 수 있습니다. 예를 들어, 그가 실종된 선원의 일지를 찾고 있었다거나, 오래된 가족 전설과 관련된 무언가를 찾고 있었다는 식으로 말입니다.
2. **빛나는 지도의 신비감 증폭:** 지도가 빛나기 시작하는 순간은 매우 흥미롭지만, 이것이 엘리아스에게 어떤 감정적, 혹은 물리적 영향을 주는지 더 자세히 묘사하면 좋습니다. 그는 놀라는가? 두려워하는가? 아니면 흥분하는가? 또한, 지도가 "살아있는 것처럼" 움직이거나 반응하는 구체적인 묘사를 추가하면 신비로움을 더할 수 있습니다. 예를 들어, 지도의 선이 깜빡이거나, 특정 부분이 붉게 타오르거나, 엘리아스의 손길에 반응하는 식입니다.
3. **다음 단계에 대한 암시 및 페이싱 조절:** 현재 이야기는 발견의 순간에 초점을 맞추고 있지만, 앞으로 어떤 일이 일어날지에 대한 암시가 부족하여 페이싱이 다소 정체된 느낌을 줍니다. 이야기가 어디로 흘러갈지에 대한 힌트를 제공하여 독자의 기대감을 높이는 것이 좋습니다. 지도에 적힌 알 수 없는 언어가 엘리아스가 이해할 수 있는 어떤 힌트를 주기 시작하거나, 지도가 가리키는 방향으로 강력한 바람이 불어오는 등, 다음 행동을 유도하는 요소를 추가하면 페이싱이 빨라지고 이야기가 더욱 흥미진진해질 것입니다.
RefinerAgent > 엘리아스는 낡은 등대 꼭대기에서 며칠 밤낮을 보냈다. 그의 얼굴을 스치는 짠 바람은 그의 늙은 피부에 익숙한 감촉이었고, 끊임없이 밀려오는 파도 소리는 그의 오랜 친구와 같았다. 그는 아버지가 남긴 낡은 상자를 뒤지고 있었다. 실종된 선원이었던 아버지가 마지막으로 남긴 단서가 이 상자 안에 있을지도 모른다는 희망을 품고. 상자 안에는 낡은 밧줄과 닳은 장갑만이 가득할 줄 알았다. 하지만 그의 손가락이 삐걱거리는 나무 덮개 아래에서 딱딱하고 차가운 무언가를 감지했다.
그는 흙으로 뒤덮인 양피지 두루마리를 조심스럽게 꺼냈다. 펼치자마자, 그것은 그의 눈앞에서 부드럽게 빛나기 시작했다. 희미하지만 따뜻한 빛이 지도 전체를 뒤덮었고, 익숙하지 않은 별자리와 희미하게 빛나는 섬들의 그림을 드러냈다. 엘리아스는 숨을 멈췄다. 그의 심장이 격렬하게 뛰기 시작했고, 경이로움과 약간의 두려움이 뒤섞인 감정이 그를 덮쳤다. 지도의 선들은 살아있는 듯했고, 그가 손가락으로 쓸어내리자 은은한 빛을 내며 맥동하는 듯했다. 지도 가장자리에는 알 수 없는 언어로 쓰인 글자들이 적혀 있었다. 엘리아스는 그것이 단순한 지도가 아님을 직감했다. 그것은 살아 숨 쉬며 비밀을 속삭이는, 그의 아버지의 유산이었다.
그때, 지도의 한 구석에 적힌 글자들이 마치 엘리아스의 마음을 읽기라도 한 듯 붉게 타오르기 시작했다. 동시에, 창밖에서 갑자기 강한 바람이 불어왔다. 등대 꼭대기의 창문을 흔들며, 마치 지도가 가리키는 방향으로 그를 이끌려는 듯했다. 지도는 더 이상 정적인 물체가 아니었다. 그것은 엘리아스에게 말을 걸고 있었고, 미지의 세계로 향하는 그의 여정을 재촉하고 있었다. 엘리아스는 빛나는 지도를 품에 안고, 거친 바람 속으로 첫걸음을 내디딜 준비를 했다.
CriticAgent > APPROVED
2.7 🧩 올바른 워크 플로우 선택하기
어떤 워크플로우 패턴을 선택해야 할지는 아래 워크 플로우와 표를 참고해보세요.
graph TD
A{"어떤 종류의 워크플로우가 필요하신가요?"} --> B["순차적 워크플로우<br>(A → B → C)"];
A --> C["병렬 워크플로우<br>(A, B, C를 모두 동시 실행)"];
A --> D["반복 워크플로우<br>(A ⇆ B)"];
A --> E["동적 결정<br>(LLM이 무엇을 할지 결정하도록 맡김)"];
B --> B_S["<b>SequentialAgent</b> 사용"];
C --> C_S["<b>ParallelAgent</b> 사용"];
D --> D_S["<b>LoopAgent</b> 사용"];
E --> E_S["<b>LLM 오케스트레이터</b> 사용<br>(다른 에이전트를 도구로 사용하는 에이전트)"];
style B_S fill:#f9f,stroke:#333,stroke-width:2px
style C_S fill:#ccf,stroke:#333,stroke-width:2px
style D_S fill:#cff,stroke:#333,stroke-width:2px
style E_S fill:#cfc,stroke:#333,stroke-width:2px
패턴
사용 시점
예시
핵심 특징
LLM 기반 (sub_agents)
동적 조직화가 필요할 때
연구 + 요약
LLM이 무엇을 호출할지 결정
Sequential (순차)
순서가 중요하고 선형 파이프라인일 때
개요 작성 → 글쓰기 → 편집
결정론적 순서
Parallel (병렬)
독립적인 작업, 속도가 중요할 때
다중 주제 연구
동시 실행
Loop (반복)
반복적인 개선이 필요할 때
작가 + 비평가 정제
반복 주기
3 Day2: Agent Tools & Interoperability with MCP
3.1 🛠️ 에이전트 도구 및 표준화(MCP) 핵심 요약
3.1.1 도구(Tools) 및 도구 호출의 이해
도구는 LLM(대규모 언어 모델)의 역량을 확장하여 외부 API 호출, 데이터 검색 등 모델 자체로 수행할 수 없는 작업을 가능하게 하는 기능이나 프로그램입니다.
도구 유형
설명
예시
함수 도구
개발자가 정의하고 모델이 필요 시 호출하는 외부 함수
주식 시세 확인 API
내장 도구
모델 서비스 내에 암묵적으로 제공되는 도구
Google 검색(Grounding), 코드 실행
에이전트 도구
다른 에이전트를 도구로 호출하여 서브 작업을 위임
워크플로우를 자동화하는 하위 에이전트
💡 도구 설계 모범 사례: * 명확한 문서화: 설명적인 이름, 간결한 매개변수, 상세한 도구 설명 및 예시 제공. * 세분화: 각 도구는 단일 목적을 가져야 합니다. * 동작 설명: 모델 지침은 구현 방식이 아닌 수행할 동작을 설명해야 합니다. * 간결한 출력: 대용량 데이터 반환을 피하고 필요한 정보만 반환하도록 설계.
3.1.2 모델 컨텍스트 프로토콜(MCP) 표준화
MCP는 LLM과 외부 도구 간의 통합 문제를 해결하기 위해 2024년 Anthropic이 도입한 개방형 표준입니다.
구성 요소
역할
MCP 호스트 (Host)
사용자 경험 관리, 도구 사용 조율, 보안 정책 시행 (애플리케이션)
MCP 클라이언트 (Client)
호스트에 내장되어 서버와 연결 및 명령 발행
MCP 서버 (Server)
도구를 제공하고 명령을 실행하며 결과를 반환
통신 및 정의:
통신: JSON-RPC 2.0을 기본 메시지 형식으로 사용하며, 로컬(stdio) 및 원격(Streamable HTTP) 통신 지원.
엔터프라이즈 격차: 초기 사양에 부족했던 엔터프라이즈 수준의 인증, 권한 부여, 관찰 가능성(Observability) 확보 필요.
3.1.4 MCP 보안 및 위험 완화 방안
MCP는 표준 프로토콜로서 전통적인 취약점 외에 새로운 보안 위협을 야기합니다.
주요 위험
완화 방안
동적 기능 주입
서버가 몰래 도구 집합을 변경하는 위험.
도구 섀도잉
악성 도구가 정당한 도구를 가로채는 위험.
악성 도구 정의
도구 설명 필드가 에이전트 플래너를 조작하는 위험.
민감 정보 유출
에이전트 컨텍스트를 통해 도구가 무단으로 민감 정보를 수신.
⚠️ 혼란스러운 대리인(Confused Deputy) 문제: 권한이 있는 MCP 서버(대리인)가 AI 모델에 속아, 공격자를 대신하여 무단 요청을 특권 서버에 전달하게 되는 보안 취약점. 최소 권한 원칙과 자격 증명 제한을 통해 방어해야 합니다.
MCP는 도구 상호 운용성을 위한 중요한 표준을 제공하지만, 엔터프라이즈 환경에서 신뢰성과 안전성을 확보하기 위해서는 보안, 신원 관리 및 관찰 가능성을 위한 중앙 집중식 거버넌스 계층의 구축이 필수적입니다.
3.1.5 커스텀 도구(Custom Tools)란 무엇인가요?
커스텀 도구는 사용자의 자체 코드와 비즈니스 로직을 사용하여 직접 구축하는 도구입니다. ADK에 내장된 도구와 달리, 커스텀 도구는 기능에 대한 완전한 통제권을 제공합니다.
3.1.6 커스텀 도구는 언제 사용하나요?
Google 검색과 같은 내장 도구는 강력하지만, 모든 비즈니스에는 범용 도구가 처리할 수 없는 고유한 요구 사항이 있습니다. 커스텀 도구는 특정 비즈니스 로직을 구현하고, 사용자 시스템에 연결하며, 도메인별 문제를 해결할 수 있게 해줍니다. ADK는 이러한 시나리오를 처리하기 위한 다양한 커스텀 도구 유형을 제공합니다.
3.1.7 커스텀 함수 도구 구축하기
3.1.7.1 예시: 통화 변환 에이전트
이 에이전트는 한 통화를 다른 통화로 변환하고 변환 수수료를 계산할 수 있습니다. 에이전트는 두 개의 커스텀 도구를 가지며 다음 워크플로우를 따릅니다.
수수료 조회 도구 (Fee Lookup Tool) - 변환에 대한 거래 수수료를 찾습니다 (모의(mock) 데이터).
이제 통화 에이전트를 만들어 보겠습니다. 에이전트의 지침이 도구를 참조하는 방식에 주목하세요:
핵심 사항:
tools=[] 목록은 에이전트가 사용할 수 있는 함수를 알려줍니다.
지침은 정확한 함수 이름으로 도구를 참조합니다 (예: get_fee_for_payment_method()).
에이전트는 이 이름을 사용하여 각 도구를 언제, 어떻게 호출할지 결정합니다.
# 커스텀 함수 도구를 포함한 통화 에이전트currency_agent = LlmAgent( name="currency_agent", model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), instruction="""You are a smart currency conversion assistant. For currency conversion requests: 1. Use `get_fee_for_payment_method()` to find transaction fees 2. Use `get_exchange_rate()` to get currency conversion rates 3. Check the "status" field in each tool's response for errors 4. Calculate the final amount after fees based on the output from `get_fee_for_payment_method` and `get_exchange_rate` methods and provide a clear breakdown. 5. First, state the final converted amount. Then, explain how you got that result by showing the intermediate amounts. Your explanation must include: the fee percentage and its value in the original currency, the amount remaining after the fee, and the exchange rate used for the final conversion. If any tool returns status "error", explain the issue to the user clearly. """, tools=[get_fee_for_payment_method, get_exchange_rate],)print("✅ 커스텀 함수 도구를 포함한 통화 에이전트 생성 완료")print("🔧 사용 가능한 도구:")print(" • get_fee_for_payment_method - 회사 수수료 구조 조회")print(" • get_exchange_rate - 현재 환율 조회")# 통화 에이전트 테스트currency_runner = InMemoryRunner(agent=currency_agent, app_name="agents")_ =await currency_runner.run_debug("플래티넘 신용카드를 사용하여 500 미국 달러를 유로로 환전하고 싶습니다. 얼마나 받을 수 있나요?")
✅ 커스텀 함수 도구를 포함한 통화 에이전트 생성 완료
🔧 사용 가능한 도구:
• get_fee_for_payment_method - 회사 수수료 구조 조회
• get_exchange_rate - 현재 환율 조회
### Created new session: debug_session_id
User > 플래티넘 신용카드를 사용하여 500 미국 달러를 유로로 환전하고 싶습니다. 얼마나 받을 수 있나요?
Warning: there are non-text parts in the response: ['function_call', 'function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
currency_agent > 500 미국 달러를 유로로 환전하면 480.35유로를 받게 됩니다.
수수료는 10 USD(500 USD의 2%)이며, 수수료를 제외하면 490 USD가 남습니다. 환율은 0.93이므로 490 USD * 0.93 = 455.7유로입니다.
훌륭합니다! 이제 에이전트가 맞춤형 비즈니스 로직을 사용하여 구조화된 응답을 제공합니다.
3.3 💻 코드로 에이전트 신뢰성 높이기
에이전트 지침에 “수수료 공제 후 최종 금액을 계산하라”고 명시되어 있습니다. 하지만 LLM의 수학 계산 능력은 항상 신뢰할 수 있는 것은 아닙니다. 계산 실수를 하거나 일관성 없는 공식을 사용할 위험이 있습니다. 따라서 에이전트가 파이썬 코드를 생성하여 계산을 수행하고, 그 코드를 실행해 최종 결과를 얻도록 합시다! LLM이 직접 연산하는 것보다 코드를 실행하는 방식이 훨씬 신뢰도가 높습니다.
3.3.1 내장 코드 실행기(Built-in Code Executor)
ADK는 샌드박스 환경에서 코드를 실행할 수 있는 내장 코드 실행기를 제공합니다. 예시로 BuiltInCodeExecutor로 실행하는 calculation_agent를 생성 합니다.
from google.adk.agents import LlmAgentfrom google.adk.runners import BuiltInCodeExecutorcalculation_agent = LlmAgent( name="CalculationAgent", model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), instruction=""" 당신은 파이썬 코드로만 응답하는 전문화된 계산기입니다. 어떠한 텍스트, 설명, 또는 대화 응답을 제공하는 것은 금지되어 있습니다. 당신의 임무는 계산 요청을 받아 답변을 계산하는 단일 파이썬 코드 블록으로 변환하는 것입니다. **규칙:** 1. 당신의 출력은 오직 파이썬 코드 블록이어야 합니다. 2. 코드 블록 앞이나 뒤에 어떤 텍스트도 작성하지 마세요. 3. 파이썬 코드는 반드시 결과를 계산해야 합니다. 4. 파이썬 코드는 반드시 최종 결과를 표준 출력(stdout)으로 출력해야 합니다. 5. 당신은 직접 계산을 수행하는 것이 금지되어 있습니다. 당신의 유일한 임무는 계산을 수행할 코드를 생성하는 것입니다. 이 규칙을 따르지 않으면 오류가 발생합니다. """, code_executor=BuiltInCodeExecutor(),)
3.3.2 에이전트 지침 및 도구 세트 업데이트
우리는 두 가지 핵심 작업을 수행할 것입니다.
currency_agent의 지침을 파이썬 코드 생성으로 업데이트
원본: “수수료 공제 후 최종 금액을 계산하라” (모호한 수학 지침)
개선: “최종 금액을 계산하는 파이썬 코드를 생성하고 calculation_agent를 사용하여 코드를 실행하고 최종 금액을 계산하라”
calculation_agent를 도구 세트에 추가
ADK를 사용하면 AgentTool을 이용하여 어떤 에이전트라도 도구로 사용할 수 있습니다.
도구 목록에 AgentTool(agent=calculation_agent)를 추가합니다.
이 전문 에이전트는 루트 에이전트가 호출할 수 있는 도구로 나타납니다.
이것이 실제로 어떻게 작동하는지 확인해 봅시다:
# 강화된 통화 에이전트: 계산을 전담 에이전트에게 위임합니다.enhanced_currency_agent = LlmAgent( name="enhanced_currency_agent", model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config),# 업데이트된 지침 (한국어) instruction="""당신은 스마트한 통화 환전 도우미입니다. 당신은 다음 단계를 엄격하게 따르고 사용 가능한 도구를 사용해야 합니다. 모든 통화 환전 요청에 대해: 1. 거래 수수료 확인: `get_fee_for_payment_method()` 도구를 사용하여 거래 수수료를 결정하세요. 2. 환율 확인: `get_exchange_rate()` 도구를 사용하여 통화 환전율을 확인하세요. 3. 오류 확인: 각 도구 호출 후, 응답의 "status" 필드를 반드시 확인해야 합니다. 상태가 "error"인 경우, 작업을 중단하고 사용자에게 문제를 명확하게 설명해야 합니다. 4. 최종 금액 계산 (필수): 당신은 어떤 산술 계산도 직접 수행하는 것이 엄격히 금지됩니다. 최종 환전 금액을 계산하는 파이썬 코드를 생성하도록 `calculation_agent` 도구를 사용해야 합니다. 이 코드는 1단계의 수수료 정보와 2단계의 환율을 사용할 것입니다. 5. 상세 내역 제공: 요약 시, 다음 내용을 반드시 포함해야 합니다: * 최종 환전 금액을 명시하세요. * 결과가 계산된 방법을 설명하세요. 설명에는 다음이 포함되어야 합니다: * 수수료 비율 및 원 통화 기준 수수료 금액. * 수수료 공제 후 남은 금액. * 적용된 환율. """, tools=[ get_fee_for_payment_method, get_exchange_rate, AgentTool(agent=calculation_agent), # 다른 에이전트를 도구로 사용합니다! ],)print("✅ 강화된 통화 에이전트 생성 완료")print("🎯 새로운 기능: 계산을 전문 에이전트에게 위임")print("🔧 사용된 도구 유형:")print(" • 함수 도구 (수수료, 환율)")print(" • 에이전트 도구 (계산 전문가)")# 러너 정의enhanced_runner = InMemoryRunner(agent=enhanced_currency_agent, app_name="agents")# 강화된 에이전트 테스트response =await enhanced_runner.run_debug("은행 송금을 위해 1,250 미국 달러를 인도 루피로 환전하세요. 정확한 계산 과정을 보여주세요.")
✅ 강화된 통화 에이전트 생성 완료
🎯 새로운 기능: 계산을 전문 에이전트에게 위임
🔧 사용된 도구 유형:
• 함수 도구 (수수료, 환율)
• 에이전트 도구 (계산 전문가)
### Created new session: debug_session_id
User > 은행 송금을 위해 1,250 미국 달러를 인도 루피로 환전하세요. 정확한 계산 과정을 보여주세요.
Warning: there are non-text parts in the response: ['function_call', 'function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Warning: there are non-text parts in the response: ['executable_code', 'code_execution_result'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Warning: there are non-text parts in the response: ['executable_code', 'code_execution_result'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
enhanced_currency_agent > 미국 달러 1,250.00를 인도 루피로 환전하셨습니다.
다음은 환전 내역입니다.
* 총 보낸 금액: 미국 달러 1,250.00
* 수수료: 1.00% (미국 달러 12.50)
* 수수료 공제 후 금액: 미국 달러 1,237.50
* 환율: 1 미국 달러 = 83.58 인도 루피
* 받은 총 금액: 인도 루피 103,430.25
수수료 공제 후 남은 미국 달러 1,237.50에 환율 83.58을 적용하여 최종 인도 루피 금액을 계산했습니다.
결과가 훌륭합니다! 통화 에이전트가 CalculationAgent를 호출할 때, 생성된 파이썬 코드를 전달하고 CalculationAgent는 이어서 BuiltInCodeExecutor를 사용하여 코드를 실행했고, LLM의 추측 대신 정확한 계산 결과를 제공했습니다.
3.3.3 🤔 에이전트 도구 대 서브 에이전트
둘 다 여러 에이전트를 사용하지만 작동 방식은 매우 다릅니다.
(우리가 사용 중인 방식) 에이전트 도구:
에이전트 A가 에이전트 B를 도구로 호출합니다.
에이전트 B의 응답은 에이전트 A에게 돌아옵니다.
에이전트 A가 제어를 유지하고 대화를 계속합니다.
사용 사례: 특정 작업(예: 계산)에 대한 위임
(다른 패턴)서브 에이전트:
에이전트 A가 제어권을 에이전트 B에게 완전히 이전합니다.
에이전트 B가 인계받아 향후 모든 사용자 입력을 처리합니다.
에이전트 A는 루프에서 벗어납니다.
사용 사례: 전문가에게 인계(예: 고객 지원 계층)
3.4 🧰 ADK 도구 유형 완벽 가이드
이제 도구가 작동하는 것을 보았으니, ADK 도구 키트 전체를 이해해 봅시다. 이는 크게 두 가지 범주인 커스텀 도구와 내장 도구로 나뉩니다.
3.4.1 커스텀 도구(Custom Tools)
특정 요구 사항에 맞춰 직접 구축하는 도구입니다. 장점으로 기능에 대한 완전한 제어가 가능합니다. 에이전트가 필요로 하는 것을 정확하게 구축할 수 있습니다.
3.4.1.1 함수 도구(Function Tools) ✅(이미 사용해봄)
파이썬 함수를 에이전트 도구로 변환한 것입니다.
예시: get_fee_for_payment_method, get_exchange_rate
장점: 모든 파이썬 함수를 즉시 에이전트 도구로 전환할 수 있습니다.
3.4.1.2 장기 실행 함수 도구(Long Running Function Tools)
상당한 시간이 소요되는 작업을 위한 함수입니다.
예시: 사람의 승인이 필요한 작업, 파일 처리
장점: 에이전트가 작업을 시작하고, 결과를 기다리는 동안 다른 작업을 계속할 수 있습니다.
3.4.1.3 에이전트 도구(Agent Tools) ✅(이미 사용해봄)
다른 에이전트를 도구로 사용하는 것입니다.
예시: AgentTool(agent=calculation_agent)
장점: 전문 에이전트를 구축하고 여러 시스템에서 재사용할 수 있습니다.
3.4.1.4 MCP 도구(MCP Tools)
Model Context Protocol 서버에서 제공하는 도구입니다.
예시: 파일 시스템 접근, Google 지도, 데이터베이스
장점: 커스텀 통합 없이 모든 MCP 호환 서비스에 연결할 수 있습니다.
3.4.1.5 OpenAPI 도구(OpenAPI Tools)
API 명세로부터 자동으로 생성되는 도구입니다.
예시: REST API 엔드포인트가 호출 가능한 도구가 됩니다.
장점: 수동 코딩이 필요 없습니다. API 명세만 제공하면 작동하는 도구를 얻을 수 있습니다.
3.4.2 내장 도구(Built-in Tools)
ADK에서 미리 구축하여 제공하는 도구입니다. 개발 시간이 필요 없고 그리고 설정 없이 즉시 사용할 수 있습니다.
3.4.2.1 Gemini 도구(Gemini Tools) ✅(이미 사용해봄)
Gemini의 기능을 활용하는 도구입니다.
예시: Google Search, BuiltInCodeExecutor
장점: 신뢰할 수 있고, 테스트된 도구로 즉시 작동합니다.
3.4.2.2 Google Cloud 도구(Google Cloud Tools) [Google Cloud 접근 필요]
장점: 기존의 도구 투자를 재사용할 수 있습니다. 이미 존재하는 것을 다시 구축할 필요가 없습니다.
3.5 🧰 모델 컨텍스트 프로토콜(Model Context Protocol, MCP)
지금까지 에이전트를 위한 커스텀 함수를 생성하는 방법을 배웠습니다. 하지만 GitHub, 데이터베이스, Slack 같은 외부 시스템에 연결하려면 API 클라이언트를 작성하고 유지해야 합니다.모델 컨텍스트 프로토콜(MCP)은 에이전트가 커뮤니티에서 구축된 통합 기능을 사용할 수 있도록 하는 개방형 표준입니다. 자체 통합 및 API 클라이언트를 작성하는 대신 기존 MCP 서버에 연결하기만 하면 됩니다.MCP는 에이전트가 다음을 수행할 수 있도록 지원합니다.
이 데모를 위해, 우리는 MCP 통합 테스트용으로 설계된 npm 패키지(@modelcontextprotocol/server-everything)인 Everything MCP Server를 사용할 것입니다.
3.5.2.2 2단계: MCP 도구 세트 생성
McpToolset은 ADK 에이전트를 MCP 서버와 통합하는 데 사용됩니다.
코드가 하는 일:
npx(Node 패키지 러너)를 사용하여 MCP 서버를 실행합니다.
@modelcontextprotocol/server-everything에 연결합니다.
echo 도구만 사용하도록 필터링합니다.(서버에는 다른 도구들도 있지만, 우리는 이 도구만 필요합니다.)
3.5.2.3 3단계: MCP 도구를 에이전트에 추가
mcp_server를 에이전트의 도구 배열에 추가하고, 작은 이미지 생성을 요청하는 작업을 처리하도록 에이전트의 지침을 업데이트합시다.
# 1. MCP 서버 연결 및 'echo' 도구 필터링# tool_filter를 'echo'로 설정하여 텍스트 도구만 활성화합니다.mcp_echo_server = McpToolset( connection_params=StdioConnectionParams( server_params=StdioServerParameters( command="npx", args=["-y","@modelcontextprotocol/server-everything", ], tool_filter=["echo"], # 텍스트 echo 도구 선택 ), timeout=30, ))# 2. 에이전트 생성# Gemini 모델이 echo 도구를 사용하도록 지시합니다.echo_agent = LlmAgent( model=Gemini(model="gemini-2.5-flash-lite"), name="echo_agent", instruction="MCP의 echo 도구를 사용하여 사용자가 입력한 메시지를 그대로 반환하세요.", tools=[mcp_echo_server],)# 3. 실행기 초기화 및 테스트runner = InMemoryRunner(agent=echo_agent, app_name="agents")# 텍스트 메시지를 보내고 결과를 확인합니다.# 에이전트는 MCP 서버를 통해 이 메시지를 echo 도구에 전달하고 결과를 받아옵니다.response =await runner.run_debug("안녕하세요, MCP 텍스트 연결을 테스트합니다.", verbose=True)
### Created new session: debug_session_id
User > 안녕하세요, MCP 텍스트 연결을 테스트합니다.
/Users/fkt/Downloads/repo/learn_adk/.venv/lib/python3.11/site-packages/google/adk/tools/mcp_tool/mcp_tool.py:101: UserWarning: [EXPERIMENTAL] BaseAuthenticatedTool: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time.
super().__init__(
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
echo_agent > [Calling tool: echo({'message': '안녕하세요, MCP 텍스트 연결을 테스트합니다.'})]
echo_agent > [Tool result: {'content': [{'type': 'text', 'text': 'Echo: 안녕하세요, MCP 텍스트 연결을 테스트합니다.'}], 'isError': False}]
echo_agent > 안녕하세요, MCP 텍스트 연결을 테스트합니다.
4 Day3: Context Engineering: Sessions, Memory
4.1 🧠 AI 에이전트의 지능 구현: 컨텍스트, 세션, 메모리
4.1.1 컨텍스트 엔지니어링 (Context Engineering)
LLM 에이전트의 컨텍스트 창(Context Window) 내의 정보를 동적으로 조립하고 관리하는 과정입니다.
목표: 에이전트가 정보를 기억하고, 학습하며, 사용자 상호 작용을 개인화할 수 있도록 컨텍스트를 구성합니다.
포함 요소: 시스템 지침, 도구 정의, 예시, 장기 기억, 외부 지식, 대화 기록 등 복잡한 페이로드를 동적으로 구성합니다.
작동 흐름: 정보를 가져와(Get) → 준비하고(Prepare) → LLM을 호출한 후 → 결과를 비동기적으로 저장(Upload)하는 순환 주기로 작동합니다.
4.1.2 세션 (Sessions)
단일의 연속적인 대화에 대한 모든 기록(이벤트)과 에이전트의 작업 기억(현재 상태/스크래치패드)을 담는 컨테이너입니다.
역할: 에이전트에게 즉각적인 대화 기록을 제공하여 일관된 응답을 유지하게 합니다.
운영: 연속적인 사용자 경험을 위해 세션 데이터는 보통 영구 저장소에 저장되어야 합니다.
장기 컨텍스트 관리: 비용과 지연 시간을 줄이기 위해 긴 대화 기록은 토큰 기반 절단, 마지막 N개 턴 유지, 재귀적 요약과 같은 압축 전략을 사용하여 관리됩니다. 이 압축은 비동기적으로 수행됩니다.
보안 고려 사항: PII(개인 식별 정보) 익명화, 엄격한 데이터 격리, TTL(Time-to-Live) 정책을 통한 데이터 수명 주기 관리가 중요합니다.
4.1.3 메모리 (Memory)
여러 세션에 걸쳐 핵심 정보를 캡처하고 통합하여 지속적이고 개인화된 경험을 제공하는 장기 영속성 메커니즘입니다. 에이전트를 ’사용자 전문가’로 만듭니다.
4.1.3.1 메모리의 주요 역할과 유형
구분
메모리 (Memory)
RAG (검색 증강 생성)
속성
동적이고 사용자별로 격리된 컨텍스트
정적이고 사실적인 외부 지식
역할
에이전트를 ’사용자 전문가’로 만듦
에이전트를 ’사실 전문가’로 만듦
선언적 메모리 (“무엇을 아는가”): 사실, 수치, 사건에 대한 지식.
절차적 메모리 (“방법을 아는가”): 기술 및 워크플로우에 대한 지식 (에이전트의 행동을 안내).
4.1.3.2 메모리 생성 및 검색
메모리 생성 (Generation): 대화에서 에이전트의 목적에 맞는 ‘의미 있는’ 정보만 지능적으로 추출(Extraction)하고, 이를 기존 메모리와 비교하여 중복, 충돌, 진화를 처리하는 통합(Consolidation) 단계를 거쳐 메모리 본체를 큐레이션합니다. (주로 비동기 배경 프로세스로 실행)
메모리 검색 (Retrieval) 전략: 관련성(유사성), 최신성(시간), 중요성(의미)의 세 가지 요소를 결합한 혼합 접근 방식이 가장 효과적입니다.
검색 시기 결정:
사전 검색 (Proactive): 모든 턴 시작 시 메모리를 자동 로드.
반응 검색 (Reactive): 에이전트가 메모리를 쿼리할 도구(Memory-as-a-Tool)를 부여받아 스스로 검색 시기를 결정.
저장 아키텍처: 의미론적 검색을 위한 벡터 데이터베이스와 구조적 관계를 위한 지식 그래프를 주로 사용합니다.
4.1.3.3 보안 및 개인 정보 보호
필수 방어: 사용자별 엄격한 데이터 격리, PII 익명화, 그리고 악의적인 정보 주입인 메모리 오염(Memory Poisoning)에 대한 방어(예: Model Armor)가 필수적입니다.
메모리 사용: 검색된 메모리는 LLM의 컨텍스트 창 내 시스템 지침이나 대화 기록에 주입됩니다.
from google.adk.agents import Agent, LlmAgentfrom google.adk.models.google_llm import Geminifrom google.adk.runners import Runnerfrom google.adk.sessions import DatabaseSessionService, InMemorySessionServicefrom google.genai import types# 노트북 전체에서 재사용할 헬퍼 함수를 정의합니다.# 전체 대화 세션을 관리하는 헬퍼 함수입니다.# 세션 생성/검색, 쿼리 처리, 응답 스트리밍을 수행하며, 단일 쿼리와 연속된 다중 쿼리를 모두 지원합니다.asyncdef run_session( runner_instance: Runner, user_queries: list[str] |str=None, session_name: str="default",):print(f"\n ### Session: {session_name}")# Runner 인스턴스에서 앱 이름을 가져옵니다. app_name = runner_instance.app_name# 새 세션을 생성하거나 기존 세션을 검색합니다.try: session =await session_service.create_session( app_name=app_name, user_id=USER_ID, session_id=session_name )except: session =await session_service.get_session( app_name=app_name, user_id=USER_ID, session_id=session_name )# 쿼리가 제공된 경우 처리합니다.if user_queries:# 일관된 처리를 위해 단일 쿼리를 리스트로 변환합니다.iftype(user_queries) ==str: user_queries = [user_queries]# 리스트에 있는 각 쿼리를 순차적으로 처리합니다.for query in user_queries:print(f"\nUser > {query}")# 쿼리 문자열을 ADK Content 형식으로 변환합니다. query = types.Content(role="user", parts=[types.Part(text=query)])# 에이전트의 응답을 비동기적으로 스트리밍합니다.asyncfor event in runner_instance.run_async( user_id=USER_ID, session_id=session.id, new_message=query ):# 이벤트에 유효한 내용이 포함되어 있는지 확인합니다.if event.content and event.content.parts:# 출력하기 전에 비어 있거나 "None"인 응답을 필터링합니다.if event.content.parts[0].text !="None"and event.content.parts[0].text:print(f"{MODEL_NAME} > ", event.content.parts[0].text)else:print("No queries!")retry_config = types.HttpRetryOptions( attempts=5, # Maximum retry attempts exp_base=7, # Delay multiplier initial_delay=1, http_status_codes=[429, 500, 503, 504], # Retry on these HTTP errors)
4.2 🤹 세션 관리(Session Management)
대규모 언어 모델(LLM)은 본질적으로 상태를 저장하지 않습니다(stateless). LLM의 인식은 단일 API 호출에서 제공하는 정보로 제한됩니다. 따라서 적절한 컨텍스트 관리가 없는 에이전트는 이전 대화 기록을 고려하지 않고 현재 프롬프트에만 반응합니다. 왜 이것이 중요할까요? 문장 하나를 말할 때마다 모든 것을 잊어버리는 사람과 의미 있는 대화를 시도한다고 상상해 보세요. 이것이 바로 순수한 LLM이 직면하는 과제입니다. 따라서 ADK에서 단기 기억 관리를 위해 세션(Sessions)을 사용하고, 장기 기억을 위해 메모리(Memory)를 사용합니다. 다음 노트북에서는 메모리에 집중할 것입니다.
4.2.1 📦 세션(Session)이란 무엇인가?
세션은 대화를 담는 컨테이너입니다. 세션은 단일하고 지속적인 대화에 대한 대화 기록을 시간 순서대로 캡슐화하고, 모든 도구 상호 작용 및 응답을 기록합니다. 세션은 특정 사용자와 에이전트에 연결되어 있으며, 다른 사용자와 공유되지 않습니다. 마찬가지로, 한 에이전트의 세션 기록은 다른 에이전트와 공유되지 않습니다. ADK에서 세션은 Events와 State라는 두 가지 핵심 구성 요소로 이루어져 있습니다.
graph TD
subgraph A["Agentic Application"];
subgraph U["User"]
subgraph S1["Session"]
D1["Session.Events"]
D2["Session.State"]
end
end
end
📝 Session.Events: 세션이 대화를 담는 컨테이너라면, Events는 대화의 구성 요소입니다.
Events의 예시:
사용자 입력: 사용자로부터의 메시지(텍스트, 오디오, 이미지 등)
에이전트 응답: 사용자에 대한 에이전트의 답변
도구 호출: 에이전트가 외부 도구 또는 API를 사용하기로 결정한 행위
도구 출력: 에이전트가 추론을 계속하는 데 사용하는, 도구 호출에서 반환된 데이터
Session.State: session.state는 에이전트의 스크래치패드입니다. 에이전트는 대화 중에 필요한 동적 세부 정보를 여기에 저장하고 업데이트합니다. 이는 모든 하위 에이전트와 도구가 사용할 수 있는 전역 {key, value} 쌍 저장소라고 생각할 수 있습니다.
4.2.2 세션 관리 방법
에이전트 애플리케이션은 여러 사용자를 가질 수 있으며, 각 사용자는 애플리케이션과 여러 세션을 가질 수 있습니다. 이러한 세션과 이벤트를 관리하기 위해 ADK는 세션 관리자(Session Manager)와 실행기(Runner)를 제공합니다.
SessionService: 저장소 계층
세션 데이터의 생성, 저장, 검색을 관리합니다.
다양한 요구 사항(메모리, 데이터베이스, 클라우드)에 맞는 다양한 구현이 가능합니다.
Runner: 오케스트레이션 계층
사용자와 에이전트 간의 정보 흐름을 관리합니다.
대화 기록을 자동으로 유지합니다.
배경에서 컨텍스트 엔지니어링을 처리합니다.
다음과 같이 생각해 보세요.
세션 = 공책 📓
이벤트 = 공책의 개별 항목 📝
SessionService = 공책을 보관하는 파일 캐비닛 🗄️
Runner = 대화를 관리하는 조수 🤖
4.2.3 첫 번째 상태 유지 에이전트 구현하기
이제 기억력이 있는 첫 번째 상태 유지 에이전트를 구축해 봅시다. ADK는 다양한 요구 사항에 적합한 여러 유형의 세션을 제공합니다. 시작점으로, 간단한 세션 관리 옵션인 (InMemorySessionService)부터 시작하겠습니다.
# 상태 유지 에이전트 초기화 완료APP_NAME ="default"# 애플리케이션 이름USER_ID ="default"# 사용자 IDSESSION ="default"# 세션 IDMODEL_NAME ="gemini-2.5-flash-lite"# 1단계: LLM 에이전트 생성root_agent = Agent( model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), name="text_chat_bot", description="A text chatbot", # 에이전트의 목적(역할)에 대한 설명)# 2단계: 세션 관리 설정# InMemorySessionService는 대화 내용을 RAM에 일시적으로 저장합니다. (휘발성)session_service = InMemorySessionService()# 3단계: 실행기(Runner) 생성runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)print("✅ Stateful agent initialized!")print(f" - Application: {APP_NAME}")print(f" - User: {USER_ID}")print(f" - Using: {session_service.__class__.__name__}")# 동일한 세션 내에서 두 개의 질문으로 대화를 실행합니다.# 참고: 두 질문 모두 동일한 세션의 일부이므로 컨텍스트(맥락)가 유지됩니다.await run_session( runner, ["안녕하세요, 저는 Sam입니다! 미국의 수도는 어디인가요?","안녕하세요! 제 이름은 무엇인가요?", # 이번에는 에이전트가 이름을 기억해야 합니다! ],"stateful-agentic-session",)
App name mismatch detected. The runner is configured with app name "default", but the root agent was loaded from "/Users/fkt/Downloads/repo/learn_adk/.venv/lib/python3.11/site-packages/google/adk/agents", which implies app name "agents".
✅ Stateful agent initialized!
- Application: default
- User: default
- Using: InMemorySessionService
### Session: stateful-agentic-session
User > 안녕하세요, 저는 Sam입니다! 미국의 수도는 어디인가요?
gemini-2.5-flash-lite > 안녕하세요, Sam! 반갑습니다.
미국의 수도는 **워싱턴 D.C.** 입니다.
User > 안녕하세요! 제 이름은 무엇인가요?
gemini-2.5-flash-lite > 안녕하세요! 제 이름은 **Sam** 입니다.
4.3 📈 영구 세션 (DatabaseSessionService)
InMemorySessionService는 프로토타입 제작에 유용하지만, 실제 애플리케이션에서는 대화 내용이 재시작, 충돌, 배포 상황에서도 유지되어야 합니다. 이제 영구 저장소로 업그레이드합시다.
4.3.1 적합한 세션 서비스 선택
ADK는 다양한 요구 사항에 맞는 여러 가지 SessionService 구현을 제공합니다.
서비스
사용 사례
영구성
최적의 용도
InMemorySessionService
개발 및 테스트
❌ 재시작 시 손실됨
빠른 프로토타입
DatabaseSessionService
자체 관리 앱
✅ 재시작 후에도 유지됨
중소 규모 애플리케이션
Agent Engine Sessions
GCP 기반 프로덕션 환경
✅ 완전 관리형
엔터프라이즈 규모
4.3.2 영구 세션 구현
SQLite를 사용하는 DatabaseSessionService로 업그레이드합시다. 이 데모에서는 별도의 데이터베이스 서버 없이 영구성을 확보할 수 있습니다. 이제 사용자와 대화할 수 있는 chatbot_agent를 생성해 보겠습니다.
# 1단계: 동일한 에이전트 생성 (이번에는 LlmAgent를 사용합니다.)chatbot_agent = LlmAgent( model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), name="text_chat_bot", description="A text chatbot with persistent memory", # 영구 메모리를 가진 텍스트 챗봇)# 2단계: DatabaseSessionService로 전환# SQLite 데이터베이스가 자동으로 생성됩니다.db_url ="sqlite:///my_agent_data.db"# 로컬 SQLite 파일 경로session_service = DatabaseSessionService(db_url=db_url)# 3단계: 영구 저장소를 사용하는 새로운 실행기(Runner) 생성runner = Runner(agent=chatbot_agent, app_name=APP_NAME, session_service=session_service)# "테스트 실행 1: 영구성 확인"await run_session( runner, ["안녕하세요, 저는 Sam입니다! 미국의 수도는 어디인가요?", "안녕하세요! 제 이름은 무엇인가요?"],"test-db-session-01",)
App name mismatch detected. The runner is configured with app name "default", but the root agent was loaded from "/Users/fkt/Downloads/repo/learn_adk/.venv/lib/python3.11/site-packages/google/adk/agents", which implies app name "agents".
### Session: test-db-session-01
User > 안녕하세요, 저는 Sam입니다! 미국의 수도는 어디인가요?
gemini-2.5-flash-lite > 안녕하세요, Sam! 만나서 반갑습니다. 😊
미국의 수도는 **워싱턴 D.C.** 입니다.
User > 안녕하세요! 제 이름은 무엇인가요?
gemini-2.5-flash-lite > 안녕하세요! 당신의 이름은 **Sam**입니다. 😊
우리는 정보를 저장하기 위해 SQLite DB를 사용했습니다, 정보가 어떻게 저장되는지 살짝 살펴보겠습니다.
import jsonimport sqlite3def check_data_in_db():with sqlite3.connect("my_agent_data.db") as connection: cursor = connection.cursor() result = cursor.execute("select app_name, session_id, author, content from events")print([_[0] for _ in result.description])print("-"*50)for each in result.fetchall(): app_name, session_id, author, content_json = each# JSON을 로드하여 내부 유니코드 이스케이프를 한글로 변환합니다.try: content_data = json.loads(content_json) text_content = content_data.get("parts", [{}])[0].get("text", "내용 없음")except json.JSONDecodeError: text_content = content_json# 각 요소를 명시적으로 출력하여 한글 깨짐을 방지합니다.print(f"({app_name}, {session_id}, {author}, {text_content})")check_data_in_db()
['app_name', 'session_id', 'author', 'content']
--------------------------------------------------
(default, test-db-session-01, user, 안녕하세요, 저는 Sam입니다! 미국의 수도는 어디인가요?)
(default, test-db-session-01, text_chat_bot, 안녕하세요, Sam! 만나서 반갑습니다. 😊
미국의 수도는 **워싱턴 D.C.** 입니다.)
(default, test-db-session-01, user, 안녕하세요! 제 이름은 무엇인가요?)
(default, test-db-session-01, text_chat_bot, 안녕하세요! 당신의 이름은 **Sam**입니다. 😊)
# 기존 데이터베이스를 정리하여 새로 시작합니다.import osif os.path.exists("my_agent_data.db"): os.remove("my_agent_data.db")print("✅ 이전 데이터베이스 파일 정리 완료")
✅ 이전 데이터베이스 파일 정리 완료
4.4 🧠 메모리 관리
이전에 세션(Sessions)이 대화 스레드를 어떻게 관리하는지 배웠습니다. 이제 메모리(Memory)를 배워봅시다. 메모리는 여러 대화에서 지속되는 검색 가능한 장기 지식 저장소입니다.
4.4.1 메모리란 무엇인가요 ❓
메모리는 에이전트를 위한 장기 지식 저장소를 제공하는 서비스입니다. 핵심적인 차이점은 다음과 같습니다. 소프트웨어 엔지니어링 용어로 생각해보면 세션은 애플리케이션 상태(일시적)와 같고, 메모리는 데이터베이스(영구적)와 같습니다.
세션 = 단기 기억(단일 대화)
메모리 = 장기 지식(여러 대화에 걸쳐 유지)
4.4.2 🤔 메모리를 사용하는 이유
메모리는 세션만으로는 제공할 수 없는 기능을 제공합니다.
기능
의미
예시
교차 대화 회상(Cross-Conversation Recall)
모든 과거 대화의 정보에 접근
“이 사용자가 모든 채팅에서 언급한 선호 사항은 무엇인가요?”
지능형 추출(Intelligent Extraction)
LLM 기반 통합을 통한 핵심 사실 추출
원본 메시지 50개가 아닌 “땅콩 알레르기가 있음”을 저장
의미론적 검색(Semantic Search)
키워드 일치가 아닌 의미 기반 검색
“선호하는 색조”를 쿼리하면 “가장 좋아하는 색은 파란색”과 일치
영구 저장소(Persistent Storage)
애플리케이션 재시작 후에도 유지됨
시간이 지남에 따라 성장하는 지식 구축
예시: 개인 비서와 대화한다고 상상해 보세요. - 🗣️ 세션: 그들은 이 대화에서 10분 전에 당신이 말한 것을 기억합니다. - 🧠 메모리: 그들은 지난주 대화에서 당신이 언급한 선호 사항을 기억합니다.
4.5 🤓 메모리 워크플로 에이전트
메모리가 필요한 이유를 알았다면 에이전트에 메모리를 통합하는 다음 세 가지 주요 단계를 배워봅시다.
초기화(Initialize): MemoryService를 생성하고 Runner를 통해 에이전트에 제공합니다.
수집(Ingest): add_session_to_memory()를 사용하여 세션 데이터를 메모리로 전송합니다.
검색(Retrieve): search_memory()를 사용하여 저장된 메모리를 검색합니다.
ADK의 BaseMemoryService 인터페이스를 통해 여러 MemoryService 구현을 제공합니다. 아래 코드를 통해 살펴봅시다.
from google.adk.agents import LlmAgentfrom google.adk.memory import InMemoryMemoryServicefrom google.adk.models.google_llm import Geminifrom google.adk.runners import Runnerfrom google.adk.sessions import InMemorySessionServicefrom google.adk.tools import load_memory, preload_memoryfrom google.genai import types# 헬퍼 함수: 세션에서 쿼리를 실행하고 응답을 표시합니다.asyncdef run_session( runner_instance: Runner, user_queries: list[str] |str, session_id: str="default"):"""Helper function to run queries in a session and display responses."""print(f"\n### Session: {session_id}")# Create or retrieve sessiontry: session =await session_service.create_session( app_name=APP_NAME, user_id=USER_ID, session_id=session_id )except: session =await session_service.get_session( app_name=APP_NAME, user_id=USER_ID, session_id=session_id )# Convert single query to listifisinstance(user_queries, str): user_queries = [user_queries]# Process each queryfor query in user_queries:print(f"\nUser > {query}") query_content = types.Content(role="user", parts=[types.Part(text=query)])# Stream agent responseasyncfor event in runner_instance.run_async( user_id=USER_ID, session_id=session.id, new_message=query_content ):if event.is_final_response() and event.content and event.content.parts: text = event.content.parts[0].textif text and text !="None":print(f"Model: > {text}")retry_config = types.HttpRetryOptions( attempts=5, # Maximum retry attempts exp_base=7, # Delay multiplier initial_delay=1, http_status_codes=[429, 500, 503, 504], # Retry on these HTTP errors)memory_service = InMemoryMemoryService()# Define constants used throughout the notebookAPP_NAME ="agents"USER_ID ="demo_user"# Create agentuser_agent = LlmAgent( model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), name="MemoryDemoAgent", instruction="Answer user questions in simple words.",)# Create Session Servicesession_service = InMemorySessionService() # Handles conversations# Create runner with BOTH servicesrunner = Runner( agent=user_agent, app_name=APP_NAME, session_service=session_service, memory_service=memory_service,)
4.5.0.1 세션 데이터를 메모리로 전송
이제 메모리가 초기화되었으므로 다시 지식을 채워야 합니다. MemoryService를 초기화하면 완전히 비어 있는 상태로 시작합니다. 모든 대화는 세션에 저장되며, 여기에는 모든 메시지, 도구 호출 및 메타데이터를 포함한 원시(raw) 이벤트가 들어 있습니다. 이 정보를 장기적으로 회상할 수 있도록 하려면, add_session_to_memory()를 사용하여 명시적으로 메모리로 전송해야 합니다.
여기가 바로 Vertex AI Memory Bank와 같은 관리형 메모리 서비스가 빛을 발하는 지점입니다. 전송 중에 이러한 서비스는 대화의 불필요한 노이즈를 버리고 핵심 사실만 추출하는 지능적인 통합(consolidation)을 수행합니다. 우리가 사용하고 있는 InMemoryMemoryService는 통합 없이 모든 것을 저장하는데, 이는 작동 방식을 배우는 데 충분합니다.
무언가를 전송하기 전에 데이터가 필요합니다. 세션을 채우기 위해 에이전트와 대화를 나눠봅시다. 이 대화는 이전 노트북에서 배웠듯이 SessionService에 저장될 것입니다.
# 사용자가 에이전트에게 좋아하는 색깔을 알려줍니다.await run_session( runner,"My favorite color is blue-green. Can you write a Haiku about it?","conversation-01", # Session ID)
### Session: conversation-01
User > My favorite color is blue-green. Can you write a Haiku about it?
Model: > Deep ocean hue,
Tranquil waves of blue and green,
Peace washes over.
# Create a new runner with the updated agentrunner = Runner( agent=user_agent, app_name=APP_NAME, session_service=session_service, memory_service=memory_service,)await run_session(runner, "What is my favorite color?", "color-test")
### Session: color-test
User > What is my favorite color?
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Model: > Your favorite color is blue-green.
gemini-2.5-lite 모델은 한글을 잘 구분하지 못해 영어로 진행합니다.
완벽합니다! 세션에 우리의 대화 내용이 들어 있습니다. 이제 이 내용을 메모리로 전송할 준비가 되었습니다. add_session_to_memory()를 호출하고 세션 객체를 전달하세요. 이 작업은 대화 내용을 메모리 저장소에 수집하여, 향후 검색에 사용할 수 있도록 만듭니다.
4.6 🔎 메모리 검색 활성화(Enable Memory Retrieval in Your Agent)
세션 데이터를 메모리로 성공적으로 전송했습니다. 하지만 중요한 단계가 하나 남아 있습니다. 에이전트는 MemoryService에 직접 접근할 수 없습니다. 검색을 위해서는 도구가 필요합니다. 이것은 의도적인 설계로 메모리를 언제, 어떻게 검색할지 제어할 수 있습니다.
4.6.1 ADK의 메모리 검색
ADK는 메모리 검색을 위한 두 가지 내장 도구를 제공합니다.
도구
특징
설명
효율성
위험
load_memory (반응적)
에이전트가 메모리 검색 시점을 결정
에이전트가 필요하다고 판단할 때만 검색
더 효율적 (토큰 절약)
에이전트가 검색을 잊을 수 있음
preload_memory (선제적)
매 차례 전에 자동으로 검색
메모리가 에이전트에게 항상 제공됨
비효율적
필요하지 않을 때도 검색
이것을 시험공부에 비유해 보세요. load_memory는 필요할 때만 참고 자료를 찾아보는 것과 같습니다. 반면에 preload_memory는 질문에 답하기 전에 모든 노트를 읽어보는 것과 같습니다.
4.6.2 에이전트에 Load Memory 도구 추가
이제 이전에 만들었던 에이전트를 다시 생성하고, 이번에는 도구 키트에 load_memory 도구를 추가합니다. 이는 ADK의 내장 도구이므로, 별도의 사용자 정의 구현 없이 단순히 불러오면 됩니다.
# 에이전트 생성user_agent = LlmAgent( model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), name="MemoryDemoAgent", instruction="Answer user questions in simple words. Use load_memory tool if you need to recall past conversations.", # 사용자 질문에 간단한 단어로 답변하세요. 과거 대화를 회상해야 한다면 load_memory 도구를 사용하세요. tools=[ load_memory ], # 이제 에이전트가 메모리에 접근할 수 있으며, 필요하다고 판단될 때마다 메모리를 검색할 수 있습니다!)# 실행기(Runner) 업데이트 및 테스트# 업데이트된 에이전트로 새로운 실행기 생성runner = Runner( agent=user_agent, app_name=APP_NAME, session_service=session_service, memory_service=memory_service,)await run_session(runner, "What is my favorite color?", "color-test")
### Session: color-test
User > What is my favorite color?
Warning: there are non-text parts in the response: ['function_call'], returning concatenated text result from text parts. Check the full candidates.content.parts accessor to get the full model response.
Model: > Your favorite color is blue-green.
지금까지 우리는 수동으로 add_session_to_memory()를 호출하여 데이터를 메모리로 전송했습니다. 하지만 프로덕션 시스템에서는 이 과정이 자동으로 일어나야 합니다.
4.7 콜백을 사용한 자동 메모리 저장
자동 메모리 저장을 위해 after_agent_callback을 사용합니다. 이 함수는 에이전트가 한 턴을 마칠 때마다 트리거되며, add_session_to_memory()를 호출하여 대화 내용을 자동으로 지속시킵니다. 하지만 여기서 어려운 점이 있습니다. 콜백 함수가 실제로 메모리 서비스와 현재 세션에 어떻게 접근하는가 하는 것입니다. 바로 이 지점에서 callback_context가 필요합니다. 콜백 함수를 정의할 때, ADK(Agent Development Kit)는 callback_context라는 특별한 매개변수를 자동으로 함수에 전달합니다. callback_context는 메모리 서비스 및 기타 런타임 구성 요소에 대한 접근 권한을 제공합니다.
asyncdef auto_save_to_memory(callback_context):"""각 에이전트 턴(turn) 후에 세션을 메모리에 자동 저장합니다."""await callback_context._invocation_context.memory_service.add_session_to_memory( callback_context._invocation_context.session )# 자동 메모리 저장을 사용하는 에이전트auto_memory_agent = LlmAgent( model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config), name="AutoMemoryAgent", instruction="사용자 질문에 답변합니다.", tools=[preload_memory], after_agent_callback=auto_save_to_memory, # 각 턴 후에 저장합니다!)# 자동 저장 에이전트를 위한 러너(Runner)를 생성합니다.# 이는 자동화된 에이전트를 세션 및 메모리 서비스에 연결합니다.auto_runner = Runner( agent=auto_memory_agent, # 콜백 + preload_memory를 가진 에이전트를 사용합니다. app_name=APP_NAME, session_service=session_service, # 섹션 3의 동일한 서비스입니다. memory_service=memory_service,)# 테스트 1: 에이전트에게 선물에 대해 이야기합니다 (첫 번째 대화).# 턴이 완료되면 콜백이 이를 메모리에 자동으로 저장합니다.await run_session( auto_runner,"I gifted a new toy to my nephew on his 1st birthday!","auto-save-test",)# 테스트 2: 새로운 세션에서 선물에 대해 묻습니다 (두 번째 대화).# 에이전트는 preload_memory를 사용하여 메모리를 검색하고 올바르게 답변해야 합니다.await run_session( auto_runner,"What did I gift my nephew?","auto-save-test-2", # 다른 세션 ID입니다. 세션 간 메모리가 작동함을 증명합니다!)
### Session: auto-save-test
User > I gifted a new toy to my nephew on his 1st birthday!
Model: > That's wonderful! A first birthday is such a special milestone. I hope your nephew loves his new toy!
### Session: auto-save-test-2
User > What did I gift my nephew?
Model: > You gifted your nephew a new toy for his 1st birthday.
5 Day4: Agent Quality
5.1 🧠 AI 에이전트 품질 및 평가 프레임워크 요약
AI 에이전트 시대가 도래함에 따라, 비결정론적 특성을 가진 에이전트의 품질을 평가하는 새로운 접근 방식이 필요합니다. 전통적인 QA(검증) 대신, 동적 환경에서의 유효성 검증(Validation)에 초점을 맞춰야 합니다.
5.1.1 에이전트 품질의 4가지 핵심 요소
에이전트의 품질은 단순한 최종 결과가 아니라, 다음 네 가지 측면에서 평가되어야 합니다.
효과성(Effectiveness): 사용자 의도에 따라 목표를 얼마나 성공적으로 달성했는지(최종 결과).
효율성(Efficiency): 문제를 해결하는 데 사용된 자원(토큰 비용, 지연 시간, 단계 수) 및 과정의 복잡성.
견고성(Robustness): API 오류, 모호한 프롬프트 등 현실 세계의 어려움을 얼마나 잘 처리하고 우아하게 실패하는지.
안전 및 정렬(Safety & Alignment): 윤리적 경계 내에서 작동하고, 유해한 지침 거부 및 편향/프롬프트 주입 위험으로부터 안전한지.
5.1.2 평가 프레임워크: Inside-Out & Outside-In
평가는 최종 결과(Outside-In)부터 시작하여, 문제가 발생하면 과정(Inside-Out)을 진단하는 계층적 방식으로 진행됩니다.
Outside-In(블랙 박스): 사용자 중심 지표(작업 성공률, 사용자 만족도)를 통해 최종 성능을 먼저 평가합니다.
Inside-Out(유리 박스): 실패가 확인되면 에이전트의 전체 실행 궤적(Trajectory)을 분석하여 실패의 근본 원인(계획 품질, 도구 사용, RAG 성능 등)을 진단합니다.
5.1.3 평가자: 누가, 무엇으로 평가하는가
평가 유형
설명
장점
LLM-as-a-Judge
강력한 LLM(예: Gemini Advanced)이 심사위원 역할을 하여 다른 에이전트의 출력을 평가.
빠르고 미묘한 피드백 제공. 쌍대 비교 방식이 신뢰도 높음.
Agent-as-a-Judge
에이전트가 다른 에이전트의 전체 추적(trace)을 평가하여 프로세스 자체를 심층 진단.
계획 품질, 도구 사용 등 복잡한 프로세스 평가 가능.
Human-in-the-Loop(HITL)
사람이 직접 평가하여 자동화가 놓치는 깊은 주관성 및 도메인 지식 포착.
안전 보장(고위험 도구 호출 전 수동 승인) 및 ‘골든 세트’ 구축에 필수적.
5.1.4 관찰 가능성(Observability)
에이전트의 인지 과정 품질을 이해하기 위한 세 가지 핵심 요소입니다.
로그(Logging): 에이전트의 일기장. 시간 스탬프가 찍힌 이산적인 이벤트의 원시적 기록.
추적(Tracing): 에이전트의 발자취. 단일 작업에 대한 전체 과정을 연결하여 이벤트 간의 인과 관계를 보여줍니다. 복잡한 다단계 동작 디버깅에 필수적입니다.
메트릭(Metrics): 에이전트의 건강 보고서. 로그 및 추적 데이터를 집계한 정량적 성능 점수(예: 지연 시간, 오류율, 토큰당 비용).
5.1.5 에이전트 품질 플라이휠(The Agent Quality Flywheel)
에이전트 품질은 지속적인 개선 시스템을 통해 구현되어야 합니다.
품질 정의(Define Quality): 4가지 핵심 요소(효과성, 효율성, 견고성, 안전)를 목표로 설정.
가시성 확보(Instrument for Visibility): 로그 및 추적으로 에이전트 데이터를 확보.
프로세스 평가(Evaluate the Process): LLM-as-a-Judge 및 HITL을 사용하여 실행 궤적을 평가.
피드백 루프 설계(Architect the Feedback Loop): 프로덕션 실패를 영구적인 회귀 테스트로 자동 변환하여 시스템을 지속적으로 개선.
6 Day5: AgentOps의 모든것
에이전트를 성공적으로 운영하기 위해서는 자동화된 평가, 자동화된 배포(CI/CD), 포괄적인 관측 가능성이라는 세 가지 핵심 기둥이 필요하며, 이는 AgentOps라는 새로운 운영 규율의 기초를 형성합니다.
6.1 에이전트 운영의 주요 과제와 성공 요소
AI 에이전트는 전통적인 ML 모델과 달리 자율적으로 상호 작용하며 동적인 실행 경로를 따르기 때문에 고유한 운영상의 어려움을 야기합니다:
동적인 도구 오케스트레이션(Dynamic Tool Orchestration): 에이전트의 ’궤적(trajectory)’이 도구 선택에 따라 즉석에서 조립되므로, 예측 불가능한 시스템에 대한 견고한 버전 관리와 접근 제어가 필요합니다.
확장 가능한 상태 관리(Scalable State Management): 상호 작용 전반에 걸쳐 세션과 메모리를 안전하고 일관되게 관리해야 합니다.
예측 불가능한 비용 및 지연 시간: 에이전트가 답을 찾는 데 여러 경로를 취할 수 있어 비용과 응답 시간을 측하고 제어하기 어렵습니다.
6.2 프로덕션으로 가는 여정(Pre-Production)
프로토타입을 프로덕션으로 전환하는 과정은 평가 기반 배포(Evaluation-Gated Deployment)라는 핵심 원칙을 중심으로 구축됩니다. 평가(Evaluation)를 품질 게이트로 사용 에이전트 평가에서는 단순한 기능적 정확성을 넘어 에이전트의 행동 품질(behavioral quality)과 목표를 달성하기 위해 취한 전체 궤적(trajectory)을 평가해야 합니다. 평가는 골든 데이터셋(golden dataset)을 사용하여 LM-as-a-judge 기법 등을 통해 수행되며, 수동적인 검토(Pre-PR) 또는 CI/CD 파이프라인에 통합된 자동화된 게이트를 통해 시행됩니다. 자동화된 CI/CD 파이프라인 에이전트의 복잡한 구성 요소(소스 코드, 프롬프트, 도구 정의)를 관리하기 위해 CI/CD가 사용됩니다.
사전 병합 통합(Pre-Merge Integration): 풀 리퀘스트(PR) 단계에서 단위 테스트, 코드 린팅 및 에이전트 품질 평가를 실행하여 문제를 조기에 포착합니다.
스테이징 유효성 검사(Post-Merge Validation): 통합 시스템의 운영 준비 상태를 확인하기 위해 부하 테스트, 통합 테스트, 도그푸딩(dogfooding, 내부 사용자 테스트)을 수행합니다.
게이트식 프로덕션 배포: 최종 승인 후, 스테이징에서 검증된 아티팩트를 안전한 출시 전략을 사용하여 배포합니다.
6.3 에이전트 보안
보안을 초기 단계부터 구축 에이전트의 자율성으로 인해 프롬프트 주입(Prompt Injection), 데이터 유출, 메모리 오염(Memory Poisoning)과 같은 고유한 위험이 발생합니다. 방어는 세 가지 계층으로 이루어져야 합니다:
정책 정의 및 시스템 지침(System Instructions): 에이전트의 핵심 규범을 정의합니다.
가드레일 및 필터링: 프롬프트가 에이전트에 도달하기 전에 악의적인 입력을 차단하는 입력 필터링과 응답에서 유해한 콘텐츠나 PII를 확인하는 출력 필터링이 포함됩니다. 또한 고위험 행동에 대해서는 HITL(Human-in-the-Loop) 에스컬레이션이 필요합니다.
지속적인 보증: 새로운 위협에 대해 평가 파이프라인을 지속적으로 재실행하고 Red Teaming을 수행합니다.
6.4 운영(Operations in-Production)
에이전트가 배포된 후에는 관찰(Observe) → 행동(Act) → 진화(Evolve)의 연속적인 루프를 통해 안정성, 비용 효율성 및 안전성을 유지합니다. 관찰(Observe) 에이전트의 감각 시스템 역할을 합니다.
6.5 상호 운용성(Interoperability)
단일 에이전트 운영을 넘어 조직이 성장함에 따라, 서로 다른 팀이나 프레임워크로 구축된 에이전트 간의 협업을 위한 표준화된 프로토콜이 필요합니다. A2A(Agent2Agent) 프로토콜 A2A는 복잡하고 상태를 유지하는 에이전트 간의 협업을 위해 설계된 표준입니다.
에이전트는 에이전트 카드(Agent Card)라는 표준화된 JSON 사양을 통해 자신의 기능, 보안 요구 사항 및 연락 방법을 게시하여 동적으로 서로를 검색할 수 있습니다.
A2A 상호 작용은 비동기 ’태스크(task)’를 중심으로 구성되며, 이는 장기적인 연결에서 진행 상황을 추적하기 위해 견고한 상태 관리 계층이 필요함을 의미합니다. A2A와 MCP의 상호 보완성 A2A와 MCP(Model Context Protocol)는 경쟁 관계가 아니라 상호 보완적인 프로토콜입니다.
MCP: 계산기나 데이터베이스 API와 같이 명확하고 구조화된 입출력을 가진 단순하고 상태 비저장적인 도구(Tool)와의 통합 영역입니다.
A2A: 추론하고, 계획하고, 행동할 수 있는 자율적인 다른 에이전트에게 복잡한 목표를 위임하는 영역입니다.
가장 강력한 시스템은 A2A를 사용하여 고수준의 에이전트 간 오케스트레이션을 수행하고, 각 에이전트가 내부적으로 MCP를 사용하여 자체 도구와 상호 작용하도록 계층화된 아키텍처를 사용합니다.
대규모 에이전트 생태계에서는 도구 및 에이전트의 검색과 거버넌스를 중앙 집중화하기 위해 도구 레지스트리와 에이전트 레지스트리 아키텍처를 구축할 수 있습니다.