DEV Community

Daksh Srivastava
Daksh Srivastava

Posted on

Company-Brain: A Safety-First Invoice Agent That Runs Fully Offline, on Open Tools, With Zero Per-Invoice Cost

TL;DR

A 40-person logistics company in Pune matches vendor invoices against purchase orders by hand. The data is sensitive (vendor names, negotiated rates, GSTINs), and the real rules are unwritten and sometimes contradictory.

Company-Brain is an autonomous reconciliation agent built around two ideas:

  1. A structured "Company Brain": the business's unwritten rules turned into executable YAML.
  2. A deterministic Guardrails Engine: 11 explicit safety rules enforced in plain Python, with no model in the decision loop.

The result: 13/13 regression cases pass, every decision is logged, and a human signs off on anything that matters. It runs on a single machine with only open-source tools, so there is no per-invoice cost and no data leaves the building.

Repo: github.com/Daksh-create349/Company-Brain


1. The problem

The scenario is a 40-person Pune-based logistics company. For every vendor invoice, someone has to:

  1. Find the matching purchase order (PO)
  2. Check the variance against tolerance rules
  3. Catch duplicates, dormant vendors, out-of-state vendors, and oversized invoices
  4. Apply the right payment terms for that vendor
  5. Decide: reconcile, hold, or escalate to a human

It's repetitive and easy to get wrong, and the rules aren't written down. They live in people's heads, and the people disagree. That makes it a good fit for an agent, as long as the agent is safe.


2. Why open matters here

Invoices contain exactly what a small business doesn't want to upload to someone else's server: who they buy from, at what rates, and on what terms.

Concern Closed, cloud-based approach This project (open tools, runs locally)
Data privacy Invoice and PO data leaves the premises Everything stays on the company's own machine
Cost per run Metered per request, forever None; it's plain Python and CSV files
Pricing risk A provider can change terms or prices No dependency to change
Connectivity Needs internet Works offline
Auditability Behavior can change when a vendor updates a model Rules are YAML files anyone can read and version-control
Customization Limited to what an API exposes Edit a rule, re-run the tests, done

The whole pipeline is open and inspectable, and the only dependency is PyYAML. For an agent that touches financial decisions, that transparency is the feature: an owner can read exactly why an invoice was held, because the logic is in files they can open, not in a model's weights.


3. Architecture

flowchart LR
    subgraph DATA["Data"]
        I[(invoices.csv)]
        P[(purchase_orders.csv)]
    end

    subgraph BRAIN["Company Brain (YAML)"]
        D[operating_map.yaml<br/>decision trees, tolerances, routing]
        E[guardrails.yaml<br/>11 safety rules]
    end

    subgraph AGENT["Deterministic agent (Python)"]
        F[agent.py<br/>orchestrator]
        G[guardrails.py<br/>policy engine]
        H[skills.py<br/>CSV tools]
    end

    subgraph OUT["Audit trail"]
        K[agent_log.jsonl]
        L[reconciliation_status.csv]
        M[reconciliation_report_DATE.csv]
        N[human_signoff.md]
    end

    I --> F
    P --> F
    D --> F
    E --> G
    F <--> G
    F --> H
    H --> K
    H --> L
    F --> M
    F --> N

Repository layout

├── run_batch.py              # entrypoint: processes all pending invoices
├── requirements.txt          # PyYAML
├── brain/
│   ├── operating_map.yaml    # decision trees, tolerances, routing
│   ├── guardrails.yaml       # 11 explicit, unbreakable safety rules
│   └── knowledge_extraction_template.md
├── agent/
│   ├── agent.py              # reconciliation orchestrator
│   ├── guardrails.py         # pure-Python deterministic policy engine
│   └── skills.py             # file-based tools over the company CSVs
├── data/                     # invoices.csv (10 cases), purchase_orders.csv
├── evals/                    # 13-case regression suite and improvement log
└── output/                   # audit trail and human sign-off sheet
Enter fullscreen mode Exit fullscreen mode

4. The Company Brain: turning tribal knowledge into rules

I interviewed the founder (Priya) and the ops lead (Raj). Their answers contradicted each other, which is normal for a small business. The Brain doesn't pick a winner. It encodes both as executable rules in brain/operating_map.yaml, using the template in brain/knowledge_extraction_template.md.

4.1 Two people, two tolerance policies

Person Rule
Priya (official policy) Variance must be within ₹500 or 1% of the PO total, whichever is lower
Raj (what actually happens) Ignore anything under ₹200; it isn't worth calling a vendor

The resolution: Raj's rule applies, except on small POs, where 1% of the PO is already under ₹200. On those, the strict official limit wins.

flowchart TD
    S[Invoice vs PO variance] --> Q{1% of PO total under ₹200?<br/>a small PO}
    Q -- Yes --> O[Enforce strict official limit<br/>Raj's shortcut bypassed]
    Q -- No --> R{Variance under ₹200?}
    R -- Yes --> OK1[Auto-reconcile<br/>Raj's practice]
    R -- No --> P{Within official limit?<br/>lower of ₹500 or 1% of PO}
    P -- Yes --> OK2[Auto-reconcile<br/>Priya's policy]
    P -- No --> ESC[Flag and escalate]
    O --> P

4.2 Other unwritten rules, now executable

  • A cousin override: Rajesh Transport gets Net 45 instead of Net 30, because the owner is Priya's cousin. The Brain encodes it as a vendor-specific override in Step 8 of the operating map. The rule is real even though no policy document says so.
  • Legacy partners: Patel Logistics and Singh Cargo get Net 60.
  • Out-of-state vendors: if a vendor's GSTIN prefix isn't 27 (Maharashtra's GST state code), the agent raises UNUSUAL_OUT_OF_STATE_VENDOR.
  • Large and dormant vendors: "large invoice" means over ₹10,000, and "dormant" means no activity in 180 or more days. Both are flagged for audit.
  • Fuel surcharges: vendors billing a surcharge usually send a revised PO. Rather than flagging immediately, the agent holds the invoice as AWAITING_REVISED_PO for 3 days.

5. Safety first: the guardrail engine

An agent near money needs boundaries that can't be argued with. agent/guardrails.py is pure Python with no LLM, so there is no prompt to inject and no model to talk it out of a rule.

flowchart TD
    A[Proposed action] --> G{Guardrails Engine}
    G -->|AGENT_NEVER_CHANGE_AMOUNTS| B1[Blocked<br/>never edits invoice, PO, or QuickBooks values]
    G -->|AGENT_NEVER_SEND_COMMUNICATION| B2[Blocked<br/>never emails or messages vendors]
    G -->|NO_RECONCILE_OVER_100K_WITHOUT_HUMAN| B3[MANDATORY_DUAL_REVIEW<br/>invoices over ₹1,00,000]
    G -->|NO_RECONCILE_WITH_UNRESOLVED_FLAGS| B4[Blocked<br/>handwritten, out-of-state, dormant, late]
    G -->|LIMIT_FUEL_SURCHARGE_HOLD| B5[No hold<br/>unknown vendor, or surcharge over 10% of PO]
    G -->|All checks pass| OK[Action allowed]
    B1 --> LOG[(Audit log)]
    B2 --> LOG
    B3 --> LOG
    B4 --> LOG
    B5 --> LOG
    OK --> LOG

Highlights from the policy set:

  • Never change amounts. The agent can flag, hold, escalate, or mark reconciled. It can never edit an invoice or PO value.
  • Never contact vendors. All external communication goes through humans.
  • Over ₹1,00,000 always gets dual review, bypassing auto-hold and auto-reconcile.
  • Unresolved flags block reconciliation. Handwritten format, out-of-state GST, dormant vendor, or late submission must be cleared by a person first.
  • Fuel-surcharge holds are limited to known vendors (ACME, Patel, Singh), and never when the surcharge exceeds 10% of the PO total.

6. A closed-loop test that found a real bug

The repo ships with a 13-case regression suite (evals/cases.json). It earned its keep on case C011.

flowchart LR
    A[Run evals] --> B{All pass?}
    B -- No --> C[Read failures.log]
    C --> D[Diagnose the Brain]
    D --> E[Add or fix a rule]
    E --> F[Document in<br/>improvement_log.md]
    F --> A
    B -- Yes --> G[13/13 passing]

The failure: INV-C011 billed a ₹20,000 fuel surcharge on a ₹10,000 PO, which is 200% of the PO value. The agent put it on hold under the generic fuel-surcharge rule, as if it were routine.

The diagnosis: the Brain said when to hold, but had no limit on how large a held surcharge could be.

The fix: a new EXCESSIVE_FUEL_SURCHARGE_NO_HOLD rule in both the operating map and the guardrails config. A surcharge over 10% of the PO is never held and goes straight to Raj.

The result: re-running python3 evals/eval_runner.py now gives 13/13 passing. The full trail is in evals/failures.log and evals/improvement_log.md.

Because rules are YAML and tests are plain JSON, a non-engineer can read the rule, see the failing case, and understand the fix.


7. Every decision leaves a trail

Running the included 10-invoice sample produces:

------------------------------------------------------------
                       RUN SUMMARY
------------------------------------------------------------
Total Invoices Processed : 10
Successfully Reconciled  : 3
Escalated to Human       : 5
Held (Duplicate/Awaiting): 2
Errors Encountered       : 0
------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Escalating 5 of 10 is the safe default, not a failure. The agent is built to refuse when unsure.

Output file Purpose
reconciliation_report_YYYY-MM-DD.csv Ledger of invoice, matched PO, decision, flags raised, and whether manual review is needed
human_signoff.md Pre-filled sheet showing Priya and Raj exactly which decisions need their verification and signature
reconciliation_status.csv Every mutating action (flag_invoice, escalate_to_human, mark_reconciled, hold_for_revised_po) with ISO timestamps
agent_log.jsonl Full trace of PO checks, tolerance checks, and guardrails inspected for every invoice

8. Next step: letting a local open-weight model read messy invoices

The decisions are deterministic by design. What the current version doesn't do is read invoices that arrive as PDFs, phone photos, or email bodies. That is the natural place for a local open-weight model (for example via Ollama): the model reads, the rules decide.

sequenceDiagram
    participant U as Accounts clerk
    participant X as Extractor
    participant M as Local open-weight model
    participant V as Validator
    participant A as Agent and Guardrails

    U->>X: Drop in invoice file or email text
    X->>M: Prompt with strict JSON schema, temperature 0
    M-->>X: Structured JSON
    X->>V: Schema check, total equals sum of line items?
    alt Invalid or inconsistent
        V-->>U: Route to human review
    else Valid
        V->>A: Clean invoice record
        A-->>U: Decision, reason, audit entry
    end

The design rules for that layer follow directly from the guardrail philosophy:

  • Temperature 0 and a strict schema, so output is as repeatable as a model can be.
  • Cross-check with arithmetic. The extracted total must equal the sum of the extracted line items, or a human reviews it.
  • "Never guess." A null field routes to a person; a hallucinated value never reaches the engine.
  • The model never decides. Its output only feeds the front of the existing pipeline, so every guardrail above still applies.
  • Swappable. Changing the model is a one-line change, so there is no lock-in.

This is where open-weight models would beat a closed API for this customer. Local inference keeps invoice data on-premises and costs nothing per document.


9. Why someone would pay for this

flowchart LR
    A[Setup<br/>install, load POs, encode their rules] --> B[Monthly plan<br/>support, rule updates, new vendors]
    B --> C[Interview the next client<br/>using the extraction template]
    C --> A
  • Privacy is the sales pitch. "Your invoices never leave your office" is the first thing a finance team wants to hear.
  • Margins hold up. With no per-request cost, a flat monthly price carries no usage risk.
  • Repeatable onboarding. knowledge_extraction_template.md turns a new client's interviews into YAML rules. Each client's tribal knowledge becomes the product.
  • Trust by design. Nothing mutates without a log entry, and nothing over ₹1,00,000 clears without two humans.

10. What I learned

  1. The hard part was never the automation. It was getting two people's contradictory habits into explicit rules.
  2. Guardrails should be boring code. If a prompt can talk the agent out of a rule, it was never a guardrail.
  3. Tests turn "seems fine" into evidence. C011 only surfaced because there was a case for it.
  4. Refusing is a feature. Half the sample set escalates to a human, and that's what makes it safe to adopt.

11. Try it yourself

git clone https://github.com/Daksh-create349/Company-Brain.git
cd Company-Brain
pip install -r requirements.txt

python3 run_batch.py            # reconcile the 10 sample invoices
python3 evals/eval_runner.py    # 13/13 regression cases
python3 agent/guardrails.py     # guardrail self-test
Enter fullscreen mode Exit fullscreen mode

Requires Python 3.6 or newer.

12. Roadmap

  • [ ] Local open-weight extraction for PDFs, scans, and email bodies (section 8)
  • [ ] OCR for handwritten invoices, which the Brain already flags
  • [ ] Read-only QuickBooks integration, since the guardrails already forbid writes
  • [ ] Marathi and Hindi invoice support
  • [ ] One-click installer for non-technical staff

Closing

Small businesses don't need an AI that acts clever. They need one that acts predictably, explains itself, and keeps their data in the building. Company-Brain turns unwritten business rules into code anyone can read, tests them, and leaves a trail for every decision. Adding a local open-weight model to read the messy inputs is the natural next step, and the guardrails are already in place for it.

Questions or feedback? Comment below or find me at dakshsportfolio.vercel.app.

Top comments (0)