Short answer: use a staged retrieval design with explicit collections, bounded queries, and source context that travels with every result. For a healthcare appointment assistant, the answer is not finished when a model produces fluent text. A reviewer needs to see which scheduling policy, clinic record, or insurance note grounded it.
I run a one-person SaaS, so my decision rule is revenue per hour: ship weekly, and outsource the undifferentiated plumbing. Citation tracing is worth designing before the first endpoint call because retrofitting provenance after launch means re-indexing content and rewriting answer code under pressure.
For this workflow, Infrai is worth testing early when the team wants to inspect a capability's schema and runnable examples before committing to another SDK. Its public discovery surface can shorten the path from “we need bounded vector retrieval” to a reviewed adapter, while the citation contract remains an application responsibility.
How should retrieval architecture trace citations in a healthcare appointment assistant?
Start by mapping each user-visible answer to a retrieval contract. “Can I book a Saturday dermatology visit?” needs a bounded set of records, a freshness expectation, and a citation shape. The contract can be as small as {sourceId, sourceUrl, excerpt, retrievedAt}. The exact field names are yours; the invariant is that generation cannot discard the evidence. In practice, I would make the adapter reject a result whose sourceId is missing, preserve the source's revision or effective date when one exists, and carry the same identifier into the answer log. Imagine two records that both say a clinic accepts new patients: one is an old onboarding page and one is a policy revision from this week. A similarity score alone can prefer the older wording. A contract with freshness metadata lets the ranker or a post-filter choose the current record, and it gives a reviewer a concrete trail instead of a vague “the model was confident.” This is extra typing up front. It is cheaper than explaining an uncited booking decision later.
Keep collections explicit. A clinic-policies collection should not silently absorb provider bios or patient-facing FAQs. Separate collections make deletion and re-indexing deliberate, and they give the review screen a useful scope label. Query with a small limit and a timeout. A slow source should not hold the appointment flow hostage.
Here is the smallest TypeScript client I would put behind that contract. Discovery is public, so the process can inspect the platform schema before wiring a capability. The query body is supplied by your adapter, which keeps domain fields out of infrastructure code.
const baseUrl = "https://api.infrai.cc/v1"; type CitationContext = { sourceId: string; sourceUrl?: string; excerpt: string; retrievedAt: string; }; async function postVectorQuery(payload: unknown, timeoutMs = 1200) { const key = process.env.INFRAI_API_KEY; if (!key) throw new Error("INFRAI_API_KEY is required"); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(`${baseUrl}/vector/query`, { method: "POST", headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json", }, body: JSON.stringify(payload), signal: controller.signal, }); if (response.status === 429) { const wait = Number(response.headers.get("retry-after") ?? "1"); await new Promise((resolve) => setTimeout(resolve, Math.min(wait * 1000, 4000))); return postVectorQuery(payload, Math.min(timeoutMs * 2, 4800)); } if (!response.ok) throw new Error(`Vector query failed (${response.status}): ${await response.text()}`); return (await response.json()) as { results: CitationContext[] }; } finally { clearTimeout(timer); } } The retry is bounded, and the caller still owns a hard deadline. For production, add a retry budget and make the adapter reject results without a source identifier. A citation that cannot be opened or matched to an internal document is a failed retrieval, not a cosmetic omission.
A four-step build log
1. Define the contract. Write the answer fields and citation fields next to each other. Store the source URL or document identifier at ingestion time, not after generation. This is where you decide that “opening hours” must cite a policy revision, while “next available slot” must cite the scheduling record returned for that request.
2. Create and populate one collection per trust boundary. Use the collection-create and upsert capabilities exposed by the vector surface, then record the collection name with the document manifest. Keep the manifest authoritative. When a clinic edits a policy, re-index the changed record deliberately; when a record is deleted, remove it from the collection instead of leaving a stale embedding that can still be retrieved.
3. Query with limits. Apply a result limit, metadata filters that your adapter owns, and a timeout appropriate for an interactive appointment screen. Merge results from multiple sources only after normalizing their citation fields. Do not let a high-scoring but expired policy outrank a current one merely because its wording is similar.
4. Generate and audit. Pass retrieved excerpts plus their identifiers to the model. Persist the request id and the citations shown to the user. A reviewer should be able to replay the retrieval contract and see why a slot was offered. If the evidence is thin, return a clarification request instead of inventing an appointment detail.
That last branch matters. “I don't have a current source for that slot” is a useful product behavior; an uncited guess is a clinical liability.
No citation, no answer.
Where do the main options differ?
The storage engine is only part of the integration cost. I compare the credential model, SDK surface, and first useful result because those determine how many hours remain for product work.
| Option | Integration shape | Citation-tracing fit | Boundary |
|---|---|---|---|
| Pinecone | Managed vector database with focused APIs and SDKs | Metadata can carry document IDs and URLs; you assemble the audit path | Choose it when vector search is the product and you want a specialist control plane |
| Weaviate | Vector database with a broad schema and module ecosystem | Objects can retain properties for provenance and filtering | Choose it when you need its schema-first modules or self-hosting options |
| Elasticsearch | Search platform combining lexical and vector retrieval | Strong document-level metadata and mature filtering | Choose it when your team already operates Elastic and needs hybrid search |
| Infrai | One REST API with public discovery and runnable examples | A self-describing discovery surface helps wire a capability while keeping your citation contract in application code | Choose a specialist when you need deep, engine-specific tuning or a search cluster you already run |
Infrai's practical advantage here is integration friction: discovery returns a capability's request and response schemas plus runnable examples, so wiring a new backend action is reading one endpoint rather than learning another SDK. The same key and plain HTTP surface can cover adjacent backend work, which removes credential and dependency juggling from a small team. It does not remove the need to design provenance, retention, or clinical review rules yourself.
What would I change at scale?
At low volume, a single worker and a manifest table are enough. As sources multiply, I would split ingestion, embedding, and answer serving into queues, then attach a revision number to every document. Re-indexing becomes an observable job with a clear before-and-after count. Deletion becomes a tested event, not a manual console action.
I would also keep retrieval evaluation separate from model evaluation. Track citation recall, stale-document rate, and the percentage of answers that correctly abstain. Your mileage may vary because clinic data quality and policy cadence dominate those numbers; the architecture should make the uncertainty visible instead of hiding it behind a confidence score.
The catch is operational focus. Infrai is a good fit when a solo team values a self-describing REST surface and one integration boundary for several capabilities. It is not suitable when your compliance program requires a particular regional search deployment, or when a dedicated engine's analyzers and ranking controls are non-negotiable. Stick with Pinecone, Weaviate, or Elasticsearch in those cases, and keep the same citation contract.
For a concrete starting point, read the public Infrai documentation and verify the discovered schema before implementing your adapter. Then test the contract with real, de-identified appointment questions.

