Retrieval for a regulated insurer — 40M docs, per-clause ACLs, 12 weeks #34
The problem, in the client's words
Constraints
Proposed designflowchart LR
subgraph idx["Index path — nightly + CDC"]
S[Policy docs<br/>Precedents<br/>Reg guidance] --> P[Parse + normalise]
P --> C[Structural chunking<br/>clause-level, heading path retained]
C --> A[ACL denormalised onto chunk<br/>+ its own CDC stream]
A --> E[Self-hosted encoder<br/>GPU backfill budgeted separately]
E --> V[(OpenSearch in-VPC<br/>BM25 + kNN, one filter language)]
end
subgraph qp["Query path"]
Q[Question] --> F[ACL predicate built from<br/>the handler's session claims]
F --> H[Hybrid retrieve N=100<br/>ACL pre-filtered in the query]
H --> R[Cross-encoder rerank →25]
R --> K[Pack k=6, 5k token cap]
K --> G[Generate + cite]
G --> GD{Inline guardrail<br/>grounding + policy}
GD -->|pass| U[Handler]
GD -->|fail| RG[Regenerate or abstain]
end
V --> H
Key choices and why:
What my own design costs
What I would measure firstWeek one, before any of the above: label 100 real handler questions against the What I want challenged
|
Replies: 4 comments 3 replies
Reviewing this from the regulated side, because I have shipped something close to it and the design has one assumption that will not survive contact. "ACL denormalised onto chunk + its own CDC stream." With ~400 scopes expressed as line-of-business × jurisdiction × seniority, you are not storing 400 values — you are storing a set per chunk, and sets do not denormalise cleanly. The failure is not on the read path, it is on the re-grant. Someone moves team, a jurisdiction's rules change, a whole LOB gets reorganised — and now you are rewriting the ACL field on some large fraction of 40M chunks. Your CDC stream is sized for document edits, which are rare. Permission changes are not rare and they are bursty: a reorganisation touches millions of chunks in one transaction. What I would want to see argued: why not keep ACLs out of the chunk and evaluate a predicate at query time against a separate, small, fast-changing store? You pay a join; you stop paying a rewrite. |
On cost — the design has "self-hosted encoder, GPU backfill budgeted separately". Separately from what? That reads like the line item that eats the project. If the corpus is 40M chunks and you re-embed on every encoder change, is that not the dominant cost in the whole system? |
One thing missing from the design that will be the first question in an actual client review: there is no evaluation plan. The diagram goes from documents to a handler. Nowhere does it say how you know it works, or how you would know it had stopped working. In a regulated context that is not a nice-to-have — "the model got worse and we noticed in week six" is a reportable event for some of these clients. Concretely I would want, before any of the retrieval design:
|
|
Marking Dana's comment as the answer, because the missing evaluation plan is the defect that would actually lose this engagement, and the other two are refinements to a design that would otherwise be built without a definition of "working". Synthesis of the three critiques, and what changes:
What stays as designed and why: OpenSearch in-VPC, clause-level structural chunking, and hybrid-with-rerank all survive review unchanged. The residency constraint makes the first non-negotiable, and the other two are corpus-shaped decisions that this review did not challenge. What is still open, and should be argued in its own thread rather than buried here: whether the inline guardrail is a model call or a classifier. That is #35, and it is the same question with a different failure cost. |
Marking Dana's comment as the answer, because the missing evaluation plan is the defect that would actually lose this engagement, and the other two are refinements to a design that would otherwise be built without a definition of "working".
Synthesis of the three critiques, and what changes:
acl_group_id; user→groups resolved at query time; mapping is versioned and lands in the trace