Spaces:
Sleeping
docs: ADR-025 (single-Chroma-collection rationale) + KI-018/019 backlog
Browse filesADR-025 documents the architectural decision to keep one Chroma
collection partitioned by doc_type metadata rather than splitting into
4 collections (policies, regulatory, reviews, profiles). Records the
trade-off: joint retrieval simplicity wins over per-type independence,
with explicit revisit triggers for when to split later.
KI-018 β rag/policies.duckdb is a 132-byte empty stub from ADR-004's
hybrid design that was never populated. Marketplace reads JSON
directly instead. Options: (A) populate from data/policy_facts/*.json
for SQL filtering, (B) remove dead code. Backlog.
KI-019 β kb/policies/*.md (224 hand-written policy summaries) are NOT
embedded. Retrieval would benefit from natural-language summary chunks
for "what does X cover?" style questions. Backlog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@@ -352,3 +352,49 @@ filter or boost by facet when intent is clear. Live count:
|
|
| 352 |
|
| 353 |
Verified locally; pushed to HF Dataset; live HF Space picks up
|
| 354 |
on next rebuild.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
|
| 353 |
Verified locally; pushed to HF Dataset; live HF Space picks up
|
| 354 |
on next rebuild.
|
| 355 |
+
|
| 356 |
+
### KI-018 β `rag/policies.duckdb` stub is dead code (132 bytes, never populated)
|
| 357 |
+
|
| 358 |
+
**Severity:** P3
|
| 359 |
+
**Source:** `rag/policies.duckdb` is in the repo but empty (132-byte sqlite header only)
|
| 360 |
+
**Discovered:** Data architecture audit 2026-05-14
|
| 361 |
+
|
| 362 |
+
ADR-004 designed a hybrid structured (DuckDB) + vector (Chroma) split. The
|
| 363 |
+
DuckDB half was never populated; the marketplace UI ended up reading
|
| 364 |
+
`data/policy_facts/*.json` directly (one file per policy) at request time
|
| 365 |
+
via the `/api/policies/all` endpoint. The 132-byte file in the repo is
|
| 366 |
+
visually misleading β looks like a real store, isn't.
|
| 367 |
+
|
| 368 |
+
**Fix plan (two options):**
|
| 369 |
+
- (A) Populate it from `data/policy_facts/*.json` so the marketplace can
|
| 370 |
+
do SQL filters (sum-insured β₯ X AND room-rent-cap = no AND restoration
|
| 371 |
+
= unlimited). Worth ~30 min.
|
| 372 |
+
- (B) Remove the file + the `import duckdb` + the `policies.duckdb` ADR
|
| 373 |
+
reference + add a note in ADR-025 explaining why structured filtering
|
| 374 |
+
reads JSON directly. Cleaner architecturally.
|
| 375 |
+
|
| 376 |
+
**Recommendation:** (A) β once activated, the marketplace tab gets
|
| 377 |
+
proper SQL filtering that's faster than the current N-JSON-load pattern.
|
| 378 |
+
|
| 379 |
+
### KI-019 β `kb/policies/*.md` not embedded (224 human-readable summaries left out of retrieval)
|
| 380 |
+
|
| 381 |
+
**Severity:** P3
|
| 382 |
+
**Source:** `kb/policies/<policy_id>.md` files exist (224 markdown summaries) but the ingest pipeline only reads from `rag/extracted/*.json`. The natural-language summary text is never embedded.
|
| 383 |
+
**Discovered:** Data architecture audit 2026-05-14
|
| 384 |
+
|
| 385 |
+
Each policy has a hand-written or auto-generated `kb/policies/<id>.md`
|
| 386 |
+
that summarizes the policy in 1-2 pages of natural English (e.g.
|
| 387 |
+
"Care Supreme covers in-patient hospitalization across India with a
|
| 388 |
+
36-month PED waitingβ¦"). These would be EXCELLENT retrieval targets
|
| 389 |
+
for "what does X cover?" style questions because they're already
|
| 390 |
+
phrased for human reading. Currently retrieval matches against raw
|
| 391 |
+
PDF-extracted prose which is denser + less semantic.
|
| 392 |
+
|
| 393 |
+
**Fix plan:** Add a kb-markdown ingestion path in `rag/ingest.py` that
|
| 394 |
+
walks `kb/policies/*.md`, splits at H2 boundaries (each H2 β one
|
| 395 |
+
schema-field discussion), embeds each section, and writes with
|
| 396 |
+
`doc_type="summary"` so retrieval can prefer summary chunks when the
|
| 397 |
+
intent is high-level Q&A.
|
| 398 |
+
|
| 399 |
+
Expected impact: better grounding for natural-language "tell me about X"
|
| 400 |
+
questions; cleaner citations (one summary chunk vs. 3-4 raw chunks).
|
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ADR-025: Single Chroma collection with metadata partitioning (not 4 separate collections)
|
| 2 |
+
|
| 3 |
+
**Status:** Locked
|
| 4 |
+
**Date:** 2026-05-14
|
| 5 |
+
|
| 6 |
+
## Context
|
| 7 |
+
|
| 8 |
+
The bot retrieves four logically distinct kinds of content at request time:
|
| 9 |
+
|
| 10 |
+
1. **Policy chunks** (PDF wordings, brochures, CIS, prospectuses) β what the policy says
|
| 11 |
+
2. **Regulatory chunks** (IRDAI master circulars, Insurance Act, etc.) β what the regulator mandates
|
| 12 |
+
3. **Review chunks** (per-insurer claim metrics, aggregator ratings, Reddit/YouTube sentiment, news) β what users say
|
| 13 |
+
4. **Profile chunks** (one chunk per active session, holding the user's age / dependents / income / conditions / budget) β what we know about THIS user
|
| 14 |
+
|
| 15 |
+
A reasonable mental model is "four separate databases". The literal Chroma equivalent would be four collections (`policies`, `regulatory`, `reviews`, `profiles`) queried separately, with results merged in the orchestrator.
|
| 16 |
+
|
| 17 |
+
## Decision
|
| 18 |
+
|
| 19 |
+
**Keep ONE Chroma collection (`policies`) partitioned by a `doc_type` metadata field.** All four logical types co-exist in the same vector index. Retrieval is a single cosine-similarity call; the orchestrator applies metadata filters only when the user's intent is clearly type-specific (e.g., a regulatory-only question).
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
collection: policies (Chroma; 7,366 chunks; BGE-small 384-d cosine)
|
| 23 |
+
doc_type=wordings (5,401 chunks) β policy wordings
|
| 24 |
+
doc_type=brochure ( 611 chunks)
|
| 25 |
+
doc_type=cis ( 303 chunks)
|
| 26 |
+
doc_type=prospectus ( 483 chunks)
|
| 27 |
+
doc_type=regulatory ( 498 chunks) β IRDAI + Insurance Act
|
| 28 |
+
doc_type=review ( 60 chunks) β 10 insurers Γ 6 facets (KI-017)
|
| 29 |
+
doc_type=profile (variable) β one per active session_id
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
## Alternatives considered
|
| 33 |
+
|
| 34 |
+
| Approach | Why rejected |
|
| 35 |
+
|---|---|
|
| 36 |
+
| 4 separate collections (`policies`, `regulatory`, `reviews`, `profiles`) | Joint retrieval requires N queries + manual merge + N embedding-pool warmups. Loses the natural "user profile boosted in the same retrieval as the policy text" trick. ~2Γ higher per-turn latency for marginal lifecycle benefit. |
|
| 37 |
+
| Per-insurer collections | Cross-insurer comparison (the killer feature) becomes N queries. No upside. |
|
| 38 |
+
| Per-doc_type with shared embedder | Same as 4-collection but with one embedder. Still N queries. |
|
| 39 |
+
|
| 40 |
+
## Why one-collection wins for THIS bot
|
| 41 |
+
|
| 42 |
+
1. **Joint retrieval is the headline RAG move.** The bot's most cited feature is "the brain sees policy text + regulatory mandate + user's own profile in the same context window". One Chroma query returns all three. Splitting would require an explicit fan-out + merge.
|
| 43 |
+
|
| 44 |
+
2. **Profile-chunk boost is organic.** When `session_id` is passed to `retrieve()`, the chunk with `policy_id="profile_<session_id>"` is fetched separately and prepended to the top-k. This works cleanly because the profile lives in the same collection β it's a metadata lookup, not a cross-collection fetch.
|
| 45 |
+
|
| 46 |
+
3. **Free-tier resource discipline.** Each Chroma collection in the deployment has its own HNSW index (~50 MB of RAM warmup per collection on first use). The HF Space free tier has 16 GB RAM total; 4 collections means ~200 MB of cold-start memory pressure for negligible UX gain.
|
| 47 |
+
|
| 48 |
+
4. **Metadata partitioning is functionally equivalent.** When intent IS type-specific (e.g., the user asks about IRDAI), the orchestrator can apply `where={"doc_type": "regulatory"}` filters. The retrieval is single-query, same speed, same ergonomics as a separate-collection design β just with a 0.1ms metadata filter step.
|
| 49 |
+
|
| 50 |
+
## Consequences
|
| 51 |
+
|
| 52 |
+
**Positive:**
|
| 53 |
+
|
| 54 |
+
- Joint retrieval stays cheap (one query, one warmup).
|
| 55 |
+
- Profile chunk boost is a simple Chroma `get(ids=[profile_<session>])` call.
|
| 56 |
+
- Single backup target (`rag/vectors/chroma.sqlite3` + HNSW binaries) β simpler dataset hygiene.
|
| 57 |
+
- Mental model: "one vector store, one schema, doc_type tells you what kind".
|
| 58 |
+
|
| 59 |
+
**Negative:**
|
| 60 |
+
|
| 61 |
+
- A bad embedding for one doc_type can pollute global recall on unrelated queries. We mitigate with retrieval-floor + faithfulness gates.
|
| 62 |
+
- We cannot iterate embedding models per type (e.g., a legal-domain embedder for regulatory only) without changing the architecture.
|
| 63 |
+
|
| 64 |
+
## When we'd revisit
|
| 65 |
+
|
| 66 |
+
| Condition | Migration |
|
| 67 |
+
|---|---|
|
| 68 |
+
| Per-type embedding models become necessary | Split off the heaviest type (regulatory) into its own collection with its own embedder. |
|
| 69 |
+
| Multi-tenant deployment (profiles for thousands of concurrent users) | Move profiles to a separate `profiles` collection with TTL-based eviction policy. |
|
| 70 |
+
| Review content grows to >5,000 chunks | Move reviews to a dedicated collection so policy retrieval isn't diluted. |
|
| 71 |
+
|
| 72 |
+
## Related KIs and ADRs
|
| 73 |
+
|
| 74 |
+
- **KI-017** β Enriched per-facet review chunks (10 β 60). Validates that doc_type partitioning scales for richer content without requiring a separate collection.
|
| 75 |
+
- **KI-018** β Empty `rag/policies.duckdb` cleanup. The structured store from ADR-004 is currently dormant; structured filtering reads `data/policy_facts/*.json` directly.
|
| 76 |
+
- **ADR-004** β Hybrid structured + vector retrieval (the original design β DuckDB for filtering, Chroma for citing).
|
| 77 |
+
- **ADR-011** β Local BGE-small embeddings.
|