Skip to main content

Google의 Agent Development Kit과 Agent2Agent 사용하기

AI 상호운용성을 위한 Agent2Agent(A2A) 오픈 프로토콜을 살펴보세요. A2A의 설계 원칙을 이해하고, 튜토리얼에서 Google의 ADK를 사용해 멀티 에이전트 시스템을 구축해 보세요. 이 글은 AI 번역 기사입니다. 오역이 의심되는 부분은 댓글로 알려주세요.
Created on September 12|Last edited on September 12
AI 에이전트의 환경은 빠르게 진화하고 있습니다. 일정 관리, 복잡한 연구 내용 요약, 소프트웨어 개발 보조 등 점점 더 전문화된 작업을 수행할 수 있는 지능형 어시스턴트가 급격히 늘어나고 있습니다. 이러한 에이전트는 더 이상 신기한 도구에 그치지 않고, 이제는 각 산업 전반의 워크플로에 필수적인 구성 요소가 되고 있습니다.
그러나 이러한 에이전트의 능력과 전문성이 커질수록 새로운 과제가 떠오릅니다. 바로 상호운용성입니다. 서로 다른 회사가 각기 다른 아키텍처로 개발하고 별도의 생태계에서 동작하는 여러 에이전트가 보다 복잡한 다단계 작업에서 협업해야 할 때, 과연 무엇이 일어날까요? 현재의 인프라는 이렇게 분리된 시스템 전반에 걸친 매끄러운 조정을 전제로 설계되지 않았으며, 에이전트 기반 자동화에 대한 의존도가 깊어질수록 이 한계는 더욱 뚜렷해지고 있습니다.
The Agent2Agent (A2A) 프로토콜은 바로 이러한 장벽을 허무는 것을 목표로 개발되고 있습니다. 표준화된 포트나 네트워크 프로토콜처럼, AI 에이전트를 위한 공통 언어와 운영 표준을 마련하는 것이라고 생각하면 됩니다. A2A는 이 필수적인 공통 기반을 제공하는 오픈 스펙으로서, 누가 만들었든 어디에서 동작하든 에이전트가 매끄럽게 상호작용하고 협업할 수 있도록 하는 것을 지향합니다. 기대 효과는 분명합니다. 간소화된 위임으로 인한 생산성 향상, 표준화에 따른 개발 오버헤드 감소, 그리고 다양한 에이전트 역량을 손쉽게 조합할 수 있는 더 풍부한 생태계의 조성입니다. 이러한 비전은 강력한 지원을 바탕으로 더욱 힘을 얻고 있으며 주요 기술 파트너, (그리고 Weights & Biases도 그 중 한 곳으로 자부심을 갖고 참여하고 있습니다.) A2A는 더 통합되고 강력한 AI 미래를 향한 실질적인 도약을 의미합니다. 이 가이드에서는 다음 내용을 체계적으로 풀어낼 것입니다 Agent2Agent 프로토콜 – 무엇인지, 어떻게 작동하는지, 그리고 왜 주목을 받고 있는지.


목차




Agent2Agent 프로토콜이란 무엇인가?

근본적으로 Agent2Agent는 오픈 프로토콜 명세로 확립되고 있습니다. 느슨한 합의가 아니라, 명확히 정의된 기술 표준의 집합으로 이해하면 됩니다. 구체적인 통신 스키마, API 정의, 표준화된 상호작용 패턴을 규정한 것으로, 다음을 위해 정교하게 설계되었습니다 AI 에이전트 협업핵심 기술적 기능은 서로 다른 AI 에이전트기반 아키텍처나 개발 주체와 무관하게, 에이전트들이 프로그램적으로 서로를 발견하고, 작업과 기능에 관한 구조화된 데이터를 교환하며, 시스템 경계를 넘어 기능을 신뢰성 있게 호출할 수 있도록 합니다.
지금 이 시점에서 AI 에이전트 간에 이러한 기술적 상호운용성을 달성하는 것이 왜 그렇게 중요한가요? 그러한 표준이 없으면 AI 생태계는 계속 분절된 상태로 남기 때문입니다. 강력한 에이전트들은 종종 독자적인 사일로 안에서 작동하여, 의미 있는 에이전트 간 협력 비효율적이고 비용이 많이 들며, 새로운 연결마다 맞춤형 API 통합을 요구하는 경우가 대부분입니다. Agent2Agent 프로토콜은 이러한 통합 문제를 정면으로 해결합니다.
이 프로토콜의 주요 목표는 이러한 기술적 난관을 극복하는 데 초점을 맞추고 있습니다:
  1. 워크플로 생산성 향상 에이전트 간의 작업 위임, 컨텍스트 공유, 정보 교환 방식을 표준화하여 보다 매끄러운 자동화를 가능하게 합니다.
  2. 통합 오버헤드 감소 공통된 통신 백본과 인터페이스 정의를 제공함으로써, 서로 다른 에이전트 시스템을 연결하는 데 필요한 엔지니어링 노력과 비용을 크게 낮춥니다.
  3. 복합 혁신 가속화 이러한 표준화된 인터페이스를 통해 다양한 에이전트의 특화된 기능을 구성하거나 연쇄하여 결합하는 새로운 애플리케이션을 개발자가 기술적으로 더 쉽게 구축할 수 있도록 함으로써.
요컨대 Agent2Agent는 더 협력적이고 강력하며 상호 연결된 AI 에이전트 생태계를 구축하기 위해 필요한 핵심 기술적 “배관”을 제공하는 것을 목표로 합니다.

Agent2Agent 프로토콜의 핵심 설계 원칙

Agent2Agent 프로토콜과 같은 견고한 표준을 개발하려면 명확한 철학이 필요합니다. 이는 단순히 에이전트를 연결하는 문제를 넘어, 이를 효과적이고 안전하며 유연하게 수행하는 데 초점이 있습니다. A2A 프로토콜은 그 아키텍처와 구현을 규정하는 다섯 가지 핵심 설계 원칙 위에 구축되었습니다.
  1. 에이전트형 능력 수용 이는 단순한 요청-응답 상호작용만을 다루는 것이 아닙니다. 이 프로토콜은 근본적으로 AI 에이전트가 갖춘 능력을 전제로 설계되었으며, 에이전시 – 목표 달성을 위해 추론하고, 계획을 세우며, 자율적으로 작동하고, 의사결정을 내리는 능력. A2A는 이러한 정교한 능력을 지원하여, 에이전트들이 단순한 데이터 교환을 넘어 복잡하고 다단계의 협업에 참여할 수 있도록 하는 것을 목표로 합니다.
  2. 기존 표준 위에 구축하기 Agent2Agent은 가능한 모든 곳에서 HTTP, RESTful 원칙, OAuth와 같은 보안 패턴 등 이미 확립되어 널리 채택된 웹·인터넷 표준을 활용합니다. 인용되는 개별 표준은 시간이 지나며 발전할 수 있지만, 핵심 원칙은 검증된 기존 기술 위에 구축하는 것입니다. 이러한 접근은 개발 속도를 높이고, 기존 인프라와의 상호 운용성을 개선하며, 해당 표준에 익숙한 개발자가 Agent2Agent 프로토콜을 더 쉽게 도입하고 구현할 수 있게 합니다.
  3. 기본값으로 보안을 보장하기 가지고 있을 때 자율 에이전트 사용자나 시스템을 대신해 행동할 가능성이 있는 만큼, 보안을 사후에 덧붙이는 요소로 취급할 수는 없습니다. Agent2Agent는 보안을 핵심 설계 단계에서부터 통합합니다. 이는 처음부터 인증, 인가, 안전한 데이터 교환을 위한 메커니즘을 정의하여, 에이전트 간 상호작용이 신뢰할 수 있고 오남용으로부터 보호되도록 보장한다는 뜻입니다.
  4. 장기 실행 작업 지원하기: 현실 세계의 업무는 즉시 끝나지 않는 경우가 많습니다. 에이전트는 전체 여행 일정을 계획하거나, 상세 보고서를 생성하거나, 대규모 데이터셋을 처리하는 것처럼 상당한 시간이 걸리는 복잡한 작업을 맡을 수 있습니다. Agent2Agent 프로토콜은 이러한 비동기적이고 장기 실행 작업을 처리하도록 명시적으로 설계되었으며, 수분, 수시간, 혹은 그보다 더 오래 걸릴 수 있는 작업에 필요한 통신과 상태 추적을 관리합니다.
  5. 모달리티에 구애받지 않기: 협업은 다음에 의해 제한되어서는 안 됩니다 유형 처리되는 정보의 유형과 관계없이, 에이전트가 텍스트, 이미지, 구조화된 데이터, 음성 명령 등 다양한 형태의 정보(모달리티)를 교환해야 할 때에도 프로토콜은 유연하게 동작하도록 설계되었습니다. Agent2Agent는 서로 다른 데이터 유형이 관여된 상호작용을 원활히 지원하도록 고안되어, 단일한 통신 방식에 묶이지 않고 폭넓은 애플리케이션과 에이전트 역량을 뒷받침할 수 있습니다.
이 다섯 가지 원칙은 Agent2Agent의 방향을 이끄는 나침반 역할을 하며, 결과물인 프로토콜이 정교한 에이전트 상호작용을 처리할 만큼 강력하고, 기존 기술을 활용해 실용적이며, 설계 단계부터 보안을 내재하고, 현실 세계의 작업 소요 시간을 감당할 수 있고, 미래의 요구에 대응할 만큼 유연하도록 보장합니다.

Agent2Agent 프로토콜의 핵심 구성 요소

협업을 가능하게 하기 위해 Agent2Agent 프로토콜은 상호작용의 서로 다른 부분을 처리하는 특정 아키텍처 구성 요소들을 정의합니다. 핵심 구성 요소는 다음과 같습니다:

  1. 에이전트 카드: 본질적으로 에이전트의 디지털 신원과 역량 선언서에 해당합니다. 표준화된 명함 또는 AI 에이전트를 위한 매니페스트 파일을 떠올리면 됩니다. 에이전트 카드는 다음과 같은 핵심 메타데이터를 포함할 가능성이 큽니다:
    • 식별: 에이전트가 누구인지.
    • 기능: 에이전트가 수행할 수 있는 작업이나 제공하는 서비스.
    • 엔드포인트: 다른 에이전트가 어떻게 통신할 수 있는지(예: API 주소).
    • 요구 사항: 상호작용에 필요한 모든 정보로, 요구하는 보안 프로토콜이나 데이터 형식 등이 포함될 수 있습니다. 에이전트 카드의 기본 기능은 검색입니다. 잠재적 클라이언트(다른 에이전트나 시스템)가 에이전트를 찾고, 제공하는 기본 기능과 연결 방법을 이해할 수 있도록 합니다.
      
  2. A2A 서버: 이 구성 요소는 일반적으로 서비스나 기능을 제공하는 에이전트(상호작용에서 흔히 ‘원격 에이전트’라고 함)와 함께 실행됩니다. 주요 책임은 다음과 같습니다:
    • 수신: Agent2Agent 프로토콜 명세에 따라 형식화된 수신 요청을 수신하기 위해 리슨하는 엔드포인트를 노출합니다.
    • 처리 중 유효한 Agent2Agent 요청을 수신해 해석하고, 실제 기반 AI 에이전트 로직과 연동하여 요청된 작업을 실행합니다.
    • 관리: 이 구성 요소는 작업의 라이프사이클을 관리하며, 설계 원칙에서 언급한 장기 실행 작업의 경우 특히 중요합니다.
    • 응답: 표준화된 Agent2Agent 형식을 사용해 요청 클라이언트에게 응답, 상태 업데이트, 최종 결과를 다시 전송합니다. 본질적으로 A2A 서버는 A2A 프레임워크 내에서 에이전트의 기능을 호스트하고 실행을 처리하는 핸들러 역할을 합니다.
  3. A2A 클라이언트: 이 구성 요소는 시작한다 상호작용을 시작하는 주체입니다. 다른 AI 에이전트, 애플리케이션, 또는 원격 에이전트의 기능을 활용해야 하는 사용자용 인터페이스일 수 있습니다. Agent2Agent 클라이언트의 역할은 다음을 포함합니다:
    • 탐색(Agent Card 사용) 적절한 에이전트를 찾고, 그와 어떻게 상호작용할지 이해하기.
    • 요청 구성: Agent2Agent 프로토콜 표준을 준수하도록 요청 메시지를 구성하고, 수행할 작업을 명시하며 필요한 입력 데이터를 제공하기.
    • 통신: 대상 에이전트의 A2A 서버 엔드포인트로 요청을 전송하기.
    • 응답 처리: A2A 서버로부터 응답(또는 업데이트)을 수신하고 처리하기. A2A 클라이언트는 agent2agent 상호작용에서 서비스를 시작하고 소비하는 주체입니다.
이 세 가지 구성 요소—설명 역할의 Agent Card, 서비스를 제공하는 A2A Server, 요청을 시작하는 A2A Client—는 Agent2Agent 프로토콜 하에서 서로 다른 AI 에이전트 간의 구조화되고 표준화된 통신과 작업 관리를 가능하게 하는 기본 삼각 구도를 이룹니다. 프로토콜 구조의 전체 JSON은 다음에서 확인할 수 있습니다 A2A의 GitHub 저장소.

통신과 작업 관리 촉진

그러면 Agent Card, A2A Client, A2A Server가 있습니다. 이 구성 요소들은 Agent2Agent 프로토콜을 통해 서로 다른 AI 에이전트 간에 어떻게 매끄러운 통신을 가능하게 하고 작업을 완료하게 할까요? 핵심은 구조화된 상호작용 흐름에 있습니다:
  1. 작업 시작: 일반적으로 A2A 클라이언트에서 시작됩니다. 예를 들어, 한 에이전트가(클라이언트 역할) 다른 에이전트(원격 에이전트)의 특화된 능력이 필요할 수 있습니다. 예를 들면 문서 요약이나 이미지 분석 같은 작업입니다. 클라이언트는 에이전트 카드 등을 통해 원격 에이전트와 그 능력을 발견한 뒤, 작업 요청을 구성합니다. 이는 가벼운 메시지가 아니라 A2A 프로토콜의 명세를 준수해 정교하게 포맷된 요청입니다. 표준화된 이 요청은 원하는 동작을 명확히 기술하고, 요약할 문서와 같은 필요한 입력 데이터도 함께 포함합니다.
  2. 전송 및 처리: 그다음 클라이언트는 프로토콜을 준수한 이 요청을 원격 에이전트의 A2A 서버에서 지정한 엔드포인트로 전송합니다. 서버가 요청을 수신하면 가장 먼저 하는 일은 보통 유효성 검증으로, Agent2Agent 규칙을 따르는지 확인하는 것입니다. 검증이 끝나면 서버는 요청을 해석하고, 관련된 AI 에이전트의 내부 로직을 호출하면서 작업 상세와 입력 데이터를 함께 전달합니다. 이로써 실제 작업이 시작됩니다.
  3. 상호작용 관리(특히 장기 작업의 경우): 바로 여기서 장기 실행 작업을 처리하기 위한 설계 원칙이 중요해집니다. 요청된 작업에 몇 초 이상이 소요될 경우, A2A 서버는 묵묵히 대기만 하지 않을 수 있습니다. 프로토콜은 서버가 작업 수신을 즉시 확인(ack)한 뒤, 필요에 따라 A2A 클라이언트에게 주기적인 상태 업데이트(“처리 시작”, “50% 완료” 등)를 제공할 수 있는 메커니즘을 허용합니다. 이를 통해 클라이언트가 암중모색하며 기다리는 상황을 방지하고, 진행 상황을 더욱 역동적이고 실시간에 가깝게 모니터링할 수 있습니다.
  4. 완료 및 응답 전송 원격 에이전트가 할당된 작업을 완료하면, A2A 서버는 결과(또는 처리 중 발생한 오류)를 Agent2Agent 프로토콜에서 정의한 표준화된 응답 형식으로 포장하여 원래 요청을 보낸 A2A 클라이언트로 반환합니다.
  5. 결과 수신 및 활용: A2A 클라이언트는 이 구조화된 응답을 수신합니다. 응답이 Agent2Agent 표준에 따라 포맷되어 있기 때문에, 클라이언트는 이를 정확히 파싱하고 요약본, 분석 결과 등 관련 정보를 추출한 뒤, 사용자에게 표시하거나 더 큰 워크플로의 다음 단계로 전달하는 등 자체 용도에 맞게 활용할 수 있습니다.
이와 같은 구조화된 흐름—표준화된 시작, 상태 업데이트가 가능한 서버 측 처리, 표준화된 응답 전달—이 바로 Agent2Agent가 다양한 AI 에이전트와 플랫폼 전반에서 신뢰할 수 있고 역동적인 통신과 효율적인 작업 관리를 가능하게 하는 핵심 메커니즘을 이룹니다.

실제 활용 사례와 Agent2Agent의 미래

Agent2Agent의 기술적 프레임워크는 실질적 효과를 목표로 설계되었습니다. 그렇다면 복잡한 작업을 실제로 어디에서 단순화하는 모습을 보게 될까요? 하나의 사례를 살펴보겠습니다. 통합 여행 계획입니다.
선호하는 캘린더 앱이나 전용 일정 관리 도우미(A2A 클라이언트 역할)를 사용하고 있다고 가정해 봅시다. 주말 여행을 계획하고 싶어졌습니다. 항공권, 호텔, 액티비티를 확인하려고 여러 웹사이트나 앱을 일일이 열 대신, 도우미에게 목적지와 날짜만 알려주면 됩니다. 그러면 이 클라이언트 에이전트는 A2A를 활용하여 다음을 수행할 수 있습니다:
  1. 표준화된 요청을 해당 기준과 함께 전문 항공편 검색 에이전트(자체 A2A 서버에서 실행 중)에 전송합니다.
  2. 동시에 또는 순차적으로 다른 A2A 서버인 호텔 예약 에이전트에 가용 여부와 가격을 조회하고, 필요하다면 첫 번째 에이전트의 잠재적 항공편 시간 정보를 입력으로 활용합니다.
  3. 해당 날짜에 맞춰 관심사에 부합하는 콘서트, 투어, 또는 레스토랑 추천을 찾기 위해 지역 이벤트·추천 에이전트(또 다른 A2A 서버)에 문의합니다.
각 전문 에이전트는 Agent2Agent 요청을 처리하고 프로토콜에서 정의한 표준화된 형식으로 관련 정보(항공편 옵션, 호텔 선택지, 이벤트 목록)를 반환합니다. 그런 다음 기본 도우미(클라이언트)가 이 구조화된 데이터를 집계하고, 필요하다면 의존성을 해소합니다(예: 실제로 가능한 항공편 시간대에 대해서만 호텔 가용성을 확인). 이후 하나로 정리된 일정 제안을 제공합니다. 공통 Agent2Agent 언어가 매개하는 이러한 서비스 공급자 간의 매끄러운 통합은 A2A가 제공하려는 효율성 향상의 정수입니다.
이 여행 계획 시나리오는 가능한 사례 중 하나에 불과합니다. Agent2Agent의 진정한 목표는 훨씬 더 폭넓은 에이전트 상호운용성을 촉진하는 데 있습니다. 예를 들어, 과학 연구에서 실험 데이터를 관리하는 에이전트가 A2A를 통해 분석 에이전트를 호출하고, 분석 결과를 시각화 에이전트로 전달하는 전 과정을 각 단계 사이의 수동 데이터 정리 없이 수행하는 모습을 떠올려 보세요.
텍스트를 생성하는 에이전트가 이미지를 만드는 에이전트, 배경 음악을 작곡하는 또 다른 에이전트와 협업하는 창의적 워크플로를 생각해 보세요. 이렇게 다양하고 전문화된 AI 역량을 결합하는 잠재력은 혁신을 촉진하는 중요한 동력이 될 것으로 기대되며, 개발자가 더 정교하고 강력한 복합형 AI 애플리케이션을 구축할 수 있도록 해 줍니다.
우리의 관점에서 바라볼 때, 이러한 미래를 가능하게 하는 핵심 요소는 프로토콜의 오픈 소스 특성입니다. Agent2Agent 사양을 공개적으로 제공함으로써 폭넓은 채택을 촉진하고, 누구나 이 공통 언어를 ‘구사’할 수 있는 에이전트를 만들 수 있게 합니다. 더 나아가 이는 커뮤니티 기여를 활성화합니다. 다양한 개발자와 조직이 Agent2Agent에 참여하면서 표준을 함께 다듬고, 모범 사례를 정립하며, 유용한 툴링을 개발하고, 인사이트를 공유함으로써 궁극적으로 풍부하고 상호 연결된 AI 생태계의 발전을 가속할 수 있습니다. 이러한 개방형 접근에 내재된 협업 잠재력은 A2A가 약속하는 가치를 온전히 실현하는 데 핵심적입니다.
A2A는 Google Cloud Next 2025에서 공개되어 이러한 에이전트들이 협업할 수 있는 오픈 표준 ‘언어’를 제공하지만, 실제로 견고하고 프로덕션 준비가 된 에이전트와 복잡한 멀티 에이전트 시스템을 구축하려면 전용의 강력한 툴링이 필요합니다. 이 구현 과제를 정면으로 해결하기 위해 Google은 Agent Development Kit (ADK) 같은 행사에서 공개되었습니다. ADK는 정교한 에이전트 기반 애플리케이션의 엔드 투 엔드 개발 생애주기를 간소화하도록 설계된 포괄적인 오픈 소스 프레임워크입니다. 주목할 점은, 구글의 자체 제품 내 에이전트를 구동하는 바로 그 동일한 프레임워크가 이제 전 세계 개발자에게 제공된다는 것입니다.
ADK는 다음과 같은 핵심 기능들을 중심으로 설계되었습니다:
  • 멀티 에이전트 시스템 핵심 내용 Agent Development Kit은 여러 전문화된 에이전트로 구성된 애플리케이션을 구축하고, 이들 간의 복잡한 조정과 위임을 원활히 수행할 수 있도록 명시적으로 설계되었습니다.
  • 모델과 도구의 유연성: 개발자가 선호하는 것을 선택할 수 있도록 합니다 대규모 언어 모델 (제미니, 모델을 통해 Vertex AI Model Garden 또는 LiteLLM 통합을 통한 다양한 모델을 활용할 수 있으며, 에이전트에 검색과 같은 사전 구축 기능부터 서드파티 라이브러리에 이르는 폭넓은 도구를 갖추게 할 수 있습니다 (LangChain, LlamaIndex) 그리고 다른 에이전트를 도구로 활용하는 것까지 가능합니다.
  • 풍부한 상호작용: Agent Development Kit은 더욱 자연스럽고 사람과 유사한 대화를 위해, 고유한 양방향 오디오 및 비디오 스트리밍 기능을 포함한 고급 상호작용 모델을 지원합니다.
  • 전체 수명주기 지원: 구축을 넘어, ADK는 유연한 오케스트레이션 옵션, 통합된 로컬 개발 환경(CLI와 Web UI), 에이전트 성능을 평가하기 위한 내장 평가 프레임워크, 그리고 배포를 위한 간소화된 경로(컨테이너화 또는 Google Cloud의 Vertex AI에 최적화)를 제공합니다.

자습서: 멀티 에이전트 시스템 구축

Agent Development Kit은 유연성과 제어권을 강화하여 정교한 에이전트형 애플리케이션을 구축, 테스트, 실행할 수 있는 종합 툴킷을 제공합니다. ADK가 이 과정을 어떻게 단순화하는지 체감하기 위해, 이제 개념을 구체화해 보겠습니다. 우리는 메모리 통합, 안전 가드레일 구현, 루트 에이전트로부터의 자동 위임 설정 방법을 모두 ADK로 보여 주는, 간단하지만 설명력이 높은 멀티 에이전트 시스템을 직접 만들어 보겠습니다.

도구 키트 설정하기

코드로 들어가기 전에, 이번 자습서에 꼭 필요한 핵심 Python 라이브러리와 그 이유를 간단히 살펴보겠습니다.
  • google-adk: 쇼의 주인공은 바로 Google의 Agent Development Kit입니다. 이 툴킷은 에이전트와 그 도구를 정의하고, 상호작용을 관리하며(앞서 언급한 에이전트 간 협업을 가능하게 하고), 전체 시스템을 오케스트레이션하는 핵심 프레임워크를 제공합니다.
  • litellm: ADK의 유연성을 보여 주기 위해 우리는 litellm을 사용할 것입니다. 이 라이브러리는 브리지 역할을 하여, Google의 모델뿐 아니라 OpenAI, Anthropic 등 다양한 대규모 언어 모델을 우리 ADK 에이전트가 폭넓게 사용할 수 있도록 해 줍니다.
  • weave 에이전트 시스템이 복잡해질수록 내부에서 무슨 일이 일어나는지 파악하는 것이 중요합니다. 우리는 이를 통합할 것입니다. W&B Weave 가시성을 추가하여 에이전트의 동작과 의사결정 과정을 추적하고, 시각화하며, 디버그할 수 있도록 돕습니다.
  • pyowm: 우리 에이전트에 실용적인 능력을 하나 추가해 봅시다! 이 라이브러리는 OpenWeatherMap API에 손쉽게 연결하고 실시간 날씨 데이터를 가져오는 편리한 방법을 제공합니다.
  • newsapi-python: 또 다른 차원을 더하기 위해, 이 라이브러리를 사용해 NewsAPI를 통해 특정 주제의 최신 뉴스 헤드라인을 가져오겠습니다.
이 튜토리얼은 핵심 Agent Development Kit 메커니즘을 시연합니다. 주요 코드 스니펫을 강조하고 핵심 개념을 설명하겠습니다. 완전한 실행 가능한 코드와 더 자세한 내용은 관련 GitHub 저장소를 확인해 보시길 강력히 권장합니다. 이 예시는 다음에서 발췌·응용되었습니다. 튜토리얼 공식 ADK 문서에서 확인할 수 있습니다.

우리가 만들 것

우리의 목표는 여러 기능을 수행할 수 있는 다중 에이전트 보조 팀을 구축하고, 핵심 Agent Development Kit 기능을 시연하는 것입니다.
  • 전문화된 인사 및 작별 하위 에이전트를 통한 대화상 예의 관리.
  • pyowm 라이브러리와 외부 API를 사용해 실시간 날씨 상태를 가져오고 보고하기.
  • 사용자가 입력한 주제에 대해 newsapi-python을 사용해 최신 뉴스 헤드라인 가져오기.
  • 보안 점검 구현하기
    • 특정 키워드를 포함한 요청을 차단하는 입력 가드레일.
    • 특정 도시(예: “Paris”)에 대해 날씨 도구 사용을 금지하는 도구 사용 가드레일.
이 실습 예시는 ADK가 여러 에이전트, 외부 도구, 상태 관리(도구 컨텍스트와 콜백을 통한 묵시적 관리), 그리고 안전 계층을 하나의 응집력 있는 시스템으로 오케스트레이션하는 방법을 보여줍니다.
필요한 라이브러리를 임포트합시다
import os
import warnings
import logging
import asyncio
from typing import Optional, Dict, Any
import weave

# External API Clients
import pyowm
from newsapi import NewsApiClient

# ADK Components
from google.adk.agents import Agent
from google.adk.models.lite_llm import LiteLlm # For potential multi-model use
from google.adk.sessions import InMemorySessionService
from google.adk.runners import Runner
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.base_tool import BaseTool
from google.adk.agents.callback_context import CallbackContext
from google.adk.models.llm_request import LlmRequest
from google.adk.models.llm_response import LlmResponse

# Google Generative AI types
from google.genai import types as google_types
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# --- Configuration ---
warnings.filterwarnings("ignore")
logging.basicConfig(level=logging.INFO)


weave.init("Google-Agent2Agent")

# --- Define Model Constants ---
MODEL_GEMINI_2_0_FLASH = "gemini-2.0-flash"

도구 정의하기

설정이 끝났다면, 단순한 텍스트 생성 이상으로 에이전트에게 구체적인 능력을 어떻게 부여할 수 있을까요? 바로 여기서 Agent Development Kit 내의 Tools가 필요합니다. 문서에서 강조하듯이:
ADK에서 Tools는 단순한 텍스트 생성 이상으로 에이전트에 구체적인 능력을 부여하는 구성 요소입니다. 보통 API 호출, 데이터베이스 조회, 계산 수행처럼 특정 작업을 수행하는 일반적인 Python 함수로 구현됩니다.
에이전트 팀을 위해 몇 가지 도구를 정의하겠습니다. 먼저 개념을 쉽게 익히고 핵심 아이디어를 보여 주기 위해 아주 기본적인 두 가지 도구부터 시작하겠습니다. 하나는 인사를 처리하고, 다른 하나는 작별 인사를 처리합니다. 대규모 애플리케이션에서는 이런 단순한 상호작용을 다른 방식으로 통합할 수도 있지만, 이를 개별 도구로 만드는 것은 핵심 메커니즘을 이해하는 데 매우 좋습니다. 즉, Python 함수를 정의하고, 해당 함수의 목적과 인자에 대해 명확하게 설명하는 독스트링을 작성합니다(이는 매우 중요합니다. 대규모 언어 모델), 그리고 이를 에이전트에 연결합니다.
# Greeting and Farewell Tools
@weave.op() # Add weave decorator
def say_hello(name: str = "there") -> str:
"""Provides a simple greeting."""
logging.info(f"Tool 'say_hello' executed with name: {name}")
return f"Hello, {name}!"


@weave.op() # Add weave decorator
def say_goodbye() -> str:
"""Provides a simple farewell message."""
logging.info("Tool 'say_goodbye' executed.")
return "Goodbye! Have a great day."

현실 세계 기능 추가하기: 날씨 및 뉴스 도구

간단한 인사는 기초를 보여 주는 데 유용하지만, 에이전트 시스템의 진정한 강점은 외부 데이터와 서비스와 상호작용하는 능력에 있습니다. 에이전트 팀에 더 실용적인 도구 두 가지를 추가해 보겠습니다.
  • 실시간 날씨: 우리는 …을(를) 정의할 것입니다 get_real_weather 함수입니다. 이 도구는 pyowm 라이브러리를 사용해 OpenWeatherMap API에 연결하고, 사용자가 지정한 도시의 현재 날씨 정보를 가져옵니다. 특히 이 도구는 에이전트의 메모리(세션 상태를 통해)와도 상호작용합니다. ToolContext) 사용자 선호도(예: 선호하는 온도 단위인 섭씨 또는 화씨)를 활용하거나 기억하도록 합니다.
  • 최신 뉴스 헤드라인: 다음으로, 우리는 …을(를) 추가하겠습니다 get_latest_news function이 도구는 …을(를) 활용하여 newsapi-python 라이브러리를 사용해 사용자가 제공한 주제를 바탕으로 NewsAPI에서 최신 헤드라인을 조회합니다.
이 함수들을 정의할 때, 우리는 …을(를) 추가할 것임을 확인하세요 @weave.op() 그 함수들 위에 데코레이터를 붙입니다. 이 데코레이터는 W&B Weave에 해당 함수들이 우리가 추적하려는 개별 작업임을 알리며, 이는 이후 에이전트의 동작을 관찰하고 디버깅하는 데 매우 유용합니다.
@weave.op() # Add weave decorator
def get_real_weather(city: str, tool_context: ToolContext) -> dict:
"""Retrieves the current real weather report for a specified city using OpenWeatherMap."""
logging.info(f"Attempting to get real weather for city: '{city}'")

owm_api_key = os.environ.get("OWM_API_KEY")

try:
# Initialize PyOWM
owm = pyowm.OWM(owm_api_key)
mgr = owm.weather_manager()

# Get Weather Observation
logging.info(f"Querying OpenWeatherMap API for city: {city}")
observation = mgr.weather_at_place(city)
w = observation.weather
if w is None:
# This case might be rare if NotFoundError is caught, but good to check
logging.error(f"OWM observation for '{city}' did not contain weather data.")
raise ValueError(f"Weather data not available in OWM response for {city}")
# Extract Temperature (Kelvin)
temp_data = w.temperature("kelvin")
if not temp_data or "temp" not in temp_data:
logging.error(f"Could not find 'temp' in OWM temperature data: {temp_data}")
raise ValueError("Temperature data missing 'temp' key in OWM response.")
temp_k = temp_data["temp"] # Direct access after check
logging.debug(f"Temperature (Kelvin): {temp_k}")

# Read unit preference from state
preferred_unit = tool_context.state.get(
"user_preference_temperature_unit", "Celsius"
)
# Convert temperature
if preferred_unit == "Fahrenheit":
temp_c = temp_k - 273.15
temp_value = (temp_c * 9 / 5) + 32
temp_unit = "°F"
else: # Default to Celsius
temp_value = temp_k - 273.15
temp_unit = "°C"
logging.debug(f"Converted temperature: {temp_value:.1f}{temp_unit}")

# Extract other details
status = w.detailed_status
humidity = w.humidity
wind_speed = w.wind().get("speed", "N/A")
# Format Report
report = (
f"The current weather in {city.capitalize()} is '{status}' "
f"with a temperature of {temp_value:.1f}{temp_unit}. "
f"Humidity is {humidity}%, and wind speed is {wind_speed} m/s."
)

result = {"status": "success", "report": report}
# Update last checked city in state
tool_context.state["last_city_checked_stateful"] = city.capitalize()
logging.info(f"Updated state 'last_city_checked_stateful': {city.capitalize()}")
return result

뉴스 도구도 정의합니다
@weave.op()
def get_latest_news(topic: str) -> dict:
"""Fetches the latest 5-10 news headlines for a given topic using NewsAPI."""
logging.info(f"Tool 'get_latest_news' called for topic: {topic}")

news_api_key = os.environ.get("NEWS_API_KEY")
try:
newsapi = NewsApiClient(api_key=news_api_key)

# Fetch news articles related to the topic, sorted by published date (latest first)
# Using 'everything' endpoint for broader search on a topic
articles_data = newsapi.get_everything(
q=topic, language="en", sort_by="publishedAt", page_size=10
) # Get up to 10 articles

if articles_data["status"] != "ok":
raise Exception(
f"NewsAPI returned status: {articles_data.get('code', 'unknown')} - {articles_data.get('message', 'No message')}"
)

articles = articles_data["articles"]

if not articles:
summary = f"I couldn't find any recent news articles about '{topic}'."
logging.warning(f"No news articles found for topic: {topic}")
return {
"status": "success",
"news_summary": summary,
} # Success, but no articles

# Format the top 5-10 headlines
num_headlines = min(len(articles), 10) # Show up to 10
headlines_list = []
for i, article in enumerate(articles[:num_headlines]):
title = article.get("title", "No Title")
source = article.get("source", {}).get("name", "Unknown Source")
headlines_list.append(f"{i+1}. {title} ({source})")

summary = (
f"Here are the latest {num_headlines} headlines I found about '{topic}':\n"
+ "\n".join(headlines_list)
)
result = {"status": "success", "news_summary": summary}
logging.info(f"Fetched {num_headlines} news headlines for topic: {topic}")
return result


안전성 구현: 입력 가드레일 도입 before_model_callback

이제 에이전트가 사용자 입력을 처리하고 그에 따라 외부 API와 상호작용할 수 있게 되었으므로, 안전성과 제어를 보장하는 일이 무엇보다 중요해졌습니다. 우리는 에이전트가 유해한 요청을 처리하거나 도구를 잘못 사용하는 일을 원치 않습니다. Agent Development Kit은 콜백(Callbacks)을 통해 이를 위한 메커니즘을 제공합니다. 콜백은 에이전트의 사고 및 실행 과정 중간중간에 삽입할 수 있는 일종의 체크포인트입니다.
우리가 구현할 첫 번째 방어층은 입력 가드레일 ~을(를) 사용하여 before_model_callback.
무엇은 before_model_callback?
이 콜백은 에이전트가 코어 대형 언어 모델에 요청을 보내기 직전에 서 있는 엄격한 문지기라고 생각하면 됩니다. 이 요청에는 사용자의 최신 메시지만 담기는 것이 아니라, 에이전트의 지침, 대화 기록, 그리고 필요하다면 도구 정보까지 포함됩니다. before_model_callback 다음과 같은 중요한 기회를 제공합니다:
  • 검사: LLM으로 전달될 전체 요청 패키지를 면밀히 검토하세요.
  • 수정: 필요하다면 요청의 일부를 신중하게 수정하세요(예: 컨텍스트 추가, 콘텐츠 필터링).
  • 블록: 사전에 정의된 규칙을 위반하는 요청은 LLM에 전달되지 않도록 완전히 차단하고, 대신 사용자에게 특정 응답을 직접 반환합니다.
왜 이렇게 유용할까요? 정책을 강제 적용할 수 있도록 해줍니다 이전에 LLM을 호출하는 일을 줄여 비용을 절감하고, 원치 않는 콘텐츠 생성을 방지하며, 에이전트가 지정된 범위를 벗어나지 않도록 보장합니다. 일반적인 활용 사례는 다음과 같습니다:
  • 민감한 데이터 필터링 (개인 식별 정보) 또는 사용자 프롬프트의 부적절한 언어
  • 키워드 기반 차단을 구현해 주제와 무관하거나 정책을 위반하는 요청을 막기.
  • LLM이 프롬프트를 보기 직전에 오늘 날짜나 세션 상태의 정보처럼 시의적절한 정보를 프롬프트 컨텍스트에 동적으로 추가하기.
실제로는 어떻게 작동하나요?
현재 실행 컨텍스트에 대한 세부 정보를 입력으로 받는 표준 Python 함수를 정의합니다 (CallbackContext – 에이전트 정보와 세션 상태에 대한 액세스 제공 via callback_context.state) 그리고 LLM에 전달할 실제 요청 페이로드 (LlmRequest). 함수 내부에서 검사 로직을 구현합니다. 결과에 따라:
  • 요청이 적합하다면, 함수는 다음을 반환합니다. None. Agent Development Kit은 이를 감지하고 평소와 동일하게 LLM을 호출합니다.
  • 요청을 차단해야 하는 경우, 함수가 이를 구성하여 반환합니다. LlmResponse 객체입니다. 이 객체에는 대신 사용자에게 전달할 메시지가 포함되어 있습니다. ADK는 이 응답을 가로채어 즉시 반환하며, 해당 턴의 LLM 호출을 사실상 취소합니다.
입력 가드레일 계획:
이를 보여 주기 위해, 다음 단계에 따라 특정 가드레일을 구현해 보겠습니다(코드 상세 내용은 전체 예시에 포함되어 있습니다).
  1. Python 함수를 정의해 봅시다. 이름은 이렇게 하겠습니다: block_keyword_guardrail, 다음과 같이 설계된 before_model_callback. 이 함수의 단순한 역할은 사용자의 최신 입력에 대소문자를 구분하지 않고 “BLOCK”이라는 단어가 포함되어 있는지 확인하는 것입니다.
  2. 메인 루트 에이전트의 정의를 수정하여 Agent Development Kit에 이것을 실행하도록 지시하세요. block_keyword_guardrail 주요 LLM을 호출하기 전에 실행되는 함수.
  3. ADK 설정 Runner 이 새로 구성한 에이전트를 사용하려면
  4. 일반 요청 몇 건과 “BLOCK” 키워드를 포함한 요청 한 건을 보내 테스트 상호작용을 수행하고, 가드레일이 문제 있는 요청을 올바르게 가로채면서 나머지는 통과시키는지 확인하세요.

레이어 추가하기: 도구 사용 가드레일 설정 before_tool_callback

초기 사용자 입력 검사하기 before_model_callback 은 필수적인 첫 단계입니다. 하지만 때때로 LLM이 겉보기에는 타당해 보이는 요청을 생성하더라도 여전히 바람직하지 않은 도구 동작으로 이어질 수 있습니다. 예를 들어 잘못된 인자를 사용해 도구를 호출하려 하거나, 정책상 특정 매개변수에 따라 도구 사용을 제한하고 싶을 수 있습니다. 바로 이런 상황에서 before_tool_callback 은 또 다른 중요한 제어 레이어를 제공합니다.
정확히 무엇인가요? before_tool_callback?
이 콜백을 도구 사용을 점검하는 전문 검사관이라고 생각하세요. 이는 호출되어 LLM이 요청을 분석한 뒤 “좋아, 인자 Y로 도구 X를 사용해야겠어”라고 결정했지만 직전에 도구 X의 실제 Python 함수(예를 들어 우리의 get_real_weather) 가 실행됩니다. 이 콜백의 핵심 목적은 호출될 도구와 LLM이 그에 대해 생성한 인자를 기준으로, 도구 호출을 최종적으로 검증하고 수정하거나 차단할 수 있는 마지막 기회를 제공하는 것입니다.
이는 여러 가지 중요한 기능을 가능하게 합니다:
  • 인자 검증 및 정제 LLM이 제공한 인자가 타당한지 확인하세요. 유형이 올바른가요? 허용 범위 안에 있나요? 도구로 전달하기 전에 정제가 필요한가요?
  • 리소스·정책 집행 인자에 따라 특정 도구 실행을 차단합니다. 예를 들어, 비용 관리를 위해 특정 매개변수의 API 호출을 막거나, 상태에 저장된 사용자 권한을 기준으로 접근을 제한하거나, 곧 살펴볼 예시처럼 특정 제한된 엔터티(예: 특정 도시)에 대한 쿼리를 방지하는 것입니다.
  • 동적 인자 수정: 다음의 정보를 활용할 수 있습니다 ToolContext (예: 세션 상태)와 같은 정보를 사용해, 도구가 실행되기 직전에 인자를 조정하거나 추가할 수 있습니다.
이건 기술적으로 어떻게 동작하나요?
다음의 세 가지 핵심 정보를 받는 Python 함수를 정의합니다: tool 객체 자체(어떤 도구가 실행될지 알 수 있도록), 그리고 args LLM이 생성한 인자를 담은 딕셔너리와, 그리고 tool_context 세션 상태 및 기타 컨텍스트 세부 정보에 접근하기 위해. 콜백 함수 내부에서:
  • 원한다면 허용하다 실행할 도구(당신이 내부에서 직접 조정한 수정된 인자를 포함해) args 딕셔너리)에서, 함수는 반환해야 합니다 None그러면 ADK가 원래의 도구 함수를 실행합니다.
  • 원한다면 블록 도구 호출을 완전히 차단하고 다른 결과로 대체하려면, 함수는 다음을 반환해야 합니다 사전. ADK는 이 사전을 가로채어 다음과 같이 처리합니다 실제 결과 이번 턴의 도구 호출 결과로 간주되어 원래 도구 함수의 실행을 완전히 건너뜁니다. 반환하는 딕셔너리는 이상적으로 해당 도구의 출력이 기대하는 구조와 일치해야 합니다(예: 도구가 사용하는 형식과 동일한 형식으로 사용자 정의 오류 메시지를 제공).
도구 가드레일에 대한 우리의 계획:
이걸 활용하겠습니다 before_tool_callback 우리의 구체적인 “파리 날씨 확인 금지” 규칙을 구현하려면:
  1. Python 함수를 정의하세요 block_paris_tool_guardrail, 우리의 before_tool_callback. 호출된 도구가 우리의 날씨 도구인지 여부를 구체적으로 확인합니다 (get_real_weather) 그리고 यदि city LLM이 제공한 인자가 “Paris”인지(대소문자 무시) 확인합니다.
  2. 이 특정 상황을 감지하면 콜백이 사용자 정의 오류 딕셔너리를 반환하여 실제 OpenWeatherMap API 호출을 효과적으로 차단합니다.
  3. 루트 에이전트 정의를 마지막으로 한 번 더 업데이트하여 다음을 사용하도록 구성하세요 둘 다before_model_callback (입력 키워드 차단용) 그리고 이 새로운 before_tool_callback (파리 날씨 확인을 차단하기 위한 용도)
  4. ADK 설정 Runner 최종 이중 보호 에이전트 구성과 연관되어 있습니다.
  5. 도구의 가드레일이 제한된 호출은 차단하면서 다른 요청은 허용한다는 점을 보여주기 위해, 허용된 도시들의 날씨를 요청한 뒤 이어서 “파리”의 날씨를 구체적으로 요청하는 테스트 상호작용을 실행하세요.

# Model Guardrail
def block_keyword_guardrail(
callback_context: CallbackContext, llm_request: LlmRequest
) -> Optional[LlmResponse]:
"""Inspects user input for 'BLOCK', blocks if found."""
agent_name = callback_context.agent_name
logging.debug(f"Callback 'block_keyword_guardrail' running for agent: {agent_name}")
last_user_message_text = ""
if llm_request.contents:
for content in reversed(llm_request.contents):
if content.role == "user" and content.parts:
if isinstance(content.parts[0], google_types.Part) and hasattr(
content.parts[0], "text"
):
last_user_message_text = content.parts[0].text or ""
break
keyword_to_block = "BLOCK"
if keyword_to_block in last_user_message_text.upper():
logging.warning(f"Keyword '{keyword_to_block}' found. Blocking LLM call.")
callback_context.state["guardrail_block_keyword_triggered"] = True
logging.info("Set state 'guardrail_block_keyword_triggered': True")
return LlmResponse(
content=google_types.Content(
role="model",
parts=[
google_types.Part(
text="I cannot process this request (blocked keyword)."
)
],
)
)
else:
logging.debug(f"Keyword not found. Allowing LLM call for {agent_name}.")
return None


# Tool Guardrail
def block_paris_tool_guardrail(
tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext
) -> Optional[Dict]:
"""Blocks 'get_real_weather' tool execution for 'Paris'."""
tool_name = tool.name
agent_name = tool_context.agent_name
logging.debug(
f"Callback 'block_paris_tool_guardrail' running for tool '{tool_name}' in agent '{agent_name}'"
)
logging.debug(f"Inspecting tool args: {args}")

# *** UPDATED target_tool_name ***
target_tool_name = "get_real_weather"
blocked_city = "paris"

if tool_name == target_tool_name:
city_argument = args.get("city", "")
if city_argument and city_argument.lower() == blocked_city:
logging.warning(
f"Blocked city '{city_argument}' detected for tool '{tool_name}'. Blocking execution."
)
tool_context.state["guardrail_tool_block_triggered"] = True
logging.info("Set state 'guardrail_tool_block_triggered': True")
return { # Return error dictionary, skipping the actual tool
"status": "error",
"error_message": f"Policy restriction: Weather checks for '{city_argument.capitalize()}' are disabled by a tool guardrail.",
}
else:
logging.debug(f"City '{city_argument}' is allowed for tool '{tool_name}'.")
else:
logging.debug(f"Tool '{tool_name}' not targeted by Paris guardrail. Allowing.")

logging.debug(f"Allowing tool '{tool_name}' to proceed.")
return None # Allow tool execution


에이전트 정의하기

자, 이제 인사와 작별, 실시간 날씨 조회, 뉴스 헤드라인 가져오기 같은 기능을 갖춘 도구 상자는 준비되었습니다. 이제 필요한 것은 이 도구들을 효과적으로 활용할 지능입니다. 이것이 바로 …의 역할입니다. Agent Agent Development Kit에서.
다음을 떠올려 보세요: Agent 인스턴스를 시스템 내 특정 기능 또는 페르소나의 중앙 코디네이터로 삼으세요. 이는 상호작용 흐름을 오케스트레이션하는 구성 요소로서, 사용자의 요청을 이해하고, 추론과 텍스트 생성을 위해 기반 대규모 언어 모델과 소통하며, 특정 기능을 언제 어떻게 활용할지 정확히 결정하는 역할을 담당합니다. Tools 할당되었습니다.
우리가 …을 만들 때 Agent ADK에서 우리는 몇 가지 핵심 매개변수를 통해 해당 요소의 핵심 특성과 능력을 구성합니다:
  • name: 애플리케이션 내에서 이 에이전트를 식별하는 고유 문자열(예: "greeting_agent", "news_fetcher"). 이는 구성 관리, 로깅, 디버깅에 유용합니다.
  • model: 이 매개변수는 에이전트의 “두뇌”, 즉 추론과 응답을 담당하는 LLM을 지정합니다. 일반적으로 여기에는 모델 식별자 문자열을 제공합니다(예: 우리의 상수 MODEL_GEMINI_2_0_FLASH) 또는 사용합니다 LiteLlm 래퍼(예: OpenAI나 Anthropic과 같은 다른 제공업체의 모델을 활용하려는 경우)
  • description: 이는 에이전트의 주요 목적이나 기능을 간략하고 상위 수준에서 요약한 설명입니다. 에이전트 팀을 구성하면서 보게 되겠지만, 이는 description 결정적인 역할을 합니다 위임, 다른 에이전트가 이 에이전트의 전문 분야와 어떤 경우에 작업을 넘겨야 하는지 이해하는 데 사용되기 때문입니다.
  • instruction: 에이전트의 LLM을 위한 상세 운영 매뉴얼로 생각하세요. 여기에는 페르소나, 목표, 대화 스타일, 제약 조건에 대한 구체적인 지침과—무엇보다도—명확한 지시사항을 제공합니다. 언제 그리고 방법 이를 활용해야 합니다 Tools 구성에 나열된 도구입니다. 여기에서의 효과적인 프롬프트 작성은 도구를 신뢰성 있게 사용하고 전체적인 에이전트 동작을 안정화하는 데 매우 중요합니다.
  • tools: 이것은 실제 도구 함수들(예: …)을 담고 있는 단순한 Python 리스트입니다 say_hello, get_real_weather, get_latest_news) that 이 특정 에이전트 사용 권한이 있으며 이를 사용할 준비가 되어 있습니다
각 에이전트의 매개변수를 세심하게 구성하면, 특화된 협업자들로 이루어진 팀을 구성할 수 있습니다. 이제 에이전트들을 정의해 봅시다:
greeting_agent = Agent(
model=MODEL_GEMINI_2_0_FLASH,
name="greeting_agent",
instruction="Greet the user friendly.",
description="Handles simple greetings and hellos.",
tools=[say_hello],
)
farewell_agent = Agent(
model=MODEL_GEMINI_2_0_FLASH,
name="farewell_agent",
instruction="Provide a polite goodbye.",
description="Handles simple farewells and goodbyes.",
tools=[say_goodbye],
)


news_agent = Agent(
model=MODEL_GEMINI_2_0_FLASH, # Can use a different model if desired
name="news_agent",
instruction="You are a News Reporter agent. Your goal is to fetch and present the latest news headlines on a specific topic requested by the user. Use the 'get_latest_news' tool. Clearly state the topic and present the headlines returned by the tool. If the tool returns an error or no news, inform the user politely.",
description="Fetches and presents the latest 5-10 news headlines for a given topic using the 'get_latest_news' tool.",
tools=[get_latest_news],
)


root_agent = Agent(
name="weather_news_assistant",
model=MODEL_GEMINI_2_0_FLASH, # Orchestration model
description="Main assistant: Handles real weather requests, delegates news requests, greetings, and farewells. Includes safety guardrails.",
instruction=(
"You are the main Assistant coordinating a team. Your primary responsibilities are providing real-time weather and delegating other tasks.\n"
"1. **Weather:** If the user asks for weather in a specific city, use the 'get_real_weather' tool yourself. The tool respects temperature unit preferences stored in state.\n"
"2. **News:** If the user asks for news on a specific topic (e.g., 'latest news on AI', 'updates on electric vehicles'), delegate the request to the 'news_agent'.\n"
"3. **Greetings:** If the user offers a simple greeting ('Hi', 'Hello'), delegate to the 'greeting_agent'.\n"
"4. **Farewells:** If the user says goodbye ('Bye', 'Thanks bye'), delegate to the 'farewell_agent'.\n"
"Analyze the user's query and delegate or handle it appropriately. If unsure, ask for clarification. Only use tools or delegate as described."
),
tools=[get_real_weather], # Root agent handles weather directly
sub_agents=[greeting_agent, farewell_agent, news_agent],
output_key="last_assistant_response",
before_model_callback=block_keyword_guardrail,
before_tool_callback=block_paris_tool_guardrail,
)

좋습니다. 이제 에이전트 구성이 완료되었으니 여러 가지 방식으로 상호작용할 수 있습니다. Dev UI, 터미널, 또는 API 서버를 통해 이용할 수 있습니다.
이제 ADK에 기본으로 포함된 웹 UI로 실행하겠습니다. 명령어는 다음과 같습니다:
adk web
웹 UI는 다음과 같습니다:
첫 번째 메시지에서는 에이전트가 인사 에이전트에 작업을 위임하고, 두 번째 메시지에서는 실시간 날씨 정보를 가져오는 방식을 확인해 보세요.
또한 가드레일에 따라 파리의 날씨를 알려 달라는 요청을 에이전트가 거부하는 것을 확인할 수 있습니다. 이는 가드레일이 실제로 적용되어 정상적으로 작동하고 있음을 보여 줍니다.

서로 다른 에이전트들이 어떻게 연결되어 있는지, 입력과 출력에 무엇이 오가는지도 확인할 수 있습니다
또한 에이전트가 실시간 뉴스를 가져오는 방식도 확인해 보세요.

에이전트 모니터링을 위한 Weave

여기에서 구축하는 멀티 에이전트 시스템의 한 가지 과제는 에이전트가 어떻게 의사결정을 내리는지, 그리고 그 결정이 최적의 결과로 이어지는지를 파악하는 것입니다. 에이전트가 LLM과 상호작용하고, 도구를 선택하며, 도구의 출력을 처리하고, 필요하면 다시 LLM으로 돌아가거나 다른 에이전트에 위임하는 과정은 복잡한 일련의 행동으로 이어지기 때문에, 이를 추적하는 일이 까다로울 수 있습니다.
바로 이 지점을 W&B Weave가 해결해 줍니다. 관측 가능성 플랫폼인 Weave는 이러한 복잡한 실행 흐름을 포착하고 시각화하며 분석할 수 있도록 설계되어, 그 과정에서 입력, 출력, LLM 호출, 도구 호출, 데이터 변환을 모두 로깅합니다. 이를 통해 효과적인 디버깅과 성능 평가, 그리고 에이전트가 단계별로 무엇을 하는지에 대한 진정한 이해에 필요한 투명성을 제공합니다.
다음은 우리가 확인한 화면의 스크린샷입니다 Weave 트레이스 스크립트를 실행한 후 대시보드(우리가 어떻게 사용했는지에 주목하세요) weave.op() 위의 도구에서 실행한 결과를 여기에서 확인할 수 있습니다)"
Weave 대시보드에서 서로 다른 에이전트 간의 통신 내역과 사용된 정확한 프롬프트를 확인할 수 있습니다.

메인 테이블에서는 사용된 토큰 수와 그에 따른 비용도 확인할 수 있습니다. 에이전트형 워크플로에서는 주의를 기울이지 않으면 비용이 매우 빠르게 증가할 수 있기 때문에, 이 정보는 상당히 유용합니다.


이 튜토리얼의 전체 Weave 대시보드를 확인할 수 있습니다 여기
💡

세계를 잇다: Agent2Agent로 ADK 에이전트를 연결하기

이 게시물의 튜토리얼 부분에서는 Agent Development Kit을 심도 있게 다루며, 이를 활용해 다중 에이전트로 구성된 Weather Bot 팀을 구축했습니다. 이 시스템은 특화된 하위 에이전트, 외부 도구, 메모리, 안전 기능을 갖춘 강력한 구성입니다. ADK는 다음을 위한 프레임워크를 제공하는 데 뛰어납니다. 구축하다 이 정교한 내부 로직.
하지만 ADK로 만든 우리의 Weather Bot은 다른 에이전트들이 있는 더 넓은 세계와 어떻게 소통할까요? 예를 들어, 누군가가 LangGraph로 만든 여행 계획 에이전트가 우리 봇에게 로마의 현재 날씨를 요청해야 한다면, 혹은 우리 봇이 다음과 같이 만들어진 에이전트에게 복잡한 데이터 분석을 요청해야 한다면 어떻게 될까요? Crew.ai이처럼 근본적으로 서로 다른 시스템들이, 서로의 내부 동작을 알지 못할 수도 있는데, 어떻게 효과적으로 소통할 수 있을까요?
바로 이 격차를 메우도록 설계된 것이 Agent2Agent 프로토콜입니다. Agent Development Kit은 에이전트의 ‘두뇌’와 ‘몸’을 구성하는 도구 상자라고 생각하면 되고, A2A는 에이전트가 다른 에이전트와 대화할 수 있도록 해 주는 보편적 언어와 핸드셰이크 메커니즘을 제공하여, 상대가 어떤 방식으로 만들어졌든 상관없이 소통할 수 있게 합니다. 그들 구축되었습니다. 이 프로토콜은 에이전트 상호운용성을 위한 개방형 표준을 지향합니다.
서로 다른 프레임워크(예: 왼쪽의 ADK, 오른쪽의 다른 프레임워크)로 구축된 에이전트 시스템은 A2A 프로토콜을 사용해 에이전트 간 협업을 위해 상호 통신할 수 있으며, 동시에 자신들의 특정 도구와 API와 상호작용하기 위해 MCP(Model Context Protocol)와 같은 다른 프로토콜을 사용할 수도 있습니다.

A2A와 MCP: 간단한 구분

가끔 A2A가 함께 언급되는 것을 들을 수도 있습니다 MCP(모델 컨텍스트 프로토콜)차이를 알아두면 도움이 됩니다.
  • MCP 일반적으로 에이전트를 연결하는 데 중점을 둡니다 도구, API, 리소스 구조화된 방식으로. ADK 자체는 MCP를 지원하므로, 이를 통해 구축된 에이전트는 다양한 도구를 활용할 수 있습니다.
  • A2A 특히 에이전트 간 협업을 목표로 합니다. 서로 다른 에이전트 시스템이 메모리, 리소스, 특정 도구 구현 같은 내부 세부 정보를 공유하지 않고도, 동적으로—종종 멀티모달 방식으로—소통할 수 있도록 합니다.
우리 날씨 봇으로 살펴보는 A2A 원칙 실천
  • 불투명 실행: 우리 Weather Bot이 A2A를 통해 서비스를 공개한다면, 호출하는 에이전트는 다음만 알면 됩니다 무엇 요청할 스킬(예: get_weather) 및 Agent Card를 통해 예상 입력/출력 형식. 이는 않는다 ADK, PyOWM을 썼는지, 내부 하위 에이전트를 갖고 있는지는 알 필요가 없습니다. A2A가 내부 복잡성을 숨겨 줍니다.
  • 비동기 우선: 우리 get_latest_news 도구가 시간이 걸릴 수 있습니다. A2A의 비동기 설계(폴링, SSE, 푸시 알림)는 호출자를 블로킹하지 않고도 우리 Weather Bot이 더 오래 걸리는 작업을 처리하고 상태 업데이트를 제공할 수 있음을 의미합니다 (TaskStatusUpdateEvent) 필요한 경우 스트리밍으로.
  • 모달리티 무관: 우리가 텍스트를 사용했지만, A2A의 Part 객체는 다른 데이터 유형에 대한 지원을 정의할 수 있도록 합니다 (application/json, image/png등)을 Agent Card를 통해 정의하여 더 풍부한 상호작용을 가능하게 합니다.
  • 엔터프라이즈 준비 완료 & 간결함: HTTP/JSON-RPC와 표준 인증 방식(헤더에 토큰을 전달하고 Agent Card에서 선언하는 방식 등)을 사용하면, 익숙한 기술을 활용하면서도 A2A를 비즈니스 용도로 적합하게 만들 수 있습니다.
A2A(Agent Card)를 통한 우리 날씨 봇 기능 공개
다른 에이전트에게 우리 Weather Bot이 수행할 수 있는 기능을 알리기 위해 Agent Card를 게시합니다. 튜토리얼을 기반으로 하면, 이 JSON 설명에는 다음과 같은 내용이 포함될 수 있습니다:
  • 기본 정보: nameADK Weather & News Bot description, url (A2A 엔드포인트)
  • 인증: 필요한 스킴 지정(예: schemes: ["OAuth2"]).
  • 기능: 예를 들어 streaming: true 뉴스 업데이트를 위한 SSE를 지원하는지 여부.
  • 기능: 사용할 수 있는 항목을 설명하는 배열 하다:
    • 기능 1: id: "get_weather", name: "Get Current Weather", description: "Retrieves real-time weather for a city.", tags: ["weather", "real-time"], examples: ["weather in london"], inputModes: ["text/plain"], outputModes: ["text/plain"]
    • 기능 2: id: "get_news", name: "Get Latest News", description: "Fetches recent news headlines for a topic.", tags: ["news", "headlines"], examples: ["news about AI"], inputModes: ["text/plain"], outputModes: ["text/plain"]
외부 에이전트가 이 카드를 발견하면 어떤 스킬이 제공되는지와 그것들을 어떻게 호출해야 하는지 정확히 알 수 있습니다.
날씨 요청을 위한 A2A 대화 흐름
프로토콜을 사용해 외부 에이전트(A2A 클라이언트)가 우리의 ADK Weather Bot(A2A 서버 역할)으로부터 날씨 정보를 얻는 과정을 단계별로 따라가 보겠습니다.
  1. 요청 (tasks/send) 클라이언트는 HTTP POST를 통해 A2A 서버의 URL을 대상으로 하는 JSON-RPC 요청을 구성합니다. The method"tasks/send". 해당 params 고유한 작업을 포함하십시오 id 그리고 a message 객체 (role: "user") 에는 포함되어 있으며 parts (예: a TextPart 쿼리와 함께)."
  2. 처리 중 우리 A2A 서버 래퍼는 이를 수신하고, 요청과 인증을 검증한 뒤, 세부 정보를 추출합니다.
  3. 내부 ADK 실행 래퍼가 내부 ADK Weather Bot 로직을 트리거하고(쿼리를 전달), ADK 에이전트는 상태, 가드레일, 도구를 포함한 플로우를 실행합니다.
  4. 응답 (Task 객체): ADK 에이전트가 결과를 반환합니다. 우리 A2A 래퍼가 이를 A2A 형식으로 포맷합니다 Task 객체( 원본 id, sessionId, status: "completed", 그리고 an artifacts 날씨 보고서를 포함하는 배열에서 Artifact 와 함께 TextPart). 이는 Task 객체가 JSON-RPC로 다시 전송됩니다 result.
이 구조화된 교환은 핵심 A2A 객체를 사용합니다 (Task, Message, Artifact, Part). A2A는 또한 다음을 위한 메서드를 정의합니다 tasks/get, tasks/cancel, 처리 input-required 멀티턴 대화를 위한 상태, 그리고 tasks/sendSubscribe SSE를 통한 스트리밍을 위해
우리 ADK 에이전트로의 연결과 외부로의 연결
핵심 요지는 A2A가 양방향이면서 프레임워크에 구애받지 않는 기능을 제공한다는 점입니다.
  • 우리 봇을 서버로 사용하기 A2A 서버 레이어로 감싸면, 우리의 ADK Weather Bot은 다음으로부터의 요청을 처리할 수 있습니다 임의의 LangGraph, Crew.AI, Microsoft Autogen 등으로 구축된 A2A 준수 클라이언트
  • 우리 봇을 클라이언트로 사용하기 우리 ADK 에이전트는 호출 다른 에이전트와는 A2A를 통해 상호작용합니다. 항공권 가격을 요청받으면 A2A 클라이언트로서 동작하여, 항공 에이전트의 에이전트 카드(Agent Card)를 검색하고 A2A를 전송할 수 있습니다. tasks/send 요청을 보내고 A2A를 처리합니다 Task 응답 이는 우리 ADK 에이전트와 서로 다른 기술로 구축된 에이전트 간의 직접 협업을 보여줍니다.
공식 A2A 샘플 살펴보기 A2A는 개방형 표준으로 설계되었으며, 커뮤니티 피드백에 의해 지속적으로 발전하고 있습니다. ADK, LangGraph, Crew.AI 같은 프레임워크와의 통합을 포함해 A2A 상호작용을 보여 주는 구체적인 코드 예시와 전체 사양을 확인하려면, 가장 신뢰할 수 있는 자료는 공식 A2A GitHub 저장소 및 해당 문서

결론

자, 이제 AI 세계가 점점 더 다양한 지능형 에이전트들이 효과적으로 협업하도록 만드는 데 초점을 맞추고 있다는 점을 확인했습니다. 여기에는 두 가지 핵심 과제가 따릅니다. 어떻게 하면 실제로 유능한 에이전트를 만들 수 있을까? 그리고 서로 다른 도구나 플랫폼으로 만들어진 에이전트들이 어떻게 서로 대화하게 할 수 있을까? 이 글에서는 두 가지를 모두 다뤘습니다. 먼저 Google의 Agent Development Kit(ADK)를 멀티 에이전트 시스템 구축을 위한 강력한 오픈 소스 프레임워크로 살펴보면서, 위임, 메모리를 위한 세션 상태, 콜백을 활용한 안전 가드레일 같은 실용적인 기능을 갖춘 Weather Bot 팀을 만드는 과정을 단계별로 안내했습니다. 또한 Agent2Agent(A2A) 프로토콜이 공통 언어를 제공해 ADK, LangGraph, Crew.AI 등 규격을 준수하는 어떤 프레임워크로 만든 에이전트든 상호 운용할 수 있도록 하는 방식을 깊이 있게 다뤘습니다. 아울러 W&B Weave 같은 도구는 이러한 복잡한 상호작용에서 발생하는 모든 일을 추적하는 데 필수적입니다. 요컨대, ADK가 에이전트를 만드는 데 어떻게 도움이 되는지와 A2A가 그들을 어떻게 연결하는지 이해하면, 여러분만의 협업형 AI 애플리케이션을 만들기 위한 탄탄한 기반을 갖출 수 있을 것입니다.

이 글은 AI로 번역되었습니다. 오역이 의심되는 부분이 있으면 댓글로 알려주세요. 원문 보고서 링크는 다음과 같습니다: 원문 보고서 보기