-
-
Verdikt — my entry for the Nebius x NVIDIA Global AI Hackathon 2026. AI reads. Rules decide. Templates ship.
-
The problem: thousands of PAP applications, and every response must be a compliance-audited letter. Days become weeks.
-
Every channel, one pipeline — fax, mail, web, email, REST and MCP flow through five stages: Ingest, Triage, Decide, Deliver, Remember.
-
Everything a case begins with: scanned enrollment form plus insurance card, EOB and prescription attachments.
-
Full case timeline — every AI, human and system step is hash-chained and replayable, from intake received to delivered reply.
-
Programs — each program owns the extraction schema the pipeline uses at intake; bind an enrollment template to seed the slots.
-
Per-program models — pipeline code stays fixed; each program picks its Nebius Token Factory models (Nemotron tiers) per stage
-
Templates library — design, freeze and version DOCX, AcroForm and flat PDF templates for deterministic rendering across tenants.
-
Rules library — the tenant's master eligibility rules; edit once and every attached program picks up the change on the next case.
-
Static documents — byte-stable PDF library (appeals notices, guides, forms) concatenated as-is into outbound doc packages.
-
Doc packages — ordered compositions of templates and static docs, bound per outcome and resolved by the assembler at runtime.
-
Tenants & keys — each tenant is an isolation boundary scoping templates, renders and API keys.
-
HumanLoop queue — inbox events wait for a human decision; no reply email goes out until a reviewer approves.
-
Extract stage — program slots populated from inbound attachments; blanks become the raw signal for the missing-info verdict.
-
Rules evaluated — every verdict shows its why: the exact conditions, matched values and citations behind each decision.
Inspiration
Patient-assistance programs give free medication to people who can't afford it. Applications arrive through a few channels: faxes land in an email inbox as PDFs through fax-to-email tools like RightFax, physical mail is scanned by the ops team and hits the same inbox as PDFs, and web submissions come in as JSON. Each one then crawls through siloed systems and human agents, checked against a stack of rules — a slow, manual process.
The usual AI fix, asking a language model for the answer, trades one problem for another. The answer can change between runs, and nobody can audit it. I wanted AI where it's strong, which is reading messy paperwork, and I wanted the decision itself to stay fixed, explainable and repeatable.
What it does
Verdikt is an "AI in, deterministic out" decision pipeline. A scanned enrollment bundle goes in. A cited decision and a reply letter come out, and a human approves it before anything is sent.
- Ingest: a vision model reads the scanned pages into structured fields.
- Triage: NVIDIA Nemotron Nano classifies the case and routes it.
- Decide: retrieval finds the relevant program rules. Each rule is a structured formula, and the verdict comes from evaluating those formulas, not from a model's opinion. Where a rule has no formula, Nemotron Super reasons about it, and the case is flagged for a closer look.
- Deliver: the reply letter is merged from a frozen, human-approved template. No model is involved in producing it.
- Remember: every step is written to a hash-chained, tamper-evident audit trail. A reviewer can open any case and replay the full timeline: the extracted fields, the rules that fired, the citations and the letter.
The impact: an enrollment bundle that today crawls through siloed systems and multiple human hand-offs becomes one cited, human-approved decision in a single run. That's not a hypothetical workflow — it maps directly onto the intake operations I've seen inside specialty-pharma hubs, which is Verdikt's commercial path.
How I built it
Models (all on Nebius Token Factory, one endpoint and one API key)
- Ingest: MiniCPM-V 4.5 reads every page of the packet into the program's fields. NVIDIA Nemotron-3.5-Super-VL can read the same pages as a second, independent reader, and any field the two disagree on goes to a human reviewer.
- Triage: NVIDIA Nemotron Nano classifies each case (new intake, refill, appeal, missing info) and routes it. A rule-based classifier takes over if the model is unavailable.
- Retrieve: Qwen3-Embedding-8B finds the program's human-authored rules that apply to the case.
- Rules: NVIDIA Nemotron Super turns each plain-language rule into a structured formula when the rule is written, and the author reviews it before saving. At run time, code evaluates those formulas, and Nemotron Super reasons only over rules that don't have one yet. When every rule a case needs has a formula, no model makes the decision, and the audit trail records it.
- Form setup: Nemotron Super labels the fields of a new PDF form template, with MiniCPM-V reading pages that are scanned images.
- Deliver: no model. The reply letter is merged from a frozen, human-approved template and held until a reviewer approves it.
The model writes the logic once, under human review. Code applies it to every case.
- The app is Python (FastAPI, Postgres, MinIO, Redis) in Docker Compose.
- The demo program is the fictional CardioVita assistance program at Alderwood Specialty Pharmacy: a 105-field form and 9 eligibility rules.
- A reviewer queue shows every case stage by stage, with the original PDF beside the extracted fields.
- Verdikt grew out of an existing document-generation platform that I significantly extended during the submission period. The decision pipeline, review queue, rule formulas and demo seed were built in that period.
Challenges I ran into
Scans lie. The model once read the drug name as "Cardiovalta", and a strict text match then denied a valid application. I added a
similar_torule operator that allows a bounded number of character edits. It's still deterministic and still shown in the case trace:edit_distance(extracted, expected) ≤ 2.Plausible nonsense. The extractor sometimes copied the date of birth into the signature date, and took the prescriber's name from the facility box. I added a section-aware merge, where the page with the most fields in a section owns that section, and it rejects signature dates that can't be real.
Same file, different answers. Early runs flipped between approve and deny. After fixing the causes above, the same scan approved five out of five times on my final deployment. That's a small sample, not proof.
Accomplishments that I'm proud of
- The verdict is reproducible and cited. Every approve or deny points to the exact rules that fired and the values they saw.
- The audit trail is hash-chained and replayable: every AI, human and system step links to the one before it, so a case history can be audited end to end but not quietly rewritten.
- It works on real scans: a 105-field scanned bundle goes from upload to a reviewable decision and reply letter in a single run.
- The letter step uses no model at all, so what a human approved is exactly what is sent.
- A test suite of 921 passing tests, plus a one-command demo seed that rebuilds the whole Alderwood tenant on an empty install. The full architecture and engineering write-up behind the build is at verdikt.aismith.ai/engineering.
What I learned
Use the model only where the input is genuinely messy, and put a rule in front of every decision that matters. Making the verdict reproducible made the AI parts easier to trust. When something went wrong, I could see exactly which field was misread and fix it in one place.
What's next for Verdikt
More programs per tenant, a second read of critical fields, and reviewer corrections that feed back into better extraction. Beyond PAP, the goal is multi-tenant decision infrastructure for intake pipelines generally — anywhere AI reads the documents but the decision has to stay deterministic, cited and human-reviewed.
Built With
- docker
- docker-compose
- fastapi
- javascript
- minicpm-v
- minio
- nebius
- nebius-token-factory
- nemotron-3.5-super-vl
- nemotron-nano
- nemotron-super
- nvidia-nemotron
- postgresql
- python
- qwen3-embedding
- redis

Log in or sign up for Devpost to join the conversation.