Building AI

Building policy-adherent agents: Symbolic Reasoning Graphs

Building policy-adherent agents: Symbolic Reasoning Graphs

Health plans want AI to reduce administrative work and accelerate clinical reviews. Those reviews are governed by the plan’s clinical guidelines, internal UM policies and other documented rules (collectively, “policies”). Achieving this requires systems that, first, faithfully capture those policies and how they are applied today and, second, apply them accurately and consistently at scale.

Both are challenging:

  1. Policies contain nuanced requirements, exceptions, and ambiguities that experienced clinicians resolve through experience and institutional knowledge. These interpretations need to be made explicit before AI can use them.
  2. The system must then apply the agreed logic accurately and consistently across millions of cases, showing how it reached each recommendation.

We developed Symbolic Reasoning Graphs (SRGs) to meet these requirements:

DomainLLM-onlyLLM using SRG
Clinical NuanceMay quietly fill gaps in the guideline with its own interpretationMakes the plan's interpretation explicit and reviewable
TransparencyGuideline interpretations can remain implicit and be difficult to auditTeams can review explicit criteria and context, and trace every AI workflow step-by-step
ConsistencyThe model can overlook requirements, or combine them differently across runsGiven the same facts, produces the same outcome across different models and runs
Analytics and modelingUnstructured nature makes guideline performance hard to compareCriterion-level results help teams investigate what drives outcomes, and identify opportunities to improve clinical quality
Breadth and interoperabilityDifferent policies and applications may require separate interpretations and output formatsA common structure supports reuse across policies, guidelines, SOPs, questionnaires and other common formats

Today, we're open-sourcing our symbolic reasoning library so others can build on this work, both in healthcare and in fields such as law and financial services — where reliable AI reasoning depends on following complex rules.

Introduction to the problem

Prior authorization illustrates these challenges in practice. It is the process through which a healthcare provider seeks a health plan's approval for certain treatments or services before they are delivered.

A central part of this process is medical necessity review: does the requested service meet the plan's clinical criteria for the patient's documented condition? Answering that requires applying complex medical necessity guidelines to long, often fragmented clinical records. Clinical reviewers must find the relevant evidence, interpret individual requirements, and account for exceptions. An error can delay appropriate care, create additional work for clinicians, or lead to an incorrect coverage decision.

The natural starting point for automating this review is to give an LLM the policy and the clinical record and ask it for a determination. We encountered three recurring problems with this approach.

Applying the policy consistently

Medical necessity review policies are long, dense, and interdependent. For example, Chapter 8 of the Medicare Benefit Policy Manual, covering extended care services, runs to well over 50 pages of terms, conditions and exceptions.

Asking an LLM agent to follow a policy comes with no guarantee that it will. An agent might correctly apply a policy across many cases, then miss a prerequisite or overlook an exception on an individual case. And because LLM inference is non-deterministic, it can reach different conclusions on the same case across runs.

These failures can be infrequent and still block automation. In prior authorization, a missed check can change a determination and delay a patient's care. We need more than an agent that sometimes follows the policy.

Interpreting ambiguous policies

Sometimes the policy itself contains uncertainty:

The criterion "Is there documentation of unsuccessful conservative therapy for at least 6 weeks?", annotated with three open questions: how explicit the documentation needs to be, how success is defined, and what counts as conservative therapy in this medical context.

A requirement for "six weeks of conservative therapy" may leave open which treatments qualify, whether the weeks must be consecutive, or what documentation is accepted. The policy text alone does not settle these questions, and the correct answer depends on how the plan interprets them. Utilization management (UM) nurses apply that interpretation through clinical context, training, and established practice, often without it ever being written down. An LLM has no access to those conventions, so it resolves the ambiguity on its own terms, and may resolve it differently across cases or runs.

Finding the relevant evidence

We also observed that agents struggled more with fact-finding in medical records as policies grew longer and more complex. Alongside interpreting the policy and tracking its requirements, the agent had to locate and assess the evidence relevant to each criterion.

We suspect that this is caused by what is commonly referred to as "context rot": growing context and competing demands leading to decreased reasoning and recall performance in LLMs.

An agent can fail at any of these independently. Of the three, the policy-related failures (the first two) are the more severe, because they are systematic. A policy interpretation error repeats across every case that touches that rule, and could result in delays or denial of care to a large population of patients.

A purely LLM-based approach therefore carries an outsized risk for any agent that has to apply a policy faithfully.

A separation of concerns

In a default LLM-based approach the policy is part of the prompt. The agent re-reads and re-interprets it on every case, alongside the case itself. Interpreting the policy and evaluating the case are two different jobs, and the agent is doing both at once with no boundary between them.

Our approach was to separate them. The policy becomes an explicit artifact: its logic is executed rather than interpreted, it has a place to record how its ambiguities were resolved, and the model is left with only the case to reason about.

We built a symbolic reasoning framework that does this by construction, moving the policy into a deterministically executable symbolic reasoning graph.

Symbolic Reasoning Graphs

A symbolic reasoning graph (SRG) is a directed acyclic graph that encodes a policy's conditional logic as an executable plan. It is built from four kinds of elements:

  • Predicates: atomic, checkable boolean questions about the case.
  • Expressions: boolean combinations of predicates and other expressions under an explicit operator: all of these, any of these, atleast 2 of these, atmost 1, exactly 3, optionally negated with not or none. Expressions nest arbitrarily and can be named for reuse. This is the policy's logic, written down.
  • Outcomes: terminal verdicts. Approved. Escalated to a human reviewer. Denied.
  • Paths: rules that connect expressions to an outcome. When the path's conditions hold, the outcome is reached.

Edges in the graph represent data dependency: an expression depends on the predicates and sub-expressions beneath it, and an outcome depends on the path that reaches it. With these constructs, an SRG represents policies of arbitrary shape in a single, uniform format.

An illustrative medical necessity policy, encoded as an SRG:

Not traversed Traversed Awaiting answer Answered this batch
100%
A recorded solve of this policy for one synthetic patient. Step through each batch of answers, and click any node to see its question, context and answer.

Note the context on conservative_tried. It resolves ambiguities the policy might leave open, like which treatments qualify, whether the weeks must be consecutive, and what counts as unsuccessful. It is also where a plan's own interpretation of the policy is recorded, so two plans with the same policy text can encode different answers.

Solving an SRG

An SRG is evaluated by a deterministic solver. At any point, the solver identifies the next set of predicates it needs answered, yields control to an external oracle to provide answers, and then continues evaluating the graph based on those answers. It iterates until outcomes are reached or every path forward has been exhausted.

The oracle is any entity that can answer predicate questions. In our case we delegate to an LLM agent, which is given the predicate question, any context attached to it, the clinical record, and, where relevant, its earlier answers on the same path. It is not given the policy. The oracle is never asked about the policy's structure, only whether a stated criterion holds for the case in front of it. The same SRG can be solved with a different oracle, such as a database lookup or a human reviewer, without modification.

Upon completion, the execution can be queried for outcomes and their corresponding reasoning traces. A reasoning trace is the record of which predicates were evaluated, what the oracle answered for each, and which path fired:

Outcome: Approved: lumbar epidural steroid injection as requested
  YES  Is the requested service a lumbar epidural steroid injection?
  YES  Is this a request for a first injection?
  YES  Does the documented diagnosis support the requested injection (lumbar radicular pain)?
  YES  Does imaging within the last 12 months show a finding consistent with the symptoms?
  YES  Is there documentation of unsuccessful conservative therapy for at least 6 weeks?
  NO   Is any listed contraindication to epidural injection present?

This trace is not an explanation generated by the LLM alongside the answer. It is the computation itself, serialized, so the rationale cannot diverge from the decision.

Executing a policy this way gives us the separation we wanted. The solver is a pure function of the graph and the oracle's answers: given the same answers, it produces the same outcome and trace every time. Non-determinism lives only in the answers themselves. An earlier answer can inform a later one, but never how they combine. The policy is interpreted once, when the SRG is written, and applied identically to every case.

Fact-finding becomes a series of narrow, checkable yes/no questions. The oracle never has to hold the whole policy, the whole record, and a running tally of requirements at once, which is what addresses the evidence-finding problem. When it gets an answer wrong, the error is local to one predicate and visible in the trace.

Authoring SRGs

Because execution is correct by construction relative to the SRG, the burden shifts to producing high quality SRGs that are lossless with respect to the source policy.

We model SRG generation as an offline process. A policy is encoded, reviewed against the source document, and versioned before it ever reaches the solver; nothing about the policy is generated or interpreted at runtime. The encoding is produced by domain experts, by LLMs, or by the two together, and it is revised repeatedly before approval. So the representation has to be readable by a clinician checking it against the policy, and reliably producible and editable by an LLM.

We started with graph representations directly, as JSON node and edge lists. We found that graph-based representations are hard for LLMs to generate, and harder still to make targeted edits to, especially for very large policies. A small change to the policy meant reasoning about node identifiers and edge lists rather than about criteria, and the result was often a dangling reference or a lost exception.

srg-lang

To address this, we built srg-lang, the language behind .srg files. srg-lang is a small declarative DSL (domain specific language) for writing SRGs. It takes inspiration from logic programming languages like Prolog, shrunk down to only the concepts needed to represent policies, and gives the author a target that reads like the policy it is transcribing. The policy from the figure above, in srg-lang:

predicate is_lumbar_esi:        "Is the requested service a lumbar epidural steroid injection?"
predicate is_initial_request:   "Is this a request for a first injection?"
predicate is_repeat_request:    "Is this a request for a repeat injection in an existing series?"
predicate dx_supported:         "Does the documented diagnosis support the requested injection (lumbar radicular pain)?"
predicate imaging_correlates:   "Does imaging within the last 12 months show a finding consistent with the symptoms?"
predicate conservative_tried:   "Is there documentation of unsuccessful conservative therapy for at least 6 weeks?"
  context: """Conservative therapy includes physical therapy, NSAIDs or other oral analgesics,
            activity modification, and home exercise programs. The 6 weeks need not be consecutive.
            Unsuccessful means documented persistence of symptoms at the end of the course.
            Documentation must appear in the treating provider's notes."""
predicate conservative_precluded: "Is conservative therapy documented as contraindicated or not tolerated?"
predicate severe_impairment:    "Is there documented severe functional impairment or intractable pain despite analgesics?"
predicate prior_relief:         "Did a prior injection produce at least 50% pain relief lasting 6 weeks or more?"
predicate under_annual_limit:   "Has the patient received fewer than 4 injections in the past 12 months?"
predicate contraindication:     "Is any listed contraindication to epidural injection present?"

outcome approve: "Approved: lumbar epidural steroid injection as requested"
outcome manual_review: "Referred for manual review: possible contraindication"

define clinical_basis:      all(dx_supported, imaging_correlates)
define repeat_criteria:     all(prior_relief, under_annual_limit)
define no_contraindication: not(contraindication)

path standard:  is_lumbar_esi => is_initial_request => clinical_basis => conservative_tried     => no_contraindication => approve
path precluded: is_lumbar_esi => is_initial_request => clinical_basis => conservative_precluded => no_contraindication => approve
path urgent:    is_lumbar_esi => is_initial_request => clinical_basis => severe_impairment      => no_contraindication => approve
path repeat:    is_lumbar_esi => is_repeat_request  => repeat_criteria                          => no_contraindication => approve

path standard_review:  is_lumbar_esi => is_initial_request => clinical_basis => conservative_tried     => no_contraindication !=> manual_review
path precluded_review: is_lumbar_esi => is_initial_request => clinical_basis => conservative_precluded => no_contraindication !=> manual_review
path urgent_review:    is_lumbar_esi => is_initial_request => clinical_basis => severe_impairment      => no_contraindication !=> manual_review
path repeat_review:    is_lumbar_esi => is_repeat_request  => repeat_criteria                          => no_contraindication !=> manual_review

There is no control flow and no mutable state. The file compiles to the graph. It is easy to read, token efficient, easy to modify, and syntax verifiable, which makes it well suited to both human and AI authors.

We work with thousands of medical policies, across prior authorization, HEDIS, claims and benefits checks, and payment integrity. To meet the needs of SRG authoring at that scale, we built custom agents and fine-tuned models to be good at writing and editing srg-lang.

Automated digitization of arbitrary policies is an active area of research at Anterior.

Policies as a human <> AI interface

As AI agents take on more of the world's work, human-written policies become both our interface for directing that work and the guardrails around it.

As an interface, policies have to get clearer. The domain experts who applied them filled the gaps with judgment. Agents will fill them too, and then apply the result at scale. The questions a policy leaves open should be answered once, deliberately, by someone accountable for the answer.

As guardrails, they only hold if we can trust that an AI system follows the policy it is given. Not usually, but every time, in a way that can be shown.

Symbolic reasoning graphs are one step towards both these goals. Making agents policy-adherent is critical to running them in production, and we intend to keep publishing what we learn. Hence we are open-sourcing a research preview of the symbolic-reasoning library and an MCP server alongside it.

Source available at: https://github.com/anteriorai/symbolic-reasoning

← Back to Insights