도구 호출 에이전트 구축
도구 호출 에이전트 구축 (Tool-Calling Agent)
질문에 답하거나 작업을 수행하기 위해 도구(tool) 를 사용하는 에이전트를 만들어 볼게요. Haystack의 Agent 컴포넌트는 LLM이 사용자 질문을 이해하고, 어떤 도구를 쓸지 스스로 결정해서 실행하도록 만들어요.
출처: 공식문서
개요
여기서는 두 가지 접근을 살펴볼게요.
Agent+ 단순 웹 검색 도구: 컴포넌트 하나를 도구로 써요.Agent+ 여러 컴포넌트의 복잡한 파이프라인: 파이프라인 전체를 도구로 써요.
Agent는 LLM을 사용해 사용자 질문을 이해하고 답에 필요한 도구를 골라 호출해요. 컴포넌트나 파이프라인을 도구로 만들려면 ComponentTool, PipelineTool 같은 래퍼를 사용해요.
환경 준비
pip install haystack-ai serperdev-haystack docstring-parser trafilatura
API 키를 입력받아 환경 변수로 저장해요.
from getpass import getpass
import os
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
if "SERPERDEV_API_KEY" not in os.environ:
os.environ["SERPERDEV_API_KEY"] = getpass("Enter SerperDev API key: ")
컴포넌트를 도구로 쓰는 에이전트
먼저 웹 검색 도구를 만든 뒤, 그 도구 하나로 에이전트를 만들어요. SerperDevWebSearch 컴포넌트를 ComponentTool로 감싸면 에이전트가 호출 가능한 도구가 돼요.
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch
from haystack.dataclasses import ChatMessage
from haystack.tools.component_tool import ComponentTool
# 웹 검색 도구를 SerperDevWebSearch로 생성
web_tool = ComponentTool(component=SerperDevWebSearch(), name="web_tool")
# 웹 검색 도구로 에이전트 생성
agent = Agent(chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=[web_tool])
# 에이전트에 질문 실행
result = agent.run(messages=[ChatMessage.from_user("Find information about Haystack AI framework")])
# 최종 응답 출력
print(result["messages"][-1].text)
Agent는 동작을 조정할 수 있는 선택적 파라미터를 여러 개 제공해요.
system_prompt: 에이전트 LLM에 지시를 내리는 시스템 프롬프트를 정의해요.exit_conditions: 에이전트가 멈출 조건이에요."text"면 LLM이 텍스트 응답만 하면 바로 종료하고, 특정 도구 이름을 넣으면 그 도구가 호출된 직후 종료해요.state_schema: 에이전트 호출 중 공유되는 상태를 정의해요.streaming_callback: LLM의 토큰을 바로 출력으로 스트리밍해요.max_agent_steps: 도구 호출 횟수를 제한해 무한 루프를 막아요.
from haystack.components.generators.utils import print_streaming_chunk
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=[web_tool], streaming_callback=print_streaming_chunk
)
result = agent.run(messages=[ChatMessage.from_user("Find information about Haystack AI framework")])
Agent는 도구를 지원하는 여러 ChatGenerator와 함께 쓸 수 있어요. AnthropicChatGenerator, CohereChatGenerator, GoogleAIGeminiChatGenerator, OllamaChatGenerator 등이 그 예시예요. 예를 들어 Hugging Face API를 쓰려면 OpenAIChatGenerator 자리를 HuggingFaceAPIChatGenerator로 바꾸면 돼요.
파이프라인을 도구로 쓰는 에이전트
좀 더 정교한 예로, 웹을 검색하고 링크의 콘텐츠를 가져와 종합적인 답을 만드는 리서치 어시스턴트를 만들어요. 먼저 에이전트가 도구로 쓸 파이프라인을 구성해요. 검색 → 콘텐츠 페치 → HTML을 문서로 변환 → 결과 포맷 순서예요.
from haystack.components.converters.html import HTMLToDocument
from haystack.components.converters.output_adapter import OutputAdapter
from haystack.components.fetchers.link_content import LinkContentFetcher
from haystack_integrations.components.websearch.serperdev import SerperDevWebSearch
from haystack.dataclasses import ChatMessage
from haystack.core.pipeline import Pipeline
search_pipeline = Pipeline()
search_pipeline.add_component("search", SerperDevWebSearch(top_k=10))
search_pipeline.add_component("fetcher", LinkContentFetcher(timeout=3, raise_on_failure=False, retry_attempts=2))
search_pipeline.add_component("converter", HTMLToDocument())
search_pipeline.add_component(
"output_adapter",
OutputAdapter(
template="""
{%- for doc in docs -%}
{%- if doc.content -%}
<search-result url="{{ doc.meta.url }}">
{{ doc.content|truncate(25000) }}
</search-result>
{%- endif -%}
{%- endfor -%}
""",
output_type=str,
),
)
search_pipeline.connect("search.links", "fetcher.urls")
search_pipeline.connect("fetcher.streams", "converter.sources")
search_pipeline.connect("converter.documents", "output_adapter.docs")
파이프라인에서 도구 만들기
search_pipeline을 PipelineTool로 감싸면 파이프라인 전체가 LLM이 호출 가능한 도구가 돼요. input_mapping과 output_mapping으로 어떤 입력·출력을 노출할지 정해요. 여기서는 query만 도구 스키마에 드러나고, output_adapter가 만든 포맷 문자열을 결과로 추출해요.
from haystack.tools import PipelineTool
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
search_tool = PipelineTool(
name="search",
description="Use this tool to search for information on the internet.",
pipeline=search_pipeline,
input_mapping={"query": ["search.query"]},
output_mapping={"output_adapter.output": "search_result"},
outputs_to_string={"source": "search_result"},
)
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=[search_tool],
system_prompt="""
You are a deep research assistant.
You create comprehensive research reports to answer the user's questions.
You use the 'search'-tool to answer any questions.
You perform multiple searches until you have the information you need to answer the question.
Make sure you research different aspects of the question.
Use markdown to format your response.
When you use information from the websearch results, cite your sources using markdown links.
It is important that you cite accurately.
""",
exit_conditions=["text"],
max_agent_steps=20,
)
에이전트를 실행하기 전에 agent.warm_up()을 호출하는 게 좋아요. 필요 시 모델을 미리 로드해 두는 역할이에요.
query = "What are the latest updates on the Artemis moon mission?"
messages = [ChatMessage.from_user(query)]
agent_output = agent.run(messages=messages)
print(agent_output["messages"][-1].text)
요약하면, Agent 는 LLM과 도구 사이의 상호작용을 조율하는 핵심 컴포넌트예요. 단일 Haystack 컴포넌트를 도구로 쓰려면 ComponentTool, 파이프라인 전체를 바로 도구로 쓰려면 PipelineTool 을 써요. 재미있는 점은, Agent 자체도 Haystack 컴포넌트라서 다른 컴포넌트처럼 파이프라인에 넣어 조합할 수 있다는 거예요.