On a Tuesday morning in March, a chief executive asked a question that should have taken thirty seconds to answer: have we ever agreed to a liability cap below one million dollars?
The answer existed. It was written down, signed, filed, and sitting on the shared drive the whole time. Finding it took three days, and not finding it in time cost forty thousand dollars.
Every organization has a version of that Tuesday. The knowledge is real, it survived, and it is spread across a million files in a hundred formats, organized by whoever was closest to the filing cabinet that day. An organization knows more than anyone in it. The hard part is getting at it.
Keyword search fails for a specific, fixable reason
The obvious first fix is to index every word and search it. Type "liability cap," get every document containing "liability" and "cap." This fails, and it fails in ways worth naming precisely, because each failure points at what the real fix has to do.
The contract does not say "liability cap." It says "limitation of liability." Two phrases, one meaning, zero shared keywords. Your search returns nothing and you conclude the document does not exist. The search bar cannot tell the difference between "we have no such contract" and "we have it, filed under different words."
Matching words is not matching meaning. Search "termination" across an employee handbook and a supplier agreement and you get firing, contract expiry, and possibly a paragraph about ending a software license, ranked by nothing more meaningful than word frequency.
People ask questions, not keywords. Nobody thinks in search terms. They think "have we ever agreed to a liability cap below a million?" A keyword engine has no idea that this is a question, let alone which words in it matter.
What actually closes the gap
The fix is to stop comparing words and start comparing meanings, which requires turning text into something you can measure distance in.
An embedding model reads a passage and returns a list of numbers, a few hundred of them, positioning that passage in a space where distance means similarity of meaning. Passages about annual leave land near each other. Passages about liability caps land somewhere else entirely. The model puts them there based on how the language is actually used, not on which characters they share.
python
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
texts = [
"4.2 Annual Leave\nFull-time employees accrue fifteen (15) days...",
"Remote Work Policy > Schedule\nUp to three (3) days per week...",
"Appendix B\nLodging: $180 per night, booked via travel portal.",
]
vectors = model.encode(texts)
print(vectors.shape) # (3, 384): three texts, one map, 384 coordinates each
Enter fullscreen mode Exit fullscreen mode
Now the interesting part. Embed the question "how many vacation days do employees get in their first year?" into that same space, and it lands nearest the annual leave passage. That passage does not contain the word "vacation." Not once. The crack that keyword search fell through closes at exactly this step, and it closes because the two phrasings mean the same thing, which is the only thing the model was ever paying attention to.
That model is 80MB. It runs on a laptop, with no GPU and no API key, which matters more than it sounds like it should: the first serious question in enterprise AI is not "which model is best," it is "where is my data allowed to go." A model that runs locally answers that question with "nowhere."
The part nobody warns you about
Here is what the tutorials skip. Before you embed anything, you have to get the text out, and the archive fights back.
A PDF is not a document. It is a set of drawing instructions that happen to produce letter shapes, and asking it for its text is a request it may decline. A spreadsheet is a database wearing a costume; flatten it to a string, and you get a row of numbers with no idea what they refer to. A scanned contract from 1994 has no text layer at all, just photographs of paper, and every naive pipeline silently returns an empty string for it and moves on, which is how the most important document in your archive becomes invisible.
Then, having extracted the text, you have to cut it into pieces. Cut naively at 500 characters, and you will split a sentence mid-word, producing a chunk that begins "during thei" and answers nothing. Cut at the document's own joints, keep the heading it lived under, and the same passage becomes something a librarian would recognize as filed.
None of this is the interesting AI part. All of it decides whether the interesting AI part works.
If you want the long version
I wrote a book about building this from scratch, following one fictional 240-person manufacturer from a single PDF in a folder to a working retrieval system, one chapter at a time. Every printed line of code was run before it was printed.
Chapter 1 is free and explains the whole problem, no code required: https://leanpub.com/learn2rag
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.