A regulatory-compliance agent built on a vectorless RAG pipeline. Instead of embedding chunks and doing similarity search, it reasons over a document's structure to answer questions with traceable, page-level citations.
Conventional RAG splits a document into opaque chunks, embeds them, and searches by vector similarity. That throws away the document's structure and makes answers hard to audit — a problem for regulatory and financial work.
This agent builds a hierarchical tree index instead (Chapters → Sections → Appendices), where each node carries a title, a one-sentence summary, and its page range. A reasoning model reads this compact map, picks the sections that answer a question — following cross-references like "see Appendix A" along the way — and only then pulls the raw text of those sections to write the answer.
The result is grounded, explainable (you can read the navigator's reasoning), and traceable (every answer cites its sources). No embeddings, no vector database.
PDF ──▶ LayoutAwareParser ──▶ page-tagged Markdown
│
▼
TreeIndexer ──▶ hierarchical JSON tree (TreeNode)
· headings become the nesting hierarchy
· each node stamped with its physical page range
· summaries generated bottom-up
│
▼
StructuralRetriever
1. navigate — model routes the abstract map to node ids
2. synthesise — pull node text, answer + cite
│
▼
grounded answer + reasoning trace + citations
app/
core/ config, LLM client, PDF parser, JSON extraction
models/ TreeNode, RetrievalResult, Citation
services/ TreeIndexer (build the tree), StructuralRetriever (navigate + answer)
data/ raw PDFs and processed Markdown/indices (git-ignored)
tests/ offline pytest suite
run_pipeline.py end-to-end CLI driver
streamlit_app.py browser UI
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -r requirements.txtRun the pipeline — no API key needed, it uses a deterministic offline mock LLM:
python run_pipeline.pyOr launch the web UI:
streamlit run streamlit_app.pyCopy the template, add a key, and pass --real:
cp .env.example .env # then set OPENAI_API_KEY
python run_pipeline.py --realMain settings (full list in .env.example):
| Variable | Default | Purpose |
|---|---|---|
OPENAI_API_KEY |
— | Required for live mode. |
COMPLIANCE_REASONING_MODEL |
gpt-4o-mini |
Model for tree navigation. |
COMPLIANCE_SYNTHESIS_MODEL |
gpt-4o |
Model for answer synthesis. |
COMPLIANCE_LLM_TEMPERATURE |
0.0 |
Deterministic output for audit trails. |
Your .env is git-ignored — don't commit real keys.
python run_pipeline.py [--mock | --real] [--rebuild] [-q QUERY ...]
--mock force the offline mock LLM (default when no key is set)
--real force OpenAI models (needs OPENAI_API_KEY)
--rebuild re-generate, re-parse and re-index from scratch
-q QUERY add an extra question (repeatable)
pip install -r requirements-dev.txt
ruff check .
pytestThe test suite is fully offline and deterministic — it runs against the mock LLM and never touches the network.