Olá a todos, aqui é a Sarah do agnthq.com. As últimas semanas foram loucas, não é? Toda vez que eu fecho os olhos, um novo agente de IA promete mudar minha vida, meu fluxo de trabalho ou pelo menos meu pedido de café. E, honestamente, muitos deles simplesmente não cumprem suas promessas… Eles são muito complexos, muito específicos ou não entregam o que prometem.
Mas de vez em quando, algo realmente interessante surge. Algo que me faz realmente parar e pensar: “Tudo bem, isso pode realmente ser útil.” Hoje, quero falar sobre uma dessas coisas. Não é um novo gadget, mas sim uma plataforma que evoluiu constantemente e, na minha opinião, atingiu um ponto ideal para uso prático do dia a dia: o novo framework Agents do LangChain com sua saída estruturada aprimorada e suas capacidades de chamada de ferramentas.
Agora, antes que você suspire e pense: “Oh ótimo, mais um artigo sobre LangChain,” deixe-me explicar. Eu estou brincando com o LangChain desde praticamente o primeiro dia. Eu me lembro dos primórdios, juntando LLMs e ferramentas, me sentindo como um Frankenstein digital. Foi poderoso, isso é certo, mas muitas vezes desajeitado, difícil de depurar, e a sensação era de que eu estava escrevendo código Python 2 em 2024. As saídas podiam ser… criativas, para dizer de forma gentil, e fazer com que um agente realizasse de maneira confiável o que você queria, especialmente com várias etapas, parecia mais uma oração do que programação.
As atualizações recentes, especialmente em relação à forma como os agentes interagem com as ferramentas e produzem uma saída estruturada, realmente mudaram o jogo para mim. Passamos de um “campo de testes experimental” para algo “realmente útil para meu trabalho como freelancer.” E isso é um grande avanço.
Minha Frustração com Agentes Pouco Confiáveis (e como isso ajuda)
Deixe-me te pintar um quadro. Alguns meses atrás, eu estava tentando construir um agente simples para uma amiga que gerencia uma pequena loja de comércio eletrônico. O problema dela: os clientes frequentemente fazem perguntas muito similares sobre os produtos, a entrega e as devoluções, e ela estava gastando tempo demais copiando e colando respostas. Minha ideia era um agente que pudesse:
- Consultar os detalhes do produto (preço, disponibilidade) a partir de um banco de dados fictício.
- Verificar as áreas de envio e os prazos.
- Formular uma resposta educada e precisa.
Parece simples, não é? Não exatamente. Minhas primeiras tentativas com o LangChain foram uma verdadeira bagunça. O agente às vezes alucinado IDs de produtos, esquecia de chamar a ferramenta de envio, ou simplesmente emitia um discurso conversacional em vez de uma resposta concisa. Conseguir um formato específico, como um objeto JSON contendo a resposta e as ferramentas usadas, era um pesadelo. Eu passei horas tentando convencê-lo com prompts elaborados, apenas para ele falhar em um caso específico.
É aqui que o novo framework de agentes brilha. O LangChain realmente melhorou a forma como os agentes decidem quais ferramentas usar e, principalmente, como eles relatam seus resultados. Não se trata mais de esperar que o LLM “encontre a solução”, mas de dar a ele um caminho claro e estruturado.
A Ideia Principal: Melhor Chamada de Ferramentas e Saída Estruturada
A maior melhoria, na minha opinião, vem de uma combinação de coisas:
- Definição Aprimorada de Ferramentas: As ferramentas agora são definidas com esquemas Pydantic, tornando muito mais claro para o LLM quais inputs ele espera.
- APIs de Chamada de Função (por exemplo, OpenAI): O LangChain utiliza isso por trás das cortinas para tornar a seleção das ferramentas muito mais confiável. O LLM não “adivinha” qual ferramenta usar; ele é explicitamente informado sobre as funções disponíveis e seus parâmetros.
- Parseadores de Saída Estruturada: Este é o Santo Graal para mim. Acabou a necessidade de tentar extrair uma resposta de um blob de texto livre via regex. Agora podemos definir exatamente qual estrutura esperamos que a resposta final do agente tenha.
Vejamos um exemplo simples para ilustrar isso. Vamos imaginar que temos uma ferramenta para obter o nível de estoque atual para um produto.
Exemplo: Uma Ferramenta de Verificação de Estoque Simples
Primeiro, vamos definir nossa ferramenta usando um modelo Pydantic para sua entrada:
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class ProductStockInput(BaseModel):
product_id: str = Field(description="O identificador único para o produto.")
@tool("get_product_stock", args_schema=ProductStockInput)
def get_product_stock(product_id: str) -> dict:
"""
Pesquisa o nível de estoque atual para um ID de produto dado.
Retorna um dicionário com product_id e stock_level.
"""
# Isso geralmente consultaria um banco de dados
stock_data = {
"P101": 50,
"P102": 0, # Esgotado
"P103": 15
}
stock_level = stock_data.get(product_id, -1) # -1 para não encontrado
if stock_level == -1:
return {"product_id": product_id, "stock_level": "Produto não encontrado"}
return {"product_id": product_id, "stock_level": stock_level}
tools = [get_product_stock]
Note o `args_schema` aqui. Isso é crucial. Isso indica ao LLM exatamente quais argumentos `get_product_stock` espera e quais são seus tipos. Não há mais ambiguidade.
Construindo o Agente com Saída Estruturada
Agora, vamos construir um agente que usa essa ferramenta e, principalmente, fornece sua resposta final de forma estruturada. Para meu amigo do e-commerce, eu queria que o agente retornasse a consulta do cliente, a resposta do agente e todas as ferramentas que ele usou, tudo em um bonito formato JSON.
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.messages import BaseMessage
from typing import List, Union, Dict, Any
# Definir o formato de saída estruturada para a resposta final do agente
class AgentResponse(BaseModel):
original_query: str = Field(description="A consulta original do cliente.")
agent_answer: str = Field(description="A resposta formulada pelo agente à consulta.")
tools_used: List[str] = Field(description="Uma lista dos nomes das ferramentas que o agente usou.")
metadata: Dict[str, Any] = Field(description="Qualquer metadado ou informação adicional proveniente das ferramentas.")
# Carregar o prompt base para o agente das ferramentas OpenAI
prompt = hub.pull("hwchase17/openai-tools-agent")
llm = ChatOpenAI(model="gpt-4-0125-preview", temperature=0) # Usando um GPT-4 recente para confiabilidade
# Criar o agente
agent = create_openai_tools_agent(llm, tools, prompt)
# Criar o executor do agente
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Agora, vamos definir como o agente deve responder usando with_structured_output
# É aqui que a mágica acontece para uma saída estruturada confiável!
structured_agent_executor = agent_executor.with_structured_output(AgentResponse)
# Vamos testar isso!
query1 = "Qual é o nível de estoque para o produto P101?"
response1 = structured_agent_executor.invoke({"input": query1})
print("\n--- Resposta 1 ---")
print(response1.json(indent=2))
query2 = "O produto P102 está disponível?"
response2 = structured_agent_executor.invoke({"input": query2})
print("\n--- Resposta 2 ---")
print(response2.json(indent=2))
query3 = "Qual é a capital da França?" # O agente não deveria usar nossa ferramenta para isso
response3 = structured_agent_executor.invoke({"input": query3})
print("\n--- Resposta 3 ---")
print(response3.json(indent=2))
Quando você executa isso, verá na saída detalhada como o LLM identifica primeiro a necessidade de `get_product_stock`, a chama com o `product_id` correto e, em seguida, utiliza o resultado para formular sua `agent_answer`. Mais importante ainda, a *saída final* é um objeto `AgentResponse`, não apenas uma string. Isso é incrivelmente poderoso para o processamento posterior, registro ou até mesmo apenas para exibir informações consistentes para um usuário.
O que eu adoro no `with_structured_output`
Esse método `with_structured_output` representa uma mudança significativa. Isso significa que eu posso integrar de forma confiável as respostas do agente em outras partes da minha aplicação. Não preciso mais escrever uma lógica de parsing frágil. Se o agente se desviar de alguma maneira, o Pydantic frequentemente capturará isso, me dando um erro claro em vez de falhar silenciosamente ou retornar dados irrelevantes.
Para meu amigo do e-commerce, isso significa que seu portal de atendimento ao cliente agora pode exibir a resposta do agente com confiança, sabendo que está no formato correto. Podemos até registrar os campos `tools_used` e `metadata` para entender com que frequência o agente utiliza ferramentas específicas ou se há perguntas comuns às quais ele não pode responder.
Além das Ferramentas Simples: Raciocínio Multietapas com Confiabilidade
O verdadeiro teste para um agente muitas vezes é o raciocínio de múltiplas etapas. Vamos adicionar outra ferramenta: uma para obter os prazos de envio estimados com base em um ID de produto e uma zona de destino.
from langchain_core.tools import tool
from pydantic import BaseModel, Field
# ... (ProductStockInput e get_product_stock permanecem os mesmos) ...
class ShippingInfoInput(BaseModel):
product_id: str = Field(description="O identificador único do produto.")
destination_zone: str = Field(description="A zona de envio (por exemplo, 'Zona A', 'Zona B').")
@tool("get_shipping_info", args_schema=ShippingInfoInput)
def get_shipping_info(product_id: str, destination_zone: str) -> dict:
"""
Fornece os prazos de envio estimados para um produto para uma zona específica.
Retorna um dicionário contendo product_id, zone e estimated_days.
"""
# Dados fictícios para o envio
shipping_times = {
("P101", "Zona A"): "3-5 dias úteis",
("P101", "Zona B"): "5-7 dias úteis",
("P103", "Zona A"): "2-4 dias úteis",
("P103", "Zona B"): "4-6 dias úteis",
}
key = (product_id, destination_zone)
estimated_days = shipping_times.get(key, "Variável, por favor entre em contato com o suporte")
return {"product_id": product_id, "destination_zone": destination_zone, "estimated_days": estimated_days}
tools_multi = [get_product_stock, get_shipping_info]
# Recriar um agente e um executor com as novas ferramentas
agent_multi = create_openai_tools_agent(llm, tools_multi, prompt)
agent_executor_multi = AgentExecutor(agent=agent_multi, tools=tools_multi, verbose=True)
structured_agent_executor_multi = agent_executor_multi.with_structured_output(AgentResponse)
# Testar uma consulta em várias etapas
multi_step_query = "Qual é o estoque para P101 e quanto tempo leva para enviar para a Zona A?"
response_multi = structured_agent_executor_multi.invoke({"input": multi_step_query})
print("\n--- Resposta em várias etapas ---")
print(response_multi.json(indent=2))
Você notará na saída detalhada que o agente agora chama de forma inteligente `get_product_stock` primeiro, depois `get_shipping_info`, e combina as informações em uma resposta coerente, enquanto respeita a estrutura `AgentResponse`. É um grande avanço em relação aos dias em que você precisava encadear explicitamente essas chamadas de ferramentas ou esperar que o LLM inferisse a sequência correta.
Minhas lições & Por que isso importa agora
Então, por que estou fazendo todo esse alarde agora? Porque isso parece indicar que os agentes LangChain, especialmente com a combinação de `create_openai_tools_agent` e `with_structured_output`, finalmente atingiram um nível de maturidade onde são realmente práticos para desenvolvedores construindo aplicações concretas. Chega de engenharia de prompts sem fim para forçar uma saída JSON, chega de parsing regex frágil, e significativamente menos de “chamadas de ferramentas alucinatórias”.
Dicas práticas para você:
- Revisite os agentes LangChain: Se você tentou os agentes LangChain há um ano e ficou frustrado, este é o momento certo para dar-lhes outra chance. As melhorias nas chamadas de ferramentas e na saída estruturada são substanciais.
- Defina as ferramentas com Pydantic: Sempre defina suas entradas de ferramentas usando modelos Pydantic com descrições claras. Isso dá ao LLM a melhor chance de entender quando e como usar suas ferramentas.
- Adote `with_structured_output`: Este é seu melhor aliado para uma integração confiável dos agentes. Defina um modelo Pydantic para a saída final do seu agente e use `agent_executor.with_structured_output(YourOutputModel)`. Isso irá economizar muitas horas de depuração e parsing.
- Comece simples e depois amplie: Não tente construir um super-agente imediatamente. Comece com uma ou duas ferramentas simples e uma estrutura de saída clara. Uma vez que isso funcione de forma confiável, adicione gradualmente mais complexidade.
- Use descrições detalhadas das ferramentas: O campo `description` no seu decorador `@tool` é o que o LLM lê para decidir se deve usar uma ferramenta. Seja claro, conciso e explique o que a ferramenta *faz* e o que ela *retorna*.
- Use a chamada de função da OpenAI: Embora este artigo se concentre no LangChain, a confiabilidade subjacente geralmente vem de LLMs, como os modelos GPT da OpenAI, que possuem sólidas capacidades de chamada de função. Certifique-se de usar modelos que suportem isso para melhores resultados.
Acredito sinceramente que esses avanços tornam a construção de agentes inteligentes muito mais acessível e, mais importante ainda, muito mais confiável. Para quem está construindo funcionalidades alimentadas por IA, seja um bot de atendimento ao cliente, um assistente de análise de dados ou simplesmente automatizando tarefas chatas, a capacidade de interagir de forma previsível com um agente e receber dados estruturados é uma mudança significativa. Isso fez com que os agentes deixassem de ser uma demonstração impressionante para se tornarem uma ferramenta realmente útil no arsenal dos desenvolvedores.
É isso para mim hoje. Experimente isso e me diga o que você está construindo! Compartilhe seus pensamentos e experiências nos comentários abaixo. Boa construção de agentes!
🕒 Published: