Reliable agents are built, not prompted

By November 3, 2026Healthcare
agent engineering healthcare og
Key Takeaways
  • Agent engineering beats prompting. Test every change against real cases before release.
  • AI prior authorization assists, never decides. A licensed reviewer approves every submission.
  • “No evidence found” is a valid answer. Cited gaps beat invented findings.
  • The dataset is the project. 40–60 real cases, with checks written by clinical reviewers.
  • AI agent evaluation needs layers. Code checks first, then a calibrated LLM judge.

Most healthcare AI agent projects die in the same place. The demo works. Someone shows it to a clinical leader. The clinical leader asks a question nobody can answer: how often is it wrong, and wrong in what way?

That question is not hostile. It is the only question that matters in a regulated workflow, and a prompt-and-pray build has no way to answer it. You can describe what the agent does. You cannot say what it does across two hundred real cases, or whether last week’s prompt tweak quietly broke the thing it used to get right.

Agent engineering is the discipline that answers it. Not a framework, not a vendor. A way of working in which every change to an agent is measured against a dataset of real cases before anyone sees it, and every production failure becomes a permanent test. It borrows product thinking for the question of what must never go wrong, software engineering for the runtime that enforces it, and data science for the measurement that proves it.

This piece walks that discipline end to end on one specific healthcare workload: assembling a prior authorization packet. It is a good test case because it is genuinely hard, genuinely painful, and has a deadline attached.

Why prior authorization, and why now

Under the CMS Interoperability and Prior Authorization final rule (CMS-0057-F), impacted payers have to stand up a FHIR-based Prior Authorization API by January 1, 2027. The operational half of the rule is already live: since January 1, 2026, expedited decisions are due within 72 hours and standard decisions within seven calendar days, denials have to carry a specific reason, and prior authorization metrics are reported publicly. CMS points at the HL7 Da Vinci implementation guides for the technical shape of it: Coverage Requirements Discovery for whether authorization is needed, Documentation Templates and Rules for what has to be supplied, and Prior Authorization Support for the submission itself.

Here is the part that gets missed. The rule shortens the clock on the payer side and standardizes the pipe. It does not make the provider-side work any smaller. Somebody still has to read the policy, find the relevant facts in a chart that was written for clinical care rather than for a reviewer, decide which of them satisfy which criterion, and write it up. That work is the bottleneck; it is almost entirely manual, and it is about to be squeezed into a shorter window.

It is also close to an ideal agent task. The inputs are structured enough to retrieve and messy enough to require judgment. The output is a document. The number of steps varies by case. And there is a natural, legally meaningful stopping point where a human has to take over.

WORKED EXAMPLE  ·  CASE 0147, AS IT RUNS TODAY

Thirty eight minutes, then a denial that was never about the medicine

A 54 year old Medicare Advantage member presents with six weeks of radicular low back pain. The orthopedist orders an MRI of the lumbar spine without contrast, CPT 72148. A utilization management coordinator picks it up.

  • Nine minutes establishing which payer policy version governs, because the member changed plans in January.
  • Twenty minutes hunting for evidence of conservative management. It exists. It is a scanned physical therapy discharge summary from an outside clinic, sitting in the document store as an image, never transcribed, never coded.
  • Six minutes assembling the packet and writing the narrative.
  • Three minutes on submission and documentation.

Two days later it is denied for insufficient documentation of conservative management. Not because the therapy did not happen. Because the coordinator summarized it in prose and the reviewer could not see the source. A peer to peer review thirteen days later overturns it. The member waits two weeks for an MRI that was always going to be approved.

That denial is the target. Not the writing, which took six minutes. The twenty minutes of retrieval and the one structural weakness in how the evidence was presented.

Case 0147 and the cases referenced alongside it through this piece are composites, assembled from the kind of work that fills a utilization management queue. The details are representative rather than drawn from any single client record.

SCOPE, STATED PLAINLY
The agent assembles and drafts. It does not decide. Medical necessity determination is a clinical and regulatory act performed by a licensed reviewer, and nothing in this architecture changes that. What the agent removes is the forty minutes of chart archaeology that happens before the reviewer can think.

Agent engineering is three jobs in one room

The reason most agent projects stall is organizational before it is technical. An agent that works in production needs three kinds of judgment, and in most companies those three people never sit together.

Product thinking decides what must never go wrong. Not the happy path. The specific failure that ends the project: a letter that asserts a conservative therapy trial the patient never had. Product defines the trust boundary, the escalation path, and what the human on the other end actually needs to see.

Engineering builds the runtime that enforces those boundaries in code rather than in prose. Tools that are read-only because they are read-only, not because the prompt asks nicely. A write path that physically cannot fire without an approval. Durable execution so a thirty-step run does not evaporate when a pod restarts.

Data science turns opinions into numbers. It builds the dataset, writes the evaluators, calibrates the LLM judge against human graders, and runs the experiment that tells you whether version four is better than version three or just differently wrong.

Add a fourth seat for the clinical subject matter expert, because in healthcare that person is the only source of truth for what “correct” means. A utilization review nurse who will spend two hours a week grading agent output is worth more to the project than another engineer.

WORKED EXAMPLE  ·  ONE DECISION, FOUR OPINIONS

What to do about that scanned physical therapy note

Case 0147 forces a decision in week one. The evidence the whole authorization turns on is a page image. Four people answer differently, and the answer you ship needs all four.

  • Engineering wants an extraction tool: OCR the page, pull the therapy start date, visit count and discharge date, return them as structured fields.
  • Data science asks how often this matters before anyone builds it. In a 48 case seed set, seven depend on scanned evidence, and six of those seven sit in the hard split. It matters.
  • Product asks what happens when the extraction is wrong. An agent that reads “6 weeks” off a smudged fax and asserts a therapy course that did not happen is the single failure that ends the program. Not a bad letter. A false clinical claim in a document submitted to a payer.
  • The utilization review nurse settles it. Extract, but never let an extracted value alone set a criterion to met. Surface the quoted span and the page image beside the claim so the reviewer confirms it in four seconds instead of searching for it in twenty minutes.

That one conversation produced a tool signature, an evaluator, and a requirement for the approval screen. Without the four of them in a room it produces three weeks of rework.

agent engineering loop

The agent engineering loop. Five stages, three disciplines, and an observability layer that spans all of them.

A demo proves the agent can produce a good packet once. A dataset proves it produces one reliably, on the cases you did not design for.

The workload, scoped to the millimetre

Vague scope is the single biggest cause of unreliable agents. Here is the whole job, written the way we would write it into a statement of work.

Trigger. An order is placed for a service that may require authorization. Our worked example is an MRI of the lumbar spine without contrast, CPT 72148, for a Medicare Advantage member.

What the agent does.

  1. Checks whether authorization is required for this service, this payer, this product and this member, and retrieves the governing criteria set by its version identifier.
  2. Pulls the documentation template for that criteria set and turns it into a concrete checklist of questions.
  3. Retrieves only the clinical evidence those questions ask for, from the FHIR server and the document store, inside the date windows the policy specifies.
  4. Maps each criterion to one of three states: met, not met, or no evidence found. Every met and not met carries a citation to a specific resource.
  5. Drafts a letter of medical necessity built exclusively from the mapped evidence, plus a reviewer summary that leads with the gaps.
  6. Stops. A named human opens the packet, sees every criterion next to its citation, and approves, edits or rejects.
  7. Only after approval does anything get emitted toward the payer, with the approver recorded in the audit trail.

WORKED EXAMPLE  ·  THE FOUR CRITERIA IN PLAY

Policy 2026.09, lumbar MRI, and exactly what the agent emits for each

  1. Radicular symptoms documented for six weeks or more. Agent returns met, citing two office notes forty four days apart.
  2. Failure of six weeks or more of conservative management. Agent returns met, citing the extracted span from the scanned physical therapy discharge summary, with the page image attached.
  3. Neurologic examination findings consistent with the suspected level. Agent returns no_evidence. The referral note names L5 radiculopathy, but that is the ordering physician’s suspicion in an indication field, not a documented exam finding. The agent is explicitly forbidden from treating the two as the same thing.
  4. Absence of red flags requiring emergent imaging instead. Agent returns met, citing the absence of documented bowel or bladder dysfunction, saddle anesthesia and progressive motor deficit across three encounters.

Three criteria satisfied with citations, one gap named precisely. The reviewer now has a single phone call to make, on day one, rather than a denial to appeal on day fifteen. That is the whole product.

In scope and worth automating

Finding the six relevant facts in four hundred pages. Knowing which policy version applies. Producing a first draft that is structurally complete. Flagging what is missing before the reviewer wastes time looking for it.

Permanently out of scope

Deciding medical necessity. Writing anything to the chart. Submitting without a named approver. Asserting any clinical fact that does not resolve to a retrievable resource in the frozen input bundle.

That last bullet is the one that carries the most weight. An agent that cannot cite a fact must say it found no evidence, and saying “no evidence found” has to be a rewarded behavior rather than a failure. Half the engineering effort on a packet agent goes into making abstention comfortable.

Why this is a deep agent and not a chain

A chain is the right shape when the steps are known in advance. This workload is not that. The number of criteria varies by policy. Some criteria need one lab value, some need a narrative from a six month old note. A sparse chart sends the agent looking in three more places; a rich one does not. Step count on a real queue varies by a factor of five.

WORKED EXAMPLE  ·  THREE CASES, THREE SHAPES

Why no fixed graph fits

  • Case 0147, sparse chart, outside records. Fourteen tool calls. Six FHIR searches, two document fetches, one extraction from a scanned page, one re-search after the first date window came back empty, four criteria mapped.
  • Case 0203, rich chart, single health system. Six tool calls. Everything the policy asks for is coded and current. The agent plans four steps and executes four steps.
  • Case 0311, member transferred mid treatment. Twenty three tool calls across two source organizations, two abstentions, and a reviewer summary that leads with a request for records rather than with a letter.

A graph that handles all three is the union of every branch, and every branch you add is a branch you have to evaluate. A planning agent handles the variance at runtime and leaves you evaluating outcomes instead of paths.

That variability is what the DeepAgents harness exists for. It gives you a supervisor that writes its own plan, delegates bounded pieces of work to subagents with their own prompts and tool sets, and keeps intermediate artifacts on a virtual filesystem instead of dragging them through the context window. Underneath it, LangGraph provides durable execution, checkpointing, streaming and the interrupt mechanic that makes a real human approval gate possible rather than cosmetic.

The subagent split is not decoration. It is the main lever on reliability, for an unglamorous reason: context hygiene. The agent that reads forty FHIR resources should not be the agent that writes the letter, because by the time it has read them its context is full of material that will leak into prose as unsupported confidence. Handing the packet writer a clean, structured criteria map and nothing else is what makes its output auditable.

prior authorization agent architecture

The packet agent. Read-only source systems on the left, a supervisor delegating to four bounded subagents in the middle, and a hard human approval gate before anything reaches the payer.

The agent, in code

What follows is the skeleton, trimmed to the parts that carry the architecture. Tool bodies, FHIR plumbing and auth are left out.

from deepagents import create_deep_agent
from langchain.agents.middleware import (
    HumanInTheLoopMiddleware, SummarizationMiddleware, wrap_tool_call,
)
from langchain.chat_models import init_chat_model

# Every tool call is logged before it runs. This is the audit trail,
# and it is also the PHI boundary: payloads are redacted on the way out.
@wrap_tool_call
defaudited(request, handler):
    audit.record(
        run_id=request.runtime.context.run_id,
        tool=request.tool_call["name"],
        args=redact_phi(request.tool_call["args"]),
    )
    return handler(request)

coverage_rules = {
    "name": "coverage_rules",
    "description": (
        "Determines whether prior authorization is required and returns the "
        "governing criteria set with its version id. Use before any chart access."
    ),
    "system_prompt": COVERAGE_PROMPT,
    "tools": [crd_coverage_check, fetch_policy_by_id, fetch_dtr_questionnaire],
}

chart_evidence = {
    "name": "chart_evidence",
    "description": (
        "Retrieves clinical evidence for a specific named criterion. "
        "Call once per criterion. Never call without a criterion id."
    ),
    "system_prompt": EVIDENCE_PROMPT,
    "tools": [fhir_search, fetch_document, extract_from_note],  # read only
    "model": init_chat_model("anthropic:claude-sonnet-4-5"),   # cheaper, high volume
}

criteria_mapper = {
    "name": "criteria_mapper",
    "description": "Maps retrieved evidence to criteria. Emits met / not_met / no_evidence.",
    "system_prompt": MAPPER_PROMPT,
    "tools": [],  # reasons over what it is given, retrieves nothing
    "response_format": CriteriaMap,
}

packet_writer = {
    "name": "packet_writer",
    "description": "Drafts the letter of medical necessity from a completed criteria map.",
    "system_prompt": WRITER_PROMPT,
    "tools": [],
}

agent = create_deep_agent(
    model=init_chat_model("anthropic:claude-opus-4-5", max_retries=8),
    system_prompt=SUPERVISOR_PROMPT,
    tools=[submit_prior_auth],          # the only write tool in the system
    subagents=[coverage_rules, chart_evidence, criteria_mapper, packet_writer],
    middleware=[
        audited,
        SummarizationMiddleware(max_tokens_before_summary=120_000),
        # The gate. The run durably pauses here; nothing resumes without a decision.
        HumanInTheLoopMiddleware(interrupt_on={
            "submit_prior_auth": {"allow_accept": True,
                                   "allow_edit": True,
                                   "allow_respond": True},
        }),
    ],
)

The harness APIs move quickly. Check the current deepagents and langchain references before copying signatures.

Three things in there are doing more work than they look like they are.

The subagent descriptions are prompts. The supervisor chooses what to delegate by reading them. “Call once per criterion. Never call without a criterion id” is not documentation, it is the instruction that stops the supervisor from asking for the whole chart in one shot. Loose descriptions produce loose delegation, and loose delegation is where step counts and costs explode.

The criteria mapper has no tools. It cannot go looking for supporting evidence when it does not like what it was given. That constraint is what makes “no evidence found” its natural output instead of a creative reach.

There is exactly one write tool. Everything else in the system is read-only at the tool layer, not at the prompt layer. The single write is wrapped in an interrupt. You can audit that property by reading twenty lines of code, which is roughly the standard a compliance review will hold you to.

WORKED EXAMPLE  ·  CASE 0147, TRACE OF A RUN

What the supervisor actually did

  • write_todos plan: coverage > evidence x4 > map > draft
  • task(coverage_rules) auth required, policy 2026.09, 4 criteria
  • task(chart_evidence) crit_1 radicular duration > 2 Encounters
  • task(chart_evidence) crit_2 conservative mgmt > 0 results
  • task(chart_evidence) crit_2 widened window > 1 DocumentRef
  • task(chart_evidence) crit_3 neuro exam > 0 results
  • task(chart_evidence) crit_4 red flags > 3 Encounters
  • write_file evidence/case_0147.json
  • task(criteria_mapper) met, met, no_evidence, met
  • task(packet_writer) draft + reviewer summary
  • submit_prior_auth INTERRUPT. run paused, awaiting approver

Two things are worth noticing. The agent re-ran evidence retrieval for criterion 2 after an empty first pass, which is the behavior that found the scanned note. And the final line is a pause, not a call. The run sat there for nineteen hours before a nurse opened it.

System prompts that make an agent more effective

Prompts are where teams spend their time and get the least leverage, usually because they are writing essays when they should be writing contracts. A few patterns move the numbers on this workload.

Put the hard boundaries first, as prohibitions

Models follow the opening of a system prompt more reliably than the middle. Role, then the small set of things that must never happen, then the method. Not the other way round.

Make every claim carry its evidence

The single highest-value instruction on this agent is an output contract that makes an uncited claim structurally impossible to express. If the schema requires a resource reference next to every clinical assertion, the model has nowhere to put a vague one.

MAPPER_PROMPT = """
You map retrieved clinical evidence onto prior authorization criteria.
You are not a clinician and you do not decide medical necessity.

NEVER:
- Infer a clinical fact that is not stated in a resource you were given.
- Convert an absence of documentation into a negative finding. "Not documented"
  and "did not occur" are different, and only the first is yours to report.
- Round, aggregate or restate durations. Six weeks is six weeks, not "months".

For each criterion in the questionnaire, emit exactly one entry:

  criterion_id   the id as given, unchanged
  status         met | not_met | no_evidence
  evidence       [] for no_evidence, otherwise one or more entries of
                 {resource_type, resource_id, date, quoted_span}
  rationale      one or two sentences, referring only to the evidence above

Rules that decide status:
- met       every element of the criterion is supported by cited evidence
- not_met   cited evidence directly contradicts an element
- no_evidence  anything else, including partial support

no_evidence is a correct and expected answer. A criteria map with three
no_evidence entries and accurate citations is more useful to the reviewer
than one with zero and a guess, and it is graded higher.
"""

The mapper prompt. The last paragraph exists because models treat abstention as failure unless told otherwise.

WORKED EXAMPLE  ·  CRITERION 3, BEFORE AND AFTER THE CONTRACT

The same model, the same case, one prompt change

Before. “Neurologic examination demonstrates findings consistent with L5 radiculopathy.”

Fluent, plausible, and false. The model lifted it from the indication field of the referral, which records what the ordering physician suspects. No exam was documented. Submitted, this is an unsupported clinical assertion in a payer-facing document, and it is the kind of sentence that gets a program shut down rather than merely denied.

After. status: no_evidence. “No neurologic examination findings located in the retrieved record. The referral note names L5 radiculopathy in its indication field, which states the ordering clinician’s suspicion rather than a documented exam.”

Nothing changed except the output schema requiring a resource reference beside every clinical claim, and the mapper prompt stating that no_evidence is graded higher than a guess. The model had nowhere to put the vague sentence, so it reported the gap instead.

Tell it what the reader needs, not what you want written

“Write a professional letter of medical necessity” produces competent, generic prose. “The reviewer reads the gaps first and has ninety seconds” produces a document with the missing items at the top, which is what the nurse actually wants.

Version the prompt like code, because it is code

Keep prompts in the LangSmith prompt hub, pull them by version at runtime, and record the version on every experiment and every production run. Without this you cannot answer “what changed between Tuesday and Thursday”, and that question will be asked.

FAILURE MODE NOBODY PLANS FOR
Prompt edits are invisible changes to a production system. A team will put a deployment pipeline, code review and a rollback plan around a two-line Python change, then let someone paste a new paragraph into a prompt on a Thursday afternoon. Prompt versions belong under the same change control as the model version and the policy corpus version.

Prototype first, but prototype to find the hard part

The prototype is not a proof of concept for stakeholders. Its job is to tell you, in about two weeks, which part of this workload is actually difficult, so you build the dataset around that instead of around your assumptions.

Build one narrow slice: one service line, one payer, one criteria set. Wire real tools against a de-identified sandbox. Trace everything from the first run. Then take twenty cases through it by hand and read every trace.

On a packet agent, the hard part is almost never the writing. The letter is the easy half. What surprises teams is some combination of: the policy version in the sandbox not matching the one in production; criteria written in prose that resists mapping into discrete checks; evidence that exists only inside a scanned PDF; and date windows that nobody can state precisely because the policy says “recent” and the reviewers have an unwritten convention. You want to discover those in week two, not month four.

WORKED EXAMPLE  ·  WHAT TWENTY CASES SURFACED IN WEEK TWO

None of it was the writing

  • Policy version drift. The sandbox carried 2026.04. Production was on 2026.09, and criterion 2 had changed from four weeks to six. Every answer the prototype produced was graded against the wrong rule.
  • Evidence trapped in images. Seven of twenty cases had their decisive evidence in a scanned document. This reordered the entire tool roadmap.
  • “Recent” is not a date range. The policy says recent conservative management. Reviewers had an unwritten convention of twelve months. Nobody could produce it in writing. We had to get it decided and then encode it.
  • Confident fabrication on sparse charts. On the four thinnest records, the agent filled gaps rather than naming them. Every single time. This became the highest priority prompt and schema work and the reason abstention has its own evaluator.

Two weeks of prototyping reshaped the next ten. That is the whole return on the exercise, and you only get it by reading traces rather than reading outputs.

Building the dataset is the project

Everything downstream depends on this and it is the step teams skip. A dataset is not a collection of prompts that worked. It is a frozen set of real inputs paired with an explicit definition of correct.

Start with retrospective cases, stratified on purpose

Pull forty to sixty closed cases from the utilization management queue. Do not sample randomly, because random sampling gives you the easy middle. Stratify so each of these is represented: approved on first pass, approved after peer to peer, denied for insufficient documentation, denied on medical necessity, withdrawn. Then cut across payer, service line, and evidence density.

Forty well chosen cases beat four hundred scraped ones. You will read every one of them at least twice.

Use assertions, not golden answers

 

Two experienced nurses will write the same packet two different ways and both will be correct. Grading against one nurse’s prose teaches the agent to imitate a style rather than be right, and it gives you a score that moves for reasons you cannot interpret.

Instead, each example carries a list of statements that must be true of the output and a list that must not. Then a failure is legible: the agent dropped criterion three. That is a fixable
sentence, not a vague drop in similarity.

agent dataset example anatomy

One dataset example taken apart. The reference output is a list of assertions, not a model answer.

Freeze the inputs

A chart that keeps changing is not a test case. Snapshot the FHIR bundle, the documents and the policy version into the example itself. The day someone updates a policy and your scores shift for no apparent reason is the day you wish you had.

from langsmith import Client

client = Client()
ds = client.create_dataset(
    "pa-packet-imaging-v1",
    description="Closed UM cases, de-identified, frozen bundles. Assertion-graded.",
)

client.create_examples(
    dataset_id=ds.id,
    examples=[
        {
            "inputs": {
                "order": {"cpt": "72148", "description": "MRI lumbar spine w/o contrast"},
                "payer": "plan_b", "product": "MA",
                "policy_version": "2026.09",
                "chart_bundle_ref": "s3://pa-eval/frozen/case_0147.json",
            },
            "outputs": {
                "must_be_true": [
                    "Addresses all four criteria in policy 2026.09",
                    "States duration of conservative therapy and cites the PT encounter",
                    "Marks the neurologic exam criterion as no_evidence",
                    "Every clinical claim cites a resource id present in the bundle",
                    "Routes to human approval before any submission",
                ],
                "must_not_be_true": [
                    "States or implies a medical necessity determination",
                    "Cites a resource that is not in the bundle",
                    "Carries DOB or MRN into the letter body",
                    "Describes the six week therapy course as 'months'",
                ],
            },
            "metadata": {"split": ["imaging", "payer_b", "hard", "sparse_evidence"]},
        },
        # ... 40 to 60 more
    ],
)

One example. The assertions are written by the clinical reviewer, not by the engineer.

WORKED EXAMPLE  ·  THE SAME PATTERN ON A DIFFERENT AGENT

Discharge summary drafting, assertion style

Assertions are not specific to prior authorization. A discharge summary agent on the same loop carries examples shaped identically, with a different clinical spine.

Must be true. Names every medication on the discharge list with dose and frequency. States which home medications were stopped and why. Carries forward every pending result outstanding at discharge. Names the responsible follow up clinician and the interval. Every diagnosis stated resolves to a coded problem list entry or a documented assessment.

Must not be true. Does not state a discharge medication absent from the final medication reconciliation. Does not describe a result as normal when it is still pending. Does not carry forward a resolved problem as active. Does not invent a follow up interval the chart does not support.

Different workflow, identical discipline. The failure mode is the same one in both: a fluent document that asserts something the record does not support.

Then grow it from production

The seed dataset is the floor, not the ceiling. Every case where the nurse edited the draft, every low online score, every outright rejection goes through an annotation queue and, once de-identified, into the dataset with the nurse’s version as the new assertion set. A packet a reviewer rewrote is the most valuable artifact your project will produce all quarter.

Evaluators: three layers, because one is never enough

Teams reach for an LLM judge first and then distrust it, which is the right instinct applied in the wrong order. Cheap deterministic checks should catch most failures. The judge handles only what code cannot read.

Evaluator Type What it catches Runs
Citation resolution Code A cited resource id that is not in the frozen bundle. Hallucinated evidence, caught for free. Offline and online
Schema conformance Code Missing criteria, malformed status values, criteria invented that are not in the questionnaire. Offline and online
PHI leak scan Code Identifiers in the letter body, in logs, or in a trace that should have been masked. Offline and online
Gate integrity Code Any path where a write tool resolved without an approval event. Hard fail, always. Offline and online
Evidence grounding LLM judge The citation resolves, but does the resource actually support the claim made about it? Offline, sampled online
Assertion coverage LLM judge Grades the output against each must and must-not in the example. Offline
Abstention correctness LLM judge no_evidence where evidence existed, and the reverse. Both directions matter. Offline
Trajectory Code Coverage checked before chart access, one evidence call per criterion, step and cost ceilings. Offline and online
Reviewer verdict Human Would you sign this and send it today? The only ground truth there is. Sampled, continuously

WORKED EXAMPLE  ·  THE FAILURE CODE CANNOT SEE

A citation that resolves and still proves nothing

On a sparse-evidence case, the agent set criterion 3 to met and cited DiagnosticReport/8812.

The citation resolver passed it. The id exists in the frozen bundle, the schema is valid, nothing is malformed, and every deterministic evaluator returned 1.0. On a dashboard, a clean run.

DiagnosticReport/8812 is an MRI of the right shoulder from 2024.

The grounding judge scored it zero and the experiment failed the gate. This is the precise division of labour between the two kinds of evaluator. Code can tell you a pointer is valid. Only a reader can tell you the thing it points at supports the sentence it was attached to.

Calibrate the judge before you trust it. Have the nurse grade thirty outputs, run the judge on the same thirty, and look only at the disagreements. Tune the judge prompt until it lines up, then keep feeding human corrections back as few-shot examples. An uncalibrated judge is a random number generator with a confident tone.

from langsmith import Client
from openevals.llm import create_llm_as_judge

GROUNDING = """
For each clinical claim in the output, you are given the resource it cites.
Score the fraction of claims the cited resource genuinely supports.
A resource that is merely topically related does not support the claim.
A claim about duration requires the resource to establish that duration.
<output>{outputs}</output>
<bundle>{reference_outputs}</bundle>
"""

grounding = create_llm_as_judge(prompt=GROUNDING, feedback_key="grounding",
                                model="anthropic:claude-opus-4-5")

def citations_resolve(outputs, inputs):
    """Deterministic. Every cited id must exist in the frozen bundle."""
    ids = bundle_ids(inputs["chart_bundle_ref"])
    cited = [e["resource_id"] for c in outputs["criteria"] for e in c["evidence"]]
    return {"key": "citations_resolve",
            "score": 1.0 if all(c in ids for c in cited) else 0.0}

results = Client().evaluate(
    run_packet_agent,
    data="pa-packet-imaging-v1",
    evaluators=[citations_resolve, schema_ok, phi_scan, gate_integrity,
                grounding, assertion_coverage, abstention_ok, trajectory_ok],
    experiment_prefix="pa-packet-v0.5-mapper-split",
    metadata={"prompt_version": "mapper:v7", "model": "claude-opus-4-5"},
    num_repetitions=3,
    max_concurrency=8,
)

Three repetitions, because a single run on a non-deterministic system tells you about sampling, not about the change you made.

Iterating: one variable at a time, against the scorecard

With the dataset and evaluators in place, improvement stops being a matter of taste. Change one thing, run the experiment, compare. The discipline is unglamorous and it is the entire difference between a system that gets better and a system that drifts.

An experiment log on this workload tends to look like the shape below. The lesson in it is not the numbers, it is which kinds of change move which metric: architecture moves coverage, prompts move grounding, and tools move both.

Version What changed Moves
v0.1 Single agent, all tools, one long prompt Baseline. Criteria coverage poor, grounding worst of any version.
v0.2 Split retrieval into its own subagent Coverage up sharply. Retrieval context stops polluting the letter.
v0.3 Citation contract in the mapper output schema Grounding up sharply. Uncited claims become inexpressible.
v0.4 DTR questionnaire as a structured tool, not prose in context Coverage up again. Dropped criteria largely disappear.
v0.5 Explicit reward for no_evidence in the mapper prompt False claims down. Mild coverage dip: real, and the right trade.
v0.6 Cheaper model on the retrieval subagent only Cost down materially, quality flat. Verified on the hard split.

WORKED EXAMPLE  ·  THE v0.5 TRADE, MADE ON PURPOSE

A metric went down and we shipped it anyway

v0.5 added one paragraph to the mapper prompt stating that no_evidence is a correct and expected answer, graded higher than a guess. On the sparse-evidence split, criterion 3 flipped from a confident and wrong met to no_evidence on most cases.

Criteria coverage dipped, because an assertion checking that the output addresses all four criteria treats a named gap as weaker than a satisfied criterion. The aggregate score moved the wrong way.

We shipped it. A reviewer would rather spend one phone call closing a named gap than discover a fabricated exam finding in an appeal. The fix was not to revert the prompt, it was to fix the assertion: a correctly identified gap now scores as a pass, and fabricated support scores as a hard fail.

That is the most common thing that happens on a real agent project. The metric was wrong before the agent was.

READ THE SPLITS, NOT THE AVERAGEOverall score can rise while the hard split falls, and the hard split is the one that generates appeals. Every release gate should carry a separate floor for the hardest stratum. If you only ever look at the aggregate, you will ship a regression on sparse-evidence cases and find out from a denial letter.

The runtime: agents are not stateless requests

A packet run takes minutes, calls out to systems that are slow and occasionally down, and then stops dead waiting for a human who may be at lunch. Nothing about that shape fits a normal request-response service, which is why agent server runtimes exist.

What LangSmith Deployment and the Agent Server give you here, in order of how much they matter on this workload:

  • Durable execution and checkpointing. A deploy, a pod restart or a transient FHIR outage does not lose thirty steps of work. The run resumes from its last checkpoint.
  • Real interrupts. The approval gate is a first-class pause in the graph, not a polling loop you maintain. The run can sit there for six hours and resume with the reviewer’s decision attached.
  • Threads and state persistence. A case that comes back after a request for additional information is the same thread, with its history intact.
  • Assistants. Configuration is versioned separately from code, so payer A and payer B can run different prompt and policy bindings against one deployment.
  • A task queue built for long runs. Bursty Monday-morning queues do not need bespoke infrastructure.

WORKED EXAMPLE  ·  FRIDAY, 4:52 PM

Nineteen hours of nothing happening, correctly

Case 0147 finishes drafting at 4:52 on a Friday and hits the approval interrupt. The nurse has gone home. The run holds its position in the graph, with its criteria map, its draft and its full message history checkpointed.

Over the weekend, v0.6 deploys. The thread is pinned to the assistant version it started under, so when the nurse opens the packet at 8:10 on Monday she sees exactly what the agent produced on Friday, graded against the policy version it used on Friday. She edits criterion 2’s wording, approves, and the run resumes from the interrupt and emits the submission with her identity attached.

Build this on a stateless service and you own all of it: a job store, a resume protocol, version pinning per in-flight case, and a recovery path for the pod that died at 5:04. None of that is differentiated work, and all of it is where the schedule goes.

On deployment topology, healthcare constrains the choice. Cloud is fine for the de-identified prototype. For production against live PHI you are generally looking at a self-hosted or hybrid arrangement where the Agent Server and its state run inside your own VPC, with appropriate agreements in place. Decide this early, because it changes the networking and the procurement timeline far more than it changes the code.

Two rules worth writing into the design document. Mask PHI at the tracing boundary rather than scrubbing traces afterwards, because afterwards is too late and incomplete. And pin everything that can drift: model version, prompt version, policy corpus version, tool schema version, all recorded on every run. Six weeks from now someone will ask why a January packet differs from a March one, and the only acceptable answer is a diff.

agent evaluation flywheel

Offline evaluation decides whether a version ships. Online evaluation decides what the next dataset looks like.

Governing it without strangling it

Governance in healthcare agents is usually discussed as a blocker. In practice the teams that treat it as a design input ship faster, because they stop relitigating the same objection every month.

Standing release gate

Written thresholds per evaluator, a separate floor on the hardest split, no regression permitted on any safety metric, and a hard zero tolerance on gate integrity and PHI leak. The gate is a file in the repository, reviewed like code.

Audit trail per run

Every tool call, every input and output, the approver’s identity, and the exact versions in play. Reconstructable without access to the model vendor.

Named human accountability

Every submission carries an approver. The agent is a drafting aid, the reviewer is the decision maker, and the record shows that unambiguously.

Continuous human sampling

A fixed percentage of approved packets goes back to a second reviewer weekly. This is how you detect quality drift that online evaluators are too agreeable to flag.

WORKED EXAMPLE  ·  THE AUDIT RECORD FOR CASE 0147

What you hand to a reviewer who asks why this packet says what it says

  • run_id 0147-20261030-1b9c
  • agent_version pa-packet v0.6
  • model claude-opus-4-5 (retrieval: sonnet-4-5)
  • prompts supervisor:v4 mapper:v7 writer:v3
  • policy_version plan_b / 2026.09
  • tool_calls 14, each with redacted args + timestamps
  • criteria_map met, met, no_evidence, met + citations
  • interrupt_raised 2026-10-30 16:52:08 PT
  • approver UM nurse, badge id, 2026-11-02 08:14 PT
  • edits_at_approval 1, criterion 2 narrative, diff stored
  • submitted 2026-11-02 08:14 PT, PAS bundle hash

Every line of that is reconstructable without access to the model vendor. That property is what turns a six month compliance conversation into a one hour one.

None of this is exotic. It is the same change control any team would apply to a clinical decision support tool, pointed at the three artifacts that are new: the prompt, the dataset and the evaluator set.

The same loop, four other healthcare agents

Prior authorization is the worked example, not the point. The discipline transfers to any healthcare workflow where a fluent document can be confidently wrong. What changes between them is which single evaluator decides whether the thing ships.

Agent What is actually hard The evaluator that decides it
Discharge summary drafting Pending results and medication reconciliation. The clinical content is easy; knowing what was still outstanding at the moment of discharge is not. Pending-result fidelity. A result described as normal that had not resulted is a patient safety event, not a quality issue.
Denial appeal letters Reading the denial reason accurately and rebutting that reason rather than restating the original request. Most human appeals fail here too. Rebuttal alignment. An LLM judge scoring whether each paragraph addresses a stated denial reason, graded per reason, not overall.
Referral triage and routing Urgency. Volume is high, most cases are routine, and the cost of the rare miss is asymmetric with everything else in the queue. Recall on the urgent class, measured alone. Overall accuracy is actively misleading when 97 percent of cases are routine.
Clinical trial pre-screening Exclusion criteria, which are more numerous, more negative and far worse specified than inclusion criteria. Exclusion-criteria recall. A missed exclusion wastes a screening visit and can enroll someone who should not be enrolled.

Four different workloads, one architecture pattern, and in each case a release gate that is dominated by a single metric chosen for the failure that matters most. Picking that metric correctly, before the build rather than after, is most of what separates agent projects that reach production from ones that demo well for eighteen months.

The common thread is the one this entire piece is about. None of these agents decide anything. They assemble, draft and flag, in front of a named human who decides, and every one of them is measured against a dataset built by the clinician who knows what correct looks like.

What this actually takes

An honest sequence for a first production packet agent, assuming one service line and one or two payers:

  • Weeks 1 to 2. Scope, trust boundary, tool inventory, sandbox access. Prototype one slice end to end, traced.
  • Weeks 3 to 5. Dataset. Pull and de-identify cases, write assertions with the clinical reviewer, calibrate the judge. The long pole, and the one that gets compressed first and should not be.
  • Weeks 6 to 9. MVP. Subagent architecture, prompt iteration, experiment per change, gate defined and met.
  • Weeks 10 to 12. Deployment topology, approval UI, audit trail, online evaluators, shadow running against live cases with no submission path enabled.
  • Ongoing. The loop. Promote failures, re-run, re-gate.

The loop is the product. Everything before it is setup.

Where to start on Monday

If you take one thing from this: pull twenty closed prior authorization cases and write the assertion lists by hand with a reviewer sitting next to you. Not the agent. The assertions. You will learn more about what reliable means in your organization in that one afternoon than in a month of prompt iteration, and you will have the beginning of the only asset that makes the rest of it possible.

Frequently asked questions

What is agent engineering, and how is it different from prompt engineering? +

Prompt engineering improves a single interaction. Agent engineering is the practice of building, measuring and operating an agent as a production system: scoping the workload, designing the runtime and its guardrails, building a dataset that defines correct behavior, writing evaluators that score it, and running a continuous loop in which every production failure becomes a permanent test case.The practical difference is accountability. Prompt engineering can tell you an output looks good. Agent engineering can tell you how often the agent is wrong, in what way, and whether yesterday’s change made it better.

Why use DeepAgents instead of building directly on LangGraph? +

DeepAgents is a harness built on top of LangChain and the LangGraph runtime. It gives you the patterns that long-horizon agents need out of the box: a planning tool, subagents with their own prompts and tool sets invoked through a task tool, a virtual filesystem for intermediate artifacts, and a middleware stack for things like summarization and human-in-the-loop interrupts.
You would build directly on LangGraph when your control flow is fixed and you want explicit graph structure. For a workload where the number and order of steps varies by case, the harness saves real time. Either way the runtime underneath is the same.

How many examples does a useful evaluation dataset need? +

Fewer than people expect, chosen far more carefully than people expect. Forty to sixty well stratified real cases will surface most failure modes on a workload like prior authorization, provided they span the outcome types, payers and evidence densities you actually see.
The size that matters is per split. If your hardest stratum has four examples, you cannot gate on it. Grow the dataset from production failures rather than from synthetic generation, which tends to produce examples that are easy in the same ways.

Can you trust an LLM-as-judge evaluator in a clinical context? +

Only after you calibrate it, and only for the things code cannot check. Deterministic checks should carry most of the load: whether a cited resource exists, whether the schema is valid, whether a write happened without an approval. Those are cheap, exact and never drift.
Use the judge for the genuinely subjective part, such as whether a cited resource actually supports the claim made about it. Then have a clinician grade a sample, compare against the judge, and tune the judge prompt until they agree. Feed human corrections back as few-shot examples and re-check the agreement periodically.

How do you handle PHI when the agent is traced and evaluated? +

Mask at the tracing boundary rather than scrubbing afterwards, so identifiers never land in the observability store in the first place. Run evaluation datasets on de-identified frozen bundles. For production against live PHI, deploy the agent server and its state inside your own environment under the appropriate agreements, and treat the trace store as in-scope infrastructure rather than as a developer tool.
Make the PHI leak scan a hard-fail evaluator that runs both offline and online, so a regression shows up as a failed gate rather than as an incident.

Does an AI agent decide prior authorization approvals? +

No, and it should not be built to. The architecture described here assembles evidence, maps it against the governing criteria with citations, and drafts a packet. A licensed reviewer makes the determination, and the submission path is physically gated behind that person’s approval with their identity recorded.
The value is in removing the retrieval and assembly work that happens before a reviewer can apply judgment, not in replacing the judgment.

What does CMS-0057-F require, and by when? +

The CMS Interoperability and Prior Authorization final rule requires impacted payers, including Medicare Advantage organizations, state Medicaid and CHIP programs and qualified health plan issuers on the federally facilitated exchanges, to implement a FHIR-based Prior Authorization API by January 1, 2027, alongside Provider Access, Payer-to-Payer and expanded Patient Access APIs.
The operational requirements came earlier: as of January 1, 2026, expedited decisions are due within 72 hours and standard decisions within seven calendar days, denials must carry a specific reason, and prior authorization metrics are publicly reported. CMS recommends the HL7 Da Vinci CRD, DTR and PAS implementation guides as the technical standard. Confirm current dates and applicability against CMS guidance for your organization type.

What other healthcare workflows suit this kind of agent? +

Any workflow where the output is a document that a human has to stand behind, and where a fluent answer can be confidently wrong. Discharge summary drafting, denial appeal letters, referral triage and routing, clinical trial pre-screening, coding and documentation support, and intake all fit the same pattern.
What changes between them is which single evaluator decides whether the agent ships. For discharge summaries it is fidelity on pending results. For referral triage it is recall on the urgent class measured on its own, because overall accuracy is misleading when most cases are routine. For trial pre-screening it is exclusion criteria recall. Choosing that metric before the build, rather than after, is most of what separates agent projects that reach production from ones that demo well for a year.

Raj Sanghvi

Raj Sanghvi is a technologist and founder of Bitcot, a full-service award-winning software development company. With over 15 years of innovative coding experience creating complex technology solutions for businesses like IBM, Sony, Nissan, Micron, Dicks Sporting Goods, HDSupply, Bombardier and more, Sanghvi helps build for both major brands and entrepreneurs to launch their own technologies platforms. Visit Raj Sanghvi on LinkedIn and follow him on Twitter. View Full Bio