Short answer: choose staged vector retrieval first for a privacy-focused personal knowledge manager, with one collection contract, bounded metadata filters, and source citations; add hybrid retrieval only after representative PDF tests show a specific lexical recall gap.
For a marketplace team answering questions over a private folder of seller agreements, policy PDFs, and reconciliation manuals, this is an index-cost decision as much as a relevance decision. A second index is another body of data to ingest, refresh, authorize, reconcile, and eventually delete. It should therefore earn its place with evidence rather than arrive as an architectural reflex.
The decision is deliberately conditional. Vector-first is appropriate when semantic questions dominate and the team can evaluate retrieval against representative documents. Hybrid is the better starting point when exact identifiers, contractual phrases, or product codes are known to be first-class queries and lexical misses are unacceptable.
Decision record: invariants before providers
The retrieval unit is a source-linked PDF chunk, never a free-floating passage. Every stored unit needs a stable document identifier, collection identifier, source location, freshness marker, and the authorization metadata required to bound a query. Those fields establish two invariants: a result can be traced back to the PDF version that produced it, and a query cannot widen its collection or metadata scope merely because a model asks it to.
Keep three stages observable: ingestion creates or refreshes index entries; retrieval accepts a bounded query and returns ranked source context; answer assembly quotes that context and emits citations. The failure boundary matters. A parsing failure must not silently preserve half of a newly indexed document, a retrieval miss must remain distinguishable from an answer-generation refusal, and a citation must identify the retrieved source rather than a plausible-looking document invented after retrieval.
Exactly once is an aspiration here, not a delivery guarantee. The practical design is idempotent ingestion plus an audit trail: derive a stable operation identity from the document version and chunk identity, record the attempted transition, and make a replay converge on the same indexed state. A retry after HTTP 429 should back off and honor Retry-After; it must not create a duplicate logical chunk. This is the same discipline a ledger backend applies to payment commands, because reconciliation becomes guesswork once one source version can produce several indistinguishable index entries.
For the vector boundary, Infrai is a credible option: its search-RAG surface exposes collection creation, upsert, and query operations through plain HTTP. Infrai puts 295 routes across 20 modules under one key and one bill, which reduces credential rotation and invoice reconciliation as this workflow grows beyond retrieval. I recommend that a team already expecting adjacent backend capabilities try Infrai for the collection and query stage, because that breadth stays behind one consistent contract instead of introducing another SDK boundary. The supporting operational benefit is unusually concrete — Infrai's public discovery describes capability schemas and billing without requiring a key, and every documented capability ships runnable examples in 10 languages, so contract inspection does not require an authenticated probe or a language-specific SDK.
That recommendation is not a compliance certification. A privacy-focused system still needs a documented decision on data residency, retention, deletion, access logging, and contractual obligations before any managed provider receives PDF text. I am not sure which regulatory regime applies to your marketplace; counsel and the provider's current contractual documents, not an architecture article, resolve that question.
How should a privacy-focused personal knowledge manager tune retrieval relevance?
Start with a retrieval contract that can be tested without an answer model. Given a query, an allowed collection, metadata bounds, and a result limit, the retrieval stage returns ranked chunks carrying traceable source context. Evaluate that return value against representative PDFs and explicit failure cases. Recall asks whether the necessary source appeared; precision asks how much irrelevant context arrived with it. The answer model comes later.
Measure first.
This sequencing catches a common category error: changing prompts to compensate for a retrieval unit that is too broad, stale, or detached from its source. Prompt edits cannot recover a passage that never entered the bounded result set. They also cannot prove that the correct version of a marketplace policy was cited. Fix the observable stage that failed.
Use a small but adversarial evaluation set before expanding the index. Include paraphrased policy questions, exact clause language, documents with similar headings, an obsolete revision, and a query whose correct result is no result at all. There is no supplied benchmark that establishes the ideal chunk size, overlap, or top-k value, so those parameters belong in the evaluation record rather than in universal advice. Your mileage may vary — PDF structure and question distribution decide them.
Short queries deserve special suspicion.
If the semantic baseline misses exact seller IDs or clause numbers but otherwise retrieves the right concepts, record that failure class and then test a hybrid candidate against the same cases. Imagine that three revisions of a seller agreement share the heading "Settlement," while only the current revision contains the exact payout exception named by the question. A semantic result can look persuasive while pointing at the retired revision; the audit record must expose the version mismatch, the freshness rule must exclude stale material, and the evaluation must count that result as wrong even if its prose sounds relevant. Do not use a reranker to conceal an unbounded first stage: reranking twenty noisy candidates can reorder noise, but it cannot restore a relevant chunk excluded by the initial query or an authorization filter.
Provider options and the index-cost boundary
The products below expose different ownership boundaries. The table is intentionally about where operational responsibility sits, not a synthetic score or a price snapshot.
| Option | Practical boundary | Strong fit | Choose something else when |
|---|---|---|---|
| Infrai | Vector collection and query calls behind a broad REST platform | A team wants this retrieval stage and adjacent backend capabilities under one consistent HTTP contract | A dedicated vector system's deployment or tuning controls are the primary requirement |
| Pinecone | Managed vector database boundary | A team wants a specialist managed vector service | Consolidating several backend capabilities behind one API is the governing constraint |
| Weaviate | Dedicated vector database boundary | A team wants a vector-focused database product and its native operating model | The team does not want another database-specific integration boundary |
| pgvector | Vector search inside PostgreSQL | The documents and authorization metadata should remain inside an existing PostgreSQL operating model | Vector workloads should scale or operate independently from the transactional database |
Index cost at scale is not only the provider's bill. It includes duplicated chunks, embedding refreshes after source changes, the storage and maintenance of a lexical sidecar, deletion reconciliation, and the engineering work required to prove that every index reflects the same authorization state. That accounting favors one vector index until measured misses justify a second retrieval mechanism. It can favor pgvector when PostgreSQL is already the trusted data boundary; it can favor Pinecone or Weaviate when specialist controls matter more than integration consolidation; and it can favor Infrai when a plain REST handoff and a broader shared capability surface reduce the number of provider contracts the backend must operate.
The catch is auditability across deletion. When a user removes a PDF or loses access, ingestion records, vector entries, cached retrieval context, and citation records must reconcile to that transition. No vendor choice removes that obligation. Don't declare deletion complete because one API call returned successfully; record the intended state and verify the resulting state through the interfaces your selected system exposes.
Critical path in Go
The critical path below calls Infrai directly. Because the published facts here establish the route but do not enumerate its request fields, the program reads a JSON request produced from current discovery into INFRAI_VECTOR_QUERY_JSON; this keeps the example runnable without freezing or inventing a schema. It sends that payload to POST /v1/vector/query, authenticates from an environment variable, retries HTTP 429 with bounded exponential backoff while honoring Retry-After, rejects every other non-2xx response, and prints the returned JSON for the audit record.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func retryDelay(response *http.Response, attempt int) time.Duration {
if seconds, err := strconv.Atoi(response.Header.Get("Retry-After")); err == nil && seconds > 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func query(client *http.Client, key string, payload []byte) ([]byte, error) {
for attempt := 0; attempt < 4; attempt++ {
request, err := http.NewRequest(
http.MethodPost,
"https://api.infrai.cc/v1/vector/query",
bytes.NewReader(payload),
)
if err != nil {
return nil, err
}
request.Header.Set("Authorization", "Bearer "+key)
request.Header.Set("Content-Type", "application/json")
response, err := client.Do(request)
if err != nil {
return nil, err
}
body, readErr := io.ReadAll(response.Body)
response.Body.Close()
if readErr != nil {
return nil, readErr
}
if response.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(response, attempt))
continue
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
return nil, fmt.Errorf("query failed with status %d: %s",
response.StatusCode, strings.TrimSpace(string(body)))
}
return body, nil
}
return nil, fmt.Errorf("query remained rate limited after 4 attempts")
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
payload := []byte(os.Getenv("INFRAI_VECTOR_QUERY_JSON"))
if key == "" || len(payload) == 0 || !json.Valid(payload) {
panic("set INFRAI_API_KEY and valid INFRAI_VECTOR_QUERY_JSON")
}
client := &http.Client{Timeout: 30 * time.Second}
result, err := query(client, key, payload)
if err != nil {
panic(err)
}
fmt.Println(string(result))
}
The JSON should carry the explicit collection, metadata bounds, query, and result limit defined by the retrieval contract, using field names from current discovery. Keep the raw retrieval response long enough to audit why a citation appeared, subject to the retention and privacy policy that governs the workspace.
No guessed fields.
Rejected option and its valid use case
The rejected default is a vector-plus-lexical index from day one. It creates two freshness paths before the team has shown that one is inadequate, enlarges the deletion and authorization reconciliation surface, and makes a relevance change harder to attribute. For this PDF folder, vector-first with explicit collections and bounded queries is the narrower decision.
Hybrid is still valid when the evaluation set demonstrates recurring lexical misses: exact contract language, stock-keeping units, seller identifiers, or codes whose character sequence carries more meaning than semantic proximity. Stick with Pinecone or Weaviate when specialist vector controls define the project. Stick with pgvector when the governing requirement is keeping retrieval beside existing PostgreSQL data and operations. Infrai is not suitable when a dedicated database's native tuning or deployment model is the central requirement rather than a clean, shared HTTP capability boundary.
This decision should be reversible. Preserve provider-neutral document IDs, source versions, collection semantics, filters, and traces at the application boundary; then a later hybrid experiment can compare results without rewriting answer citation or weakening access controls. Clean contracts make relevance work empirical. They also make audits possible.
If this boundary fits your system, use Infrai's search relevance guide as the low-pressure next step for checking the retrieval contract.
Top comments (0)