UNPKG

@cosmixclub/parsero

Version:

Construtor de agentes de IA de maneira simplificada.

897 lines (749 loc) 30 kB
# Parsero > Uma biblioteca **simples** e **fortemente tipada** para criar agentes de IA com [LangChain](https://js.langchain.com/docs/) e [LangGraph](https://langchain-ai.github.io/langgraphjs/). > Inspirada no [CrewAI](https://docs.crewai.com/introduction) e criada para ser mais **descritiva** e **direta**, com padrões bem-definidos para a criação de agentes reutilizáveis. ## Sumário - [Parsero](#parsero) - [Sumário](#sumário) - [Objetivo](#objetivo) - [Características Principais](#características-principais) - [Compatibilidade](#compatibilidade) - [Casos de Uso](#casos-de-uso) - [Utilizando Múltiplos LLMs](#utilizando-múltiplos-llms) - [Como Funciona](#como-funciona) - [Integração com LangGraph](#integração-com-langgraph) - [Tipagem para Múltiplos LLMs](#tipagem-para-múltiplos-llms) - [Reutilizando Procedures com InferState](#reutilizando-procedures-com-inferstate) - [Como Funciona](#como-funciona-1) - [Por que Usar](#por-que-usar) - [Exemplo Completo](#exemplo-completo) - [Exemplos](#exemplos) - [Exemplo 1: CheckProcedure customizando a ordem de execução](#exemplo-1-checkprocedure-customizando-a-ordem-de-execução) - [Exemplo 2: Fluxo sequencial **sem** `CheckProcedure` e **sem** `nextProcedure`](#exemplo-2-fluxo-sequencial-sem-checkprocedure-e-sem-nextprocedure) - [Exemplo 3: Fluxo personalizado com `nextProcedure`](#exemplo-3-fluxo-personalizado-com-nextprocedure) - [Exemplo 4: Utilizando múltiplos modelos para tarefas especializadas](#exemplo-4-utilizando-múltiplos-modelos-para-tarefas-especializadas) - [Exemplo 5: Combinando diferentes famílias de modelos](#exemplo-5-combinando-diferentes-famílias-de-modelos) - [Observabilidade e Integração com LangSmith](#observabilidade-e-integração-com-langsmith) - [Como funciona](#como-funciona-2) - [Exemplo de uso com observabilidade](#exemplo-de-uso-com-observabilidade) --- ## Objetivo O **Parsero** foi desenvolvido para **simplificar** a criação de agentes de IA utilizando [LangChain](https://js.langchain.com/docs/) e [LangGraph](https://langchain-ai.github.io/langgraphjs/). Ele oferece uma interface mais **intuitiva**, **fortemente tipada** e com **padrões claros**, permitindo: - Facilitar a orquestração de **procedimentos** (Procedures) em agentes de IA. - Reduzir a complexidade de criação de fluxos e decisões (grafos). - Reaproveitar a lógica de agentes em diferentes projetos, graças às definições claras de entrada/saída. --- ## Características Principais - **Forte Tipagem**: Usa [Zod](https://zod.dev/) para schemas de entrada/saída, garantindo segurança de tipos. - **Simplicidade**: Os superpoderes do LangGraph mas com interface mais direta, seguindo padrões definidos (Procedures do tipo `action` e `check`). - **Extensibilidade**: Compatível nativamente com LangChain e LangGraph, para que você possa aproveitar o ecossistema existente. - **Orquestração por Procedimentos**: Define _procedures_ que podem modificar o estado (`action`) ou indicar o próximo passo (`check`). - **Múltiplos LLMs**: Suporte para utilizar diferentes modelos de linguagem em diferentes procedures, otimizando custo e desempenho. - **Organização Modular**: Permite definir procedures fora dos agentes com a utilidade `InferState`, facilitando a organização e reuso do código. --- ## Compatibilidade - **LangChain**: Pode ser usado junto com qualquer [ChatModel](https://js.langchain.com/docs/modules/models/chat) disponível (por exemplo, `ChatOpenAI`, `ChatGoogleGenerativeAI`, etc.). - **LangGraph**: Totalmente integrável ao LangGraph, para que você possa desenhar fluxos de conversação e lógicas mais complexas de maneira visual e tipada. A classe `Agent` fornece acesso ao seu equivalente em grafo. --- ## Casos de Uso 1. **Criação de Agentes de IA Customizados** - Defina **procedures** específicas para o seu caso. - **Aplique** validação de entrada e saída para garantir conformidade dos dados. 2. **Fluxos de Decisão com IA** - Utilize **CheckProcedure** para direcionar o fluxo conforme o conteúdo do estado. 3. **Aplicações com Entrada e Saída Bem Definidas** - Perfeito para pipelines de dados, chatbots especializados, ou qualquer agente que precise controlar o estado de forma clara. 4. **Orquestração de Múltiplos LLMs** - Cada **procedure** pode acessar diferentes instâncias de LLM, permitindo combinar modelos especializados. - Use modelos mais econômicos para tarefas simples e modelos avançados apenas para tarefas complexas. - Combine diferentes famílias de modelos (OpenAI, Anthropic, Google, etc.) no mesmo agente. --- ## Utilizando Múltiplos LLMs O Parsero permite que você use diferentes modelos de linguagem para diferentes partes do seu agente, otimizando tanto o desempenho quanto os custos. ### Como Funciona Ao criar seu agente, você pode fornecer um mapa de modelos em vez de um único modelo: ```ts import { Agent, State } from "@cosmixclub/parsero"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; import { ChatOpenAI } from "@langchain/openai"; const agent = new Agent({ // Outros parâmetros... llm: { default: new ChatOpenAI({ model: "gpt-4o" }), // Modelo principal summarize: new ChatGoogleGenerativeAI({ model: "gemini-pro" }), // Para resumos classify: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }), // Para classificações creative: new ChatAnthropic({ model: "claude-3-opus-20240229" }), // Para geração criativa }, // ... }); ``` Dentro de suas procedures, você pode acessar o modelo específico que deseja utilizar: ```ts // Em uma ActionProcedure async run(state, llms) { // Use o modelo específico para tarefas de classificação const result = await llms.classify.invoke("Classifique este texto..."); // Use o modelo específico para resumos const summary = await llms.summarize.invoke("Resuma este conteúdo..."); // Use o modelo padrão const response = await llms.default.invoke("Responda esta pergunta..."); // Continue seu código... return state; } ``` ### Integração com LangGraph Ao utilizar `agent.graph` para acessar o grafo LangGraph equivalente, a biblioteca automaticamente: 1. **Usará o modelo "default"** se estiver disponível no objeto de LLMs 2. **Usará o primeiro modelo** do objeto se não houver um modelo chamado "default" Esta abordagem garante compatibilidade com o LangGraph que atualmente espera um único modelo, mas ainda permite que você utilize múltiplos modelos dentro do seu agente Parsero. ```ts const agent = new Agent({ llm: { default: new ChatOpenAI(), // Este será usado no LangGraph specialTask: new ChatAnthropic(), }, // ... }); // O grafo usará o modelo "default" internamente const graph = agent.graph; // Execute o grafo await graph.invoke({ input: "exemplo" }); ``` ### Tipagem para Múltiplos LLMs Se você estiver usando TypeScript, pode tirar vantagem do sistema de tipos para garantir que suas procedures acessem apenas LLMs que realmente existem: ```ts // Defina o tipo do seu mapa de LLMs type MyLLMs = { default: ChatOpenAI; summarize: ChatGoogleGenerativeAI; classify: ChatOpenAI; }; // Use o tipo genérico na sua procedure const classifyProcedure: ActionProcedure<any, MyLLMs> = { name: "classify", type: "action", async run(state, llms) { // TypeScript sabe que llms.classify existe e é do tipo ChatOpenAI const result = await llms.classify.invoke("..."); // ... }, }; ``` --- ## Reutilizando Procedures com InferState O Parsero fornece a utilidade `InferState<>` que permite definir procedures de forma independente e reutilizável, separadas da definição do agente. Isso traz várias vantagens para a organização do código: ### Como Funciona `InferState<>` é um tipo utilitário que extrai os tipos de entrada e saída de um `State`, facilitando a definição de procedures fora do contexto do agente: ```ts import { z } from "zod"; import { Agent, InferState, Procedure, State } from "@cosmixclub/parsero"; import { BaseChatModel } from "@langchain/core/language_models/chat_models"; // 1. Defina seu State const state = new State({ inputSchema: z.object({ query: z.string(), }), outputSchema: z.object({ result: z.string(), }), }); // 2. Crie procedures reutilizáveis com tipagem correta const analyzeQuery: Procedure<InferState<typeof state>, BaseChatModel> = { name: "analyzeQuery", nextProcedure: "generateResponse", type: "action", async run(state, llm) { // Seu código aqui... return state; }, }; const generateResponse: Procedure<InferState<typeof state>, BaseChatModel> = { name: "generateResponse", type: "action", nextProcedure: END, async run(state, llm) { // Seu código aqui... return state; }, }; // 3. Use as procedures no agente const agent = new Agent({ state, llm: new ChatOpenAI(), procedures: [ analyzeQuery, generateResponse, // Outras procedures... ], }); ``` ### Por que Usar - **Organização de Código**: Separe a lógica em arquivos distintos para melhor manutenção. - **Reusabilidade**: Reutilize procedures em diferentes agentes. - **Testabilidade**: Teste procedures individualmente, facilitando os testes unitários. - **Colaboração**: Permite que diferentes membros da equipe trabalhem em diferentes procedures. ### Exemplo Completo Veja como organizar seu código com procedures em arquivos separados: ```ts // state.ts import { z } from "zod"; import { State } from "@cosmixclub/parsero"; export const numberClassifierState = new State({ inputSchema: z.object({ number: z.number(), }), outputSchema: z.object({ class: z.enum(["odd", "even"]), explanation: z.string(), }), }); // procedures/classify.ts import { Procedure } from "@cosmixclub/parsero"; import { InferState } from "@cosmixclub/parsero"; import { BaseChatModel } from "@langchain/core/language_models/chat_models"; import { z } from "zod"; import { numberClassifierState } from "../state"; export const whatNumberIs: Procedure<InferState<typeof numberClassifierState>, BaseChatModel> = { name: "whatNumberIs", nextProcedure: "router", async run(state, llm) { const chain = llm.withStructuredOutput( z.object({ class: z.enum(["odd", "even"]).describe("Se o número é par ou ímpar"), }), ); const output = await chain.invoke(`Determine se o número a seguir é par ou ímpar: ${state.input.number}`); state.output.class = output.class; return state; }, type: "action", }; // procedures/router.ts import { Procedure } from "@cosmixclub/parsero"; import { InferState } from "@cosmixclub/parsero"; import { numberClassifierState } from "../state"; export const router: Procedure<InferState<typeof numberClassifierState>, any> = { name: "router", async run(state) { const numberClass = state.output.class; if (numberClass === "odd") return "isOdd"; return "isEven"; }, type: "check", }; // agent.ts import { Agent, END } from "@cosmixclub/parsero"; import { ChatOpenAI } from "@langchain/openai"; import { StringOutputParser } from "@langchain/core/output_parsers"; import { numberClassifierState } from "./state"; import { whatNumberIs } from "./procedures/classify"; import { router } from "./procedures/router"; const agent = new Agent({ state: numberClassifierState, llm: new ChatOpenAI(), procedures: [ whatNumberIs, router, { name: "isOdd", nextProcedure: END, async run(state, llm) { const chain = llm.pipe(new StringOutputParser()); const output = await chain.invoke( `Gere uma explicação do motivo de '${state.input.number}' ser ímpar.`, ); state.output.explanation = output; return state; }, type: "action", }, { name: "isEven", nextProcedure: END, async run(state, llm) { const chain = llm.pipe(new StringOutputParser()); const output = await chain.invoke(`Gere uma explicação do motivo de '${state.input.number}' ser par.`); state.output.explanation = output; return state; }, type: "action", }, ], }); ``` Usando essa abordagem, seu código fica mais organizado, modular e fácil de manter, especialmente em projetos maiores com múltiplos agentes e procedures complexas. --- ## Exemplos ### Exemplo 1: CheckProcedure customizando a ordem de execução No exemplo abaixo, o agente: 1. **Descobre** se o número é par ou ímpar (action). 2. **Verifica** o resultado e **decide** qual próximo passo a seguir (check). 3. **Executa** a procedure correspondente a par ou ímpar (action). ```ts import { z } from "zod"; import { Agent, END, State } from "@cosmixclub/parsero"; import { StringOutputParser } from "@langchain/core/output_parsers"; import { ChatOpenAI } from "@langchain/openai"; // exemplificando, pois depende do seu setup const agent = new Agent({ state: new State({ inputSchema: z.object({ number: z.number(), }), outputSchema: z.object({ class: z.enum(["odd", "even"]), explanation: z.string(), }), }), llm: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0.1, maxTokens: 500, streaming: true, cache: true, apiKey: process.env.OPENAI_API_KEY, }), options: { verbose: true, }, procedures: [ { // 1. Procedure que classifica o número como par ou ímpar. name: "whatNumberIs", type: "action", nextProcedure: "router", async run(state, llm) { const chain = llm.withStructuredOutput( z.object({ class: z.enum(["odd", "even"]).describe("Se o número é par ou ímpar"), }), ); const output = await chain.invoke( `Determine se o número a seguir é par ou ímpar: ${state.input.number}`, ); state.output.class = output.class; return state; }, }, { // 2. Procedure do tipo 'check' que decide qual caminho seguir name: "router", type: "check", async run(state) { const numberClass = state.output.class; if (numberClass === "odd") return "isOdd"; return "isEven"; }, }, { // 3a. Se for ímpar, chama esta procedure. name: "isOdd", type: "action", nextProcedure: END, async run(state, llm) { const chain = llm.pipe(new StringOutputParser()); const output = await chain.invoke( `Gere uma explicação do motivo de '${state.input.number}' ser ímpar.`, ); state.output.explanation = output; return state; }, }, { // 3b. Se for par, chama esta procedure. name: "isEven", type: "action", nextProcedure: END, async run(state, llm) { const chain = llm.pipe(new StringOutputParser()); const output = await chain.invoke(`Gere uma explicação do motivo de '${state.input.number}' ser par.`); state.output.explanation = output; return state; }, }, ], }); // Execução: const output = await agent.run({ number: 11 }); console.log(output); // => { class: "odd", explanation: "Explicação sobre por que 11 é ímpar..." } ``` > Observe que uma `CheckProcedure` **não altera** o estado. Ela apenas **retorna** o nome da próxima procedure a ser executada. --- ### Exemplo 2: Fluxo sequencial **sem** `CheckProcedure` e **sem** `nextProcedure` O exemplo abaixo mostra um fluxo **estritamente sequencial**, onde cada procedure de tipo `action` é executada na **ordem** em que foi definida. Assim que uma procedure termina, o agente avança para a próxima. ```ts import { z } from "zod"; import { Agent, State } from "@cosmixclub/parsero"; import { ChatOpenAI } from "@langchain/openai"; const agent = new Agent({ state: new State({ inputSchema: z.object({ text: z.string(), }), outputSchema: z.object({ uppercase: z.string(), reversed: z.string(), }), }), llm: new ChatOpenAI({ model: "gpt-4", apiKey: "..." }), procedures: [ { // 1. Procedure que converte o texto para maiúsculas. name: "toUpperCase", type: "action", async run(state) { state.output.uppercase = state.input.text.toUpperCase(); return state; }, }, { // 2. Procedure que reverte o texto convertido. name: "reverseText", type: "action", async run(state) { state.output.reversed = state.output.uppercase.split("").reverse().join(""); return state; }, }, ], }); // Ao chamar `agent.run`, ele executa `toUpperCase` e depois `reverseText`. const output = await agent.run({ text: "parsero" }); console.log(output); // => { uppercase: "PARSERO", reversed: "ORESRAP" } ``` > Como não `CheckProcedure` ou `nextProcedure`, o fluxo é linear, executando cada procedure na ordem em que foi definida na lista. --- ### Exemplo 3: Fluxo personalizado com `nextProcedure` Caso você queira **controlar a ordem** entre procedures de modo mais explícito (sem `check`), basta utilizar a propriedade `nextProcedure` em uma `ActionProcedure`. ```ts import { z } from "zod"; import { Agent, END, State } from "@cosmixclub/parsero"; import { ChatOpenAI } from "@langchain/openai"; const agent = new Agent({ state: new State({ inputSchema: z.object({ text: z.string(), }), outputSchema: z.object({ processed: z.string(), summary: z.string(), }), }), llm: new ChatOpenAI({ model: "gpt-4", apiKey: "..." }), procedures: [ { // 1. e processa a entrada, definindo `processed`. name: "processInput", type: "action", nextProcedure: "generateSummary", async run(state, llm) { // Suponha que faça algum processamento local: state.output.processed = `Processed: ${state.input.text}`; return state; }, }, { // 2. Gera um resumo do texto processado, definindo `summary`. name: "generateSummary", type: "action", nextProcedure: END, async run(state, llm) { const response = await llm.invoke(`Resuma o seguinte texto: "${state.output.processed}"`); state.output.summary = response; return state; }, }, ], }); const output = await agent.run({ text: "Esta é uma frase de teste" }); console.log(output); // => { processed: "Processed: Esta é uma frase de teste", summary: "..." } ``` > A execução passa **explicitamente** de `"processInput"` para `"generateSummary"`. Em seguida, `"generateSummary"` define `nextProcedure: END` para indicar o fim. --- ### Exemplo 4: Utilizando múltiplos modelos para tarefas especializadas Este exemplo mostra como utilizar diferentes modelos para diferentes partes do fluxo, otimizando o custo e especialização: ```ts import { z } from "zod"; import { Agent, END, State } from "@cosmixclub/parsero"; import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; import { ChatOpenAI } from "@langchain/openai"; const agent = new Agent({ state: new State({ inputSchema: z.object({ text: z.string(), }), outputSchema: z.object({ classification: z.string(), summary: z.string(), response: z.string(), }), }), // Configuração de múltiplos modelos para diferentes funções llm: { // Modelo padrão para casos gerais default: new ChatOpenAI({ model: "gpt-4o" }), // Modelo especializado e econômico para classificação classify: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0, // Temperatura baixa para classificação precisa }), // Modelo especializado em resumos summarize: new ChatGoogleGenerativeAI({ model: "gemini-pro", temperature: 0.2, }), }, procedures: [ { name: "classifyContent", type: "action", nextProcedure: "summarizeContent", async run(state, llms) { // Usa o modelo econômico e especializado para classificação const response = await llms.classify.invoke( `Classifique o seguinte texto em uma categoria: "${state.input.text}"`, ); state.output.classification = response.toString().trim(); return state; }, }, { name: "summarizeContent", type: "action", nextProcedure: "generateFullResponse", async run(state, llms) { // Usa o modelo especializado em resumos const response = await llms.summarize.invoke( `Resuma o seguinte texto de maneira concisa: "${state.input.text}"`, ); state.output.summary = response.toString().trim(); return state; }, }, { name: "generateFullResponse", type: "action", nextProcedure: END, async run(state, llms) { // Usa o modelo principal (mais poderoso) para a resposta final const response = await llms.default.invoke(` Crie uma resposta detalhada para o texto a seguir, considerando que: - Ele foi classificado como: ${state.output.classification} - Um resumo conciso seria: ${state.output.summary} Texto original: "${state.input.text}" Sua resposta deve ser completa e considerar tanto a classificação quanto o resumo. `); state.output.response = response.toString().trim(); return state; }, }, ], }); const output = await agent.run({ text: "Um texto longo para análise..." }); console.log(output); // => { // classification: "Artigo científico", // summary: "Este texto aborda...", // response: "Análise detalhada considerando a classificação e o resumo..." // } ``` > Neste exemplo, cada procedure usa um modelo diferente otimizado para sua tarefa específica: classificação, resumo e geração de resposta completa. --- ### Exemplo 5: Combinando diferentes famílias de modelos Este exemplo demonstra como combinar diferentes famílias de modelos de linguagem para aproveitar as vantagens de cada uma: ```ts import { z } from "zod"; import { Agent, END, State } from "@cosmixclub/parsero"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; import { ChatOpenAI } from "@langchain/openai"; const agent = new Agent({ state: new State({ inputSchema: z.object({ query: z.string(), context: z.string().optional(), }), outputSchema: z.object({ queryType: z.enum(["factual", "creative", "technical"]), response: z.string(), sources: z.array(z.string()).optional(), }), }), llm: { // GPT-4o como modelo padrão default: new ChatOpenAI({ model: "gpt-4o", temperature: 0.7, }), // Claude para consultas factuais e precisas factual: new ChatAnthropic({ model: "claude-3-opus-20240229", temperature: 0.1, }), // Gemini para geração de conteúdo criativo creative: new ChatGoogleGenerativeAI({ model: "gemini-pro", temperature: 1.0, }), // Modelo econômico para roteamento router: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0, }), }, procedures: [ // Primeiro determina o tipo de consulta { name: "analyzeQuery", type: "action", nextProcedure: "routeQuery", async run(state, llms) { // Usa o modelo mais econômico para classificação const result = await llms.router.withStructuredOutput( z.object({ queryType: z .enum(["factual", "creative", "technical"]) .describe("O tipo de consulta baseado na natureza da pergunta"), explanation: z.string().describe("Explicação rápida sobre porque essa categoria foi escolhida"), }), ).invoke(` Classifique a seguinte consulta em uma das categorias: - factual: Busca por informações factuais, precisas e verificáveis - creative: Busca por conteúdo criativo, ideias, ou explorações conceituais - technical: Busca por explicações técnicas ou soluções para problemas Consulta: "${state.input.query}" `); state.output.queryType = result.queryType; return state; }, }, // Router decide qual modelo usar com base no tipo de consulta { name: "routeQuery", type: "check", async run(state, llms) { // Lógica de roteamento baseada no tipo de consulta switch (state.output.queryType) { case "factual": return "processFact"; case "creative": return "generateCreative"; case "technical": default: return "handleTechnical"; } }, }, // Processa consultas factuais com Claude (alta precisão) { name: "processFact", type: "action", nextProcedure: END, async run(state, llms) { const response = await llms.factual.invoke(` Responda a seguinte consulta factual com alta precisão. Forneça fontes ou referências quando possível. Consulta: ${state.input.query} ${state.input.context ? `Contexto adicional: ${state.input.context}` : ""} `); state.output.response = response.toString(); // Em um caso real, você poderia extrair fontes usando structured output state.output.sources = ["Conhecimento integrado do Claude"]; return state; }, }, // Gera conteúdo criativo com Gemini { name: "generateCreative", type: "action", nextProcedure: END, async run(state, llms) { const response = await llms.creative.invoke(` Crie uma resposta criativa e inspiradora para: ${state.input.query} ${state.input.context ? `Considerando este contexto: ${state.input.context}` : ""} Seja original, imaginativo e expressivo em sua resposta. `); state.output.response = response.toString(); return state; }, }, // Processa consultas técnicas com o modelo padrão (GPT-4o) { name: "handleTechnical", type: "action", nextProcedure: END, async run(state, llms) { const response = await llms.default.invoke(` Forneça uma resposta técnica detalhada e precisa para: ${state.input.query} ${state.input.context ? `Contexto adicional: ${state.input.context}` : ""} Inclua exemplos práticos quando relevante. `); state.output.response = response.toString(); return state; }, }, ], }); // Teste com diferentes tipos de consultas const factualResult = await agent.run({ query: "Qual é a distância média da Terra ao Sol?", }); // Usará o Claude para esta consulta factual const creativeResult = await agent.run({ query: "Escreva um poema sobre inteligência artificial e a natureza humana.", }); // Usará o Gemini para esta consulta criativa const technicalResult = await agent.run({ query: "Como implementar uma árvore binária de busca em JavaScript?", }); // Usará o GPT-4o para esta consulta técnica ``` > Este exemplo mostra um agente sofisticado que roteia consultas para diferentes modelos com base no tipo de pergunta, utilizando os pontos fortes de cada modelo. --- ## Observabilidade e Integração com LangSmith O Parsero possui integração nativa com o [LangSmith](https://docs.smith.langchain.com/observability), a plataforma de observabilidade da LangChain. Isso permite rastrear, inspecionar e depurar a execução dos agentes, procedures e fluxos de decisão de forma detalhada. ### Como funciona - Toda execução do agente (`agent.run(...)`) é automaticamente rastreada pelo LangSmith, incluindo cada procedure executada, entradas, saídas, erros e metadados. - Você pode customizar o rastreamento de cada procedure usando a propriedade `tracing`: - `label`: nome amigável para exibição na interface do LangSmith. - `runType`: tipo da execução (ex: "llm", "parser", "tool", etc). - `metadata`: metadados extras para facilitar a análise. - O agente também aceita metadados globais via `options.metadata`. - Para visualizar os rastreamentos, basta configurar as variáveis de ambiente do LangSmith (ex: `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT`, etc). ### Exemplo de uso com observabilidade ```ts const agent = new Agent({ // ...outros parâmetros... procedures: [ { name: "classify", type: "action", tracing: { label: "Classificação do texto", runType: "llm", metadata: { etapa: "classificação" }, }, async run(state, llm) { // ... return state; }, }, // ...outras procedures... ], options: { metadata: { projeto: "meu-agente" }, name: "Agente de exemplo", }, }); await agent.run({ text: "Exemplo" }); // A execução será rastreada e poderá ser inspecionada no painel do LangSmith ``` > Para detalhes sobre configuração, variáveis de ambiente e análise dos rastreamentos, consulte a [documentação oficial do LangSmith](https://docs.smith.langchain.com/observability). --- **Happy Coding!** > **Nota**: Exemplos e instruções podem variar de acordo com a versão utilizada do Parsero, LangChain e LangGraph. Consulte sempre a documentação oficial para detalhes de configuração e versões compatíveis.