← Back to portfolio
Build Notes

Why This Claims Pipeline Reaches for Regex Before an LLM

Inside Claims Intake Note Structuring: a hybrid workflow that turns messy insurance claim notes into reviewer-ready records — and only calls a local LLM for the notes that genuinely need one.

Jul 2026 ~5 min read View the repo →

Insurance ops teams spend a lot of first-pass review time doing something that isn't really analysis — it's transcription. A claim comes in as a free-text note, and a human has to pull out the policy number, the date, the amount, figure out which queue it belongs in, and flag anything missing before real review can even start. That's the gap this tool closes: it converts unstructured claim intake notes into structured, reviewer-ready output automatically.

The obvious approach is "throw an LLM at it." I didn't do that, and the reason why is the actual design decision worth writing about.

Stack: Python · FastAPI · Pydantic · Ollama (gemma3:1b) — CLI, REST API, and notebook interfaces over the same core engine

Three paths, and the LLM is the last one

Every incoming note runs through the same routing logic, in order:

  1. Sample lookup — checks against 13 ground-truth reference records first. Exact match, no ambiguity, no LLM involved.
  2. Dataset lookup + regex — for the other 72 synthetic records, a regex layer pulls policy IDs, claimant references, dates, cities, and amounts directly out of the text, then a rule-based summarizer assembles a grounded 1–3 sentence summary from what was actually extracted.
  3. Dynamic mode — only for genuinely new, unseen notes does the pipeline call Ollama (gemma3:1b) for open-ended summarization, with the same regex extraction running underneath as a fallback if the model is unavailable.

The result: all 85 records in the synthetic dataset process end-to-end through regex and lookup alone — the LLM is only in the loop for notes that don't match anything the system has already seen. That's not a cost-cutting afterthought, it's the actual architecture. Reach for the deterministic, free, instant tool first; escalate to the slower, non-deterministic one only when the cheap path genuinely can't answer.

The rule I kept coming back to: don't invent values

The output schema has a field called missing_information_flags, and it exists because the alternative — quietly filling in a plausible-looking policy number or date when the note doesn't actually contain one — is worse than admitting the gap. If a field can't be extracted or confidently generated, it gets flagged as missing instead of guessed. The same discipline shows up in the LLM path: Ollama's structured output gets validated against a constrained JSON schema, so a malformed or half-formed response can't silently pass through as a clean record.

Every output also carries a source_grounding_note — a field whose entire job is telling the reviewer which parts of the answer were extracted directly from the text versus generated by the model. It's a small field, but it's the difference between a tool a reviewer can actually trust and one they have to double-check line by line anyway.

Verification

I built a verification script that replays all 13 reference records through the full pipeline and diffs the output against known-correct results:

Comparing 13 reference records… SUCCESS! All 13 reference records match sample_processed_outputs.csv perfectly.
The offline fallback is the part I'm most glad I built: because paths 1 and 2 cover 85 of 85 synthetic records without ever touching Ollama, the entire demo — CLI, API, notebook — still works end-to-end even with the local LLM not running. A reviewer can validate the whole system before deciding whether the LLM dependency is even worth standing up.

What it deliberately doesn't do

The README is explicit about scope, and I think the boundary is as important as the feature list. This is a first-pass structuring aid only. It does not:

It hands a reviewer a clean, structured, honestly-flagged starting point instead of a wall of free text — and stops exactly there.

View the full repo → ← Back to portfolio