AIARTICLE

Convex Agent Component: how native memory and RAG work for AI agents

Convex's official component bundles threads, persistent memory, and hybrid vector/text search for those building AI agents, without setting up a parallel vector DB stack.

What the Agent Component solves

Anyone building an AI agent from scratch usually ends up with a fragmented stack: a relational database for the app, a vector DB (Pinecone, Weaviate, pgvector) for semantic memory, a queue or worker to orchestrate LLM calls, and some manual way of persisting conversation history. The Agent Component, documented in the Convex Developer Hub, tries to eliminate this fragmentation by packaging threads, memory, and semantic search inside Convex itself, using the Components system (something like backend plugins with their own database and functions) that Convex already offers for other integrations.

The core idea, described by the Convex team itself in the post that motivated the component ("AI Agents with Built-in Memory"), is that message history with an LLM should behave like any other reactive Convex data: persisted by default, updated in real time across all connected clients, and composable with the rest of the business logic via code, not configuration.

Threads as the central unit

In the Agent Component's model, everything revolves around threads. A thread stores the message history and can be shared by multiple users and multiple agents, including human agents (a support rep taking over a bot's conversation, for example). This is already a different approach from agent libraries that treat the conversation as ephemeral state in process memory: here the thread is an entity persisted in Convex's database, so it can be queried, listed, and resumed from anywhere in the application.

The documentation's example shows the creation of an agent and a thread:

ts
import { Agent } from "@convex-dev/agent";
import { convexGateway } from "@convex-dev/ai-sdk-provider";

const supportAgent = new Agent(components.agent, {
  name: "Support Agent",
  languageModel: convexGateway("openai/gpt-5-mini"),
  instructions: "You are a helpful assistant.",
});

export const createThread = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }) => {
    const { threadId, thread } = await supportAgent.createThread(ctx);
    const result = await thread.generateText({ prompt });
    return { threadId, text: result.text };
  },
});

One detail worth noting: languageModel takes an AI SDK provider (here using the Convex AI Gateway with openai/gpt-5-mini as an example), which means the Agent Component doesn't lock the developer into a single model provider, it's possible to swap the provider without rewriting the thread logic.

To continue a conversation later, just call continueThread passing the threadId. The component already injects the previous history into the call's context:

ts
export const continueThread = action({
  args: { prompt: v.string(), threadId: v.string() },
  handler: async (ctx, { prompt, threadId }) => {
    const { thread } = await supportAgent.continueThread(ctx, { threadId });
    const result = await thread.generateText({ prompt });
    return result.text;
  },
});

Where memory and hybrid search come in

The part that justifies dropping a separate vector DB, at least for conversation history, is the hybrid vector/text search built into thread messages. According to the documentation, conversation context is automatically included in every LLM call, combining vector (semantic) search with text search over messages already exchanged. In practice, this solves the classic context window problem: instead of sending the entire conversation (which gets expensive and eventually exceeds the token limit) or naively truncating to the last N messages (which loses relevant older context), the Agent Component can retrieve the most semantically relevant excerpts, with no extra indexing code.

Here it's important to separate two levels of RAG that Convex's documentation treats as distinct things, and this distinction is the point that causes the most confusion:

  • Conversation memory: hybrid search over the thread's own messages, built into the Agent Component with no additional configuration.
  • RAG over an external knowledge base (documents, PDFs, an FAQ base): for this, Convex points to a separate component, the RAG Component, which can be used either to augment the prompt beforehand or as a tool call the agent decides to trigger during the conversation.

In other words, "native RAG" in the Agent Component covers conversational memory well, but document RAG still requires attaching another Convex component, the RAG Component, which reduces but doesn't eliminate the extra piece of infrastructure. The advantage is that both pieces run inside the same Convex backend, with the same reactive database system, instead of needing to integrate a third-party service with its own account, SDK, and network latency.

What comes along: workflows, playground, billing

The Agent Component isn't just threads and memory, it brings a set of pieces designed for those putting this into production:

  • Workflows: for multi-step operations that span multiple agents and users, with durable execution guarantees (if the process fails midway, it resumes at the right point).
  • Files in history: messages can include files, with automatic saving to Convex's file storage, without the developer managing upload and reference manually.
  • Agent Playground: an interface for inspecting each call's metadata and iterating on prompts and context settings without needing to redeploy code for every adjustment.
  • Usage tracking and rate limiting: per-user/team usage counters (for billing) and call rate control, to avoid exceeding the LLM provider's limit.

This bundle is the component's selling point: instead of integrating five different libraries for each of these functions, everything already talks to the same reactive Convex database.

When it's worth it and when it isn't

The natural path to test this would be starting from a project already running on Convex and adding the @convex-dev/agent package for a simple support case, threads per user, without touching document RAG yet, and only afterward plugging in the RAG Component for an FAQ base. This validates conversational memory before adding the complexity of searching over external documents.

But there are situations where this choice costs more than it helps. If the application already runs on another backend (Postgres, Django, a Python API) and the idea is just to "bolt on" an agent, importing all of Convex as a database and function layer is a much bigger infrastructure decision than adding a LangChain client library or plain AI SDK pointing to a standalone vector DB. The Agent Component assumes that the agent's functions (the actions) live within Convex's function model, in TypeScript. It's also worth considering the cost model: vector search and message storage at scale run on Convex's usage-based pricing, so teams with high conversation volume should simulate this cost before assuming it comes out cheaper than a homegrown stack with a dedicated vector DB, a point the documentation doesn't detail and that each team needs to measure for its own case.

For teams that already use Convex as their main BaaS and want to add agents without multiplying dependencies, the component solves the most tedious part (thread persistence, automatic context, hybrid search) with little code, as shown by the createThread/continueThread example in the documentation itself. For those not using Convex, the gain doesn't justify migrating just for this.

Translated from the Brazilian Portuguese original · Read the original

View profile →