Inspiration
A small consumer-electronics exporter in Shenzhen gets buyer emails in a dozen languages every day — quote requests, price negotiations, restocking questions. One person can't keep up with the volume, but full automation without oversight is worse: a single mis-approved discount at scale can wipe out a quarter's margin. We wanted to see if an agent could actually close that gap — handle the volume, but know precisely when to stop and hand the decision to a human.
What it does
TradePilot is one agent core with three triggers and a single human checkpoint:
- Quote & negotiate (the hero flow) — reads an inbound email in any language, resolves the exact product against the seller's real catalog, and negotiates using competitor pricing as a live market signal.
- Reprice — scans the catalog against competitor prices on a schedule, aligns automatically within a ±10% band, and escalates anything beyond that.
- Reorder — watches stock levels and drafts a production memo the moment a SKU drops below threshold.
Every decision that crosses a risk threshold — a discount over 15%, a price swing over 10%, any restock — lands in one approvals queue in the dashboard, filterable by type/language/volume, one click to approve or reject. Everything else runs unattended. The dashboard also has a live "send a test email" panel — you can watch Qwen process a real request in front of you, not a replayed recording.
How we built it
Backend: Python + FastAPI on Alibaba Cloud Function Compute, deployed as a custom runtime
(so it runs as a real persistent web server, not an event handler). qwen-flash handles
the high-volume extraction step (intent, product, quantity, discount ask); qwen-plus is
reserved for the one output a human actually reads — the negotiation draft. Alibaba Cloud
Tablestore holds the catalog, buyer memory, the approvals queue, and an idempotency
cache. The catalog itself was generated with 80% deliberate overlap across three sellers
— same product names, different SKUs and prices — specifically to defeat a naive
RAG/keyword approach and force the agent to resolve by context.
The dashboard is a separate Next.js app on our own VPS, behind Nginx and Let's Encrypt, talking to the backend through server-side proxy routes so the browser never touches the Function Compute URL directly. Repricing and reorder are triggered by an external cron job hitting the backend every 10 minutes — simpler and more auditable than wiring a native Function Compute timer trigger under deadline pressure.
The one rule we didn't compromise on anywhere in the stack: the LLM never decides how much money gets approved. Every discount, every repriced value, every reorder quantity comes out of deterministic Python policy. Qwen reads language and writes language; it never signs off on a number.
Challenges we ran into
Most of our real bugs were the quiet kind — the ones that don't crash, they just answer wrong. Two stand out:
- Our variant-matching logic used substring containment to resolve near-identical SKU names. That worked until a buyer asked for "iPhone 16 Pro" and got quoted plain "iPhone 16" instead — because "iPhone 16" is a literal substring of "iPhone 16 Pro" and happened to appear first in the candidate list. The fix was to always try an exact match before falling back to substring, and to prefer the most specific candidate when substrings do collide.
- Two of our seven test languages (Chinese and Arabic) were silently getting quoted the
wrong product color. The root cause: our text-normalization step stripped non-Latin
script down to an empty string, and in Python
'' in anythingisTrue— so an untranslated variant name was matching the first catalog entry instead of correctly asking for clarification. We fixed the root cause (guard against empty normalization) and, more importantly, moved variant translation into the extraction prompt itself, against a controlled vocabulary pulled from the real catalog — letting Qwen's actual multilingual strength do the work instead of a fragile string comparison.
Deployment had its own fight: Function Compute's custom runtime build image and its
actual execution image aren't the same environment. We shipped a version that ran
python3 and got Python 3.7 instead of the 3.10 our dependencies were built against —
twice, guessing wrong both times about where the interpreter actually lived. We stopped
guessing and had the container tell us directly (a deliberately failing bootstrap script
that dumps which/find output into the error log Function Compute returns), which
pointed straight at the real path.
Accomplishments that we're proud of
Getting the anti-RAG catalog design to actually do its job — watching the agent correctly refuse to confuse a competitor's exclusive SKU with the seller's own catalog, and correctly ask for clarification instead of guessing when a product name is genuinely ambiguous. Getting a real, working deploy on Alibaba Cloud Function Compute with a custom runtime serving FastAPI, end to end, with a public URL that's live right now. And getting the human-in-the-loop queue to feel like one coherent system across three completely different business decisions (a discount, a price change, a production order) instead of three bolted-together features.
What we learned
That "it works in the demo I just ran" and "it's actually correct" are two different claims, and the gap between them is where the real bugs live — several of ours never threw an error, they just answered confidently wrong, and only showed up when we read the output line by line instead of trusting a green checkmark. That matching logic for near-identical strings needs exact-match-first as a hard rule, not an afterthought. And that a serverless platform's build environment and runtime environment can quietly diverge — the only reliable way to find out what's actually available at runtime is to ask the runtime itself, not the documentation.
What's next for TradePilot — Cross-Border Ops Agent
Authentication on the scan endpoints (currently open for hackathon simplicity). Real email delivery so "reject" in the approvals queue actually notifies the buyer, not just updates a status. A native Function Compute timer trigger to replace the external cron once we've verified its event contract properly. And extending the same controlled- vocabulary approach we used for variant matching to product-line matching itself, so a buyer describing a product in their own language resolves correctly even when the catalog's literal name doesn't share a single token with what they wrote.
Log in or sign up for Devpost to join the conversation.