主题
接口测试失败日志的自动分类:提示词设计与 20 例验证
回归跑完,20 条失败。人工逐条排查大约 1.5 小时,而其中大半是"数据脏了""环境没刷新"。 这篇记录我用 AI 做初筛的完整过程:提示词、脱敏脚本、20 条的真实对比结果,以及 3 条判错的案例。 文中日志与数据为脱敏示例数据,脚本与流程经过实测。
0. 先看结果(TL;DR)
| 项 | 结果 |
|---|---|
| 输入 | 20 条失败用例的日志与请求响应 |
| AI 初筛耗时 | 约 3 分钟(含脱敏) |
| 与人工结论一致 | 17 / 20(85%) |
| 不一致 | 3 条(其中 1 条 AI 主动给出低置信度转人工,判错 2 条) |
| 人工节省 | 从"逐条看 20 条"变成"只看 5 条"(AI 唯一的代码缺陷 + 2 条判错 + 2 条低置信度) |
| 实测结论 | 适合排掉数据/环境/用例三类低级问题;代码缺陷仍需人确认 |
| 可拿走 | prompts/triage-log.md、scripts/sanitize_logs.py、CI 接入片段 |
1. 材料与环境(准备 10 分钟)
每条失败需要给 5 样东西(少一样准确率就下降):
bash
# ① 失败用例名与步骤(从 junit xml 提)
grep -o 'testcase name="[^"]*"' reports/junit.xml | head -20
# ② 完整堆栈(不要只给最后一行)
# ③ 原始请求响应(从用例日志或抓包取)
# ④ 环境信息:版本 + commit + 环境标识
echo "version=$(cat version.txt) commit=$(git rev-parse --short HEAD)"
# ⑤ 该用例近 5 次执行结果(判断是否首次失败)
grep "test_ORDER_009" reports/history.log | tail -5⑤ 别省:一条"昨天还过今天挂"的用例,和一条"从来没成功过"的用例,排查方向完全不同。
2. 完整提示词(复制即用)
text
【角色】你是测试开发工程师,擅长故障排查与日志分析。
【任务】把这次失败归类到以下四类之一,并给出排查步骤。
四类定义:
A 代码缺陷:断言失败且业务字段值不符;同用例稳定复现;错误堆栈指向业务逻辑代码行
B 测试数据问题:数据不存在/被消费/状态被改动;换账号或重造数据后可过
C 环境问题:连接超时、依赖服务 5xx、证书错误、配置未生效、资源紧张(连接池/线程池耗尽)
D 用例自身问题:定位器过期、断言写死时间、依赖其它用例的执行顺序
【输入】
- 用例:{{CASE_NAME_AND_STEPS}}
- 错误堆栈:{{STACKTRACE}}
- 请求与响应:{{REQUEST_RESPONSE}}
- 环境信息:{{ENV_INFO}}
- 该用例历史:{{LAST_5_RUNS}}
- 近期变更:{{RECENT_DIFF_SUMMARY}}
【输出格式】
1. 归类:A/B/C/D + 中文类别名 + 置信度(0-1)
2. 判断依据:逐条引用输入中的具体行/字段(不要只给结论)
3. 排查步骤:最多 3 步,按成本从低到高排序(先做"重跑一次""换个账号"这类低成本验证)
4. 若判定为 A(代码缺陷):疑似模块/函数 + 最小验证方式
5. 信息不足时:明确列出"还需要哪些信息",不要臆测
【硬性约束】
- 先排除 C(环境)与 B(数据)两类,再考虑 A(代码缺陷);除非堆栈明确指向业务逻辑代码
- 不要输出输入材料中未出现的异常类名或堆栈
- 不要建议"加 sleep / 加大超时"作为最终解法(可作为临时隔离手段,但要注明)
- 置信度低于 0.7 时,直接输出"无法判定,建议人工介入"3. 真实输入与输出
输入(第 1 条,脱敏):
text
用例:test_ORDER_012_create_order_concurrent
堆栈:
2026-09-21 10:02:31.884 ERROR [orders] OrderService.createOrder - create order failed, userId=1001, productId=2001
java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:696)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:196)
...
请求响应:
POST /api/v1/orders -> 500 (30012 ms)
环境:v2.4.1 / commit 9f3c1ab / staging
历史:最近 5 次 = PASS, PASS, FAIL, PASS, FAIL(不稳定)
近期变更:OrderMapper 新增批量查询方法;连接池 maxPoolSize 未变AI 输出:
text
1) 归类:C 环境问题(资源紧张 / 连接池耗尽)|置信度 0.88
2) 依据:
- 堆栈第 2 行:SQLTransientConnectionException ... request timed out after 30000ms(等连接超时)
- 响应:500 且耗时 30012ms(几乎等于连接等待上限,非业务处理耗时)
- 历史:近 5 次 2 次失败(不稳定,符合资源类特征)
- 变更:OrderMapper 新增批量查询(可能增加连接占用),但连接池配置未改
3) 排查步骤(按成本从低到高):
① 重跑该用例 3 次,观察是否失败(确认可复现性)
② 查同时间窗其它用例日志,是否同样报连接超时(判断是否全局)
③ 查 DB 当前连接数与 order 相关慢查询,确认是否存在连接未释放
4) 若③发现长事务未释放 → 则需要转 A(代码缺陷)复查;当前材料不足以判定为 A
5) 补充信息需求:连接池配置(maxPoolSize/idleTimeout)、该时间窗 DB 连接数曲线我按它给的步骤跑了一遍:① 重跑 3 次失败 2 次;② 同时段其它用例也有 2 条超时 → 确实是环境/资源问题,不是这条用例的问题。人工结论一致 ✅
20 条的分类结果对比:
| 人工最终结论 | AI 判定一致 | AI 判错 |
|---|---|---|
| 代码缺陷(8 条) | 7 | 1(AI 判成环境) |
| 测试数据问题(6 条) | 5 | 1(AI 判成用例问题) |
| 环境问题(4 条) | 4 | 0 |
| 用例自身问题(2 条) | 1 | 0(另 1 条 AI 给 0.55 置信度转人工) |
| 合计 20 | 17(85%) | 2 + 1 转人工 |
4. 完整脱敏脚本(必须先跑,再喂模型)
失败日志里几乎必然含凭证与 PII,必须自动化脱敏(人工检查一定会漏):
python
#!/usr/bin/env python
"""scripts/sanitize_logs.py
对失败日志做脱敏后输出,供 AI 初筛使用。
用法:python scripts/sanitize_logs.py raw/001.log > sanitized/001.log
"""
import re
import sys
RULES = [
# 凭证类
(re.compile(r"(Authorization:\s*)(\S+)", re.I), r"\1<TOKEN>"),
(re.compile(r"(Cookie:\s*)(.+)", re.I), r"\1<COOKIE>"),
(re.compile(r"(session|sid|jsessionid)=[A-Za-z0-9_\-\.]+", re.I), r"\1=<SESSION>"),
(re.compile(r'(?i)(password|passwd|pwd)"\s*:\s*"[^"]+"'), r'\1":"<PWD>"'),
(re.compile(r"(api[_-]?key|secret|access[_-]?token)[\"'\s:=]+[A-Za-z0-9_\-\.]{8,}", re.I),
r"\1=<SECRET>"),
# PII
(re.compile(r"1[3-9]\d{9}"), "138****0000"),
(re.compile(r"\b\d{17}[\dXx]\b"), "<IDCARD>"),
(re.compile(r"\b\d{16,19}\b"), "<BANKCARD>"),
(re.compile(r"[\w\.\-]+@[\w\-]+\.[A-Za-z]{2,}"), "<EMAIL>"),
# 内网与主机信息
(re.compile(r"\b(10|172\.(1[6-9]|2\d|3[01])|192\.168)\.\d{1,3}\.\d{1,3}\b"), "<INTRANET_IP>"),
(re.compile(r"\b[\w\-]+\.(internal|local|corp)\b", re.I), "<INTERNAL_HOST>"),
# 其他
(re.compile(r"\b(?:mysql|redis|postgres)://\S+"), "<DB_URL>"),
]
def sanitize(text: str) -> str:
for pattern, repl in RULES:
text = pattern.sub(repl, text)
return text
if __name__ == "__main__":
raw = open(sys.argv[1], encoding="utf-8", errors="replace").read()
cleaned = sanitize(raw)
# 截断:只保留错误堆栈 + 前后 20 行上下文(避免超长)
lines = cleaned.splitlines()
keep, idx = [], [i for i, l in enumerate(lines) if re.search(r"(ERROR|Exception|FAILED|assert)", l)]
windows = {j for i in idx for j in range(max(0, i - 20), min(len(lines), i + 20))}
keep = [lines[i] for i in sorted(windows)]
sys.stdout.write("\n".join(keep))
sys.stderr.write(f"[sanitize] 原文 {len(lines)} 行 -> 保留 {len(keep)} 行\n")运行:
bash
python scripts/sanitize_logs.py raw/001.log > sanitized/001.log
# [sanitize] 原文 4182 行 -> 保留 156 行 ← 2.1MB 压到 18KB
grep -c "<TOKEN>\|<COOKIE>\|<INTRANET_IP>" sanitized/001.log
# 7 ← 脱敏命中 7 处5. 3 个卡住我的坑
坑 1:直接把 2MB 原始日志丢过去 → 请求超限且模型只看开头
现象:返回被截断,结论只基于日志前 200 行(恰好是启动日志,没有错误)。 原因:日志体积远超上下文,且错误信息通常在末尾。 解决:只保留错误行 ±20 行上下文(见脚本里的截断逻辑),2.1MB → 18KB,准确率明显提升。验证方式:
bash
wc -c sanitized/001.log # 18k坑 2:脱敏漏了 Cookie,测试账号的 session 直接进了模型
现象:第一次没写 Cookie 规则,脱敏后仍能看到 Cookie: JSESSIONID=...。 原因:我只按"Authorization"做了规则,忽略了其他凭证载体。 解决:把凭证载体列成清单(Authorization / Cookie / session / api key / secret / DB URL),每条一个正则;并加一步自检:
bash
grep -Ei "authorization|cookie|session|secret|apikey" sanitized/001.log || echo "无残留凭证"坑 3:模型倾向判"代码缺陷",把环境问题也算成 bug
现象:前 5 条里 AI 把 3 条环境问题判成 A(代码缺陷),导致误提缺陷。 原因:模型默认往"最有价值"的方向归因。 解决:在约束里强制排除顺序:"先排除 C(环境)与 B(数据),再考虑 A(代码)",并要求"判 A 必须给出指向业务代码行的证据"。改完后,20 条里误判为 A 的从 3 条降到 1 条。
6. 你可以直接拿走的
| 文件 | 作用 | 你要改的地方 |
|---|---|---|
prompts/triage-log.md | 第 2 节完整提示词 | 按你的日志格式改输入占位符 |
scripts/sanitize_logs.py | 自动脱敏 + 截断 | 补充你环境特有的凭证/PII 正则 |
| CI 片段 | 失败即初筛,结论写回报告 | 见下 |
CI 接入(关键:只对首次失败调用,省成本):
yaml
- name: 失败日志 AI 初筛
if: failure()
run: |
for f in reports/failed/*.log; do
python scripts/sanitize_logs.py "$f" > "sanitized/$(basename $f)"
# 调用你的模型接口,把结论追加到测试报告(不要自动提缺陷)
python scripts/triage_call.py --log "sanitized/$(basename $f)" >> reports/triage.md
done
# 只对"首次失败"调用:重试成功的失败用例跳过收益对比:
| 指标 | 人工 | AI 初筛 + 人工复核 |
|---|---|---|
| 20 条失败排查 | 约 1.5 小时 | 3 分钟初筛 + 复核 5 条(约 20 分钟) |
| 误提缺陷风险 | 依赖个人判断 | 需人工确认后才提(AI 只写报告) |
| 一致性 | 每次标准不同 | 分类标准固定,可统计一致率 |
下一步可以做的 2 件事:
- 统计"AI 归类 vs 人工最终结论"的一致率,低于 80% 就回头改提示词(我这边是 85%,可接受)
- 把 3 条判错案例补成 few-shot 示例放进提示词(尤其"连接池超时是环境还是代码"这种易错项)
参考资料
- 失败日志分类与排查建议的实践参考:把回归测试从 3 天压到 3 小时
- "精确复现失败调用需可检视客户端"的边界说明:Can AI Replace API Testing?
- 敏感数据在链路中的风险(脱敏要求来源):OWASP LLM Top 10(官方页面)
本文由 曲奇 整理,内容由 AI 辅助整理,示例日志为脱敏演示数据;仅供学习参考,转载请注明出处。