VITON13School

Generative AI for BeginnersLesson 15 of 21

The AI app lifecycle and evals

A prompt that worked in three test chats can fail on the fourth user, and a model update can change answers overnight. This lesson gives Study Buddy a safety net: a small eval set you run before every change, and logs that show quality, speed and cost once real people use it.

Course by , Founder of VITON13 SCHOOL
  • Part 3: Make it trustworthy
  • 45 min

You will be able to

  • Describe the four stages of an LLM app lifecycle and how they loop
  • Write an eval set with clear pass rules for your own app
  • Run evals as regression tests before changing a prompt or a model
  • Log each model call with prompt version, latency, tokens and outcome
  • Estimate and reduce the running cost of an AI feature

Why a demo is not a product

Classic machine learning teams spend most of their effort training models. With LLM apps you usually rent a model as a service, so the real work moves to prompts, data, integration and checking the output. That shift has a name, LLMOps, and it borrows the discipline of software testing for something far less predictable than normal code.

Three things make LLM features fragile. The same input can produce different text on different runs. The provider can update or retire a model while your code stays the same. A tiny wording change in the system prompt can fix one case and quietly break five others.

The answer is not to stop changing things. It is to make every change measurable, so you know whether Study Buddy got better or worse instead of guessing from a few chats.

The lifecycle: ideate, build, evaluate, operate

Think of the lifecycle as loops, not a straight line. Each stage feeds the next, and what you learn in production flows back into the next round of ideas.

Around all four stages sits governance: who may see the data, which laws apply, how keys are stored and who signs off a release. Lessons 4 and 14 covered most of it, and it never goes away.

Do not skip the ideate stage because coding feels like progress. Ten minutes of trying the task by hand often shows that the idea needs retrieval, a tool or a narrower scope, and that is far cheaper to learn before you write the app around it.

  • Ideate: write down the job in one sentence, try it by hand in a playground with two or three models, and collect the first real examples of input you expect.
  • Build: turn the best prompt into code, add retrieval, tools or structured output where needed, and keep prompts in files under version control.
  • Evaluate: run a fixed set of test cases with pass rules, compare versions side by side and decide with numbers.
  • Operate: deploy, log every call, watch errors, latency and spending, collect user feedback and add new failures to the eval set.

Eval sets and regression tests for prompts

An eval case is an input plus a rule that says what a good answer must contain or avoid. Ten to thirty cases are enough to start, as long as they cover typical requests, edge cases and things the app must refuse.

Good cases come from real use. Start with questions you asked Study Buddy yourself, add the ones friends type when they try it, and write a few deliberately awkward inputs: a misspelled topic, a question in another language, a request for the answer key instead of a quiz. Store them in a JSON file next to the code so they are versioned together.

Pass rules come in four strengths. Code checks are the cheapest: does the answer contain a keyword, parse as JSON or stay under a length limit. A second model can grade answers against a written rubric, which handles open-ended text but needs spot checks by a person. Human review is the slowest and the most trustworthy, so keep it for a sample. Comparison tests show two versions of an answer and ask which one is better.

Treat the eval set as a regression suite. Every time you edit a prompt, swap the model or change retrieval, run the whole set and compare the pass count with the previous version. A failure that a user reports becomes a new case, and that case stays in the set for good.

Model output varies even at temperature 0, so a single run can mislead you. Run important cases several times and look at the pass rate, not one lucky answer. Change one thing at a time, otherwise you cannot tell which edit caused the difference.

What to measure

A single pass count hides too much. Track a few separate numbers so you can see which dimension moved after a change.

  • Quality: does the answer do the job the user asked for, judged by your pass rules.
  • Groundedness: when the app uses notes or documents, is every claim supported by them.
  • Harm: does the app refuse what it should and avoid unsafe, biased or private content.
  • Latency: seconds until the first token and until the full answer.
  • Cost: input and output tokens per request, multiplied by your provider's rates.
  • Failures: timeouts, invalid JSON, empty answers and provider errors per hundred calls.

Logging and cost in operation

Write one structured log line per model call. Include a timestamp, the prompt version, the model name, token counts, latency, whether the answer passed any automatic checks, and an id that links it to user feedback. JSON Lines files are fine for a small app; a database or a tracing tool helps later.

Logs can hold personal data, so decide what you store. Mask emails and names where you can, keep logs only as long as you need them, and tell users in plain words that conversations may be reviewed to improve the service.

Cost grows with tokens, and output tokens usually cost more than input tokens. Most savings come from boring moves: shorter system prompts, sending only the relevant notes, a sensible max_tokens limit, caching answers to repeated questions and routing easy tasks to a smaller model. Set a spending limit or alert in your provider's dashboard before anything goes public.

Close the loop with feedback. A thumbs up and thumbs down under each answer costs little to build, and every thumbs down is a candidate eval case. Review a handful of logged answers every week, because dashboards show that something changed, while reading answers shows what changed.

Example

A tiny eval runner with a JSON Lines log

import json, os, timefrom openai import OpenAI client = OpenAI(base_url=os.environ["LLM_BASE_URL"], api_key=os.environ["LLM_API_KEY"])PROMPT_VERSION = "explain-v2"SYSTEM = "You are Study Buddy. Explain the topic for a beginner in at most three sentences."CASES = [    {"input": "Explain photosynthesis", "must": ["light", "oxygen"]},    {"input": "Explain what a prime number is", "must": ["itself"]},    {"input": "Explain the French Revolution", "must": ["1789"]},] passed = 0with open("eval_log.jsonl", "a", encoding="utf-8") as log:    for case in CASES:        start = time.perf_counter()        r = client.chat.completions.create(model=os.environ["LLM_MODEL"], temperature=0, messages=[            {"role": "system", "content": SYSTEM}, {"role": "user", "content": case["input"]}])        answer = r.choices[0].message.content or ""        ok = all(word.lower() in answer.lower() for word in case["must"])        passed += ok        log.write(json.dumps({"prompt": PROMPT_VERSION, "input": case["input"], "ok": ok,            "seconds": round(time.perf_counter() - start, 2),            "tokens": r.usage.total_tokens if r.usage else None, "answer": answer}) + "\n")        print("PASS" if ok else "FAIL", case["input"])print(f"{passed}/{len(CASES)} passed with {PROMPT_VERSION}")

Each run appends one log line per case with the prompt version, time, token count and answer. Change SYSTEM, bump PROMPT_VERSION and run it again to compare versions. Some local servers do not report usage, which is why the token field may be empty.

Practice

Give Study Buddy a regression suite

  1. Save the example as evals.py in your Study Buddy folder and replace the three cases with ten of your own: six normal topics, two tricky ones and two requests Study Buddy should decline.
  2. For each case write a pass rule a script can check, such as required words, a maximum length or a refusal phrase.
  3. Run the script twice with the current prompt and note the pass count both times.
  4. Edit the system prompt in one way only, change PROMPT_VERSION, and run the suite again.
  5. Open eval_log.jsonl and compare the answers of one case that changed between the two versions.

Done when: The script prints a pass count for two prompt versions, the log has one line per case per run, and you can point to one case whose result changed and explain why.

Common mistakes

  • Judging a prompt change by chatting with the app a few times instead of running the same fixed cases.
  • Changing the prompt and the model in one step, so a better or worse score cannot be traced to either edit.
  • Building an eval set from easy, happy-path questions only, which hides the cases where users get hurt.
  • Logging full conversations with personal details forever because nobody decided what the logs are for.

Check yourself

Pick an answer to see whether it is right and why.

You shorten Study Buddy's system prompt. What should you do before shipping it?

Right.The eval set is your regression test, and comparing scores between versions is the only objective way to see whether the edit helped or hurt.

Not quite.The right answer is “Run the full eval set and compare the pass count with the previous version”. The eval set is your regression test, and comparing scores between versions is the only objective way to see whether the edit helped or hurt.

Why run an important eval case several times?

Right.Sampling makes answers differ from run to run, even at low temperature, so several runs show how often a case really passes.

Not quite.The right answer is “Model output can vary between runs, so a pass rate is more reliable than one result”. Sampling makes answers differ from run to run, even at low temperature, so several runs show how often a case really passes.

A user reports a wrong answer in production. What is the best next step?

Right.Turning a real failure into a permanent test case means you can confirm the fix and catch the same problem if it ever returns.

Not quite.The right answer is “Add the input as a new eval case with a pass rule, then fix and rerun the suite”. Turning a real failure into a permanent test case means you can confirm the fix and catch the same problem if it ever returns.

From the founder

Write your ten eval cases before you touch the prompt again. From today, I want you to run evals.py before every change to Study Buddy, however small it looks.
Vitalii Tarasov, Founder of VITON13 SCHOOL

Course by Vitalii Tarasov, VITON13 SCHOOL. Draws in part on Microsoft's Generative AI for Beginners, MIT licence, Copyright (c) Microsoft Corporation. microsoft/generative-ai-for-beginners