npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@cosmixclub/parsero

v1.2.0

Published

Construtor de agentes de IA de maneira simplificada.

Downloads

10

Readme

Parsero

Uma biblioteca simples e fortemente tipada para criar agentes de IA com LangChain e LangGraph.
Inspirada no CrewAI e criada para ser mais descritiva e direta, com padrões bem-definidos para a criação de agentes reutilizáveis.

Sumário


Objetivo

O Parsero foi desenvolvido para simplificar a criação de agentes de IA utilizando LangChain e LangGraph. 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 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 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:

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:

// 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.

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:

// 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:

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:

// 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).
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.

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 já 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 há 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.

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. Lê 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:

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:

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, 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

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.


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.