Most teams build their first retrieval system the same way: slice every document into fixed 512-token chunks, push them into a vector database, and hope the embedding model does the hard work. That hope gets you through a demo. It does not survive contact with real users.
In production, a legal contract falls apart when you sever a liability clause from its exceptions. API documentation turns useless when a code sample gets detached from its function signature. A customer support thread becomes noise when you rip a single complaint out of its conversational history. The problem is rarely the language model sitting at the end of the pipeline. The problem is what you feed it.
We learned this the hard way. Our initial retrieval layer looked standard but behaved inconsistently. So we rebuilt it around a simple idea: treat retrieval as measured infrastructure, not magic. Here is exactly what changed, and how we pushed recall to 95 percent while cutting the 95th-percentile latency from 850 ms to 320 ms.
The Fixed-Chunk Trap
Uniform token counts are easy to code and easy to explain. That convenience masks a basic truth: documents have structure. When you ignore that structure, you destroy signal.
Consider a ten-page master service agreement. A fixed 512-token slice will land mid-obligation, splitting a clause from the very cap table that limits it. The retrieval step then returns half a thought. The generator hallucinates the rest. In API documentation, a chunk that is too large dilutes the embedding with boilerplate headers, burying the specific method a developer needs. In support tickets, a fixed window treats a conversation as a bag of sentences, stripping away the back-and-forth that reveals what actually failed.
We stopped treating chunk size as a hyperparameter we guessed. We started treating it as a mapping exercise between the document type and the information architecture inside it.
Match Your Chunking to the Data
The fix is not one perfect chunk size. The fix is three distinct strategies tuned to three distinct data shapes.
Legal documents now go through recursive splitting. The algorithm first looks for the largest natural boundaries—sections, then subsections, then numbered clauses—and only falls back to smaller splits when necessary. This keeps a termination clause attached to its survival conditions. The retrieval step sees complete logical units, which sharply reduces the model’s temptation to invent missing exceptions.
API and code documentation get structure-aware chunking. Markdown headers, code fences, and parameter tables are parsed as atomic units. We do not split inside a code block. We keep docstrings adjacent to their signatures. The result is that a query for a specific class method retrieves the full context a developer needs: the description, the typed parameters, and the working example.
Support and conversational data use semantic chunking. Instead of counting tokens, we look for shifts in topic or intent. If a customer describes a bug in message three and pastes a stack trace in message seven, we chunk by meaning, not by message index. The retrieval layer then returns the full arc of the problem rather than a orphaned sentence.
Why Vector Search Alone Fails
Even perfect chunks die in a pure vector search. Dense embeddings excel at capturing meaning and synonymy, but they are notoriously fuzzy on exact strings. If an engineer searches for the precise error code ERR_CONNECTION_REFUSED, vector similarity might return a dozen conceptual neighbors and miss the exact match buried at rank fourteen.
Keyword search with BM25 has the opposite problem. It finds exact tokens but misses semantic intent. A user asking “why is my database down” will never match a document that says “troubleshooting connection timeouts.”
We now run both. Vector and keyword results are fed into Reciprocal Rank Fusion, which blends the two ranked lists without requiring calibrated scores. The fused list is then passed through a cross-encoder reranker. The reranker is slower than the initial retrieval, but it is far more precise because it judges query-document relevance directly rather than through compressed embeddings. That hybrid pipeline alone lifted our recall by 15 percent.
Fixing Bad Queries Before They Hit the Index
משתמשים לא כותבים שאילתות חיפוש אידיאליות. הם מדביקים שורות לוג מקוטעות. הם מקלידים "זה שבור". הם משתמשים בטרמינולוגיה שהתיעוד שלכם מעולם לא אימץ. אם אתם סומכים על השאילתה הגולמית, אתם סומכים על רעש.
כעת אנחנו מרחיבים כל שאילתה נכנסת לשלושה עד חמישה וריאציות לפני שליחתן לשכבת השליפה (retrieval layer). וריאציה אחת עשויה להיות ניסוח מחדש ישיר. אחרת עשויה להיות כותרת מסמך אידיאלית היפותטית. שלישית מסירה מילות קישור (conversational filler) ומבודדת מילות מפתח טכניות. כל וריאציה עוברת embedding וחיפוש. לאחר מכן אנחנו מבצעים deduplication ומיזוג של מאגרי המועמדים.
זה לא בחינם. קריאות ה-embedding הנוספות הללו עולות כסף ומוסיפות כמה מילישניות. אך ההשפעה על ה-recall הייתה דרמטית: עברנו מ-78 אחוזים ל-96 אחוזים על ידי הרחבת השאילתות לפני השליפה. מכיוון ששליפה טובה יותר מצמצמת את חלון היצירה (generation window) ומעגנת את המודל בהקשר (context) נכון, סופנו שחסכנו כסף בשלבים הבאים. שלב שליפה יקר מעט יותר הוא זול יותר משלב יצירה ארוך ומזויף (hallucinated).
הפסיקו לנחש. התחילו לחפש.
ברגע שהיה לנו את ה-chunking, ה-hybrid retrieval והרחבת השאילתות הנכונים, עדיין עמדנו בפני בלאגן קומבינטורי. גודל chunk, חפיפת chunk (overlap), עומק שליפת top-k, חיתוכי reranker ומשקלי fusion – כולם משפיעים זה על זה. חיפוש רשת (grid search) ידני היה לוקח שבועות ועדיין היה משאיר אותנו בנקודת מקסימום מקומי.
עברנו לאופטימיזציה בייסיאנית (Bayesian optimization) כדי לחקור את המרחב. במקום לבדוק כל שילוב באופן מקיף, אלגוריתם החיפוש שומר על "אמונה" (belief) לגבי אילו קונפיגורציות צפויות לתפקד היטב ומצמצם בהדרגה את החיפוש לאזורים מבטיחים.
הפלט אינו הגדרה אחת מושלמת. זהו חזית פארטו (Pareto frontier) של אפשרויות. בקצה אחד, יש לנו קונפיגורציה רזה המותאמת לנקודת הקצה (endpoint) של ה-API שלנו עם תפוקה גבוהה: הסקה (inference) מהירה, recall מתון וזמן השהיה (latency) נמוך ככל האפשר. בקצה השני, יש לנו קונפיגורציה אגרסיבית עבור סקירה משפטית: שליפה עמוקה יותר, reranking כבד יותר וחפיפה הדוקה יותר, תוך הקרבת מילישניות לטובת יסודיות. מכיוון שהחזית מפורשת, אנחנו יכולים לבחור את הנקודה הנכונה עבור המוצר במקום להעמיד פנים ש"מידה אחת מתאימה לכולם".
איך המספרים נראים בפועל
השינויים הללו העבירו את המערכת מפורוטוטייפ שביר לצנרת (pipeline) ייצור מבוססת מדידה.
ה-Recall ב-ten השתפר מ-78 אחוזים ל-95 אחוזים. זה אומר שכאשר התשובה הנכונה קיימת בקורפוס שלנו, אנחנו מוצאים אותה תשע עשרה פעמים מתוך עשרים.
ה-Latency ב-percentile 95 ירד מ-850 מילישניות ל-320 מילישניות. ה-hybrid stack נשמע כבד יותר על הנייר, אך אינדוקס חכם יותר, rerankers קטנים יותר והיכולת להגיש chunks אגרסיביים רק כשצריך, הפכו את המערכת כולה למהירה יותר.
שיעור ההזיות (hallucination rate) – שעוקב על ידי מתייגים אנושיים על סט נתונים זהב (golden dataset) מופרד – ירד מ-12 אחוזים ל-3 אחוזים. כאשר המודל מקבל הקשר (context) מלא ורלוונטי, הוא מפסיק להמציא עובדות.
העלות לשאילתה ירדה מ-$0.008 ל-$0.005. שליפה טובה יותר פירושה prompts קצרים וממוקדים יותר ל-LLM ופחות ניסיונות התאוששות. ההוצאה הנוספת על embedding עבור הרחבת שאילתות נבלעת על ידי החיסכון בשלב היצירה.
בנו סט נתונים זהב והתייחסו לשליפה כאל קוד
אם תקחו דבר אחד מכאן, זה צריך להיות המשמעת של המדידה. בנינו סט נתונים זהב קטן של שאלות אמיתיות ומיקומי תשובות מאומתים. לפני שכל שינוי מגיע לייצור, הוא נבדק מול סט הנתונים הזה. ה-Recall וה-latency מנוטרים בזמן אמת, ולא רק "בעין" בתוך notebook.
שליפה היא לא דמו למחקר. היא תשתית. היא ראויה לבדיקות יחידה (unit tests), מבחני רגרסיה (regression benchmarks) ואופטימיזציה אוטומטית בדיוק כמו שאר ה-stack שלכם. בצעו chunking לפי מבנה המסמך, לא לפי אמונות תפלות של טוקנים (tokens). שילבו חיפוש וקטורי ומילות מפתח עם reranker. הרחיבו את השאילתות שהמשתמשים שלכם באמת כותבים. ואז תנו לאלגוריתם חיפוש לכוונן את הכפתורים במקום האינטואיציה שלכם.
ה-pipeline שתיארנו אינו תיאורטי. אתם יכולים לקרוא את המאמר המקורי כאן, ואם תרצו לדון בהנדסת שליפה (retrieval engineering) עם קהילה שאכפת לה מהדברים האלה, קבוצת ה-GyaanSetu AI group פתוחה בפניכם.
