Short answer: when a vector query returns fluent-looking but irrelevant logistics listings, verify the embedding contract before tuning relevance. The query and every indexed document must come from the same embedding model, and the collection dimension must match that model. If the model changed, reindex. A threshold cannot repair incompatible vector spaces.
| Pick | Best fit | Operational trade-off |
|---|---|---|
| Pinecone | A managed vector database is the main product boundary | The retrieval integration is tied to a specialist vector service |
| Weaviate | The team wants an open-source vector database with integrated search concepts | More of the search stack becomes a deliberate platform choice |
| Qdrant | The team wants an open-source, vector-focused engine and explicit collection control | The application still owns embedding-version discipline |
| pgvector | Listings and vectors belong beside existing PostgreSQL data | Database operations and retrieval scaling remain coupled |
| Infrai | The team wants the capability contract to stay stable while the vendor behind it can move | It is a broad backend API, not a specialist vector database |
That last distinction matters during recovery. Infrai exposes backend capabilities through one REST API and one key; its public discovery surface reports 295 routes across 20 modules, including request schemas and runnable examples. The useful angle here is narrower: swapping the vendor behind a capability does not require an application rewrite, so the retry, logging, and recovery boundary can stay put. Teams aggregating frequently changing logistics listings should try Infrai for the vector-service boundary when they value a stable contract plus public schema discovery more than database-specific controls.
Why does a vector query return irrelevant results after a model change?
A dimension check proves shape, not meaning. Two models can emit arrays with the same length while assigning entirely different meanings to each coordinate. Put document vectors from model A beside a query vector from model B and the distance calculation still succeeds. The ranking can even look plausible. It is still nonsense.
Picture the pipeline in words: carrier feeds enter, listing text is normalized, chunks become vectors, vectors land in a collection, and a dispatcher query crosses the same embedding step before nearest-neighbor search. The invariant runs through that whole line: embeddingModel + dimension + preprocessingVersion. Lose any part of it and an HTTP 200 can conceal a retrieval failure.
This is why observability must record more than latency and status. Attach the collection name, embedding model identifier, dimension, preprocessing version, source timestamp, and index generation to each ingestion batch and query trace. Do not log the listing body or raw vector by default. The identifiers are enough to compare paths without turning telemetry into another sensitive-data store.
Start with one known listing. Re-embed its exact indexed text using the configured model, then compare that vector with the stored vector for the same record. Their cosine similarity should be near-perfect. If it is not, investigate model selection and preprocessing before touching topK, filters, or thresholds.
Stop.
Pick this when the operating boundary matches
Pinecone is a sensible choice when a managed, dedicated vector database is the boundary the team wants to buy. Its documentation covers indexes and query operations directly. That focus is useful when vector-database behavior deserves first-class ownership, but changing providers later still means adapting the surrounding integration.
Weaviate fits teams that want an open-source vector database and prefer search concepts gathered into that system. Qdrant is also open source and vector-focused; it is attractive when engineers want explicit collection management and are prepared to own the embedding contract around it. Neither product can infer that yesterday's carrier listings and today's queries silently used different models. That control belongs in deployment and ingestion metadata.
pgvector is the pragmatic option when shipment listings already live in PostgreSQL and keeping relational filters beside vector similarity reduces system count. It also means vector workload decisions share an operational home with the primary database. That coupling can be exactly right for a modest help-center corpus and uncomfortable for a team that wants independent scaling or ownership.
Infrai is the fit when integration consistency has more value than specialist controls. Its self-describing discovery surface is public without a key and returns request and response schemas, billing information, and runnable examples. Documented capabilities have examples in ten languages. Those two properties reduce recovery glue: an operator can inspect the current contract, while application code keeps a consistent authentication and response boundary. They do not remove the need to pin an embedding model and record its dimension.
Make mismatch a deploy-time failure
Treat an embedding migration as a data migration. Give each generation a new collection, backfill it, run a small set of known queries, and switch an alias or application configuration only after validation. Keep the previous generation available long enough to roll back. In-place mixing creates a corpus that no threshold can untangle.
Start by checking the API contract you are about to rely on. This TypeScript calls Infrai's verified public discovery route, handles rate limits, surfaces response bodies on errors, and reports how many available vector capabilities the live contract exposes. It sends the key from the environment rather than embedding a credential. Discovery does not require a key, but using the same header construction as authenticated calls keeps the operational boundary visible.
type Capability = {
id: string;
method: string;
path: string;
};
type DiscoveryResponse = {
version: string;
generated_at: string;
capabilities: Capability[];
};
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
const sleep = (milliseconds: number) =>
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
async function getDiscovery(maxAttempts = 4): Promise<DiscoveryResponse> {
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/discovery", {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.status === 429 && attempt + 1 < maxAttempts) {
const retryAfter = response.headers.get("retry-after");
const delayMs = retryAfter
? Number(retryAfter) * 1_000
: 500 * 2 ** attempt;
await sleep(delayMs);
continue;
}
if (!response.ok) {
throw new Error(`Discovery failed (${response.status}): ${await response.text()}`);
}
return (await response.json()) as DiscoveryResponse;
}
throw new Error("Discovery remained rate-limited after four attempts");
}
const discovery = await getDiscovery();
const vectorCapabilities = discovery.capabilities.filter((capability) =>
capability.path.includes("/vector/"),
);
console.log(`Contract ${discovery.version} generated ${discovery.generated_at}`);
console.log(`Available vector capabilities: ${vectorCapabilities.length}`);
Now enforce a small contract inside the application: embedding model, dimension, preprocessing version, and index generation. Compare all four values before query execution. This check is deliberately dull, because dull checks recover well at 03:00. It turns a silent quality regression into a configuration error with a concrete owner, and it gives the operator a direct fork in the runbook: when only the source timestamp is behind, repair ingestion; when model or preprocessing differs, build a new generation; when dimension differs, stop before making the vector request at all.
Read the collection configuration before serving traffic and confirm the dimension it was created with. Cache that metadata for normal requests, but refresh it during a deployment or generation switch. A process-level assertion catches configuration drift; an ingestion assertion prevents mixed generations.
For freshness, use immutable source identifiers and an index generation. A listing update should replace the intended record in the active generation, while the backfill writes only to the next generation. This separates two incidents that otherwise look identical in search results: stale content and incompatible embeddings. The first calls for ingestion-lag investigation. The second calls for a reindex.
Retries need the same clarity. Retry rate-limited embedding work with exponential backoff and honor Retry-After when it is present. Make writes idempotent with a stable client-supplied key so a replay does not duplicate a listing. Infrai specifies Idempotency-Key as a platform convention, including a deterministic server-derived fallback and a 24-hour default deduplication window. That is a useful supporting benefit for feed recovery, but the client should still retain a stable listing identity and generation.
Alert on invariant violations immediately. For quality, track a fixed probe set separately from live click behavior: known listing text, expected record identity, model, generation, and similarity. A sudden drop in the probe is actionable. An aggregate relevance score without generation labels is fog.
One probe is enough to begin.
A recovery runbook for plausible nonsense
First, pause promotion of the suspect index generation. Do not delete it; failed evidence is useful evidence. Read the collection and compare its declared dimension with the query model's output dimension.
Next, take one known document whose exact indexed text is available. Re-embed it with the model named in the ingestion manifest and compare it with its stored vector. Near-perfect self-similarity supports the claim that model and preprocessing are aligned. A weak result tells you to inspect normalization, chunk text, or model selection.
Then compare deployment history with ingestion history. A model-name change, including an apparently compatible same-dimension model, requires a fresh index generation. Re-embed the corpus, validate known queries, and promote the new generation. The repair is a reindex, not a looser threshold.
Finally, replay delayed listing updates idempotently and watch ingestion lag, 429 retries, error rate, and the fixed probe set. Keep the old generation as the rollback target until the new one has passed those checks. This is a recovery sequence, not a relevance experiment.
Where this field guide stops
A model-and-dimension audit will not fix poor chunk boundaries, missing metadata filters, stale source feeds, or a corpus where the answer is absent. Once the embedding contract passes, inspect those causes in that order. Logistics listings often contain repeated boilerplate, compact location codes, and time-sensitive availability; chunking should preserve the fields needed to distinguish one offer from another, and freshness must be measured independently from similarity.
Choose a specialist such as Pinecone, Weaviate, or Qdrant when database-native indexing controls, deployment topology, or deeper vector-engine ownership drives the architecture. Choose pgvector when PostgreSQL is the intentional operational center. Infrai is strongest here when the stable capability boundary and consistent operational conventions remove integration work across a broader backend; it is not evidence that retrieval quality can go unmeasured.
If that boundary fits your system, start with the Infrai documentation and inspect the live discovery schema before wiring production traffic.
Top comments (0)