AIAutoestimate
AI Vehicle Damage Estimation Platform
A collision estimating platform that reads repair photos, decodes the VIN, and builds a priced line-item estimate with grounded OEM part pricing an estimator can defend.
- Industry
- Automotive / Collision Repair
Collision estimating is slow, inconsistent between estimators, and hard to defend line by line. Shops were writing estimates from photos by hand, looking up part prices across supplier sites one tab at a time, and rebuilding the same labor assumptions on every job. The parts and operations that carry most of an estimate's value are the ones no photo shows.
- Estimates taking hours of an estimator's day, most of it spent looking up part prices
- Hidden parts, brackets and companion operations missed because no photo shows them
- Part prices sourced inconsistently, with no record of where a figure came from
- Labor rates, tax handling and operation codes applied differently by different estimators
- No reusable record of past jobs, so every similar vehicle started from nothing
BrainGenz built AIAutoestimate as a full estimating pipeline rather than a damage detector. Photos are read for damage and text, the VIN is decoded, detected parts are expanded into the operations a human estimator would add, and every replaced part is priced through a grounded web search that keeps its sources. The output is a line-item estimate the shop can edit, defend and export as a customer-facing PDF.
- Built a photo-to-estimate pipeline on FastAPI: OCR and VIN decode, damage detection, expansion, then pricing
- Split estimate expansion into a deterministic code layer and a single cheap model call, divided by how certain the rule is
- Added a coherence audit that rejects detections on the wrong side of a one-sided impact before they reach pricing
- Priced every replaced part through confirmed-invoice data first and a grounded supplier search second, with sources retained
- Moved operations, labor types, positions and damage types into master tables so the vocabulary has one source of truth
- Instrumented per-step token and search cost against a dated rate card, and logged every run to a persistent audit trail
How it was built
The obvious build is a vision model over damage photos returning a parts list. That version works, and it is not an estimate. Four independent reviews of one real shop estimate against its own photo set agreed that roughly 76% of the estimate's value came from parts and operations nobody could see: reinforcements behind a bumper, brackets, harnesses, the alignment a shop performs after a suspension hit.
A photo shows a torn fender. An estimator knows what a torn fender took with it. That gap, not detection accuracy, is what separates a demo from something a shop will put its name on. So detection became the first step in the pipeline and the smallest one.
Some operations follow with certainty from what was detected. Replace a headlamp and you have touched the harness. Those are computed in code, with no model involved, and they appear exactly once on every run. They cost nothing and they never vary.
The rest needs vehicle-specific knowledge that a rules table cannot hold: which hidden members a damaged assembly carries on this particular year and model, and which Repair calls should escalate to Replace. That is one cheap text-only model call with no images attached. Everything it produces is capped, deduplicated, and visibly flagged as inferred, so the estimator accepts or rejects each line instead of discovering it inside a total.
We left companion operations to model discretion first, and it did not hold. Balance, alignment and diagnostic scans appeared and vanished between runs on the same photos, and hidden members never appeared at all. Splitting the work by certainty fixed both failures at once.
An early run put a right-hand hood and fender on a left-side sideswipe, on a job where 15 of the 18 human estimate lines were left-hand. Detection was not wrong about the pixels. It simply has no notion that a car gets hit once, and that one impact traces one physical path across the vehicle.
So a post-filter counts how many detections are unambiguously left or right. When a scene is decisively one-sided, low-confidence detections on the minority side are dropped, and the ones that survive are demoted and flagged for the estimator to verify rather than deleted silently. Items belonging to neither side, like alignment or a roof panel, are never touched.
It runs as pure code with no additional model call, and it is idempotent, so a response that passes through it twice comes out unchanged the second time. A genuine two-impact vehicle can switch it off. Its thresholds sit in named constants at the top of the file rather than inline, because they are tuning parameters and someone will want to move them.
Detection answers which parts. Pricing answers what they cost, and the two cannot share a call. Detection is one pass over a photo set. Pricing is a slow, per-item grounded web search against supplier pages, and its cost scales with the number of parts rather than the number of photos.
Prices resolve from three sources in order: parts already confirmed against real invoices, then a grounded search, then an explicit low-confidence zero. That last one matters. A wrong price the estimator trusts is worse than a blank one, so the system is built to say it does not know rather than to produce a plausible number.
The pricing prompt asks the model to echo each part name back and preserve the input order. Models do not reliably honor that instruction. They reorder rows, merge two parts into one, drop rows, and truncate the response when they hit a token ceiling.
Merging by position, which is the natural implementation, then lands one part's price on a different part's line. Nothing throws. No log entry appears. The estimate is quietly wrong in a way that looks entirely fine on screen, which is the worst failure mode available to a pricing system.
Results are matched back to their items by normalized part identity, with position used only for rows the model returned without a resolvable name. Truncated responses are scanned with a brace matcher that recovers every complete row, so one cut-off row at the end does not discard the fourteen good ones above it.
We assumed a grounded search took about 25 seconds. It takes 48 to 90, and an obscure part takes longer than that. One wrong number set the first concurrency limit far too low: nineteen items queued behind five slots and the run took nearly five minutes.
Concurrency was raised twice against real jobs, watching for rate limiting instead of guessing at headroom. Per-item timeouts were loosened after we found searches being cut off mid-answer and shipped to the estimator as no price found. The model had not failed. We had stopped it. Inferred lines are now priced lazily, so a run does not pay for a search on a line the estimator is likely to reject.
Grounded search billing has two rules that are easy to get wrong, and getting both wrong understates a run by roughly five times. Thinking tokens bill at the output rate, and on a grounded search the thinking runs several times longer than the visible answer. Search grounding bills per query rather than per request, and one request can issue several queries.
Rates now live in one table with the source and the date recorded against each entry, so per-step cost per estimate is a number the team can answer rather than approximate. The same exercise ruled out a routing option we had been using: an aggregator that runs the search separately and injects the full text of the hits into the prompt as input tokens, which on a flagship model meant hundreds of thousands of input tokens and around two dollars per pricing run. A provider's own search tool returns structured results for a fraction of that.
A pricing failure reaches the user as a banner and reaches the engineer as nothing, because container logs are gone by the next deploy. Structured events now append to an in-memory queue synchronously, flush to the database in batches, and expire after two weeks. No logging path is permitted to raise into the request that produced it, and if the database is unreachable the whole trail degrades to a no-op.
This is the least interesting component in the system, and it changed how fast anyone could answer why a particular estimate came out the way it did.
The expansion layer. Deterministic operations cover the certain cases and a model call covers the vehicle-specific ones, but the boundary between them is drawn by hand and it has moved every time we learned something. Every rule promoted out of the model call and into code makes a run cheaper, faster and more repeatable at the same time. Front-loading that migration is the change we would make given the project again.
- Estimators spending hours per job, most of it on manual part price lookups
- Hidden parts and companion operations missed or remembered inconsistently
- Identical photo sets producing different line items between runs
- Part prices with no recorded source, and no way to reconstruct a past estimate
- One pass over the photo set produces a priced, editable line-item estimate
- Operations that follow with certainty are generated in code and appear on every run
- Derived lines arrive flagged as inferred, so the estimator confirms rather than discovers them
- Every priced line retains its source, and every run is reconstructable from a persistent audit trail
- Per-estimate AI cost is measured per step against a dated rate card rather than estimated
