Skip to content

Querying ​

Ask questions with POST /query. The response is an answer grounded strictly in the tenant's data, plus the source chunks it used.

Basic query ​

bash
curl -s localhost:8000/query -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"question": "What does the safety manual say about work at height?"}'

The response includes the answer and a sources array of cited chunks with their retrieval scores.

Optional fields ​

FieldPurpose
historyPrior turns, for multi-turn follow-ups (see below)
collectionRestrict retrieval to a single collection
source_typesRestrict retrieval to certain source types

Citations ​

Every answer carries the chunks it was built from, each with a relevance score, so answers are auditable back to their source. The built-in chat UI renders these under each answer.

Multi-turn follow-ups ​

Pass previous turns as history and Evo.ai condenses the follow-up plus the history into a standalone question before retrieving — so "what about at night?" resolves against the earlier subject rather than retrieving on its own.

The relevance gate ​

Before any LLM call, Evo.ai checks the best retrieval score against RELEVANCE_THRESHOLD (default 0.55). If nothing clears the bar — off-topic questions, general knowledge, prompt-injection attempts — the request is refused without calling the model, and the response carries gated: true with the configured gated answer.

This keeps the assistant honest: it answers from the workspace's records or says they don't cover the question. See Configuration for tuning the threshold and the wording.

The chat UI ​

A ready-to-use chat interface is served at /ui. It talks to /query, supports multi-turn conversations, and shows the source chunks (with scores) behind each answer. It's the quickest way to try retrieval against a tenant's data without writing a client.

Structured analytics answers ​

For evo.ehs tenants, aggregate questions ("how many permits are open?", "average time from reported to closed?") are answered by a validated read-only SQL query rather than chunk retrieval, and appear in sources tagged source_type: "analytics". See Connectors for how that layer works and its guardrails.

The views resolve people to their display names, so an answer reads "assigned to Kara Errands" rather than leaking a raw address. Each view that stores an assignee also exposes assigned_to_email for matching and filtering — the name is what gets rendered, the address is what gets compared.

When an answer says the records don't cover it ​

Two different things produce that response, and they are worth telling apart when a question you expected to work is refused:

  • The relevance gate fired — nothing retrieved cleared the threshold, and gated: true comes back.
  • The data isn't indexed. Anything a view or connector doesn't expose is invisible to the assistant, even when it exists in the source system.

The second is the more common surprise, and it is a coverage question rather than a tuning one — the fix is exposing the column or the source, not lowering the threshold.

When no model is configured ​

If a tenant has no LLM configuration of its own and isn't permitted to use the server default, /query answers with unconfigured: true and an explanation that an administrator needs to add an API key. This is deliberate: see DEFAULT_LLM_ALLOWED_TENANTS in Configuration for why a shared deployment should fail closed rather than silently bill the operator's provider account.

Documentation hub for Evomedia.net LLC products.