在 AI Agent 的研发过程中,许多团队都会陷入同一个泥潭——“靠感觉调 Prompt(Vibe Checking)”。改了一行提示词,跑了三个用例感觉变聪明了,结果一上线却在另一个边缘场景下疯狂死循环。
软件工程有黄金准则:“无法度量,就无法改进。” 本文将剥离空洞的理论,从一线工程落地的视角,系统拆解评估 AI Agent 的 4 大核心维度、15 项关键指标,并提供一套不到 200 行代码即可在本地跑通的 Python 自动化评测方案。
一、 为什么传统的 LLM 评测对 Agent 失效了?
评测一个普通的大模型对话应用(Chatbot),通常只需要看“输入 Question”和“输出 Answer”的文本相似度或语义质量。但 Agent(智能体)的本质是“具备多步推理与工具调用能力的自主系统”。
如果只看最终输出的文本,你会漏掉大量致命的工程隐患:
- 结果对了,过程全错:用户问订单状态,查询接口超时,Agent 靠瞎猜碰巧蒙对了“已发货”,这在生产环境是严重的幻觉隐患。
- 隐形破产(成本失控):本来 2 步就能查完的数据,Agent 在后台反复调错了 8 次工具才修正过来。用户看起来结果完全正常,但 Token 账单和接口耗时翻了四倍。
- 状态污染(副作用):Agent 嘴上回答“已为您取消退款”,但实际上根本没有成功触发后端的 cancel_refund 接口。
因此,评估 Agent 必须完成从 “点对点文本评估” 到 “全链路轨迹(Trajectory)与环境状态(State)评估” 的范式转移。
二、 AI Agent 核心评估指标全景图
要全方位度量一个 Agent 的表现,我们需要建立一套由外到内、兼顾质量与成本的四维评估体系:
1. 任务与结果维度(Outcome Metrics)—— 交付底线
关注 Agent 最终是否真正解决了用户的业务问题。
- 任务成功率(Task Success Rate / \(\text{Pass@1}\)):单次运行达成最终业务目标的任务占比。这是最基础的交付底线。
- 稳定性指标(\(\text{Pass}^k\)):同一任务连续运行 \(k\) 次全部成功的概率。
💡 为什么 \(\text{Pass}^k\) 比 \(\text{Pass@}k\) 更重要? 在代码生成领域常用 \(\text{Pass@}k\)(跑 \(k\) 次只要成功 1 次就算过),但在生产级 Agent 中,用户只会提问一次。如果一个 Agent 单次成功率为 80%,连续跑 5 次全过的概率(\(\text{Pass}^5 = 0.8^5\))只有 32.7%。\(\text{Pass}^k\) 能够极其敏锐地放大系统的不确定性。
- 进度率(Progress Rate):已完成的子目标数 / 任务总子目标数。针对多步长链路任务,避免只给冷冰冰的 0 分或 1 分,帮助研发定位 Agent 到底卡在了哪一环。
- 事实忠实度(Groundedness / Faithfulness):最终回答中有工具返回数据(Observation)支撑的陈述比例,用于精准量化“编造数据”的幻觉率。
2. 轨迹与工具维度(Trajectory & Tool Metrics)—— 推理内功
打开黑盒,评估 Agent 在执行过程中的规划质量与容错能力。
3. 性能与成本维度(System & Cost Metrics)—— 商业可行性
- 端到端延迟(E2E Latency & P95/P99):从发起请求到完成所有工具调用并输出最终回答的总耗时。分析时需拆分为 LLM 推理耗时 与 外部 API 响应耗时。
- 首动作耗时(Time to First Tool / Token):决定用户体感流畅度的关键指标。
- 单次成功任务成本(Cost per Successful Task):
\(\text{Cost per Success} = \frac{\text{测试集总消耗成本(含失败重试的 Token 与 API 费用)}}{\text{最终成功的任务总数}}\)
⚠️ 注意:切勿只统计“平均每次请求消耗多少 Token”。如果失败率高,失败消耗的成本必须摊销到成功任务上,这才是真实的商业 ROI。
4. 安全与合规维度(Safety & Policy Metrics)—— 生产红线
- 越权操作拦截率:面对敏感写操作(如删库、大额退款、修改权限)时,成功拒绝或按规定触发人工二次确认(Human-in-the-loop)的比例。
- 领域规则遵循率(Policy Compliance):是否严格遵守业务红线(如“未核实身份前绝不透露订单详情”),不会为了讨好用户而违规。
- 提示词注入抵御率(Injection Resistance):当外部检索到的网页、邮件或文档中夹带恶意指令(Indirect Prompt Injection)时,Agent 不被劫持的比例。
三、 动手实战:不到 200 行代码搭建自动化评测流水线
许多团队迟迟没有做量化评测,是因为觉得搭建评测平台太重。事实上,你不需要一开始就引入庞大的第三方系统,只需要遵循以下 “三步走” 架构,用纯 Python 就能在一下午跑通完整的基准测试:
第一步:改造 Agent 接口,暴露运行轨迹(Trace)
要评测 Agent,你的主入口函数绝对不能只返回一个字符串 response。我们需要在 Agent 内部循环中顺手记录下它走过的每一步,并返回标准化的字典结构:
import time
def run_my_agent(user_query: str, mock_env: dict) -> dict:
"""
包装 Agent 入口函数,除返回最终回复外,完整暴露执行轨迹 (Trace) 与沙盒状态
"""
start_time = time.time()
first_action_time = None
called_tools = []
total_tokens = 0
# ================= 模拟 Agent 内部执行循环 =================
# [动作 1] 调用订单查询工具
if first_action_time is None:
first_action_time = time.time() - start_time
tool_output_1 = {"order_id": "ORD_123", "status": "paid", "amount": 199}
called_tools.append({
"name": "get_order",
"args": {"order_id": "ORD_123"},
"output": str(tool_output_1),
"error": None
})
total_tokens += 450
# [动作 2] 调用退款工具(注意:写操作直接修改传入的 mock_env 沙盒字典)
mock_env["orders"]["ORD_123"]["status"] = "refunded"
called_tools.append({
"name": "refund_order",
"args": {"order_id": "ORD_123", "reason": "user_request"},
"output": '{"success": true}',
"error": None
})
total_tokens += 380
final_answer = "已为您办理订单 ORD_123 的退款,金额 199 元将原路返回。"
# ==========================================================
e2e_latency = time.time() - start_time
return {
"final_answer": final_answer,
"called_tools": called_tools, # 核心:实际调用的工具链
"e2e_latency": e2e_latency, # 端到端耗时 (秒)
"ttft": first_action_time or e2e_latency, # 首动作耗时 (秒)
"total_tokens": total_tokens, # 消耗的总 Token
"final_env_state": mock_env # 核心:执行完毕后的沙盒环境状态
}
第二步:构建带沙盒状态的基准测试集(test_cases.json)
与传统问答测试集不同,Agent 的测试用例必须包含 初始环境状态(initial_env)、期望工具轨迹(expected_tools) 以及 最终状态断言(expected_env_check)。
先挑选 20~50 条业务高频场景和容易出错的边界场景,存为 test_cases.json:
[
{
"id": "case_001",
"query": "帮我把订单 ORD_123 退掉,我不想要了",
"initial_env": {
"orders": {
"ORD_123": {"status": "paid", "amount": 199}
}
},
"expected_tools": [
{"name": "get_order", "args": {"order_id": "ORD_123"}},
{"name": "refund_order", "args": {"order_id": "ORD_123"}}
],
"expected_env_check": {
"path": ["orders", "ORD_123", "status"],
"equals": "refunded"
}
}
]
第三步:编写核心评测脚本计算“硬指标”(eval_runner.py)
有了 Trace 和测试集,我们不需要任何黑盒魔法,直接用确定性的 Python 逻辑就能算出 80% 的核心指标。新建 eval_runner.py:
import json
import copy
import numpy as np
def evaluate_single_run(trace: dict, case: dict) -> dict:
"""计算单次运行的各项硬性指标"""
actual_tools = trace["called_tools"]
expected_tools = case["expected_tools"]
# 1. 工具选择召回率与精确率 (Tool Recall & Precision)
actual_names = [t["name"] for t in actual_tools]
expected_names = [t["name"] for t in expected_tools]
matched_tools = sum(1 for name in expected_names if name in actual_names)
tool_recall = matched_tools / len(expected_names) if expected_names else 1.0
tool_precision = matched_tools / len(actual_names) if actual_names else 0.0
# 2. 参数提取准确率 (Parameter Accuracy)
correct_params, total_params = 0, 0
for exp_t in expected_tools:
act_t = next((t for t in actual_tools if t["name"] == exp_t["name"]), None)
for k, v in exp_t.get("args", {}).items():
total_params += 1
if act_t and act_t.get("args", {}).get(k) == v:
correct_params += 1
param_accuracy = correct_params / total_params if total_params > 0 else 1.0
# 3. 轨迹效率 (Step Efficiency)
step_efficiency = min(1.0, len(expected_tools) / len(actual_tools)) if actual_tools else 0.0
# 4. 死循环检测 (Loop Detection:连续两次调用同名同参工具)
has_loop = any(
actual_tools[i]["name"] == actual_tools[i-1]["name"] and
actual_tools[i]["args"] == actual_tools[i-1]["args"]
for i in range(1, len(actual_tools))
)
# 5. 端到端环境状态验证 (State Assertion:检查数据库状态是否真正改变)
check = case["expected_env_check"]
state_val = trace["final_env_state"]
for key in check["path"]:
state_val = state_val.get(key, {})
task_success = (state_val == check["equals"])
return {
"task_success": int(task_success),
"tool_recall": tool_recall,
"tool_precision": tool_precision,
"param_accuracy": param_accuracy,
"step_efficiency": step_efficiency,
"has_loop": int(has_loop),
"latency": trace["e2e_latency"],
"tokens": trace["total_tokens"]
}
def run_benchmark(test_cases_file: str, k_runs: int = 3):
"""批量运行测试集,支持计算 Pass^k 稳定性指标"""
with open(test_cases_file, "r", encoding="utf-8") as f:
cases = json.load(f)
all_metrics, pass_at_1_list, pass_hat_k_list = [], [], []
for case in cases:
k_successes = []
for _ in range(k_runs):
# 每次运行前深拷贝沙盒环境,确保测试之间互不污染
env_copy = copy.deepcopy(case["initial_env"])
trace = run_my_agent(case["query"], env_copy)
res = evaluate_single_run(trace, case)
all_metrics.append(res)
k_successes.append(res["task_success"])
pass_at_1_list.append(k_successes[0])
# 只有 k 次运行全部成功,Pass^k 才记为 1
pass_hat_k_list.append(1 if sum(k_successes) == k_runs else 0)
total_success_runs = sum(m["task_success"] for m in all_metrics)
total_tokens = sum(m["tokens"] for m in all_metrics)
latencies = [m["latency"] for m in all_metrics]
report = {
"1_任务成功率 (Pass@1)": f"{np.mean(pass_at_1_list) * 100:.1f}%",
f"2_稳定性指标 (Pass^{k_runs})": f"{np.mean(pass_hat_k_list) * 100:.1f}%",
"3_工具召回率 (Tool Recall)": f"{np.mean([m['tool_recall'] for m in all_metrics]) * 100:.1f}%",
"4_参数准确率 (Param Accuracy)": f"{np.mean([m['param_accuracy'] for m in all_metrics]) * 100:.1f}%",
"5_轨迹效率 (Step Efficiency)": f"{np.mean([m['step_efficiency'] for m in all_metrics]) * 100:.1f}%",
"6_死循环率 (Loop Rate)": f"{np.mean([m['has_loop'] for m in all_metrics]) * 100:.1f}%",
"7_平均耗时 / P95耗时": f"{np.mean(latencies):.2f}s / {np.percentile(latencies, 95):.2f}s",
"8_单次成功平均Token成本": f"{total_tokens / total_success_runs:.0f} tokens" if total_success_runs else "INF"
}
print("================ Agent 自动化评测报告 ================")
print(json.dumps(report, indent=2, ensure_ascii=False))
if __name__ == "__main__":
run_benchmark("test_cases.json", k_runs=3)
四、 进阶评测:如何搞定“软指标”与“混沌测试”?
代码断言能解决工具和状态校验,但面对回答是否存在幻觉以及接口挂了 Agent 能否自愈这类复杂场景,我们需要引入两个进阶武器。
1. 引入 LLM-as-a-Judge 评测幻觉与忠实度
对于开放式文本输出,推荐使用开源评测框架 DeepEval(pip install -U deepeval)。将我们在第一步拿到的 trace 喂给裁判模型,即可自动检验 Agent 是否存在“脱离工具返回结果瞎编乱造”的行为:
from deepeval import evaluate
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import ToolCorrectnessMetric, TaskCompletionMetric, FaithfulnessMetric
# 1. 运行 Agent 获取运行轨迹
env = {"orders": {"ORD_123": {"status": "paid", "amount": 199}}}
trace = run_my_agent("帮我把订单 ORD_123 退掉", env)
# 2. 组装 DeepEval 测试用例
test_case = LLMTestCase(
input="帮我把订单 ORD_123 退掉",
actual_output=trace["final_answer"],
tools_called=[
ToolCall(name=t["name"], input_parameters=t["args"], output=t["output"])
for t in trace["called_tools"]
],
expected_tools=[
ToolCall(name="get_order", input_parameters={"order_id": "ORD_123"}),
ToolCall(name="refund_order", input_parameters={"order_id": "ORD_123"})
],
# 将工具返回的原始 JSON 作为事实依据(Context),用于检测幻觉
retrieval_context=[t["output"] for t in trace["called_tools"]]
)
# 3. 执行多维度自动化打分
metrics = [
ToolCorrectnessMetric(threshold=0.8), # 工具与参数匹配度
TaskCompletionMetric(threshold=0.8), # 端到端任务完成度
FaithfulnessMetric(threshold=0.9) # 忠实度:核对回答中的数字/状态是否严格源自工具返回
]
evaluate(test_cases=[test_case], metrics=metrics)
2. 无侵入故障注入:测试“故障恢复率”
线上环境瞬息万变,外部 API 随时可能超时或报错。我们无需修改业务工具源码,只需在测试脚本中加一个故障注入装饰器(Fault Injection Wrapper),就能测试 Agent 的自愈能力:
def inject_fault_wrapper(tool_func):
"""混沌测试装饰器:强制让工具的第一次调用抛出 503 超时异常"""
call_count = 0
def wrapper(*args, **kwargs):
nonlocal call_count
call_count += 1
if call_count == 1:
return {"error": "503 Service Unavailable: Database timeout, please retry."}
return tool_func(*args, **kwargs)
return wrapper
# 评测逻辑:
# 将被包装后的工具挂载给 Agent 跑完测试集。
# 故障恢复率 = (经历首次报错后最终依然达成 task_success == 1 的用例数) / 总测试数
五、 工程化闭环:如何将评测融入研发日常?
写好评测脚本只是第一步,真正让 Agent 质量实现螺旋上升的,是建立团队级的 Eval-Driven Development(评测驱动开发) 规范:
- 建立质量基线(Baseline):在项目初期构建包含 30~50 条核心场景的 Golden Dataset(黄金测试集),跑出第一份体检报告。
- 设立变更门禁(CI/CD Gate):每当团队成员修改了 System Prompt、调整了工具的 Docstring 描述、或者升级了底层基座模型时,必须运行 python eval_runner.py。如果 \(\text{Pass}^3\) 或参数准确率出现倒退,严禁合入主干分支。
- 生产 Badcase 持续回流(Data Flywheel):在生产环境接入 Langfuse 或 Arize Phoenix 等开源可观测性平台。每周定期筛选线上触发死循环、耗时超长或用户点踩的真实 Trace,脱敏后转化为新的测试用例追加进 test_cases.json。
结语
AI Agent 的开发正在从“手工作坊式的 Prompt 炼丹”走向“严密的现代软件工程”。当你把黑盒的 Agent 拆解为清晰的结果、轨迹、成本、安全四大指标,并用自动化脚本将其牢牢锁进研发流水线时,你的每次模型升级和提示词调优都将变得心中有数、有据可依。
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
In the development of AI Agent, many teams are in the same quagmire - "Vibe Checking." Changed one line of prompt, ran three examples to get smart, and then the first line went up and died in another limbo.
There is a gold standard for software engineering: “No improvement is possible without measurement”. This paper will strip the theory of the void, from the perspective of first-line engineering landings, the systematic dismantling assessment of the 4 core dimensions of AI Agent, 15 key indicators, and the provision of an automated Python assessment program with less than 200 line codes that can run locally.
I. Why is the traditional LLM rating wrong with Agent?
Evaluate an ordinary LLM dialogue application (Chatbot) which usually requires only the text likeness or semantic quality of Input and Output Answer. But the essence of Agent (AI agent) is “an autonomous system capable of multistep reasoning and tool call”.
If you look only at the final output, you'll miss a lot of fatal engineering hazards:
- The result was correct, and the process was all wrong: the user asked about the order status, the query interface timeout, and Agent happened to be “designed”, which was a serious illusion in the production environment.
- Invisible bankruptcy (cost out of control): Agent was able to check the data at 2 steps, and it was wrong eight times backstage to fix the tool. Users seem to have a perfectly normal result, but Token billing and interfaces have doubled fourfold.
- State pollution (by-effects): Agent's mouth answered “refunds have been cancelled for you”, but there was no successful trigger of the back end's cancel refund interface.
Therefore, the assessment Agent must complete the paradigm shift from “point-to-point text assessment” to “Trackory and State of the Environment assessment”.
II. AI Agent Core Assessment Indicator Panorama
To measure the performance of Agent in its entirety, we need to establish a four-dimensional assessment system that is both internal and qualitative and cost:
Task Metrics - Delivery Bottom Line
Focus on whether Agent ultimately really solves the business problems of users.
- Task success rate (Task Access Late / (\text{Pass@)): One-time operation to achieve the final operational objective. This is the most basic delivery line.
- Stability indicator (\(\text{Pass}^k\)): The probability that the same task is running continuously \(k\) every success.
💡 Why is it more important than (\text{Pass@k\)? \\(\text{Pass@k\) is common in the field of code generation (\k\) (runs only once if they succeed once), but only once in the production level Agent. If a single Agent success rate is 80%, only 32.7% of the total probability of running five times (\text{Pass}^5 = 0.8^5). \\(text{Pass}^) is extremely sensitive to the uncertainties of the system.
- Progress Late: Number of sub-goals completed / Number of sub-goals for tasks. For multi-step long-link missions, avoid giving only 0 or 1 cents to cold ice, and help develop and locate which link Agent is stuck to.
- Groudedness / Faithfulness: The final answer contains the percentage of statements supported by the return of data (Observation), which is used to precision the hallucinogenic rate of quantization “compiled data”.
Tracks and tool dimensions (Trackory & Tool Metrics) - reasoning
Open the black box to assess the quality and tolerance of Agent's planning during implementation.
Performance & Cost Dimensions - Commercial Feasibility
- End-to-end delay (E2E Latency & P95/P99): from initiation of the request to completion of all tool call and output of the total time taken to respond. Analysis requires split to be LLM reasoning time-consuming with external API response.
- First action time (Time to First Tool / Token): key indicators determining the flow flow of the user body.
- Cost per successful mission:
\ (\text{Cost per Access} = \frac/2003/text{total consumption cost of test set (including cost of failed Z97Z Token and API)} \text{total number of final successful tasks}
Note: Do not count only “average consumption per request token”. If the failure rate is high, the cost of failure has to be amortized to a successful mission, which is the real commercial ROI.
Safety and compliance dimensions (Safety & Policy Metrics) - production of red lines
- Excessive operational interception rate: the percentage of manual double confirmations (human-in-the-load) that have been successfully refused or triggered as required in the face of sensitive writing operations (e.g., redacting, large refunds, modification of permission).
- Compliance rate for field rules (Policy Compliance): strict adherence to operational red lines (e.g., “no details of orders are disclosed without verification of identity”) will not be irregular in order to favour users.
- Injection of resistance (Injection Research) by prompt: the proportion of persons who are not kidnapped when a malicious instruction (Indirect Prompt Injection) is attached to a page, mail or document that is retrieved externally.
III. Operational: less than 200 lines of code to automate current assessment
Many of the teams were delayed in conducting quantization assessments because they felt that the platform was too heavy. In fact, you don't need to introduce a huge third-party system at the outset, but follow the following three-step structure, using pure Python, to run the full baseline test in the afternoon:
Step 1: Alter the Agent interface to expose the running trajectory
To evaluate Agent, your primary entry function must not return a string. We need to keep track of every step it takes in the Agent internal cycle and return to a standardized dictionary structure:
import time
def run_my_agent(user_query: str, mock_env: dict) -> dict:
"""
Packing Agent portal function, complete exposure to the execution track (Trace) and sandbox state, in addition to returning the final return
"""
start_time = time.time()
first_action_time = None
called_tools = []
total_tokens = 0
== sync, corrected by elderman == @elder man
# [Action 1] Call order query tool
if first_action_time is None:
first_action_time = time.time() - start_time
tool_output_1 = {"order_id": "ORD_123", "status": "paid", "amount": 199}
called_tools.append({
"name": "get_order",
"args": {"order_id": "ORD_123"},
"output": str(tool_output_1),
"error": None
})
total_tokens += 450
# [Action 2] Call refund tool (note: write to directly modify the mock env sandbox dictionary)
mock_env["orders"]["ORD_123"]["status"] = "refunded"
called_tools.append({
"name": "refund_order",
"args": {"order_id": "ORD_123", "reason": "user_request"},
"output": '{"success": true}',
"error": None
})
total_tokens += 380
Final answer = "A refund of ORD 123 has been processed for you in the amount of 199 yuan to return the same path."
# ==========================================================
e2e_latency = time.time() - start_time
return {
"final_answer": final_answer,
"called tools": called tools, # Core: actual calling tool chain
"e2e latency": e2e latecy, #end-to-end time (sec)
"ttft": first action time or e2 latency, # first action time (sec)
"Total tokens": total tokens, # total consumption Token
"final env state": mock env # Core: state of the sandbox environment after execution
}
Step 2: Build baseline test sets for sandbox status (test cases.json)
Unlike the traditional question-and-answer test set, Agent ' s test examples must include initial state of the environment (initial env), expected tool tracks (expected tools) and end-state assertions (expected env check).
20-50 operational high frequency (HF) scenes and error-prone border scenes first.
[
{
"id": "case_001",
Query: "Help me get the order ORD 123 back, I don't want it anymore."
"initial_env": {
"orders": {
"ORD_123": {"status": "paid", "amount": 199}
}
},
"expected_tools": [
{"name": "get_order", "args": {"order_id": "ORD_123"}},
{"name": "refund_order", "args": {"order_id": "ORD_123"}}
],
"expected_env_check": {
"path": ["orders", "ORD_123", "status"],
"equals": "refunded"
}
}
]
Step 3: Preparation of core assessment scripts to calculate “hard indicators” (eval runner.py)
With Trace and the test set, we don't need any black box magic, and we can calculate 80% of the core indicators with certainty Python logic. New eval runner.py:
import json
import copy
import numpy as np
def evaluate_single_run(trace: dict, case: dict) -> dict:
"""""""""""""""""""""""
actual_tools = trace["called_tools"]
expected_tools = case["expected_tools"]
# 1. Tool selection recall rate and accuracy (Tool Recall & Precism)
actual_names = [t["name"] for t in actual_tools]
expected_names = [t["name"] for t in expected_tools]
matched_tools = sum(1 for name in expected_names if name in actual_names)
tool_recall = matched_tools / len(expected_names) if expected_names else 1.0
tool_precision = matched_tools / len(actual_names) if actual_names else 0.0
# 2. Accuracy of parameter extraction (Parameter Accuracy)
correct_params, total_params = 0, 0
for exp_t in expected_tools:
act_t = next((t for t in actual_tools if t["name"] == exp_t["name"]), None)
for k, v in exp_t.get("args", {}).items():
total_params += 1
if act_t and act_t.get("args", {}).get(k) == v:
correct_params += 1
param_accuracy = correct_params / total_params if total_params > 0 else 1.0
# 3. Track efficiency (Step Efficacy)
step_efficiency = min(1.0, len(expected_tools) / len(actual_tools)) if actual_tools else 0.0
#4. Dead circulation test (Loop Development: two consecutive calls to the same name and participation tool)
has_loop = any(
actual_tools[i]["name"] == actual_tools[i-1]["name"] and
actual_tools[i]["args"] == actual_tools[i-1]["args"]
for i in range(1, len(actual_tools))
)
#5. End-to-end environmental status verification (State Asservation: Check for real changes in database status)
check = case["expected_env_check"]
state_val = trace["final_env_state"]
for key in check["path"]:
state_val = state_val.get(key, {})
task_success = (state_val == check["equals"])
return {
"task_success": int(task_success),
"tool_recall": tool_recall,
"tool_precision": tool_precision,
"param_accuracy": param_accuracy,
"step_efficiency": step_efficiency,
"has_loop": int(has_loop),
"latency": trace["e2e_latency"],
"tokens": trace["total_tokens"]
}
def run_benchmark(test_cases_file: str, k_runs: int = 3):
""Bulk Run Test Set, which supports the calculation of Pass^k stability indicator"
with open(test_cases_file, "r", encoding="utf-8") as f:
cases = json.load(f)
all_metrics, pass_at_1_list, pass_hat_k_list = [], [], []
for case in cases:
k_successes = []
for _ in range(k_runs):
# Every time a deep copy of the sandbox environment before running, make sure there's no contamination between tests
env_copy = copy.deepcopy(case["initial_env"])
trace = run_my_agent(case["query"], env_copy)
res = evaluate_single_run(trace, case)
all_metrics.append(res)
k_successes.append(res["task_success"])
pass_at_1_list.append(k_successes[0])
# Only when the k runs all succeeded, Pass^k will be one
pass_hat_k_list.append(1 if sum(k_successes) == k_runs else 0)
total_success_runs = sum(m["task_success"] for m in all_metrics)
total_tokens = sum(m["tokens"] for m in all_metrics)
latencies = [m["latency"] for m in all_metrics]
report = {
"1 Task success rate (Pass@1)": f"{np.mean (pass at 1 list)*100:1f},
f "2 Stability Indicators": f" {np.mean” (pass hat k list) *100:1f}%
"3 Tool Recall": f"{np.mean (['tool recall'] for m in all metrics])* 100:1f}%,
"4 Accuracy of Parameters": f"{np.mean(m['param accuracy'] for m in all metrics]* 100:1f}%,
"Step Efficacy": f"{np.mean] (m ['step efficacy'] for m in all metrics]*100:1f},
"6 Loop Rate": f"{np.mean (['has loop'] for m in all metrics])* 100:1f}%,
"7 average latency/P95 Time-consuming: f"{np.mean (latencies): 2f}s / {np.percentile (latencies, 95): 2f}s,
"8 Single Success Average Token Cost": f"{total tokens /total success runs:.0f}tokens" if total subs runs else "INF"
}
== sync, corrected by elderman == @elder man
print(json.dumps(report, indent=2, ensure_ascii=False))
if __name__ == "__main__":
run_benchmark("test_cases.json", k_runs=3)
Progress assessment: How to address soft indicators and confusion tests?
The code says it solves the tools and the verification of the state, but we need to introduce two step-by-step weapons in the face of the illusion of the answer and the fact that the interface hangs up a complex scene like Agent.
1. Introduction of LLM-as-a-Judge to evaluate hallucinations and loyalty
For the open text output, it is recommended to use the Open Source Assessment Framework. Feeding this to the referee model, which we got in the first step, will automatically test whether Agent's "return from the tool" is being fabricated:
from deepeval import evaluate
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import ToolCorrectnessMetric, TaskCompletionMetric, FaithfulnessMetric
#1. Run Agent to get track
env = {"orders": {"ORD_123": {"status": "paid", "amount": 199}}}
Trace = run my agent
#2. Build DeepEval Test Example
test_case = LLMTestCase(
Input= "Help me get the order RD 123 back,"
actual_output=trace["final_answer"],
tools_called=[
ToolCall(name=t["name"], input_parameters=t["args"], output=t["output"])
for t in trace["called_tools"]
],
expected_tools=[
ToolCall(name="get_order", input_parameters={"order_id": "ORD_123"}),
ToolCall(name="refund_order", input_parameters={"order_id": "ORD_123"})
],
# Use the original JSON of the tool returned as the basis of fact (Context) to detect hallucinations
retrieval_context=[t["output"] for t in trace["called_tools"]]
)
# 3. Implement multidimensional automated scoring
metrics = [
ToolCorrectnessMetric (threshold = 0.8), # tools match parameters degrees
TaskComppletionMetric (threshold = 0.8), #end-to-end task completed degrees
FaithfulMetric (threshold = 0.9) # Faith: check whether the numbers/states in the answers are strictly derived from the tool return
]
evaluate(test_cases=[test_case], metrics=metrics)
Injection without intrusion: test "facility recovery rate"
The online environment is rapidly changing, and external API can be timeout or wrong at any time. We don't need to change the business tool source code, we just need to add a Fault Injection Wrapper to the test script to test Agent's self-rehabilitation:
def inject_fault_wrapper(tool_func):
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""")"""""""""""""""""""""""""")"""""""""""""""""""" """"""""""""""""""""""
call_count = 0
def wrapper(*args, **kwargs):
nonlocal call_count
call_count += 1
if call_count == 1:
return {"error": "503 Service Unavailable: Database timeout, please retry."}
return tool_func(*args, **kwargs)
return wrapper
# Evaluation logic:
# A packaged tool will be mounted on Agent after running the test set.
# Failure recovery rate = (the number of cases that eventually reached after the first error) / Total test
V. Engineering closed loops: How can evaluations be integrated into R & D routines?
Writing a good script is only the first step, and it is the team-level Eval-Driving Development that really gives Agent mass a spiral.
- Establishment of a quality baseline (Baseline): Construction of Golden Dataset (Golden Test Set), consisting of 30-50 core scenarios, in the early stages of the project, to run out of the first medical report.
- Create door change (CI/CD Gate): python eval runner.py must run whenever team members modify System Prompt, adjust tool Docstring descriptions or upgrade bottom base models. If \\(text{Pass} or parameter accuracy is reversed, integration into the backbone branch is strictly forbidden.
- Production Badcase Continuous Return (Data Flywheel): Open source observational platforms such as Langfuse or Arize Phoenix, in the production environment. The real Trace, which triggers a dead cycle, takes too long or is stepped on by the user point on a regular weekly filter line, is replaced by a new test example after dissensitisation.
Concluding remarks
The development of AI Agent is moving from a "workshop-style Prompt Refinery" to a "close modern software project". When you break the black box's Agent into clear results, tracks, costs, safety indicators, and lock it in an automated script, every upgrade of your model and prompt will become measurable and evidence-based.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.