Architecture — Copilot and LLM
DATTA's AI is not a separate screen: it is embedded where the work happens — suggesting the formula while you edit a chart, answering questions about the dashboard you have open, writing the report narrative, pointing out what deserves attention before you ask, and processing entire columns inside your pipelines. This page describes how those four surfaces are assembled, what each one sends to the model, where the cache sits and which limits protect the platform.
1. The pieces
| Role | What it does |
|---|---|
| Conversation proxy | Streams the answer (SSE) between the frontend and the model, builds the context, manages the session and dispatches tool calls |
| Model management | Model catalog (Qwen3, DeepSeek, the active model, Gemini as the alternative), health checks and routing |
| Model access layer | Unifies access to the local vLLM and to the cloud alternative behind an OpenAI-style API |
| Dashboard orchestration | Builds the panel's context, applies the usage limit, generates the narrative and produces the proactive suggestions |
| Vector generation | Local embeddings with the active model, used by RAG |
| Prompt repository | Prompts versioned by purpose and by context |
When each surface comes in
| Situation | Surface | What it sees |
|---|---|---|
| I am editing a chart and cannot recall the measure syntax | DATTAX.AI bar | The chart's current DATTAX and the dataset schema |
| The dashboard is published and I want to understand a number | Dashboard copilot ("Perguntar ao dashboard") | The data actually rendered in that panel's charts |
| I want a question answered from the ingested documents | The excerpts retrieved by semantic search in the indices | |
| I need to classify or extract fields from thousands of rows | DATTAX AI primitives | The column processed in batches, inside the pipeline |
2. Edit-time copilot (DATTAX.AI bar)
The analyst edits a chart, types "add percentage vs previous year" in the bar, and the platform:
- Builds the prompt with the chart's current DATTAX, the dataset schema and the described intent.
- Forwards it to model routing, which resolves to the local vLLM.
- Receives the suggested DATTAX.
- Shows the difference side by side — the user is the one who accepts or rejects.
The system prompt is blunt: "Você é um assistente DATTAX. Retorne SOMENTE DATTAX válido. Não inclua markdown." ("You are a DATTAX assistant. Return ONLY valid DATTAX. Do not include markdown."), followed by the schema context.
The design foresees validating the output before it reaches the screen, failing with a message in Portuguese when the model returns something invalid. That validator is not implemented yet — it is design intent recorded for phase F10. Today the effective protection is the diff itself: nothing enters the chart without the user's explicit acceptance.
3. Dashboard copilot ("Perguntar ao dashboard")
The flow
- The user opens a published dashboard and the copilot appears in the top bar.
- They type the question — "Which region grew the most in Q1?".
- Before anything else, the platform validates access: an IDOR guard denies the question if the user is not the dashboard's owner, has no share and presents no valid public token.
- The usage limit is consumed: 20 questions per minute and 200 per hour, per user.
- The context is assembled: for each chart, the platform takes the last cached render (
datta:dattabi:chart:<id>:render) or forces a fresh one, and concatenates the metadata (title, type, DATTAX) with a data sample of up to 1 KB per chart. - The final prompt has the shape
Pergunta: …followed byContexto:with the assembled JSON, and the answer is streamed back. - If the model calls the
dattax.executetool, the query runs in the DATTAX engine and the result goes back to the model to compose the answer.
The tools exposed to the model
| Tool | What it does |
|---|---|
dattax.execute(script) | Runs DATTAX and returns JSON |
catalog.search(query) | Searches metadata in the platform catalog |
explain.chart(chartId) | Explains how that chart was generated |
Every tool has a documented JSON schema; models with function-calling support can invoke them.
Cache and limits
- Dashboard context:
datta:dattabi:copilot:ctx:<dashboardId>, 2-minute TTL. - Usage limit:
datta:dattabi:copilot:rl:<user>:minand:hour.
4. Executive narrative
When it runs: on PDF export with the narrative option checked (includeNarrative=true).
Available styles:
| Style | What it produces |
|---|---|
executiva | Brief overview, focused on the indicators |
tecnica | Per-chart detail, correlations |
storytelling | Narrative prose with chained recommendations |
Each style has its own system prompt in the prompt repository. The narrative is cached under datta:dattabi:dashboard:narrative:<dashId>:<style>:<hash>, where the hash is the content of the renders — refreshing the data therefore invalidates the narrative automatically.
If AI is unavailable, the PDF comes out without the narrative, along with an explanatory message. The report never gets stuck.
5. Proactive suggestions
When the dashboard opens, the platform fires an asynchronous analysis looking for outliers, trends and correlations. When the finding passes the configured threshold, the model is called only to phrase it in natural Portuguese, in the "did you know that…?" spirit, and the suggestion shows up in the top bar.
The result is cached under datta:dattabi:dashboard:suggestions:<dashId>:<hash> and the user can dismiss the suggestion permanently for that dashboard.
6. AI primitives in DATTAX
Executed inside the DATTAX engine, they process entire columns in the pipeline:
LLM EXTRACT FROM <col> INTO {schema} -- schema-driven extraction
LLM CLASSIFY FROM <col> LABELS [...] -- multi-label classification
LLM EMBED FROM <col> MODEL "..." -- embedding generationHow the executor behaves
- Processes in batches of N rows per request (default 20).
- 30-second timeout per batch.
- Exponential retry on 429 and 5xx.
- Validates the response against the schema before injecting it into the result.
- Reuses already-computed answers through the
datta:dattax:llm:<primitive>:<hash>cache, keyed by the text hash plus the model — the same text is never paid for twice.
Quotas
AI consumption has a daily and monthly limit per user, checked before execution. Costs accumulate in the platform cache and the administrative quota panel shows the breakdown per user.
7. Local vLLM and the cloud alternative
Local stack: the default models are Qwen3-30B-A3B-Instruct and DeepSeek-Coder-V2-Lite, plus the active embedding model. The deployment uses GPUs when available, or CPU only for the smaller models, and exposes an OpenAI-compatible endpoint at http://vllm:8000/v1.
Cloud alternative: GEMINI_API_KEY or OPENAI_API_KEY, kept as an installation secret — always optional. The model access layer picks the destination by three criteria:
- availability of the local model;
- the administrator's preference (per tenant);
- failure — which triggers the alternative.
Architecture requirement (Project Guidelines §6): the platform must work with empty cloud keys.
8. RAG in chat
For the main — not for the dashboard copilot:
- The platform generates embeddings of the documents and the queries with the active model.
- Retrieval is by vector search (KNN) in the
datta-rag-docsindex. - The top-K retrieved excerpts are injected into the prompt.
- A tool lets the model fetch catalog metadata.
The dashboard copilot does not use external RAG: its context is strictly the open panel's. That is a scoping decision, not a temporary limitation.
9. Observability
| Metric | What it measures |
|---|---|
datta_chat_stream_duration_seconds | Conversation streaming duration (p50/p95/p99) |
datta_llm_tokens_generated_total | Tokens generated |
datta_llm_quota_exceeded_total | Quota overruns |
datta_dattabi_copilot_requests_total | Copilot requests, per endpoint |
Audit events: COPILOT.ASK, COPILOT.NARRATIVE and LLM.QUOTA_EXCEEDED. Distributed tracing covers the whole chain — one span per request, from the click to the model call.
10. Security
- IDOR: the dashboard access guard runs on every copilot interaction, no exception.
- Personal data: the prompt instructs the model to refuse fields marked
pii=true. - Prompt injection: input is sanitized before reaching the model (
<|…|>-style markers are removed). - Usage limit: per user, on the interactive surfaces and in the pipeline quotas.
- Audit: every interaction is recorded.
Details in platform security.
11. References
- DATTA BI user guide, Copilot section: DATTABI — User Guide.
- Conversation, model and copilot endpoints: API reference.