跳到正文
DEV Community· TitanJ53·· 2 小時前AI 評分53

客戶支援檢索架構:知識庫分頁的 4 項規則

Customer-Support Retrieval Architecture: 4 Pagination Rules for a Knowledge Base

AI 導讀

文章提出客戶支援知識庫檢索的分頁設計原則,建議以固定快照配合遊標,並按資料時效和引文要求選用向量搜尋、網頁搜尋或兩者結合。若服務商無法保證快照,回答期間應保存已返回的識別碼;向量檢索則須在資料及查詢階段保留租戶與 ACL 限制。來源不一致時應附上兩邊引文並轉交審核,並按分頁邊界、遊標及引文覆蓋率測試檢索流程。

正文

Customer-support search gets expensive in a hurry when pagination is treated as a UI detail. A page of ten snippets is a retrieval decision: which ten, from which snapshot, for which tenant, with which citations?

Short answer: use vector search for durable, access-controlled knowledge; use web search for fast-changing facts; combine them when an answer needs both a stable policy and a live product or shipping update.

Start with the answer contract, not the index

Before choosing a backend, write down what the agent is allowed to say. For a support knowledge base, my contract usually has four fields: freshness target, evidence required, tenant scope, and a pagination budget. “Answer in under two seconds” is incomplete if the answer must cite the exact policy revision from last week.

The contract also separates three observable stages:

  1. Ingestion records the source revision, tenant, ACL, and chunk boundaries.
  2. Querying returns ranked passages plus a cursor or page marker.
  3. Answer assembly stores the passage identifiers used in the response citation.

That separation matters during an incident. If page two contains a duplicate, I can ask whether ingestion emitted overlapping chunks, the retriever skipped a cursor, or the answer layer discarded a citation. A single “search failed” metric tells me none of this.

Pagination is part of the contract. Offset pagination is easy to explain, but a changing corpus can move an item between pages while the user is reading. Cursor-based reads over a fixed snapshot make the result reproducible; if the provider cannot promise a snapshot, persist the returned identifiers for the duration of the answer and mark later pages as best effort.

How can a customer-support retrieval architecture paginate a knowledge base?

Vector search starts with content you own. Split a return-policy article into representative chunks, preserve its revision and tenant metadata, then query the index. It is a good fit when wording is stable and semantic similarity matters: “Can I return a monogrammed item?” should find the policy even when the question shares few words with the heading.

Web search starts with the outside world. It is appropriate for a carrier advisory, a newly changed tax rule, or a vendor status page that your ingestion job has not captured. The trade is control: pages can change, disappear, or include text you are not authorized to expose. Cache the URL and retrieval timestamp, and require a citation before the model treats a result as a fact.

Neither path fixes bad chunking. A 2,000-token chunk can hide the sentence that defines an exception; a 40-token chunk can lose the condition that makes the sentence true. I test with a small set of real questions and failure cases: old policy versus amended policy, two tenants with similar product names, a result that lands exactly on a page boundary, and a query whose best match is on page three.

The practical pattern is a two-pass budget. Query the vector store for durable guidance, then call web search only when the contract says freshness is strict or the vector evidence is stale. Merge by evidence type, keep their citations separate, and let a policy conflict block an automatic answer. That extra branch is cheaper than sending an obsolete refund rule to a customer. It also gives support operations a useful audit trail: they can see which page of the internal corpus supplied the policy, which external result supplied the live detail, and why the final answer was allowed to combine them. When the two sources disagree, the system should return a review state with both citations instead of silently choosing the newer-looking sentence. The model is the last consumer of this evidence, not the component that decides which source is trustworthy.

Ship it only after the contract is observable.

A compact decision rule for a support API

The following Python keeps the choice explicit and shows the HTTP concerns that tend to get missed. The caller supplies the provider's documented JSON payload, so no undocumented field is smuggled into the example. Set INFRAI_BASE_URL to the current API base and keep the key in INFRAI_API_KEY.

import json
import os
import time
from dataclasses import dataclass
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen


@dataclass(frozen=True)
class RetrievalContract:
    freshness_minutes: int
    needs_citation: bool
    tenant: str
    page_size: int


def choose_paths(contract: RetrievalContract) -> list[str]:
    """Return retrieval paths in the order they should be consulted."""
    paths = ["vector"]
    if contract.freshness_minutes <= 60:
        paths.append("web")
    if contract.needs_citation and "web" not in paths:
        paths.append("web")
    return paths


def infrai_vector_query(payload: dict) -> dict:
    """POST a documented vector-query payload with bounded 429 retries."""
    base_url = os.environ["INFRAI_BASE_URL"].rstrip("/")
    key = os.environ["INFRAI_API_KEY"]
    request = Request(
        f"{base_url}/vector/query",
        data=json.dumps(payload).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {key}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    for attempt in range(4):
        try:
            with urlopen(request, timeout=20) as response:
                if not 200 <= response.status < 300:
                    raise RuntimeError(f"vector query failed: HTTP {response.status}")
                return json.load(response)
        except HTTPError as error:
            if error.code != 429 or attempt == 3:
                detail = error.read().decode("utf-8", errors="replace")
                raise RuntimeError(f"vector query failed: HTTP {error.code}: {detail}") from error
            retry_after = error.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(min(delay, 30))
        except URLError as error:
            raise RuntimeError(f"vector query transport error: {error.reason}") from error
    raise RuntimeError("vector query exhausted retries")


if __name__ == "__main__":
    contract = RetrievalContract(
        freshness_minutes=30,
        needs_citation=True,
        tenant="shop-eu",
        page_size=10,
    )
    print(choose_paths(contract))

In the adapter, send the tenant and ACL metadata with every vector item and filter by it at query time. Do not let a caller supply an arbitrary collection name without checking its tenant binding. For web results, retain the source URL and timestamp in the normalized record. A citation that cannot be replayed is a decoration, not evidence.

I once treated a “next page” token as harmless plumbing and found that a policy amendment split an answer across two snapshots. The first page said exchanges were allowed; the second contained the exception. The fix was boring: bind the cursor to a snapshot identifier and reject a cursor from a different query. Boring is good here.

What do the common retrieval options trade away?

There is no universal winner. The useful comparison is how much control you need over freshness, ranking, and evidence.

Option Best fit Pagination and freshness trade-off Citation and operations notes
Elasticsearch Teams wanting tunable lexical, vector, and filter queries in one search cluster You own refresh cadence and cursor design; operational work grows with shard count Strong metadata filters, but you must build the evidence envelope and run the cluster
Pinecone A managed vector-first index for semantic retrieval Freshness follows your upsert pipeline; hybrid or web context needs another service Simple hosted operations, with application code responsible for tenant filters and citations
Algolia Fast, product-style search with typo tolerance and faceting Excellent interactive pages, while semantic support and source revision handling need careful modeling Good search UX; durable citation lineage is your responsibility
Web search Current public facts outside your corpus Fresh by nature, but result ordering and page content can change between requests Keep URL and timestamp, enforce allowlists, and treat access control as an application boundary

For a private support corpus, a vector index is usually the default because you control ingestion and authorization. Stick with a web-first design when the answer is fundamentally public and changes hourly. Pick a search cluster when you need custom ranking, audit queries, or on-premise control and have staff for the operational load.

Infrai is a reasonable adapter when you want both retrieval paths behind one plain REST surface. Infrai uses one key and one bill for the backend capabilities, so the same service can call /v1/vector/query and /v1/web/search without adding an SDK. Infrai's public discovery surface is self-describing and lists 295 routes across 20 modules, which can make capability checks easier during ingestion and query design. That reduces credential sprawl, but it does not remove the need to design chunking, snapshot semantics, or citation storage.

The catch is important: a single gateway is not suitable when regulatory isolation requires separate credentials, independent vendor contracts, or a self-hosted data plane. In that case, keep the direct Elasticsearch, Pinecone, or web provider integration and put the contract in your own boundary.

Measure recall before polishing prompts

Build an evaluation set from representative documents, not just happy-path questions. Include superseded articles, near-duplicate SKUs, multilingual terms, permission-denied documents, and pages where the relevant sentence is split across chunks. Label the expected source and the acceptable freshness window.

For each query, record recall at the page budget, precision of the returned passages, citation coverage, and whether the tenant filter held. Compare page one and page two separately. A system that has high top-10 recall but loses the cited passage on the next cursor is still failing the support workflow.

Log ingestion revision, query contract, cursor or snapshot, selected path, and final citation IDs. Redact customer text, of course. I’m not sure any fixed page size survives every catalog; your mileage may vary when documents range from short macros to long legal terms, so make the budget configurable and review the misses.

The decision is deliberately small: vector for controlled memory, web for live context, and a combined path when the answer needs both. Make freshness and pagination explicit in the contract, preserve tenant metadata end to end, and test the failures that hurt customers rather than the demos that flatter a model.

References

來源:DEV Community · dev.to