A semantic search layer over 510 commercial contracts finds the clause that matters and cites the exact sentence it came from.
Problem
An audit request lands on a Friday
A compliance team is told to produce every vendor and licensing agreement with a Most Favored Nation clause, an Audit Rights provision, or an uncapped indemnity, ahead of a regulator’s information request. The portfolio runs to several hundred contracts. None are tagged by clause type.
A keyword search for “audit” finds the word. It does not find the sentence that grants the right, thirty pages into an addendum.
Approach
Search the clause, not the file
The fix is an index built for clauses, not files. Every contract is split into passages and embedded, then indexed in Qdrant. A query like “audit rights” or “change of control trigger” retrieves the passages closest in meaning, including ones that never use those exact words. (Technically: dense vector retrieval with all-MiniLM-L6-v2 embeddings.)
Each match is then re-scored sentence by sentence against the query, so the one sentence doing the work gets highlighted, not the paragraph around it. (Technically: cross-encoder reranking with bge-reranker-v2-m3.)
A retrieval-grounded chat sits on the same index. Ask “which of these agreements cap liability below $1M?” and the answer cites the passages it came from, not a model’s general memory of contract law.
How it works
Query to citation, in four hops
The chat model never answers from memory alone, and every result links back to the source PDF.
In practice
What the reviewer sees
A reviewer searching the audit rights question doesn’t get a document list. They get the clause, in context, with the sentence that matters marked.
MASTER_SERVICES_AGREEMENT_2019.pdf, p. 14, §9.2
Vendor shall maintain accurate books and records relating to this Agreement, and Client, or its designated independent auditor, may inspect and audit such records upon thirty (30) days’ written notice, no more than once per calendar year, at Client’s expense, during Vendor’s normal business hours.
The same mechanism runs on the categories compliance teams ask for most:
- ›Audit Rights
- ›Change of Control
- ›Most Favored Nation
- ›Cap on Liability
- ›Anti-Assignment
- ›IP Ownership Assignment
Outcome
One query, not one file at a time
A portfolio of 500 contracts at 10 minutes of manual review each runs to roughly 80 hours per sweep. Clause search cuts that to the time it takes to read the results. (Illustrative estimate, not a measured client result.)
The harder win is what happens next time. A new regulation naming a clause type the portfolio was never tagged for doesn’t mean opening every file again. It means writing one query, with every result citing the sentence it came from.
Evidence
What isn’t solved yet
Engineering note
Latency scales with chunk count, not query complexity. The sentence-level highlighter calls the reranker once per sentence, in sequence. At the default top_k of 10, with 15 to 25 sentences per chunk, one search can mean 150 to 250 round trips to the inference API before a result renders. Batching that call is next on the project’s own roadmap, not a hidden defect.
The corpus is worth naming plainly too. CUAD is a public research dataset of historical commercial contracts, standing in here for a firm’s live matter files. Moving this from illustration to production means pointing the same architecture at a real, access-controlled repository, with the audit trail that implies.
