🧩 Por que LangChain existe
Construir um agente do zero significa implementar o loop de tool calling, gerenciar o histórico de mensagens, parsear as respostas do LLM e tratar erros — tudo do zero. LangChain abstrai esse boilerplate em componentes reutilizáveis para que você foque na lógica do agente.
O que LangChain abstrai para você
Sem LangChain (do zero)
- • Implementar o loop while tool_calls
- • Parsear tool calls do JSON da API
- • Gerenciar lista de mensagens manualmente
- • Implementar retry em falhas
- • Gerenciar memória e contexto
- • Tratar erros de cada provider diferente
Com LangChain
- ✓ AgentExecutor/LCEL cuida do loop
- ✓ Tool calling automático
- ✓ Memory objects para histórico
- ✓ Retry configurável
- ✓ Memory integrada ao prompt
- ✓ Interface unificada para OpenAI/Anthropic/etc.
Quando LangChain ajuda
Prototipagem rápida, integração com muitos LLMs, uso de integrações prontas do ecossistema (vectorstores, loaders, tools)
Quando LangChain complica
Agentes muito simples (1-2 tools), quando você precisa de controle total do loop, quando abstração vira complexidade acidental
Alternativas
LlamaIndex (RAG-first), smolagents (Hugging Face, mais simples), Pydantic AI (type-safe), LangGraph (grafos de agentes)
🆚 AgentExecutor vs. LCEL
LangChain tem dois paradigmas de construção de agentes: o AgentExecutor (API legada mas ainda dominante em tutoriais) e o LCEL (LangChain Expression Language — o jeito novo, mais flexível). Você vai encontrar ambos na prática.
AgentExecutor (jeito antigo)
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.invoke({"input": "Pergunta"})
- + Fácil de entender para iniciantes
- + Muito material na internet
- - API considerada legada
- - Menos observabilidade por padrão
LCEL (jeito novo)
from langchain import hub
from langchain.agents import create_react_agent
from langchain_core.runnables import RunnableWithMessageHistory
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
# Composição com operador |
chain = prompt | llm.bind_tools(tools) | parser
- + Composição declarativa com operador |
- + Streaming nativo
- + Melhor integração com LangSmith
- - Curva de aprendizado maior
💡 Qual usar?
Para aprender: comece com AgentExecutor (mais simples). Para produção nova: prefira LCEL (mais observável e flexível). Para projetos legados: não migre por migrar — só migre quando tiver ganho claro.
💾 Adicionando memória ao agente
Sem memória, cada invocação do agente começa do zero. Para agentes conversacionais, você precisa persistir o histórico entre chamadas. LangChain tem memória in-memory (sessão atual) e persistente (entre sessões com SQLite ou Redis).
Implementação completa com RunnableWithMessageHistory
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# Persistência em SQLite — memória entre sessões!
def get_session_history(session_id: str):
return SQLChatMessageHistory(session_id, "sqlite:///memoria.db")
agent_with_memory = RunnableWithMessageHistory(
agent_executor,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history"
)
# session_id permite múltiplos usuários com memórias separadas
resposta = agent_with_memory.invoke(
{"input": "Olá! Meu nome é Ana."},
config={"configurable": {"session_id": "usuario-123"}}
)
# Na próxima chamada, o agente já sabe que o usuário se chama Ana!
Opções de persistência de memória
In-Memory
ChatMessageHistory — apenas na sessão Python atual. Perde tudo quando o processo reinicia.
SQLite
SQLChatMessageHistory — persiste entre sessões. Ideal para desenvolvimento e escala pequena.
Redis
RedisChatMessageHistory — escala horizontalmente. Ideal para produção com múltiplos workers.
🛠️ Tools com LangChain
LangChain tem duas formas de criar tools: o decorator @tool (simples, gera schema da docstring) e StructuredTool (para tools com múltiplos parâmetros tipados). A docstring vira a description da tool no schema JSON.
@tool decorator — simples
from langchain.tools import tool
@tool
def buscar_tempo(cidade: str) -> str:
"""Retorna a previsão do tempo atual para
uma cidade. Use quando o usuário perguntar
sobre clima ou temperatura."""
return f"25°C em {cidade}, ensolarado"
# Schema gerado automaticamente!
StructuredTool — múltiplos parâmetros
from langchain.tools import StructuredTool
from pydantic import BaseModel
class BuscaInput(BaseModel):
cidade: str
dias: int = 1
tool = StructuredTool.from_function(
func=buscar_tempo_fn,
args_schema=BuscaInput,
handle_tool_error=True # não quebra o agente
)
Tools built-in do LangChain
TavilySearchResults
Busca web com Tavily
DuckDuckGoSearchRun
Busca gratuita
WikipediaQueryRun
Busca na Wikipedia
PythonREPLTool
Executa Python
📧 Projeto: assistente de email
O projeto completo: um agente que lê emails, classifica por prioridade e rascunha respostas para os urgentes. Integra memória, structured output e tools customizadas em um sistema funcional e extensível.
Código completo comentado
from langchain.agents import AgentExecutor, create_react_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from pydantic import BaseModel
from langchain import hub
INBOX = [
{"id": 1, "de": "joao@empresa.com", "assunto": "URGENTE: servidor caiu",
"corpo": "Produção está fora desde 14h. Clientes reportando erro 500."},
{"id": 2, "de": "newsletter@startup.com", "assunto": "10 dicas de produtividade",
"corpo": "Confira as melhores práticas para..."},
]
class ClassificacaoEmail(BaseModel):
categoria: str # "urgente", "normal", "spam"
prioridade: int # 1-5
requer_resposta: bool
@tool
def ler_inbox() -> str:
"""Lista todos os emails na caixa de entrada. Use para ver os emails disponíveis."""
return str([{"id": e["id"], "assunto": e["assunto"], "de": e["de"]} for e in INBOX])
@tool
def ler_email(email_id: int) -> str:
"""Lê o conteúdo completo de um email pelo ID."""
email = next((e for e in INBOX if e["id"] == email_id), None)
return str(email) if email else "Email não encontrado"
@tool
def rascunhar_resposta(email_id: int, tom: str = "profissional") -> str:
"""Rascunha uma resposta para o email. Tom pode ser: profissional, amigável, urgente."""
email = next((e for e in INBOX if e["id"] == email_id), None)
if not email: return "Email não encontrado"
return f"Rascunho para {email['de']}: [Texto personalizado baseado em '{email['assunto']}' em tom {tom}]"
tools = [ler_inbox, ler_email, rascunhar_resposta]
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
resultado = executor.invoke({
"input": "Verifique minha caixa de entrada, classifique os emails e rascunhe resposta para os urgentes."
})
🐛 Depurando sem LangSmith
LangSmith é ótimo, mas nem sempre está disponível ou configurado. Com verbose=True e callbacks customizados, você tem visibilidade completa do que o agente está fazendo direto no terminal — sem dependência externa.
Callback handler para debug completo
from langchain.callbacks.base import BaseCallbackHandler
from datetime import datetime
class DebugCallbackHandler(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
print(f"\n[LLM START] {datetime.now().strftime('%H:%M:%S')}")
print(f"Input tokens estimados: ~{len(prompts[0])//4}")
def on_llm_end(self, response, **kwargs):
output = response.generations[0][0].text
print(f"[LLM END] Output: {output[:200]}...")
def on_tool_start(self, serialized, input_str, **kwargs):
print(f"\n[TOOL CALL] {serialized['name']}")
print(f"Input: {input_str}")
def on_tool_end(self, output, **kwargs):
print(f"[TOOL RESULT] {str(output)[:300]}")
executor = AgentExecutor(
agent=agent, tools=tools,
callbacks=[DebugCallbackHandler()] # ← adicione aqui
)
verbose=True — o mais simples
Adicione verbose=True ao AgentExecutor para ver a trace completa de Thought/Action/Observation no console:
executor = AgentExecutor(
agent=agent, tools=tools,
verbose=True # ← basta isso
)
Identificando o passo que falhou
- Leia o output do verbose do fim para o início
- Ache o último "Thought" antes do erro
- Veja qual "Action" foi chamada e com quais inputs
- Veja o "Observation" (resultado da tool)
- Decida: bug no prompt, na tool ou na lógica?
✅ Resumo do Módulo 2.7
Próximo Módulo:
2.8 — Depuração e Rastreamento: LangSmith em 3 minutos, lendo traces, identificando root causes e boas práticas de logging em produção.