Condensed from SPEC.md (spec v2). Read this first; open SPEC.md
for the full rationale.
grounded is an open, evaluated, domain-specialized RAG platform. The
core engine is domain-agnostic; each domain profile is a drop-in folder of
{curated corpus + domain config + eval set}. First profile: energy
(renewable cost & transition — NREL, IRENA, EIA, LBNL, IPCC, Ember).
Niche / moat: domain-specific + curated corpus + RAGAS evaluation + open + multi-provider. Ask a domain in plain English → get a synthesized, cited, measured-trustworthy answer, or an abstain when out-of-corpus.
profiles/<domain>/sources.yaml → download → data/raw/<domain>/*.pdf
→ ingest: parse (PyMuPDF, table-aware) → chunk (+metadata) → embed
→ store: per-domain Chroma collection + BM25
→ retrieve: hybrid (dense + BM25) → BGE rerank
→ answer: prompt "answer only from context, cite [src, p.X], else abstain" (LiteLLM)
→ eval: golden_qa.yaml → RAGAS (faithfulness, answer relevancy, ctx precision/recall)
Every chunk carries metadata: source, title, year, page, section, url.
Python + uv · LiteLLM (all LLM providers; default local Ollama) · BGE-M3 / sentence-transformers embeddings (local, swappable) · PyMuPDF parsing · Chroma vector DB · hybrid dense + BM25 retrieval · BGE-reranker-v2 · LlamaIndex framework · RAGAS evaluation · Chainlit UI (later).
src/grounded/— engine:config.pyproviders.pyprofiles.pydownload.pyingest.pystore.pyretrieve.pyanswer.pycli.pyconfig/settings.yaml— provider, models, chunk,top_k,rerank_k, profileprofiles/<name>/—sources.yaml,config.yaml,golden_qa.yamleval/run_eval.py— RAGAS runner ·tests/·data/(raw/, processed/ — gitignored)
- Config over hardcoding. Provider, models, chunk size,
top_k,rerank_k, and profile all come fromconfig/settings.yaml— never hardcode them. - Cite or abstain. Anti-hallucination prompt; low temperature; abstain when context doesn't support an answer. Never invent numbers.
- Profiles are drop-in. Adding a domain = a new
profiles/<name>/folder, zero core changes. Keep the engine domain-agnostic. - Metadata on every chunk, hybrid + rerank from the start, rebuildable per-domain index, per-query retrieval logging for debugging.
- Licensing: never commit copyrighted PDFs — only URLs in
sources.yaml; fetch todata/raw/at runtime. Secrets live in.env(gitignored). - Import-light modules (Slice 0 rule). Keep module-level imports to stdlib +
typer/pyyaml. Import heavy deps (llama-index, chromadb, litellm, sentence-transformers, ragas, pymupdf, httpx, dotenv) inside function bodies so the package imports and the CLI runs without the full stack. - Stubs raise
NotImplementedError— fail loudly, never return fake data.
- Scaffold ✅ (this) — structure, config, energy profile, import-light stubs.
- Ingestion — download → parse(+tables) → chunk(+metadata) → embed → Chroma.
- Retrieval + answer — hybrid + rerank → LiteLLM answer + citations + abstain.
- CLI —
grounded --profile energy ask "...". - Evaluation — golden set + RAGAS runner (the 4 metrics + abstain accuracy).
- Chat UI — Chainlit + domain filters + citation cards. 6+. Extensions — user-upload, lit-matrix, agentic, MCP, local-SLM, podcast.
Current: Slice 0 complete. Next: Slice 1 (ingestion).
Each slice: Lavish (visual plan → approve) → Claude Code (build) →
No Mistakes (verify). Install the full stack with uv sync from Slice 1.