Growing the evaluation set from traces
In this lesson we read a week of traces from an agent that staff use every day and label each run good or bad. The failures of a kind that the golden set has no case for become new cases. The previous lesson put a gate in front of every change, and the gate ran 21 questions. Staff ask hundreds a week, and some of their questions are unlike anything in the set. Trace-driven evaluation brings those questions into the set. The agent logs every run, and people read a sample of the traces. Each bad run of a new kind becomes a case that the next change must pass.
The agent is the handbook assistant of
Gating a deploy on the evaluation,
and the rollout of that lesson is finished. The shorter fixed prompt is
live for the whole office, and it is prompts/current.txt in this
lesson’s copy. The gate, the golden set and the stricter candidate prompt are the
files of that lesson. The files are in
site/examples/building-agents/evals-from-traces/. Copy the
evals-from-traces folder, and run each step below from inside the copy,
as python3 traces.py <step> or python3 gate.py <prompt>. No
application programming interface (API) key is needed.
The traces in this lesson are invented, and so are the people and their employee numbers. Real traces come from the trace store that a production agent writes to, and they hold what real people typed, which can be personal data.
Log every run, and read a sample
Section titled “Log every run, and read a sample”Before people or a model can judge what an agent did, its runs have to be
logged [1]. The assistant writes one trace per run. Its records
use the fields of
Recording and grading the path the agent took.
A tool record holds the search, and a model record holds the answer. The
records here leave out the token counts, the latency and the cost, which
this lesson doesn’t use, and they add user, the employee number of the
person who asked.
How much to read changes over time. Husain reads as much as he can when
a product is new, and reads a smaller sample as it matures, but he never
stops reading [1]. This team picks 20 runs at random from each
week and reads them on Friday. He also says that reading must take as
little effort as possible, and he often builds his own viewer that puts
everything a reader needs on one screen [1]. The week
step is this team’s viewer. It prints each run of the sample with its
question and the answer the person got.
The traces of week 1
Section titled “The traces of week 1”Run this, and compare what you see with the output below.
python3 traces.py week 1week 1 sample: 20 runs
w1-01 Mon How many days can I work from home?
answer: Staff may work from home up to three days per week.
w1-02 Mon What is the budget for training?
answer: Every employee has a training budget of 1500 euros per year.
w1-03 Mon Can I expense a taxi to the airport?
answer: not found
w1-04 Mon How many days of sick leave do I get?
answer: Every employee accrues 25 days of annual leave per year.
w1-05 Tue Who is the cost center owner of the payments team?
answer: The cost center owner of the payments team is Maria Duarte, head of platform.
w1-06 Tue What do I do with a phishing email?
answer: Forward a phishing email to the security team and delete it afterwards.
w1-07 Tue Where do I report a lost laptop?
answer: Report a lost laptop to the service desk the same day.
w1-08 Tue How many vacation days do I have?
answer: not found
w1-09 Wed How much do I get for my home office?
answer: A home office allowance of 30 euros per month is paid with salary.
w1-10 Wed Is there free coffee?
answer: not found
w1-11 Wed My wife is due in May, how many days of parental leave do I get?
answer: Every employee accrues 25 days of annual leave per year.
w1-12 Wed How far ahead are flights booked?
answer: Flights are booked through the travel desk at least 14 days ahead.
w1-13 Thu Who approves a new laptop for the data team?
answer: The cost center owner of your team approves a replacement laptop before that, see the team pages.
w1-14 Thu Where are passwords kept?
answer: Passwords are kept in the company password manager.
w1-15 Thu Can I carry over unused leave days?
answer: Unused days carry over until 31 March.
w1-16 Thu How fast are service desk tickets answered?
answer: The service desk answers tickets within one working day.
w1-17 Fri How much are meals reimbursed on a trip?
answer: Meals on a work trip are reimbursed up to 40 euros per day with a receipt.
w1-18 Fri When do I get my accounts as a new starter?
answer: New starters get their accounts on day one from the service desk.
w1-19 Fri Is there a gym at the office?
answer: not found
w1-20 Fri What are core hours?
answer: Core hours, when everyone in the office is reachable, are 10 to 15.Output verified in CI from site/examples/building-agents/evals-from-traces/week1.py.
Why read traces when the gate passes?
Section titled “Why read traces when the gate passes?”A team runs a support agent for its customers. Every change goes through a gate on a golden set of 40 items, and every change since the rollout has passed it. The live rates, such as the share of conversations that end with not found, have stayed level.
Every Friday the team reads 30 traces from the week. A colleague asks why, since the gate passes and the live rates are level. What’s the reason?
Where did the 40 questions of the set come from, and what does the gate know about a question that is not among them?
Label each run good or bad
Section titled “Label each run good or bad”A trace has no expected answer. Nobody wrote one, because nobody knew the
question would come. The reader decides whether the run was good, here by
checking the answer against the handbook pages in docs/. Husain starts
with a label of good or bad for each trace, because finer scores are
harder to manage than two labels [1].
Sixteen of the twenty runs are good. Three of them answer “not found”, and that’s right. The handbook says nothing about taxis or coffee, and nothing about a gym. Runs w1-04, w1-08, w1-11 and w1-13 are bad. For each one, write down the first step that went wrong, the way Reading failures one by one did:
| Run | What went wrong |
|---|---|
| w1-04 | no rule for sick leave, and the agent quoted the annual leave rule |
| w1-08 | the handbook calls it “annual leave”, and the search got expenses |
| w1-11 | no rule for parental leave, and the agent quoted the annual leave rule |
| w1-13 | the answer needs a second lookup, the data team page |
Runs w1-04 and w1-11 are the worst of the four. A person who asked about sick leave was told a rule that the handbook doesn’t have. The question shares two words with the annual leave sentence, “days” and “leave”, and two shared words are enough for the agent to answer.
Which failures does the set already have?
Section titled “Which failures does the set already have?”In Reading failures one by one each
of the ten failures added an item. A weekly sample brings the same kinds
back week after week. So here a bad run of a kind that the set already
has adds to the count of that kind, and a bad run of a new kind becomes
a case. For each bad run, look in golden-set.csv for an item that
would fail on the same mistake, at the same step.
- w1-08 has the kind of a01, “How much holiday can I take?”. In both runs
the search doesn’t return the page with the answer, because the handbook
doesn’t use the user’s word. For w1-08 it returns the expenses page, and
for a01 it doesn’t find a page. The set accepts “not found” from a01, and the
helpfulmetric already counts it as a failure. - w1-13 has the kind of r08. The search returns the laptop page, and the answer needs a second lookup, the team page. r08 fails on every prompt today, and the routine threshold of the previous lesson allows it.
- w1-04 and w1-11 have no item of their kind. The refuse items x01 to x06 all get “not found” today, because none of them shares two words with a sentence in the handbook. So the gate passed while staff got a made-up leave rule.
A failure of a kind that the set already has tells you how often that kind happens, and the count helps you choose what to fix first. A new kind tells you what the set is missing. This week has one new kind, with two runs.
New kind or known kind?
Section titled “New kind or known kind?”A handbook assistant searches the handbook for one page, and then answers with a sentence from that page, or says not found when no sentence shares two words with the question. Every item of its golden set gets a right answer today, apart from these. Two ask about holiday and days off, words the handbook does not use, so the search does not return the leave page, and the set accepts not found. One asks where to keep passwords: the search returns the security page, which answers it, but the best sentence shares only one word with the question, so the agent says not found, and the set accepts that. One asks who approves a laptop for a team: the search returns the laptop page, and the answer needs a second lookup, the team page. Six ask about things the handbook does not cover, and each gets not found. A kind of failure means the step where the run went wrong, and what went wrong there.
For each failure, is there an item in the set that would fail on the same mistake, or would every item still pass?
Remove personal data before a trace becomes a case
Section titled “Remove personal data before a trace becomes a case”The golden set is a file in version control. Everyone who works on the
agent reads it, and it is kept for years. A trace holds what a person
typed, and run w1-11 holds more than its question needs. The show step
prints its records:
The trace of run w1-11
Section titled “The trace of run w1-11”Run this, and compare what you see with the output below.
python3 traces.py show w1-11{"run": "w1-11", "span": 1, "kind": "tool", "name": "execute_tool search_docs", "tool": "search_docs", "args": {"query": "My wife is due in May, how many days of parental leave do I get?"}, "result": {"name": "leave.txt"}, "user": "emp-2613"}
{"run": "w1-11", "span": 2, "kind": "model", "name": "chat fake-model", "asked": "answer", "answer": "Every employee accrues 25 days of annual leave per year.", "user": "emp-2613"}Output verified in CI from site/examples/building-agents/evals-from-traces/show_w1_11.py.
The employee number names a person, and the first half of the question says something about that person’s family that a colleague shouldn’t learn from a test file. Neither is needed to test the failure. Remove the details that point at a person, and keep the structure of the request: “How many days of parental leave do I get?”. The case leaves out the employee number, and its origin names the trace id. The trace id leads back to the full trace only for people who may read the trace store.
A rewritten question can also lose the failure. Check that the stripped question still fails the same way on the live prompt before you add it:
The stripped question on the live prompt
Section titled “The stripped question on the live prompt”Run this, and compare what you see with the output below.
python3 traces.py ask current "How many days of parental leave do I get?"search returned: leave.txt answer: Every employee accrues 25 days of annual leave per year.
Output verified in CI from site/examples/building-agents/evals-from-traces/ask_parental.py.
The same page and the same wrong sentence, so the stripped case tests what the trace showed.
A trace with a health detail
Section titled “A trace with a health detail”A handbook assistant answers staff questions, and its traces record the employee number of the person who asked. The handbook has no rule for sick leave. The assistant answers when a handbook sentence shares two words with the question. A team adds cases from traces to a golden set that is kept in version control.
A trace from the employee emp-5530 asks: “I need surgery in June, how many days of sick leave do I get?”. The assistant answered with the annual leave rule. What goes into the golden set?
What does the case need to fail the way the trace did, and who reads the golden set file?
Add the cases, and gate the next change
Section titled “Add the cases, and gate the next change”The two new cases go into golden-set.csv in the format of the golden
set lesson. They are refuse items, because the handbook covers neither
question. Their held_out is no, because the team will change the
agent while it looks at them, and
A golden set is the agent’s regression suite
keeps such items in the part that runs after every change. The origin
column names the trace:
x07,refuse,How many days of sick leave do I get?,,,no,no,trace w1-04: gave the annual leave rule as sick leavex08,refuse,How many days of parental leave do I get?,,,no,no,trace w1-11 with personal details removed: gave the annual leave rule as parental leaveAdd the two rows to the end of golden-set.csv in your copy, and run the
gate on the prompt that staff get today:
The gate on the live prompt, with the new cases
Section titled “The gate on the live prompt, with the new cases”Run this, and compare what you see with the output below.
python3 gate.py currentprompt: current refused hard 6 of 8 needs 100% FAIL routine hard 9 of 10 needs 90% pass helpful soft 11 of 15 needs 70% pass deploy: blocked
Output verified in CI from site/examples/building-agents/evals-from-traces/gate_current_new.py.
The prompt that passed the gate last week fails it now. The agent didn’t change, and the set did. The two cases fail on the live prompt, which shows that they catch the failure from the traces. From now on the hard gate blocks every deploy until a change passes them, even a change that fixes something else. A team that doesn’t want that adds the cases in the same change as the fix. This team adds them now and accepts that nothing else ships until the fix does. Staff get a made-up leave rule today. The refusal gate needs 100% because any answer to a refuse item can be a made-up policy, and that makes the fix the first change to ship. The next change is the fix.
The previous lesson had a candidate that might be that fix. The stricter prompt answers only when one sentence covers every keyword of the question, and it waited for a sign-off because it lost the helpful answers to a03 and a04. Run the gate on it with the new cases:
The gate on the stricter prompt, with the new cases
Section titled “The gate on the stricter prompt, with the new cases”Run this, and compare what you see with the output below.
python3 gate.py stricterprompt: stricter refused hard 8 of 8 needs 100% pass routine hard 9 of 10 needs 90% pass helpful soft 9 of 15 needs 70% FAIL new failures: none less helpful: a03 a04 deploy: waits for a person to sign off
Output verified in CI from site/examples/building-agents/evals-from-traces/gate_stricter_new.py.
The decision is still a person’s, but the person now has more to weigh. On one side are two helpful answers that the stricter prompt loses. On the other side are two refusals that it gets right, from questions that staff asked this week. Before the traces, the set had no case on the other side. Next Friday the team reads a new sample, and from now on the gate checks every change against a set that has these two cases.
Order one round of growing the set
Section titled “Order one round of growing the set”A team runs an agent in production and grows its golden set from the agent's traces. A gate runs the golden set on every change before it ships.
- Log a trace for every run the agent makes
- Pick a sample of the week's traces and read each one
- Label each run good or bad
- For each bad run, look for an item of the same kind in the set
- Write a case for each new kind, without personal data, and check that it fails today
- Add the cases to the set
- Run the gate on the next change, with the new cases in the set
What must exist before anyone can read a run? And what must be true of a case before it goes into the set?
What goes into the case?
Section titled “What goes into the case?”A support agent told a customer, who gave their name and order number, that a product could be returned after 60 days. The return policy says 30 days. The trace has the customer's name, the order number and the question. A team turns the trace into a golden set case, which a script checks without a person reading it.
Which of these belong in the case?
Which lines does the case need to fail on the same mistake, and which lines point at the customer?
The new case passes on the live prompt
Section titled “The new case passes on the live prompt”A handbook assistant answered a question about a bike allowance with a rule about the home office allowance. The handbook has no bike allowance. The assistant gives the same answer to the same question every time. A team member rewrote the question without personal details as What does the company pay toward a bicycle?, added it as a refuse case, and ran the gate on the live prompt, which has not changed.
The new case passes on the same live prompt that gave the wrong answer in the trace. What does that tell you?
The live prompt gave the wrong answer in the trace. What should the case do on that same prompt?
The exercise uses the sample of the second week. The week 2 step lists
it.
The traces of week 2
Section titled “The traces of week 2”Run this, and compare what you see with the output below.
python3 traces.py week 2week 2 sample: 20 runs
w2-01 Mon How long is the onboarding checklist?
answer: The onboarding checklist takes two weeks and ends with a review with your manager.
w2-02 Mon Who do I tell about a lost labtop?
answer: not found
w2-03 Mon What is the default class for short flights?
answer: Economy class is the default for flights under six hours.
w2-04 Mon How often are laptops replaced?
answer: Laptops are replaced every four years.
w2-05 Tue When are claims for a work trip due?
answer: Meals on a work trip are reimbursed up to 40 euros per day with a receipt.
w2-06 Tue Who runs the warehouse?
answer: The data team runs the warehouse and the reports.
w2-07 Tue Can I carry over my training budget?
answer: Unused days carry over until 31 March.
w2-08 Tue Is home office equipment company property?
answer: Equipment stays company property.
w2-09 Wed Is there a parking garage?
answer: not found
w2-10 Wed What do I do with a fishing email?
answer: not found
w2-11 Wed Who builds the checkout?
answer: The payments team builds the checkout and billing services.
w2-12 Wed When is the building open on weekdays?
answer: The building is open from 7 to 20 on weekdays.
w2-13 Thu Can my dog come to the office?
answer: not found
w2-14 Thu When do unused leave days expire?
answer: Every employee accrues 25 days of annual leave per year.
w2-15 Thu Do I need a receipt for meals?
answer: Meals on a work trip are reimbursed up to 40 euros per day with a receipt.
w2-16 Thu How many euros is the training budget?
answer: Every employee has a training budget of 1500 euros per year.
w2-17 Fri Who is the cost center owner of the data team?
answer: The cost center owner of the data team is Jonas Berg, head of analytics.
w2-18 Fri Who books train tickets?
answer: Train tickets are booked through the travel desk.
w2-19 Fri Do conference tickets count against my budget?
answer: Conference tickets count against the training budget.
w2-20 Fri What is the home office allowance?
answer: A home office allowance of 30 euros per month is paid with salary.Output verified in CI from site/examples/building-agents/evals-from-traces/week2.py.
Exercise
In your copy, with x07 and x08 added, read the sample of week 2 with
python3 traces.py week 2, and label each run good or bad against the
pages in docs/. For each bad run, write the first step that went wrong,
and look in golden-set.csv for an item of the same kind. For each new
kind, write one case per bad run in the person’s words, without the
employee number, and with the trace id in origin. Check with
python3 traces.py ask current "<question>" that each case fails today.
Add the rows, and run python3 gate.py current and
python3 gate.py stricter. Twenty minutes is enough. A sample read this
way shows what the gate didn’t know before.
A good result has five bad runs and one new kind. In w2-02 and w2-10 a
misspelled word, “labtop” and “fishing”, leaves one word of the question
that the handbook has, “lost” and “email”. The search returns the right
page on that one word, the laptop page and the security page. One shared
word is too few for an answer. The agent says “not found” to a question
that the page answers. That is the kind of a05, “Where do I keep my
passwords?”, which gets the security page and “not found” on the one
word “passwords”. Run w2-07 has the kind of x07 and x08, an uncovered
question answered with a rule that shares two words with it. These
three runs add to the counts of kinds that the set has. In w2-05 and
w2-14 the search also returns the right page, and the agent quotes the
wrong sentence on it. The set has no item of that kind. The two new
cases are routine items, with the page and a phrase from the right
sentence, such as 30 days for w2-05. Week 2 has no personal data in its
questions, and the employee numbers stay out of the set. These are the
two rows of a good result:
r13,routine,When are claims for a work trip due?,expenses.txt,30 days,no,no,trace w2-05: quoted the meals sentencer14,routine,When do unused leave days expire?,leave.txt,31 March,no,no,trace w2-14: quoted the annual leave sentenceWith x07, x08 and these two rows in the set, the two gate runs print this:
The gate on both prompts, with the cases of week 2
Section titled “The gate on both prompts, with the cases of week 2”Compare this with the output of your own two gate runs.
python3 gate.py currentpython3 gate.py stricterprompt: current refused hard 6 of 8 needs 100% FAIL routine hard 9 of 12 needs 90% FAIL helpful soft 11 of 17 needs 70% FAIL deploy: blocked prompt: stricter refused hard 8 of 8 needs 100% pass routine hard 9 of 12 needs 90% FAIL helpful soft 9 of 17 needs 70% FAIL new failures: none less helpful: a03 a04 deploy: blocked
Output verified in CI from site/examples/building-agents/evals-from-traces/gate_week2.py.
The two new cases fail on both prompts. The routine metric drops to 9 of 12, and the gate blocks both. The kind of a05 got two more runs this week, and the new kind has two runs. Which of the two would you fix first, and why?
Stretch: Run python3 traces.py ask stricter with the question of each new case. Which of the new kinds of both weeks does the stricter prompt fix, and which part of the agent would a fix for the other change?
Recap
- Log the agent’s traces, because people and models can only judge the runs that were logged [1].
- Read a sample of the traces on a schedule, more at first and fewer later, and label each run good or bad [1].
- For each bad run, look for an item of the same kind in the set. Each bad run of a new kind becomes a case, with the quality a good answer has written down and the trace id as its origin.
- Take out the details that point at a person before a trace becomes a case, and check that the stripped case still fails on the live version.
- A new case fails on the live version until a change fixes it, and the gate then checks that change against it.
You can now
- Grows the evaluation set from what goes wrong in real use
References
Section titled “References”- Hamel Husain. Your AI Product Needs Evals. hamel.dev. Reference.
Husain 2024