Projeto Intermediário: Sistema de Pesquisa Multi-Agente
Construa um sistema de pesquisa multi-agente pronto para produção com um agente supervisor coordenando agentes de pesquisa especializados para geração de relatórios abrangentes.
Construa um sistema de pesquisa multi-agente que coordena agentes especializados para pesquisar temas, analisar descobertas e gerar relatórios abrangentes. Este projeto reúne estado avançado, persistência, padrões multi-agente e streaming.
Arquitetura do Sistema
User Query
↓
Supervisor Agent ──→ Planner Agent
↓ ↓
Researcher Agent ←── Research Plan
↓
Analyst Agent ──→ Synthesizes findings
↓
Writer Agent ──→ Generates final report
↓
Reviewer Agent ──→ Quality check
↓
Final Report
Passo 1: Definir Estado e Mensagens
from langgraph.graph import StateGraph, START, END, add_messages
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt, Command
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from typing_extensions import TypedDict, Annotated
from typing import List, Any, Optional
from operator import add
from datetime import datetime
import json
# Structured agent message
class AgentMessage(TypedDict):
sender: str
recipient: str
content: str
message_type: str # plan, research, analysis, draft, review, final
timestamp: str
class ResearchState(TypedDict):
query: str # Original user query
messages: Annotated[List[AgentMessage], add] # Agent conversation log
research_plan: str # Structured research plan
research_findings: List[str] # Raw research data
analysis: str # Synthesized analysis
draft_report: str # Written report
review_feedback: str # Quality review
final_report: str # Approved final report
next_agent: str # Supervisor's decision
errors: Annotated[List[str], add] # Error log
status: str # Overall statusPasso 2: Inicializar LLMs
# Different models for different agents
planner_llm = ChatOpenAI(model="gpt-4o", temperature=0.3)
researcher_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.5)
analyst_llm = ChatOpenAI(model="gpt-4o", temperature=0.2)
writer_llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
reviewer_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
supervisor_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)Modelos diferentes otimizam o custo. Tarefas simples (pesquisa, revisão) usam gpt-4o-mini. Raciocínio complexo (planejamento, análise) usa gpt-4o.
Passo 3: Definir Agentes
Agente Supervisor
def supervisor_agent(state: ResearchState) -> dict:
context = "\n".join(
f"{m['sender']} → {m['recipient']}: {m['content'][:200]}"
for m in state["messages"][-5:]
) if state["messages"] else "No activity yet."
prompt = f"""Research Query: {state['query']}
Current Status: {state.get('status', 'starting')}
Recent Activity:
{context}
Available agents and their order:
1. planner — Creates a research plan
2. researcher — Executes the research
3. analyst — Analyzes research findings
4. writer — Writes the report draft
5. reviewer — Reviews the draft for quality
6. complete — Task is finished
Which agent should work next? Respond with one word."""
response = supervisor_llm.invoke(prompt)
next_agent = response.content.strip().lower()
if next_agent not in ["planner", "researcher", "analyst", "writer", "reviewer"]:
next_agent = "complete"
return {"next_agent": next_agent,
"messages": [{"sender": "supervisor",
"recipient": next_agent,
"content": f"Proceed to {next_agent}",
"message_type": "instruction",
"timestamp": datetime.now().isoformat()}]}Agente Planejador
def planner_agent(state: ResearchState) -> dict:
prompt = f"""Create a detailed research plan for: {state['query']}
The plan should include:
1. Key topics to investigate
2. Search queries to execute
3. Data sources to consult
4. Analysis methodology
Format as a structured plan with numbered sections."""
response = planner_llm.invoke(prompt)
plan = response.content
return {"research_plan": plan,
"status": "planned",
"messages": [{"sender": "planner",
"recipient": "supervisor",
"content": plan,
"message_type": "plan",
"timestamp": datetime.now().isoformat()}]}Agente Pesquisador
def researcher_agent(state: ResearchState) -> dict:
plan = state.get("research_plan", state["query"])
prompt = f"""Research Plan: {plan}
Conduct thorough research on each topic. For each area:
1. Find key facts and statistics
2. Note different perspectives
3. Identify authoritative sources
4. Highlight recent developments
Provide detailed findings for each topic."""
response = researcher_llm.invoke(prompt)
return {"research_findings": state.get("research_findings", []) + [response.content],
"status": "researched",
"messages": [{"sender": "researcher",
"recipient": "supervisor",
"content": response.content[:500],
"message_type": "research",
"timestamp": datetime.now().isoformat()}]}Agente Analista
def analyst_agent(state: ResearchState) -> dict:
findings = "\n\n".join(state.get("research_findings", ["No findings"]))
prompt = f"""Research Findings:
{findings}
Analyze these findings and provide:
1. Key insights and patterns
2. Contradictions or debates
3. Gaps in the research
4. Implications and conclusions
5. Recommendations for the report
Be critical and thorough."""
response = analyst_llm.invoke(prompt)
return {"analysis": response.content,
"status": "analyzed",
"messages": [{"sender": "analyst",
"recipient": "supervisor",
"content": response.content[:500],
"message_type": "analysis",
"timestamp": datetime.now().isoformat()}]}Agente Escritor
def writer_agent(state: ResearchState) -> dict:
report_prompt = f"""Write a comprehensive research report.
Query: {state['query']}
Analysis: {state['analysis']}
Research Findings: {json.dumps(state.get('research_findings', []))}
The report should include:
1. Executive Summary
2. Introduction
3. Methodology
4. Key Findings (with data and evidence)
5. Analysis and Discussion
6. Conclusions
7. Recommendations
8. References
Write in a professional, academic style. Use markdown formatting."""
response = writer_llm.invoke(report_prompt)
return {"draft_report": response.content,
"status": "drafted",
"messages": [{"sender": "writer",
"recipient": "supervisor",
"content": "Draft report completed",
"message_type": "draft",
"timestamp": datetime.now().isoformat()}]}Agente Revisor
def reviewer_agent(state: ResearchState) -> dict:
review_prompt = f"""Review this research report for quality:
Report Draft:
{state['draft_report']}
Original Query:
{state['query']}
Check for:
1. Accuracy of claims and data
2. Completeness — does it answer the query?
3. Clarity and organization
4. Grammar and style
5. Missing sections or information
6. Bias or unsupported statements
Provide specific, actionable feedback."""
response = reviewer_llm.invoke(review_prompt)
return {"review_feedback": response.content,
"status": "reviewed",
"messages": [{"sender": "reviewer",
"recipient": "supervisor",
"content": response.content[:500],
"message_type": "review",
"timestamp": datetime.now().isoformat()}]}Agente Finalizador
def finalizer_agent(state: ResearchState) -> dict:
final_prompt = f"""Based on the draft report and review feedback, produce the final report.
Draft: {state['draft_report']}
Review Feedback: {state['review_feedback']}
Incorporate the feedback and polish the report. Ensure it is comprehensive,
well-structured, and directly addresses the query: {state['query']}"""
response = writer_llm.invoke(final_prompt)
return {"final_report": response.content,
"status": "completed",
"messages": [{"sender": "finalizer",
"recipient": "supervisor",
"content": "Final report ready",
"message_type": "final",
"timestamp": datetime.now().isoformat()}]}Passo 4: Roteador e Construção do Grafo
def supervisor_router(state: ResearchState) -> str:
if state.get("status") == "completed":
return "finalize"
return state["next_agent"]
builder = StateGraph(ResearchState)
# Add nodes
builder.add_node("supervisor", supervisor_agent)
builder.add_node("planner", planner_agent)
builder.add_node("researcher", researcher_agent)
builder.add_node("analyst", analyst_agent)
builder.add_node("writer", writer_agent)
builder.add_node("reviewer", reviewer_agent)
builder.add_node("finalizer", finalizer_agent)
# Build edges
builder.add_edge(START, "supervisor")
# Supervisor routes to any agent
builder.add_conditional_edges(
"supervisor",
supervisor_router,
{
"planner": "planner",
"researcher": "researcher",
"analyst": "analyst",
"writer": "writer",
"reviewer": "reviewer",
"finalize": "finalizer"
}
)
# All agents return to supervisor
builder.add_edge("planner", "supervisor")
builder.add_edge("researcher", "supervisor")
builder.add_edge("analyst", "supervisor")
builder.add_edge("writer", "supervisor")
builder.add_edge("reviewer", "supervisor")
builder.add_edge("finalizer", END)
# Compile with persistence
app = builder.compile(checkpointer=MemorySaver())O padrão de laço do supervisor: START → supervisor → agente → supervisor → agente → ... → finalizador → END. Cada agente devolve o controle ao supervisor depois de concluir seu trabalho.
Passo 5: Executar o Sistema
def research_topic(query: str, thread_id: str = "research-1") -> str:
config = {"configurable": {"thread_id": thread_id}}
# Stream the execution
for event in app.stream(
{
"query": query,
"messages": [],
"research_plan": "",
"research_findings": [],
"analysis": "",
"draft_report": "",
"review_feedback": "",
"final_report": "",
"next_agent": "",
"errors": [],
"status": "starting"
},
config,
stream_mode="updates"
):
for node, update in event.items():
if node == "__end__":
continue
if "status" in update:
print(f"[{update['status'].upper()}] {node} completed")
# Get the final result
final = app.get_state(config)
return final.values.get("final_report", "No report generated.")
# Run the research system
report = research_topic(
"What are the environmental impacts of quantum computing?",
"research-quantum-1"
)
print(report)Passo 6: Adicionando Revisão Humana
Adicione uma etapa opcional de revisão humana:
def human_review_node(state: ResearchState) -> dict:
response = interrupt({
"draft": state["draft_report"],
"prompt": "Review the draft. Approve or request changes."
})
if response.get("approved"):
return {"status": "approved"}
return {"status": "rejected",
"review_feedback": response.get("feedback", "Revise")}
def review_router(state: ResearchState) -> str:
if state.get("status") == "approved":
return "finalize"
return "revise"
# Add human review to graph
builder.add_node("human_review", human_review_node)
builder.add_edge("reviewer", "human_review")
builder.add_conditional_edges("human_review", review_router, {
"finalize": "finalizer",
"revise": "writer" # Send back to writer with feedback
})Adicionar revisão humana em pontos estratégicos (antes da finalização) pega erros enquanto mantém a maior parte do fluxo de trabalho autônoma.
Diagrama do Sistema
Perguntas de Prática (Practice Questions)
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What pattern does the multi-agent research system use?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
How does the supervisor communicate its decision?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
Why does the project use different LLMs for different agents?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What is the role of the planner agent?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
Where does the analyst agent get its input from?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What terminates the supervisor loop?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What type of message structure enables agent communication tracking?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What advantage does streaming ('updates' mode) provide in this system?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
How can you add a human review step to the research workflow?
Como responder
Questão de múltipla escolha com 4 opções. Selecione uma opção e escolha Verificar resposta.
What is the purpose of the errors field in the research state?
Principais Conclusões
- Sistema de pesquisa multi-agente com padrão de laço de supervisor
- Cada agente se especializa em uma tarefa (planejamento, pesquisa, análise, escrita, revisión)
- O AgentMessage estruturado viabiliza a rastreabilidade completa da comunicação
- Modelos de LLM diferentes para agentes diferentes otimizam custo versus qualidade
- O streaming oferece visibilidade em tempo real do progresso do agente
- A revisão humana pode ser adicionada com interrupt() para controle de qualidade
- O supervisor decide quando o trabalho está completo, quebrando o laço
- O checkpointing permite pausar/retomar tarefas de pesquisa de longa duração