Draft¶
What the model is asked at 2.1 draft_reply. One call, and only for items that reach it.
Rules for every prompt, including the ten-section anatomy and the cache boundary, are in architecture: prompts. This page is what fills those sections for Draft.
| Engine | Capture |
| Runs | Only when the chosen action needs generated content |
| Model | Better than Classify |
| Output | Prose, as plain text or light markdown |
| Improves via | Retrieval of approved examples, and the diff on edited drafts |
At a glance¶
| # | Section | Block | Cached | Filled with |
|---|---|---|---|---|
| 1 | Role | System | ✅ | Writes a reply for a human to approve. Does not send, does not decide |
| 2 | Vocabulary | System | ✅ | The tenant's voice: formality, sign-off, standing instructions |
| 3 | Tie-breaks | System | ✅ | Accuracy beats voice. The rule when they conflict |
| 4 | Escape hatch | System | ✅ | Say what is not known rather than inventing it |
| 5 | Output contract | System | ✅ | Body text only. No subject, no recipients, no HTML |
| 6 | Examples | System | ✅ | Approved drafts retrieved for this situation |
| 7 | Facts | User | ❌ | KNOWN and NOT KNOWN |
| 8 | State | User | ❌ | Last two messages in full, plus what we already said |
| 9 | Permitted actions | User | ❌ | n/a. The action is already decided |
| 10 | The content | User | ❌ | The message being replied to, masked |
The snippets below show shape, not the live text. The prompt itself is a versioned template in the brain's source.
Accuracy over voice¶
Three things Draft could optimise, and they conflict:
| Accurate | Says only true things |
| In their voice | Sounds like the person who normally writes |
| Complete | Answers everything they asked |
A safely accurate reply hedges everything and sounds nothing like a tradesman. A voice-perfect reply may confidently state something we do not know.
Accuracy wins, and the prompt says so explicitly.
A draft that sounds slightly generic gets approved with a small edit. A draft that promises a Tuesday callback nobody agreed to gets sent, and then someone rings the customer back to unpick it.
The system block¶
1. Role¶
You write email replies on behalf of {business_name}, a {business_type}.
A human reviews every draft before it is sent.
You do not send. You do not decide what happens next. You do not
choose the subject or the recipients.
Stating the negative matters here more than anywhere. Given an email, a model's instinct is to be maximally helpful: offering times, confirming things, adding next steps. Every one of those is a commitment we may not be able to keep.
2. Voice¶
Injected from tenant config (reply_voice).
Formality: first names, no "Dear"
Sign-off: "Cheers, {name}"
Length: short. Three or four sentences is normal
Standing rules: never quote a price. never confirm a technician by name
Standing rules are the useful part. "Never quote a price in an email" is a business rule the model can follow, and it is not something the retrieved examples would reliably teach.
3. Accuracy beats voice¶
If matching the customary tone would require stating something you
do not know, choose accuracy. A slightly stiff reply that is true
is better than a natural one that is wrong.
4. Say what is not known¶
If answering properly requires something in NOT KNOWN, say you will
confirm it and come back to them.
Do not estimate. Do not use a typical value. Do not say "usually"
about something specific to this customer.
"I'll confirm the arrival time and come back to you" is an honest draft. Inventing 9am is not, and 9am is what such an email usually says, which is exactly why the model will reach for it.
5. Output contract¶
Return the body text only, as plain text or light markdown.
No subject line. No greeting block beyond the salutation. No
signature. No HTML.
The subject and recipients come from the mail connector, and the signature and HTML wrapper are applied by the brain from the tenant's template (2.1). The model writes the body and nothing else.
6. Examples¶
Retrieved by the Remember engine via pgvector: past approved drafts for this tenant, similar to the current situation.
This is the only place in the product that uses Remember, and it is what makes a reply sound like them rather than like a language model.
An empty example set is expected, not a bug
Client-zero has no approved drafts on day one. Retrieval returns nothing and
Draft falls back to reply_voice alone.
The result is more generic prose, corrected by the approver, and those corrections seed the store. Worth knowing so nobody spends a day debugging why retrieval is empty.
Only approved drafts are retrieved, and where a human edited before approving, the edited version is stored. The unedited one is what they rejected.
The user block¶
7. KNOWN and NOT KNOWN¶
"Be accurate" is an instruction models agree to and then ignore. It needs a structure.
KNOWN:
customer: Preston Warehousing
job: J-4471, switchboard upgrade
job status: scheduled
scheduled date: Thursday 21 August
job link certainty: certain
NOT KNOWN:
who will attend
arrival time
whether the part has arrived
The NOT KNOWN block is the load-bearing part. Without it, a model asked about a Thursday job will write "our technician will be there Thursday morning", because that is what such an email contains.
| Rule | Guards against |
|---|---|
| Never state anything not in KNOWN | Invention |
| Never commit to a time, date or person unless it is in KNOWN | The failure that costs money |
Hedge when job_link_certain is false |
"Regarding your Preston job", not "regarding job 4471" |
NOT KNOWN is generated, not written. It is derived from what Resolve failed to find and what the job record leaves null, so it stays honest without anyone maintaining a list.
8. State¶
Draft needs the wording, where Classify needed only summaries.
| Age | Sent as |
|---|---|
| The message being replied to | Full |
| The two before it | Full |
| Older | One-line summaries |
| What we already sent | Full. So it does not repeat or contradict |
This is the expensive half of the split, and it is only paid on items that actually produce a reply. "We said someone would call Tuesday" has to be quoted accurately, not paraphrased.
Two facts change the framing:
| Fact | Effect |
|---|---|
was_chased |
Open by acknowledging the delay, not with a fresh greeting |
| Regenerated for staleness | Same |
human_response_reason |
The reason a human rejected the last attempt. Goes in verbatim |
That last one is why the regenerate reason is mandatory. "Too formal", "do not promise Tuesday", "they already know this" is what makes attempt two different from attempt one.
10. The content¶
The message being replied to, masked, with quoted history stripped.
Tokens, never plausible fake names. A failed replacement produces Dear [PERSON_1], which the approver catches, rather than Dear Robert, which reads as a normal email and gets sent to Sarah (2.1).
Scheduling¶
What the draft may say about times depends on scheduling_mode and on what the connector can actually answer (ADR-0035).
| Mode | The draft says |
|---|---|
none |
"Someone will call to arrange a time" |
propose |
"Thursday or Friday morning look open, subject to availability" |
hold |
"I have pencilled you in for Thursday 9am, held until Monday" |
Slots go into KNOWN, so proposing a time is not invention. And the draft may only claim what the answer supports:
| The connector answered | The draft may say |
|---|---|
| Slots with a named, skilled resource | "Dave is free Thursday morning" |
| Slots, no resource attribution | "Thursday morning looks open", nothing about who |
| Nothing | Times sit in NOT KNOWN. No time is offered |
That middle row is the one to get right. A system with a single shared calendar can say a slot is free and cannot say anyone qualified is free, so the draft must not imply one.
Proposed times go stale faster than the rest of a draft
A draft written Monday saying "Thursday morning looks open", approved Thursday, may be offering a slot that filled on Tuesday.
"Subject to availability" covers the wording. Tenants on propose also want
a shorter draft_staleness, and re-checking availability at approval
time would be better than regenerating the whole draft. Not yet designed.
Open¶
- OI-082: attachments.
document_requestis nearly worthless without them, and the draft needs to reference what it is sending. - Re-check availability at approval rather than regenerating. Better, undesigned.
- When a draft hedges on everything, is it worth sending at all, or should it become an acknowledgement? Undecided.