Skip to content

Latest commit

 

History

History
79 lines (61 loc) · 3.81 KB

File metadata and controls

79 lines (61 loc) · 3.81 KB

CLAUDE.md — grounded

Condensed from SPEC.md (spec v2). Read this first; open SPEC.md for the full rationale.

What this is

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.

Architecture (data flow)

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.

Stack

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).

Repo layout

  • src/grounded/ — engine: config.py providers.py profiles.py download.py ingest.py store.py retrieve.py answer.py cli.py
  • config/settings.yaml — provider, models, chunk, top_k, rerank_k, profile
  • profiles/<name>/sources.yaml, config.yaml, golden_qa.yaml
  • eval/run_eval.py — RAGAS runner · tests/ · data/ (raw/, processed/ — gitignored)

Conventions (don't miss these)

  • Config over hardcoding. Provider, models, chunk size, top_k, rerank_k, and profile all come from config/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 to data/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.

Build plan (slices)

  1. Scaffold ✅ (this) — structure, config, energy profile, import-light stubs.
  2. Ingestion — download → parse(+tables) → chunk(+metadata) → embed → Chroma.
  3. Retrieval + answer — hybrid + rerank → LiteLLM answer + citations + abstain.
  4. CLI — grounded --profile energy ask "...".
  5. Evaluation — golden set + RAGAS runner (the 4 metrics + abstain accuracy).
  6. 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).

Workflow

Each slice: Lavish (visual plan → approve) → Claude Code (build) → No Mistakes (verify). Install the full stack with uv sync from Slice 1.