Skip to content

43 · Reply safety

With replies: {rules: ...} set, every turn a person sends is screened for personal data before the router or the model reads it, and every reply the model writes is screened again before it is shown, whether or not an action came before it. Your rules say what each class of personal data gets, which phrases are never said, which offers a reply may make and which sentences must go with a write. No model judges any of it: the runtime on your machine decides each rule.

Language support

The selector understands turns written in English. Portuguese is its next grammar, and has not shipped yet; until it does, a turn written in Portuguese is not read as Portuguese.

Personal data

The classes the runtime finds, how each is found, what it gets where your rules say nothing, and how it is masked, generated from the runtime's detectors:

Class How it is found Default Masked as
email an address of the form name@domain allow j***@example.com
iban an account number of the length its country gives, whose check digits hold redact GB** **** 2681
national_id a US social security number, a UK national insurance number, or a Brazilian CPF whose check digits hold redact ***6789
card 13 to 19 digits whose checksum (Luhn) holds: a number that fails it is left as it is redact **** 1111
phone an international number of 8 to 15 digits, or a national one of 10 or 11 allow ***0123

A card number is found by its checksum, not by its shape: sixteen digits that fail the Luhn check are left as they are, because they are not a card number. Each class takes one of three actions:

Action In a turn sent to the model In a reply the model wrote
redact masked before the router and the model read the turn masked in the sentence
withhold the whole turn is held back: nothing reads it, and the person is asked to send it again without it the sentence is removed
allow left as it is left as it is

A value an outcome sentence may repeat. A sentence reporting a write may name a value the write was sent -- the card a refund went to -- and that value is masked in it rather than removed, whatever the class's action, unless the class is allowed.

Wording

  • never_say: phrases never said. A sentence holding one, whole words and any case, is removed.
  • offers: the offers a reply may make. A sentence holding one of the markers (such as discount or voucher) and none of the allowed offers is removed.
  • must_accompany: sentences that must go with something: a disclaimer added when a tool's call ran in this turn (when_done), or when an answer cites a passage with a tag (when_cited). It is added once, where the reply does not already say it.

The reply rules file

The rules are one file, YAML or JSON, named in agent.yaml as replies: {rules: reply-rules.yaml} or in Python as Agent(..., replies={"rules": "reply-rules.yaml"}). The shop's:

# The shop's reply rules, as the console's Reply rules page saves them.
personal_data:
  card: redact            # the default, written out
  national_id: withhold   # a turn holding one reaches no model; a reply holding one loses the sentence
  email: allow
never_say:
  - "guaranteed refund"
must_accompany:
  - when_done: refund_card
    add: "Refunds reach the card in three to five working days."

Every key the runtime reads, generated from the runtime:

Key What it says
personal_data for each class of personal data, redact, withhold or allow
must_accompany sentences that must go with a write done or a passage cited
must_accompany[].add the sentence added
must_accompany[].when_done added when this tool's call ran in this turn
must_accompany[].when_cited added when an answer cites a passage with this tag
never_say phrases never to say: a sentence holding one is removed
offers the offers a reply may make
offers.allowed the offers allowed
offers.markers the words that make a sentence an offer: one with a marker and no allowed offer is removed

The same rules are kept on the console's Reply rules page, as a form over the same file, and at /v1/reply-rules, in versions. A file the runtime cannot read -- a class it does not know, an action that is not one of the three -- is refused with the key that is wrong.

The shop's replies, screened

The model below stands in for yours, and records what it was given:

SEEN = []          # what the model was given, turn by turn

# Stands in for your model: what it is asked, and the reply it writes.
SCRIPT = {
    "zork": (None, ""),
    "my card is **** 1111, is it on file?": (
        None,
        "Your card 4111 1111 1111 1111 is on file; the card 4111 1111 1111 1112 is not."),
    "the parcel for 4412 vanished": (
        {"name": "refund_card", "arguments": {"order_id": "4412", "amount": 12.50}},
        "I've refunded 12.50 pounds for order 4412. It is a guaranteed refund."),
}


def model(conversation):
    turn = [m for m in conversation if m["role"] == "user"][-1]["content"]
    SEEN.append(turn)
    call, reply = SCRIPT[turn]
    if conversation[-1]["role"] == "user" and call:
        return {"content": "", "tool_calls": [call]}
    return reply
desk = Agent("desk", tools=[get_order, refund_card], model=model, on_unread="model",
             replies={"rules": "reply-rules.yaml"})

for text in ("zork",
             "my card is 4111 1111 1111 1111, is it on file?",
             "my social security number is 123-45-6789",
             "the parcel for 4412 vanished"):
    reply = desk.chat(text, user="u-17")
    if reply.waiting:
        reply = desk.confirm("u-17", "approve")
    print(f"{text}\n  -> {reply.text}")

print("The model was given:", SEEN)
zork
  -> I didn't recognise anything in that. I can get order or refund card. Which do you mean?
my card is 4111 1111 1111 1111, is it on file?
  -> Your card **** 1111 is on file; the card 4111 1111 1111 1112 is not.
my social security number is 123-45-6789
  -> That message held details I can't take here, so I haven't read it or done anything. Please send it again without them.
the parcel for 4412 vanished
  -> I've refunded 12.50 pounds for order 4412. Refunds reach the card in three to five working days.
The model was given: ['my card is **** 1111, is it on file?', 'the parcel for 4412 vanished', 'the parcel for 4412 vanished']
  • The card number the person typed was masked before anything read the turn: the model was given **** 1111.
  • The model's reply named the same valid card, masked, and a sixteen-digit number that fails the checksum, left as it is.
  • The turn with a social security number was held back whole, under national_id: withhold: nothing read it, and the person was asked to send it again without it. It is not in what the model was given.
  • After the refund ran, It is a guaranteed refund. was removed (never_say), and the disclaimer the rules require for a refund was added.

The counts

Each check is counted, by the meter it is recorded under, with your licence's other meters. A count is a number: it carries no text, no value and no turn.

Count What it counts
reply_pd_detected_<class> a value of that class of personal data found
reply_pd_masked a value a write was sent, repeated in its own outcome sentence, masked
reply_pd_withheld a turn held back, or a sentence removed, for a value the rules withhold
reply_pd_redacted a value masked because the rules redact its class
reply_assertion_refused a sentence saying a write was done that nothing in the session attempted, replaced
reply_outcome_replaced a sentence its action's outcome does not allow, replaced by the phrasing
reply_value_replaced a sentence stating a value its action was neither sent nor returned, replaced
reply_outcome_added a write of this turn the reply left out, added from its phrasing
reply_wording_removed a sentence holding a phrase never to say, removed
reply_offer_removed a sentence making an offer outside the allowed ones, removed
reply_wording_added a sentence the rules require, added

The limits

  • A detector finds the classes it names, and no others. A name, a postal address, a date of birth or an identifier of another country is not detected, and is left as it is.
  • A detector finds a value written as the detector reads it. A card number spelt out in words, or split across two messages, is not found.
  • No model judges a reply. A rule matches the words it is given: a phrase said in other words is not a phrase never_say lists.
  • The screen holds the turns the agent reads and the replies its model writes. A reply no model wrote -- a fixed question, a refusal, a phrasing -- is the product's own wording.