How feedback gets routed by mood
A reply came back — a tapped star or a few typed words. Now the router has to turn it into one of three buckets: happy, unhappy, or unclear. Get this wrong and you nudge a frustrated customer toward a public review, or you bury a delighted one who would have left you five stars. A star tap is settled by plain rules. Free text gets a quick mood read from a model. Four small steps sit between the raw reply and the bucket.
Key takeaways
- A star tap is read by plain Python: 4–5 is happy, 1–2 is unhappy, 3 is unclear. No model.
- Free text gets one short Bedrock Haiku 4.5 read that returns a mood and a confidence score.
- A low-confidence read or a mixed reply is routed to unclear, not guessed.
- The bucket and the reason are written to the log, so any routing decision can be checked later.
- Every gate is a deterministic check or one cheap model call — no surprises.
Four steps to a bucket
Step 1: tap or text
The first thing the router checks is what kind of reply it’s holding. A star tap arrives as a number from one to five — clean, unambiguous, no reading required. Free text arrives as a sentence or two. The two need completely different handling, so the router splits the path right away. In practice most replies are taps, because tapping a star is the easiest thing in the world and the ask is built to make it a single touch. That matters for cost: the cheap, no-model path handles the bulk of the traffic, and the model only runs on the minority who actually write something.
Step 2: score the stars
For a tap, the rule is fixed and lives in the rules doc so you can change it without a deploy: four or five stars is happy, one or two is unhappy, three is unclear. That’s it. A three is genuinely ambiguous — it’s the customer who shrugged — so rather than guess, the system treats it as unclear and lets a human glance at it. There is no model call on this path; a star is a number and a number maps to a bucket. The vast majority of replies settle here, in microseconds, for no model cost at all.
Why put the thresholds in the doc? Because different businesses feel differently about a four. A fine-dining restaurant might decide a four isn’t a public-review-worthy “wow” and route fours to unclear; a busy takeaway might be thrilled with any four or five. Moving the line is a one-word edit in the rules doc, not a code change.
Step 3: read the words
Free text is where a model earns its place. “It was lovely, thank you” is clearly happy. “Waited 40 minutes and the food was cold” is clearly unhappy. But plenty of replies are slippery — “it was fine,” “could’ve been better,” “the food was great but the service was slow.” Simple keyword rules fall over on these. So for free text only, the router makes one short call to Bedrock Haiku 4.5.
The prompt is tight: “Read this customer’s reply. Return JSON only: a mood of happy, unhappy, or unclear, and a confidence from 0 to 1. If the reply is mixed, sarcastic, or too short to be sure, return unclear. Do not invent praise or complaints that aren’t there.” The reply text is a sentence or two, so the call is a few hundred tokens in and a tiny bit out — a fraction of a cent. Haiku is the right model here: it’s the cheap, fast path, and reading the mood of one short sentence is well within what it does reliably. The heavier Sonnet model is reserved for jobs that actually need deeper reasoning, which this isn’t.
Step 4: check confidence, then commit
The model returns a mood and a confidence. The last step is a guardrail: if the confidence is below the threshold set in the rules doc (default 0.7), the reply is forced to unclear regardless of which way the model leaned. The same goes for a three-star tap from Step 2. The reason is the asymmetry of the mistake. Routing an unhappy customer to a public review page is the one outcome the whole system exists to prevent; nudging an ambiguous reply to a human costs a few seconds of someone’s attention. When in doubt, the system spends the human’s seconds, not the customer’s goodwill.
Once the bucket is settled, the router writes it to the fc-feedback row along with the reason — “5-star tap,” “model: unhappy 0.92,” “model: unclear 0.55 → forced unclear.” That line is what makes any routing decision auditable later: if a customer ever says “I left you good feedback and you ignored me,” you can see exactly what came in and where it went.
Next post: what each bucket actually triggers — the gentle one-tap review nudge for happy, the immediate private ping to the owner for unhappy, and the quiet hold for unclear.
All posts