Ground an AI assistant in your records (a RAG recipe)

Engineering

Ground an AI assistant in your records (a RAG recipe)

FastYoke Engineering · 8 min read · Aug 11, 2026

  • Tutorial
  • AI
  • RAG

Most AI assistants answer from the open internet. That's fine for trivia and useless for "which of my customers are overdue?" — a question whose answer lives in your records, not in a model's training data. Yoker, FastYoke's built-in assistant, flips that around: it retrieves from your tenant's own entity records and text attachments, then answers with references back to the rows it used. The embeddings that power retrieval are computed locally on your VM; when you ask a question, only the specific records that match are sent to the language model as grounding context — nothing else in your corpus leaves the box.

This recipe walks the full loop: turn the assistant on, seed its corpus, scope what it can read, ask a question, and read the citations that prove the answer came from your data.

What you'll build

By the end you'll have Yoker answering plain-English questions grounded in one of your entities — say, your Customer records — with each answer accompanied by the specific chunks it retrieved. Ask "which customers are overdue?" and you get a synthesized reply plus a list of the exact source records that backed it, so you can click through and verify. No hallucinated accounts, no data borrowed from someone else's tenant.

Before you start

  • A tier or add-on that includes Yoker. Yoker is included with Enterprise Platform. On Pay-as-you-go it's available as the paid yoker add-on ($299/mo — check the pricing page for current rates). The Free plan can't enable it. Without an active entitlement, every Yoker call fails closed with a 403 Forbidden explaining that Yoker requires Enterprise Platform or the paid add-on.
  • An app with some entity records. Yoker retrieves over your entity records and their text attachments, so you need at least one entity (a Customer, Order, Ticket — whatever your app models) with a handful of rows worth querying.
  • A tenant-admin session. Enabling the add-on and running a backfill are admin actions; the API endpoints require a tenant-scoped admin token.

Steps

1. Enable the assistant

If you're on Enterprise Platform, Yoker is already on — skip to the dock. On Pay-as-you-go, activate the yoker add-on from your billing settings. Once the entitlement is active, Yoker appears in the right-toolbar dock, sharing that surface with Messaging on a separate tab. The dock is persistent: your conversation stays open as you navigate between pages.

Two things to internalize before you type a question. First, the corpus starts empty — enabling the entitlement doesn't retroactively index anything. Second, conversations are ephemeral: the client holds your chat during a session and the server forgets each turn after answering, so there's no server-side history to leak or subpoena.

2. Seed the corpus with a backfill

Because the corpus is empty on day one, your first move is a one-time backfill that walks every existing entity record and every text attachment, embeds them, and stores the vectors:

POST /api/v1/tenant/rag/backfill
Authorization: Bearer <tenant admin JWT>
{ "embedded": 142, "skipped": 25 }

The backfill is idempotent — it fingerprints each chunk by content hash, so running it twice won't re-embed anything unchanged (that's the skipped count). Run it after enabling Yoker, and again after any bulk import. Day-to-day you won't call it: once seeded, write-through keeps the corpus current automatically — every entity create or update re-embeds that record within seconds, and attachment uploads and deletes are reflected the same way.

3. Understand what gets indexed

Yoker indexes two things today, and it's worth knowing exactly what:

  • Entity records. Each record's data payload is rendered to text, split into overlapping windows (roughly 1,500 characters with 200 characters of overlap so a sentence spanning a boundary isn't lost), and embedded.
  • Text attachments. Files uploaded to an entity with a text/*, CSV, or Markdown MIME type are parsed and indexed, with the source filename prefixed for context. PDF, Word, and Excel attachments are not parsed yet — if the answer lives inside a PDF, Yoker can't see it today.

Everything lands in the tenant-scoped rag_chunks table. That table is subject to the same tenant-isolation rule as the rest of your data, which is the whole reason retrieval can't cross tenants (more on that below).

4. Ask a question

Open the Yoker tab in the dock and ask in plain language — "which customers are overdue?", "summarize the open tickets tagged urgent", "what's the shipping address on order 4021?". The same thing is available over the API if you're building on top of it:

POST /api/v1/tenant/ai/assistant/ask
Content-Type: application/json
Authorization: Bearer <tenant JWT>
{ "message": "Which customers are overdue?" }

Under the hood, Yoker embeds your question with the same on-VM model it used to index your records, ranks stored chunks by cosine similarity, takes the top matches above a relevance floor, and hands only those chunks to the language model as grounding context. The reply comes back as a single complete message — there's no token-by-token streaming yet.

5. Read the citations

This is the step that separates a grounded assistant from a confident guess. The response doesn't just contain an answer — it reports the source records that backed it:

{
  "answer": "Three customers are currently overdue: …",
  "used_sources": ["crm_company_acme", "crm_company_globex"],
  "tools_invoked": ["crm_customers"],
  "grounded": true
}

used_sources lists the exact source records Yoker placed in the model's grounding context — the ids you can click through to verify the answer came from your data — and grounded tells you whether the reply was backed by retrieved records at all. If Yoker's answer surprises you, used_sources tells you which records it read. And if retrieval finds nothing above the floor, Yoker does not fall back to the model's general knowledge — it returns a grounded: false refusal ("I do not have any tenant records matching that question.") instead of inventing an answer, which is the honest failure mode you want.

Take it further

Scope retrieval to the entity that matters. The retrieval layer can filter to a single entity type, so a question about customers pulls only from Customer records instead of ranking across your whole corpus. Narrowing the scope both sharpens relevance and keeps the grounding tight — useful when one noisy entity would otherwise dominate the top-k.

Keep the corpus honest at scale. Write-through means you rarely re-backfill, but after a heavy data-migration import it's worth one more idempotent backfill pass to catch anything that landed outside the normal create/update path.

Trust the isolation, then verify it. Because rag_chunks is tenant-scoped and retrieval always filters by the caller's tenant, one organization's assistant physically cannot retrieve another's records — the same prime directive that governs every query on the platform. If you want the mechanics of how that boundary is enforced, the type system does a lot of the heavy lifting; see the related reading below.