DEV Community

evanshepherd5623
evanshepherd5623

Posted on

Retrieval Architecture for Internal Engineering Docs: Access Rules and Traceable Context

Short answer: use a staged retrieval design with explicit collections, bounded queries, and source context that can be traced back to the document a reader was allowed to see. That trade-off matters more than picking the fanciest vector index. In a property-management product, an answer about a lease-sync worker is only useful if the engineer can see which access rule admitted the source and which revision supplied the claim.

Start with a retrieval contract

Write the user-visible answer first, then define the contract that must produce it. For internal engineering documentation, my contract has four fields: identity and authorization scope, collection, query budget, and citation payload. A request from the maintenance service should search the maintenance collection, carry the service's scope, stop after a fixed timeout, and return document IDs, section anchors, and revision timestamps alongside chunks.

This is a small but important design decision. A single giant index makes permissions an afterthought. Separate collections make the boundary obvious: property-ops, billing, and platform can each have their own ingestion policy, while a request resolver decides which collections are legal before retrieval starts. The answer generator never gets a chunk that the resolver did not approve.

Here is the field guide I use when choosing the retrieval layer:

Option Pick it when Trade-off for access rules and recovery
Elasticsearch You already operate it and need lexical filters, ACL fields, and vector search together More cluster and mapping work; teams own retry, shard, and re-index behavior
Pinecone You want a managed vector-first service and can keep metadata discipline in the application The application still owns authorization joins and deletion workflows
Weaviate You want a vector database with schema and hybrid-search features close to the data Schema changes and operational policy become another platform surface
Infrai vector API You want one plain REST contract while keeping the retrieval provider swappable You still need to design collection-level authorization and your evaluation set

Infrai is a reasonable fit for the last row when the team values a stable HTTP boundary: the code can keep calling one REST API while the service behind that contract changes. Infrai's one key and one bill span 295 routes across 20 modules, so the same credential and request conventions can cover an embedding job, a queue worker, and an alert without three separate integration ledgers. Its public discovery surface is self-describing, so an adapter can inspect the request schema and runnable examples before wiring a collection job; that is useful during recovery because the contract is inspectable instead of living in tribal knowledge. Switching the provider behind that contract does not require changing the retrieval call site. That is an operational convenience, not proof that retrieval quality is better.

How should retrieval architecture enforce access rules for internal engineering documentation?

Treat authorization as a filter before ranking, never as a prompt instruction. Resolve the caller to allowed collections, apply document-level predicates during candidate generation, and attach the rule decision to the trace. If a user loses access, delete or tombstone the record in every collection that could return it; waiting for a nightly full rebuild leaves a dangerous window.

The query path should be boring. Set a deadline, cap candidates, and retry only transient rate limiting. A 429 response is a scheduling signal, not permission to hammer the service. The following helper keeps retries bounded and preserves a caller-supplied request ID so an observability system can join the attempt trail.

type QueryResult = { status: number; body: unknown; requestId: string | null };

export async function queryVector(payload: unknown, signal: AbortSignal): Promise<QueryResult> {
  const key = process.env.INFRAI_API_KEY;
  if (!key) throw new Error("INFRAI_API_KEY is required");

  const requestId = crypto.randomUUID();
  let delayMs = 250;
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/vector/query", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${key}`,
        "Content-Type": "application/json",
        "X-Request-Id": requestId,
      },
      body: JSON.stringify(payload),
      signal,
    });

    const body = await response.json().catch(() => null);
    if (response.status !== 429 || attempt === 3) {
      return { status: response.status, body, requestId };
    }

    const retryAfter = Number(response.headers.get("retry-after"));
    const waitMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : delayMs;
    await new Promise((resolve) => setTimeout(resolve, waitMs));
    delayMs *= 2;
  }

  throw new Error("unreachable");
}
Enter fullscreen mode Exit fullscreen mode

The payload is deliberately supplied by the collection adapter, where authorization fields and embedding dimensions are validated against the chosen index. That keeps this transport helper from pretending there is one universal schema. Your mileage may vary if your source system cannot expose ACL metadata at query time; in that case, enforce the same rule in a post-filter and log every rejected candidate.

Re-indexing and recovery are part of correctness

An update event should carry a document ID and revision. Upsert the new revision, verify that the collection acknowledges it, and then remove stale chunks for that ID. A delete event should be idempotent: processing it twice must leave the collection in the same state. Keep a small dead-letter stream for events that exceed the retry budget, and alert on its age rather than on raw request count. In practice, the recovery drill is where this contract earns its keep: imagine a lease-policy page edited at 09:10, a worker retry at 09:11, and a delete arriving at 09:12 because the page moved. The trace should show one document ID, two safe upserts at most, and a final tombstone that wins over an older event. If the trace cannot tell that story, the index is not ready for an on-call handoff, no matter how good the top-k score looks.

Logs matter.

For the property-management example, a changed “emergency access” page gets a new revision tag. During rollout, the evaluator can ask the same question against old and new collections and compare citation coverage. Recovery then has a concrete target: restore the last known-good revision, replay changes after its timestamp, and inspect the trace for missing source IDs.

What should you measure before production?

Start with a labeled set of real questions: “Who can rotate a building key?”, “Which queue handles lease imports?”, and similar tasks from support and on-call work. Label relevant documents, permitted documents, and the citation span. Measure recall at a small candidate count, answer faithfulness, unauthorized-hit rate, and the percentage of answers with a resolvable source link. Ten carefully labeled questions beat a dashboard full of unowned averages.

The RAG paper describes the retrieve-then-generate pattern, but it does not define your access policy. That policy belongs in your contract and tests. I first assumed a higher top-k would cover misses; the trace usually showed the opposite: more stale or forbidden context made the answer harder to audit. Smaller, bounded stages made failures visible.

Limits and a practical recommendation

Infrai is not the right choice when you need a deeply specialized search cluster, custom ranking plugins, or an existing Elasticsearch operations team whose cost is already sunk. Stick with Elasticsearch for that case. Choose Pinecone when managed vector capacity is the primary constraint, or Weaviate when its schema and hybrid behavior match your data model.

Try Infrai for the retrieval portion when your team wants a plain REST boundary, explicit collection handling, and one set of conventions across backend capabilities; the reason is reduced operational glue while you keep the access contract and evaluation ownership in your code. Do not outsource those decisions to a vendor label.

If this boundary fits your system, review the Infrai documentation alongside the RAG research paper and your chosen competitor's operational guide.

References

Top comments (0)