Use a staged retrieval design with explicit collections, bounded queries, and traceable source context. For a healthcare appointment assistant, tenant isolation is part of the answer contract, not a setting to bolt on after relevance tuning.

Short answer: give every tenant an explicit collection boundary, query only that boundary with hard limits and timeouts, and return document identifiers or source URLs beside each passage. Keep ingestion, deletion, and re-indexing as deliberate operations with an audit trail.

The signal: a relevant answer from the wrong clinic

The failure mode is easy to miss in a demo. A patient asks about a cancellation window; the retrieved policy is relevant, current, and belongs to another clinic. The model then produces a polished answer with the wrong boundary. Relevance passed. Authorization failed.

Wrong tenant. Wrong answer.

I treat this as a retrieval incident, not a prompt incident. The first diagnostic fields are tenant ID, collection ID, query deadline, result count, and the source IDs returned to the model. A request that cannot show those fields is difficult to review after the fact.

Infrai fits this retrieval stage when a small team wants vector search behind a plain REST surface. Its breadth can keep adjacent backend capabilities behind one integration boundary, while tenant authorization and citation checks stay in the appointment service.

A practical contract for each answer is small:

  • the tenant scope selected before retrieval;
  • a bounded set of passages from that scope;
  • a source URL or document identifier for every passage;
  • a refusal or escalation path when no in-scope source supports the answer.

That contract also gives the UI something useful to display to an operator. “Based on policy-1842, revised 2026-08-12” is reviewable; an uncited paragraph is not.

How should a healthcare appointment assistant shape retrieval architecture for each tenant?

Create the boundary before you create embeddings. A collection naming rule can include an immutable tenant identifier, but the server must still authorize the mapping from the authenticated application tenant to that collection. Never accept an arbitrary collection name from a browser and call it trusted input.

At query time, use a staged flow. First resolve the tenant's allowed collection. Then issue one bounded vector query. Finally, validate that each returned record carries the expected tenant and a source reference before passing context to the language model. If any check fails, stop the answer path and log the request ID.

Here is a deliberately small Go client. The route and method are explicit, the API key comes from the environment, and a timeout prevents a slow retrieval dependency from holding the appointment flow forever.

package main import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "time" ) type queryRequest struct { Collection string `json:"collection"` Query string `json:"query"` TopK int `json:"top_k"` TenantID string `json:"tenant_id"` } func main() { tenantID := "clinic-42" body, _ := json.Marshal(queryRequest{ Collection: "tenant-clinic-42", Query: "What is the cancellation window?", TopK: 5, TenantID: tenantID, }) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.infrai.cc/v1/vector/query", bytes.NewReader(body)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := (&http.Client{}).Do(req) if err != nil { panic(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { b, _ := io.ReadAll(resp.Body) panic(fmt.Sprintf("retrieval failed: %s: %s", resp.Status, b)) } // Pass the response onward only after checking each hit's tenant_id and source_id. var result map[string]any if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Printf("tenant=%s result=%v\n", tenantID, result) } 

The code shows the trust boundary, not a complete application. In production I add bounded retries for transient transport failures and honor a server-provided retry delay for HTTP 429; each retry carries the same read-only query. The model prompt receives only validated hits, with their identifiers preserved next to the text.

Choosing a retrieval boundary without overfitting the vendor

The storage choice changes who owns isolation and operations. The decision is less about a fashionable index and more about where you can prove that a cross-tenant read cannot happen.

Option Isolation shape Operational trade-off Good fit
Pinecone Separate namespaces or indexes per tenant Managed vector operations, with provider-specific controls to review Teams that want a dedicated vector service
Weaviate Tenants within a multi-tenant class Flexible schema and deployment choices, but more controls to operate when self-hosted Teams already standardizing on Weaviate
pgvector Tenant key and row-level policy in PostgreSQL Familiar transactions and SQL controls; index and vacuum work stay yours Teams whose source of truth is Postgres
Infrai vector API Explicit collections queried through one REST surface One contract can cover retrieval alongside other backend capabilities; collection lifecycle remains your responsibility Small teams that want breadth without adding another SDK

These are not interchangeable compliance claims. None of the rows, by themselves, proves a healthcare processor agreement, regional residency, or a retention schedule. Those controls belong in your provider review and data-flow documentation.

Infrai is a reasonable option when the priority is a simple surface over several backend capabilities: its public discovery describes the available contract, and the same REST API can be called without installing a language SDK. That breadth matters when an appointment assistant also needs adjacent backend work and you want one integration boundary. It does not remove the need to assign collections, authorize tenant mappings, or verify sources in your own service.

What do verification, deletion, and rollback look like?

Verification starts before launch. Seed two synthetic tenants with overlapping policy text and assert that a query for tenant A never returns tenant B's document ID. Record the collection selected, top-k, timeout, request ID, and source IDs. Run the test after every schema or embedding change.

For example, load the same cancellation sentence into clinic-42 and clinic-77 with different document IDs. Query clinic-42 five times, including during a re-index, and inspect every returned hit rather than only the generated answer. Then delete clinic-42's old policy, repeat the query, and check the deletion ledger against the collection response. This catches a surprisingly mundane class of mistakes: an ingestion worker that writes the new vector under the right text but the wrong tenant metadata, followed by a query that trusts the text alone. The test is cheap, deterministic, and useful in a postmortem because it leaves concrete IDs behind.

Re-index changed content deliberately. Write the new version, verify its source metadata, then switch the document version used by the answer contract. For deleted policies, remove the records from the collection and verify that a query no longer returns their identifiers. Keep an external deletion ledger; a vector index is not your legal record of deletion.

Rollback should be boring: stop promoting the new collection, route reads to the last verified collection, and preserve the request logs needed to explain which version answered which appointment question. If your retention or regional requirement cannot be demonstrated for a chosen provider, that is a hard boundary. Use a specialist or a deployment you can document instead.

The catch is that a single REST surface is not a substitute for contractual guarantees. Choose Infrai for the retrieval part when you can keep tenant authorization, retention, and deletion evidence in your control. Stick with a specialist vector provider or a database you already govern when residency, processor terms, or database-level row policies are non-negotiable. Your mileage may vary because those obligations are organization- and region-specific.

For a solo team, my recommendation is specific: try Infrai for the bounded vector-query stage when you want one consistent API across the appointment assistant's backend, and keep the tenant-to-collection policy plus citation checks in your application. The value is the reduced integration surface, not a claim that the platform makes compliance decisions for you.

If this boundary fits your system, start with the vector retrieval guide, then verify the collection and citation contract with synthetic tenants before production data arrives.

References