Architecture

The Nia Advisor

Retrieval-grounded answering over a fund's knowledge: retrieval, grounding, refusals, citations, and conversation persistence.

The Nia Advisor answers questions using only the knowledge indexed for a tenant. It retrieves the most relevant chunks, grounds an answer in them with citations, refuses when the corpus doesn't support an answer, and saves the exchange as a conversation. It is implemented in @repo/advisor, with retrieval in @repo/rag and model access in @repo/ai.

What it does for users

  • Grounded answers. Responses come from the fund's indexed documents and the shared global playbook, not from the model's general knowledge.
  • Citations. Answers reference the source chunks they used.
  • Honest refusals. If the knowledge base doesn't contain the answer, the advisor says so and points the user to the Nia team instead of guessing.
  • Knowledge-gap signals. When an answer leans on thin or weak organization context, the turn is flagged so operators can see where the corpus needs work.
  • Conversations. Each thread is saved with its messages and citations, so users can return to earlier answers and operators can review them.

Answer flow

Embed the user's question into a query vector.
Retrieve the most relevant chunks for the tenant (see scope below).
Decide whether retrieval is strong enough to answer. If not, return a refusal.
Ground a prompt with the retrieved context and recent conversation history.
Generate the answer, then attach citations and any knowledge-gap flag.
Persist the user and assistant messages, and record usage for cost and quota.

Retrieval scope

Retrieval is tenant- and plan-aware (@repo/rag):

PlanCorpus searched
bot_onlyThe global playbook only.
policy_hub, fullThe organization's own documents plus the global playbook.

Searches run as approximate-nearest-neighbour queries against the HNSW vector index, filtered to indexed, non-superseded documents in the allowed scope. Only chunks the tenant is permitted to see are considered.

Grounding and refusals

The advisor refuses rather than answering beyond the evidence. A turn is refused when retrieval returns nothing relevant enough, measured by a minimum relevance threshold and a minimum count of sufficiently relevant chunks. The grounding prompt also tells the model to reply with a fixed refusal message when the context doesn't contain the answer, and the system normalizes any model output that drifts toward a refusal into that exact message. Refused assistant messages are stored with refused = true, which feeds the operator Knowledge Gaps view.

Knowledge-gap detection

Beyond outright refusals, the retrieval layer flags a turn as a knowledge gap when the organization's own context is thin, or noticeably weaker than the global playbook for the question. When that happens, global content is favoured in ranking and the turn carries a gap flag. This shows which tenants and topics need a stronger org corpus.

Conversation persistence

Conversations and messages are stored per organization:

  • A conversation records the channel (web or whatsapp), the owning user (for web), a title, and a retention horizon.
  • Each message stores its role, content, citations, refusal flag, the model used, and token counts.

Web conversations are owner-scoped, so a user can only open their own threads, while platform operators can review any conversation through the Command Center. Recent prior messages are included as context on each new turn so multi-turn threads stay coherent.

Usage and cost

Every advisor turn writes an aiUsageLog entry (organization, user, channel, kind = 'advisor', model, tokens in/out) and increments the organization's monthly usageCounter. The usage-cap middleware checks the counter against the plan's run cap before spending, so a tenant can't exceed its plan. Operators see this rolled up under Intelligence.

On this page