Skip to main content
Completed
Ai

SmartSMB Human-in-the-Loop AI Workflow

A learning system for small-business enquiries: LangGraph routes the job, code calculates the quote, PostgreSQL holds state, and quotes above a configurable threshold require approval.

2026
Personal Project — Architecture and Implementation

Technologies

  • TypeScript
  • Node.js
  • LangGraph.js
  • PostgreSQL
  • Hono
  • Drizzle ORM
  • Zod
  • Vitest
  • LLM Tool Calling
  • Human-in-the-Loop

Key Achievements

  • Quote / complaint / FAQ / handoff routing in a stateful graph, not a single prompt
  • Prices calculated in code; the model only gathers fields and picks tools
  • Approve, revise, reject as graph interrupts — revisions go back through approval

Project Links

Why I built this

I wanted to learn how far a stateful agent can go on a small-business front desk without giving the model the company chequebook. The question was not “can an LLM talk like a receptionist.” It was: can it triage an enquiry, fill a quote, and require a person to approve amounts above a configurable threshold.

This is a personal learning project. It is not running a business.

Scope

The sample accepts a customer message and routes it:

  • Quote — collect job details, call pricing/availability tools, compute a price in application code, and pause when the amount exceeds a configurable approval threshold
  • Complaint — dedicated path, not mixed into quoting
  • FAQ — common information
  • Unclear — hand off instead of guessing

What it does not do: production auth, real telephony, a live company knowledge base, or real customer-channel delivery. Quotes at or below the configured threshold follow the automatic send path; larger quotes pause for an operator. Models are swappable; the graph, schemas, and side effects are the system.

Repo: nilushan/langgraph-frontdesk-agent.

Core workflow

A quote thread looks like this:

  1. Classify the message.
  2. If it is a quote, loop: ask for missing fields, call Zod-validated tools (pricing, availability).
  3. A code node turns those fields into a number. The model does not write the total.
  4. Above the configured threshold, LangGraph interrupts. An operator approves, revises the structured fields, or rejects; quotes at or below it continue to delivery automatically.
  5. A revision cannot jump to the customer. It re-enters approval, with who changed what.
  6. Delivery is idempotent on quote identity, then the thread continues.

State is checkpointed in PostgreSQL after each step. Restart the process while someone is thinking; the thread is still there.

Design choices

Probabilistic where language is, deterministic where money is. The model is good at “they want the gutters done next Thursday.” It is not the pricing engine. Keeping that split means I can test quotes without mocking prose.

Approval is a node, not a Slack side-channel. If human review lives outside the graph, resume logic, audit, and “did we send this” all rot. Interrupts make the wait part of the program.

Checkpoints plus idempotent send. Durable state without a delivery key is how you double-email after a retry. The send step is keyed on the quote.

Tools are schemas, not “the model can call the database.” Invalid arguments fail at the boundary. Routing between quote/complaint/FAQ is application code, not a vibe.

I also used this repo to practise writing the spec and ADRs first, then implementing with coding agents, then treating the diff like production code: tests, failure paths, CI. Generated code that fails those is not a pass.

What I learned

A long-running front-desk job is a state machine that sometimes calls a model, not a chat log with extra steps. Once you admit that, persistence, interrupts, and idempotency stop being optional.

Specialist routes only help if each path has a different contract. Dumping complaints into the quote ReAct loop just makes a worse quote agent.

Model portability was easy after the graph and the price code stopped importing a vendor SDK. Before that, “swap the LLM” meant swapping the business rules by accident.

Not production

A real rollout would still need threat modelling, prompt-injection and tool-abuse controls, evaluation on real transcripts, rate limits, retrieval over actual company data, channel integration (email/SMS/phone), and someone on-call for the approval queue. None of that is claimed here.