How a Model Gets Built

For Both Executive and Technical Readers

Elicitation, sparse data, integration, and the model library, the method from expert knowledge to a running causal model.

DAGs are not the only language for causal structure.

A directed acyclic graph is a powerful representation, but it is not the only one. String diagrams, from applied category theory, represent causal processes as morphisms that compose along a shared interface. Where a DAG names nodes and edges, a string diagram specifies how processes wire together and sequence. It allows algebraic reasoning across compositions without re-specifying the full graph. Recent work by Fong, Jacobs, and Zanasi has shown that several core causal inference results, including the do-calculus, can be derived cleanly in this framework, often with shorter proofs and tighter compositionality guarantees than the graph-theoretic originals.

Why Rung3 uses DAGs anyway. Enterprise causal inference tooling, Bayes Server, GeNIe, AgenaRisk, is built on the DAG / CPT / junction-tree stack. String diagrams remain a research formalism with no production implementation. The practical choice is DAGs. The intellectual horizon is wider.

Two equivalent representations of the same causal model. Left: a directed acyclic graph with observed variables W, X, Z, Y and latent variables U1 and U2 shown as grey nodes. Right: a factor graph showing the same structure as probability factor boxes connected by variable nodes, making the conditional distributions explicit. Both encode identical causal assumptions, the DAG emphasises conditional dependencies, the factor graph emphasises probabilistic composition.
The DAG (left) and the factor graph (right) encode exactly the same causal assumptions. W, X, Z, Y are observed; U₁ and U₂ are latent. The DAG shows which variables cause which; the factor graph makes each conditional distribution, P(X | U₁, U₂, W), P(Z | X, W), P(Y | Z, U₂), an explicit, named object. Rung3 elicits the DAG; Bayes Server computes over the factor graph internally.

Getting the model out of the expert’s head.

If the domain model can’t be learned from your data, it has to come from the people who carry it. Elicitation is the discipline of turning what a senior expert knows, often tacitly, into a graph and a set of mechanisms a machine can run. This is the hard part, and it is where most of the method lives.

It is not an interview. The expert rarely volunteers a DAG; they volunteer judgments, war stories, and rules of thumb. The work is to convert those into structure and mechanism, then check the result against cases the expert will recognize.

The procedure
01 · Structure
Draw the graph from the expert’s reasoning, which variables, which arrows, and which arrows are absent. The absences matter most.
02 · Mechanism
For each node, elicit how it responds to its parents, a CPT, a conditional-linear-Gaussian response, a monotonicity constraint.
03 · Encode
Commit it to a runnable model, a CLG Bayesian network, with the exogenous terms made explicit.
04 · Validate
Put recognizable cases to the model; check the answers against the expert’s gut. Disagreement localizes a wrong arrow or a mis-set strength.

What a senior expert is actually doing

The expensive skill isn’t recalling facts; it’s creative abduction: positing the hidden variable that explains a pattern. “These accounts churn for a reason we don’t log.” The latent cause is named, given a place in the graph, and made queryable. Most of what separates a senior expert from a junior one is a richer set of these unobserved variables; elicitation’s job is to surface them.

When experts disagree

They will, on structure and on strength. Two experts draw different arrows (a structure disagreement); two experts agree on the arrow but not its magnitude (a strength disagreement). Neither is fatal. Structure disagreements resolve by finding the case that distinguishes the two graphs and asking which answer is right. Strength disagreements are pooled, the spread carried forward as uncertainty, not averaged away. Both have full worked treatments in the reconciliation models.

Proof, an absent arrow is an assumption

Movement 1 showed that direction isn’t in the data: arrows can’t be recovered from correlation. The corollary is decisive, whoever supplies the arrows supplies identification.

Before & after the expert’s assumption
U X Y U unobservedNOT IDENTIFIED Z X Y Z named & measuredIDENTIFIED

Before. X and Y share a common cause. If it’s unobserved, P(Y | do(X)) is not a function of the data, not identified.

The expert’s move. Assert the structure: “the only common cause is Z, and I can measure it”, equivalently, there is no unobserved U once we condition on Z. That assertion is an absent arrow: untestable from the data, decisive for the answer.

After. The back-door criterion is satisfied: P(Y | do(X)) = ∑z P(Y | X, Z=z) · P(Z=z). Identified.

Elicitation isn’t a softer, less rigorous input than data. It is the production of exactly these arrows, the input identification actually requires.

A model you can run is now in hand. But the expert’s mechanisms are approximate and the data to sharpen them is thin. That is the next problem.

Substituting for the data you don’t have.

The decisions that matter most are the ones you face least often: a price move in a new segment, a once-a-decade capital call, a failure mode seen twice. The data is thin by nature, and thin data is exactly where machine learning has nothing to say. A regression needs many more rows than parameters; at a dozen records it overfits, at three it can’t start. The honest output of ML on sparse data is a shrug.

An elicited model doesn’t wait for the data to exist. The expert’s mechanism is already a full predictive distribution, a prior: so the model answers from the first day, with a calibrated error bar instead of a shrug. Then, as records arrive, you don’t retrain. You update.

Bayesian updating (averaging, but weighted by precision)

Bayesian updating fuses what you believed with what you just saw, each weighted by its precision: the inverse of its variance. A confident expert and a noisy measurement: the expert dominates. A vague prior and a clean reading: the reading dominates. Nothing is averaged blindly; every source counts for exactly its certainty.

Precision-weighted fusion

Several noisy estimates of one latent quantity combine into a single sharper one:

1 / σ²post  =  ∑i 1 / σ²i    precisions add
μpost  =  ( ∑i μi / σ²i ) / ( ∑i 1 / σ²i )

The posterior is sharper than any input, combining estimates reduces variance.

Proof, the numbers

Three readings of one unknown weight, 10, 15, 17, on scales of differing quality (variances 1, 2, 3). Fuse them:

Worked fusion
precision  =  1/1 + 1/2 + 1/3  =  1.833  →  σ²post = 0.545,   σpost = 0.74
μpost  =  (10/1 + 15/2 + 17/3) / 1.833  =  12.64
12.64 ± 0.74
Bayes Server monitor showing the posterior distribution over the latent weight: mean 12.64, standard deviation 0.739.

The same fusion, computed in Bayes Server.

The fused estimate is tighter than the best single scale, variance 0.545 against 1.0. Now read the first reading as an expert prior and the rest as data: the math is identical. The expert is simply the first, usually most precise, estimate; sparse data sharpens it, it doesn’t have to replace it.

The contrast is the whole point. At N = 3 a regression returns nothing usable. The Bayesian model returns 12.64 ± 0.74, an answer and an honest error bar, and tightens with every record after.

Download the worked model (.bayes) →, enter the readings one at a time and watch the posterior tighten.

This is why elicitation and sparse data are one method, not two. The expert gives you a model when you have no data; precision-weighted updating makes every scrap you later collect count for exactly what it’s worth. With a model that answers and improves, the only thing left is to let people put questions to it in plain language.

LLM → SCM → LLM.

The language model is the interface, not the reasoner. It reads the question, decides what is being asked, hands a precise query to the causal model, and turns the model’s answer back into language. The reasoning happens in the model; the LLM translates in and narrates out.

The query loop
Question
natural language
LLM
Estimand
which P(·)?
LLM → SCM
Inference
junction-tree, exact
SCM
Posterior
a distribution
SCM
Answer
natural language
LLM

The LLM owns the two ends, language in, language out. The single bridge it must get right is the estimand: which probability the question is really asking for.

A worked instance, a subscription business weighing a price cut, the textbook Rung-2 decision: “If we cut price 10%, what happens to churn?” The LLM recognizes a Rung-2 question, forms the estimand P(churn | do(price↓)), compiles it to an exact inference on the SCM, and narrates the posterior back, with the error bar intact.

What runs underneath

The engine
Representation
Discrete and conditional-linear-Gaussian Bayesian networks, structural causal models with an explicit graph and the mechanism behind each node.
Counterfactuals
A Balke–Pearl response-function encoding: exogenous U-nodes make each mechanism deterministic-given-noise, so abduction–action–prediction runs as ordinary inference.
Inference
Exact, by junction-tree propagation, not sampling. Where a query isn’t point-identified, the model returns bounds rather than a false point.
Tooling
Authored in Bayes Server for the examples here, queried from R or Python, exported to open formats. The artifact is yours, no lock-in to a vendor or a hosted endpoint.

Proof, the silent failure

The danger isn’t a wrong answer that looks wrong; it’s a wrong answer that sounds right. Ask the churn question of a language model alone and it answers fluently, from association. But the words of a question do not carry their own rung.

Same words, two rungs

Question: “what happens to churn if we cut price?”

Rung 1 · P(churn | price = low)
What self-selected low-price customers already did. Fluent, plausible, and confounded. This is what the LLM answers on its own.
Rung 2 · P(churn | do(price = low))
What cutting price actually causes. The estimand the decision needs, and only the model computes it.

Same words, two rungs, two numbers, and the wrong one is the confident one. The integration’s whole job is to force the rung to be named: the LLM can’t tell the two apart from the words, and the model can’t be asked the wrong one without saying so.

That is the unit of work, one question, correctly routed, truthfully answered. The next step is to scale it: not one model, but a library.

One model is a tool. A library is an asset.

A single SCM answers one domain. An organization runs on dozens of coupled decisions, pricing touches churn touches capacity touches risk. So the deliverable isn’t a model; it’s a versioned library of them, each elicited from the people who own that domain, together forming the organization’s explicit domain model. Three properties make a library more than a folder of files.

Composition

Models connect, the posterior of one is evidence for the next. A pricing model’s output feeds a churn model; a supplier-risk model feeds a capacity model. Questions cross domains without a human stitching spreadsheets together. That hand-off holds only where the shared variable d-separates the two models, otherwise the seam leaks dependence, and the join must be checked on the merged graph.

Provenance

Every arrow and every parameter carries its source: which expert, which session, which document or dataset, and when. The library is auditable: you can show a board or a regulator not just the answer but the chain of assumptions behind it. That is the difference between a model you trust and a model you can defend.

Scope & validity

Each model declares the regime where it holds. Ask a question outside that regime and the library refuses rather than fabricates, the exact opposite of a language model’s confident extrapolation past the edge of what it knows.

Proof, a composed query, and its trail

A composed query
Supplier-risk SCM
Capacity SCM
On-time delivery
posterior + trail

“If our tier-2 supplier’s failure rate doubles, what happens to on-time delivery?” routes through both models, the posterior of the first becomes evidence for the second. The answer arrives as a distribution and a provenance trail naming every assumption it rests on. No single dataset contains this question; the composed library answers it anyway.

A library that composes, remembers its sources, and knows its limits is no longer a consulting deliverable, it is infrastructure the organization owns and compounds. Which points at the real product: not a model, but the standing capability to keep building them.

The deliverable isn’t a model. It’s an organization that reasons causally.

Everything to here describes one model and one question. The engagement leaves the organization with more than that. A library its people can read, extend, and defend. The habit of asking “what will this cause?” instead of “what does the data show?” A capability that compounds with every decision domain added. That is the transformation. It is what Rung3 advises.

The shape of the work

The engagement is Movements 3 through 7, run on one decision domain at a time: elicit the experts, stand up the model, fuse what data exists, wire it to the LLM, fold it into the library. Eight to sixteen weeks per domain. The first leaves you with a working model and a team that can run it; each one after is faster, because the method and the library are already in place.

What that capability looks like in full, one domain, every rung, composed and mediated, is the movement that follows.

Healthcare: one library, mediated.

The whole method, on one domain, what Movements 3 through 7 produce: the working artifact an organization is left with, a clinical library it reasons with across all three rungs, put to it in plain language.

One model, all three rungs

Start with a single elicited model, a patient’s medication burden and its effect on weight, cholesterol, hospitalisation, and mortality. The same model answers a different question at each rung:

One model, three questions
Rung 1 · Seeing“Among patients like her, what is the outcome?”P(outcome | age, SES)
Rung 2 · Doing“If we deprescribe, what changes?”P(outcome | do(Medications))
Rung 3 · Imagining“Would she have done better on fewer meds?”P(outcomemeds=5 | her record)

Step through the model answering each in turn:

Same model, three rungs. The data never changed, the question did, and only a structural model tells the three apart. The organization gets exactly this for every clinical sub-domain it stands up.

Many models, one library

The healthcare causal-model library as a shelf of six labelled book spines held between two elephant bookends: Iatrogenic (polypharmacy harm), Oncology (immunotherapy benefit), Sepsis (dynamic treatment), Pharmacovigilance (adverse-event attribution), Drug repurposing (effect transport), and TR depression (treatment sequencing).

Each sub-domain is its own elicited SCM, scoped to the decisions it was built for. They are not siloed files: they share variables, and a posterior computed in one becomes evidence in the next, so a question can cross domains without a person stitching spreadsheets together. That hand-off is sound only when the shared interface d-separates the two modules, a condition checked on the merged graph and recorded in each model’s scope, so a composition carries its own license.

They converge on one composed clinical model, a cardiometabolic domain model linking blood pressure, cholesterol, diabetes, and heart disease to hospitalisation, mortality, and cost. Every node carries its provenance; each model declares its regime, so a query outside scope returns bounds or a refusal rather than a fabricated point.

The composed clinical model in Bayes Server: lifestyle and medication levers feeding blood pressure, cholesterol, blood glucose, BMI, hypertension and diabetes, on to heart disease, hospitalisation, mortality risk, quality of life and healthcare costs.

The composed clinical model. Intervention levers (age band, statins, cholesterol) feed the cardiometabolic chain; hospitalisation, mortality, and cost sit downstream. Each sub-domain model above plugs into this surface.

A composed query

“If she starts immunotherapy, how does the added cardiotoxicity change her ten-year cardiac outlook?” routes through two models: the oncology SCM returns a toxicity posterior, which becomes evidence in the cardiometabolic model. The answer carries both effects and the trail of assumptions behind them, a question no single dataset contains.

Mediated by an LLM

The clinician never names a model or a rung. The language model reads the question, routes it to the right sub-model and the right rung, runs the exact query, and narrates the posterior back, the loop from Movement 5, now over the whole library. Two exchanges, two routes.

Clinician
“She’s 78, on 18 active prescriptions; her weight and her non-cardiac admissions keep climbing. If we deprescribe down to the essential few, what actually changes for her?”
LLM · route
Iatrogenic model, individual counterfactual. Anchor her history, then lower the burden:P(BMI, hospitalisation, mortality | do(Medications = low), her record)
SCM · infer
Abduct her exogenous terms from the record, clamp Medications, propagate, exact inference, inbound edges severed.
LLM · narrate
Deprescribing lowers her BMI and her non-cardiac admissions, but the model surfaces a trade-off: statins sit inside that count, so LDL rises as the burden falls. The cut isn’t fewer meds, it’s which.
do(Medications) in the iatrogenic model: the medication node is clamped and BMI, LDL cholesterol, hospitalisation and mortality update downstream.

do(Medications): the node is clamped (the spike), its inbound edges severed; the downstream monitors settle to their post-intervention posteriors.

Clinician
“She’s PD-L1 positive, ECOG 1, moderate comorbidity. She had chemotherapy and relapsed. Would immunotherapy have changed her twelve-month survival?”
LLM · route
A different model, and a counterfactual, the oncology SCM, Rung 3:P(Survival₁₂ | do(Treatment = immunotherapy), her profile)
SCM · infer
Abduct her genomic, performance, and comorbidity terms from the record; set Treatment to immunotherapy; read survival and toxicity together.
LLM · narrate
Under immunotherapy her modeled twelve-month survival is higher, but severe-toxicity risk rises with her comorbidity. A real trade-off the trial’s average would have hidden for her.

What the LLM answers alone: the population guideline, the deprescribing rule, the trial’s average survival, fluent and confident. The words of a question carry neither their rung nor their model; the library supplies both.

Worked example: sepsis, LLM-mediated →

The models

Every model in the library ships as a runnable file, authored in Bayes Server for the examples here, queried from R or Python, yours to keep. Bayes Server and its peers are about as “No Code” as we can get.

Iatrogenic medicationspolypharmacy harm, weight, lipids, admissions.bayes ↓
Oncology immunotherapywho benefits from checkpoint inhibition.bayes ↓
Sepsisdynamic treatment under a time-varying confounder.bayes ↓
Pharmacovigilanceadverse-event attribution.bayes ↓
Drug repurposingtransporting an effect across populations.bayes ↓
TR depressiontreatment sequencing.bayes ↓
Composed clinical modelthe cardiometabolic surface the sub-models plug into.bayes ↓

The same method runs across insurance, finance, marketing mix, and policy, the engagements →

The wedge from Movement 1 holds all the way down. You cannot learn a domain model of your business from your business’s data, but you can elicit it, run it, and compound it. That is the capability Rung3 builds, and it is the one that doesn’t leave when the engagement ends.

Most of the funded causal-AI field tries to mine cause and effect from a company’s own data. This method does the opposite. Here’s the full map of who’s selling what, and the corner Rung3 holds alone.

Each section of this page has a dedicated page in the Architecture series, where the mechanism is described in full detail with worked examples, schemas, and LLM dialogues.

02 Elicitation → Architecture · Discovery 03 Sparse Data → Architecture · Evidence 04 Integration → Architecture · Pipeline 05 The Library → Architecture · Cognitive Primitives 06 The Transformation → Architecture · BPMN Integration 07 Healthcare, In Full → Domain Plug-Ins · Healthcare

Composition · validity

d-separation at the seam

In a composed model there is no separate composition-level independence, only d-separation on the merged graph. Composition is valid exactly when the shared interface d-separates the modules, so the bridge variable’s graphical role decides whether stitching is sound.

Composing rewrites the independencies

Wiring a bridge variable is a graph operation, so you re-read d-separation on the union, not on either module alone. An independence that held inside one SCM can fail once a new path runs through the bridge, the parts don’t tell you the whole.

The bridge variable’s role decides everything

Identifiability is itself a d-separation question

… on the merged graph, back-door or front-door over the union. Composition can help (a mediator that front-door-identifies an effect confounded within one module) or hurt (an opened collider), so the composed estimand must be checked on the union, never assumed from the parts.

A forward hand-off, only when licensed

One-way message passing, A’s posterior as B’s evidence, is correct only when the interface d-separates A from B after conditioning. Otherwise you owe full joint inference over the merged graph. And the interface must carry the distribution, not a point estimate. That is why a posterior, not a mean, crosses the seam.

The counterfactual wrinkle

Abduction conditions on factual evidence, which can d-connect variables through the exogenous U-terms. A U shared or correlated across two modules is itself an unblocked cross-seam path, so jointly abducting two sub-models is exactly where a “clean” interface can quietly stop d-separating.

Each model records its interface and this d-separation check in its scope and provenance, so a composed query carries its own proof of validity.

Healthcare · LLM mediation

Worked example: sepsis, LLM-mediated

The same separation runs end to end: the causal model estimates, an explicit rule layer decides, and the LLM only translates. None of the three does another’s job.

Clinician speaks → LLM extracts evidence
causal model estimates state
rule layer applies policy
LLM explains in plain language → Clinician

The model estimates, it does not prescribe

Give the model evidence, fever, high WBC, low blood pressure, mild organ dysfunction, and it returns calibrated posteriors: P(sepsis), P(bacterial infection), P(septic shock). It stops there. A causal model estimates uncertain state; it does not recommend treatment.

Policy is a separate layer

Thresholds on those posteriors, “if P(sepsis) is high and organ dysfunction is present, start the protocol”, turn probabilities into actions. That policy can live as utility nodes in a decision network, as deterministic nodes inside the model, or as an external rule layer reading the model’s output. The third is usually right. Clinical guidelines change far faster than disease models. Keeping them apart lets a clinician revise policy without touching the model. Every recommendation stays auditable back to a posterior and a rule.

The LLM sits at the ends, not the middle

It converts speech into structured evidence and posteriors into explanation. It never weighs the evidence and never picks the treatment, that is the model’s job and the policy layer’s job.

Illustrative architecture. The variables, probabilities, and thresholds are synthetic and are not clinical guidance.