Initial Release
Ask a FHIR repository a clinical question in English, and be able to check the answer.
A natural-language agent over FHIR R4, built entirely on InterSystems IRIS for Health —
FHIR SQL for the data, vector search for the terminology, and an Interoperability production
as the agent runtime. The only component outside IRIS is a small SapBERT sidecar.
docker compose up -d

A model asked to write WHERE Code = '...' has to recall the code, and recall fails
silently — the query parses, executes, returns a plausible number, and nothing says the
codes were invented. Published measurements put this at 48.6% nonexistent codes in one
study, and 79 of 84 cohort definitions returning no patients in another.
So this agent never lets a model recall a code. Terminology resolution is an explicit,
traced pipeline stage, and every decision it makes is recorded with a reason — which is the
screenshot above.
question ──► Agent.Service ──► Agent.Process ──┬──► LLM.Extract 1. stem + context
(CSP page) business orchestrator │
service ├──► Embed.Op 2. SapBERT, 768-d ◄─┐
│ │ per
├──► Vector.Op 2. VECTOR_COSINE │ concept
│ │
├──► LLM.Rerank 4. keep/drop + why ◄─┘
│
└──► LLM.SQL 5. SQL over the projection
│
SqlGuard ─┴─► IRIS executes ──► rows + audit trail
Stage 3 — $expand / $subsumes / RxNav expansion — is implemented in
Terminology.Client but not yet wired into the orchestrator. See the article’s §9.
Embed.Op and Vector.Op are business operations rather than inline method calls
specifically so that each appears in the Interoperability trace with its own request and
response. The message log is the audit record.
Measured on this deployment, not quoted — the full trace is in the article.
And the consequence that matters: resolving the cohort properly rather than with
LIKE '%diabet%' moves a reported quality indicator from 65.6% to 52.8% — and takes 28
non-diabetic patients off a nurse’s call list.

| the architecture needs | IRIS supplies |
|---|---|
| a FHIR R4 repository | the FHIR server, same namespace |
| a flat SQL target over it | FHIR SQL projection |
| ANN search over embedded terminology | VECTOR(DOUBLE,768) + AS HNSW + VECTOR_COSINE |
| retrieval joined to clinical facts | one SELECT, one engine, one transaction |
| an agent runtime | an Interoperability production — typed messages, per-stage settings, pool, retry, resend |
| a durable trace of every step | the message bank |
| a key store | a Credentials entry |
| a web tier | a CSP page |
| a biomedical text encoder | not in the box — SapBERT in a sidecar, 109M params, CPU |
Everything is under https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/:
| https://github.com/adelelsayed/fhirsqlAgent/blob/main/https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/README.md | build notes, the quickstart, measurements, and the platform gotchas that cost real time |
| https://github.com/adelelsayed/fhirsqlAgent/blob/main/https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/ARTICLE_DC.md | the write-up: how the field got here, the architecture, and one question traced through 18 messages |
| https://github.com/adelelsayed/fhirsqlAgent/blob/main/https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/synthea/README.md | how the synthetic data was generated, and its provenance |
cd iris
cp setup/password.txt.example setup/password.txt # then edit it
docker compose up -d
docker exec fhirsqlllm iris session IRIS -U %SYS \
"##class(INT.FHIRSQLLLM.Setup.Installer).Run()"
Then generate the data, build the projection and the terminology index — the full sequence is
in https://github.com/adelelsayed/fhirsqlAgent/blob/main/https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/README.md.
Chat page: http://localhost:52773/csp/healthshare/fhirsqlllm/INT.FHIRSQLLLM.UI.Chat.cls
All data is synthetic, generated with Synthea
— 337 patients, no PHI, no real patient data at any point.
No patient data reaches a model: the extract stage sees the question, the rerank stage sees
candidate concept names, and results are never sent anywhere. The API key lives in an IRIS
Credentials entry and is handed to the sidecar through a shared volume — never in source, a
setting, a command line or the process list.
Agent.SqlGuard allows a single read-only SELECT and nothing else.
MIT. Third-party terms — including the SNOMED CT and CDT restrictions that matter
if you extend this — are listed in https://github.com/adelelsayed/fhirsqlAgent/blob/main/https://github.com/adelelsayed/fhirsqlAgent/blob/main/iris/README.md.
Built on IRIS for Health 2026.1 Community.