Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Omniagent Ai Data Analyst

FreeNot checked

Open source AI data analyst that answers questions about your data in plain English. Governed by a dbt semantic layer, runs locally on DuckDB, refuses to guess

GitHubEmbed

About

Open source AI data analyst that answers questions about your data in plain English. Governed by a dbt semantic layer, runs locally on DuckDB, refuses to guess when it is unsure, and publishes execution accuracy on every commit. Text to SQL you can put in a board deck.

README

A governed answer engine for tabular data. Ask a question in plain English, get back a number with the SQL that produced it, or a clear refusal instead of a confident guess. Semantic layer first, guarded SQL fallback second, a deterministic gate stack around both.

The scorecard

Regenerated fresh from real data on every run, never hand-assembled:

python scripts/generate_samples.py
python scripts/load_warehouse.py
python scripts/run_eval.py
metric                           n     mean   ci_low  ci_high
-------------------------------------------------------------
execution_accuracy             147   100.0%   100.0%   100.0%
route_accuracy                 147   100.0%   100.0%   100.0%
metric_match_accuracy          147   100.0%   100.0%   100.0%
redteam_refusal_rate             6   100.0%   100.0%   100.0%

147 golden questions across two packs (e-commerce, SaaS), generated backwards from real execution against the real warehouse, not hand-written (see docs/adr/0011). 6 red team cases (prompt injection, destructive SQL, PII exfiltration), each scripted to keep trying its attack on every retry, refused every time by the gate stack, not by a model declining.

The same SQL, gated or not

python scripts/compare_governed_vs_raw.py

Runs the red team's own SQL strings two ways: once with no gates at all against a disposable copy of the warehouse, once through the real gate stack. As of this writing: 4 of 6 execute with no gates, all 6 are refused when governed. The two that fail unguarded do so by DuckDB dialect luck (legacy syntax the parser happens to reject), not by design, which is the actual point: a deterministic gate stack holds regardless of the SQL dialect, the model, or the day. Chart written to reports/governed_vs_raw.html.

90 seconds, end to end

python scripts/demo.py

Five acts, all real code, no GROQ_API_KEY required: the trap above, an answer card with a chart, a genuinely ambiguous question that pauses for clarification and resumes, the same governed graph answering through MCP instead of a human, then the scorecard.

How it decides

Every box below is real code, not a plan. Blue nodes are the only places a model is called at all, and each one answers a single narrow question, not "what should happen next" (see docs/adr/0002). Green is the path that skips the model entirely. Red is the one place a model writes SQL directly, which is exactly why it carries the heaviest gating.

flowchart TD
    U["Streamlit UI · Next.js UI · REST client · MCP client"] --> ASK["one question, one thread_id"]
    ASK --> MASTER{"master<br/>deterministic catalog match"}

    MASTER -->|hit| SEM["semantic_agent<br/>1 LLM call: time phrase + filters"]
    MASTER -->|miss| ROUTER["router<br/>1 LLM call: intent"]

    ROUTER -->|ambiguous| CLARIFY["clarify<br/>pauses via interrupt()"]
    ROUTER -->|data question| FASTPATH{"fast_path<br/>verified-query cache lookup"}
    CLARIFY -.->|resume with answer| MASTER

    FASTPATH -->|hit| REEXEC["re-execute the cached SQL<br/>never trust a stored result"]
    FASTPATH -->|miss| SQLAGENT["sql_agent<br/>schema-linked SQL, bounded retries"]

    SEM --> COMPILE["compile deterministic SQL<br/>semantic layer, no model"]

    COMPILE --> GATES
    REEXEC --> GATES
    SQLAGENT --> GATES

    GATES["8-gate GuardrailPolicy<br/>allowlist · row cap · timeout · empty-result<br/>numeric recompute · PII mask · provenance · LLM budget<br/>every gate runs, pre- and post-execution, no short-circuit"]

    GATES --> ENGINE["DuckDB / Postgres<br/>read-only, external access disabled"]
    ENGINE --> NARRATE["narrator + charts + suggester<br/>template-first, no model call"]
    NARRATE --> ENV["AnswerEnvelope<br/>narration, rows, chart, executed_sql, confidence"]
    ENV --> U

    classDef model fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    classDef free fill:#dcfce7,stroke:#16a34a,color:#14532d
    classDef risky fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
    classDef gate fill:#fef3c7,stroke:#d97706,color:#78350f
    class SEM,ROUTER model
    class FASTPATH,REEXEC free
    class SQLAGENT risky
    class GATES gate

A catalog hit costs exactly one model call for the entire turn. A fast-path hit costs zero, because the SQL was already verified, though it still re-executes against current data rather than serving a stored answer. The fallback (sql_agent) is the one place a model writes SQL from scratch, so it is the one path that runs schema-linked generation with a bounded self-correction loop and pays for it with the most retries and the strictest scrutiny. Narration, chart selection, and follow-up suggestions are template-first everywhere, always zero extra calls (see docs/adr/0001).

Where the cost actually goes

  • One model call is the default, not the exception. A catalog-matched question (the common case in any well-modeled domain) makes exactly one narrow LLM call -- extracting a time phrase and filters -- then compiles deterministic SQL and narrates from a template. No routing decision, no narration, no chart choice ever costs a model call.
  • The verified-query cache is the real cost saver, not a caching trick. A thumbs-up on a fallback answer stores its SQL. A later paraphrase of the same question skips the model entirely and re-executes that stored query against current data -- cached at the query level, not the answer level, so it can never go stale (see docs/adr/0006). A same-shape-different-metric near miss scores high enough on embedding similarity to fool a low threshold, so this path only trusts a hit above roughly 0.9 cosine similarity, calibrated against the real embedder, not guessed.
  • Cheap model for routine calls, a different model where it's worth it. build_governed_graph takes an independent model_id for the main path, router_model_id for intent routing, and sql_agent_model_id for the guarded fallback -- an operator can point routine extraction at a fast, inexpensive tier (Groq's gpt-oss-20b runs about $0.075 in / $0.30 out per million tokens) and reserve a stronger model for the one path where a model writes SQL directly. This is a deployment choice made once at startup, not automatic runtime escalation -- OmniState carries a tier_bump field for that kind of adaptive routing, and it is honestly unused today, not wired to anything.
  • Prompt caching is the provider's job, not a feature built here. ModelCapabilities.prompt_caching records that Groq caches repeated prompt prefixes automatically at the API level; OmniAgent's own prompts are already narrow and templated (see adapters/llm/prompting.py), so they are exactly the repeated-prefix shape that caching helps with, but the caching itself happens on Groq's side, not in this codebase.

Try it

git clone <this repo> && cd omniagent-ai-data-analyst
just install                    # uv sync --locked --all-extras --dev
python scripts/generate_samples.py
python scripts/load_warehouse.py
just eval                       # the scorecard above
just compare                    # the governed-vs-raw chart
just demo                       # the 90-second walkthrough

export GROQ_API_KEY=...         # a real model, for the actual services
just serve                      # REST API on :8000
just serve-mcp                  # MCP server, stdio by default

just serve &                    # keep the API running, then pick a UI:
streamlit run omniagent/channels/streamlit_app.py   # on :8501
just web-install && just web    # Next.js app on :3000 -- same API, same gates, no Streamlit

docker compose up --build       # the whole stack (init, api, ui, web): see below

docker compose up builds one image, generates both packs' sample data and loads the warehouse in an init service, then brings up the REST API (:8000) and two independent frontends against it -- the Streamlit UI (:8501) and a Next.js app (:3000); an mcp service is available behind docker compose --profile mcp up. Needs GROQ_API_KEY in the environment (or a .env file) to actually answer questions; without one it still builds, generates data, and serves /health//datasets, failing fast with a clear error on /ask instead of guessing.

What's actually here

  • Semantic layer first (docs/adr/0001): a catalog match compiles to deterministic SQL with one narrow LLM call for filter/time extraction. No catalog match escalates to a guarded SQL fallback with schema linking and a bounded self-correction loop, behind the same gate stack.
  • A deterministic gate stack, not an ML guardrail (0005): SQL allowlist, row cap, timeout, empty-result abstention, numeric recompute against ground truth, PII masking, provenance, LLM budget. Every gate runs, not just the first that fails, for a full audit trail.
  • Durable, resumable clarification (0007): a genuinely ambiguous question pauses the graph via LangGraph's interrupt() and resumes exactly where it left off once answered.
  • An evaluation harness with published numbers (0011): golden sets generated backwards from real execution, never checked in as static data, so they can't drift out of sync with the packs or warehouse.
  • MCP with no raw-SQL tool (0012): the same four capabilities (discover, ask, resume, feedback) both UIs (Streamlit and the Next.js app) get, nothing more direct, over the same gate stack.
  • Two real datasets, one real engine, one real semantic provider: e-commerce and SaaS packs, fully exercised through DuckDB and a self-contained YAML semantic layer. Postgres and dbt/MetricFlow are written to their full ports but honestly documented as open, not faked (see BUILD_STATUS.md).

Full phase-by-phase status, what's genuinely done versus honestly open, and every real bug this build's own validate-then-fix discipline caught: see BUILD_STATUS.md. Architecture decisions, one per real design choice made across the build: docs/adr/. Running a pilot: docs/PILOT_RUNBOOK.md.

License

MIT. See LICENSE.

from github.com/hoomanesteki/omniagent-ai-data-analyst

Installing Omniagent Ai Data Analyst

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/hoomanesteki/omniagent-ai-data-analyst

FAQ

Is Omniagent Ai Data Analyst MCP free?

Yes, Omniagent Ai Data Analyst MCP is free — one-click install via Unyly at no cost.

Does Omniagent Ai Data Analyst need an API key?

No, Omniagent Ai Data Analyst runs without API keys or environment variables.

Is Omniagent Ai Data Analyst hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Omniagent Ai Data Analyst in Claude Desktop, Claude Code or Cursor?

Open Omniagent Ai Data Analyst on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Omniagent Ai Data Analyst with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All data MCPs