Ir al contenido

Mastra — el agéntico

Mastra es el más nuevo de los 4 y el único que no es primariamente un framework de RAG. Su superficie principal es agentes: LLMs equipados con tools, memoria, y workflows con state. El RAG es un componente — específicamente, un vectorQueryTool que el agent puede decidir invocar.

La filosofía declarada: “construyas chatbots y workflows con tools, no pipelines lineales”. Para casos donde el RAG es uno de varios capabilities (búsqueda en base de docs + búsqueda web + APIs propias + memoria de la conversación), Mastra te ahorra la integración manual.

Trade-off: para un RAG vanilla (Naive RAG sin agentes), Mastra es over-engineering. Te hace pasar por el agent + tool model aunque tu caso no lo necesite.

packages/04-mastra/src/query.ts expone dos paths que muestran exactamente este trade-off:

04-mastra/query.ts: modo Naive
async function directRetrievalQuery(q: string): Promise<void> {
const { embedding: queryVector } = await embedV1({ model: embeddingModel, value: q });
const results = await store.query({
indexName: COLLECTION,
queryVector,
topK: TOP_K,
});
const context = results.map((r, i) => /* ... */).join('\n\n');
const prompt = `You are a helpful assistant. Answer the question using ONLY the provided context.\n\nContext:\n${context}\n\nQuestion: ${q}\n\nAnswer:`;
const response = await fetch(`${OLLAMA_URL}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: OLLAMA_LLM, prompt, stream: false }),
});
const data = await response.json();
console.log('\nAnswer:', data.response);
}

Este path es estructuralmente idéntico al de Vercel AI SDK. Embed con la primitiva embedV1 de Mastra (delgada wrap sobre ollama-ai-provider), retrieve con QdrantVector.query, y generate via raw fetch a Ollama. Es lo que el harness mide porque expone los contextos crudos de manera comparable a los otros 3 frameworks.

04-mastra/query.ts: modo Agentic
async function agentQuery(q: string): Promise<void> {
const vectorQueryTool = createVectorQueryTool({
vectorStore: store,
indexName: COLLECTION,
model: embeddingModel,
includeSources: true,
});
const ragAgent = new Agent({
id: 'rag-agent',
name: 'RAG Agent',
instructions:
'You are a helpful assistant. Use the provided retrieval tool to find relevant information before answering. Always cite your sources.',
model: llm,
tools: { vectorQueryTool },
});
const result = await ragAgent.generate(q);
console.log('\nAnswer:', result.text);
}

Este es el path idiomático Mastra. El agent recibe la pregunta, decide invocar el vectorQueryTool, mira los resultados, eventualmente llama el tool de nuevo si necesita más info, y consolida la respuesta. Más expresivo pero opaco — los contextos se pasan al LLM dentro de agent.generate, sin exposición uniforme.

Tres razones:

  1. Comparabilidad cross-framework: Vercel, LangChain y LlamaIndex ejecutan un pipeline lineal. Para que las métricas sean comparables, Mastra tiene que correr el mismo shape — Naive RAG.
  2. Determinismo: el agent puede decidir hacer 1, 2 o 5 retrieves según vea necesario. Eso introduce variance que rompe el setup de “1 query → 1 retrieve → 1 generate”.
  3. Exposición de contextos: las métricas retrieval@k y faithfulness necesitan los chunks crudos que el LLM vio. El modo Agent los empaqueta dentro del state del agent y no los expone uniformemente.

Esto NO significa que Mastra sea peor. Significa que Mastra está optimizado para un caso (agents) que es difícil de comparar contra pipelines lineales. Para ese caso específico, Mastra brilla y los otros 3 frameworks requieren más boilerplate.

B — mejor que LangChain/LlamaIndex (ambos C), peor que Vercel (A). Tipos relativamente estrictos en la surface, pero hay accesos por string a metadata:

tipos en Mastra
const text = (r.metadata?.['text'] as string | undefined) ?? '';
const filename = (r.metadata?.['filename'] as string | undefined) ?? 'unknown';

El cast a string | undefined es explícito y aceptable. Para la mayoría del codebase, los tipos son limpios.

  • Casos donde RAG es uno de varios tools: el agent decide cuándo usar RAG, cuándo usar otra fuente, cuándo combinar. Mastra se siente natural ahí.
  • Workflows multi-paso con state: si tu chatbot tiene flujos como “preguntá X → si la respuesta no convence, preguntá Y → al final, resumí” — eso encaja en el modelo de Mastra.
  • Equipos que ya construyen con tools/agents: si vienen de OpenAI Assistants o de Anthropic Claude with tools, Mastra es el match más directo en el ecosistema TS.
  • Memoria persistente: Mastra tiene primitivas de memory (Memory class) integradas. En los otros frameworks tenés que armarlo o agregar piezas extras.
  • Para un RAG vanilla: como vimos, el modo Naive existe pero no es lo idiomático. Estás luchando contra el framework si tu caso es lineal.
  • Edad del proyecto: el más joven de los 4. APIs cambian, breaking changes son frecuentes. Pinea versiones y leé changelogs antes de bumps.
  • Documentación: cubre los casos comunes pero gaps en casos avanzados o edge.
  • Compatibilidad de model providers: vimos arriba que el modo Agent puede romperse por desfasaje de versiones del SDK. La sensibilidad a versiones cuesta cuando algo deja de andar.

LOC mínimo del query (path Naive): 13 líneas. Latencia base (Naive): ~2400 ms. El más conciso de los 4 — pero con la advertencia de que las 13 líneas no son representativas: si tu caso requiere el modo Agent, el código se duplica.

Hasta acá fue análisis. Ahora corremos la misma pregunta en los 4 frameworks y leemos los outputs lado a lado. Los chunks recuperados, las respuestas, las latencias — todas las diferencias visibles en una sola corrida.