멀티 에이전트 시스템
멀티 에이전트 시스템 (Multi-Agent Systems)
멀티 에이전트 시스템을 쓰면 여러 Agent 인스턴스를 더 큰 아키텍처로 조합할 수 있어요. 코디네이터(coordinator) 에이전트가 전문가(specialist) 에이전트에게 일을 위임하는 구조죠. 각 전문가는 각자의 도구와 시스템 프롬프트를 갖고 특정 작업에 집중하고, 코디네이터는 각 작업이 실제로 어떻게 처리되는지는 몰라도 계획을 세우고 작업을 배분합니다.
에이전트를 도구로 만들어 내는(spawn) 것이 유용한 상황은 다음과 같아요.
- 단일 에이전트가 안정적으로 처리하기엔 작업이 너무 넓을 때,
- 서로 다른 능력을 집중된 재사용 가능한 에이전트로 분리하고 싶을 때,
- 더 나은 의사결정과 낮은 토큰 사용을 위해 코디네이터의 컨텍스트를 가볍게 유지하고 싶을 때.
Haystack에서는 AgentTool로 전문가 에이전트를 도구로 만들어 낼 수 있습니다.
출처: 공식문서
에이전트를 도구로 변환하기 (Converting an Agent to a Tool)
AgentTool은 전문가 에이전트를 감싸서 코디네이터가 호출할 수 있게 해줘요. 코디네이터의 모델은 위임할 작업을 단일 사용자 메시지로 보내고, 전문가의 최종 답변을 텍스트로 받습니다. 그래서 에이전트의 인터페이스를 설명하거나 결과 dict를 풀어낼 필요가 전혀 없어요.
이 페이지의 예제들은 serperdev-haystack 패키지로 옮겨진 SerperDev 웹 검색 컴포넌트를 사용합니다. 예제를 실행하려면 설치해야 해요.
pip install serperdev-haystack
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.components.generators.utils import print_streaming_chunk
from haystack.dataclasses import ChatMessage
from haystack.tools import AgentTool, ComponentTool
from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch
from haystack.utils import Secret
research_agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[
ComponentTool(
component=SerperDevWebSearch(
api_key=Secret.from_env_var("SERPERDEV_API_KEY"),
top_k=3,
),
name="web_search",
description="Search the web for current information on any topic",
),
],
system_prompt="You are a research specialist. Search the web to find information.",
)
research_specialist = AgentTool(
agent=research_agent,
name="research_specialist",
description="A specialist that researches topics on the web",
)
coordinator = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[research_specialist],
system_prompt="You are a coordinator. Delegate research tasks to the research specialist.",
streaming_callback=print_streaming_chunk,
)
result = coordinator.run(
messages=[
ChatMessage.from_user("What are the latest developments in Haystack AI?"),
],
)
전문가의 전체 설정은 직렬화할 때 인라인으로 캡처됩니다. 코디네이터를 파이프라인으로 감싼 뒤 pipeline.dumps()를 호출하면 YAML을 얻을 수 있고, 그 YAML은 Pipeline.loads()로 다시 불러올 수 있어요.
AgentTool의 대안 (Alternatives to AgentTool)
Agent는 Haystack 컴포넌트이므로 ComponentTool로 감쌀 수도 있어요. 다만 이렇게 하면 코디네이터에게 tools, generation_kwargs, hook_context 같은 에이전트의 전체 컴포넌트 인터페이스가 노출되고, 코디네이터는 완전한 결과 dict를 받게 됩니다. 반면 AgentTool은 기본적으로 위임할 작업을 담는 단일 입력 messages 하나를 노출하고, 전문가의 필수 프롬프트 변수 하나마다 파라미터 하나를 추가로 노출합니다. 그리고 전문가 최종 답변의 텍스트만 반환하죠.
@tool 데코레이터로도 비슷하게 좁은 인터페이스를 만들 수 있어요. 커스텀 입력 인자가 필요하거나, 위임할 작업을 변환하거나, 전문가의 응답을 후처리해야 할 때 유용합니다. 단, 직렬화 측면에서 트레이드오프가 있어요. 데코레이터가 붙은 도구는 함수의 import 경로로 직렬화되므로, 전문가의 설정이 위의 YAML이 아니라 여러분의 Python 모듈에 남게 됩니다.
코디네이터 / 전문가 패턴 (Coordinator / Specialist Pattern)
코디네이터/전문가 패턴은 책임을 깔끔하게 나눠요. 코디네이터는 계획과 위임을 맡고, 각 전문가는 집중된 도구 세트와 타깃팅된 시스템 프롬프트를 소유하죠.
이것은 또한 **컨텍스트 엔지니어링(context engineering)**의 한 형태예요. 각 에이전트가 무엇을 보는지를 의도적으로 통제하는 것이죠. 전문가는 자신만의 도구 호출 흔적(trace)을 쌓지만, 코디네이터는 최종 답변만 필요합니다. AgentTool은 전문가의 최종 답변만 표면화해서 코디네이터의 컨텍스트를 가볍게 유지해 줘요.
여러 주제를 다룰 때, 코디네이터는 하나의 응답 안에서 같은 전문가 도구를 여러 번 호출할 수 있습니다. 하나의 LLM 응답에서 나온 모든 도구 호출은 스레드 풀을 사용해 동시에 실행되죠. 병렬 처리 수준은 tool_concurrency_limit init 파라미터로 제어합니다(기본값: 4).
아래 예제는 코디네이터에게 두 주제를 물어봅니다. 그러면 research_specialist를 두 번 호출하고, 두 전문가 모두 병렬로 실행되죠.
HTMLToDocument는 Trafilatura를 사용해 HTML 페이지에서 깨끗한 텍스트를 추출합니다. 실행 전에 설치하세요.
pip install trafilatura
from typing import Annotated
from haystack.components.agents import Agent
from haystack.components.converters import HTMLToDocument
from haystack.components.fetchers.link_content import LinkContentFetcher
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.components.generators.utils import print_streaming_chunk
from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch
from haystack.dataclasses import ChatMessage
from haystack.tools import AgentTool, ComponentTool, tool
from haystack.utils import Secret
search_tool = ComponentTool(
component=SerperDevWebSearch(
api_key=Secret.from_env_var("SERPERDEV_API_KEY"),
top_k=3,
),
name="web_search",
description="Search the web for current information on any topic",
)
@tool
def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str:
"""Fetch the content of a web page given its URL."""
try:
streams = LinkContentFetcher().run(urls=[url])["streams"]
if not streams:
return "No content found."
documents = HTMLToDocument().run(sources=streams)["documents"]
return documents[0].content if documents else "No content extracted."
except Exception as e:
return f"Failed to fetch page: {e}"
research_agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[search_tool, fetch_page],
system_prompt=(
"You are a research specialist. Search the web to find relevant pages, "
"then fetch their full content for detailed information. "
"Return a concise summary of your findings in 3-5 sentences."
),
)
research_specialist = AgentTool(
agent=research_agent,
name="research_specialist",
description="Research a topic on the web and report a summary of the findings",
)
coordinator = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[research_specialist],
system_prompt=(
"You are a coordinator. Delegate research tasks to the research specialist. "
"For questions covering multiple topics, research each one independently. "
"Keep your final answer concise."
),
streaming_callback=print_streaming_chunk,
tool_concurrency_limit=4, # run up to 4 specialist calls in parallel
)
result = coordinator.run(
messages=[
ChatMessage.from_user(
"What are the latest developments in the Haystack framework, "
"and what is the current state of the Model Context Protocol?",
),
],
)
더 알아보기 (Learn more)
- 멀티 에이전트 시스템 (Multi-Agent Systems) — 원문 문서.
- Agent — 에이전트 컴포넌트 자체에 대한 문서.
AgentTool— 에이전트를 도구로 감싸는 방법.- State — 에이전트와 도구 간 데이터 공유.
- 📚 튜토리얼: Creating a Multi-Agent System