Three walls, three retrievers
The design is short to describe and hard to shortcut. One ChromaDB collection per role, named finance_docs, engineering_docs, admin_docs. Every query carries a role. The retrieval function picks the matching collection and runs similarity search inside it. No document outside that collection is ever a candidate for the top-k result.
The full pipeline
Login mints a session and a role. Every query carries the role down to a role-scoped retrieval against ChromaDB. Results and role fuse into a role-aware prompt for OpenRouter.
def search_documents(self, role: str, query: str, top_k: int = 3) -> list[dict]:
if role not in ROLES:
return []
results = self.collections[role].query(
query_texts=[query],
n_results=top_k,
)
# ...format and return
The signature is the design. Role is a required parameter, the role selects the collection, and nothing else can be queried. You cannot accidentally skip the wall because there is no no-role code path.
Quiz: Quiz
Loading practice…
Each module in this course picks one box in that pipeline and goes deep. Auth, retrieval, role-aware prompt, rate limiter, admin flow. By the end you will have touched every layer and you will know what each one is actually for.