The native RAG is part of thenative AI integrated into the Aurabasebackend, alongside NL2SQL: not a third-party service to be assembled on top of a general base. Prerequisites to follow this tutorial: an existing Aurabase project, a configured LLM provider (OpenAI or Google Gemini for embeddings, one of the three native providers for generation), and a project API key.
- An Aurabase RAG pipeline consists of two calls:
ragIngest()to index a document,rag()to query and generate a response. Chunking, embeddings and vector search are managed on the server side. - Under the hood, it's standard PostgreSQL and pgvector: a
embeddingstable with one vector column per dimension class (768, 1536, 3072) and a partial HNSW index per class. - Chunking uses a real tokenizer (
tiktokeno200k), with configurable overlap and an anti-explosion guard on large documents. - The retrieved content is neutralized before being injected into the prompt: a document that attempts to escape its tag to spoof system instructions is explicitly defused.
- Only OpenAI and Gemini generate embeddings on the Aurabase side. Anthropic/Claude does not have a public embeddings API, it remains reserved for generating the final response.
How this RAG pipeline works
The pipeline takes place in five stages. Upon ingestion, the text is cut up, each chunk is vectorized in batch then stored. Upon querying, the question is vectorized in turn, compared to the chunks stored by cosine similarity, and the closest snippets are injected into the prompt sent to the generation model.
Chunking (tiktoken)→Embeddings (batch)→pgvector storage (HNSW)→Similarity search→Augmented generation
An architectural detail that matters in production: no Postgres connection is held during network calls to the embedding or generation provider. The DB transaction closes before the external call and reopens after, to never block a shared PgBouncer backend for third-party network latency.
Create the project
Unlike a typical self-hosted pgvector, you don't need to run CREATE EXTENSION vector or create a table yourself for this pipeline. The project's embeddings schema, with its vector columns and HNSW indexes, is provisioned automatically when the project is created.
The embedding provider is configured once, on the project side (Studio → IA → Suppliers). It is this provider which determines the effective dimension of your vectors, therefore the column used on the embeddingstable.
Index your documents with ragIngest()
One call is enough to index a document: the text is divided into chunks, each chunk is vectorized, then stored in the requested namespace. The splitting uses a real tokenizer (tiktoken, encoding o200k_base), not a simple splitting by spaces, which remains correct on text without spaces like some Asian languages.
In raw HTTP, the equivalent route is POST /v1/ai/{project_id}/rag/ingest, authenticated by the project's API key.
Ingestion is idempotent by default: a document identifier is derived automatically (SHA-256 hash of the content, or metadata.document_id if you provide it). Reingesting the same content replaces its existing chunks instead of duplicating them, making a periodic sync job safe to replay.
What actually lands in Postgres
No proprietary magic here: the table that receives your vectors is an ordinary Postgres table, with one vector column per dimension class and a partial HNSW index per column (active only on the rows that populate it). Here is its real definition, simplified:
Each search compares the vectors with the cosine distance operator (<=>), the one targeted by the index's vector_cosine_ops and halfvec_cosine_ops operator classes. For details of the recall/latency trade-offs of HNSW versus IVFFlat, see the article dedicated to HNSW indexing. For the choice of the embedding dimension itself, see the comparison 768 vs 1536 vs 3072.
Query and generate response with rag()
On the query side, rag() chains the vectorization of the question, the search by similarity in the namespace, the construction of the augmented prompt and the call to the generation model, in a single network round trip on the client side.
The response carries the generated response and its sources, with the provider actually used:
The retrieved content is never injected raw into the system prompt. This is unreliable data (user upload, indexed page): a document which contains, for example, a closing section tag followed by false instructions is neutralized before assembly, its angle brackets replaced by brackets, text preserved but structure defused.
If rag() finds nothing even though your namespace is not empty, the response carries an explicit warning rather than a misleading silence: your corpus is probably indexed under another model or another embedding dimension. Reindex it via POST /v1/ai/{project_id}/rag/{namespace}/reindex.
From LangChain and a handmade pgvector: what changes
If you've already built a RAG chatbot on PostgreSQL with LangChain, each manual brick has a server-side managed equivalent here, without changing the underlying database.
| Breaking up the text | RecursiveCharacterTextSplitter to set yourself | ragIngest(): integrated tiktoken chunking, 512 tokens / 64 overlap by default |
|---|---|---|
| Embeddings | Manual call to OpenAIEmbeddings, management of batch limits | Automatically batched, built-in transient retry |
| Vector storage | pgvector table + HNSW index to create and migrate yourself | Schema and indexes provisioned per project |
| Search + prompt | PGVector.similarity_search() then manually assembling the prompt | rag(): search and augmented generation in one call |
| Recovered content | Injected as is in the prompt | Automatic neutralization of structure tags before assembly |
Are you migrating an existing project built on Supabase with this kind of manual assembly? The migration logic for the rest of the backend (schema, RLS policies, SDK) is covered in our Supabase to Aurabase migration guide.
Settings and limits to be aware of before going into production
Three settings directly affect cost and latency, all verified in the aura-aiservice code. The number of attempts on a transient embedding call (network failure, provider error 429) is 3 by default, with a base backoff of 100 ms. Large documents are embedded in sub-batches limited to 2048 chunks by default, to respect the suppliers' API ceilings without failing on a single massive document. A guardrail (10,000 chunks by default, configurable) explicitly rejects the ingestion of a document that would produce an aberrant number of chunks.
On the search side, the size of the HNSW candidate list automatically adjusts to max(64, top_k × 4): the more results you request, the more candidates the index explores to preserve recall. A fixed value remains possible via an environment variable if your corpus has a particular profile.
The vector spaces of different models are never compared with each other: each search remains limited to the current embedding model, and changing models requires an explicit reindexing rather than a silent switch which would break the consistency of the results.
Current limits to be aware of
The embedding dimension must fall into one of three supported classes: 768, 1536, or 3072. A provider that returns another dimension is rejected with an explicit error, never truncated or silently cast.
Embedding generation is only available via OpenAI or Google Gemini among the three native providers: Anthropic/Claude does not expose a public embeddings API, so it is only used for generating the final response in this pipeline, never for vectorization.
The JavaScript SDK does not yet expose the top_k and threshold overrides on the rag() call: they remain accessible in direct HTTP, limited respectively to [1, 50] and [0, 1], but not from aura.ai.rag() as is today. The default similarity threshold (0.3) is deliberately permissive; narrow it down to a dense corpus to avoid irrelevant sources in the prompt.
To go further
The RAG covers questions on unstructured content (documents, notes, tickets). For questions about your relational data, Aurabase's native NL2SQL directly translates a question into validated SQL. For details of the HNSW parameters and dimension classes mentioned above, see the article on HNSW indexing and the comparison of embedding dimensions. The full API reference remains the RAG & pgvector documentation and the AI Gateway documentation.