A member and a staff user ask the same question about a policy. Their permitted evidence differs. Build a document assistant whose retrieval, derived summaries, and saved answers preserve that boundary through updates and reconnects.
Before you begin: You understand chunking, hybrid retrieval, reranking, compression, and per-user authorization.
The central deliverable is a working evidence path. A polished chat box is only one part of it.
Build ingestion as a versioned process
Choose a small collection with headings, a table, an old policy, and a restricted document. Preserve document IDs, versions, source locations, and access metadata. Parse structures before splitting, and inspect the resulting chunks manually.
Record the embedding model revision and index configuration. When the embedding space changes, plan a compatible reindex rather than mixing unrelated vectors. When a source is updated or deleted, invalidate affected chunks and derived summaries.
Start with a lexical or dense baseline, then add hybrid retrieval and reranking only when labeled failures justify them. Keep a fixed query set so each change can be assessed.
What happens to an old answer when access changes?
Access control is not only an ingestion-time decision. A document may be restricted after it has been indexed or cited in an answer. Decide what history, caches and source previews may reveal after that change, consistent with the product's retention and authorization rules.
Test retrieval and later history access with the same user before and after revocation. The challenge follows a source across those surfaces so a protected search endpoint is not undermined by an unprotected cached response or citation preview.
A member receives an answer citing document D.
Document D becomes restricted and the member reloads history or a source preview.
The application must apply its current access policy to any protected material it exposes.
Enforce access before model-visible processing
The following simulation separates permitted records before matching. It uses word overlap as a deliberately simple baseline, not semantic embeddings.
# Runnable: Python 3, standard library.
records = [
{'id': 'public-hours', 'owner': None, 'text': 'pottery opens Saturday'},
{'id': 'booking-a', 'owner': 'a', 'text': 'pottery booking for member a'},
{'id': 'booking-b', 'owner': 'b', 'text': 'pottery booking for member b'},
]
def retrieve(user_id, query):
allowed = [r for r in records if r['owner'] in (None, user_id)]
terms = set(query.lower().split())
scored = [(len(terms & set(r['text'].lower().split())), r)
for r in allowed]
return [r for score, r in sorted(scored,
key=lambda pair: (-pair[0], pair[1]['id'])) if score > 0]
hits = retrieve('a', 'pottery booking')
assert 'booking-b' not in [r['id'] for r in hits]
assert 'booking-a' in [r['id'] for r in hits]
print([r['id'] for r in hits])
In production, derive identity from the authenticated session and enforce it in the storage query. Repeat the check for parent expansion, reranking inputs, cached answers, and citations. Filtering only the visible final answer is too late if private text already reached another service.
Revoke a source after a successful answer
Let user A retrieve a private policy and receive a cited answer. Then revoke access and repeat the question. Test the raw retrieval path, cached result, parent expansion, citation preview, and saved-answer policy. Decide explicitly whether previously saved content remains visible under your product's retention rules.
Why is deleting the index row insufficient?
The source may have produced caches, summaries, expanded parents, or saved artifacts. These derived copies can preserve restricted information after the original row disappears. Track dependencies and enforce the intended revocation policy at each read boundary. Do not claim immediate revocation if the product deliberately retains historical copies without rechecking access.
Add a source-version change with the same title but a different deadline. Verify that the answer and citation refer to the same version. A current answer beside an old supporting passage is a provenance failure even when the sentence happens to be correct.
Define the answer contract
Each factual answer should cite supporting source locations, preserve important qualifications, and distinguish absent evidence from a negative fact. “No price appears in the record” is different from “the workshop is free.”
When sources conflict, use an explicit version or authority rule, or describe the conflict. Treat instructions embedded in retrieved documents as untrusted content. They must not alter system permissions or cause unrelated tool actions.
Make empty, loading, and error states useful. On a phone, a user should be able to open a citation, read its supporting passage, return to the answer, and retry a failed request without losing the question. Persist server-owned conversation or task state under the correct user when continuity is part of the product.
Evaluate before release
Prepare questions requiring one passage, several passages, a table, a missing fact, an old-versus-new distinction, and a restricted record. Test with at least two users whose private records overlap in wording. Include deletion and permission-change cases.
Measure retrieval evidence coverage separately from answer correctness and citation support. Record latency by stage and cost per successful answer. A final response can be correct by chance even when retrieval failed; inspect the evidence path.
Completion exercise: submit the ingestion manifest, query set, observed results, and five inspected answer traces. Release only if required permission and correctness checks pass. If the advanced pipeline does not beat the baseline, keep the simpler system and document what failed to improve.
Practice with feedback
Revoke a source after a successful answer
A member asks a question and gets an answer citing document D. Ten minutes later D is restricted. The member reloads their history.
Enforce access before model-visible processing and version ingestion.
Check your understanding
Your task
Specify the ingestion versioning, the access enforcement points, and the revocation behaviour.
These notes stay on this page. Download them before leaving.
What to include
- Access is enforced inside retrieval, not after
- At least three enforcement points are named including one at render time
- Revocation covers in-flight, stored, and index cases
- The evaluation includes an access test, not only quality
Compare with a worked answer
Here is one way to answer. Check how it uses the information in the task.
Ingestion as a versioned process: a new upload creates doc_version N+1 with its own chunks, embeddings, and ingested_at. Version N's chunks are marked retired: excluded from retrieval, retained for audit. Nothing is overwritten, so an answer from last month can still be explained against the text that produced it.
Access enforcement points: 1. Inside the retrieval query: visibility filter applied in both the lexical and vector searches, before truncation to k. Nothing restricted is ever returned to the application, let alone the model. 2. At context assembly: a defensive re-check of each chunk's visibility against the session, which should be a no-op. It has fired twice, both times because of a caching bug in step 1. 3. At render time for stored answers: citations are resolved live against current visibility, so a previously public source that is now restricted renders as unavailable.
Revocation behaviour: in-flight request: the retrieval filter is evaluated at query time, so a request starting after the revocation never sees it. A request already holding the text completes; the window is seconds and I accept it, having written that down rather than pretending it is zero. saved answer on reload: the answer text is shown as it was, with the citation marked 'source no longer available to you'. The restricted text is not re-fetched or re-displayed. the search index: chunks are not deleted on revocation, their visibility field is updated, which takes effect on the next query. Deletion happens only when the document itself is deleted.
The answer contract: every factual sentence carries a chunk id; unanswerable questions return a fixed refusal; partial answers name what is not covered. Release evaluation: 60 labelled questions for quality, 12 unanswerable ones, and an access suite that asks 20 questions as a member who should not see D and asserts D never appears in retrieval, context, or citations. The access suite blocks release independently of the quality numbers.
When you are signed in, opening the challenge carries your edited working notes into its draft in this browser. The challenge has its own completion record. Practising here does not award points or mark it complete.
Next, explore a graph-based retrieval approach for questions that depend on relationships across many documents.
Sources
Retrieval-Augmented Generation provides the foundational architecture. Sentence Transformers' retrieve-and-rerank guide documents a practical retrieval stage. The access-control simulation here is an application invariant, not a complete production backend.