Inspiration
A Vietnamese IT reseller's day starts with a pile of RFQs: a scanned purchase order, a spreadsheet, an email in half-Vietnamese half-English. Turning one into a quote takes 30–90 minutes — reading it, hunting SKUs, pricing by tier, applying 2026's 8% VAT (10% for telecom), typing a bilingual PDF. It's skilled work, and almost all of it is mechanical. We wanted to automate the mechanical part without automating away the one decision that actually matters: whether the price is right.
What it does
QuoteMind is an autopilot for that job. An RFQ arrives — pasted, uploaded, or dropped into an OSS bucket — and the system runs the whole path: read it (OCR if it's a scan), match the catalog, resolve the pricing tier, price it, self-check, render a bilingual PDF. Then it stops, and asks a human.
The dashboard shows the approval queue, the quote detail, the critic's verdict next to the model's own note, the full reasoning trace, and a waiver modal for anything flagged. Nothing is ever sent automatically.
Try it live: https://quotemind.cyberskill.world — the dashboard, and a public /eval page rendered straight from a committed benchmark snapshot, so the numbers below aren't a claim you have to take on faith.
How we built it
The idea the whole system is built around: a quote is a document a customer gets invoiced from, so the model is never allowed to do arithmetic.
Qwen does what it's excellent at — reading a messy Vietnamese email, working out that "20 con lap Dell i7 32GB" means twenty Latitude 5450 i7 laptops, and picking that SKU out of a catalog of near-identical variants. Everything after that is ordinary, unit-tested Python: Decimal prices, VAT bands, totals, amount-in-words. A critic agent then recomputes the entire quote from source data and refuses to pass one whose numbers don't reconcile.
On top of that:
- Memory — every human decision (approve, reject, waive) is written as an episodic memory. Before quoting a known customer, QuoteMind recalls the top three prior decisions ranked by
similarity × recency × importance, so last week's rejection outranks last year's approval. Memory informs the reviewer; it never touches a price. - Planning — non-trivial quotes are decomposed in AgentScope's
PlanNotebook, and the plan reports what actually ran, including any steps handed off to a human. - Audit trail — a flagged quote can't be silently approved. Any waiver goes onto a hash-chained audit trail recording who signed it and why.
Stack: Qwen (qwen3-max, qwen-plus, qwen-vl-ocr, text-embedding-v4) on DashScope Singapore · AgentScope · Alibaba Cloud Function Compute 3.0 (HTTP API + OSS-triggered ingest) · Tablestore for durable state and agent memory · OSS for inputs, PDFs and traces · Python 3.12 / FastAPI.
Deployment is two Function Compute functions sharing one codebase: quotemind-api on an HTTP trigger and quotemind-ingest on an OSS object-created trigger over quotemind-inbox/rfq/. They share the same pricing and validation pipeline, on purpose — an ingest path with its own copy of the quoting logic would be a second system that could disagree with the first about the price.
Challenges we ran into
- FC 3.0 doesn't hand an HTTP function a WSGI environ on its own domain. It hands the function an event envelope and expects one back — every wrong guess about that shape produces the same symptom, a 502 with no stack trace.
- The default
*.fcapp.rundomain injectsContent-Disposition: attachmenton every response.curlignores that header; a browser obeys it — so for a while the operator dashboard downloaded instead of rendering, for everyone, while every API health check stayed green. Fixed with a custom domain and a Let's Encrypt cert issued via an OSS-served ACME challenge (no redeploy needed to renew). - DashScope serves the same models under two, non-interchangeable bases —
/compatible-mode/v1(what the OpenAI-style clients need for chat, embeddings and vision) and/api/v1(what AgentScope's native chat model wants). The first time our CD pipeline deployed for real, one wrong fallback value sent embeddings to a 404 while chat kept working — the site was up, the models answered, and it could not produce a single quote. We normalized the setting at the only boundary it enters through, added a regression test, and taught CD to prove the pipeline still produces a quote end-to-end on every deploy, not just that the right commit is live. - The frozen model id is not a frozen model. Our headline score moved from 97% to 93% between two runs of the same eval on unchanged code, because the model behind
qwen3-maxchanged underneath us. We measure the baseline in the same run, against the same models, so the comparison stays fair even when the absolute number drifts — and we say so plainly rather than quoting the best number we ever saw.
Accomplishments that we're proud of
We measured whether "the model never touches arithmetic" actually matters, against a single monolithic agent given the identical models, catalog, and 30 labelled RFQs (five of them real scans):
| task success | price exact | flagged the problem | $ / quote | |
|---|---|---|---|---|
| QuoteMind | 93% | 93% | 10% | $0.013 |
| single agent | 40% | 40% | 0% | $0.011 |
The single agent reads and matches almost as well as we do — its SKU accuracy is within two points of ours. It gets the money wrong on 60% of quotes, and never notices. Taking arithmetic away from the model and putting a critic behind it is worth +53 points of task success, for about 18% more cost per quote.
The two points we lose, we chose not to buy back: one adversarial case asks for a laptop the catalog doesn't sell (64GB RAM, 2TB SSD). The matcher is shown the closest thing we do sell — a 32GB machine — and refuses to substitute it. The label expects the substitution; we think a system whose entire premise is stop rather than guess shouldn't be penalized for stopping, so the refusal is surfaced at the approval gate with the reason and the near-misses, and a human decides in seconds.
We're also proud that it's genuinely, checkably live: /health reports the deployed git SHA and probes every frozen model id from inside Function Compute on cold start, so a retired model announces itself instead of failing silently; /eval renders the table above from a committed snapshot, in public.
What we learned
Every bug we hit this build lived in a seam nothing had crossed yet — the first real run of continuous deployment, the first time a browser (not curl) hit the dashboard, the first daily health check running independently of a deploy. The fix each time wasn't "patch the instance," it was "enforce the invariant at the one place the value can enter" — and then write the test that fails on the old code and passes on the fix, so the seam stays closed. We also learned to be paranoid about our own verification scripts: more than once, a script "confirming" a bug turned out to be checking the wrong field — the same class of mistake the product itself exists to catch.
What's next for QuoteMind
- A production tenant would put the dashboard behind a real identity provider instead of a single rotatable demo token — the architecture already assumes this, it's a deployment decision, not a rebuild.
- Splitting one document into several unrelated RFQs, deliberately deferred rather than solved badly — mis-splitting produces two wrong quotes instead of one right question.
- Pre-warmed Function Compute concurrency ahead of high-traffic windows, and a public uptime badge wired to our daily health check.
- Structured output for the vision/OCR path once a tool-calling-capable Qwen OCR model is available —
qwen-vl-ocrtoday is OCR-only, and the hand-parsed result is already defended by the same critic that recomputes everything else.
Built With
- agentscope
- alibaba-cloud
- dashscope
- fastapi
- function-compute
- oss
- pydantic
- pytest
- python
- qwen-plus
- qwen-vl-ocr
- qwen3-max
- serverless-devs
- tablestore


Log in or sign up for Devpost to join the conversation.