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:
- A structured "Company Brain": the business's unwritten rules turned into executable YAML.
- 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:
- Find the matching purchase order (PO)
- Check the variance against tolerance rules
- Catch duplicates, dormant vendors, out-of-state vendors, and oversized invoices
- Apply the right payment terms for that vendor
- 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
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 raisesUNUSUAL_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_POfor 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
------------------------------------------------------------
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.mdturns 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
- The hard part was never the automation. It was getting two people's contradictory habits into explicit rules.
- Guardrails should be boring code. If a prompt can talk the agent out of a rule, it was never a guardrail.
- Tests turn "seems fine" into evidence. C011 only surfaced because there was a case for it.
- 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
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)