Part 4 of 7 · Feedback collector series ~5 min read

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

Four steps between a raw reply and the happy, unhappy, or unclear bucket A horizontal flow diagram. On the far left, a "Reply in" box: the router received either a star tap or a few words of free text. Four step gates sit in a row to the right, each drawn as a vertical bar. Gate 1: Tap or text — splits the path; a star tap goes to the plain-rule scorer, free text goes to the model reader. Gate 2: Score the stars — plain Python; four or five stars is happy, one or two is unhappy, three is unclear; no model call. Gate 3: Read the words — for free text only, one short Bedrock Haiku 4.5 call returns a mood label (happy, unhappy, or unclear) and a confidence score; the prompt asks it to flag mixed or sarcastic replies as unclear rather than guess. Gate 4: Check confidence — if the model's confidence is below the threshold in the rules doc, or the stars were three, the reply is forced to unclear so a human decides; otherwise the bucket stands. After all four gates, the reply lands in exactly one bucket: happy, unhappy, or unclear, and the bucket plus the reason is written to the fc-feedback table. A note at the bottom: a mixed or low-confidence reply becomes unclear, never a guess — the system would rather ask a human than misroute. Reply in star tap or a few typed words Step 1 Tap or text? tap → rules text → model split the path most replies are taps Step 2 Score the stars plain Python 4–5 happy 1–2 unhappy 3 unclear no model Step 3 Read the words free text only Haiku 4.5 read mood label + confidence flags mixed ones Step 4 Check confidence below the threshold? force to unclear — a human decides Bucket — happy, unhappy, or unclear happy → review nudge · unhappy → owner · unclear → human bucket and reason written to DDB fc-feedback — auditable later A mixed or low-confidence reply becomes unclear, never a guess — the system would rather ask a human.
Fig 4. Four steps between a raw reply and a bucket. Split tap from text. Score the stars by rule. Read the words with one cheap model call. Check confidence and force the doubtful ones to unclear. Then write the bucket and the reason to the log.

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