Where we are: the journey so far
not a chatbot
code decides
Foundry agents
guarded tools
you are here
In most software, tests and guardrails protect the product. Here they largely are the product. What a firm is buying is the confidence that an automated report can be signed off on. Three layers deliver it.
Layer 1: test discipline
The suite is 300+ tests and runs entirely offline by default: fake HTTP transports and canned model chats, so every guard, merge rule, and failure path is exercised without Azure. Live smoke tests against the real public services (Soil Data Access, wetlands, Planetary Computer) are a deliberate opt-in flag.
# default: everything offline, no cloud, no credentials
pytest
# opt in to live smoke tests against the public services
pytest -m live --run-live
The multiplicity guards from Part 2 are unit-tested against the hard cases: a real four-parcel assemblage and a synthetic two-jurisdiction divergence. Because the suite runs on fake transports and canned chats, the full 300+ tests validated every behavior while the Azure environment was completely torn down. That independence is what made tearing it down, and someday redeploying it, a non-event.
| What is tested | How | Needs Azure? |
|---|---|---|
| Boundary guards, merge rules, divergence logic | Unit tests over the Pydantic models | NO |
| Every tool's failure path (timeouts, empty results, bad input) | Fake HTTP transports | NO |
| Extraction + summary grounding gates | Canned model chats, including deliberately bad ones | NO |
| Public service contracts still hold | Live smoke tests | OPT-IN |
Layer 2: runtime gates
The invariant checkpoints run in production on every request, and they hard-fail rather than degrade. If consolidation cannot prove that no parcel was dropped and every value is cited or explained, the pipeline halts, emits the trace, and surfaces the failure to the engineer. A report that cannot pass its own audit never ships.
The model-facing stages get the same treatment:
- Extraction output is grounding-checked against the chunks it cites.
- The QA score is computed in code from the issue list, rather than asked of the model. A model grading its own homework is not a gate.
- High-severity revisions must re-pass the grounding gate. A fix to the summary cannot introduce a new fact on its way out.
Layer 3: production evaluation
A custom Foundry evaluator re-runs the invariant assertions over sampled live traffic, so drift or a regression shows up as an Azure Monitor alert linked to the exact trace, not as a bad report discovered by a client. Because provenance and the trace are the same record (Part 3), the evaluator has everything it needs in the spans: site ID, parcel IDs, each value's source endpoint.
Docs, if you want to build the same thing: custom evaluators and continuous evaluation on the agent monitoring dashboard.
The cost of all three layers was real: roughly as much code enforces the report as produces it. That ratio is the honest price of putting an LLM anywhere near a liability document, and budgeting for it up front is cheaper than retrofitting trust later.
What changes for the business
The pipeline turns report production from days of expert data-gathering into a machine run plus a focused review. The economics follow from where the hours go:
- Turnaround. The gathering-and-transcription phase, the bulk of elapsed time on a manual SIR, becomes minutes of pipeline execution. The engineer's time shifts to what clients actually pay for: reviewing divergences, exercising judgment, signing off.
- Consistency. Every report draws from the same sources, applies the same source-authority ranking, and renders through the same template. Quality stops depending on which engineer had bandwidth that week.
- Auditability. Each value links to its authoritative record and each run leaves a complete trace. When a client, lender, or insurer asks "where did this flood zone come from?", the answer is a citation.
- Leverage, not replacement. The terminal state is
draft_for_engineerby design. Senior engineers review more sites instead of transcribing fewer; the disambiguation pause keeps them in control of exactly the decisions that need a human. - Scalability. Marginal cost per report is API calls and model tokens, not engineer-days, which changes what portfolio-scale due diligence (screening dozens of candidate sites, not just confirming one) can look like.
None of this is hypothetical plumbing. The stack was deployed to Azure and validated end to end, a real Durham, NC parcel resolved through disambiguation, enrichment, consolidation, and QA to a rendered, cited DOCX, before being deliberately torn down, reproducible from one Bicep template and a runbook.
Six lessons for agentic document pipelines
1. Write the invariants before the agents. The project's most valuable artifact is the shared Pydantic model plus the boundary guard every hop must pass. Once "no parcel ever disappears, every value is cited or explained" existed as executable code, agent design became easy: any agent that violates the contract fails loudly at the boundary, so you can iterate on prompts and orchestration without fearing silent corruption.
2. Decide what the model is for, narrowly. "LLM for language, code for facts" sounds obvious and is constantly tempting to violate. Letting the model "just pick" the jurisdiction or "just merge" the conflict would have saved code in the moment and destroyed the audit story. Each carve-out for determinism (resolution, merging, divergence, scoring) is a place the system cannot hallucinate, and their sum is the product's credibility.
3. Post-check the model; don't trust the prompt. Every instruction to the model, no new facts, surface divergences, cite chunks, exists twice: once in the prompt, once as a deterministic check on the output. The prompt gets compliance most of the time; the check makes "most of the time" irrelevant.
4. Design the escalation, not just the happy path. The single best-received behavior is the pipeline refusing to guess: pausing on ambiguous parcels and asking one well-formed question with a map. Knowing when to stop is a feature you must design deliberately. It will not emerge from a capable model on its own.
5. Make the tests independent of the cloud. Fake transports and canned chats meant the full suite ran while Azure was torn down. Tearing down, and redeploying, became a non-event.
6. Provenance doubles as observability. Stamping each value's source onto the OpenTelemetry span meant the compliance story and the debugging story became a single investment rather than two.
What's next: the lakehouse roadmap
The report path is done; the roadmap now points at the data around it. And yes, for regular readers, this is where the two series on this blog meet.
- A reference-data lakehouse. At portfolio scale, clipping the big federal datasets (wetlands, soils, building footprints) to the service area on a quarterly schedule and serving them as versioned Delta tables beats fetching per report. The provider protocols are already the seam, so agent code doesn't change. Serving tables would carry dual provenance (upstream source and snapshot vintage) so citations still reach the federal record.
- Portfolio analytics and ad-hoc Q&A. Landing consolidated site objects in the lakehouse opens conversational analysis, "which pipeline sites touch a flood zone?", and dashboards over divergence and evaluator trends.
- One guardrail, kept deliberately. Analytics layers are LLM-mediated and belong beside the engineer, not inside the report. Report values keep flowing through the deterministic providers with per-value provenance, whatever gets built around them.
The broader takeaway
The takeaway travels beyond real estate. Any regulated document, environmental assessments, insurance inspections, compliance filings, has the same shape: facts that must be authoritative and auditable, wrapped in narrative that must be readable. The pattern here, deterministic facts, guarded tools, attach-only state, machine-checked narration, and a human holding the pen at the end, is how you get the speed of automation without gambling the signature on the last page.
I hope this helps someone staring at a "just use an LLM" request for a document that someone has to sign. As usual, send me a note if you need to know more about this. Thank you!