Self-Hosted AI Operating System for Procurement & Finance
A private AI operating system that runs a company's whole tender business on its own servers, with self-hosted Ollama models and no data leaving the building. One integrated platform carries every deal from the RFQ email to the final payment: mail and document agents, per-line supplier sourcing, a supplier and product knowledge graph, bid pricing and approval, logistics, billing, and a finance engine that tracks money from quote to settlement, all behind confidentiality rules stricter than a normal role system.
Screenshots stay inside the company.
This system runs on the client's own servers and GPU, with self-hosted models, and its screens, data, and name are covered by a signed NDA. The build itself is described in full below.
- 01RFQ received
- 02Document processing
- 03Assignment
- 04Supplier outreach
- 05Quote collection
- 06Commercial comparison
- 07Bid approval
- 08Purchase order
- 09Logistics
- 10Billing & collection
- 11Project closure
- NDA signed
- Self-hosted Ollama
- On-prem GPU inference
- Data never leaves the network
- TypeScript
- pnpm workspaces
- Turborepo
- React 19
- Vite
- NestJS 11
- Python agent services
- FastAPI
- Zod v4
- Pydantic
- LangGraph
- Multi-agent orchestration
- pg-boss
- PostgreSQL 16
- Prisma 7
- pgvector
- Knowledge graph
- Graph data science
- RAG
- bge-m3 embeddings
- Embedding rerank
- Ollama
- llama.cpp
- llama-server
- Qwen3
- Self-hosted GPU inference
- Docling
- PaddleOCR
- PaddleOCR-VL
- RapidOCR
- SearXNG
- Managed web search
- Microsoft Graph API
- Microsoft 365 / Outlook
- IMAP
- Excel and PDF generation
- TanStack Table
- TanStack Query
- Tailwind v4
- SSE
- PWA
- RBAC
- Field-level masking
- Audit trail
- httpOnly sessions + CSRF
- Docker Compose
- Zero-downtime deploys
- Encrypted backups
- Biome
Procurement AI
Mail as an event feed
Every connected Microsoft 365 mailbox is read, not only the shared one. Tender portal notifications are treated as events: each one is matched to a request already held, or opens an entry in an intake queue of work still to be brought in. The system never scripts logins against the customer's portal.
Mail router agent
Messages are classified (new RFQ, extension, quotation, purchase order, shipment update, invoice, supplier reply) and filed against the record they belong to. Anything ambiguous goes to a short triage list instead of being guessed, and important mail is raised on the CEO dashboard.
Document understanding
PDF, Excel, Word, and scanned files. Native versus scanned is detected per document, so OCR runs only where it must. Line items are extracted with part number, alternate part / CAGE code, description, quantity, UOM, condition, certification required, deadline, and priority markers, across multi-page tables, merged columns, and header-less layouts. A corpus-diff harness replays every historical document against new extraction code, so a model or prompt change can't silently regress.
Human confirmation queue
Extraction never writes straight to the records. A person sees the extracted fields beside the original document, with low-confidence fields flagged, and confirms. A part number read from a scan is a suggestion until somebody agrees with it.
Part-number normalisation
Part numbers are stored exactly as written and normalised separately for matching, so differently written strings of the same part still link up. The system never silently corrects anything, and the customer's original line numbering (gaps included) is kept as data, never regenerated.
Duplicate detection
The same requirement can arrive through two channels under two references. Every incoming document is checked by reference, by part numbers and quantities, and by the file itself, and likely duplicates are flagged for a person to decide.
Supplier sourcing agent ("scout")
Sourcing runs per line item, not per request. Multi-provider web research (managed search plus self-hosted SearXNG), content extraction, embedding rerank, and a deep async research phase, with admission control and per-run token and time budgets so an agent can't run away with cost.
Knowledge graph and graph data science
Suppliers, customers, products, OEMs, parent groups, distributors, tenders, and quotes linked in a knowledge graph. Routing rules and their exceptions are first-class records, and graph data science algorithms run over the graph to surface supplier relationships, similar products, and sourcing paths that flat tables hide.
Supplier intelligence
Every approach is a record: who was contacted, when, whether and how fast they replied, what they quoted per line, and what happened. Supplier suggestions use visible, rule-based scoring over that history, so the reason behind every suggestion can be read and tuned. Suggestions are advice; procurement decides.
Outreach and chaser agents
One drafted enquiry per supplier from the RFQ lines, responses tracked across every mailbox, and one screen listing every silent supplier with follow-ups already drafted as replies inside the original thread. A person reviews and sends.
Quote intelligence
Supplier replies read into a per-line comparison grid: price, MOQ against requested quantity, lead time (quoted and confirmed), condition, certificate, validity, currency, and payment terms. Quotes are matched back to RFQ lines by normalised part number, never by position. Evidence is surfaced; the human decides, and the choice is recorded.
Requirements and readiness
Every RFQ carries a requirements checklist (certification, alternates, MOQ, shelf life, condition, delivery terms), partly pre-filled from extraction. Readiness is a state, not an opinion, and it gates document generation.
Semantic search and institutional memory
Confirmed documents, quotes, and linked messages embedded into a vector database. Plain-language search cites the source record, and exact part-number matches always rank above similarity.
Agentic doc generation
Technical compliance matrix and commercial offer BOQ filled in the company's existing Excel layouts, with requested-versus-offered pairing, composed descriptions, automatic part-number restatement, and an availability note grouped by lead time. Plus RFQ sheets, letterhead PDFs, delivery papers, and packs per deal, each version stored against the RFQ.
Finance & Commercial AI
Pricing engine
Line total, duty, margin, shipping, and contingency reproduce the company's existing pricing sheet to the cent, live against the recorded quotes: buying price, freight, duty, total landed cost, selling price, and estimated profit. Every figure is fixed arithmetic, never a model output.
Margin memory
The margin approved on a part and for a customer is held and shown when that part comes up again, with every change recorded as who, when, and from what to what. It fixes repeat items being prepared below the margin already approved.
Price consistency check
Every quoted line is checked against what was quoted for the same part on any earlier request, to any customer. Differences are flagged while the bid is priced and again at approval, before the customer notices.
One-screen bid approval
The approver sees the whole bid at once: both documents, the requirements and how each was answered, every supplier approached, every quote per line, the chosen supplier against the others, margins against previously approved ones, and who changed what. Approval is stored against the exact version.
Multi-currency deal ledger
Pegged and floating currencies, side-aware payment terms held per purchase order, instalments derived from those terms in both directions, payment ladders that settle oldest-first, advances held as unapplied credit until the invoice exists, and overdue computed on read.
Working-capital exposure
Supplier and customer sides of a deal sit on one project, so money paid out against money in is visible per deal and in total: the number that actually limits how many deals can run at once.
Customer billing profiles and bill checker
Each customer's advance terms, payment terms, submission route, and required documents are recorded once. Billing opens automatically when goods arrive, and each bill is checked for missing documents, figures that don't reconcile, and tax presentation before a person sends it. Rejections feed back into the profile.
Profit, VAT, and ageing
Net, VAT, and gross held separately, so profit is never overstated by tax. Estimated-vs-actual profit per tender, per batch, and per line, with several supplier POs rolled up to one project. Receivables aged from delivery date.
Watchdog
Instalments due or late, receivables past terms, RFQ deadlines approaching (stored to the hour), and suppliers gone quiet, on one self-compiling screen ordered by urgency.
Verified executive digest
A scheduled agent writes the daily narrative with an LLM, and a second verifier agent checks every figure against the API before it ships.
Direction of travel
Autonomous cash-flow forecasting, anomaly detection on supplier pricing, agent-drafted month-end close, and e-invoicing compliance, moving the finance stack from reporting to predicting.
Logistics & Operations
Shipments and custody
Several shipments per purchase order, custody modelled as a handover on signature, and inbound and outbound AWBs as separate records. Location is a property of the shipment, so a future warehouse is a new location, not a redesign.
Document checklist
Packing list, commercial invoice, delivery note, certificate of conformity, AWB, and customs paperwork shown as present or missing per shipment. Items that gate delivery or cost money are tracked as such, and proof of delivery opens invoicing.
Post-delivery discrepancies
Shortages, excess, missing certificates, and document requests tracked against the deal instead of an inbox. The certificate promised at quote, submission, PO, and delivery is captured at each point so any divergence shows.
Daily reporting agent
Short structured end-of-day reports from procurement, logistics, finance, and operations, consolidated into one management summary, with non-submission visible.
CEO dashboard
Active RFQs, deadlines, quotes, POs, active and delayed shipments, outstanding payments, cash position, expected profit, staff activity, and alerts, computed live and usable on a phone. Down to a single RFQ: which suppliers were approached, who replied, how fast, and who is still silent.
Private line of enquiry
The CEO can point the sourcing agent at any request, including ones assigned to someone else, and get findings visible to them alone. The agent finds and suggests; it never contacts anyone or changes the request.
Security & Access Control
Three-layer authorisation
Route guards decide who can reach an endpoint. Row scoping in a base repository decides what records a person can see, and no service builds a raw query. A field-masking interceptor strips margins and company figures from anyone not allowed to see them.
Confidential at any stage
A request can be made confidential at any point, hiding its lines, suppliers, quotes, pricing, PO, shipment, and payments from every list, search, count, and report. Private suppliers never surface in search or suggestions. Access can be granted back per person and per record.
Audit independent of visibility
Every mutation is written to the audit trail with full before-and-after history and version restore, even where the person making it can't see the record. Restricting visibility never switches off the audit.
Tested where mistakes are unrecoverable
A role × resource × action permission matrix test suite, and pricing arithmetic tests pinned to the company's real sheet to the cent.
Sessions and credentials
httpOnly cookie sessions with CSRF tokens (no tokens in localStorage), secrets generated at install, and mailbox access scoped by an Exchange application access policy so the app reaches the named mailboxes and no others.
Built without touching their data
Developed entirely on dummy and sample data in a separate environment. No developer access to production, servers, mail, or live figures at any point. Each phase is handed over as a deployment package and configured by the client's own team.
Engineering
Stack
pnpm workspaces + Turborepo monorepo: React 19 / Vite SPA and NestJS 11 API, with Python agent services and a FastAPI document service. A single shared Zod v4 contract is the source of truth, with a Pydantic-to-Zod wire tripwire test so the Python and TypeScript sides can't drift.
Orchestration
LangGraph state machines for multi-step, multi-agent runs on a pg-boss durable queue: resumable, budget-capped, retry-safe jobs, dead-letter queues, and concurrency set per job type to match what OCR and the model can absorb.
Document pipeline
Docling for parsing and layout with a pluggable OCR engine: a standard RapidOCR path, and a direct PaddleOCR-VL path for difficult scans where the hardware allows. Chosen partly on licensing, so the client owns a deployment with no copyleft or revenue-capped dependencies.
Inference
Self-hosted Ollama and llama.cpp / llama-server on the company's own hardware, GPU if present and CPU if not, with the model set in configuration and a pluggable backend seam for A/B-ing models (Qwen3 family by default, Apache 2.0). Local bge-m3 embeddings for RAG. Prompts, documents, and figures never leave the company's servers.
Data
PostgreSQL 16 with Prisma 7 for transactional data, pgvector for semantic retrieval, and a knowledge graph for relationship analytics. One backup covers records, embeddings, and the job queue. Hand-written migrations are replayed into a throwaway database before every deploy.
Frontend
Tailwind v4 with a custom token set and no component library, TanStack Table for dense 90-line requests and quote grids, TanStack Query, hand-rolled SVG charts, SSE live notifications, installable PWA, responsive down to phone.
Ops and delivery
Four containers, one .env, one command to install, nothing on the host but Docker. Zero-downtime deploys with one-command rollback, offline docker-save handover for locked-down networks, and encrypted backups to three destinations with restore tested before handover. Every phase ships with full source, migrations, install and configuration guides.
Guardrails as code
Hard rules enforced by tests: the model handles language and code handles numbers; nothing becomes a record without a person; nothing is sent without a person. No autonomous email on any trigger. Agents propose; people decide; everything is logged.