{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 智能API测试助手(API Test Assistant)\n", "\n", "## 📝 项目简介\n", "\n", "基于 Hello-Agents 框架的多智能体应用,输入一份 OpenAPI 文档,\n", "自动完成「解析 → 生成用例 → 执行测试 → 验证结果 → 生成报告」全流程。\n", "\n", "采用 5 个 Agent 组成流水线:\n", "① ParserAgent(解析)→ ② GeneratorAgent(LLM 生成)→ ③ ExecutorAgent(执行)\n", "→ ④ ValidatorAgent(验证)→ ⑤ ReporterAgent(报告)\n", "\n", "## 👤 作者信息\n", "- GitHub:@senming666\n", "- 日期:2026-08-20\n", "- 框架版本:hello-agents >= 1.0.0\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 🔧 第1部分:环境配置" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 安装依赖(如果需要)\n", "# !pip install -r requirements.txt\n", "\n", "import os\n", "import sys\n", "\n", "# Windows 中文控制台默认 GBK,重配为 UTF-8,否则打印中文会崩\n", "if hasattr(sys.stdout, \"reconfigure\"):\n", " sys.stdout.reconfigure(encoding=\"utf-8\", errors=\"replace\")\n", "\n", "# 确保能 import src 包(notebook 在项目根目录运行)\n", "sys.path.append(\".\")\n", "\n", "from dotenv import load_dotenv\n", "\n", "# 关键:先加载 .env,再 import Agent(Agent 实例化时要读 LLM 配置)\n", "load_dotenv()\n", "\n", "print(\"✅ 环境配置完成\")\n", "print(f\"📁 当前目录: {os.getcwd()}\")\n", "print(f\"🐍 Python 版本: {sys.version.split()[0]}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 🧰 第2部分:工具层(Tools)\n", "\n", "工具层是\"没有大脑、只会干活\"的模块,本项目有两个工具:\n", "- **HttpClient**:发 HTTP 请求(带超时 + 自动重试)\n", "- **SchemaValidator**:校验响应(状态码比对 + JSON Schema 结构校验)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from src.tools.http_client import HttpClient\n", "from src.tools.schema_validator import SchemaValidator\n", "\n", "# 创建工具实例\n", "http_client = HttpClient()\n", "schema_validator = SchemaValidator()\n", "\n", "print(\"📋 工具层就绪:\")\n", "print(f\"1. HttpClient - 支持方法: {http_client.SUPPORTED_METHODS}\")\n", "print(\"2. SchemaValidator - 校验状态码 + JSON Schema 结构\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 🤖 第3部分:智能体构建(5 个 Agent 流水线)\n", "\n", "5 个 Agent 排成流水线,上一个的输出是下一个的输入。\n", "其中只有 **GeneratorAgent** 真正调用 LLM(想\"该测什么\"),其余是确定性逻辑。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from src.agents.parser_agent import ParserAgent\n", "from src.agents.generator_agent import GeneratorAgent\n", "from src.agents.executor_agent import ExecutorAgent\n", "from src.agents.validator_agent import ValidatorAgent\n", "from src.agents.reporter_agent import ReporterAgent\n", "\n", "# 创建 5 个 Agent 实例\n", "parser = ParserAgent() # ① 解析:读懂 OpenAPI 文档\n", "generator = GeneratorAgent() # ② 生成:用 LLM 生成测试用例\n", "executor = ExecutorAgent() # ③ 执行:真实调用目标接口\n", "validator = ValidatorAgent() # ④ 验证:判断结果对错\n", "reporter = ReporterAgent() # ⑤ 报告:汇总成 HTML 报告\n", "\n", "print(\"🚀 5 个 Agent 创建完成,流水线就绪:\")\n", "print(\"Parser → Generator → Executor → Validator → Reporter\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 🎯 第4部分:功能演示\n", "\n", "以项目自带的 `api.yaml`(JSONPlaceholder 的 /users、/posts 两个接口)为例,\n", "对公网 API 发起真实测试。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "BASE_URL = \"https://jsonplaceholder.typicode.com\"\n", "\n", "# ① 解析文档\n", "endpoints = parser.parse_file(\"api.yaml\")\n", "print(f\"[1/5] 解析完成:发现 {len(endpoints)} 个接口\")\n", "for ep in endpoints:\n", " print(f\" {ep['method']:<6} {ep['path']}\")\n", "\n", "# ② 生成用例(每个接口都生成,extend 合并成大列表)\n", "all_cases = []\n", "for ep in endpoints:\n", " all_cases.extend(generator.generate(ep))\n", "print(f\"[2/5] 生成完成:共 {len(all_cases)} 个测试用例\")\n", "\n", "# ③ 执行测试\n", "execution_results = executor.execute(all_cases, BASE_URL)\n", "print(f\"[3/5] 执行完成:已发送 {len(execution_results)} 个请求\")\n", "\n", "# ④ 验证结果\n", "validated_results = validator.validate(execution_results)\n", "print(\"[4/5] 验证完成\")\n", "\n", "# ⑤ 统计汇总\n", "summary = reporter.summarize(validated_results)\n", "print(\"[5/5] 报告汇总完成\")\n" ] }, { "cell_type": "code", "id": "f0881fff", "source": "# ==== 功能扩展:从 URL 抓取 + Markdown 报告 ====\n\n# 扩展1:从 URL 直接抓取 OpenAPI 文档(无需先下载到本地)\nurl_endpoints = parser.parse_url(\"https://httpbin.org/spec.json\")\nprint(f\"从 URL 抓取到 {len(url_endpoints)} 个接口\")\nfor ep in url_endpoints[:3]:\n print(f\" {ep['method']} {ep['path']}\")\n\n# 扩展2:生成 Markdown 格式报告(上面已生成 HTML,这里额外生成 .md)\nmd_report = reporter.generate_markdown(validated_results)\nprint(\"\\nMarkdown 报告预览(前 10 行):\")\nprint(\"\\n\".join(md_report.split(\"\\n\")[:10]))", "metadata": {}, "execution_count": null, "outputs": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 打印每个用例的验证结果\n", "print(\"=\" * 60)\n", "print(f\"总用例 {summary['total']},通过 {summary['passed']},\"\n", " f\"失败 {summary['failed']},通过率 {summary['pass_rate']}%\")\n", "print(\"=\" * 60)\n", "\n", "for r in validated_results:\n", " case = r[\"case\"]\n", " result = r[\"result\"]\n", " mark = \"✅ 通过\" if r[\"passed\"] else \"❌ 失败\"\n", " print(f\"\\n{mark} | {case['method']} {case['path']} | {case['name']}\")\n", " print(f\" 类型={case.get('case_type')} \"\n", " f\"期望状态码={case.get('expected_status')} \"\n", " f\"实际状态码={result.get('status_code')} \"\n", " f\"耗时={result.get('elapsed')}s\")\n", " if not r[\"passed\"]:\n", " print(f\" 失败原因: {'; '.join(r['errors'])}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": "## 📊 第5部分:性能评估\n\n三套被测对象的测试结果速览(详细失败分析见 README.md):\n\n| 被测对象 | 接口 | 用例数 | 通过率 | 失败原因 |\n|---|---|---|---|---|\n| JSONPlaceholder | 2 个 GET | 6 | 66.7% | error 用例被 mock 服务宽容处理 |\n| 自己测自己 | GET + POST | 6 | 83.3% | 三轮迭代抓到并修复 2 个 bug |\n| httpbin.org | 4 个 GET/POST | 12 | 66.7% | error 用例被测试服务宽容处理 |\n\n**核心结论**:失败几乎都集中在 error 用例——被测的测试/mock 服务对异常输入\n不严格校验,统一返回 200;工具如实报告了这一行为,而非\"粉饰\"结果。" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# 性能评估:用下面的命令可复现任一被测对象的结果\n# python main.py --file api.yaml --base-url https://jsonplaceholder.typicode.com\n# python main.py --file openapi_service.yaml --base-url http://localhost:8000\n# python main.py --file httpbin.json --base-url https://httpbin.org\n\nprint(\"📊 详细测试数据与失败分析见 README.md 的「性能评估」章节\")" }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 📝 第6部分:总结与展望\n", "\n", "### 项目总结\n", "\n", "#### ✅ 实现的功能\n", "- 5 个 Agent 组成的多智能体流水线\n", "- LLM 智能生成测试用例(正常 / 边界 / 异常三类)\n", "- 真实 HTTP 调用 + 自动结果校验\n", "- HTML 报告 + 通过率统计\n", "- FastAPI 后端 + Vue3 前端 + 命令行入口\n", "\n", "#### 🎯 遇到的挑战\n", "- hello-agents 1.0.0 的 `invoke()` 返回 LLMResponse 对象而非字符串,需用 `.content` 取值\n", "- LLM 生成的用例缺 path/method 字段,改为从 endpoint 注入确定性信息\n", "- Windows GBK 控制台打印中文崩溃,需重配 stdout 为 UTF-8\n", "\n", "#### 🚀 未来改进方向\n", "- [ ] 支持认证请求头(Authorization),可测需鉴权的接口\n", "- [ ] 从 URL 抓取 OpenAPI 文档\n", "- [ ] 并发执行测试用例\n", "- [ ] 补充性能评估数据\n", "\n", "---\n", "\n", "🎓 **恭喜!你已经跑通了智能 API 测试助手的完整流程!**\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.13" } }, "nbformat": 4, "nbformat_minor": 5 }