Automated GL coding & invoice routing (Beta)
Task: given an invoice's text (sender, product, description) and its company, predict three things a finance team would otherwise fill in by hand:
- Processor โ which employee should handle it (~63 candidates per company)
- Acceptor โ who approves it (~10 candidates per company)
- GL code โ the accounting category (32 codes)
Processor and acceptor are company-scoped cross-table link predictions (invoice โ employee), the kind of high-cardinality target that plain bag-of-features models handle poorly. Aito predicts each independently from the invoice text and company alone โ the targets are never inputs.
Methods. Aito is measured with both engines โ v1 (rep1 / TableDb,
/api/v1) and v2 (rep2 / CollectionDb, /api/v2) โ from the
InvoiceRoutingEvaluation booktest. Baselines are a tuned scikit-learn Random
Forest, LightGBM (with a company-local class-index encoding) and FLAML
AutoML, on the identical 200-row held-out test set; reproduction scripts and
method notes live in tasks/generated-invoice-benchmark/.
Top-1 accuracy (10k training rows, 200-row test)
| Target | RF | LightGBM | Aito v1 | Aito v2 |
|---|---|---|---|---|
| GL code (32 classes) | 60.0% | 46.5% | 84.5% | 83.0% |
| Processor (high-cardinality link) | 6.5% | 0.0% | 11.0% | 9.0% |
| Acceptor (link) | 29.0% | 48.0% | 32.0% | 24.5% |
Read honestly:
- GL code โ Aito wins decisively on both engines (~83โ85% vs 60% / 47%). The field-level priors carry the low-cardinality target well.
- Processor โ the hard cross-table target, where the tree models nearly collapse (LightGBM's local-index encoding drops to 0% at this scale). Aito leads on both engines, modestly (11% / 9%).
- Acceptor โ the one target Aito loses: LightGBM's local index works when there are only ~10 candidates, reaching 48% vs Aito's 32% / 24.5%. Honest benchmarks include the cases you lose.
v1 vs v2: top-1 is close, but v2 ranks far better
Top-1 accuracy understates the v2 engine. On the link-heavy targets, v2's cross-table link priors rank the true candidate dramatically higher than v1, even where top-1 is a touch lower:
| Target | v1 mean rank | v2 mean rank |
|---|---|---|
| Acceptor | 23.9 | 3.7 |
| Processor | 125.2 | 34.2 |
| GL code | 2.0 | 2.6 |
For acceptor, v2 puts the correct employee in the top ~4 on average (vs ~24 for v1); for processor, top ~34 out of ~63 (vs ~125). The link/property signal is clearly there โ the remaining gap is the final top-1 calibration/tie-break, which is exactly where rep2 is still being tuned (v2 is in public beta). So on these targets v2 is better positioned than the top-1 number suggests, and closing the calibration gap is the active work.
The number that isn't accuracy: training time
| Method | Time to absorb new data (update the model) |
|---|---|
| Aito (v1 or v2) | 0 s โ incremental; new rows are queryable immediately |
| Random Forest | ~100 s (full refit) |
| LightGBM | ~40 min (full refit) |
| FLAML AutoML | budgeted โ default 1,200 s per target |
This is the part a top-1 table hides. Every baseline needs a retraining
pipeline to absorb new invoices; Aito absorbs them on insert, with a $why
explanation behind every prediction. On a workload where routings change weekly,
"0 s and explainable" competes hard with "a few points more, but retrain nightly
and serve a separate model."
How to read this
- Aito wins on GL code and on the hard cross-table processor target, and is a couple of points off on acceptor โ with no training and full explanations.
- v2 vs v1: top-1 is close; v2 ranks the true candidate much higher (the link priors are strong), and its remaining top-1 gap is a known, active calibration item.
- Statistics: n=200 test set (95% CI โ ยฑ7pp at 50%), single synthetic-data seed. Read the direction โ which method handles which target โ as the durable signal; treat individual cells as point estimates.
Generated. Aito's figures are interpolated at docs-build time from the
InvoiceRoutingEvaluation compare-* booktest snapshots (via a metrics.json
the docs generator produces), so they update whenever the evaluation is
re-baselined and can't silently drift. RF / LightGBM / FLAML are from the
published Python baselines. Multi-scale numbers (1k / 100k) are a follow-up.