Why your agent asks for the order number it already has
The customer clicked Chat about this order on the order page. The widget passed the order number in. The agent’s first message asked for it. Nobody wrote a bug; two people wrote two correct lines a year apart, and the older one is an instruction.
TL;DR An agent re-asks for a fact the host already passed in when an unconditional ask rule sits beside that fact, so make “does the host know it” a typed input and let the build keep only the rule that applies.
Re-asking is the small failure customers notice most, because it tells them the system is not listening. In a chat that opened from an order page, it is also the first thing the agent says. It is rarely the model being forgetful. The prompt contains the answer and an order to ask for it, and the order wins.
§1A rule from before the widget knew who you were
Linden & Vale sells homeware online. When its chat assistant launched, chat lived on the help page and every conversation started anonymous, so the first rule anyone wrote was the sensible one: get the order number before anything else. A year later the platform team added a Chat about this order button to the order page and a line to the prompt that the host fills in. Nobody touched the rules.
# Role You are the chat assistant for Linden & Vale, an online homeware store. The customer's order number is {{order_number}}. # Rules - ALWAYS ask the customer for their order number first. - WHEN the customer asks about an order THEN MUST call @[lookup_order]. - MUST answer in at most 3 sentences. $TOOL lookup_order - description:: Find an order and its delivery status. - input:: - order_number:: the order to look up.
$ typeglish check before.tg ✓ 1 file — 0 error, 0 warning, 0 info $ typeglish score before.tg before.tg — B (86/100) proven errors: none tiers: base+z3 planes runtime 99 (what the model reads) · hygiene 50 (source only) facets enforceability 100 x.21 · hardness 100 x.12 · directness 88 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 100 x.08 · security 100 x.08 lever annotation 0/100 (up to +12 overall) — 5 statements without a @@ note (L2 L3 L6 L7 L8) — put a "@@ why" line directly above each rules 3 of 3 rule-shaped lines read $ typeglish build before.tg && cat .typeglish/dist/before.txt ✓ built .typeglish/dist/before.txt ← before.tg (8587a24030b4, full) # Role You are the chat assistant for Linden & Vale, an online homeware store. The customer's order number is {{order_number}}. # Rules - ALWAYS ask the customer for their order number first. - WHEN the customer asks about an order THEN MUST call lookup_order. - MUST answer in at most 3 sentences.
{{order_number}} slot is not TypeGlish syntax, so it passes through as text for the host to fill. The checker has no way to know that the slot and line 6 are about the same number.Run it forward with the slot filled. The customer opened chat from order LV-48213, so the model reads The customer’s order number is LV-48213, and then, two lines later, an instruction to ask for it:
Customer: Hi, where is this? It said Thursday.
Agent: Happy to help! Could you share your order number so I can
look into it?
Customer: I clicked chat ON the order. It is right there.
§2A rule beats a fact
The two lines are different kinds of thing. Line 3 is a declaration: it states what is true. Line 6 is a directive: it says what to do, with ALWAYS in front. Nothing in the prompt connects them. No line says if you already have the order number, do not ask for it, because when line 6 was written the agent never had it.
So the model has to infer that the fact settles the rule, and against an explicit always that inference loses often enough to show up in your transcripts. When it guesses the other way and skips the ask, it is breaking a rule you wrote. An agent that sometimes asks and sometimes does not is the worst of the three outcomes, and it is what this prompt is built to produce.
There is a second path to the same failure. When chat still opens from the help page, the host has no order to fill in. Depending on the platform, the model reads The customer’s order number is . or the raw {{order_number}}. One line is serving two entry points, and in one of them it is a sentence with a hole in it.
A fact never cancels a rule. Only a condition does.
§3The ban that fires, and the one that does not
The usual fix arrives after the first complaint: a ban, appended at the bottom.
# Role You are the chat assistant for Linden & Vale, an online homeware store. The customer's order number is {{order_number}}. # Rules - ALWAYS ask the customer for their order number first. - WHEN the customer asks about an order THEN MUST call @[lookup_order]. - MUST answer in at most 3 sentences. # Added after the complaints - NEVER ask the customer for their order number. $TOOL lookup_order - description:: Find an order and its delivery status. - input:: - order_number:: the order to look up.
$ typeglish check ban.tg ✓ 1 file — 0 error, 0 warning, 0 info $ diff ban.tg ban2.tg 6c6 < - ALWAYS ask the customer for their order number first. --- > - ALWAYS ask the customer for their order number. $ typeglish check ban2.tg ban2.tg:6:1 error logic/contradiction Conflicts with line 11. Logical conflict — "ask the customer for their order number" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 11: - NEVER ask the customer for their order number. ban2.tg:11:1 error logic/contradiction Conflicts with line 6. Logical conflict — "ask the customer for their order number" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 6: - ALWAYS ask the customer for their order number. ✗ 1 file — 2 error, 0 warning, 0 info
The clean result is the dangerous one. The model now reads always ask first and never ask in the same prompt, and settles it one chat at a time. (Teardown: the call-recording block from compliance found the same thing with again: a trailing word changes the phrase the checker compares.) The refusal is the honest outcome, and the checker’s message names the real fix: scope the two rules so they cannot both apply.
The obvious way to scope them is in prose. Guard only the ask, and the checker still refuses, because an unguarded NEVER covers the guarded case too:
$ tail -2 missing.tg - WHEN the order number is missing THEN MUST ask the customer for their order number. - NEVER ask the customer for their order number. $ typeglish check missing.tg missing.tg:5:1 error logic/contradiction Conflicts with line 6. Logical conflict — "ask the customer for their order number" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 6: - NEVER ask the customer for their order number. missing.tg:6:1 error logic/contradiction Conflicts with line 5. Logical conflict — "ask the customer for their order number" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...). ↳ line 5: - WHEN the order number is missing THEN MUST ask the customer for their order number. ✗ 1 file — 2 error, 0 warning, 0 info $ tail -2 known.tg - WHEN the order number is missing THEN MUST ask the customer for their order number. - WHEN the order number is known THEN NEVER ask the customer for their order number. $ typeglish check known.tg known.tg:6:1 info prompt/agentless-passive The doer is deleted (agentless passive) - who performs this? Name the actor or use the imperative: "Verify the order." instead of "The order is verified." ✓ 1 file — 0 error, 0 warning, 1 info
But look at who decides missing and known. The model does, at runtime, by reading a sentence that may say LV-48213, may say nothing, or may say {{order_number}}. The host knew the answer before the conversation started. It clicked the button. Asking the model to work it out again is the same mistake one level down.
§4Let the host say what it knows, and the build pick the rule
Whether the order is known is not something to infer. It is a fact the host has, as a yes or a no, the moment the chat opens. So declare it as a typed input and branch on it with a $IF chain. A deterministic arm is resolved by the compiler: bind the variable at build time and the losing arm never reaches the model.
$REQUIRE variable order_known: boolean $REQUIRE variable order_number: string # Role @@ role: the desk this file speaks for You are the chat assistant for Linden & Vale, an online homeware store. # Rules @@ order_source: the order page passes the order in; only the help page opens a chat without one $IF @{order_known}: - NEVER ask the customer for their order number. - MUST call @[lookup_order] with order number @{order_number}. $ELSE: - MUST ask the customer for their order number. @@ status_needs_tool: never guess where a parcel is - WHEN the customer asks about an order THEN MUST call @[lookup_order]. @@ brevity: a chat bubble, not an essay - MUST answer in at most 3 sentences. $TOOL lookup_order - description:: Find an order and its delivery status. - input:: - order_number:: the order to look up. $TEST where_is_it - input:: Where is my order? It said Thursday. - expect:: - calls lookup_order - at most 3 sentences $TEST no_reask - input:: I am on the order page, it is right there. - expect:: - does not ask the customer for their order number - matches /^(?!.*[Oo]rder number\?)/
$ typeglish check fixed.tg ✓ 1 file — 0 error, 0 warning, 0 info $ typeglish score fixed.tg fixed.tg — A (99/100) proven errors: none tiers: base+z3 planes runtime 99 (what the model reads) · hygiene 100 (source only) facets enforceability 100 x.21 · hardness 100 x.12 · directness 88 x.08 · consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08 lever directness 88/100 (up to +1 overall) — Use the imperative or a named doer ("Verify the order.", "You must ..."), keep the active voice (not "is verified" / "a review must take place"), and make every it/this point at something the prompt introduced. rules 5 of 5 rule-shaped lines read · 5/5 covered by $TEST $ typeglish test fixed.tg --dry ✓ fixed.tg coverage: 5/5 rules exercised · where_is_it — "Where is my order? It said Thursday." (not run) ✓ at most 3 sentences · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY) · no_reask — "I am on the order page, it is right there." (not run) ✓ matches /^(?!.*[Oo]rder number\?)/ · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY) ✓ 1 prompt — 0 failed $ typeglish build fixed.tg --vars '{"order_known":true}' && cat .typeglish/dist/fixed.txt ✓ built .typeglish/dist/fixed.txt ← fixed.tg (b1a6459dfe61, full) # Role You are the chat assistant for Linden & Vale, an online homeware store. # Rules - NEVER ask the customer for their order number. - MUST call lookup_order with order number {order_number}. - WHEN the customer asks about an order THEN MUST call lookup_order. - MUST answer in at most 3 sentences. $ typeglish build fixed.tg --vars '{"order_known":false}' && cat .typeglish/dist/fixed.txt ✓ built .typeglish/dist/fixed.txt ← fixed.tg (18d51942916e, full) # Role You are the chat assistant for Linden & Vale, an online homeware store. # Rules - MUST ask the customer for their order number. - WHEN the customer asks about an order THEN MUST call lookup_order. - MUST answer in at most 3 sentences.
{order_number} slot in the first artifact, for the host to fill per chat.Wire it the way the host already works: the order-page button loads the first artifact and fills the slot, the help-page launcher loads the second. If you ever add a third entry point, say a link from the shipping email, it gets its own build, and you decide on purpose which rule it carries instead of discovering it from a transcript. The no_reask test is the line to keep forever: matches /^(?!.*[Oo]rder number\?)/ runs offline and fails the moment a reply asks for the number with a question mark. For the bigger version of this idea, Your system prompt has a compile time sorts every fact in a prompt by when it is known.
§5Common questions
- Why does my AI support agent ask for an order number the customer already gave?
- Usually because an unconditional rule like
ALWAYS ask the customer for their order numberwas written before the chat widget passed the order in. The order number arrives as a fact, the rule arrives as an instruction, and nothing in the prompt says the fact settles the instruction, so the model obeys the rule. - How do I stop my chatbot asking for information it already has?
- Make whether the host knows the fact an input, and branch on it at build time:
$REQUIRE variable order_known: boolean, then$IF @{order_known}withNEVER askin one arm andMUST askin the other. Build once per entry point, and each artifact carries only the rule that applies. - Can I just add NEVER ask for the order number to my prompt?
- Not on its own. Beside the old always-ask rule it is either a contradiction the checker refuses to build, or, if the old rule ends in a word like first, two rules the checker does not compare and the model has to referee. The ask and the ban need a condition between them.
- What happens if the host leaves the order number placeholder empty?
- The model reads a sentence with a hole in it, or the raw placeholder, and has to guess whether it knows the order. A boolean input the host sets on purpose, and a build per value, means an empty slot never reaches a prompt that promised not to ask.
Re-asking has more causes than the order number. Search your prompt for every ask the customer for and, for each one, list the entry points that already know the answer: a signed-in session knows the email, a logged-in app knows the account, a link from a shipping email knows the order. Each of those is a boolean the host can pass and a $IF arm the build can resolve. If the list is long, a $SWITCH ON over a typed entry_point input does the same job with one arm per launcher, and Bake-off: prose conditionals vs $SWITCH for multichannel agents shows why the compiler, not the model, should own that choice.