资讯动态

Agent Skills实战:打造可复用的接口自动化测试技能包

发布时间:2026/10/20 1:46:08 来源:尧图企业网站定制
最近接到一个需求让团队的 Agent 在不动业务代码的前提下自动跑接口冒烟测试。折腾了两天最后用 Agent Skills 的方式整理出一个接口测试技能包效果比我预期好不少。这里把整套做法和踩坑过程完整复述一遍。Agent Skills 和普通 Prompt 最大的不同是它把“技能说明 配套脚本 参考样例”打包成一个独立目录Agent 只有在遇到匹配任务时才会加载这套能力不会无端占上下文。接口测试这个场景特别适合做成技能包流程固定、规则明确、重复性高但每次要测的接口又千差万别正好需要这种“可动态组合”的载体。这篇文章适合两类人一类是在做 Agent 应用开发想让 Agent 具备真实工具操作能力另一类是测试开发想把手上的接口测试流程沉淀成可复用的“能力集”。下文里的代码和方法都是我实际跑通过的你可以直接照抄改抄。1. 为什么要给 Agent 定制接口测试技能包1.1 我理解的 Agent Skills 到底是什么Agent Skills 不是某个独立框架的专属概念更像是一种工程实践给智能体追加一个可插拔的“工具箱”。这个工具箱由一组结构化的文件组成核心入口是一个 Markdown 描述文件里面写清楚这个技能是干什么的、在什么时候调用、需要哪些输入参数。旁边可以放着 Python 脚本、Shell 脚本、提示词模板、参考数据样例等。以前做 Agent 工具调用是把十几个函数直接扔进工具列表Agent 自己根据函数描述挑来用。但函数一多选错工具的概率就明显上升。技能包的思路变成了“按需加载”Agent 先判断当前任务属于哪个技能域再进入对应技能目录读取完整说明和脚本。相当于以前你扔给 Agent 一堆散件现在你给了它几个装好的工具箱每个箱子上贴了标签和使用说明书。我这边是在某开源 Agent 框架上做的集成整体思路在主流框架上都通用。你只需要按约定建一个技能目录框架在启动时扫描到目录Agent 就能识别并使用这个技能。1.2 接口测试这个场景为什么适合做技能包接口测试天然适合 Agent 来执行原因是它足够“结构化”。一个接口测试任务无非是根据接口文档或业务需求明确请求方法、路径、请求头、请求体然后发出请求最后对响应做校验。这套流程里有很多“确定性动作”例如构建 JSON、解析 JSON、判断状态码、提取字段这些完全可以交给脚本去做。而 Agent 擅长的是“判断测什么、用什么参数、怎么解读结果”。把两者结合起来就是接口测试技能包的核心价值。我举个例子。过去要测一条登录链路测试人员要先打开文档、复制 URL、填 token、选方法、填参数、跑断言、看报告手动做一遍至少十几分钟。技能包接入后我只需要对 Agent 说一句“请按照接口文档跑一遍用户中心的登录、资料查询、资料更新三段链路重点检查登录后的 token 是否能穿透到后续接口。”Agent 会自动加载技能、解析文档、生成用例、调用脚本、返回测试结果。更关键的是接口文档经常变。如果逻辑都写死在一次性脚本里接口一变又要改脚本技能包里的脚本是通用执行器接口的具体用例由 Agent 根据需求临时生成改起来就快很多。1.3 技能包和普通 Prompt 的本质区别把技能包做成目录而不是把规则写进 Prompt最大的优势在于上下文管理。你如果在系统 Prompt 里详细描述接口测试的每个步骤、每种断言规则、每个工具用法这段长度至少占据上万 token不管当前任务是不是接口测试模型都想一遍。技能包则完全相反Agent 在平时不读取技能内部说明只有当前任务被分类为接口测试时才加载对应目录。这个模式有点像经验丰富的开发者的“知识库”——我不会把每天要用的工具命令都背在脑子里而是知道遇到问题去哪查。技能包就是让 Agent 知道去哪查、查到之后怎么执行。另外技能包是文件形态天然支持版本管理和团队复用。我把这个技能包放进 Git 仓库换一拨人接手功能、依赖、文档全都在目录里不依赖某一次对话里的上下文。这一点是普通 Prompt 做不到的。2. 技能包结构设计与关键文件2.1 一份通用技能包的目录规范一个标准的接口测试技能包不需要太多文件核心脉络是“入口描述 执行脚本 参考样例”。我常用的目录结构大概是这样的api_test_skill/ ├── SKILL.md ├── requirements.txt ├── scripts/ │ ├── run_test.py │ ├── parse_openapi.py │ └── json_extract.py └── assets/ ├── sample_request.json ├── sample_response.json └── examples.mdSKILL.md 是 Agent 读取技能时的第一个文件也可以说是一份“给 Agent 看的说明书”。scripts 目录放具体的执行逻辑assets 目录放各种参考数据让 Agent 在不确定请求格式时可以照着样例写。这里有一个我在初期走弯路得出的经验不要把脚本逻辑写得过于复杂。技能包里的脚本应该是“确定性工具”负责 Agent 不擅长的精确运算而不是把所有测试规则都写死在代码里。例如解析 JSONPath 这种需要精确匹配的活脚本搞定至于测哪些接口、用什么参数留给你和 Agent 在对话里决策。2.2 SKILL.md 中的核心字段怎么填SKILL.md 不是写给人看的普通文档它是 Agent 的“调用指南”。写得好不好直接决定 Agent 能不能在恰当时机识别出这个技能。我建议至少包含三块元信息、调用描述、参数定义。一份典型的 SKILL.md 开头会长这样--- name: api_test_runner description: 执行接口测试任务。当用户要求测试API、验证接口返回、检查接口链路、根据接口文档生成并执行测试用例时使用。技能支持HTTP请求发送、动态参数关联、状态码与JSON字段断言、测试报告生成。 --- # API 接口测试技能 ## 使用场景 需要发起 HTTP 请求并验证响应的任务。 ## 基础指令 1. 先向用户确认被测环境地址、鉴权方式、需要覆盖的接口范围。 2. 如需解析接口文档调用 scripts/parse_openapi.py。 3. 使用 scripts/run_test.py 发送请求并按断言规则检查结果。 4. 最终给出 Markdown 格式测试报告。 ## 参数定义 - base_url: 被测环境根地址例如 http://service.test.local - headers: 全局请求头例如 Authorization 字段 - test_cases: 测试用例列表每个用例包含 name、method、path、params、body、assertions注意 description 里一定要写清触发条件而且要范围收敛。我一开始写的是“处理与HTTP相关的任务”结果 Agent 在生成文案的时候也来调用我这个接口测试技能乱得不行。后面改成“仅当用户要求测试接口、验证接口返回内容、检查接口链路时使用”误调用率就降下来了。参数定义不是给用户看的是给 Agent 看的结构化约束。你可以用简洁的 Markdown 列表也可以用 JSON Schema 风格写明每个字段类型。多数框架对 SKILL.md 的元信息解析都比较宽松但是字段命名尽量保持稳定不要一会叫 baseUrl一会叫 base_urlAgent 容易晕。2.3 配套模块脚本、模板、需求说明、样例响应光有 SKILL.md 描述还不够客户端工具型技能必须配上实际脚本。我这里准备了三个脚本各管一摊run_test.py负责发送请求、处理超时与重试、执行断言、输出报告。parse_openapi.py负责解析 OpenAPI/Swagger 文档提取接口列表和参数约束供 Agent 生成用例。json_extract.py负责用 JSONPath 从响应中提取字段实现接口间参数依赖。assets 目录里的样例数据同样重要。比如 sample_request.json 可以放一个带鉴权的 POST 请求样例sample_response.json 放一条典型的 JSON 响应。Agent 在构造用例时如果能参考这些样例格式准确率会高很多。examples.md 里我还塞了两条完整用例一条是登录后拿 token另一条是查询订单列表并断言字段。我有一次测试发现Agent 在没有样例时生成请求体经常少一层嵌套比如 data 字段忘了包一层。放上样例之后这个问题基本消失。所以不要小看资产文件的作用。3. 核心实现接口测试能力如何拆成可复用模块3.1 请求执行器GET、POST、鉴权与超时处理请求执行器是整个技能包的地基。无论 Agent 要测什么接口最终都要落到一个稳定的 HTTP 请求发送模块上。我用 Python 的 requests 库封装了一个带重试的请求函数。核心逻辑如下import requests import json import time import os def send_request(method, url, headersNone, json_bodyNone, timeout15, max_retries2): 统一请求入口支持 GET/POST/PUT/DELETE默认带超时与重试 if headers is None: headers {} if json_body is not None: headers.setdefault(Content-Type, application/json) for attempt in range(max_retries 1): try: resp requests.request( methodmethod, urlurl, headersheaders, jsonjson_body, timeouttimeout ) # 保留原始响应文本给后续断言用 return { status: resp.status_code, latency_ms: int(resp.elapsed.total_seconds() * 1000), headers: dict(resp.headers), body_text: resp.text, body_json: try_parse_json(resp.text) } except requests.exceptions.Timeout: if attempt max_retries: return {status: -1, latency_ms: -1, error: timeout} time.sleep(0.5 * (attempt 1)) except requests.exceptions.ConnectionError: if attempt max_retries: return {status: -1, latency_ms: -1, error: connection_error} time.sleep(0.5 * (attempt 1)) def try_parse_json(text): try: return json.loads(text) except Exception: return None这里有两个小细节值得注意。第一是超时时间不要写死成默认值接口测试场景里每个接口的响应速度可能差很多最好让 Agent 传进来比如 15 秒给足而重试次数默认 2 次只对网络层错误做重试HTTP 4xx、5xx 一般不做盲目重试因为那可能是业务逻辑错误。第二是 latency_ms 一定要统计因为很多测试断言要检查“响应时间小于 500ms”如果没有这个字段Agent 没法做耗时断言。鉴权这块我单独做了一个小处理。Authorization 这类通用 header 优先从环境变量或外层配置读取技能脚本里不硬写死 token。否则换环境时脚本就废了。3.2 动态数据关联用 JSONPath 串联接口依赖接口测试里最常见的痛点不是“一个接口怎么测”而是“接口之间有依赖”。比如先登录拿 token再用这个 token 查用户信息最后把返回的用户 id 塞进更新接口。Agent 能理解这种逻辑但它提取字段不够稳定所以我把动态关联做成脚本模块。用 Python 的 jsonpath-ng 库来实现字段提取封装成一个小函数from jsonpath_ng import parse def extract_by_jsonpath(obj, expr): 从对象中按 JSONPath 提取信息。 返回所有命中的值列表如果没命中返回空列表。 if obj is None: return [] try: parser parse(expr) matches parser.find(obj) return [match.value for match in matches] except Exception: return [] # 示例从登录接口响应中取 token resp send_request(POST, http://service/login, json_body{account: demo, pwd: 123456}) tokens extract_by_jsonpath(resp[body_json], $.data.token) if tokens: print(token:, tokens[0])在实际技能包里我会把“提取变量”和“渲染变量”放到一起。Agent 生成测试用例时可以在用例中声明前置接口例如“先调用 login把 $.data.token 存为 {token}后续请求头中使用 {token}”。run_test.py 会先执行前置接口提取字段然后再渲染主请求。我之前试过让 Agent 直接用正则去 extract效果很差因为接口返回嵌套太深。换用 JSONPath 之后准确率基本是百分之百。这个环节一定要脚本化别指望 AI 每次临时推理都能算对复杂路径。3.3 断言与报告校验状态码、数据字段、响应耗时执行完请求之后下一步就是断言。断言规则也应该从用例参数里传入而不是写死在代码里。比如一个用例可以这样声明test_case { name: 正常登录, method: POST, path: /api/login, json: {account: demo, pwd: 123456}, assert: { status: 200, jsonpath_expressions: { $.code: 0, $.data.token: {exists: True}, $.data.user_type: {in: [1, 2, 3]} }, max_latency_ms: 500 } }断言执行器遍历这些规则逐条比对。如果没命中就把失败详情记录下来。最后生成一份 Markdown 报告给 Agent方便 Agent 直接对用户做总结。这里有一个认知要转换Agent 不适合做“精确断言”但它很适合“失败分析”。因为响应内容可能是动态的或者业务返回了一个不在预期中的错误码脚本只能判断通过或失败而 Agent 可以看失败详情结合上下文推断是不是接口参数传错了、是不是环境数据没初始化、是不是服务器在发布窗口。所以技能包的报告生成必须要带上响应原文和关键上下文不要只给一行“通过/失败”。3.4 让 Agent 自动编排完整测试流程有了请求执行器、变量关联和断言模块最后一步是把它们串成工作流。这里我说的不是代码工作流而是 Agent 的决策逻辑。我在 SKILL.md 里给 Agent 定义了这样的执行顺序确认测试范围跟用户确认被测系统、环境地址、是否需要解析接口文档。解析接口文档如果用户给了 OpenAPI 文档或接口说明调用 parse_openapi.py 把接口列表拉出来。生成测试用例由 Agent 根据业务场景组合用例尽量覆盖正常链路、异常参数、缺失字段、权限不足四种类型。提交执行调用 run_test.py按依赖顺序执行。分析并汇报Agent 读取报告把失败用例和可能的根因整理成给用户看的结论。这套流程跑稳之后我基本上只需要给 Agent 一个需求描述和接口文档地址剩下的活它都能自己干完。当然对于复杂链路我会要求 Agent 每个用例执行前打印出“准备调用哪个接口、传什么参数”方便我随时打断纠正。4. 手把手实操从零构建并接入你的 Agent4.1 初始化技能包目录与环境第一步很直观先在本地建目录。我习惯把技能包放在 Agent 配置目录下的 skills 文件夹内例如/my_agent/ ├── skills/ │ └── api_test_skill/然后在 api_test_skill 里建子目录和虚拟环境cd my_agent/skills/api_test_skill mkdir -p scripts assets python -m venv venv source venv/bin/activate pip install requests jsonpath-ng PyYAML如果你用的是现成 Agent 框架记得看框架支持的 skills 目录路径有些框架需要你在配置里显式声明skills_path。这一步不难但容易漏。4.2 编写最小可用的 SKILL.md不用一上来就写得很复杂先让技能能跑通一个请求。最小的 SKILL.md 我建议这样写--- name: api_test_runner description: 仅当用户要求测试接口、验证接口返回结果、检查API链路时使用。该技能可以发送HTTP请求、解析JSON响应、执行断言并输出测试报告。 --- # 接口测试技能 ## 输入 - base_url - headers可选 - test_cases ## 步骤 1. 将用户需求拆解为多个 test_case。 2. 调用 scripts/run_test.py --config test_cases.json。 3. 分析报告并回复。 ## 注意事项 - 不要把本技能用于非接口测试任务。 - 动态关联字段使用 JSONPath 语法。这里我不放完整参数 Schema 了因为后面的脚本可以直接读取 JSON 配置文件Agent 只要把用户需求翻译成 test_cases.json 就行。这个模式初期最好用。4.3 实现请求执行与断言脚本脚本能跑通才能给 Agent 用。我贴一个简化版 run_test.py它接收一个 config.json 文件执行用例并输出 report.md#!/usr/bin/env python3 import json import sys from urllib.parse import urljoin from jsonpath_ng import parse def try_parse_json(text): try: return json.loads(text) except Exception: return None def extract(obj, expr): try: return [m.value for m in parse(expr).find(obj)] except Exception: return [] def send_request(method, url, headersNone, json_bodyNone, timeout15): import requests try: resp requests.request(methodmethod, urlurl, headersheaders, jsonjson_body, timeouttimeout) return { status: resp.status_code, latency_ms: int(resp.elapsed.total_seconds() * 1000), text: resp.text, json: try_parse_json(resp.text) } except Exception as exc: return {status: -1, latency_ms: -1, error: str(exc), text: , json: None} def run_one_case(base_url, case): url urljoin(base_url, case[path]) method case.get(method, GET).upper() headers case.get(headers, {}) json_body case.get(json, None) result send_request(method, url, headersheaders, json_bodyjson_body) assertions case.get(assert, {}) errors [] if status in assertions and result[status] ! assertions[status]: errors.append(f状态码期望 {assertions[status]}实际 {result[status]}) for expr, expect in assertions.get(jsonpath_expressions, {}).items(): values extract(result[json], expr) if not values: errors.append(fJSONPath {expr} 未命中) elif typeof_expect(expect): # 简化处理若期望值是字典则只检查是否存在 if isinstance(expect, dict) and expect.get(exists) is True: continue if values and values[0] ! expect: errors.append(f{expr} 期望 {expect}实际 {values[0]}) if max_latency_ms in assertions and result[latency_ms] assertions[max_latency_ms]: errors.append(f响应耗时 {result[latency_ms]}ms 超过阈值 {assertions[max_latency_ms]}ms) return {case: case, result: result, errors: errors} def main(config_path): with open(config_path, r, encodingutf-8) as f: config json.load(f) base_url config[base_url].rstrip(/) results [] for case in config.get(test_cases, []): results.append(run_one_case(base_url, case)) lines [# 测试报告, ] all_pass True for item in results: if item[errors]: all_pass False ... with open(report.md, w, encodingutf-8) as f: f.write(\n.join(lines)) print(report.md generated) if __name__ __main__: main(sys.argv[1])真实场景里脚本要再多几百行但核心逻辑就是这个负责执行的工具不要引入过多业务判断把“规则判断”交给 Agent。4.4 在 Agent 配置中注册技能并跑通 Demo技能写完接下来是接入。我这边是在 Agent 的主配置文件里把 skills 路径加进去类似agent: name: qa_bot skills_path: ./skills框架启动时会扫描 api_test_skill 目录读取 SKILL.md 和脚本。然后我在对话窗口里输入“帮我测试用户中心登录接口环境地址 http://127.0.0.1:8000账号 demo/pwd123要求返回 code 为 0 且有 token 字段响应时间小于 800ms。”Agent 收到任务后先判断这是接口测试任务加载技能包它生成 test_cases.json调用 run_test.py最后把 report.md 转成一句简洁结论。整个过程差不多 30 秒。这个 Demo 跑通后我信心就上来了后续才逐步加 OpenAPI 解析和链路测试。5. 接口测试技能包的调试与排错实录5.1 常见问题速查表我在使用这个技能包的两个月里陆陆续续踩了不少问题。整理成一张速查表方便你直接对着查症状主要原因解决办法Agent 一直不调用技能SKILL.md 的 description 太宽泛或太窄模型识别不到触发场景重写 description明确“仅当测试接口、验证返回、检查API链路时使用”调用技能但参数乱传参数定义不清晰缺少枚举值和示例在 SKILL.md 增加字段类型、允许值、参考样例脚本执行报 JSONDecodeError响应体不是合法 JSON或请求头没带 Content-Type脚本里统一 try_parse_json并检查请求头设置动态关联字段总为空JSONPath 写错或上游接口没执行成功先用调试模式打印上游响应全文再核对路径各地环境跑不通技能包里硬编码了地址或 token全部改从环境变量或 config 传入网络抖动导致误报失败脚本没有重试机制增加带超时和重试的请求发送函数报告内容太长Agent 总结困难日志和响应原文全量输出到报告限制输出行数保留关键字段与失败详情省略冗长响应体5.2 我在接入过程中踩过的三个坑第一个坑是技能描述写得太“野心勃勃”。我最初写的是“处理与HTTP相关的任务”结果 Agent 在帮我写邮件模板时也去调用技能频繁报错。后来我改成“仅当用户明确要求测试接口或验证接口返回内容时使用”误调用的比例明显下降。这提醒我技能描述写得越聚焦Agent 的调度越准。第二个坑是脚本里的鉴权信息。最初为了图方便我直接在脚本里写了一个测试环境 token结果换到另一个环境后所有用例都鉴权失败。后来统一改成从环境变量AUTH_TOKEN读取SKILL.md 里也提醒 Agent 优先使用环境变量中的鉴权信息。这个问题不大但一旦遇到排查成本会很高。第三个坑是响应体解析不严谨。早期我对响应文本直接json.loads一旦接口返回的是纯文本或空字符串整个脚本就抛异常。后来我把解析逻辑改成“能解析就解析解析不了返回 None”异常处理兜住之后脚本稳定性高了一个量级。做工具型脚本防御式编程一定是必修课。5.3 技能包生产化日志、超时、鉴权过期与并发如果只是自己本地玩上面那套已经够用。但如果你要放在团队里我建议再做三件事。第一是加日志。脚本执行过程中要把每一步的输入输出摘要打印到日志文件里例如“调用 POST /api/login状态码 200耗时 320ms”。不建议打印完整请求体和响应体一个是占空间另一个是可能有敏感信息。日志文件要按天轮转无否则跑半个月之后磁盘就满了。第二是处理鉴权过期。测试环境很多接口的 token 有效期只有一小时长时间执行用例经常中途失效。我做了个简单的 token 刷新模块如果断言发现鉴权失败就尝试调用 SKILL.md 里配置的 refresh 接口拿到新 token再重跑受影响用例。虽然实现不复杂但对长链路测试帮助很大。第三是并发策略。我看到有人一上来就给技能包加线程池并发执行用例结果业务系统扛不住误报一堆。接口测试里建议默认串行执行只有在明确都是只读接口、且不影响数据一致性的前提下再允许并发。稳定永远排在速度前面。一点个人体会这套接口测试技能包做下来我最深刻的感受是Agent 的价值不在于替你把 curl 命令敲对而在于它能像测试人员一样思考要测什么、顺序怎么安排、失败代表什么。技能包则是把那个“能思考的大脑”和一个“动手能力稳定的身体”接起来。最后分享一个小技巧在 SKILL.md 的末尾加一个 examples 区块放一条带完整依赖的请求示例比如“登录成功后用返回的 token 查询用户资料”。Agent 参考这条链路后处理类似场景的成功率会有肉眼可见的提升。这个操作几乎零成本却能让你的技能包立刻“聪明”起来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑