Model status: The released ONNX checkpoint runs on this Space's CPU. All records are fictional. The output is a review proposal—not an approval or journal entry.

Training Specialized HRMs, Explained Like You Are Five

Think of an HRM as a child learning to solve little boxes of accounting puzzles.

Each training example is one box containing:

  1. The puzzle pieces.
  2. The rules for which pieces may fit together.
  3. The correct answer.
  4. Any special reason the puzzle needs adult review.

The HRM studies thousands of solved boxes and learns how to solve new ones.

The basic dataset shape

Do not think of the dataset as one enormous spreadsheet where every row is unrelated. It should be a collection of complete cases:

Case 1
├── Company and accounting period
├── Input records
├── Possible relationships
└── Correct answer

Case 2
├── Company and accounting period
├── Input records
├── Possible relationships
└── Correct answer

In our bank-reconciliation model, one case looks like:

Input:
  20 bank transactions
  19 general-ledger entries

Correct answer:
  Bank row 1 → GL row 8
  Bank row 2 → GL row 3
  Bank row 3 → unmatched bank fee
  ...

The entire group is one example because the model must reason globally: two bank transactions cannot both consume the same GL entry.

Example: accounts-payable matching

Suppose we want an HRM that performs invoice-to-purchase-order matching.

A human-readable training example could look like this:

{
  "case_id": "AP-2025-0042",
  "as_of_date": "2025-10-31",
  "context": {
    "company": "COMPANY_A",
    "currency": "USD"
  },
  "inputs": {
    "invoice": {
      "invoice_id": "INV-104",
      "vendor": "VENDOR_17",
      "invoice_date": "2025-10-14",
      "total_cents": 125000,
      "description": "Office chairs"
    },
    "purchase_orders": [
      {
        "po_id": "PO-88",
        "vendor": "VENDOR_17",
        "total_cents": 125000,
        "description": "20 office chairs"
      },
      {
        "po_id": "PO-91",
        "vendor": "VENDOR_17",
        "total_cents": 140000,
        "description": "Office desks"
      }
    ],
    "receipts": [
      {
        "receipt_id": "REC-31",
        "po_id": "PO-88",
        "quantity_received": 20
      }
    ]
  },
  "answer": {
    "purchase_order": "PO-88",
    "receipts": ["REC-31"],
    "disposition": "MATCH",
    "exception": "NONE"
  }
}

That example teaches the model:

Invoice INV-104 belongs to PO-88 and receipt REC-31.

An exception example might instead say:

{
  "answer": {
    "purchase_order": "PO-88",
    "receipts": ["REC-31"],
    "disposition": "REVIEW",
    "exception": "PRICE_VARIANCE"
  }
}

Now it learns both matching and exception handling.

What the model actually sees

The data adapter turns the readable records into arrays:

Invoice features:
  amount, date, vendor, currency, description

PO features:
  amount, date, vendor, currency, description

Candidate mask:
  PO-88 is allowed
  PO-91 is allowed
  unrelated-company POs are forbidden

Answer:
  correct candidate index = PO-88
  exception class = NONE

In tensor terms:

input rows       [cases, maximum rows, features]
candidate mask   [cases, invoice rows, PO rows]
match targets    [cases, invoice rows]
exception target [cases, invoice rows]
padding masks    [cases, maximum rows]

Padding lets one case contain 10 records and another contain 20 while still fitting into fixed-size batches.

Examples for other CFO specialists

Specialist Puzzle pieces Correct answer
AP matching Invoices, PO lines, receipts Which records match and why review is needed
Intercompany Due-to and due-from entries Correct counterparty pairs and elimination group
Journal review Journal lines, accounts, policy context Accept, review, or reject; violated control
Expense audit Expense, receipt, employee, policy Valid, duplicate, missing receipt, policy violation
Close management Tasks, dependencies, status, owners Next task, correct order, blocker category
Revenue recognition Contract, invoice, delivery, obligations Allocation and recognition schedule
Variance analysis Actuals, budget, operational drivers Driver classification and materiality
Cash forecasting Historical and scheduled cash flows Future cash balances and uncertainty bands

HRMs are especially attractive when there are several related records, multi-step reasoning, global constraints, and an answer you can objectively check.

For ordinary forecasting, traditional time-series and regression baselines may still be better. An HRM should have to beat those baselines.

How to create a new specialist

1. Define the puzzle

Write down exactly:

What information is available before the decision?
What answer should the model produce?
What rules must never be violated?
When should it abstain and ask a human?

If you cannot define the correct answer precisely, you cannot train the model reliably.

2. Find trustworthy answers

Good labels come from:

  • approved reconciliations;
  • finalized invoice matches;
  • controller-reviewed journal decisions;
  • completed close workflows;
  • resolved exceptions; or
  • carefully generated synthetic cases.

Avoid using unreviewed historical decisions as truth. Historical accounting records can contain mistakes.

3. Separate inputs from answers

Never accidentally show the model the answer inside its input.

For example, do not give it:

{
  "matched_po_id": "PO-88"
}

as an input when PO-88 is the answer.

Other dangerous leakage fields include:

  • final reconciliation status;
  • approval timestamp;
  • payment created from the matched invoice;
  • exception resolution code;
  • downstream journal-entry ID; and
  • fields populated only after review.

4. Include difficult and rare cases

A useful dataset needs more than ordinary matches:

Exact matches
Small amount differences
Date differences
Missing references
Duplicate invoices
Partial receipts
Split payments
Aggregated payments
Currency differences
Reversals
Wrong entities
Unmatched records
Policy exceptions
Ambiguous cases requiring review

If the model never sees duplicate invoices during training, it will not magically understand duplicate invoices later.

5. Split by time

A safe split looks like:

Train:      January 2023 – June 2025
Validation: July 2025 – September 2025
Test:       October 2025 – December 2025

Do not randomly put almost-identical records from the same transaction into both training and testing. That lets the model memorize instead of reason.

For an even harder test, hold out an entire company, vendor group, or accounting system.

6. Build an adapter and output head

You usually cannot point our current model at a new CSV and expect it to work.

The existing model has outputs specifically designed for:

  • bank-to-GL pair scores;
  • unmatched scores; and
  • six reconciliation exception classes.

For AP matching, we could reuse much of the architecture and replace the row types and exception classes.

For close planning, we would need a different output head that predicts next actions or task order.

For forecasting, we would need a numerical prediction head and a different loss function.

A practical folder format

A clean specialist dataset could use:

ap-matching/
├── schema.json
├── train.jsonl
├── validation.jsonl
├── test.jsonl
└── manifest.json

Each line in train.jsonl is one complete case, not one invoice row.

The manifest records:

{
  "dataset_version": "1.0",
  "created_at": "2026-07-23",
  "source_system": "anonymized-read-only-export",
  "train_period": ["2023-01", "2025-06"],
  "validation_period": ["2025-07", "2025-09"],
  "test_period": ["2025-10", "2025-12"],
  "currencies": ["USD"],
  "label_method": "controller-approved final disposition"
}

The simplest rule is:

One example should contain everything needed to solve one complete accounting puzzle, followed by an answer key created from a trustworthy outcome.

For multiple specialists, give each HRM its own dataset and answer type. Then connect their proposals through the typed Go control layer instead of mixing AP, forecasting, reconciliation, and close-management records into one undifferentiated training file.

·