Parte 2: Construindo seu Primeiro Agente de IA: Guia Prático com LangChain

A maioria dos tutoriais de agentes de IA ignora a parte caótica do desenvolvimento. Veja como construí um agente funcional com LangChain, tRPC e PostgreSQL — incluindo os erros reais que cometi ao longo da jornada.

Parte 2: Construindo seu Primeiro Agente de IA: Guia Prático com LangChain
Feng LiuFeng Liu
19 de dezembro de 2025

O hype dos agentes de IA é real. Todo mundo está falando sobre sistemas autônomos que podem pensar, planejar e executar tarefas. Mas aqui está o ponto que ninguém te conta: a maioria dos tutoriais mostra apenas o "caminho feliz" (happy path) e pula as partes onde as coisas quebram.

Na semana passada, passei dois dias construindo um agente de IA do zero. Não um exemplo de brinquedo — um real, que gerencia uma plataforma de blog, cria usuários, escreve posts e funciona de verdade. Vou mostrar exatamente como fiz isso, incluindo as partes que não funcionaram na primeira tentativa.

Código completo: github.com/giftedunicorn/my-ai-agent

O Que Estamos Realmente Construindo

Esqueça os exemplos abstratos. Estamos construindo um agente que:

  • Cria e gerencia usuários em um banco de dados PostgreSQL
  • Gera posts de blog sob demanda
  • Responde de forma conversacional enquanto usa ferramentas
  • Mantém o histórico da conversa
  • Faz o deploy de verdade (nada de demos apenas em localhost)

A stack: Next.js, tRPC, Drizzle ORM, LangChain e o Gemini do Google. Não porque está na moda — mas porque é type-safe, rápido e realmente funciona em produção.

A Arquitetura (Mais Simples do Que Você Imagina)

Aqui está o que me surpreendeu: Agentes de IA não são tão complicados. No núcleo, eles são apenas:

  1. Um LLM que pode chamar funções
  2. Um conjunto de ferramentas que o LLM pode usar
  3. Um loop que executa essas ferramentas
  4. Memória para manter o contexto

É só isso. A complexidade vem de fazer essas peças trabalharem juntas de forma confiável.

O Schema do Banco de Dados

Primeiro, a fundação. Precisamos de tabelas para usuários, posts e mensagens:

export const User = pgTable("user", (t) => ({
  id: t.integer().primaryKey().generatedAlwaysAsIdentity(),
  name: t.varchar({ length: 255 }).notNull(),
  email: t.varchar({ length: 255 }).notNull().unique(),
  bio: t.text(),
  createdAt: t.timestamp().defaultNow().notNull(),
  updatedAt: t.timestamp().defaultNow().notNull(),
}));

export const Post = pgTable("post", (t) => ({
  id: t.integer().primaryKey().generatedAlwaysAsIdentity(),
  userId: t
    .integer()
    .notNull()
    .references(() => User.id, { onDelete: "cascade" }),
  title: t.varchar({ length: 500 }).notNull(),
  content: t.text().notNull(),
  published: t.boolean().default(false).notNull(),
  createdAt: t.timestamp().defaultNow().notNull(),
  updatedAt: t.timestamp().defaultNow().notNull(),
}));

Nada extravagante. Apenas dados relacionais limpos com PostgreSQL. A tabela Message armazena o histórico da conversa — crucial para manter o contexto entre as requisições.

Construindo as Ferramentas (Onde a Mágica Acontece)

É aqui que a maioria dos tutoriais fica vaga. "Apenas crie algumas ferramentas", eles dizem. Deixe-me mostrar como isso realmente se parece.

Ferramentas são funções que sua IA pode chamar. Com o DynamicStructuredTool do LangChain, você define:

  1. O que a ferramenta faz (descrição)
  2. Quais inputs ela precisa (schema com Zod)
  3. O que ela realmente executa (função)

Aqui está a ferramenta para criar usuários:

const createUserTool = new DynamicStructuredTool({
  name: "create_user",
  description:
    "Create a new user in the database. Use this when asked to add, create, or register a user.",
  schema: z.object({
    name: z.string().describe("The user's full name"),
    email: z.string().email().describe("The user's email address"),
    bio: z.string().optional().describe("Optional biography"),
  }),
  func: async (input) => {
    const { name, email, bio } = input as {
      name: string;
      email: string;
      bio?: string;
    };
    const user = await caller.user.create({ name, email, bio });
    return `Successfully created user: ${user.name} (ID: ${user.id}, Email: ${user.email})`;
  },
});

A descrição importa mais do que você imagina. O LLM a usa para decidir quando chamar esta ferramenta. Seja específico sobre quando usá-la.

O valor de retorno? É isso que o LLM vê. Eu retorno texto estruturado com todos os detalhes relevantes — IDs, nomes, confirmação. Isso ajuda o LLM a dar respostas melhores aos usuários.

O Agente: Juntando Tudo

Aqui é onde fica interessante. A nova API do LangChain (v1.2+) simplificou tudo:

const agent = createAgent({
  model: new ChatGoogleGenerativeAI({
    apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY,
    model: "gemini-2.0-flash-exp",
    temperature: 0.7,
  }),
  tools: [...createUserTools(caller), ...createPostTools(caller)],
  systemPrompt: AGENT_SYSTEM_PROMPT,
});

const result = await agent.invoke({
  messages: conversationMessages,
});

É só isso. Sem ChatPromptTemplate, sem AgentExecutor, sem chains complexas. Apenas createAgent e invoke.

O System Prompt (A Personalidade do Seu Agente)

É aqui que você ensina seu agente como se comportar:

const AGENT_SYSTEM_PROMPT = `You are an AI assistant that helps manage a blog platform.

You have access to tools for:
- User management (create, read, list, count)
- Post management (create, list)

When users ask you to perform actions:
1. Use the appropriate tools to complete the task
2. Be conversational and friendly
3. Provide clear confirmation with specific details
4. When creating mock data, use realistic names and content

Always confirm successful operations with relevant details.`;

Aprendi isso da maneira difícil: seja explícito. Diga ao agente exatamente o que fazer, como responder e quais detalhes incluir. Prompts vagos levam a comportamentos vagos.

Lidando com o Histórico da Conversa

A maioria dos exemplos pula isso, mas é crítico para uma boa experiência do usuário. Veja como eu lido com isso:

// Obter as últimas 10 mensagens do banco de dados
const history = await ctx.db
  .select()
  .from(Message)
  .orderBy(desc(Message.createdAt))
  .limit(10);

// Converter para o formato do LangChain
const conversationMessages = [
  ...history.reverse().map((msg) => ({
    role: msg.role === "user" ? "user" : "assistant",
    content: msg.content,
  })),
  { role: "user", content: input.message },
];

Simples, mas eficaz. O agente agora lembra das últimas 10 trocas de mensagens. O suficiente para contexto, mas não tanto a ponto de ficar confuso ou caro.

As Partes Complicadas (O Que Realmente Quebrou)

Dependências Circulares: Minha primeira tentativa falhou porque agent.ts importava appRouter, que importava agentRouter, criando uma dependência circular. Solução? Criar um router temporário inline apenas com os routers necessários para as ferramentas.

Extração de Resposta da Ferramenta: O formato de resposta do LangChain mudou na v1.2. O resultado agora está em result.messages[result.messages.length - 1].content, não em result.output. Levei uma hora para descobrir isso.

Type Safety: O parâmetro func da ferramenta precisa de tipagem explícita. Você não pode apenas desestruturar — precisa fazer o cast do input primeiro. O TypeScript não vai te ajudar aqui automaticamente.

Configurando o Seu Próprio

Aqui está o que você realmente precisa:

  1. Instalar dependências:
pnpm add @langchain/core @langchain/google-genai langchain drizzle-orm
  1. Variáveis de ambiente:
POSTGRES_URL="your-database-url"  # Tente Vercel Postgres, Supabase ou PostgreSQL local
GOOGLE_GENERATIVE_AI_API_KEY="your-gemini-key"  # Obtenha em https://aistudio.google.com/app/apikey
  1. Configuração do banco de dados:
pnpm db:push  # Cria tabelas a partir do schema
  1. Comece a construir:
  • Defina seu schema de banco de dados
  • Crie procedures tRPC para operações CRUD
  • Construa ferramentas LangChain que envelopam essas procedures
  • Crie o agente com suas ferramentas
  • Conecte tudo ao seu frontend

O Que Eu Faria Diferente

Se eu começasse de novo amanhã:

Começaria com menos ferramentas. Eu construí 7 ferramentas inicialmente. Fique com 3-4 principais primeiro. Faça elas funcionarem perfeitamente, depois expanda.

Testaria as ferramentas independentemente. Não espere até que o agente esteja construído para testar suas ferramentas. Chame-as diretamente com dados de teste primeiro.

Monitoraria o uso das ferramentas. Adicionei logs para ver quais ferramentas o agente chama e por quê. Isso revelou que as descrições das minhas ferramentas precisavam de ajustes.

Usaria streaming. No momento, os usuários esperam pela resposta completa. Streaming faria parecer mais rápido, mesmo que leve o mesmo tempo.

O Choque de Realidade

Construir agentes de IA não é mágica, mas também não é trivial. Você gastará mais tempo em:

  • Design de ferramentas (o que cada ferramenta deve fazer?)
  • Engenharia de prompt (como faço o agente se comportar corretamente?)
  • Tratamento de erros (e se o banco de dados cair? e se o LLM alucinar?)
  • Type safety (deixar o TypeScript feliz com respostas dinâmicas de LLM)

Do que na parte de IA propriamente dita.

Tente Você Mesmo

O código deste tutorial é real — eu o construí enquanto escrevia isto. Você pode:

  • Testar com: "create 3 mock users" (crie 3 usuários de teste)
  • Tentar: "create 2 blog posts for user 1" (crie 2 posts para o usuário 1)
  • Perguntar: "how many users do we have?" (quantos usuários nós temos?)

O agente lida com tudo isso decidindo quais ferramentas chamar, executando-as e respondendo de forma conversacional.

O Que Vem a Seguir

Isto é apenas a fundação. A partir daqui, você poderia:

  • Adicionar autenticação (quem pode criar o quê?)
  • Implementar respostas via streaming
  • Adicionar ferramentas mais complexas (busca, analytics, integrações)
  • Construir um loop de feedback (a chamada da ferramenta teve sucesso?)
  • Adicionar rate limiting (não deixe usuários criarem 10.000 posts)

Mas comece simples. Tenha uma ferramenta funcionando bem antes de adicionar dez medíocres.

A melhor parte? Uma vez que você entende esse padrão — ferramentas + LLM + memória — você pode construir agentes para qualquer coisa. Gerenciamento de banco de dados, suporte ao cliente, geração de conteúdo, o que for.

A parte difícil não é o código. É projetar ferramentas que realmente resolvam problemas reais.


Recursos:

Compartilhar

Feng Liu

Feng Liu

shenjian8628@gmail.com

Parte 2: Construindo seu Primeiro Agente de IA: Guia Prático com LangChain | Feng Liu