Restaurants & Food Service// diagnostic

Three modifiers in one breath and only two reach the ticket

In short

When a voice ordering agent drops modifiers on a complex order, transcription is usually not the cause. Diff 4 points — transcript, held order, POS payload, accepted order — score it per modifier rather than per order, and the pattern of which one goes missing names the layer: a catalog gap, a wrong parent, an overwritten line object, or a group rule silently rejecting the extra.

Key takeaways

  • Score per modifier, not per order. Order-level accuracy hides the fact that failure rises with modifier count.
  • Diff 4 points, not 2: transcript, held order, POS payload, accepted order. Most teams compare the ends and cannot locate the hop.
  • If accuracy is flat across modifier counts, the loss is per-modifier — catalog or transcription. If it falls as the count rises, it is accumulation.
  • Deterministic loss past the nth modifier is a modifier-group maximum being normalised away on injection, not a model problem.
  • Read-back catches a dropped modifier only when the drop happened before the read-back. Anything lost on injection is invisible to it.

The recording contains 3 customisations and the printed chit carries 2. The instinct is to blame transcription, and in most builds transcription is the last cause on the list rather than the first. The words are usually in the transcript. Something between the transcript and the kitchen decided not to keep them.

There are 5 candidate layers, they leave different fingerprints, and the fingerprint is visible in a diff you can build in an afternoon. Guessing between them by listening to calls does not work, because every cause sounds identical on the recording: the guest asked for 3 things and got 2.

Build the diff at four points, not two

The usual investigation compares the recording with the printed ticket. That tells you a modifier was lost and nothing about where. Modifiers pass through 4 representations, and each hop can drop one for a different reason.

  1. Capture the transcript, with timings. This is what the agent heard, before any menu logic ran.
  2. Capture the held order at the moment of read-back. The structured object the agent believed it had, item by item, modifier by modifier.
  3. Capture the payload sent to the POS. Not a log line saying 'order sent' — the actual body, serialised.
  4. Capture the order the POS returns after acceptance. Platforms normalise on write, and the difference between what you sent and what came back is where the quietest losses live.
  5. Sample at least 50 recent orders that carried a modifier, and bucket them by how many the utterance contained: 1, 2, 3, or 4 and above.

What the curve tells you before anything else

If per-modifier accuracy is roughly flat from 1 modifier to 4, each modifier is failing independently. That points at the catalog or at transcription — a per-item property, not a per-order one. If accuracy falls as the count rises, the loss is in accumulation: the 3rd modifier fails because the 1st and 2nd exist. That is state or a group rule, and no amount of transcription work will move it.

The five causes, ranked, and the signature of each

Signature in the diffLayerConfirming check
The same modifier, on the same item, every single timeCatalog: the option does not exist under that itemQuery the item's modifier groups directly, by name and by alias
The modifier appears, attached to the wrong lineState: item scoping — applied to the current line, not the referenced oneFilter for orders containing 2 of the same item; the rate should jump sharply
Only the last survives, or only the firstState: the line object is replaced rather than merged on each turnReplay one call's turns against the order builder with transcription removed
Everything past the nth is dropped, deterministicallyCatalog rules: a modifier-group maximum, normalised away on injectionCompare the payload you sent against the order the POS returned
Which one is lost varies, and it worsens by hourTranscription: the words are not in the transcript at allSplit per-modifier accuracy by hour of day and by line
Which modifier goes missing tells you which layer dropped it

The 4th row is the one teams find last, because nothing errors. A modifier group with a maximum of 2 will often accept a payload carrying 3 and return an order carrying 2, with a success status. If you only log the request and the status code, that loss is invisible until a guest complains. This is also why the accepted order, not the payload, is the authoritative fourth capture point.

The 5th row is real but rarer than expected, and it has its own diagnosis. If accuracy is fine at 15:00 and collapses at 19:00, you are looking at an audio problem rather than a modifier problem, and the three noise causes that look identical in a transcript are separated in accurate at three in the afternoon, useless at seven.

The two catalog cases people mistake for each other

A modifier that does not exist for an item and a modifier that exists only under a different parent produce almost the same symptom, and they need different fixes. The first is a menu-modelling gap: the option was never attached to the item, so no retrieval can find it. The second is worse, because the option is findable — under the wrong parent — and an agent that resolves it there will attach 'extra cheese' to the fries.

There is a third catalog shape that cannot express the request at all. 'Half pepperoni, half veggie' needs a portion-scoped modifier, and an item that has no concept of halves will accept the words and produce a whole pizza — the failure traced in half-and-half orders arriving as whole ones. No prompt engineering fixes an order shape the item cannot hold.

The words were in the transcript. Something between the transcript and the pass decided they did not fit, and returned a success.

The state cases: overwrite, and wrong scope

Two state bugs account for most count-dependent loss. The first is replacement rather than merge: each turn rebuilds the line from the current utterance, so 'no onions' followed by 'and extra cheese' produces a line with extra cheese and onions. The signature is that the surviving modifier is always the last one spoken.

The second is scope. 'No onions on the second one' requires the agent to hold a reference to a line it is not currently editing. Builds that keep only a pointer to the current line silently apply the change to the wrong burger. The same merge failure appears on the floor rather than the phone when two servers work one check and a round goes missing — a shared object, two writers, last write wins.

The decision tree

  1. Is the modifier in the transcript? If no, and it correlates with hour, stop here: it is an audio problem, not an ordering one.
  2. Is it in the held order at read-back? If no, it is catalog or resolution. Query the item's modifier groups; if the option is absent, the fix is a catalog fix.
  3. If it is in the held order but not in the payload, the order builder dropped it. Check whether the surviving set is 'the last n' or 'the first n' — that names the merge direction.
  4. If it is in the payload but not in the accepted order, the platform rejected it silently. Read the modifier group's minimum and maximum, and log the returned order permanently.
  5. If it is in the accepted order but not on the chit, the failure is downstream of ordering entirely — printing, routing or the ticket template, not the agent.

The tree ends in exactly 3 kinds of change: a catalog fix, a state fix, or a read-back fix. Read-back only helps for losses that happen before it — the read-back that turns a spoken order into a ticket cannot catch a modifier the POS strips 200 milliseconds later, which is why teams that trust read-back as the whole safety net keep finding wrong tickets anyway.

The version of this bug that matters most

The same 4-point diff finds the highest-consequence case: an allergy note spoken clearly, present in the transcript, and absent from the ticket. That one usually fails for a schema reason rather than a modifier reason — there is no typed field for it, so it lands in free text that a downstream hop truncates. That is the allergy that was spoken and left no trace on the ticket, and it deserves a blocking rule of its own rather than a place in the general modifier pipeline.

The durable fix is instrumentation rather than tuning: persist all 4 representations for every order, and alert when the modifier count changes between any two of them. Comparing what an operational system believed against what it actually wrote is the general shape of this work — the same argument, applied to a different kind of floor, runs through AI in logistics operations.

Modifier loss is a catalog and state problem wearing a speech-recognition costume. The surrounding decisions sit in voice and phone ordering and across software for restaurants and food service; building and instrumenting the agent itself is AI agents and automation work.

Frequently asked questions

Short answers to the follow-ups this page tends to raise.

How many orders do we need to sample before the pattern is readable?

Around 50 orders carrying at least one modifier is usually enough to see which of the five signatures you have, because the causes produce qualitatively different patterns rather than small differences in a rate. You need more only when you are trying to measure improvement after a fix, where the comparison is between two similar numbers and the sample has to be large enough to tell them apart.

Would a better speech model fix dropped modifiers?

Only for the one cause in five where the words never reach the transcript. If the modifier is present in the transcript and absent from the ticket, the model already did its job and the loss happened in the catalog, the order builder or the POS write. Swapping models is an expensive way to discover that, which is why the diff comes first.

Should the agent refuse orders above a certain modifier count?

No — cap what the catalog cannot express, not what the agent finds hard. A hard refusal above 3 modifiers punishes exactly the guests who order most, and it hides the underlying defect rather than fixing it. Where a modifier group genuinely has a maximum, surface that as a spoken constraint during the call instead of letting the platform silently normalise it away afterwards.

Why does the ticket look right on screen and wrong on paper?

Because the chit is a separate rendering with its own field limits and its own template. A modifier can be present in the accepted order and still be cut from the printed line, either by character truncation or by a template that only prints priced modifiers. That is a printing defect, and it is diagnosed against the accepted order rather than against the recording.

  • voice ordering
  • modifiers
  • menu modelling
  • diagnostics
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Working on something in this space?

Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.

Start the conversation