딥 리서치 에이전트 만들기
딥 리서치 에이전트 만들기 (Build a deep research agent)
연구 질문을 여러 단계로 쪼개고, 전문화된 sub-agent에게 위임한 뒤, 결과를 종합해 보고서로 만드는 웹 리서치 에이전트를 Deep Agents로 직접 만들어 볼게요. 팀 단위 병렬 리서치에서 각 컨텍스트를 격리하고 인용까지 잡아내는 패턴을 자연스럽게 익힐 수 있어요.
출처: 공식문서
개요 (Overview)
이 가이드에서 만들 에이전트는 이렇게 동작해요.
- opt-in todo list 미들웨어로 연구 계획 수립
- 컨텍스트를 격리한 sub-agents에게 연구 작업 위임
- 정보를 모으면서 검색 결과를 평가하고 다음 단계 계획
- 인용을 포함해 종합한 최종 보고서 작성
위임된 sub-agents는 Tavily로 웹 검색을 하고, 분석을 위해 전체 웹페이지 내용을 가져와요.
핵심 개념: Subagents(병렬·컨텍스트 격리 리서치), 커스텀 tools(웹 검색), opt-in planning tool(다단계 계획).
사전 준비 (Prerequisites)
설정 (Setup)
mkdir deep-research-agent
cd deep-research-agent
의존성 설치 — Claude:
pip install deepagents tavily-python httpx markdownify langchain-anthropic langchain-core
Gemini:
pip install deepagents tavily-python httpx markdownify langchain-google-genai langchain-core
(uv를 쓸 땐 uv init → uv add ... → uv sync.)
API 키 세팅 — Claude: ANTHROPIC_API_KEY, TAVILY_API_KEY, (선택) LANGSMITH_API_KEY. Gemini: GOOGLE_API_KEY, TAVILY_API_KEY, (선택) LANGSMITH_API_KEY.
에이전트 만들기
프로젝트 디렉토리에 agent.py를 만들고 단계별로 채워볼게요.
1단계. 도구 추가 — tavily_search 도구는 Tavily로 URL을 발견한 뒤 전체 웹페이지 내용을 가져와 markdown으로 반환해요. 요약이 아니라 원문 전체를 분석할 수 있게 하는 거죠.
import os
from typing import Annotated, Literal
import httpx
from langchain.tools import InjectedToolArg, tool
from markdownify import markdownify
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
def fetch_webpage_content(url: str, timeout: float = 10.0) -> str:
"""Fetch webpage and convert HTML to markdown."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
try:
response = httpx.get(url, headers=headers, timeout=timeout)
response.raise_for_status()
return markdownify(response.text)
except Exception as e:
return f"Error fetching {url}: {e!s}"
@tool(parse_docstring=True)
def tavily_search(
query: str,
max_results: Annotated[int, InjectedToolArg] = 1,
topic: Annotated[
Literal["general", "news", "finance"], InjectedToolArg
] = "general",
) -> str:
"""Search the web for information on a given query.
Uses Tavily to discover relevant URLs, then fetches and returns full webpage content as markdown.
Args:
query: Search query to execute
max_results: Maximum number of results to return (default: 1)
topic: Topic filter - 'general', 'news', or 'finance' (default: 'general')
Returns:
Formatted search results with full webpage content
"""
search_results = tavily_client.search(
query,
max_results=max_results,
topic=topic,
)
result_texts = []
for result in search_results.get("results", []):
url = result["url"]
title = result["title"]
content = fetch_webpage_content(url)
result_texts.append(f"## {title}\n**URL:** {url}\n\n{content}\n---")
return f"Found {len(result_texts)} result(s) for '{query}':\n\n" + "\n".join(
result_texts
)
2단계. 프롬프트 추가 — 오케스트레이터 워크플로우와 sub-agent 프롬프트 템플릿을 agent.py에 넣어요. RESEARCH_WORKFLOW_INSTRUCTIONS는 계획→저장→위임→종합→보고→검증 워크플로우를, RESEARCHER_INSTRUCTIONS는 실제 리서처의 검색 예산과 중단 규칙을, SUBAGENT_DELEGATION_INSTRUCTIONS는 sub-agent 위임 전략(단일 우선, 명확한 비교에만 병렬화)을 담아요. (프롬프트 원문은 아주 길어서 여기서는 행동 원칙 위주로 요약했지만, 아래 agent.py 코드에서 RESEARCH_WORKFLOW_INSTRUCTIONS·RESEARCHER_INSTRUCTIONS·SUBAGENT_DELEGATION_INSTRUCTIONS 상수로 그대로 사용됩니다. 원문을 그대로 붙여 쓰시면 돼요.)
3단계. 작업 계획 활성화 — Task planning은 opt-in이에요. 연구 워크플로우는 write_todos로 질문을 작은 작업으로 쪼개므로 에이전트를 만들 때 TodoListMiddleware를 넘겨요.
from langchain.agents.middleware import TodoListMiddleware
4단계. 에이전트 생성 — 모델 초기화와 에이전트 생성을 agent.py에 추가해요. 공급자를 골라서 쓰면 됩니다. Claude 버전:
from datetime import datetime
from deepagents import create_deep_agent
from langchain.agents.middleware import TodoListMiddleware
from langchain.chat_models import init_chat_model
max_concurrent_research_units = 3
max_researcher_iterations = 3
current_date = datetime.now().strftime("%Y-%m-%d")
INSTRUCTIONS = (
RESEARCH_WORKFLOW_INSTRUCTIONS
+ "\n\n"
+ "=" * 80
+ "\n\n"
+ SUBAGENT_DELEGATION_INSTRUCTIONS.format(
max_concurrent_research_units=max_concurrent_research_units,
max_researcher_iterations=max_researcher_iterations,
)
)
research_sub_agent = {
"name": "research-agent",
"description": "Delegate research to the sub-agent. Give one topic at a time.",
"system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date),
"tools": [tavily_search],
}
model = init_chat_model(model="anthropic:claude-sonnet-4-5-20250929", temperature=0.0)
agent = create_deep_agent(
model=model,
tools=[tavily_search],
system_prompt=INSTRUCTIONS,
subagents=[research_sub_agent],
middleware=[TodoListMiddleware()],
)
Gemini 버전은 from langchain_google_genai import ChatGoogleGenerativeAI를 쓰고 model = ChatGoogleGenerativeAI(model="gemini-3-pro-preview", temperature=0.0)로 지정하면 돼요.
에이전트 실행하기
에이전트는 동기적으로(전체 결과를 기다렸다 출력) 또는 스트리밍으로(업데이트가 들어오는 대로) 실행할 수 있어요. agent.py 하단에 해당 코드를 추가하세요.
동기 실행:
from langchain.messages import HumanMessage
if __name__ == "__main__":
result = agent.invoke(
{
"messages": [
HumanMessage(
content="What are the main differences between RAG and fine-tuning for LLM applications?"
)
]
}
)
for msg in result.get("messages", []):
if hasattr(msg, "content") and msg.content:
print(msg.content)
스트리밍:
from langchain.messages import HumanMessage
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
HumanMessage(content="Compare Python vs JavaScript for web development")
]
},
version="v3",
)
for message in stream.messages:
for token in message.text:
print(token, end="", flush=True)
프로젝트 루트에서 실행:
python agent.py
실행 전에 LANGSMITH_API_KEY를 설정해 두면 LangSmith에서 multi-step trace를 보고 디버깅·모니터링할 수 있어요.
전체 코드: Deep Research 예제를 GitHub에서 봐 주세요.
다음 단계 (Next steps)
에이전트 파일의 프롬프트 상수를 바꿔 워크플로우·위임 전략·리서처 동작을 커스터마이즈할 수 있고, 위임 제한을 조정해 더 많은 병렬 sub-agent나 위임 라운드를 허용할 수도 있어요.
더 알아보기 (Learn more)
- Subagents — 도구·프롬프트가 다른 subagent 설정법
- Customization — 모델·도구·시스템 프롬프트·task planning 커스터마이즈
- LangSmith — 리서치 실행 trace로 다단계 행동 디버깅
- Deep Research Course — LangGraph로 deep research 전체 강의