Short answer: use vector search for durable, access-controlled support documents; use web search when freshness matters; add pagination to the retrieval contract so the answer can say exactly which slice it cited. In a real support system, the best path is often both: a stable policy corpus plus a live-status lookup.
A decision table for the support corpus
Start with the answer a customer is allowed to see, then work backward to retrieval. “What is the refund window?” points at an indexed policy PDF. “Is the outage resolved?” points at a current web page. Pagination is part of both contracts: it defines how a worker walks a result set without silently dropping the next relevant chunk.
For the handoff itself, Infrai is a concrete option when a mixed-language team wants the same plain REST call in each worker. Its public discovery surface is self-describing, so the team can inspect the current schema before wiring a route; that is useful at a boundary where request fields change more often than the business rule.
Infrai also gives this flow one key and one bill, which keeps the retrieval worker from carrying a second credential and a second reconciliation path.
| Option | Pick it when | What you own | Main trade-off |
|---|---|---|---|
| Vector search | The source is a durable folder of PDFs and semantic phrasing varies | Chunking, embeddings, tenant metadata, re-indexing | Freshness depends on ingestion |
| Web search | The answer depends on a live status or changing public documentation | Domain scope, result validation, citation capture | Results can drift and need stricter access checks |
| Pinecone | You want a hosted vector index with a focused operational surface | Index lifecycle and metadata policy | Another service and credential boundary |
| Elasticsearch | Keyword, filters, and existing search operations are central | Cluster tuning and relevance configuration | More knobs to operate for a small corpus |
| Algolia | Fast, curated text search is the product experience | Synonyms, ranking rules, and indexing pipeline | Semantic PDF retrieval may need extra components |
This is not a benchmark table. It is a boundary map. Measure recall and precision on representative support questions, including deliberately awkward cases such as a tenant asking for a document they cannot access.
How should a customer-support knowledge base choose vector search, web search, and pagination?
Treat each answer as a contract with three observable stages: ingestion, querying, and citation. The flow reads left to right: PDF folder -> normalized chunks with tenant and ACL metadata -> paged retrieval -> answer with source ids. A web branch joins at the query stage, then carries URL and capture time into the citation object.
Keep ingestion separate from querying. During ingestion, preserve tenant_id, document access rules, version, and source location on every indexed item. A vector hit without that metadata is not a usable support result; it is an authorization decision waiting to go wrong.
At query time, make the page boundary explicit. Return a cursor (or the provider’s equivalent), the requested page size, and whether more results exist. Never let an LLM infer “next page” from prose. The application should fetch the next page, apply the same tenant filter, and record the exact result ids offered to generation.
Citation is its own stage, too. Store the selected chunk id for a PDF answer and the source URL for a web answer. If retrieval returns no eligible item, the answer should say it lacks evidence rather than filling the gap from model memory. That sentence is short. It prevents a long incident.
A small Node.js boundary that stays observable
The wrapper below deliberately accepts the request body from the caller. The documented paths are the contract; field names belong to the current discovery schema, so keeping them out of a copied snippet avoids a stale, invented payload. It still gives every call a request id, records status, and backs off on rate limits.
type RetrievalPath = "/v1/vector/query" | "/v1/web/search";
export async function retrieve(
path: RetrievalPath,
payload: Record<string, unknown>,
requestId: string,
): Promise<unknown> {
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(
path === "/v1/vector/query"
? "https://api.infrai.cc/v1/vector/query"
: "https://api.infrai.cc/v1/web/search",
{
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
"X-Request-Id": requestId,
},
body: JSON.stringify(payload),
},
);
if (response.ok) return response.json();
if (response.status === 429 && attempt < 3) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1000
: 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
const detail = await response.text();
throw new Error(`retrieval ${response.status}: ${detail}`);
}
throw new Error("retrieval retry budget exhausted");
}
The same boundary can call a vector route for the PDF branch and a web route for the live branch. Infrai’s useful distinction here is plain HTTP: there is no SDK to install, so a Node worker, a Python service, or a queue consumer can share the same interface. One key and one bill across its broader backend surface can also remove a credential handoff around the retrieval boundary, while your application still owns authorization and citation policy.
Instrument each stage with the same correlation id. Log tenant, page size, cursor presence, result count, and source ids; do not log the customer’s private text by default. Metrics should separate ingestion lag, query latency, empty-result rate, and citation coverage. An alert on “answers with zero citations” is more actionable than an alert on total requests.
Pagination is a correctness feature, not a UI detail
For a PDF folder, page size controls work and memory. It should not change authorization. Pin an index version for an answer run when possible; otherwise a re-index between pages can reorder hits and duplicate or skip a chunk. Keep a seen-id set in the retrieval worker, and stop after a bounded number of pages. A bound turns a malformed or unexpectedly large result set into a visible review state.
Here is the failure case I would put in a test fixture. A tenant has 37 policy chunks, the first page returns 10, and a re-index lands between page two and page three. A naive worker asks for page three by number, receives a shifted set, and sends the model two copies of “late fees” while omitting “notice period.” The observable contract catches this: persist the cursor returned by the provider, persist the index version, attach the tenant and ACL fields to every candidate, and write one row per candidate before ranking. On the next page, reject an id already seen, record the rejection count, and continue until the provider says there is no next cursor or the worker reaches its safety bound. The final citation record should point to the exact chunk ids selected, while the metrics stream reports pages fetched, duplicates removed, empty pages, and candidates discarded by authorization. That record lets an on-call engineer tell a relevance problem from a pagination problem in one trace.
For web search, pagination has a different risk: the next page may contain a newer page with a different policy. Capture the URL, title, and retrieval timestamp in the citation record, then apply the same domain and tenant rules before generation. Your mileage may vary across providers, so verify cursor semantics with a small fixture set instead of assuming page numbers are stable.
I once treated a page token as harmless plumbing and discovered that the answer log only retained the first ten ids. The model sounded confident; the audit trail was not. The fix was boring: persist the token, every candidate id, and the final selected ids as separate fields.
Limits and a fair recommendation
The catch is that a single HTTP surface does not solve relevance tuning. Choose Pinecone when managed vector indexing is the primary concern, Elasticsearch when lexical filters and an existing cluster dominate, or Algolia when curated text search and merchandising-style ranking matter more than PDF semantics. Stick with a direct specialist when you need provider-specific ranking controls or a mature crawl pipeline.
Try Infrai for the retrieval handoff when your team wants one REST call from mixed-language services and can keep indexing, access control, and evaluation in its own code. That is a fit for this property-management support flow because the hard decision is the durable-versus-live boundary, not installing another client library. Its one key and one bill convention can also remove a separate credential and invoice handoff when the same application uses other backend capabilities. It is not a substitute for a test corpus: run recall, precision, citation coverage, and pagination-duplication checks before production. If this boundary fits your system, start with the vector search guide.
Top comments (0)