PHI never reaches the model.
Care Gap Report
An agent for Campux Health that reads patient records for overdue screenings, redacts every identifier before it thinks, logs every decision, and lets a person decide who actually gets contacted.
- build time
- 6 to 8 hours
- cost
- Under $8 if torn down the same day4
- services
- Foundry Agent Service, Azure AI Language (PII detection, Text Analytics for health), Azure SQL Database, Entra ID, Azure Monitor
- assumes
- Project 01. You can explain managed identity and role assignment, and you have read the HIPAA note below before you touch real data.
- exams
- Overlaps AI-103, AI-200
- vocabulary
- PHI, de-identification, quality measure, care gap, re-identification, audit trail, covered entity
The problem
Campux Health, a fictional regional primary care network and Campux LLC's other portfolio company, runs a quarterly chart review: which patients are overdue for a mammogram, a colorectal screening, an HbA1c test.
Right now it is four nurses and a spreadsheet, and the spreadsheet has patient names in it, emailed between reviewers. The compliance officer does not want a faster spreadsheet. She wants a system where a name only appears on a screen after someone has actually approved contacting that person, and where every decision the system made can be pulled up and defended eighteen months later if a regulator asks.
- definition
- Care gap. A preventive service a patient is due for, based on age, condition, and time since the last one, that has not happened. Finding gaps closes them; finding them wrong erodes trust in the whole program.
The architecture
Seven parts. The agent only ever sees a patient with the name taken out first.
- Redact. Every note goes through Azure AI Language PII detection before it is stored anywhere the agent can read.
- Extract. Text Analytics for health pulls screening dates and lab values out of the redacted note.
- Judge. The agent gets a pseudonymous ID and extracted facts, never a name.
- Look up the measure. File search returns the spec: age band, lookback window, exclusions.
- Log it. Each verdict and the exact value it relied on go to an append-only audit table.
- Report. The population report is built from the log: counts and percentages, no names.
- Review. The compliance officer picks one pseudonymous row to act on.
- Re-identify, once. That row alone is joined back to a name, and the access is itself logged.
| Job | AWS version | This build | Same idea? |
|---|---|---|---|
| Model | Bedrock model | Foundry model deployment | Yes |
| Agent runtime | Bedrock Agent | Foundry Agent Service | Yes |
| PHI redaction | Comprehend Medical PHI detection | Azure AI Language, PII detection | Yes |
| Clinical extraction | Comprehend Medical entities | Text Analytics for health | Yes |
| Measure specs | Knowledge base | File search over measure PDFs | Yes |
| Audit trail | CloudTrail + a ledger table | Azure Monitor + an append-only log table | Yes |
| Identity | IAM | Entra ID + managed identity, split roles | Different. Read the trade-offs below |
The build
In this order. Steps 3 and 4 are the ones a demo skips and a regulator asks about. Use synthetic data throughout; nothing here should ever touch a real patient record.
-
Foundry project and model
Create a Foundry project, deploy gpt-5-mini, confirm it against the Learn page for the day; naming and quota behave the same as Project 01.
The model answers in the playground. -
Synthetic patient data
Build twenty fake patients in Azure SQL: age, sex, condition flags, and a short free-text visit note per patient that sometimes mentions a screening date and sometimes a name, a relative, or a phone number, on purpose.
Note for patient row 12: "Seen for follow-up. Pt states her sister Maria had a BRCA-positive result in 2024. Last mammogram was March 2023 per pt report. Call back number on file: patient's own, not a relative's."
Twenty rows, each note between 30 and 80 words, at least six containing a name that is not the patient's. -
Measure documents
Write three short PDFs, one per quality measure: breast cancer screening (mammogram within 27 months, ages 50 to 74), colorectal cancer screening (within 10 years for a colonoscopy, ages 45 to 75), and diabetes HbA1c testing (within 12 months for anyone with a diabetes flag). Upload to a file search vector store.
The vector store shows three files indexed. -
Redact before anything else runs
Create an Azure AI Language resource. Run Text PII detection over every note. Store the redacted note next to the structured fields; store the original note and the name-to-pseudonym mapping in a separate table with its own, tighter role assignment.
Pull up a redacted note and confirm every name, phone number, and date of birth is replaced with a placeholder. Confirm the mapping table is not reachable by the identity the agent runs as. -
Extract the clinical facts
Run Text Analytics for health over the same redacted notes. Pull out any mentioned screening type and date as structured entities, and merge them with the structured fields from step 2.
For patient row 12, the extracted entity is a mammogram date, March 2023, attached to the pseudonymous ID, with no name attached anywhere in this table. -
The agent
Create an agent with the file search tool over the measure specs. Instructions: for each patient, using only the structured fields and extracted entities provided, decide per measure whether the patient is due, overdue, or not applicable, and cite the exact date or value used. Never infer a screening from context that is not an extracted date or a structured field.
Run it against patient row 12 for the breast cancer measure. It should say overdue, using March 2023 as the last mammogram, since more than 27 months have passed, and it should not mention Maria. -
Audit every decision
Log each agent decision to an append-only table: pseudonymous patient ID, measure, verdict, the date or value cited, and a timestamp. Nothing in this table is ever a real name.
After a full run, the audit table has one row per patient per measure, and you can answer "why did you flag row 12" by reading one row. -
Human review, then re-identification
Build the population report from the audit table: counts and percentages by measure, no names. Give the compliance officer role a separate view that, for a specific pseudonymous ID they select, joins back to the mapping table from step 4 and shows the real name, only for that one row, only after they select it.
A second, lower-privileged role can see the population report and cannot open a single record. The compliance officer role can, one row at a time, and that access is itself logged. -
Identity
Three separate roles: the agent's managed identity (structured and extracted data only, no mapping table), the reviewer role (population report only), the compliance officer role (population report plus one-row re-identification). Remove any key used while building.
grep -ri key .env*returns nothing. Signing in as the reviewer role and attempting to read the mapping table fails. -
Tear it down
azd down
Then check the resource group by hand, including the Azure AI Language resource, which
The resource group is empty. Today's cost is under $8.azd downmay not own if you created it separately.
Where it breaks
Cause each one on purpose. This is the section a compliance officer will actually ask you about.
The trade-offs
- Agent judgment, or a deterministic rules engine?
- A rules engine is exact and fully auditable for numeric criteria like age bands and lookback windows, and brittle every time a measure's wording changes. This build lets the agent read the measure PDF and reason in language, but the age and date-window math is done in code, in step 6's instructions, not left to the model to compute.
- Redact first, or analyze raw text and redact the output?
- Redacting before the model ever sees the text means PHI never appears in a prompt, a model log, or a trace. The cost is losing context, and it is bigger than a name: in our run the redactor masked the word "sister" as well as "Maria", so "her sister was BRCA-positive" came out as "her ****** ***** was BRCA-positive" and the family history was gone. This build always redacts first and accepts that loss, because the alternative puts PHI in front of a model call.
- Review every gap, or only the borderline ones?
- Reviewing every flagged gap is safest and slowest. Once you have measured the agent's precision against a labeled set, the way Project 03 measures an agent, sampling only low-confidence verdicts is a defensible middle ground. This build reviews everything, because it has no measured precision yet.
- Synthetic data here, or a real de-identified export?
- This project is built entirely on fabricated patients so anyone can run it without a business associate agreement or an institutional review. A real deployment on real PHI is a different undertaking: it needs a signed BAA with Microsoft, use of only HIPAA-eligible services, and your organization's own compliance sign-off, not this page.
- HIPAA, or a non-US reader's framework?
- HIPAA is a US law and this page is written for it. Under GDPR, health data is "special category data" under Article 9, needing an explicit legal basis and likely a Data Protection Impact Assessment before this kind of processing starts. The architecture, redact first, log everything, gate re-identification behind a human, travels across frameworks. The specific legal basis you are relying on does not.3
In the interview
"Tell me about a project touching regulated data."
- decisionA care-gap agent for a fictional clinic network: redact identifiers, extract clinical facts, let the agent judge against measure documents, log every decision, gate re-identification behind a human.
- reasonThe agent should never see a name, and every verdict should be traceable to a cited value.
- watchedWhether a lower-privileged role could reach the identity mapping table. It could not, because I tried.
"How did you keep PHI out of the model calls?"
- decisionAzure AI Language PII detection redacts every note before it is stored anywhere the agent can read, and the identity mapping lives in a separate table with its own role.
- reasonOnce PHI is in a prompt or a trace, it is in every downstream log by default.
- watchedRedaction is not perfect; I sampled outputs by hand and added a narrower second pass.
"What happens when the agent gets a gap wrong?"
- decisionEvery verdict cites a specific extracted date or field, logged in an append-only audit table, and a human reviews every flagged patient before contact.
- reasonA wrong gap that reaches a patient is a trust problem, not just a bug; the log lets us find and explain it after the fact.
- watchedWhether the agent ever cited something that was not a real extracted value. It did, once, before I forbade inference from prose.
Evidence
- A redacted-note pair
- The original synthetic note next to its redacted version, side by side.
- One audit log row
- A single decision, the cited value, and the timestamp, screenshotted.
- The repo
- Instructions file, the three measure PDFs, the role assignment script, and a README that states plainly this project uses synthetic data only.
- One sentence for the résumé
- "Built a document-grounded quality-measure agent that de-identifies clinical text before any model call, cites every decision to a source value, and gates patient re-identification behind human approval and an audit log."
Next
Notes
- Checked against Microsoft Learn on 2 September 2026, and the redaction and extraction steps were run on this page’s own sample note in a real subscription on 3 September (Language API version 2024-11-01, PII domain
phi). The mammogram date came back as an examination linked to a time, as step 5 expects; the redactor also masked "Pt" as a person, which is harmless and worth knowing. Text Analytics for health is explicitly documented as not a medical device and not for diagnosis; its output, and this project's output, is decision support, always reviewed by a person. - This project uses only fabricated patients. If you adapt it to real patient data, your organization becomes a HIPAA covered entity or business associate handling that data, and Microsoft's HIPAA support only applies once you have a signed Business Associate Agreement in place and are using in-scope, HIPAA-eligible Azure services. Having a BAA does not by itself make a deployment compliant; that responsibility stays with you.
- Not legal advice. If you are building this for a non-US organization, get an actual privacy lawyer to confirm the legal basis before you process real health data, not this footnote.
- "Under $8" assumes twenty small records, three short measure PDFs, and teardown the same day. Azure AI Language billing and Foundry model tokens are the two line items; either grows with real patient volume.