Define success criteria and build evaluations
Define measurable success criteria for your LLM application and build evaluations to test it, from exact match checks to LLM-based grading.
Building a successful LLM-based application starts with clearly defining your success criteria and then designing evaluations to measure performance against them. This cycle is central to prompt engineering.

Define your success criteria
Good success criteria are:
-
Specific: Clearly define what you want to achieve. Instead of "good performance," specify "accurate sentiment classification."
-
Measurable: Use quantitative metrics or well-defined qualitative scales. Numbers provide clarity and scalability, but qualitative measures can be valuable if consistently applied along with quantitative measures.
- Even "hazy" topics such as ethics and safety can be quantified:
Safety criteria Bad Safe outputs Good Less than 0.1% of outputs out of 10,000 trials flagged for toxicity by the content filter.
Quantitative metrics:
- Task-specific: F1 score, BLEU score, perplexity
- Generic: Accuracy, precision, recall
- Operational: Response time (ms), uptime (%)
Quantitative methods:
- A/B testing: Compare performance against a baseline model or earlier version.
- User feedback: Implicit measures like task completion rates.
- Edge case analysis: Percentage of edge cases handled without errors.
Qualitative scales:
- Likert scales: "Rate coherence from 1 (nonsensical) to 5 (perfectly logical)"
- Expert rubrics: Linguists rating translation quality on defined criteria
- Even "hazy" topics such as ethics and safety can be quantified:
-
Achievable: Base your targets on industry benchmarks, prior experiments, AI research, or expert knowledge. Your success metrics should not be unrealistic to current frontier model capabilities.
-
Relevant: Align your criteria with your application's purpose and user needs. Strong citation accuracy might be critical for medical apps but less so for casual chatbots.
| Criteria | |
|---|---|
| Bad | The model should classify sentiments well |
| Good | The sentiment analysis model should achieve an F1 score of at least 0.85 (Measurable, Specific) on a held-out test set* of 10,000 diverse Twitter posts (Relevant), which is a 5% improvement over the current baseline (Achievable). |
*More on held-out test sets in the next section.
Common success criteria
Here are some criteria that might be important for your use case. This list is non-exhaustive.
How well does the model need to perform on the task? You may also need to consider edge case handling, such as how well the model needs to perform on rare or challenging inputs.
How similar do the model's responses need to be for similar types of input? If a user asks the same question twice, how important is it that they get semantically similar answers?
How well does the model directly address the user's questions or instructions? How important is it for the information to be presented in a logical, easy to follow manner?
How well does the model's output style match expectations? How appropriate is its language for the target audience?
What is a successful metric for how the model handles personal or sensitive information? Can it follow instructions not to use or share certain details?
How effectively does the model use provided context? How well does it reference and build upon information given in its history?
What is the acceptable response time for the model? This depends on your application's real-time requirements and user expectations.
What is your budget for running the model? Consider factors like the cost for each API call, the size of the model, and the frequency of usage.
Most use cases need multidimensional evaluation along several success criteria.
| Criteria | |
|---|---|
| Bad | The model should classify sentiments well |
| Good | On a held-out test set of 10,000 diverse Twitter posts, the sentiment analysis model should achieve: - an F1 score of at least 0.85 - 99.5% of outputs are non-toxic - 90% of errors would cause inconvenience, not egregious error* - 95% response time < 200ms |
*In reality, you would also define what "inconvenience" and "egregious" mean.
Build evaluations
Eval design principles
- Be task-specific: Design evals that mirror your real-world task distribution. Don't forget to factor in edge cases!
- Irrelevant or nonexistent input data
- Overly long input data or user input
- [Chat use cases] Poor, harmful, or irrelevant user input
- Ambiguous test cases where even humans would find it hard to reach an assessment consensus
- Automate when possible: Structure questions to allow for automated grading (for example, multiple-choice, string match, code-graded, LLM-graded).
- Prioritize volume over quality: More questions with slightly lower signal automated grading is better than fewer questions with high-quality human hand-graded evals.
Example evals
What it measures: Exact match evals measure whether the model's output matches a predefined correct answer, typically after normalizing whitespace and case. It's a simple, unambiguous metric that's perfect for tasks with clear-cut, categorical answers like sentiment analysis (positive, negative, neutral).
Example eval test cases: 1,000 tweets with human-labeled sentiments.
tweets = [
{"text": "This movie was a total waste of time. 👎", "sentiment": "negative"},
{"text": "The new album is 🔥! Been on repeat all day.", "sentiment": "positive"},
{
"text": "I just love it when my flight gets delayed for 5 hours. #bestdayever",
"sentiment": "negative",
}, # Edge case: Sarcasm
{
"text": "The movie's plot was terrible, but the acting was phenomenal.",
"sentiment": "mixed",
}, # Edge case: Mixed sentiment
# ... 996 more tweets
]
client = anthropic.Anthropic()
def get_completion(prompt: str):
message = client.messages.create(
model="claude-opus-5",
max_tokens=50,
messages=[{"role": "user", "content": prompt}],
)
return next(block.text for block in message.content if block.type == "text")
def evaluate_exact_match(model_output, correct_answer):
return model_output.strip().lower() == correct_answer.lower()
outputs = [
get_completion(
f"Classify this as 'positive', 'negative', 'neutral', or 'mixed': {tweet['text']}"
)
for tweet in tweets
]
accuracy = sum(
evaluate_exact_match(output, tweet["sentiment"])
for output, tweet in zip(outputs, tweets)
) / len(tweets)
print(f"Sentiment Analysis Accuracy: {accuracy * 100}%")What it measures: Cosine similarity measures the similarity between two vectors (in this case, sentence embeddings of the model's output using Sentence-BERT (SBERT)) by computing the cosine of the angle between them. Values closer to 1 indicate higher similarity. It's ideal for evaluating consistency because similar questions should yield semantically similar answers, even if the wording varies.
Example eval test cases: 50 groups with a few paraphrased versions each.