What it does

Measuring an FTIR spectrum takes minutes; interpreting it is the bottleneck. On hard samples, a spectral library returns a list of near-tied candidates — in our main demo, the top two candidates score 0.9137 and 0.9092, a gap of 0.0045, with fifteen plausible names. A ranked list like that does not answer the analyst's real questions: what can I defensibly conclude, and what should I do next?

ChemSpectra Agent answers both. Qwen (qwen3.7-max via Alibaba Cloud DashScope) routes each request through six real tools — library search over 130,000+ measured spectra, peak-meaning explanation, spectrum-quality perception, deterministic direction arbitration, chemistry-rule cross-validation, and public-case lookup — and resolves every analysis to one of three defensible levels:

  • entity / GREEN — evidence is sufficient to act on the top candidate;
  • library_direction / YELLOW — no single compound can be locked, but the chemical family can, and the agent generates a concrete lab plan to narrow it down (e.g. a nitrogen/elemental check or DSC scan to separate polystyrene from ABS/SAN);
  • uncertain_direction / RED — candidates diverge, and the agent refuses to guess.

The key mechanic is the level shift. In the demo sample, the Top-1 candidate holds only 6.9% of the weighted candidate pool — locking one compound is indefensible — but 100% of the fifteen candidates map to the styrenic family, so the direction is locked with full direction confidence. Uncertainty at one level, certainty at another: that is what the analyst actually needs. On a diverging sample (Top-1 score 0.8029, direction share 36.4%), the same agent returns RED and says honestly that no defensible call exists.

Everything the judges see is deterministic where it must be: confidence = library_confidence × rule_check_multiplier, computed by Python — the LLM is not allowed to invent numbers. Three consecutive runs on the RED fixture return the identical verdict (score 0.8029, confidence 0.6423). The LLM's tool route may vary; the deterministic verdict does not. Peak assignments come back with literature DOIs, every step lands in a timestamped JSON audit trail, and the pipeline always pauses at a human confirmation checkpoint — because in pharma, forensics and QC, an unsupervised AI verdict is not a feature, it is a compliance violation.

Try it live: https://chemspectra.ftir.fun — this exact code, against the real library.


Inspiration

I am a materials scientist with nearly 20 years in analytical testing. FTIR is the workhorse for identifying unknown materials, but one difficult spectrum can take hours, and the failure mode is always the same: a page of near-tied library candidates that even experienced analysts cannot separate.

I taught myself Python in 2024 and built FTIR.fun, a spectral platform that now has 130,000+ reference spectra and users in 50+ countries. But search alone only sorts — it never decides. What was missing is the judgment layer: look at all the evidence, say what level of conclusion is defensible, expose what is not, and tell the human what experiment would settle it. Qwen's function calling made that layer buildable.


How I built it

Built during the hackathon (the FTIR.fun platform itself is pre-existing and serves only as the evidence source through its API):

  1. A custom ReAct loop — no LangChain, no framework. Direct DashScope calls with enable_thinking=True, so the reasoning chain streams live into the UI's Thinking Panel.
  2. Qwen Function Calling over six real tool schemas with intent routing: "what material is this?" goes to search_library; "what does 1715 cm⁻¹ mean?" goes to explain_peaks; "is my spectrum OK?" goes to check_spectrum_quality. Follow-up questions run through the same function-calling loop — ask about a peak mid-session and Qwen routes to the peak tool, not another search.
  3. A deterministic arbitration engine (pure Python): entity share vs direction confidence over the Top-15, three-level verdicts, named thresholds, no hidden model scores.
  4. Cross-validation with seven chemistry checks, including hard-forbidden functional groups — negative evidence that can veto a candidate (a polyolefin candidate with a strong 1730 cm⁻¹ C=O band is chemistry-inconsistent, whatever its similarity score).
  5. A host required-evidence gate: if Qwen tries to synthesize without the required evidence (library search, arbitration on near-tied results, cross-validation), the host runs the missing tool and feeds the result back.
  6. Deterministic confidence formula, JSON self-repair with error-context retry, per-request sessions, and a full JSON audit trail of every UI event, API call, LLM request and tool result.
  7. Human-in-the-loop checkpoint with follow-up chat before any report is generated.

Stack: qwen3.7-max (Alibaba Cloud DashScope SDK), Python, FastAPI, vanilla JS single-page UI, REST + MCP tools.


Challenges I ran into

Making "I don't know" a first-class output. The hardest design problem was not identification — it was refusing honestly. Early versions let the LLM's optimism leak into conclusions; in one test the model wanted to call a direction while the deterministic arbiter said uncertain. The fix was architectural: the LLM narrates and routes, but verdict numbers and levels come only from Python rules, and disagreements are exposed as flags instead of being smoothed over.

Near-tied candidates break naive confidence. A 0.91 top score looks great until you see the 0.9092 right under it. Single-score confidence is meaningless there. The answer was arbitration at a different granularity: normalize the Top-1 against the whole candidate pool (entity share 6.9% — honest signal that one compound cannot be locked) and separately measure family convergence (100% — the direction can).

Encoding bench knowledge as rules, not prompts. Which functional groups a family must show, which bands forbid a candidate, which orthogonal method separates family members — these came from 20 years of lab work and live as auditable Python rules, because a prompt cannot be audited and a rule can.

LLM output robustness. Qwen occasionally wraps JSON in markdown. Instead of stricter prompting (which degrades reasoning), a self-repair loop feeds the parse error back with context.


Accomplishments that I'm proud of

  • The determinism contract holds in practice: three consecutive live runs on the same spectrum return byte-identical verdict numbers, with the full audit trail to prove it.
  • The agent knows three different things to say — GREEN, YELLOW with a next-step lab plan, RED with an honest refusal — and real samples exercise all of them.
  • Every peak assignment shown to the user is traceable to literature (DOIs returned by the knowledge-graph layer of the platform API).
  • It runs in public, on the real library, today: https://chemspectra.ftir.fun
  • A domain expert built this, not a software engineer. The arbitration thresholds, forbidden-group rules and verification plans are the part no generic agent template can reproduce.

What I learned

Verification beats accuracy claims: a deterministic rule engine is easier to trust than another LLM round, and exposing conflicts earns more credibility than hiding them. Let the model choose the route but never the verdict — my first version hardcoded the tool sequence and it was a pipeline, not an agent; the current one lets Qwen route by intent while the host gate guarantees the evidence floor. And the best AI applications come from domain experts who learn to code, not the other way around.


What's next

  • Multi-round hypothesis-driven investigation: a session that accepts multiple spectra of one case (as-received, extract, ash), keeps a hypothesis state machine (confirmed / revised / rejected / uncertain), and proposes the next experiment with an explicit decision rule each round — the production formulation-analysis workflow, generalized.
  • Surface more of the production platform's evidence axes (knowledge-graph and literature-retrieval scoring) through the agent.
  • Batch QC mode: run 50 overnight samples through the same deterministic arbitration and route only the YELLOW/RED ones to the analyst.

Built With

  • alibaba-cloud-dashscope-(model-studio)
  • custom-react-agent
  • fastapi
  • function-calling
  • mcp
  • python
  • qwen3.7-max
  • vanilla
Share this project:

Updates