从 0 到 1:我如何为 ai-robot 设计一套基于 Agent + Skills + MCP 的测试体系
一、为什么我想写这套方案
最近我在整理自己的 ai-robot 项目时,发现一个很典型的问题:
传统 Web 项目的测试思路,放到 AI Web 项目里,经常只够用一半。
为什么这么说?
因为 ai-robot 不是单纯的 CRUD 系统,它同时包含:
- 普通聊天
- SSE 流式输出
- 联网搜索增强
- RAG 智能客服
- Markdown 分片上传
- 异步向量化
- PostgreSQL + pgvector
如果还按传统方式去做测试,通常只能测到:
- 接口有没有通
- 状态码是不是 200
- 某个字段有没有返回
但 AI 项目真正容易出问题的地方,其实不是这些。
真正难测的是:
- SSE 流有没有完整结束
- Advisor 是否真的注入了记忆 / 联网 / RAG 上下文
- 文件上传、分片合并、异步向量化有没有形成闭环
- 大模型输出能不能稳定断言
- 失败之后能不能快速定位到底是 Controller、Service、数据库、向量化还是外部依赖的问题
所以我没有继续堆一堆零散接口脚本,而是尝试把 ai-robot 的测试设计成一套更像“产品”的体系:
Agent + Skills + MCP/Tools
这篇文章就完整记录一下这套方案的设计思路、落地过程,以及最后跑出来的效果。
二、项目背景:ai-robot 到底是什么
先简单交代一下项目背景。
ai-robot 是一个基于 Spring Boot 3 + Spring AI 的 AI Web 项目,核心能力有两类:
1. 通用聊天
- 支持普通聊天
- 支持 SSE 流式输出
- 支持对话记忆
- 支持联网搜索增强
- 支持消息落库
2. 智能客服
- 支持 Markdown 知识库上传
- 支持分片上传和断点续传
- 支持文件合并
- 支持异步向量化
- 支持基于 pgvector 的 RAG 问答
换句话说,这是一个典型的 AI 应用型 Web 项目。
而这类项目的测试,不仅要测“接口”,更要测“链路”。
三、传统测试思路为什么不够
如果只是用传统接口自动化去测这个项目,通常会遇到几个明显问题。
1. 只能测到接口,测不到增强链路
比如:
/chat/completion返回了 200,不代表记忆增强真的生效/customer-service/completion返回了内容,不代表 RAG 检索真的命中了知识库
也就是说,你能测到“结果返回了”,但测不到“为什么返回这个结果”。
2. AI 输出不能逐字断言
AI 项目很难像普通接口那样直接写:
expect(response.text).toBe("xxx")
因为只要模型输出语气稍微变一点,测试就会抖。
所以 AI 测试更适合:
- 结构化断言
- 关键词断言
- 规则断言
- 状态机断言
3. 文件和异步链路很容易被忽略
知识库上传这一条链路其实很长:
file/check -> upload-chunk -> merge-chunk -> event -> vectorize -> completion
如果你只测最终的 /completion,很多中间问题会被掩盖掉。
比如:
- 分片没清理
- 状态机没流转
- 文件落盘失败
- 向量化失败
- 删除时清理不彻底
4. 缺少统一编排
很多项目最后会变成:
- 一个脚本测聊天
- 一个脚本测上传
- 一个脚本测数据库
- 一个脚本测客服
最后脚本越来越多,但没有统一入口,也没有统一质量门禁。
所以我想做的不是“再补几个脚本”,而是做一套完整测试代理。
四、核心设计:为什么是 Agent + Skills + MCP
这套方案的核心思想可以概括成一句话:
把测试从“脚本集合”升级成“可编排的测试系统”。
我把它拆成三层:
1. Agent:负责任务编排
Agent 不直接执行具体测试动作,而是负责:
- 接收测试任务
- 决定当前要跑哪个阶段
- 调用对应的 Skill
- 汇总结果
- 生成最终报告
你可以把它理解成测试编排器。
2. Skills:负责能力模块化
每个 Skill 只做一类明确的事情。
例如:
- 场景识别
- 用例清单生成
- 本地门禁
- 在线冒烟
- 文件链路回归
- 聊天 + RAG 回归
- 全量回归与质量门禁
这样做的好处是:
- 结构清晰
- 易复用
- 易扩展
- 出问题容易定位
3. MCP / Tools:负责连接外部能力
这里的 MCP / Tools 不一定是严格意义上的 MCP Server, 更重要的是它承担了“连接外部能力”的职责。
比如我这里实际接入了:
- 源码结构扫描
- HTTP / SSE 请求
- JDBC 直连 PostgreSQL
- 文件系统校验
后续如果再扩展,也可以继续接:
- 向量库校验
- Mock SearXNG
- Mock 模型服务
- CI 系统
所以这套架构的本质不是某个具体技术名词,而是职责边界清晰。
五、我把 ai-robot 的测试拆成了 5 个阶段
为了让这套方案可逐步落地,而不是一上来就做成大而全,我把它分成了 5 个阶段。
第一阶段:离线分析
这一阶段不依赖运行中的后端服务,只做“结构级理解”。
目标是回答两个问题:
- 这个项目到底有哪些核心测试场景?
- 当前代码结构是否已经具备支撑这些测试场景的基本条件?
这一阶段我实现了三个核心能力:
1. 场景盘点
自动识别项目中的核心场景:
- 普通聊天与记忆增强
- 联网搜索与 SSE 输出
- RAG 智能客服
- 知识库上传与异步向量化
输出文件:
scene_inventory.json
2. 用例清单生成
根据场景生成结构化测试清单,而不是自然语言说明。
输出文件:
case_manifest.json
3. 本地门禁
检查:
- Controller 是否存在
- Advisor 是否存在
- Service 是否存在
- 关键端点是否覆盖
输出文件:
local_gate.jsonsummary.json
这一阶段最大的价值是:
即使后端没跑起来,也能快速发现“测试对象是否就绪”。
第二阶段:在线冒烟
第二阶段开始真正连运行中的后端。
但我没有一上来就测复杂模型输出,而是选择了一个非常稳定的策略:
直接利用项目里已有的“时间类问题后端兜底”能力。
为什么这样做?
因为它有两个优点:
- 不依赖真实模型波动
- 能稳定验证 HTTP + SSE 链路本身
这一阶段验证两个接口:
/chat/completion/customer-service/completion
输出文件:
online_smoke.json
这一阶段关注的是:
- 服务是不是在线
- SSE 能不能返回
- 链路是不是通的
第三阶段:知识库文件回归
第三阶段是我觉得最有价值的一步,因为它开始进入真正的“业务闭环测试”。
这里我没有只测一个接口,而是把整个知识库文件链路串起来:
file/checkupload-chunkmerge-chunkfile/list- JDBC 查数据库
- 文件系统校验
file/delete
这一阶段我验证了:
- 文件初始不存在
- 分片上传成功
uploadedChunks正常返回- 数据库里
t_ai_customer_service_file_storage状态正确 - 数据库里
t_file_chunk_info记录正确 - 合并后分片目录被清理
- 最终文件真正落盘
- 删除之后数据库和文件系统都被清理
输出文件:
knowledge_file_regression.json
这一阶段还帮我测出了一个真实 bug:
删除知识库文件时,后端还会尝试再次删除已经被 mergeChunk() 清理掉的分片目录,
导致 FileNotFoundException,返回 10000。
我顺手把这个 bug 修掉了:
- 分片目录不存在时,删除逻辑应当幂等跳过,而不是抛异常
这个细节其实非常适合写进博客,因为它体现了测试不仅“验证”,还“推动修复”。
第四阶段:聊天落库 + RAG 回归
第四阶段我把两条高价值链路合到了一起:
1. 聊天链路
完整验证:
chat/new -> chat/completion -> 数据库落库 -> chat/delete
这里验证的不是“模型回答是否完美”,而是:
- 是否创建了对话 UUID
t_chat是否写入t_chat_message是否真的落入 user + assistant 两条消息- 删除对话后主表和消息表是否都清理干净
2. RAG 问答链路
完整验证:
上传知识库 -> 等待向量化完成 -> customer-service/completion -> 删除知识库
这里我特意设计了一个稳定问题:
小哈 AI 机器人支持哪些核心能力?
因为 fixture 里本来就写了:
- 普通聊天
- 联网搜索
- 智能客服
所以我可以用关键词断言来判断回答是否命中知识库。
输出文件:
chat_rag_regression.json
这个阶段的意义在于:
它第一次把“落库正确”和“RAG 命中正确”都纳入测试范围。
第五阶段:全量回归与质量门禁
如果前四个阶段解决的是“各模块怎么测”, 那第五阶段解决的就是:
“能不能一条命令把整套测试体系跑完,并给出最终门禁结论?”
所以我实现了一个总编排器:
- 依次执行前四个阶段
- 聚合通过率
- 汇总失败项
- 输出 JSON 报告
- 输出 Markdown 报告
输出文件:
full_regression.jsonfull_regression.md
最后的质量门禁长这样:
- 总检查数:44
- 通过数:44
- 失败数:0
- Gate:PASS
这一阶段让整套测试体系真正具备了:
- 一键执行
- 可视化汇总
- 可接 CI
- 可做团队门禁
六、这套方案最终产出了什么
到目前为止,这套测试代理已经不只是“一个想法”,而是完整可运行的一套体系。
主要产物包括:
1. 测试代理代码
路径:
Resume/ai-robot/test-agent-mvp/
里面包括:
cli.pyrunner.pyscene_inventory.pycase_manifest.pylocal_gate.pyonline_smoke.pyknowledge_file_regression.pychat_rag_regression.pyfull_regression.pyjdbc_snapshot.pyPostgresSnapshot.java
2. 测试报告
路径:
Resume/ai-robot/out/test-agent-mvp/
包括:
scene_inventory.jsoncase_manifest.jsonlocal_gate.jsonsummary.jsononline_smoke.jsonknowledge_file_regression.jsonchat_rag_regression.jsonfull_regression.jsonfull_regression.md
3. 技术方案与博客文档
路径:
ATestDocs/ai-robot-Agent化测试技术方案.mdATestDocs/ai-robot-Agent测试方案-博客版.md
七、为什么这套方案值得写进博客
我觉得这套方案比较有意思的地方,不是“用了 Agent 这个词”,而是它确实解决了 AI 项目测试里的几个真实问题。
1. 它不是空谈架构,而是逐阶段落地
很多文章讲 Agent,最后都停留在概念层。
这套方案的特点是:
- 第一阶段有产物
- 第二阶段能跑接口
- 第三阶段能测文件 + 数据库
- 第四阶段能测聊天落库 + RAG
- 第五阶段能做总回归与门禁
这是一条很清晰的落地路线。
2. 它适合 AI Web 项目,而不只是普通接口项目
因为它考虑了:
- SSE
- 流式响应
- AI 输出不稳定
- RAG 检索
- 文件上传与异步链路
- 向量化前后状态
这些都是 AI 应用常见但传统测试方案不太擅长覆盖的地方。
3. 它验证的不只是接口,而是完整链路
比如第三阶段和第四阶段,本质上都在做一件事:
从“接口是否成功”升级到“链路是否闭环”。
这类思路特别适合写博客,因为它体现了测试设计能力,而不是单纯脚本技巧。
4. 它还能反向发现和修复真实 bug
比如我在第三阶段就测出了删除逻辑里的幂等问题。
这说明测试体系不仅是验证工具,也是发现系统设计缺陷的放大镜。
八、如果继续演进,下一步可以做什么
虽然现在这套方案已经能跑完整 5 个阶段,但如果继续做,还有几个值得继续演进的方向。
1. 向量检索精细断言
目前第四阶段已经验证了“回答命中知识库关键词”, 但还没有继续下沉到:
- TopK 命中文档校验
- 向量表精细校验
- metadata 过滤断言
2. Mock 外部依赖
目前很多回归是基于真实服务跑的。
如果后面接团队 CI,可以继续加入:
- Mock SearXNG
- Mock LLM
- Mock Embedding
这样会让回归更稳定。
3. 失败样本归档
后续可以把失败时的:
- 请求参数
- SSE 结果
- 数据库快照
- 文件路径
- 错误日志摘要
都沉淀成统一失败样本,方便复盘。
4. 接入 CI/CD
第五阶段已经具备质量门禁能力了, 下一步就可以直接接成:
- GitHub Actions
- Jenkins
- 本地 pre-release 检查
九、我对这套方案的最终总结
如果让我用一句话总结这套方案,我会这样说:
我不是想给 ai-robot 再补几条测试脚本,而是想把它的测试体系设计成一个可编排、可扩展、可复用的测试代理系统。
这套系统的核心价值在于:
- 用
Agent负责任务编排 - 用
Skills负责测试能力模块化 - 用
MCP/Tools负责连接代码、接口、数据库和文件系统
最终把测试从“单点脚本”升级成“多阶段回归体系”。
到现在为止,这套方案已经完成了 5 个阶段:
- 离线分析
- 在线冒烟
- 知识库文件回归
- 聊天落库 + RAG 回归
- 全量回归与质量门禁
并且已经跑到了:
- 总检查数:44
- 通过数:44
- Gate:PASS
我觉得这不仅是一套适合 ai-robot 的测试方案,也是一套可以迁移到其他 AI Web 项目的测试设计思路。
如果以后我再做新的 AI 项目,我大概率还会沿着这条路线继续演化。