பெரும்பாலான RAG டெமோக்கள் லேப்டாப்பில் பார்க்கும்போது மிகச்சிறப்பாகத் தெரியும். ஒரு இருபது பக்க PDF கோப்பை ஸ்கிரிப்டிற்கு அளித்து, ஒரு கேள்வியைக் கேட்டால், அது சரியான பத்தியைக் குறிப்பிடுவதை நீங்கள் காணலாம். ஆனால் அதே pipeline-ஐ தயாரிப்பு நிலைக்கு (production) கொண்டு செல்லும் போதுதான் அந்த உற்சாகம் முடிவுக்கு வருகிறது. சட்ட ஆவணங்கள் வாக்கிய அளவில் பாதியாகப் பிரிக்கப்படுகின்றன. அடர்த்தியான API குறிப்புகள், முக்கியமான தகவல்களை வழக்கமான (boilerplate) தேவையற்ற சத்தங்களுக்குள் மூழ்கடித்துவிடுகின்றன. தாமதம் (Latency) அதிகரிக்கிறது. பயனர்கள் காத்திருக்கிறார்கள், எரிச்சலடைகிறார்கள், பிறகு வெளியேறுகிறார்கள். நாங்கள் இந்தத் தடையை மிகக் கடுமையாக எதிர்கொண்டோம். எனவே, நாங்கள் retrieval லேயரை முழுமையாகத் தகர்த்து, அதை அளவிடக்கூடிய மற்றும் மாற்றியமைக்கக்கூடிய (tunable) ஒரு அமைப்பாக மீண்டும் உருவாக்கினோம். இதன் விளைவாக, பயனர் அனுபவத்தை மெதுவான ஸ்லைடுஷோவாக மாற்றாமல், 95% recall-ஐ எட்டும் ஒரு pipeline உருவானது.

ஏன் டெமோ RAG தயாரிப்பு நிலையில் (Production) தோல்வியடைகிறது

பொழுதுபோக்குத் திட்டங்கள் (hobby projects) மற்றும் ஆரம்பக்கட்ட தயாரிப்புகளில் (early-stage products) வழக்கமான ஸ்டேக் ஆச்சரியப்படும் விதமாக ஒரே மாதிரியாக உள்ளது: நிலையான டோக்கன் துண்டுகள் (fixed token chunks), சந்தையில் கிடைக்கும் எம்பெடிங்ஸ் (off-the-shelf embeddings) மற்றும் ஒரு ஒற்றை வெக்டர் தேடல் அழைப்பு (single vector search call). அந்த எளிமை கவர்ச்சிகரமானது, மேலும் உங்கள் தரவுத் தொகுப்பு (corpus) சுத்தமாகவும், சிறியதாகவும், கணிக்கக்கூடியதாகவும் இருக்கும்போது அது வேலை செய்யும். ஆனால் தயாரிப்பு நிலை தரவு (Production data) இவை எதுவுமே இல்லை. 512 டோக்கன்கள் கொண்ட ஒரு நிலையான துண்டு (fixed chunk), ஒரு SaaS ஒப்பந்தத்தில் உள்ள இழப்பீடு வழங்கும் நிபந்தனையை (indemnification clause) அதன் நடுவிலேயே துண்டிக்கக்கூடும். திடீரென்று உங்கள் retrieval லேயர், ஒரு சட்டக் கடமையின் பாதியை மட்டும் மொழி மாதிரியிடம் (language model) கொடுத்து, ஒரு பொறுப்பு குறித்த கேள்விக்கு பதிலளிக்கச் சொல்கிறது. சூழல் (context) சிதைந்துவிட்டதால், மாதிரி தவறான தகவல்களை (hallucinate) உருவாக்குகிறது.

பெரிய தொழில்நுட்ப ஆவணங்கள் இந்தப் பிரச்சனையை மேலும் சிக்கலாக்குகின்றன. API ஆவணங்கள் function signatures, அட்டவணைகள் மற்றும் code blocks ஆகியவற்றால் நிறைந்துள்ளன. ஒரு நிலையான விண்டோ (fixed window), ஒரு TypeScript interface-ன் நடுப்பகுதியைத் பிடிக்கலாம், ஆனால் அதற்கு மேலே உள்ள function name மற்றும் கீழே உள்ள பயன்பாட்டு உதாரணத்தைத் தவறவிடலாம். இதனால், பயனர் கேட்கும் உண்மையான திறனுக்குப் பதிலாக, embedding vector என்பது வெறும் தொடரியல் துண்டுகளையும் (syntax fragments) தேவையற்ற சத்தத்தையும் மட்டுமே குறிப்பதாக அமைந்துவிடுகிறது. Garbage in, hallucination out.

டோக்கன் எண்ணிக்கையை வைத்து அல்ல, கட்டமைப்பைக் கொண்டு துண்டாக்குங்கள் (Chunk by Structure, Not by Token Count)

நாங்கள் செய்த முதல் மாற்றம், துண்டுகளை (chunks) வெறும் டோக்கன்களின் தொகுப்பாகக் கருதுவதை நிறுத்தியதுதான். துண்டுகள் என்பவை அர்த்தமுள்ள அலகுகள் (semantic units). சரியான உத்தி நீங்கள் எதை இண்டெக்ஸ் (indexing) செய்கிறீர்கள் என்பதைப் பொறுத்தே அமையும்.

சட்ட ஆவணங்களுக்கு, ஆவணத்தின் படிநிலையை (hierarchy) மதிக்கும் 'recursive chunking' முறைக்கு நாங்கள் மாறினோம். இது பிரிவுகள் (sections), துணைப் பிரிவுகள் (subsections) மற்றும் நிபந்தனைகளை (clauses) எல்லைகளாகக் கருதுகிறது. ஒரு நிபந்தனை என்பது ஒரு அர்த்தமுள்ள அலகு என்பதால் அது முழுமையாக இருக்கும். நீங்கள் அதைத் துண்டித்தால், சட்ட ரீதியான தர்க்கம் சிதைந்துவிடும்.

API ஆவணங்களுக்கு, 'structure-aware chunking' முறை functions, classes மற்றும் endpoints ஆகியவற்றைத் தனித்தனி அலகுகளாகக் (atomic) கருதுகிறது. ஒரு துண்டில் ஒரு function signature, அதன் arguments மற்றும் அதன் docstring ஆகியவை இருக்கலாம். டோக்கன் எண்ணிக்கை அதிகரித்த காரணத்திற்காக மட்டும் அது அடுத்த utility function-க்குள் தன்னிச்சையாகப் பரவாது. இது embedding-ஐ ஒரு குறிப்பிட்ட திறனில் மட்டும் கவனம் செலுத்த வைக்கிறது.

சப்போர்ட் டிக்கெட்டுகள் (Support tickets) இன்னும் சிக்கலானவை. அவை உரையாடல் வடிவில், தொடர்ச்சியான விவாதங்களாக (threaded) மற்றும் நேரியல் அல்லாதவை (nonlinear). நிலையான துண்டுகள் (fixed chunks), ஒரே விவாதத் தொடரிலிருந்து ஒரு பொறியாளரின் நிலைத் தகவலையும் (status update) ஒரு வாடிக்கையாளரின் புகாரையும் எடுத்துக்கொண்டு, அவை இரண்டும் ஒரு ஒருங்கிணைந்த அலகு போலச் செயல்படுவதாகக் காட்டும். நாங்கள் 'semantic chunking' முறைக்கு மாறினோம்; அதாவது டோக்கன் வரம்பு முடிவடையும் போது அல்லாமல், தலைப்பு அல்லது பேசுபவர் மாறும்போது துண்டிக்கிறோம்.

நிறுவனங்களில் உள்ள உள் விக்கிகள் (Internal wikis) பெரும்பாலும் மிகவும் ஒழுங்கற்ற தரவுகளைக் கொண்டிருக்கும். வடிவமைப்பானது சீரற்றதாகவும், தலைப்புகள் விடுபட்டதாகவும், பிரிவுகள் ஒன்றோடொன்று கலந்தும் இருக்கும். இதற்காக, நாங்கள் LLM-அடிப்படையிலான chunking முறையைப் பயன்படுத்துகிறோம். ஒரு சிறிய மாதிரி (small model) முன்கூட்டியே படித்து, நாங்கள் embedding-ஐ உருவாக்குவதற்கு முன்பே தர்க்கரீதியான எல்லைகளைக் கண்டறிகிறது. இது எழுத்துக்களைப் பிரிப்பதை விட ஆரம்பத்தில் அதிகச் செலவைச் செய்தாலும், அதன் மூலம் கிடைக்கும் retrieval தரம் அந்தச் செலவை உடனடியாக ஈடுகட்டிவிடுகிறது.

கலப்புத் தேடல்: சிக்னல்களை இணைக்கவும் (Hybrid Retrieval: Combine Signals)

வெக்டர் தேடல் (Vector search) சக்தி வாய்ந்தது, ஆனால் அதில் சில குறைகள் (blind spots) உள்ளன. ERR_CONNECTION_REFUSED_0x800 போன்ற ஒரு துல்லியமான பிழை குறியீட்டை (error code) உள்ளீடு செய்தால், embedding space அவற்றை அருகருகே வைத்திருப்பதால், ஒற்றுமைத் தேடல் (similarity search) தொடர்பில்லாத ஒரு மாட்யூலுக்கான தீர்வு வழிகாட்டியைத் தரக்கூடும். துல்லியமான பொருத்தங்கள் (exact matches) முக்கியமானவை, ஆனால் வெக்டர் தேடல் மட்டும் அவற்றைச் சரியாகக் கண்டறியத் தவறிவிடலாம்.

BM25 மூலம் செய்யப்படும் Keyword search, இந்தத் துல்லியமான பொருத்தம் தொடர்பான சிக்கலை அழகாகத் தீர்க்கிறது. ஆனால் கருத்து ரீதியான தூரத்தைப் (conceptual distance) பொறுத்தவரை அது தடுமாறுகிறது. ஒரு பயனர் "performance degradation under heavy load" என்று கேட்டால், "slow throughput during traffic spikes" என்று விவரிக்கும் ஒரு கண்டறியும் குறிப்பை BM25 தவறவிடக்கூடும், ஏனெனில் அங்கு போதுமான keyword ஒற்றுமை இல்லை.

நாங்கள் இரண்டில் ஒன்றைத் தேர்ந்தெடுப்பதை நிறுத்திவிட்டு, இரண்டையும் இணையாக (parallel) இயக்கத் தொடங்கினோம். வெக்டர் மற்றும் keyword தேடல்கள் ஒவ்வொன்றும் அவற்றின் சொந்த வரிசைப்படுத்தப்பட்ட பட்டியல்களைத் (ranked lists) தருகின்றன. நாங்கள் அவற்றை Reciprocal Rank Fusion மூலம் இணைக்கிறோம். RRF அதன் செயல்திறனில் எளிமையானது மற்றும் மிகத் துல்லியமானது. இது ஒவ்வொரு பட்டியலிலும் ஒரு ஆவணம் எந்த இடத்தில் உள்ளது என்பதன் அடிப்படையில் அதற்கு மதிப்பெண் வழங்குகிறது. இரண்டு அமைப்புகளிலும் முன்னணியில் இருக்கும் ஆவணங்களுக்குப் பெரும் முன்னுரிமை கிடைக்கிறது. ஒரு இயந்திரம் மட்டும் விரும்பும் ஆவணங்கள் கூட இறுதித் தேர்வுக் குழுவில் (final candidate set) இடம் பெறுகின்றன.

After fusion, we run the top candidates through a cross-encoder reranker. This is not free. It adds roughly 50 milliseconds of compute. It also increases recall by 15%. The cross-encoder evaluates the full query and each candidate chunk together, producing a relevance score far more nuanced than a bi-encoder embedding ever could. That extra 50 milliseconds is a bargain. It prevents you from shipping a garbage context window to the LLM and spending two seconds waiting for a confused or hallucinated answer.

Users do not write queries like search engineers. They type "app broken." They paste cryptic log fragments. They ask vague, ambiguous questions. If you send those raw strings straight to the index, you get garbage back.

We transform every query before it touches the retrieval engine.

First, query expansion. The system generates multiple search terms from a single short question. A user asks, "How do I fix the timeout?" The engine expands that to cover connection timeouts, read timeouts, gateway timeouts, and retry logic. This approach alone moved our recall from 78% to 96%.

Second, query decomposition. Complex questions get broken into smaller sub-questions. A query like "What's the refund policy for enterprise customers past 90 days and how does it differ from monthly plans?" becomes two focused searches rather than one bloated embedding lookup. Each sub-question hits the index independently. The results are stitched back together downstream. This keeps retrieval narrow and precise, which stops the dilution that happens when a single embedding tries to match a dozen concepts at once.

Let Bayesian Search Tune Your Pipeline

If you are still hand-tuning chunk size, overlap ratios, and retrieval weights, you are leaving performance on the table. We stopped guessing.

We defined a search space where chunk size, overlap percentage, vector-versus-BM25 weights, and reranker thresholds are all variables. Then we applied Bayesian optimization. Instead of grid-searching through hundreds of random configurations, Bayesian search builds a probabilistic model of what works. It proposes a configuration, observes the recall and latency, updates its beliefs, and proposes the next one. Over time it converges on balances a human would never stumble into manually.

It found combinations we never would have tried. Smaller chunks with heavier overlap. A slightly lower weight on dense vector search paired with a more aggressive reranker threshold. These non-obvious tradeoffs gave us both higher recall and lower latency.

This is not a one-time setup task. We re-run hyperparameter optimization monthly. Your corpus drifts. User behavior shifts. Your pipeline should adapt instead of rusting in place.

The Payoff

The raw output of that rebuild is hard to argue with.

Recall at position ten went from 78% to 95%. When the correct answer lives in our knowledge base, we surface it nineteen times out of twenty. Latency at the 95th percentile fell from 850 milliseconds to 320 milliseconds. The chat feels instant instead of ponderous.

Better retrieval gave the language model better grounding. Hallucination rate dropped from 12% to 3%. When the model has the right context in front of it, it stops inventing facts. Cost per query fell by 38%. Faster, sharper retrieval means fewer tokens wasted on irrelevant context, retry loops, and verbose but useless prompts.

Build It Like Infrastructure

If you are moving from prototype to production, treat retrieval as infrastructure code rather than a configuration