引子
上周我在公司跑一组智能体回归测试,同一个任务跑了 30 遍,通过率 67%。
同事看了一眼说:"那这智能体到底行不行?"
我答不上来。
在传统软件测试里,"行不行"是个非黑即白的问题。同一个输入,跑 100 遍,结果应该一模一样。assert add(2, 3) <span class="wx-em-red"> 5,这个测试要么 pass,要么 fail,没有中间状态。
但智能体不是这样的。同一个任务,temperature 不同,输出的文本可能不同。同一组 30 次运行,可能出现多种不同的输出文本,但其中大部分都算"答对了"。
这不是智能体的问题。这是测试方法的问题。
我们用确定性测试的尺子,去量一个概率性的东西,量出来的数据没有决策价值。
这篇文章不讨论智能体本身,只讨论一件事:传统测试方法为什么对智能体失效,以及失效之后该怎么调整思路。
传统测试的三个假设
所有传统软件测试方法论都建立在三个前提上。这三个前提在智能体场景下全部不成立。
假设一:相同输入产生相同输出
这是测试可重复性的基础。一个 bug 能复现,说明输入和输出之间有确定的因果关系。测试工程师写用例、造数据、跑脚本,核心逻辑就是控制变量、观察输出。
智能体打破了这个假设。同样的任务描述,同一个模型,同样的 API 调用,输出可能不同。原因有三:
- temperature 参数:控制生成的随机性。temperature > 0 时,每次采样路径不同。
- 上下文窗口截断:对话历史超过窗口限制时,早期信息被丢弃。同样的任务,在第 3 轮和第 13 轮跑,上下文不同,输出不同。
- 外部依赖波动:工具调用(API、数据库、网页抓取)的响应时间和返回内容可能有微小差异,这些差异会进入 LLM 的下一轮推理,放大成不同的输出。
假设二:预期结果可以精确描述
传统测试用例的标准格式是:
| 输入 | 预期输出 | 实际输出 | 结果 |
|---|---|---|---|
add(2, 3) | 5 | 5 | pass |
预期输出是精确的、可比较的。assert expected </span> actual,一行代码搞定。
智能体的输出不是这样的。"分析这份销售数据"——什么叫分析对了?
- 智能体 A 输出了 3 个图表 + 一段文字总结,得 78 分
- 智能体 B 输出了 5 个图表 + 三段文字总结,得 72 分
- 智能体 C 只输了一段文字,但指出了关键趋势,得 85 分
三个输出格式完全不同,但质量有高低。"预期输出"无法用精确值描述,只能用评分标准来衡量。
评分代替断言之后,新问题来了:谁来评?怎么评?评的标准一致吗?这些是后面几篇要展开的内容,这里只点出问题。
假设三:缺陷可以精确复现
传统测试发现 bug 后,第一步是复现。复现不了,开发不认。
智能体的缺陷复现困难。一个任务在第 7 次运行时失败了,第 8 次用同样的参数跑,可能又成功了。不是 bug 修了,是随机性导致的波动。
这导致三个后果:
- 缺陷报告质量下降:无法提供稳定的复现步骤,开发无法定位根因。
- 回归判断失真:改了一行代码,跑测试发现通过率从 67% 变成 65%,不知道是代码改坏了还是随机波动。
- 根因定位靠猜:输出错误时,不知道是 Prompt 问题、工具问题、模型问题、还是上下文问题。
失效的五个环节
传统测试流程有五个核心环节。智能体场景下,每个环节都需要改造。
环节一:用例设计
传统方式:写精确的预期输出。
智能体场景:预期输出是模糊的。需要改为写"成功标准"而非"预期值"。
比如,"计算 25 * 4 + 100 / 5"这个任务:
- 传统测试:预期输出 =
"120" - 智能体测试:成功标准 = 输出中包含数字
120,且没有明显的计算错误
再比如,"分析销售数据生成周报":
- 传统测试:无法写用例(输出不固定)
- 智能体测试:成功标准 = 包含至少 3 个分析维度 + 有明确的结论 + 数据引用正确
环节二:环境控制
传统方式:固定测试环境,控制所有变量。
智能体场景:需要控制的是 LLM 相关的变量:
- temperature:测试时建议固定,避免随机性干扰
- seed:如果模型支持,固定随机种子提高可重复性
- 模型版本:锁定具体版本(如
qwen-plus-0813),避免模型升级导致行为漂移 - 工具状态:Mock 外部依赖,避免网络波动影响测试结果
环节三:执行策略
传统方式:每个用例跑一次,pass 或 fail。
智能体场景:需要多次运行,统计分布。
单次运行 → 统计 30 次运行的成功率 单次断言 → 统计分布(P50、P90、通过率)这不是"多跑几次"那么简单。需要定义:
- 运行次数:30 次是经验值(中心极限定理,n≥30 时样本均值近似正态分布)
- 通过阈值:成功率 ≥ 70% 算通过,还是 ≥ 80%?这取决于业务场景
- 波动容忍度:P50 和 P90 的差异超过多少需要告警?
环节四:缺陷定位
传统方式:看日志、看堆栈、看输入输出差异。
智能体场景:智能体的执行过程是黑盒的。需要额外的可测性设计:
- 结构化日志:每个阶段(规划、执行、反思、总结)输出标准化日志
- 状态暴露:暴露当前阶段、已执行子任务、失败原因
- 检查点机制:关键节点输出检查点,支持断点验证
这些是第 6 篇的内容,这里只提结论:不可测的系统无法自动化,智能体需要主动暴露内部状态。
环节五:回归判断
传统方式:跑测试,对比 pass/fail 数量。
智能体场景:需要对比的是分布,不是单次结果。
旧版本:30 次运行,通过率 67%,平均 token 消耗 4200 新版本:30 次运行,通过率 65%,平均 token 消耗 4500 结论:通过率下降 2%,token 消耗增加 7%。需要判断: 1. 2% 的下降是统计显著还是随机波动? 2. 7% 的 token 增加是否在预算范围内? 3. 哪些用例从 pass 变成了 fail?这需要版本对比工具,不是简单的 diff。这是第 15 篇的内容。
代码:温度对稳定性的影响
理论讲完了,看数据。
下面这个脚本,用同一个智能体、同一个任务、不同 temperature 各跑 30 次,统计成功率和输出一致性。
#!/usr/bin/env python3 """ 温度对智能体稳定性的影响实验 对比 temperature=0.3/0.7/1.0 三种设置下, 同一任务跑 30 次的成功率和输出一致性。 注:CustomAgent 是你项目中的封装,实际使用时替换为你的 Agent 类。 核心逻辑:固定 temperature,多次运行,统计分布。 """ import os import statistics import requests from collections import Counter # 你的 API Key,从环境变量读取 API_KEY = os.getenv("DASHSCOPE_API_KEY") API_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" MODEL = "qwen-plus" TASK = "计算 25 * 4 + 100 / 5,把结果存储到记忆中" EXPECTED_ANSWER = "120" N_RUNS = 30 TEMPERATURES = [0.3, 0.7, 1.0] def call_llm(temperature: float) -> dict: """调用 LLM API""" resp = requests.post(API_URL, headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", }, json={ "model": MODEL, "messages": [{"role": "user", "content": TASK}], "max_tokens": 2048, "temperature": temperature, }, timeout=60) if resp.status_code <span class="wx-em-red"> 200: result = resp.json() return { "success": True, "content": result["choices"][0]["message"]["content"], "tokens": result.get("usage", {}).get("total_tokens", 0), } return {"success": False, "error": resp.text[:200]} def check_success(result: dict) -> bool: """检查任务是否成功(输出包含正确答案)""" if not result.get("success"): return False return EXPECTED_ANSWER in result.get("content", "") def check_output_consistency(results: list) -> float: """检查输出一致性:最高频输出占总成功数的比例""" outputs = [r.get("content", "").strip() for r in results if r.get("success")] if not outputs: return 0.0 counts = Counter(outputs) most_common_count = counts.most_common(1)[0][1] return most_common_count / len(outputs) def run_experiment(): print(f"任务: {TASK}") print(f"运行次数: {N_RUNS} 次/温度") print(f"模型: {MODEL}") print() for temp in TEMPERATURES: results = [] for i in range(N_RUNS): result = call_llm(temp) results.append(result) # 统计 success_count = sum(1 for r in results if check_success(r)) success_rate = success_count / N_RUNS consistency = check_output_consistency(results) tokens = [r.get("tokens", 0) for r in results if r.get("success")] avg_tokens = statistics.mean(tokens) if tokens else 0 print(f"temperature={temp}: 成功率={success_rate:.0%}, " f"一致性={consistency:.0%}, 平均Token={avg_tokens:.0f}") if __name__ </span> "__main__": run_experiment()数据是 2026-05-08 真实跑的,模型 qwen-plus,每个温度跑 30 次:
| temperature | 成功率 | 输出一致性 | 唯一输出数 | 平均 Token | P50 Token | 平均耗时 |
|---|---|---|---|---|---|---|
| 0.3 | 100% | 50% | 6 | 167 | 165 | 3.6s |
| 0.7 | 100% | 50% | 7 | 165 | 165 | 3.7s |
| 1.0 | 100% | 67% | 9 | 156 | 151 | 3.5s |
几个观察:
简单任务下 temperature 影响有限:计算题对所有 temperature 都 100% 正确。temperature 的差异在更复杂的任务(如创意写作、推理)中会更明显。
输出一致性不是单调的:temperature=1.0 时一致性反而更高(67% vs 50%)。这说明一致性不仅受 temperature 影响,还和采样路径、任务类型有关。
Token 消耗没有随 temperature 单调变化:temperature=1.0 时平均 token 反而更少(156 vs 167)。temperature 控制的是概率分布的平滑度,不是输出长度。这个实验说明,至少在数学计算这类确定性任务上,temperature 对 token 消耗的影响不大。至于创意写作或推理任务,需要单独验证。
耗时差异不大:3.5s vs 3.6s,在误差范围内。
这个实验说明一件事:temperature 对简单任务影响有限,但对复杂任务(推理、创意、安全)的影响需要单独评估。测试时固定 temperature 是为了保证可比性,不是因为 temperature 一定会导致"失败"。
补充说明:这个实验用的是 qwen-plus 模型,任务类型是数学计算。不同模型、不同任务类型的 temperature 敏感度可能完全不同。下一篇我们会测创意写作和推理任务,数据会更有趣。
交付物:智能体测试 vs 传统测试对比清单
下面这个清单,列出了 12 个关键差异点。每个差异点都对应一个需要改造的环节。
| # | 对比维度 | 传统软件测试 | 智能体测试 | 改造方向 |
|---|---|---|---|---|
| 1 | 输出确定性 | 相同输入 → 相同输出 | 相同输入 → 不同输出 | 从断言改为统计分布 |
| 2 | 预期描述 | 精确值(expected == actual) | 模糊标准(评分) | 从 assert 改为评分机制 |
| 3 | 缺陷复现 | 可精确复现 | 概率性出现 | 多次运行 + 日志追踪 |
| 4 | 用例设计 | 输入 + 预期输出 | 输入 + 成功标准 | 从精确值改为条件判断 |
| 5 | 执行次数 | 1 次 | ≥30 次 | 统计显著性 |
| 6 | 环境控制 | 固定 OS/依赖/配置 | 固定 temperature/seed/模型版本 | 控制 LLM 相关变量 |
| 7 | 失败判断 | pass/fail | 成功率/质量分 | 从二元判断改为连续评分 |
| 8 | 回归对比 | diff 输出 | 对比分布 | 版本间统计检验 |
| 9 | 根因定位 | 日志 + 堆栈 | 结构化日志 + 检查点 | 可测性改造 |
| 10 | 性能指标 | 响应时间/吞吐量 | 延迟/Token 消耗/成功率 | 增加 LLM 特有指标 |
| 11 | 测试数据 | 边界值/等价类 | 梯度难度/对抗样本 | 增加模糊测试 |
| 12 | 报告输出 | pass/fail 统计 | 多维度得分 + 趋势 | 从表格改为雷达图 |
温度设置建议
基于上面的实验,给出测试场景的温度设置建议:
| 测试阶段 | 建议 temperature | 理由 |
|---|---|---|
| 单元测试 | 0.1-0.3 | 最大化可重复性,快速定位问题 |
| 集成测试 | 0.3 | 平衡稳定性和真实性 |
| 回归测试 | 0.3 | 版本间可比,减少随机波动干扰 |
| 探索性测试 | 0.7-1.0 | 发现边界 case,测试多样性 |
| 生产环境 | 按业务场景定 | 客服场景低温度(稳定),创意场景高温度(多样) |
核心原则:测试时温度要低,生产时温度按需求定。测试的目的是发现问题,不是模拟生产。生产环境的 temperature 由业务需求决定,但测试环境必须固定,否则数据不可比。
总结
传统测试方法对智能体失效,不是"不太好使",是"根本不对"。确定性测试的尺子量不了概率性的东西。
需要改造的不是一两个环节,是从用例设计到回归判断的全链路。这篇文章列出了 12 个差异点,给出了温度设置建议。但具体怎么改造——测试数据怎么设计、评分机制怎么做、可测性怎么加——后面逐篇展开。
下一篇讲智能体测试的 6 维能力模型。单一分数没有决策价值,需要按业务场景设计权重。