Turning AI Agent Evaluation Scores into Code Improvements: A Complete Workflow
After running DeepEval on an AI agent, the article explains how to move from raw metric scores to actionable code changes by reading detailed transcripts, diagnosing root causes, applying quality‑gate thresholds, choosing appropriate pass@k or pass^k metrics, and iterating with CI/CD integration.
The previous five articles built the evaluation infrastructure; with DeepEval and defined metrics (ToolCorrectnessMetric, FaithfulnessMetric, AgentGoalCompletionMetric) you can now run a test and obtain scores such as:
ToolCorrectnessMetric: 0.72 ✗ FAILED
FaithfulnessMetric: 0.85 ✓ PASSED
AgentGoalCompletionMetric: 0.61 ✗ FAILEDScores tell you where problems exist but not what they are. Anthropic stresses the importance of reading the transcripts —examining every step the agent took, which tools were called, their outputs, and the final response.
Example code shows how DeepEval provides a reason field:
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import ToolCorrectnessMetric
from deepeval import evaluate
test_case = LLMTestCase(
input="帮我查一下北京到上海的高铁票",
actual_output="北京到上海的高铁最快约 4.5 小时,G1 次列车,票价二等座 ¥553。",
tools_called=[
ToolCall(name="WebSearch"),
ToolCall(name="TrainQuery"),
ToolCall(name="WeatherQuery"), # 多调了一个天气查询
],
expected_tools=[ToolCall(name="TrainQuery")],
)
metric = ToolCorrectnessMetric(threshold=0.8)
metric.measure(test_case)
print(f"分数:{metric.score}")
print(f"原因:{metric.reason}")The printed reason might be:
分数:0.67
原因:Agent 调用了 3 个工具,但预期只需要 TrainQuery。
WebSearch 是冗余调用,WeatherQuery 与任务无关。
正确工具 TrainQuery 被调用,但额外调用降低了精确度。This detailed feedback reveals a "tool‑redundancy" issue, which can be fixed by adding a prompt rule such as "only call tools necessary to complete the task".
Choosing the right success metric
Anthropic defines two related metrics:
pass@k : probability that at least one of k attempts succeeds.
pass^k : probability that all k attempts succeed.
Example: running a test five times yields three passes. pass@5 = 3/5 = 60% (at least one success) pass^5 = 0% (no run succeeded all five times)
Use pass@k for auxiliary tools where a single successful run is enough (e.g., code generation). Use pass^k for user‑facing agents that must be reliable on every call (e.g., customer‑service bots).
Quality gates
To prevent scores from becoming a vanity metric, introduce a quality gate that blocks a pull‑request if core metrics fall below a threshold. Core metrics (e.g., AgentGoalCompletionMetric) must pass; auxiliary metrics (e.g., ToolCorrectnessMetric) may only emit warnings.
# tests/test_agent_quality_gate.py
import pytest
from deepeval import assert_test
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import (
ToolCorrectnessMetric,
AgentGoalCompletionMetric,
FaithfulnessMetric,
)
from deepeval.dataset import EvaluationDataset
dataset = EvaluationDataset()
dataset.pull(alias="客服 Agent 测试集 v2")
@pytest.mark.parametrize("golden", dataset.goldens)
def test_quality_gate(golden):
result = run_agent(golden.input)
test_case = LLMTestCase(
input=golden.input,
actual_output=result["output"],
tools_called=result["tools_called"],
expected_tools=golden.expected_tools,
retrieval_context=result["tool_outputs"],
)
goal_metric = AgentGoalCompletionMetric(threshold=0.75)
faithfulness_metric = FaithfulnessMetric(threshold=0.70)
tool_metric = ToolCorrectnessMetric(threshold=0.65)
assert_test(test_case, [goal_metric, faithfulness_metric, tool_metric])GitHub Actions workflow runs the same test suite; core failures abort the job, preventing the merge.
# .github/workflows/quality-gate.yml
name: Agent Quality Gate
on:
pull_request:
branches: [main]
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: pip install deepeval -r requirements.txt
- name: Run quality gate
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CONFIDENT_API_KEY: ${{ secrets.CONFIDENT_API_KEY }}
run: |
deepeval test run tests/test_agent_quality_gate.py \
--n-workers 4 \
--exit-on-first-failure # any core metric failure stops the jobFrom scores to code changes – a concrete example
Using the Agent‑EvalKit report, the failure at line 47 ( get_exchange_rate) lacked handling for empty API responses. Adding a prompt instruction to explicitly handle empty results fixes the issue.
Typical failure‑to‑fix mapping:
Tool‑call redundancy : add "only call tools required for the current request" to the system prompt.
Low faithfulness : add "if a tool returns no result, tell the user the information is unavailable".
Goal completion failure : require the agent to restate the task before proceeding and confirm each step.
After updating the system prompt and re‑evaluating, scores improved dramatically:
AgentGoalCompletionMetric: 0.78 ✓ (+0.17)
FaithfulnessMetric: 0.81 ✓ (+0.09)
ToolCorrectnessMetric: 0.83 ✓ (+0.15)Beware of evaluation saturation
When pass rates stay above 95 % across several releases, the test suite no longer surfaces useful signals. Anthropic calls this "evaluation saturation" and recommends adding harder cases (multi‑step tasks, extreme edge cases) and separating regression tests (near‑100 % pass) from capability tests (target 50‑70 % pass).
# Separate datasets example
regression_dataset = EvaluationDataset()
regression_dataset.pull(alias="回归测试集")
capability_dataset = EvaluationDataset()
capability_dataset.pull(alias="能力挑战集")Visualising results and keeping a history
DeepEval can be combined with Confident AI to generate dashboards, or you can log scores yourself:
import json
from datetime import datetime
from deepeval import evaluate
from deepeval.dataset import EvaluationDataset
def run_and_record(version_tag: str):
dataset = EvaluationDataset()
dataset.pull(alias="客服 Agent 测试集 v2")
metrics = [
AgentGoalCompletionMetric(threshold=0.7),
FaithfulnessMetric(threshold=0.7),
ToolCorrectnessMetric(threshold=0.7),
]
results = evaluate(test_cases=dataset.test_cases, metrics=metrics)
record = {
"version": version_tag,
"timestamp": datetime.now().isoformat(),
"scores": {
"goal_completion": results.metrics_data["AgentGoalCompletionMetric"].avg_score,
"faithfulness": results.metrics_data["FaithfulnessMetric"].avg_score,
"tool_correctness": results.metrics_data["ToolCorrectnessMetric"].avg_score,
},
}
with open("eval_history.jsonl", "a") as f:
f.write(json.dumps(record, ensure_ascii=False) + "
")
return record
run_and_record("v1.2.3")Key takeaways
Read the full transcript, not just the score; the transcript reveals the concrete failure reason.
Enforce quality gates: core metrics block merges, auxiliary metrics warn, and set thresholds relative to the current baseline (e.g., no more than 5 % drop).
Choose the appropriate metric: use pass^k for user‑facing agents that must be reliable on every attempt, pass@k for auxiliary tools where a single success suffices.
Monitor for evaluation saturation and continuously introduce harder test cases while keeping regression tests separate.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qborfy AI
A knowledge base that logs daily experiences and learning journeys, sharing them with you to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
