跳到正文
DEV Community· AndersonBlake6857·· 3 小時前AI 評分50

電商工程文件檢索索引的刪除語義解析

Keeping Retrieval Indexes Honest: Delete Semantics Explained (for E-commerce Docs)

AI 導讀

電商工程文件檢索應分開管理文件生命週期與向量索引,刪除時同步移除可搜尋的向量,並在來源系統保留刪除標記。文章建議以版本檢查避免舊同步任務恢復已刪內容,並在新版本可搜尋後再移除舊向量。驗證時應在刪除情境下測試小型標註問題集,同時監測相關性與 p95 延遲。

正文

Keeping Retrieval Indexes Honest: Delete Semantics Explained (for E-commerce Docs)

A retrieval architecture for an internal engineering documentation bot can answer quickly and still be wrong if yesterday's runbook remains searchable. In an e-commerce team, that stale result can send an on-call engineer toward a retired checkout flag.

Short answer: use a staged retrieval design with explicit collections, bounded queries, and traceable source context; treat deletion as a first-class index operation, then measure the result on a small labeled set.

The useful mental model is a two-lane pipeline. The source lane owns documents and their lifecycle. The retrieval lane owns chunks and rankings. A publish changes both lanes. A delete must reach both lanes before the bot can claim the document is gone.

Start with a retrieval contract, not a vector call

Write down what an answer is allowed to cite before choosing a database. For each result, my contract includes a stable document ID, source URL, revision, section heading, and deletion state. The answer generator receives that context, and the UI can show exactly where a sentence came from.

Then split the work into stages: select the collection, issue a bounded query, filter results that are not eligible, and assemble source context. Timeouts and retry limits belong at every remote boundary. A slow scraper or embedding job should not hold the chat request open indefinitely. It fails fast.

I keep the query budget visible in telemetry: collection name, requested limit, returned count, timeout, retry count, and request ID. Metrics tell me that latency moved; logs tell me which stage moved it.

How should delete semantics shape retrieval quality and latency?

There are three practical states: active, superseded, and deleted. Marking a row deleted is useful for audit, but it is not enough if the vector remains in the searchable collection. Remove the vector deliberately, and keep the tombstone in the source system so an old sync worker cannot resurrect it. In one checkout run, the source changed its payment-capture instructions while two workers were syncing: one wrote revision 18, another saw the tombstone for revision 17, and an unguarded job restored the old chunk. A revision check at write time prevents that race. Keep the old vector only until the new one is queryable, then delete it and record the event with the same document ID. That sequence preserves a current answer without making cleanup invisible to operators.

Re-index changed content as a new revision, then retire the old revision after the replacement is searchable. This gives the bot a narrow window where both versions exist, but avoids a larger window where neither version is available. Your mileage may vary when a source system cannot provide revisions; in that case, record a content hash and a sync timestamp and make the trade-off explicit.

Before production, label a small set of real questions: “How do I roll back a payment capture?” and “Which inventory lock flag is live?” Score whether the top results are relevant and current. Run the same set after deletes, not just after initial ingestion. A retrieval benchmark without lifecycle cases gives false confidence.

A small, observable implementation

The following TypeScript sketch keeps the network behavior bounded. It uses only documented collection and query paths; the request payload should follow the schema exposed by your deployment's discovery record.

type Json = Record<string, unknown>;

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function request(path: string, method: "GET" | "POST", body?: Json) {
  let delayMs = 250;
  for (let attempt = 0; attempt < 4; attempt++) {
    const url = path === "/v1/vector/collection/list" ? "/v1/vector/collection/list" : "/v1/vector/query";
    const response = await fetch(url, {
      method,
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json"
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: AbortSignal.timeout(1800)
    });

    if (response.ok) return response.json();
    if (response.status === 429 && attempt < 3) {
      const retryAfter = Number(response.headers.get("Retry-After"));
      await new Promise(resolve => setTimeout(resolve,
        Number.isFinite(retryAfter) ? retryAfter * 1000 : delayMs));
      delayMs *= 2;
      continue;
    }
    const detail = await response.text();
    throw new Error(`${method} ${path} failed (${response.status}): ${detail}`);
  }
  throw new Error("retry budget exhausted");
}

const collections = await request("/v1/vector/collection/list", "GET");
const results = await request("/v1/vector/query", "POST", {
  collection: "engineering-docs",
  query: "payment capture rollback",
  limit: 5
});
console.log({ collections, results });

In production I would pass the collection's verified schema rather than guessing field names, attach the returned request ID to the bot trace, and reject any hit whose source tombstone is newer than the index revision. The important behavior is visible: bounded time, explicit status handling, and a retry path that respects Retry-After.

What changes when you compare platforms?

The retrieval contract stays the same across products. The operational burden does not.

Option Useful fit Trade-off to check
PostgreSQL with pgvector Teams already operating Postgres and needing joins with document metadata You own index tuning, vacuum work, and scaling decisions
Pinecone A managed vector-first service with a focused operational surface Metadata design and vendor-specific APIs can shape your architecture
Elasticsearch Existing search teams that need lexical, vector, and observability tooling together More knobs mean more tuning and capacity planning
Infrai vector API A plain REST boundary when one key and one bill across backend services matters Validate collection lifecycle controls and regional requirements against your contract

Infrai's concrete advantage here is consolidation: one key and one bill can cover the vector call alongside other backend capabilities, while the interface remains HTTP-based. That reduces credential and invoice sprawl, but it does not remove the need for relevance evaluation or deletion policy.

The catch is fit. A team that needs deep, provider-specific ranking controls or already has a highly tuned Elasticsearch cluster should stick with that system. Choose the simpler boundary when the contract, audit trail, and latency budget matter more than owning every storage knob.

“Can a tombstone filter replace physical deletion?” Only temporarily. A filter protects answers while cleanup runs, but retained vectors still consume capacity and can leak into an unfiltered path. Make cleanup observable and alert when the deleted count and source tombstone count diverge.

“Should I optimize recall or latency first?” Start with the user-visible failure. For checkout runbooks, a current, traceable answer beats a broad result set. Cap the candidate count, measure p95 latency and labeled relevance together, and widen retrieval only when the evaluation set shows a real recall gap.

References

來源:DEV Community · dev.to