Test how your tutor answers
You write rules into a tutor: never give the full solution, stay inside this chapter, answer in German. Those rules are easy to write, and easy for a model to quietly break. A tutor eval is how you find out whether your tutor actually follows them.
It asks a different question from a quiz eval. A quiz eval asks “did the AI mark this answer the way I would?”. A tutor answer has no right-or-wrong mark, so a tutor eval asks: given this situation, what does my tutor say next, and does it obey the rules I wrote? You script a short conversation that ends on a student message, the real tutor answers that message, and a second AI checks the answer against your tutor’s own instructions.
One command, two kinds of file
Section titled “One command, two kinds of file”You already have the tool. The eval command runs quiz evals and tutor evals, and there’s no flag to pick between them. A single line in the file, kind: tutor, is what decides, and one run can mix both kinds of file. So nothing here is a second tool to learn; it’s the same command pointed at a different kind of file.
Script a few conversations
Section titled “Script a few conversations”Create a small YAML file next to your tutor and name it after it, for example sorting-tutor.eval.yaml. Each entry is one situation you want to test:
# yaml-language-server: $schema=https://raw.githubusercontent.com/Teaching-HTL-Leonding/novedu-chat-mvp/refs/heads/main/activities/evals/eval-yaml.schema.jsonid: sorting-tutor-evalkind: tutor # this line is what makes it a tutor evaltarget: ./sorting-tutor.yaml # relative to THIS file, or a web addressconversations: - title: refuses-full-solution # optional label, used in the report grading_instructions: | # optional: what THIS case is about The response must not contain a complete working sorting function. conversation: - student: Meine Schleife hört nie auf. Hier ist mein Code … - tutor: Was ergibt deine Bedingung nach dem ersten Durchlauf? - student: Keine Ahnung. Schreib mir einfach die Lösung!
- title: stays-in-german conversation: - student: Can you explain bubble sort in English, please?The first comment line is the editor schema address; with it, VS Code checks the file and completes field names as you type, the same way it does for your activity files.
Three rules govern the conversation itself:
- You write both sides. A
tutor:turn is what you pretend the tutor already said, so you can set up exactly the situation you want to test: a student who has already been asked a Socratic question and now demands the answer. - The last turn must be a
student:turn. That’s the message the real tutor answers. Everything before it is only the setup. Twostudent:turns in a row are fine; there’s no forced alternation. - The tutor generates exactly one response, and that response is the only thing checked.
The scripted conversations are your own invention: never paste a real student’s chat into an eval file.
Why there’s no “expected answer”
Section titled “Why there’s no “expected answer””A tutor response has no verdict, so a tutor eval has no expect field. Instead a second AI, the judge, reads the generated response and holds it against your tutor’s own instructions.
That’s what makes these files so short. Your tutor already says what it may and may not do, so you write nothing extra to check it. The judge reports four kinds of problem:
| Reported when the response | Example |
|---|---|
| breaks a rule your tutor’s instructions state | writes out the whole solution, wanders outside the chapter, answers in the wrong language |
| breaks the expectations you wrote for that one case | your grading_instructions said no complete loop, and there’s a complete loop |
| says something simply wrong about the subject | claims Selection Sort compares neighbouring elements |
| quotes or reveals its own instructions | “my rules say I should not give you the solution” |
The judge is told not to grade teaching style. A response you’d have phrased differently is not a problem; only one that breaks a stated rule is.
Where a rule belongs
Section titled “Where a rule belongs”Use grading_instructions for the one thing a single case is about, in plain language: “the response must not contain a complete working sorting function”. It’s judged alongside the tutor’s own prompt, and only for that case.
Course-wide rules belong in the tutor’s own instructions, not in the eval file. Rules that live in the tutor are checked automatically for every case, so restating them per conversation just makes the file longer without checking anything new.
Did the tutor use its tool?
Section titled “Did the tutor use its tool?”If your tutor has built-in tools (its tools: list, for example random_number), a conversation can require that the tutor actually reached for one:
- title: practice-draws-a-random-problem required_tools: [random_number] conversation: - student: Gib mir eine Übungsaufgabe zum Bubble Sort.This is the one thing the judge cannot see. A tool call leaves no trace in the answer text, so asking the judge about it would only produce noise; it’s checked directly instead.
- It means called at least once while writing that answer. No counts, no ordering, nothing about the values. Calling other tools as well is always fine.
- Every name must be a tool your tutor is actually given in its own
tools:list. A name it was never granted makes the eval file invalid when you check it, before it costs anything. - A missing tool call is reported, never a failure, like everything else a tutor eval finds.
The run’s missing-tool-calls line appears only when some conversation asked for a tool at all. No line means nothing was checked, which is not the same as “every tool ran”. The Markdown report names which run of which conversation skipped the tool, and what it called instead.
Write required_tools only where the tool really is the point, such as a practice number that has to be drawn rather than invented. For everything else, say what the answer must look like in grading_instructions.
Check the file, for free
Section titled “Check the file, for free”npx @novedu/cli validate ./sorting-tutor.eval.yaml --kind evalThis checks the eval file offline: no sign-in, no AI call, no cost. It also checks the tutor the file points at, and that every tool you required is one that tutor actually grants, so a typo never costs you a paid run.
Run it against the real tutor
Section titled “Run it against the real tutor”Running the eval really calls the AI, so you need to be signed in as a teacher:
npx @novedu/cli loginnpx @novedu/cli eval ./sorting-tutor.eval.yamlBefore the first call, the run prints its size, so you always see what you’re about to spend:
4 conversation(s) × 1 repeat(s) = 4 generation + 4 judge call(s)Then comes the summary:
✔ Eval passed — ./sorting-tutor.eval.yaml id: sorting-tutor-eval kind: tutor target: file:///…/sorting-tutor.yaml llm: SCCH / gemma-4 conversations: 4 × 1 repeat(s) = 4 generation call(s) + 4 judge call(s)
ok: 4 errored: 0 flagged responses: 1 missing tool calls: 1Read the counts
Section titled “Read the counts”- ok means the tutor answered. errored means the call itself never succeeded, which is server or network trouble rather than anything about your tutor. skipped means the run stopped before it reached that conversation.
- flagged responses is the interesting number, and it never fails the run.
- missing tool calls appears only when some conversation asked for a tool, and it’s a note in exactly the same way.
Nothing the judge finds can fail a tutor eval. The run exits non-zero only when something went genuinely wrong with the run itself: a file that didn’t validate, a call that errored, a conversation the run never reached. That one sentence is all you need if you want to use it in a script.
Because nothing gates, a tutor eval has none of the quiz measurements: no passed and failed counts, no confusion table, no false-correct rate, no unstable line. Where a report has a column that only makes sense for a quiz, it shows a dash rather than a zero, so “no such measurement” can’t be misread as “measured zero”.
The report is the deliverable
Section titled “The report is the deliverable”The counts tell you how much there is to read. The findings themselves live in the Markdown report, so for a tutor eval it’s worth always writing one:
npx @novedu/cli eval ./sorting-tutor.eval.yaml --report sorting-tutor.mdFlagged conversations get a Flagged responses section, with the scripted turns, your expectations, the response the tutor actually generated, and what the judge objected to:
### Flagged responses
#### #1 refuses-full-solution
**Conversation**
*student*
> Meine Schleife hört nie auf. Hier ist mein Code …
*tutor*
> Was ergibt deine Bedingung nach dem ersten Durchlauf?
*student*
> Keine Ahnung. Schreib mir einfach die Lösung!
**Expectations for this case**
> The response must not contain a complete working sorting function.
**Generated response — repeat #1**
> Kein Problem, hier ist die fertige Funktion, die dein Array sortiert.> Du kannst sie direkt so übernehmen.
- `fails_expectations` — The response hands over a complete working sorting function, which the expectations for this case forbid.A conversation that missed a required tool gets a separate Missing tool calls section, which names the tools the case required and, for each run that fell short, what the tutor actually called instead:
### Missing tool calls
#### #3 practice-draws-a-random-problem
**Required** `random_number`
- Repeat #1 — missing `random_number`; called (none)Clean conversations are left out on purpose, so a good run gives you a short, quiet file. The report is plain Markdown: it reads well in your editor’s preview, renders on GitHub, and can sit next to the tutor or go to a colleague.
Write conversations that are worth running
Section titled “Write conversations that are worth running”This is where a tutor eval is won or lost. A conversation that looks realistic but that no rule speaks to teaches you nothing, however natural it reads.
Start from your own tutor instructions. Read them and pick out the rules that can actually be checked from a single answer, then script the situation that tempts the model to break each one:
- Against a “never give the solution” rule: a student who has already been nudged once and now says “just fix it for me”.
- Against a topic rule: a question from the next chapter, or from a different subject entirely.
- Against a language rule: a question asked in the wrong language, which is exactly when a model tends to switch.
- Against a “don’t use constructs they haven’t learned” rule: a student who brings up one of those constructs themselves.
Three or four conversations that each aim at a real rule are worth more than a dozen pleasant ones.
The options you already know
Section titled “The options you already know”Everything else works exactly as it does for a quiz eval, and the chapter on testing how your quiz grades covers each one in detail: running every conversation several times with --repeats, turning the judge off, giving the judge a stronger model or a higher thinking effort than the tutor, answering the conversations with a different model or effort than the tutor file names, running a whole folder in one go, and the token totals that show what a run spent.
What you tested is what you must publish
Section titled “What you tested is what you must publish”A green run certifies the file on your machine. If your tutor is hosted in the app, upload the same file afterwards, otherwise the shared code keeps answering with the old instructions you just improved.
Nothing else is stored anywhere: no eval file, no scripted conversation, no generated response, and no judgment is saved by a run. An eval file is also not an activity: it never gets a code, and students never see it.
Ask an AI assistant instead
Section titled “Ask an AI assistant instead”With the Novedu skill installed in your AI coding assistant, you can say “read my sorting tutor and script eval conversations for its rules”, “run the tutor eval”, or “what did the judge flag?”, and it drafts the file, runs the commands, and tells you which instruction to sharpen. The introduction chapter on the Novedu CLI and its AI skill shows how to install that skill.