Why a sales agent should refuse to answer
A price, a delivery commitment or a warranty that Knowledge does not support is refused and passed to a person rather than improvised. safe_sales treats that refusal as the correct outcome rather than a gap to close, because an invented commercial term binds a business to something nobody agreed. On the realtime voice path the rule can only live in the instructions, so a breach is detected rather than prevented.
The questions that create an obligation#
Most of what a customer asks is a question about facts, and an agent grounded in a workspace's own Knowledge answers those well. A small set is different in kind: the answer is not information, it is a commitment, and the business is bound by it whoever typed the sentence.
- Price, discount and terms — what something costs, what can be taken off, what the payment terms are.
- Time and service commitments — how fast, by when, what happens if it slips.
- Warranty and liability — what is covered, for how long, and who bears a loss.
The failure is not that a model gets these wrong at random. It is that a model asked a direct commercial question produces a fluent, specific, plausible answer — which is exactly the kind of answer a customer quotes back later.
Refusing is the behaviour, not the bug#
safe_sales.py refuses a commercial term Knowledge does not support and escalates it. That reads as a limitation until you price the alternative: a single improvised discount on a live call is worth more than the entire efficiency gain of automating the conversation, and a customer who was told a delivery date is entitled to it.
This is why the refusal is written down as correct rather than logged as unhandled. A gap invites somebody to close it — by adding a fallback price, by widening what counts as supported. Naming it as the designed outcome changes the review question from *why did it refuse* to *why is this not in Knowledge yet*, which is the question with a useful answer.
Prevented on one path, detected on the other#
The carrier voice path and email both run each generated sentence through the check before anything leaves, so a priced sentence never reaches the customer. The realtime path is different by construction: the model speaks directly to the caller, and nothing sits between its output and the line.
| Path | Mechanism | Guarantee |
|---|---|---|
| Email and turn-based voice | Checked before the sentence is sent or spoken | Prevention |
| Realtime voice | The rule lives in the instructions; price_spoken counts the lines the voice priced | Detection after the fact |
price_spoken deliberately uses the same expression the carrier path checks each sentence with, so the two paths agree on what a price is. Without that, the detector and the preventer would drift apart and the realtime numbers would mean something slightly different from the email ones — which is worse than not measuring.
Keeping the deal alive while refusing#
The agent declines to state the term and says a person will confirm it.
Result The customer has an answer that is true, and no commitment has been created.
The opportunity is raised for review (
raise_for_review) with the question attached.Result It appears for a person with the context around it rather than as a bare alert, and
clear_reviewcloses it when the answer exists.A follow-up is created with its reason, on the channel that suits the answer.
Result The commitment to come back is itself recorded, so the refusal does not become silence.
The Assistant's rights are narrower than a person's for the same reason: it cannot set pricing, and it cannot clear a do-not-contact entry. Two capabilities are operator-only because they are the business of running the platform rather than using it — verifying customer payments, and JBRH's own pricing.
Where this is still weak#
- A refusal that sounds like a stonewall costs the deal anyway. The wording is part of the mechanism, not decoration around it.
- Knowledge that is out of date produces confident wrong answers that the rule does not catch, because the term *is* supported — by a stale source.
- The realtime detector counts priced lines; it cannot count the ones that were correctly refused, so the rate on live calls is UNKNOWN here.
- A customer can extract a commitment by implication — *so that would be within the week?* — which reads as agreement rather than as a stated term.
Questions#
Why not let the agent quote a price if the price is in Knowledge?
It does. The rule is about terms Knowledge does not support, not about secrecy. The distinction is what makes it workable: a workspace that puts its published terms into Knowledge gets an agent that answers them, and one that has not gets an agent that says a person will confirm.
Is a refusal recorded anywhere?
Yes. The audit trail records what was decided, by what, under which rule, and what happened — refusals included, because a refusal is a decision. It is also the cheapest way to find out which questions your Knowledge is missing.
Can the rule be switched off for a trusted workspace?
The safer instrument is the autonomy mode. Holding replies for approval on the channel where commercial questions arrive gives a person the last word without removing the check, which is a different thing from letting an agent improvise terms.