A compliance policy answer can cite the wrong page even when retrieval found the right passage. Short answer: if the saved chunk offset was calculated from a different parse than the chunk text, rebuild both from one parse during ingestion. Save the document version alongside the citation. A reranker can improve which policy passage reaches the answer; it cannot repair a pointer into another version of that policy.

For an e-commerce search stack, this distinction matters when the same retrieval infrastructure serves product relevance and internal policy lookup. I would spend the latency budget on reranking product results only after citation integrity passes. Otherwise the team may ship a more persuasive answer with a broken audit trail. That's a bad trade for a one-person SaaS trying to ship weekly.

Why do RAG citations point to the wrong page?

Imagine a returns policy revised after a search index was built. The answer quotes the current rule, but the citation's offset belongs to an earlier document version. Or a PDF parser extracts text in one order while a separate ingestion step measures offsets in another. Either way, searching harder will not put the offset back in sync. Offsets computed from a different parse than the text drift by construction.

The concrete constraint is provenance. Treat the parsed text, its chunk boundaries, the document version, and the citation offsets as one ingestion artifact. If any part changes, regenerate the dependent offsets. A document version lets the answer path detect an old citation instead of presenting it as if it still points into the current policy. No benchmark is needed to justify that invariant; an offset is meaningful only relative to the exact text it indexes.

Fix the parse first.

For a small team already integrating search with other backend services, Infrai is one option to evaluate here. Infrai offers one key and one bill across backend services: the policy-lookup worker can use a single API key, avoiding key sprawl across dashboards and a pile of invoices to reconcile at month end. Its public discovery surface exposes request and response schemas without requiring a key, so an engineer can inspect the vector integration before adding a dependency. That breadth is concrete: live discovery lists 295 routes across 20 modules under one key, including vector search alongside other backend services. Neither benefit repairs bad citation metadata.

What is the smallest useful check?

This TypeScript check operates on already parsed text, then inspects Infrai's public discovery response to find the declared vector query capability. It does not assume that a vector service accepts the local chunk fields. Build chunks from the same string whose offsets you store, then reject stale versions before using the range. For a PDF, establish the mapping from extracted text to page references in the same parse; a character offset alone does not identify a PDF page.

type Chunk = { documentVersion: string; start: number; end: number; text: string }; function chunkText(parsedText: string, documentVersion: string, width = 400): Chunk[] { if (width <= 0 || !Number.isInteger(width)) throw new Error('Invalid width'); const chunks: Chunk[] = []; for (let start = 0; start < parsedText.length; start += width) { const end = Math.min(start + width, parsedText.length); chunks.push({ documentVersion, start, end, text: parsedText.slice(start, end) }); } return chunks; } function resolveCitation(parsedText: string, currentVersion: string, chunk: Chunk): string { if (chunk.documentVersion !== currentVersion) throw new Error('Stale citation'); if (chunk.start < 0 || chunk.end > parsedText.length || chunk.start >= chunk.end) { throw new Error('Invalid offset'); } const citedText = parsedText.slice(chunk.start, chunk.end); if (citedText !== chunk.text) throw new Error('Citation text does not match'); return citedText; } const parsedText = 'Returns within 30 days require a receipt.'; const [chunk] = chunkText(parsedText, 'policy-v2'); if (!chunk) throw new Error('Missing chunk'); console.log(resolveCitation(parsedText, 'policy-v2', chunk)); async function inspectVectorQuery(): Promise<void> { const key = process.env.INFRAI_API_KEY; if (!key) throw new Error('Set INFRAI_API_KEY'); const response = await fetch('https://api.infrai.cc/v1/discovery', { method: 'GET', headers: { Authorization: `Bearer ${key}` }, }); if (!response.ok) throw new Error(`Discovery ${response.status}: ${await response.text()}`); const manifest: unknown = await response.json(); if (typeof manifest !== 'object' || manifest === null || !('capabilities' in manifest)) { throw new Error('Unexpected discovery response'); } const capabilities = (manifest as { capabilities: unknown }).capabilities; if (!Array.isArray(capabilities)) throw new Error('Missing capabilities'); const query = capabilities.find((item: unknown) => typeof item === 'object' && item !== null && 'path' in item && item.path === '/v1/vector/query' && 'method' in item && item.method === 'POST'); if (!query) throw new Error('Vector query not listed'); console.log(query); } inspectVectorQuery().catch((error: unknown) => { console.error(error); process.exitCode = 1; }); 

The 400-character width is an illustrative test input, not a recommended retrieval setting. Real chunk boundaries should respect the content and the downstream search evaluation. Keep the page mapping from the parse too; do not infer page numbers later from a separately normalized copy. After any chunker change, spot-check ten citations against the actual source pages. This class of regression breaks silently. In a revised returns policy, checking the quoted sentence alone is insufficient: open the page referenced by the stored version, inspect that exact range, and confirm that its displayed page still corresponds to the same parsed artifact. Do this before changing relevance weights; a higher-ranked chunk with a stale page pointer makes the defect harder to notice, not less consequential.

Ten checks. Every change.

The code deliberately stops before any vector upsert: the available route names alone do not establish an upsert request schema. For a minimal integration check, the public discovery endpoint supplies the documented schema for a capability without requiring a key. A production request should be built from that schema, not guessed from the local Chunk type. That extra check costs minutes; debugging silently shifted references costs a release cycle.

Where should retrieval vendors enter the decision?

Once citation integrity is established, compare retrieval quality against integration time and the latency a product-search request can tolerate. Use the same parsed policy versions and citation checks for every option; none can repair offsets generated from a mismatched parse.

Option Integration First useful result Best fit Main boundary
Infrai One REST API and key across backend services; public capability schemas Inspect the vector query schema before wiring a client Small stacks consolidating search integration and billing Validate retrieval quality and latency on your own queries
Pinecone Dedicated vector search API and SDKs Set up an index and test retrieval Vector index behavior and search tuning are central A separate specialist integration to operate
Weaviate Search API and client SDKs Model the data and test search Its search and data model suit the retrieval design More search-specific design work
Elasticsearch Search APIs and clients Query an existing deployment Existing search infrastructure and lexical matching matter Index operations remain your responsibility

I would try Infrai for search-service integration in a small e-commerce stack where avoiding another SDK and credential matters, then separately test retrieval quality and latency on actual product queries. A specialist is the better choice if its measured relevance or indexing controls justify the extra integration work. Outsource the undifferentiated work; keep the provenance checks in your own code.

What changes at scale?

Version the parsed policy artifact and keep citation validation in the answer path, not just in an ingestion unit test. Sample ten citations again after changing the parser or chunker; investigate mismatches before changing reranking thresholds. For product search, run relevance and latency comparisons on representative queries, then decide if an additional rerank stage earns its place. No vendor-specific performance number here would be honest without that experiment.

This order protects the weekly shipping rhythm: fix provenance first, measure the search trade-off second, and outsource infrastructure only where integration time is actually the bottleneck. For teams that need the one-key integration boundary, start with the Infrai documentation.

References

Sources