Agents Course documentation
멀티 에이전트 시스템
멀티 에이전트 시스템
멀티 에이전트 시스템은 특화된 에이전트들이 협력하여 복잡한 작업을 해결할 수 있게 해주며, 모듈성, 확장성, 견고성을 높여줍니다. 단일 에이전트에 의존하는 대신, 각기 다른 능력을 가진 에이전트들이 작업을 분담합니다.
smolagents에서는 다양한 에이전트를 결합해 파이썬 코드 생성, 외부 도구 호출, 웹 검색 등 다양한 작업을 수행할 수 있습니다. 이러한 에이전트들을 조율함으로써 강력한 워크플로우를 만들 수 있습니다.
일반적인 구성 예시:
- 매니저 에이전트: 작업 분배 담당
- 코드 인터프리터 에이전트: 코드 실행 담당
- 웹 검색 에이전트: 정보 검색 담당
아래 다이어그램은 매니저 에이전트가 코드 인터프리터 도구와 웹 검색 에이전트를 조율하는 간단한 멀티 에이전트 아키텍처를 보여줍니다. 웹 검색 에이전트는 DuckDuckGoSearchTool과 VisitWebpageTool 같은 도구를 활용해 정보를 수집합니다.
멀티 에이전트 시스템 실전 예시
멀티 에이전트 시스템은 여러 특화 에이전트가 오케스트레이터 에이전트의 조율 아래 협력하는 구조입니다. 이 방식은 역할이 다른 에이전트들에게 작업을 분산시켜 복잡한 워크플로우를 구현할 수 있게 해줍니다.
예를 들어, 멀티 에이전트 RAG 시스템은 다음을 통합할 수 있습니다:
- 웹 에이전트: 인터넷 브라우징 담당
- 리트리버 에이전트: 지식 베이스 정보 검색 담당
- 이미지 생성 에이전트: 시각 자료 생성 담당
이 모든 에이전트는 오케스트레이터가 작업 분배와 상호작용을 관리합니다.
멀티 에이전트 계층 구조로 복잡한 문제 해결하기
이 노트북을 따라가며 코드를 직접 실행해볼 수 있습니다.
리셉션이 다가오고 있습니다! 여러분의 도움으로 Alfred는 거의 모든 준비를 마쳤습니다.
하지만 문제가 생겼습니다: 배트모빌이 사라졌습니다. Alfred는 대체 차량을 빨리 찾아야 합니다.
다행히 브루스 웨인의 삶을 다룬 바이오픽 영화가 몇 편 제작되었으니, Alfred는 영화 세트장에 남겨진 차량을 찾아 현대식으로 개조할 수 있을지도 모릅니다. 물론 자율주행 기능도 필수겠죠.
하지만 이 차량들은 전 세계 촬영지 어디에나 있을 수 있습니다.
그래서 Alfred는 여러분의 도움이 필요합니다. 이 문제를 해결할 수 있는 에이전트를 만들어볼까요?
👉 전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물선(비행기)으로 이동하는 데 걸리는 시간을 계산하고, 이를 지도에 표시하세요. 이동 시간에 따라 색상을 다르게 하고, 슈퍼카 공장도 같은 방식으로 표시하세요.
함께 만들어봅시다!
이 예시에는 추가 패키지가 필요하니 먼저 설치해 주세요:
pip install 'smolagents[litellm]' plotly geopandas shapely kaleido -q먼저 화물기 이동 시간 계산 도구를 만듭니다.
import math
from typing import Optional, Tuple
from smolagents import tool
@tool
def calculate_cargo_travel_time(
origin_coords: Tuple[float, float],
destination_coords: Tuple[float, float],
cruising_speed_kmh: Optional[float] = 750.0, # 화물기 평균 속도
) -> float:
"""
두 지점(위도, 경도) 간 화물기 이동 시간을 대권거리로 계산합니다.
Args:
origin_coords: 출발지 (위도, 경도) 튜플
destination_coords: 도착지 (위도, 경도) 튜플
cruising_speed_kmh: 순항 속도(km/h, 기본값 750)
Returns:
float: 예상 이동 시간(시간 단위)
Example:
>>> # 시카고(41.8781° N, 87.6298° W) → 시드니(33.8688° S, 151.2093° E)
>>> result = calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093))
"""
def to_radians(degrees: float) -> float:
return degrees * (math.pi / 180)
# 좌표 변환
lat1, lon1 = map(to_radians, origin_coords)
lat2, lon2 = map(to_radians, destination_coords)
# 지구 반지름(km)
EARTH_RADIUS_KM = 6371.0
# haversine 공식으로 대권거리 계산
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (
math.sin(dlat / 2) ** 2
+ math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
)
c = 2 * math.asin(math.sqrt(a))
distance = EARTH_RADIUS_KM * c
# 우회 경로, 관제 등 감안해 10% 추가
actual_distance = distance * 1.1
# 비행 시간 계산(이착륙 1시간 추가)
flight_time = (actual_distance / cruising_speed_kmh) + 1.0
return round(flight_time, 2)
print(calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093)))에이전트 설정하기
모델 제공자는 Together AI를 사용합니다. GoogleSearchTool은 Serper API를 사용하므로, 환경 변수 SERPAPI_API_KEY(provider=“serpapi”) 또는 SERPER_API_KEY(provider=serper)를 설정해야 합니다.
Serp API가 없다면 DuckDuckGoSearchTool을 사용할 수 있지만, 속도 제한에 유의하세요.
import os
from PIL import Image
from smolagents import CodeAgent, GoogleSearchTool, InferenceClientModel, VisitWebpageTool
model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", provider="together")간단한 에이전트를 만들어 기본 리포트를 받아봅시다.
task = """전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물기로 이동하는 시간을 계산해 pandas 데이터프레임으로 반환하세요. 슈퍼카 공장도 같은 방식으로 포함하세요."""agent = CodeAgent(
model=model,
tools=[GoogleSearchTool("serper"), VisitWebpageTool(), calculate_cargo_travel_time],
additional_authorized_imports=["pandas"],
max_steps=20,
)result = agent.run(task)
result
실행 결과 예시:
| | Location | Travel Time to Gotham (hours) |
|--|------------------------------------------------------|------------------------------|
| 0 | Necropolis Cemetery, Glasgow, Scotland, UK | 8.60 |
| 1 | St. George's Hall, Liverpool, England, UK | 8.81 |
| 2 | Two Temple Place, London, England, UK | 9.17 |
| 3 | Wollaton Hall, Nottingham, England, UK | 9.00 |
| 4 | Knebworth House, Knebworth, Hertfordshire, UK | 9.15 |
| 5 | Acton Lane Power Station, Acton Lane, Acton, UK | 9.16 |
| 6 | Queensboro Bridge, New York City, USA | 1.01 |
| 7 | Wall Street, New York City, USA | 1.00 |
| 8 | Mehrangarh Fort, Jodhpur, Rajasthan, India | 18.34 |
| 9 | Turda Gorge, Turda, Romania | 11.89 |
| 10 | Chicago, USA | 2.68 |
| 11 | Hong Kong, China | 19.99 |
| 12 | Cardington Studios, Northamptonshire, UK | 9.10 |
| 13 | Warner Bros. Leavesden Studios, Hertfordshire, UK | 9.13 |
| 14 | Westwood, Los Angeles, CA, USA | 6.79 |
| 15 | Woking, UK (McLaren) | 9.13 |계획 단계(planning step)를 추가하고 프롬프트를 보강하면 더 나은 결과를 얻을 수 있습니다.
계획 단계는 에이전트가 다음 단계를 미리 생각하고 계획할 수 있게 해줍니다.
agent.planning_interval = 4
detailed_report = agent.run(f"""
여러분은 전문 분석가입니다. 여러 웹사이트를 방문해 종합 리포트를 작성합니다.
for 루프를 활용해 여러 쿼리를 한 번에 검색하세요.
각 데이터 포인트마다 소스 URL을 방문해 수치를 확인하세요.
{task}
""")
print(detailed_report)detailed_report
실행 결과 예시:
| | Location | Travel Time (hours) |
|--|--------------------------------------------------|---------------------|
| 0 | Bridge of Sighs, Glasgow Necropolis, Glasgow, UK | 8.6 |
| 1 | Wishart Street, Glasgow, Scotland, UK | 8.6 |이처럼 프롬프트와 계획 기능만으로도 훨씬 간결한 리포트를 얻을 수 있습니다!
모델의 컨텍스트 윈도우가 빠르게 채워집니다. 따라서 상세 검색 결과와 다른 결과를 결합하도록 요청하면 느려지고 토큰·비용이 급증할 수 있습니다.
➡️ 시스템 구조를 개선해야 합니다.
✌️ 두 에이전트로 작업 분할하기
멀티 에이전트 구조는 서로 다른 하위 작업의 메모리를 분리해 두 가지 큰 이점을 제공합니다:
- 각 에이전트가 핵심 작업에 집중해 성능이 향상됩니다.
- 메모리 분리로 각 단계의 입력 토큰 수가 줄어, 지연과 비용이 감소합니다.
웹 검색 전담 에이전트와 이를 관리하는 매니저 에이전트로 팀을 만들어봅시다.
매니저 에이전트는 최종 리포트 작성을 위해 추가 import(plotly, geopandas, shapely)가 필요합니다.
model = InferenceClientModel(
"Qwen/Qwen2.5-Coder-32B-Instruct", provider="together", max_tokens=8096
)
web_agent = CodeAgent(
model=model,
tools=[
GoogleSearchTool(provider="serper"),
VisitWebpageTool(),
calculate_cargo_travel_time,
],
name="web_agent",
description="웹에서 정보를 찾는 역할",
verbosity_level=0,
max_steps=10,
)매니저 에이전트는 더 강력한 모델 DeepSeek-R1을 사용하고, planning_interval도 추가합니다.
from smolagents.utils import encode_image_base64, make_image_url
from smolagents import OpenAIServerModel
def check_reasoning_and_plot(final_answer, agent_memory):
multimodal_model = OpenAIServerModel("gpt-4o", max_tokens=8096)
filepath = "saved_map.png"
assert os.path.exists(filepath), "saved_map.png 파일이 저장되어야 합니다!"
image = Image.open(filepath)
prompt = (
f"다음은 사용자 요청과 에이전트 단계입니다: {agent_memory.get_succinct_steps()}. 아래는 생성된 지도입니다."
"추론 과정과 지도가 요청을 올바르게 해결하는지 확인하세요."
"먼저 이유를 나열한 뒤, 최종 결정(PASS/FAIL)을 작성하세요."
"너무 엄격할 필요는 없습니다. 대부분 해결했다면 PASS입니다."
"지도는 px.scatter_map으로 만들어야 PASS입니다."
)
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": prompt,
},
{
"type": "image_url",
"image_url": {"url": make_image_url(encode_image_base64(image))},
},
],
}
]
output = multimodal_model(messages).content
print("피드백: ", output)
if "FAIL" in output:
raise Exception(output)
return True
manager_agent = CodeAgent(
model=InferenceClientModel("deepseek-ai/DeepSeek-R1", provider="together", max_tokens=8096),
tools=[calculate_cargo_travel_time],
managed_agents=[web_agent],
additional_authorized_imports=[
"geopandas",
"plotly",
"shapely",
"json",
"pandas",
"numpy",
],
planning_interval=5,
verbosity_level=2,
final_answer_checks=[check_reasoning_and_plot],
max_steps=15,
)이 팀의 구조를 시각화해봅시다:
manager_agent.visualize()
아래와 같이 구조와 도구 사용 관계를 한눈에 볼 수 있습니다:
CodeAgent | deepseek-ai/DeepSeek-R1
├── ✅ Authorized imports: ['geopandas', 'plotly', 'shapely', 'json', 'pandas', 'numpy']
├── 🛠️ Tools:
│ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│ ┃ Name ┃ Description ┃ Arguments ┃
│ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ │ calculate_cargo_travel_time │ 두 지점 간 화물기 이동 시간 계산 │ origin_coords (`array`): 출발지 좌표 │
│ │ │ │ destination_coords (`array`): 도착지 │
│ │ │ │ cruising_speed_kmh (`number`): 속도 │
│ │ final_answer │ 문제에 대한 최종 답변 제공 │ answer (`any`): 최종 답변 │
│ └─────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
└── 🤖 Managed agents:
└── web_agent | CodeAgent | Qwen/Qwen2.5-Coder-32B-Instruct
├── ✅ Authorized imports: []
├── 📝 Description: 웹에서 정보를 찾는 역할
└── 🛠️ Tools:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name ┃ Description ┃ Arguments ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ web_search │ 구글 웹 검색 수행 │ query (`string`): 검색어 │
│ │ │ filter_year (`integer`): 연도 제한 │
│ visit_webpage │ 웹페이지 방문 및 내용 읽기 │ url (`string`): 방문할 URL │
│ calculate_cargo_travel_time │ 두 지점 간 화물기 이동 시간 계산 │ origin_coords (`array`): 출발지 │
│ │ │ destination_coords (`array`): 도착지│
│ │ │ cruising_speed_kmh (`number`): 속도 │
│ final_answer │ 문제에 대한 최종 답변 제공 │ answer (`any`): 최종 답변 │
└─────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┘manager_agent.run("""
전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물기로 이동하는 시간을 계산하세요. 슈퍼카 공장도 같은 방식으로 포함해 총 6개 이상 지점을 지도에 산점도로 표시하고, 이동 시간에 따라 색상을 다르게 하세요. 결과 이미지는 saved_map.png로 저장하세요!
지도 예시:
import plotly.express as px
df = px.data.carshare()
fig = px.scatter_map(df, lat="centroid_lat", lon="centroid_lon", text="name", color="peak_hour", size=100,
color_continuous_scale=px.colors.sequential.Magma, size_max=15, zoom=1)
fig.show()
fig.write_image("saved_image.png")
final_answer(fig)
문자열을 코드로 처리하지 마세요. 문자열이 있으면 print로 출력하세요.
""")제 실행에서는 매니저 에이전트가 웹 에이전트에게 1. 배트맨 촬영지 검색, 2. 슈퍼카 공장 찾기로 작업을 분배한 뒤, 결과를 집계해 지도를 그렸습니다.
에이전트 상태에서 지도를 직접 확인해봅시다:
manager_agent.python_executor.state["fig"]아래와 같이 지도가 출력됩니다:

참고 자료
- 멀티 에이전트 시스템 – 멀티 에이전트 시스템 개요
- Agentic RAG란? – Agentic RAG 소개
- Multi-Agent RAG System 🤖🤝🤖 레시피 – 단계별 구축 가이드