Case study — Indian Army
Inventory AI Agent
An assistant that answers inventory questions in plain language, built to run on the Army's own hardware with no internet connection at all.
- Client
- Indian Army, via Talentelgia Technologies
- My role
- Backend & infrastructure
- Deployment
- On-prem, air-gapped
- Stack
- LangGraph · PostgreSQL · pgvector · PaddleOCR · Mistral 8B
<3 min
to generate a report. It used to take hours
No public link: private, air-gapped deployment
Context
Inventory records came in on paper challans. Getting an answer out of them meant someone going through the paper, or someone who could write SQL going through the database. Neither scales when the people asking the questions are the staff who run the stores.
The ask was simple to state: let staff ask about inventory in plain language and get a correct answer back, from data that's actually up to date.
Constraints
Most of the design follows from these four constraints.
- No external connectivity
- The system is fully air-gapped. No calls out to any API, no cloud services, no package pulls at runtime.
- Self-hosted inference only
- Every model runs locally. That rules out hosted LLMs and means working within what one on-prem GPU can serve.
- Data stays on-site
- Records never leave the on-prem GPU and server hardware, which I specced and configured, including storage.
- Safe queries from unsafe input
- Questions arrive as free text, but they end up touching real inventory records. A bad question must never turn into a bad query.
Architecture
Paper challans go through an OCR pipeline built on PaddleOCR, and a REST API writes the results into PostgreSQL, with pgvector embeddings alongside the rows. When someone asks a question, a LangGraph router reads it and sends it down one of two paths. Everything, the router included, runs on a self-hosted Mistral 8B.
Input
Paper challans
Ingestion
OCR pipeline
PaddleOCR, then a REST API that writes the results
Storage
PostgreSQL + pgvector
Inventory rows, with embeddings alongside
Orchestration
LangGraph router
Reads the question and picks a path
Path A
Retrieval agent
pgvector + keyword search
Path B
Text2SQL agent
Read-only role, one schema, allow-listed queries
Output
Answer in plain language
- Why a router instead of one model doing everything
- The questions come in two kinds. Some are fuzzy ("what came in on that challan last week?") and are best answered by searching the records. Others are exact ("how many of X are in stock?") and need a real query. An 8B model is noticeably more reliable when each call has one narrow job and a short prompt than when a single prompt carries every tool and every rule. Splitting the paths also means each can be tested on its own, and the SQL path's permissions stay with the SQL path.
- Hybrid retrieval
- The retrieval agent combines pgvector similarity search with plain keyword search. Vector search handles loosely worded questions. Keyword search catches the things embeddings blur together, like item codes and challan numbers.
- Why Text2SQL runs under a locked-down role
- Telling a model "only write SELECT statements" in its prompt is a request, not a guarantee. A small local model can misread a question or be talked into something. So the limits live in the database: the Text2SQL agent connects as a read-only role with access to one schema, and every generated query is checked against an allow-list before it runs. If the model gets it wrong, Postgres refuses. The prompt is the first line of defence, not the only one.
- No external calls, anywhere
- OCR, embeddings, routing and generation all run on the same on-prem hardware. Nothing in the request path depends on a network connection the site doesn't have.
Outcome
Generating an inventory report went from hours to under three minutes. Staff ask in plain language instead of waiting on someone who can read the paperwork or write SQL.
I built the backend and the infrastructure: the agents, the query safety layer, the OCR ingestion and API, and the on-prem hardware it all runs on.