Skip to content

The support agent

thinkless.demo.support is a complete customer support agent for a fictional store, Cobalt Market. It exists to show the pattern on something realistic and to give the benchmarks a workload. Everything is in the package, so thinkless demo and thinkless bench support work right after installing.

What it handles

A ticket is a subject, a message and the signed-in customer id. The agent can issue refunds for duplicate charges, report order status, cancel memberships, answer questions from a small help center, and hand anything else to the right human queue. 53 labeled tickets cover every path, including the hard cases: order numbers written in ways a regex misses, a customer asking about someone else's order, prompt injection attempts, explicit requests for a person, signed-out users, and tickets in Spanish and German.

The flow

authenticate   rule        signed in?                        no: ask to sign in (template)
triage         decide      intent, urgency, churn_risk,      one batch through the cascade
                           wants_human, injection, order
act            rules/tools security, human handoff, then route by intent:
                             refund     order belongs to customer? duplicate in payments?
                                        under the automatic limit? then refund_payment
                             status     lookup_order
                             cancel     get_subscription, cancel_subscription
                             question   search_kb, then decide: does the article answer it?
                             other      create_ticket for the right queue
reply          generate    two to four sentences from the established facts
               rule        every amount and id in the reply is backed by the facts

The code is ordinary Python in agent.py. The decision plane is whatever engine the agent is given, which is what lets the benchmark run the same agent three ways.

Decisions versus rules

The agent draws a hard line between what a model may decide and what code decides:

Models decide Code decides
What the customer wants Whether the order belongs to the customer
How urgent it is, whether they may leave Whether a duplicate charge exists in the payment records
Whether they ask for a person Whether the amount is under the automatic refund limit
Whether the message tries to instruct the system Whether the reply mentions only amounts and ids backed by facts
Which order they mean
Whether a help article answers the question

The models never authorize anything. A model saying refund_duplicate_charge only selects the refund workflow; the refund itself happens only when the payment records show a duplicate.

Handling uncertainty

  • An intent below threshold goes to a person (escalate_triage) rather than a guess.
  • Only a confident injection answer routes to the security queue. An unsure answer that leans yes still fails closed: the ticket's priority rises, the trace records injection_suspected, and automatic refunds are blocked.
  • Urgency and churn risk only set priority, so their thresholds are low (0.2 and 0.5).

Thresholds

Routing questions use per-provider thresholds calibrated on a separate labeled set; see calibration for the method and the numbers.

Running it

thinkless demo --ticket T-001                    # hybrid mode, local models
thinkless demo --ticket T-051 --mode hybrid --mode llm
thinkless demo --message "My parcel 7730 is late" --customer C-1002 --view

--view writes the HTML trace viewer and opens it.

From Python:

from thinkless.demo.support import SupportAgent, SupportStack, World, load_scenarios
from thinkless.llm import from_spec

stack = SupportStack(from_spec("local"))
agent = SupportAgent(stack.engine("hybrid"))
ticket = load_scenarios()[0]
outcome, summary = agent.handle(ticket, World())
print(outcome.action, outcome.reply)
print(summary.llm_calls, summary.decisions_by_plane)

Adapting it

To build your own agent from it, keep the structure and replace the parts: the questions in questions.py, the tools in world.py, and the routing in agent.py. Then write a calibration set for your questions before choosing thresholds.