2. Cut sets, importance and the FMEA

The structural result is available before any measurement exists, and it is often the more actionable half. This tutorial reads it.

from hiphopsllm import AgenticReliabilityStudy, load_example

study = AgenticReliabilityStudy(load_example("react_calculator"),
                                name="ReAct + calculator")
study.analyse()

Cut sets

A minimal cut set is a smallest combination of basic events sufficient to produce the hazard. Its order is how many events it takes.

for cs in sorted(study.cut_sets("H2"), key=lambda c: (len(c), c)):
    print(len(cs), " + ".join(cs))
1 BE-__start__-BADREQ
1 BE-coder-WRONGEXPR
1 BE-generator-HALLUC
1 BE-generator-NONDET

Order 1 everywhere. Each of those alone delivers a wrong answer the user accepts. That is the honest description of a single-agent-plus-tool architecture, and no amount of prompt engineering changes the structure.

Compare the parallel architecture:

parallel = AgenticReliabilityStudy(load_example("parallel_aggregator"))
parallel.analyse()
sorted(len(c) for c in parallel.cut_sets("H2"))
# [1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2]

Eight of eleven are order 2 — the vote is real. Three are not, and they are the interesting three:

BE-__start__-BADREQ                        an ill-posed request
BE-aggregator-OWN                          the judge hallucinated its own answer
CCF-LLM-Qwen-Qwen2-5-Math-1-5B-Instruct    both agents are the same snapshot

The last one is why the redundancy is architectural only. Two agents running one model do not fail independently, and the vote buys nothing against a fault they share.

Single points of failure

import pandas as pd
pd.DataFrame(study.single_points())

Every order-1 cut set, across all hazards, ranked by severity. This is the fix list.

Importance

Two rankings, answering two different questions.

analysis = study.report.analysis("H2")
pd.DataFrame([
    {"event": r.event_id, "P": r.probability,
     "Fussell-Vesely": round(r.fussell_vesely, 4),
     "Birnbaum": round(r.birnbaum, 4), "SPOF": r.single_point}
    for r in analysis.importance
])
                 event     P  Fussell-Vesely  Birnbaum  SPOF
0  BE-generator-HALLUC  0.15          0.3947    0.7841  True
1  BE-generator-NONDET  0.12          0.3158    0.7573  True
2   BE-coder-WRONGEXPR  0.10          0.2632    0.7405  True
3  BE-__start__-BADREQ  0.01          0.0263    0.6732  True

Fussell–Vesely is the fraction of the top-event probability this event contributes. Rank by it to decide what to fix first — here, hallucination in the generator, which accounts for 39% of the risk.

Birnbaum is the sensitivity: how much the top event moves per unit change in this event. Rank by it to decide where measurement is most worth buying. Note that BE-__start__-BADREQ has a Fussell–Vesely of 0.026 but a Birnbaum of 0.67 — it barely contributes at its current probability, but if that probability were wrong, the answer would move a lot. That is exactly the event whose placeholder you should replace with a measurement first.

The FMEA

study.fmea().head()
   component                event            failure mode          class     P  … severity      mitigation
0      coder      BE-coder-UNSAFE  Model-authored code …    Commission  0.02  … catastrophic  Restricted evaluator (AST …
1  generator  BE-generator-HALLUC  Plausible but incorr…  Value (subtle) 0.15  … critical      Sample-based semantic unc…
2  generator  BE-generator-NONDET  Sampling non-determi…  Value (subtle) 0.12  … critical      Pin seed and temperature …
3      coder   BE-coder-WRONGEXPR  Executes correctly b…  Value (subtle) 0.10  … critical      Cross-check with an indep…
4  __start__  BE-__start__-BADREQ  Ill-posed or ambiguo…  Value (subtle) 0.01  … critical      Input validation and clar…

It is derived from the trees, not written separately, so it cannot disagree with them. Each row carries the direct effect (which hazard this event reaches), the further effects, the worst severity it can cause, and a mitigation appropriate to its class — which is where keeping VC and VS apart pays off. A schema validator is a complete answer to VC and no answer at all to VS.

What to do with it

Three moves, in the order they usually pay:

Break the order-1 cut sets. BE-coder-UNSAFE is catastrophic and has a one-line fix (a restricted AST evaluator instead of eval). Fix that before tuning anything.

Make the redundancy real. If two agents share a snapshot, the common-cause event dominates. Change one of them to a genuinely different model and re-run — the CCF-LLM-… cut set disappears:

study = AgenticReliabilityStudy(
    load_example("parallel_aggregator"),
    resource_overrides={"cot_agent": {"llm": "a-different-model", "runtime": "cuda:1"}},
)
study.analyse()
[c for c in study.cut_sets("H2") if len(c) == 1]
# the CCF cut set is gone

Then measure. Structure tells you where redundancy is missing; only measurement tells you whether what remains is good enough. Tutorial 3 does that.

Exports for other tools

study.save("artifacts/")

Writes per-hazard mermaid, DOT, JSON and Open-PSA MEF XML (readable by XFTA and SCRAM), plus *_cutsets.csv and *_fmea.csv, and the Markdown report that carries the loop handling and common-cause notes.