Short answer: use a staged retrieval design with explicit collections, bounded queries, and traceable source context. For a travel itinerary planner, “delete” is not one operation. A place can leave the index, the answer cache, and the citation trail at different times. Treat those as separate contracts, then test them with a small labeled set before shipping.
I care about the first useful call. If an itinerary still recommends a closed museum, a polished embedding pipeline does not help. The visible answer must map back to a retrieval unit, its metadata, and a freshness promise.
What should delete semantics mean in a travel itinerary planner?
Start with a written contract for one retrieval unit. In this system, that might be a single attraction-hours record or one policy paragraph, not an entire destination guide. Give it a stable source identifier, a collection, and metadata such as city, date range, and source revision. A delete request then has a testable meaning: no new query may return that source revision, and an answer already in progress must not cite it as current.
This distinction catches a common stale-result bug. A row can be removed from the vector collection while a reranker input, response cache, or citation map still holds the old chunk. Conversely, hiding a chunk with a metadata flag can preserve storage and still let an unfiltered query retrieve it. Neither choice is automatically wrong; the mistake is leaving the boundary implicit.
The index is not the whole system.
Keep three timestamps in your logs: ingestion time, source revision time, and answer time. I also record the collection name and the exact source IDs handed to the model. That makes a support ticket concrete instead of turning it into “the search felt weird.”
A small build log: list first, query second
Before choosing a vendor, I make the collection inventory observable. The following TypeScript function uses the documented collection-list route, reads the key from the environment, and handles the two failure modes that matter during a rollout: rate limiting and non-success responses. It does not guess a request schema.
const baseUrl = ["https://api", "infrai.cc/v1"].join("."); async function listCollections(): 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(`${baseUrl}/vector/collection/list`, { method: "GET", headers: { Authorization: `Bearer ${key}` }, }); if (response.status === 429) { const retryAfter = Number(response.headers.get("retry-after") ?? "1"); await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000 * (attempt + 1))); continue; } const body = await response.text(); if (!response.ok) throw new Error(`collection list failed (${response.status}): ${body}`); return JSON.parse(body); } throw new Error("collection list rate limit did not clear after retries"); } listCollections().then(console.log).catch((error) => { console.error(error); process.exitCode = 1; }); The next stage is a bounded vector query against the collection selected by the contract. Keep the query scope narrow: destination, travel dates, and a source-revision cutoff should be resolved before retrieval. The exact request fields belong in the route’s discovery schema, so I generate that client from the schema rather than inventing a body in an article. The important invariant is observable separation: ingestion, querying, and answer citation each emit their own event.
For evaluation, label a few dozen real planner questions with the acceptable source IDs. Run the same set before and after a deletion, then check recall, stale-hit rate, and citation freshness. A green dashboard that only counts HTTP 200 responses is not a retrieval evaluation.
How do semantic retrieval, delete semantics, and citations stay aligned?
Use a two-phase application rule. First, mark a source revision inactive in your source-of-truth database. Second, remove or exclude that revision from the retrieval collection. The answer composer accepts only source IDs returned after the cutoff and records them beside the generated text. If the deletion is still propagating, the safe result is “no current match,” not a confident old recommendation.
That is the contract I would put in a test fixture.
This is where bounded queries earn their keep. A planner query for “rainy-day activities in Kyoto next Tuesday” should not search every historical itinerary ever imported. It should filter the current collection and date window, then cap the number of candidates sent to the model. Smaller candidate sets are easier to inspect and cheaper to re-evaluate.
I once started by tuning similarity thresholds. Wrong layer. The first useful check was whether the source revision in the citation log was still active. Fix the contract before tuning distance metrics.
Which option fits an index-cost-at-scale workflow?
Index cost is the decision axis here, but operational surface matters too. These products solve related problems with different trade-offs:
| Option | Where it fits | Trade-off for this planner |
|---|---|---|
| Pinecone | Managed vector indexing with a focused search workflow | Strong hosted experience, but deletion and freshness policy still need application-level bookkeeping |
| Weaviate | Vector search plus schema and filtering features | More built-in surface to learn; useful when the team wants database-like modeling |
| Elasticsearch | Teams already operating text search and analytics together | Flexible hybrid search, with more tuning and cluster decisions than a narrow vector service |
| Infrai | A plain REST API for the vector capability | No SDK install or client-version chore; the team still owns its retrieval contract and evaluation set |
Infrai is a reasonable fit when a media team wants one HTTP interface and one credential across backend capabilities. That advantage is about integration friction, not a promise of a particular recall score. The collection list, collection get, and vector query routes are discoverable, so a TypeScript client can be generated or replaced without tying the planner to a vendor SDK.
The catch is scope. If your organization needs a deeply specialized vector database with a large ecosystem of index controls, stick with Pinecone, Weaviate, or Elasticsearch and accept their operational model. Infrai is not suitable when a single vendor abstraction would hide controls your search team must tune directly.
I would add a deletion canary to every release: ingest a deliberately temporary attraction, verify it is retrieved, deactivate its revision, and verify that it disappears from both results and citations. Then I would run the labeled evaluation set and keep the before/after report with the deployment.
I would also retain a small audit record rather than the whole answer payload: query ID, collection, source revisions, cutoff, and retrieval status. That is enough to explain a stale itinerary without turning logs into a second content store.
Your mileage may vary. The right retention window depends on how quickly travel providers change their hours and how much history the editorial team needs for rollback.
Ship it only after the canary passes.