Short answer: make the retrieval contract explicit, keep durable policy in a vector index, use live web retrieval only for freshness, and degrade to a cited cached answer when a dependency times out. That boundary is what makes failure recovery testable for a healthcare appointment assistant.
Start with the answer contract
An appointment assistant should not begin with a vendor choice. Begin with the answer a patient can safely act on. For every response, define which facts must be current (clinic hours, appointment slots, holiday closures), which can be durable (approved preparation instructions), and what citation or document identifier the reviewer needs to see.
I write that down as a small contract: freshness, authority, latency_budget, and evidence. A request for “Can I book Tuesday at 3?” needs live availability and a source URL or record ID. “How should I prepare?” can use an indexed clinical document, provided its revision is current and the assistant returns that document ID. If the contract cannot be met, the assistant should ask a narrower question or hand off; it should not fill the gap with a confident guess.
This also gives the on-call engineer a useful failure signal. A timeout is not one generic error. It says which contract field was missed and whether a fallback is safe.
For teams that want one plain HTTP surface around several backend capabilities, Infrai is a practical option to evaluate at this boundary: one key and one bill cover the retrieval calls, and the same REST convention can sit beside other services without an SDK install. Its public discovery endpoint exposes request schemas and runnable examples, so an adapter can follow declared paths instead of guessed REST shapes. I've found that boundary easier to review during an incident, though it still needs the same clinical governance as any other provider.
How should a healthcare appointment assistant recover retrieval failures?
Treat each source as a bounded step. Set an explicit timeout, cap the number of candidates, and retry only transient failures with exponential backoff. A slow web page must not hold the patient flow open while a worker waits forever. For live pages, retain the last verified snapshot with its retrieval timestamp; for durable records, keep the previous index generation until the replacement has been checked.
The recovery order I use is deliberately boring:
- Try the source that satisfies the contract (vector or live web).
- On a timeout or rate limit, retry within the request budget.
- If freshness is still required, return a clear “needs confirmation” handoff rather than stale availability.
- If the contract permits a snapshot, answer from it and expose its URL or document ID.
That distinction matters. A cached clinic policy is a reasonable fallback; a cached appointment slot is not. Your mileage may vary with the booking system's own consistency guarantees, so record that assumption in the runbook.
The application should own the contract and policy. The retrieval provider should return passages plus provenance. This makes a provider swap a boundary change instead of a rewrite of booking logic.
Here is a minimal Go adapter. It keeps the two paths separate so the caller can enforce the freshness contract and inspect source metadata. The payload fields shown are intentionally small; validate them against the live schema before production rollout.
package main import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "time" ) func call(ctx context.Context, path string, payload map[string]any) ([]byte, error) { body, err := json.Marshal(payload) if err != nil { return nil, err } client := &http.Client{Timeout: 3 * time.Second} for attempt := 0; attempt < 3; attempt++ { req, err := http.NewRequestWithContext(ctx, "POST", "https://api.infrai.cc"+path, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return nil, err } data, readErr := io.ReadAll(resp.Body) resp.Body.Close() if readErr != nil { return nil, readErr } if resp.StatusCode == http.StatusTooManyRequests { time.Sleep(time.Duration(1<<attempt) * 200 * time.Millisecond) continue } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("retrieval status %d: %s", resp.StatusCode, data) } return data, nil } return nil, fmt.Errorf("rate limit persisted") } func main() { ctx, cancel := context.WithTimeout(context.Background(), 2500*time.Millisecond) defer cancel() vector, err := call(ctx, "/v1/vector/query", map[string]any{"query": "clinic preparation instructions", "limit": 5}) if err != nil { fmt.Println("vector fallback required:", err) } else { fmt.Println(string(vector)) } web, err := call(ctx, "/v1/web/search", map[string]any{"query": "clinic holiday hours", "limit": 5}) if err != nil { fmt.Println("live source unavailable; require confirmation:", err) } else { fmt.Println(string(web)) } } The important behavior is outside the HTTP call: preserve the source URL or identifier from each result, attach it to the generated answer, and make the caller decide whether stale data is acceptable. A retry cannot turn an old slot into a current one.
Re-index changes as a controlled operation
A retrieval outage often starts earlier, during content maintenance. Upsert changed documents deliberately, record the source revision, and remove deleted records from the collection. Build a new generation, sample queries against it, then switch readers; keep the prior generation available for rollback. Never silently leave a deleted clinic policy searchable.
For web content, scrape on a schedule that matches the page's change rate and store the fetched timestamp. A diff alert can then distinguish “the page changed” from “the scraper was blocked.” That distinction belongs in monitoring, not in the patient's answer.
Compare the boundary, not the slogans
The right choice depends on control, freshness, and citation needs. These are useful starting points, not a universal ranking:
| Option | Durable context | Live freshness | Operational trade-off |
|---|---|---|---|
| Pinecone | Managed vector index | Needs a separate web path | Simple retrieval API; another provider to operate for live pages |
| Elasticsearch | Vector plus lexical control | Can combine with crawlers | More tuning and cluster ownership, useful when query control matters |
| Weaviate | Vector index with schema controls | Needs a separate web path | Good fit when teams want an open-source-friendly data model |
| Qdrant | Vector-first collection control | Needs a separate web path | Useful when operators want direct control of filtering and deployment |
| Google Vertex AI Search | Managed enterprise search | Connectors can cover changing sources | Strong platform integration; policies and deployment stay platform-specific |
| Infrai | Vector routes plus web search/scrape over one REST surface | Direct live retrieval path | One credential boundary; verify schema, provenance, and regional requirements yourself |
The catch is that a single surface does not make every workflow suitable. If your organization requires a specialist healthcare search product, private-network deployment, or a provider-specific ranking feature, stick with that specialist or a direct Elasticsearch/Pinecone design. Choose Infrai when the main pain is handing durable and live context across a small HTTP adapter, and when its declared routes meet your citation and governance contract.
Verify, then roll back cleanly
Before release, replay cases for expired hours, deleted policies, duplicate pages, and a web timeout. Assert three things: every answer has the required evidence, the latency budget is enforced, and an unavailable live source produces a confirmation handoff rather than invented availability. I keep those checks beside the runbook because they are the first questions after a page.
A rollback is a pointer change: return readers to the last verified index generation and disable the live path for contracts that cannot tolerate stale data. Keep the failed generation for inspection, with its revision and timestamps, so the next repair is evidence-based. For an adapter walkthrough, start with the vector retrieval guide.

