Knowledge & Retrieval
@kuralle-agents/rag provides everything between a raw document and a grounded
agent answer: chunkers, embedders, vector stores, retrievers, rerankers, and a
pipeline that wires them together. This guide covers the retrieval
architecture; the full type-level reference lives in the
@kuralle-agents/rag API docs and the package’s
guides/PRIMITIVES.md.
The two retrieval tiers
Section titled “The two retrieval tiers”Production knowledge bases need two complementary tiers:
- Vector (semantic) tier — embedding similarity. Right for conceptual, paraphrased questions (“can I send it back if it broke?”).
- Keyword (lexical) tier — BM25-ranked exact-term matching. Right for SKUs, order codes, names, and exact phrases, which embedding similarity fuzzes over. It is also ~10× cheaper in returned tokens for exact-term queries.
FusionRetriever runs both in parallel and fuses the scores:
import { FusionRetriever, BM25Index } from '@kuralle-agents/rag';
const retriever = new FusionRetriever({ keywordIndex: new BM25Index(), vectorStore, embedder, indexName: 'docs', bm25Weight: 0.3, // 70% vector, 30% keyword});Attach any retriever to an agent with createVectorRetrievalTool from
@kuralle-agents/tools. The tool descriptions ship with tier guidance — the
model is steered to grep/find for exact terms before paying for semantic
search.
The keyword tier: KeywordIndex
Section titled “The keyword tier: KeywordIndex”Two implementations of the same contract:
BM25Index— in-memory, zero dependencies, runs everywhere. Rebuilt per process.Fts5KeywordIndex— persistent, over SQLite FTS5. On Cloudflare, Durable Object SQLite supports FTS5, so the keyword tier survives hibernation with zero rebuild; on Node/Bun, back it withbun:sqliteorbetter-sqlite3.
import { Fts5KeywordIndex } from '@kuralle-agents/rag';import { createSqlExecutor } from '@kuralle-agents/cf-agent';
const keywordIndex = new Fts5KeywordIndex({ sql: createSqlExecutor(ctx.storage.sql), // Durable Object SQLite});Incremental ingestion and the embedder lock
Section titled “Incremental ingestion and the embedder lock”Give RagPipeline a persistent IngestManifest and ingestion becomes safe
and incremental:
import { RagPipeline, SqlIngestManifest } from '@kuralle-agents/rag';
const pipeline = new RagPipeline({ embedder, vectorStore, chunker, indexName: 'docs', manifest: new SqlIngestManifest({ sql }), keywordIndex, // kept in sync at ingest});- Embedder lock — the manifest records the model identity that built the index. Ingesting or querying with a different model — even one with the same dimension — throws instead of silently corrupting relevance.
- Hash-skip — unchanged documents (SHA-256) are skipped: a stable corpus re-ingests with zero embed calls.
- Stale-chunk cleanup — changed documents have their old chunks removed from the vector store and keyword index.
Cloudflare: Workers AI + Vectorize
Section titled “Cloudflare: Workers AI + Vectorize”On Workers, pair Vectorize with Workers AI embeddings — the model runs
inside Cloudflare’s network (env.AI binding), with no provider key and no
public-internet round trip per query (measured ~2× faster per query embedding
than a cloud embedding API):
import { CloudflareVectorizeStore } from '@kuralle-agents/vectorize-store';import { AiSdkEmbedder } from '@kuralle-agents/rag';import { createWorkersAI } from 'workers-ai-provider';
const workersai = createWorkersAI({ binding: env.AI });const embedder = new AiSdkEmbedder({ model: workersai.textEmbeddingModel('@cf/baai/bge-m3'),});const vectorStore = new CloudflareVectorizeStore({ index: env.VECTORIZE });KnowledgeFs: the knowledge base as a filesystem
Section titled “KnowledgeFs: the knowledge base as a filesystem”KnowledgeFs exposes an indexed knowledge base as a read-only FileSystem,
so agents explore it with the same workspace tool verbs (ls, cat,
grep, find) used for bundled docs — the cheap structural tier before any
semantic search. grep is BM25-ranked when a keywordIndex is configured,
and a pre-populated persistent index lets a hibernated Durable Object wake
without reseeding. See the package’s guides/KNOWLEDGEFS.md for the metadata
contract and RBAC filters.
Quality tracking
Section titled “Quality tracking”RetrievalQualityChecker.assess() buckets retrieval quality from the score
distribution and reports estimatedTokens — the prompt-token cost of the
result set — so a retriever that is “accurate” only by flooding the context
window shows up in metrics.