Inspiration

Tiruppur, near where I study, is one of the world's largest knitwear export hubs. Most of its exporters are small: one or two merchandisers handle every buyer email, every purchase order and every revision by hand. The expensive mistakes are quiet ones. A revised PO raises a quantity and pulls the ex-factory date forward by two weeks, buried in a reply thread. Nobody notices until the factory can't make it, and the exporter pays for air freight or loses the order.

I wanted an assistant that reads those emails the way a careful merchandiser would, checks whether the factory can actually deliver, and never sends anything without a person approving it.

What it does

ExportAgent turns a buyer email into a checked, approved action:

  1. Reads the email and any PO attachment. It classifies every request (new order, revision, date change, samples, payment, documents) and extracts buyer, PO number, style, colours, sizes, quantities, price, Incoterm, port and ex-factory date. Each field comes with the exact quote it was taken from.
  2. Finds the order and detects what changed. It handles revision suffixes ("Rev A", "Amendment 1"), orders with no PO number yet, and body-only revisions. When it isn't sure which order an email is about, it asks a person instead of guessing.
  3. Assesses risk. Python checks factory capacity and the fabric lead time. A Nemotron agent then decides what else to look up (destination labelling rules and buyer background through Tavily, or a what-if feasibility check) and judges the risk. Every flag has to cite its evidence.
  4. Drafts the reply. The draft acknowledges every request and pushes back with options computed in Python, such as a later date or a split shipment. A fact-checker verifies that every date, quantity and price in the draft exists in the source data.
  5. A person approves. Nothing is sent automatically. The buyer's decision is recorded as a new internal PO version.
  6. Generates the export documents. Commercial Invoices and Packing Lists are produced as PDFs, one set per shipment for a split. Every number is computed in Python, and anything unknown prints as "TO BE CONFIRMED" instead of being invented.

Example from the demo: a buyer's PO revision raises the order to 13,500 pieces and pulls delivery forward 15 days. ExportAgent detects the changes and finds the factory is 4,186 pieces short for the new date. It escalates the risk judgement to Nemotron Ultra and drafts a reply offering two feasible plans: everything a week later, or 9,300 pieces on time and 4,200 a week later. After approval, it produces two invoices and two packing lists.

The same order can be checked against a Tiruppur (India) or Dhaka (Bangladesh) factory profile without code changes: the revision is infeasible in one and feasible in the other.

How we built it

NVIDIA Nemotron on Nebius Token Factory, with a model for each job. Every model call goes through one router that runs on Token Factory's OpenAI-compatible API and logs model, tokens, latency and cost per call.

Task Model Why
Classification and extraction Nemotron 3 Nano (reasoning off) Fast and cheap for high volume
Re-reading a field that failed a Python check Nemotron 3 Super Only when Nano's answer fails validation
Risk assessment and drafting Nemotron 3 Super Matched Ultra on high-risk findings at a fraction of the cost
Hard risk cases Nemotron 3 Ultra Only when the plan is infeasible, the thread has evidence that could change feasibility, or Super's result conflicts with the Python check

We tested this choice instead of assuming it. Running Ultra on every risk assessment scored worse (76.9% precision / 90.9% recall on high-risk flags) than our cascade (89.7% / 100%), at about twice the risk-step cost. In our frozen evaluation, Ultra handled about 21% of risk runs, and the whole pipeline cost about $0.0094 per email.

Deterministic where possible, agentic where it matters. Order matching, version diffs, capacity maths, currency rounding, document totals and the draft fact-check are plain Python with tests (535 backend tests). The agent loop, capped at 8 rounds, is used where judgement helps. For example, it reads "fabric is already in-house" in a quoted message and re-runs the feasibility check, which turns a false high alarm into a "confirm the new date" note.

Tavily provides live compliance and buyer lookups. Every compliance flag shows its source as a clickable link.

Evaluation. We built a labelled dataset of messy, realistic buyer emails: revisions, reply threads, contradictions, European number formats, JPY prices, ambiguous dates and multi-request emails. Every result is reported, not only the good ones:

  • Held-out TEST v2: 30 cases written by a separate assistant session from a written spec alone, with no access to the code or prompts, and committed before any run. Results: 96.5% field extraction, 100% high-risk recall (80.1% precision), and 98.7% of emails matched to the right order.
  • What it exposed (date-order ambiguity, unknown revision suffixes) we then fixed using new development cases only. The held-out numbers in the README are left exactly as first reported.

Stack: Python (FastAPI, Pydantic, SQLAlchemy), Next.js with TypeScript and Tailwind, Python PDF generation, Docker, and Caddy for HTTPS. It's deployed on a small AWS Graviton instance, with images built in GitHub Actions.

Challenges we ran into

  • Nano returned empty answers at first: it spent its whole token budget reasoning. Turning reasoning off for extraction fixed it and made Nano fast and cheap.
  • The first test set humbled us. Change detection found only 28% of real changes on unseen emails, mostly because of revision suffixes and orders referenced without the word "PO". Deterministic matching fixes raised it to 92% on that set.
  • Dates like 03/02/2027. Is it 3 February or 2 March? Now the buyer's country and any unambiguous date in the thread decide it. If it's still unclear, ExportAgent asks the buyer instead of guessing.
  • Over-eager risk flags. Early versions flagged compliance on every order. A policy layer now requires specific, cited evidence for each flag.

Accomplishments that we're proud of

  • A complete workflow, from a buyer email to an approved reply to export PDFs, with a person in control at every step.
  • Model choices backed by measurements, including the finding that the biggest model wasn't the best choice for every step.
  • An honest held-out evaluation, and drafts that never state a number that isn't in the source data.

What we learned

  • Small models do excellent work when Python checks their output and a bigger model is called only when a check fails.
  • Most agent errors were matching and parsing problems, not reasoning problems, and plain code fixed them more reliably than prompts.
  • Writing the test set before tuning keeps you honest.

What's next

  • Reading orders directly from Gmail, Outlook and WhatsApp.
  • Reading scanned PO PDFs.
  • A capacity model connected to the factory's real production plan. The current one is deliberately conservative.
  • Testing with real exporters in Tiruppur and Dhaka.

Limitations: the demo uses fictional buyers and illustrative factory capacity. Processing a new email takes about a minute end to end.

Built With

Share this project:

Updates

Submission history