尧图精选

我把大模型接进CI后,失败用例自动归因,报告直接发飞书

🕒 发布时间:2026/10/1 15:37:43 📁 来源:尧图网络
关注 霍格沃兹软件测试开发 公众号回复「资料」, 领取人工智能测试开发技术合集以前CI红了一群人围在屏幕前翻日志猜原因。现在CI红了飞书里直接弹出一条消息“本次失败50条其中45条为环境问题5条疑似代码Bug详情见下。”大家好我是某互联网公司的测试架构师。上个月团队里一个做自动化的同事跟我吐槽“哥我每天花在‘看失败报告’上的时间比写测试脚本还多。”我问他怎么回事。他说每天CI跑完少则二三十条失败多则上百条。点开一看有的是环境超时有的是元素定位失效有的是测试数据被其他用例污染了真正是Bug的可能就三五条。但为了找出那三五条他得一条条翻日志、看截图、对代码变更两三个小时就没了。我说“你让大模型替你看。”他问“怎么看”我说“CI跑完把失败日志丢给大模型让它做归因分类然后把结论发到飞书。你只看结论。”两天后他的CI流水线里多了一个步骤。现在CI跑完飞书群里直接弹出一条消息本次回归失败50条。环境问题42条连接超时/容器启动失败用例问题3条定位器失效/数据污染疑似Bug5条断言失败涉及订单状态不一致 详情点击查看完整报告他跟我说“我现在每天花在失败排查上的时间从两小时变成了十分钟。”今天这篇文章我把整个方案拆开讲。从CI配置到Prompt模板到飞书机器人全部可复制。一、先搞清楚这个方案的核心逻辑是什么传统CI流水线的测试阶段是这样的代码提交 → 触发CI → 跑测试 → 生成报告 → 人工看报告 → 人工归因 → 决定下一步人工看报告、人工归因是整个链路里最耗时的环节。我们的方案是在“生成报告”和“人工看报告”之间插进去一个AI归因节点代码提交 → 触发CI → 跑测试 → 生成原始报告 → AI归因分析 → 生成结构化结论 → 飞书推送AI归因节点做三件事收集失败用例的日志、截图路径、相关代码变更调用大模型对每条失败进行分类环境/用例/代码Bug/数据/未知汇总成结构化报告通过飞书机器人推送人工只需要看AI筛出来的“疑似Bug”那几条。二、准备工作你需要什么环境要求一个跑在CI上的测试项目我们用的是GitLab CI pytest一个飞书群并创建自定义机器人拿到webhook地址一个大模型API KeyDeepSeek、GLM、通义千问都行我们用的DeepSeek测试框架输出pytest 建议用 --junitxmlreport.xml 生成结构化报告。或者用 pytest-json-report 插件生成JSON解析更方便。飞书机器人在飞书群设置里添加“自定义机器人”拿到webhook地址安全设置选“签名校验”或“IP白名单”。我们用的签名校验后面代码里会体现。三、核心脚本失败归因分析器整个方案的核心是一个Python脚本放在项目根目录的 ci/analyze_failures.py。#!/usr/bin/env python3-- coding: utf-8 --import jsonimport osimport sysimport xml.etree.ElementTree as ETfrom openai import OpenAI 配置 DEEPSEEK_API_KEY os.environ.get(“DEEPSEEK_API_KEY”)FEISHU_WEBHOOK os.environ.get(“FEISHU_WEBHOOK”)FEISHU_SECRET os.environ.get(“FEISHU_SECRET”)client OpenAI(api_keyDEEPSEEK_API_KEY,base_url“https://api.deepseek.com”) 解析pytest报告 def parse_junit_xml(xml_path):“”“从JUnit XML中提取失败用例信息”“”tree ET.parse(xml_path)root tree.getroot()failures []for tc in root.iter(“testcase”):failure tc.find(“failure”)error tc.find(“error”)if failure isnotNoneor error isnotNone:node failure if failure isnotNoneelse errorfailures.append({“classname”: tc.get(“classname”),“name”: tc.get(“name”),“time”: tc.get(“time”),“message”: node.get(“message”, “”),“text”: (node.text or)[:2000], # 截断避免token爆炸})return failures 收集上下文 def collect_context(failures):“”“收集Git变更、环境信息等上下文”“”import subprocesstry:git_diff subprocess.check_output([“git”, “diff”, “HEAD~1”, “–stat”],stderrsubprocess.DEVNULL).decode(“utf-8”, errors“ignore”)except Exception:git_diff “无法获取Git变更信息”return { failures: failures, git_diff: git_diff[:3000], ci_env: os.environ.get(CI_ENVIRONMENT, unknown), timestamp: os.environ.get(CI_PIPELINE_CREATED_AT, ), } 调用大模型归因 PROMPT_TEMPLATE “”你是一个资深的测试开发工程师正在分析CI流水线中失败的测试用例。请对下面每一条失败用例进行归因分类只能选择以下类别之一环境问题网络超时、容器启动失败、依赖服务不可用、配置错误等用例问题定位器失效、测试数据污染、用例逻辑错误、断言过强/过弱代码Bug业务逻辑错误、接口返回不符合预期、状态不一致等数据问题测试数据不存在、数据被其他用例修改、数据过期未知无法判断同时请给出一句话归因结论置信度高/中/低如果是代码Bug给出建议的复现步骤输入数据{context}请严格输出JSON数组每个元素包含{{“name”: “用例名称”,“category”: “环境问题|用例问题|代码Bug|数据问题|未知”,“conclusion”: “一句话结论”,“confidence”: “高|中|低”,“reproduce_steps”: “如果是代码Bug给出复现步骤否则为空字符串”}}只输出JSON不要解释。“”def analyze_with_llm(context):prompt PROMPT_TEMPLATE.format(contextjson.dumps(context, ensure_asciiFalse, indent2))resp client.chat.completions.create(model“deepseek-chat”,messages[{“role”: “user”, “content”: prompt}],temperature0.1,response_format{“type”: “json_object”})content resp.choices[0].message.contentreturn json.loads(content) 生成飞书报告 def build_feishu_card(results, total_failures):“”“构建飞书消息卡片”“”from collections import Countercounter Counter(r[“category”] for r in results)bugs [r for r in results if r[“category”] “代码Bug”]# 按类别统计 summary_lines [] for cat in [环境问题, 用例问题, 代码Bug, 数据问题, 未知]: count counter.get(cat, 0) if count 0: summary_lines.append(f**{cat}**{count}条) # 疑似Bug详情 bug_lines [] for b in bugs[:5]: # 最多展示5条 bug_lines.append(f- {b[name]}{b[conclusion]}置信度{b[confidence]}) ifnot bug_lines: bug_lines.append(无) card { msg_type: interactive, card: { header: { title: {tag: plain_text, content: CI回归失败归因报告}, template: red }, elements: [ {tag: div, text: {tag: lark_md, content: f**失败总数**{total_failures}条\n\n \n.join(summary_lines)}}, {tag: hr}, {tag: div, text: {tag: lark_md, content: **疑似代码Bug**\n \n.join(bug_lines)}}, {tag: hr}, {tag: note, elements: [{tag: plain_text, content: 由AI自动归因请人工复核疑似Bug项}]} ] } } return card 发送飞书消息 def send_feishu(card):import time, hmac, hashlib, base64timestamp str(int(time.time()))string_to_sign f{timestamp}\n{FEISHU_SECRET}hmac_code hmac.new(string_to_sign.encode(“utf-8”), digestmodhashlib.sha256).digest()sign base64.b64encode(hmac_code).decode(“utf-8”)import requests payload { timestamp: timestamp, sign: sign, msg_type: interactive, card: card[card] } resp requests.post(FEISHU_WEBHOOK, jsonpayload) print(f飞书发送结果{resp.status_code} {resp.text}) 主流程 def main():report_path “report.xml”ifnot os.path.exists(report_path):print(“未找到报告文件退出”)returnfailures parse_junit_xml(report_path) ifnot failures: print(无失败用例无需归因) return context collect_context(failures) results analyze_with_llm(context) card build_feishu_card(results, len(failures)) send_feishu(card) print(归因完成)ifname “main”:main()四、接入CIGitLab CI示例在 .gitlab-ci.yml 里增加一个 stagestages:-test-analyzerun-tests:stage:testscript:-pipinstall-rrequirements.txt-pytest–junitxmlreport.xmlartifacts:when:alwayspaths:-report.xmlexpire_in:1weekai-analyze:stage:analyzeneeds:[“run-tests”]script:-pipinstallopenairequests-pythonci/analyze_failures.pyvariables:DEEPSEEK_API_KEY:DEEPSEEKAPIKEYFEISHUWEBHOOK:DEEPSEEK_API_KEY FEISHU_WEBHOOK:DEEPSEEKA​PIK​EYFEISHUW​EBHOOK:FEISHU_WEBHOOKFEISHU_SECRET:$FEISHU_SECRETonly:-merge_requests-main关键点artifacts 必须把 report.xml 传递到下一个stage。when: always 确保测试失败时也会生成报告。五、实际效果从两小时到十分钟我们拿一个跑了3个月的订单中台项目做对比。改造前CI跑完测试同学打开报告50条失败逐条点开看日志、看截图、对代码平均每条排查2-3分钟总计约2小时最终确认5条真Bug45条环境/用例问题改造后CI跑完飞书收到AI归因报告测试同学只看“疑似代码Bug”那5条人工复核5条每条1-2分钟总计约10分钟其余45条自动分类不占用注意力效率提升12倍。六、避坑指南坑一把AI归因当成最终结论。AI归因是“预筛”不是“判决”。我们永远在飞书卡片末尾加一句“请人工复核疑似Bug项”。AI负责把噪音过滤掉你负责做最终判断。坑二日志截断太狠AI看不到关键信息。一开始我只给AI传了失败消息的前200个字符结果它经常把“连接超时”误判为“代码Bug”。后来改成传完整failure text的前2000字符准确率明显提升。坑三Prompt写得太模糊。如果你只写“分析这些失败”AI会给你一堆模棱两可的结论。必须明确分类选项环境/用例/代码Bug/数据/未知并强制输出JSON。分类选项本身就是对AI的约束。坑四Token成本失控。50条失败每条2000字符加上Git diff和Prompt模板一次调用大概消耗3-5万Token。DeepSeek的价格一次不到一毛钱。但如果你每天跑几十次CI成本会累积。建议只在MR和main分支触发feature分支不跑归因。坑五忽略安全。失败日志里可能包含敏感信息接口地址、Token、用户数据。在传给大模型之前做一个简单的脱敏处理正则替换掉Bearer Token、手机号、邮箱、IP地址。这一步不能省。最后CI跑完测试不是终点。把“失败归因”也自动化才是终点。以前你面对50条失败要一条条翻。现在你面对一条飞书消息只看5条。你不需要成为大模型专家。你只需要在CI里加一个节点让AI替你做那80%的噪音过滤。下次CI红了的时候别打开日志了。让飞书给你发结论。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →