App dev: RAG over governed knowledge
The problem
Most RAG stacks are a vector database bolted next to the system of record: embeddings live in one place, access control lives somewhere else (if it exists at all), and by the time a document is chunked and embedded, its provenance and permissions are usually lost. That's fine for a demo. It's not fine for an agent answering questions over data that has row-level access rules, or for a product that needs to say why it gave the answer it gave.
The scenario
A developer is building an internal assistant that answers questions over a governed knowledge base — policy documents, case notes, whatever the tenant has ingested — and needs every answer to (a) respect the same ACL that protects the underlying rows, (b) come back with a source citation, and (c) persist what the agent learns across sessions, not just within one conversation.
Retrieve with governance built in, not bolted on
HYBRID_SEARCH (aliased as RAG_RETRIEVE for a RAG-shaped surface) fuses BM25 and vector similarity with reciprocal-rank fusion, runs through the same ACL and cell-masking path as every other query, and returns results a caller can cite.
PURPOSE 'investigation'
RAG_RETRIEVE FROM Document QUERY 'what were the Q3 findings on vendor risk' LIMIT 10Typed SDK snippet
from relata import RelataClient, SearchBuilder, Memory
with RelataClient(
"http://localhost:9090",
bearer_token="relata-dev",
purpose="product_research",
) as client:
# Governed retrieval: BM25 + vector, ACL-filtered before results are returned
results = client.search(
SearchBuilder("Q3 findings on vendor risk")
.types(["Document"])
.limit(10)
.highlight(True)
)
context = "\n\n".join(
f"[{hit.id}] {hit.highlight or ''}" for hit in results.hits
)
# ... pass `context` to your LLM call; each chunk carries its own
# source id, so the model's answer can cite exactly what it used ...
# Give the agent memory that persists across sessions
with Memory("http://localhost:9090", purpose="agent-notes") as mem:
mid = mem.add("User's team owns vendor-risk review for Q3")
recalled = mem.search("who owns vendor risk", top_k=3)
# recalled entries are re-ranked by confidence x recency, not just similarityWhy this is more than "add a vector column"
- Retrieval and access control are the same query.
HYBRID_SEARCH/RAG_RETRIEVEruns through the same ACL-aware path as any other read — a principal never gets a chunk back that a directSELECTon the same row would have denied. See Hybrid Search for the pre-filter vs. post-filter strategy that keeps this fast even for narrow principals. - Every result is citable. Hits carry
id,score, andhighlight— an agent's answer can point back to the exact source row, not a black-box embedding. - Three signals, not one. BM25 catches exact jargon and account numbers an embedding blurs past; vector similarity catches paraphrase; identity matching catches the same entity referenced under a different surface form. Fused with RRF, not manually weighted.
- Memory is rows in the same governed store, not a separate cache. The
Memoryclient'sadd/search/forgetverbs are governed writes and reads — bi-temporal, purpose-scoped, and erasable — not an ungoverned side-channel next to your real database. See Agent Memory.
See also
- Hybrid Search —
HYBRID_SEARCH,RAG_RETRIEVE,RERANK, and the BM25 + HNSW/DiskANN internals - Agent Memory — the
remember / recall / forget / justifyverb surface - Governance — ACL-aware pre-filtering and why retrieval can't bypass it
- Python SDK quickstart — full
RelataClient,SearchBuilder, andMemorysetup