☰
AI Agent Skill 测试指南:Pytest、Agent 集成与 LLM/LVM 评测
2026/10/30 14:19:13 网站建设 项目流程

很多同学在接触 AI Agent 开发时会发现:写一个 Skill 并不难,难的是怎么测试这个 Skill。尤其是当 Skill 要调用工具、依赖大模型输出、甚至还要处理图片等多模态输入时,传统的测试思路往往派不上用场。本文会围绕 Skill 测试这个主题,拆解 Pytest、Agent 集成测试、LLM + LVM 效果评测这三套方案,并把每一套方案的适用场景、代码示例和坑点都讲清楚。如果你正在做 Agent 开发、Skill 封装或 LLM 应用落地,这篇文章值得收藏。

揭秘 Skill 测试 3 大方案:Pytest + Agent + Skills + LVM + LLM(只讲干货)

1. 为什么 Skill 测试不能照搬传统测试

1.1 什么是 Skill

在 AI Agent 开发语境下,Skill(技能)指的是把某个领域的操作步骤、工具调用、上下文知识和输出规范封装成可复用的能力单元。比如“前端开发 Skill”可以封装 HTML/CSS 代码生成的规则,“数据分析 Skill”可以封装数据读取、清洗、可视化的固定流程。

Skill 和普通函数的区别在于:普通函数的输入和输出都是确定性的,而 Skill 的运行过程往往包含大模型推理、工具调用、外部系统交互,输出结果存在一定的不确定性。这就给测试带来了全新的挑战。

1.2 传统测试与 Skill 测试的核心差异

传统单元测试关注“输入 -> 输出”的确定性断言,比如 assert add(1, 2) == 3。而 Skill 测试至少需要覆盖三个层面:

  • 确定性逻辑:Skill 内部的参数校验、状态流转、工具调用参数拼接是否正确。
  • 集成链路:Agent 是否能在合适的场景下正确选择并调用 Skill,Skill 是否能和大模型、外部工具正常协作。
  • 生成效果:大模型生成的最终结果是否满足用户需求,是否存在幻觉、格式错误、信息缺失。

换句话说,Skill 测试既要测“代码逻辑”,又要测“模型行为”,还要测“人机交互效果”。如果只测其中一层,线上很容易出问题。

1.3 本文的三大方案定位

结合当前 Agent 开发的主流实践,我把 Skill 测试拆成三个可落地的方案:

  • 方案一:Pytest 工具级测试,解决 Skill 内部逻辑的确定性问题。
  • 方案二:Agent 运行时集成测试,解决 Skill 在真实 Agent 链路中的调用问题。
  • 方案三:LLM + LVM 效果评测,解决生成结果质量与多模态场景的验收问题。

这三个方案不是互相替代的关系,而是从底向上逐层覆盖。下面逐一拆解。

2. 环境准备与测试策略设计

2.1 环境说明

本文示例以 Python 3.10+ 环境为基础,测试框架使用 Pytest,Agent 运行时以常见的函数调用式 Agent 为例。实际项目中,你可能使用 LangChain、LlamaIndex、自研 Agent 框架或商业化平台,但测试思路是通用的。

需要准备的依赖包括:

pip install pytest pytest-asyncio

如果你需要调用 OpenAI 兼容接口做效果评测,还需要安装对应 SDK:

pip install openai

版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。

2.2 测试金字塔策略

我的建议是把 Skill 测试设计成三层金字塔:

层级测试对象执行频率失败影响
第一层:Pytest 单元测试Skill 内部函数、工具调用参数每次提交定位最准
第二层:Agent 集成测试Skill 注册、路由、调用链路每次发版发现链路问题
第三层:LLM/LVM 评测生成结果质量、多模态效果发布前/定期反映用户体验

越底层的测试越应该频繁执行,越上层的测试越依赖真实模型,执行成本也越高。将三层结合,才能覆盖 Skill 从实现到交付的全链路质量。

3. 方案一:Pytest 工具级测试

3.1 为什么先用 Pytest 测 Skill

Skill 虽然包含大模型推理,但绝大部分 Skill 仍然有大量确定性逻辑。比如:

  • 解析用户输入中的参数。
  • 根据参数决定调用哪个工具。
  • 拼接工具调用所需的 JSON 请求体。
  • 对工具返回结果做后处理。

这些逻辑完全可以用 Pytest 做确定性断言。Pytest 的优势在于 fixture 管理、参数化测试、断言直观,还能和 pytest-asyncio 配合测试异步 Skill。

把 Skill 中的确定性逻辑单独抽出来测试,可以在不消耗 Token、不依赖模型的情况下快速发现问题。这也是所有 Skill 测试方案中最便宜、最稳定的一层。

3.2 先解决 Skill 的可测试性

很多 Skill 不好测,是因为逻辑全写在一个巨大的 prompt 里,或者全写在一个 process 方法中。要想让 Skill 可测试,需要在设计阶段就把“纯逻辑”和“模型调用”分开。

举个例子,下面是一个电商客服 Skill 的简化结构:

# skill.py class CustomerServiceSkill: def __init__(self, llm_client, tool_client): self.llm_client = llm_client self.tool_client = tool_client def parse_intent(self, user_input: str) -> dict: """从用户输入中解析意图,纯逻辑函数,可单测""" if "退款" in user_input: return {"intent": "refund", "keywords": ["退款"]} elif "物流" in user_input: return {"intent": "logistics", "keywords": ["物流"]} else: return {"intent": "unknown", "keywords": []} def build_tool_params(self, intent: str, order_id: str) -> dict: """根据意图构造工具调用参数,纯逻辑函数,可单测""" if intent == "refund": return {"api": "refund", "order_id": order_id} elif intent == "logistics": return {"api": "track", "order_id": order_id} return {} async def run(self, user_input: str, order_id: str): """Skill 主入口,包含模型推理和工具调用""" intent_info = self.parse_intent(user_input) params = self.build_tool_params(intent_info["intent"], order_id) if not params: return await self.llm_client.generate_response(user_input) tool_result = await self.tool_client.call(params) return await self.llm_client.generate_response(tool_result)

这样设计之后,parse_intent 和 build_tool_params 都不依赖外部模型和工具,可以直接用 Pytest 做测试。

3.3 Pytest fixture 与参数化实战

下面编写针对上述 Skill 的 Pytest 测试。重点是测试纯逻辑函数,并用 fixture 准备测试数据。

# test_skill.py import pytest from skill import CustomerServiceSkill class MockLLMClient: async def generate_response(self, content): return f"mock response: {content}" class MockToolClient: async def call(self, params): return {"status": "ok", "params": params} @pytest.fixture def skill(): llm_client = MockLLMClient() tool_client = MockToolClient() return CustomerServiceSkill(llm_client=llm_client, tool_client=tool_client) @pytest.mark.parametrize( "user_input,expected_intent", [ ("我想申请退款", "refund"), ("我的订单什么时候发货", "logistics"), ("你好", "unknown"), ], ) def test_parse_intent(skill, user_input, expected_intent): result = skill.parse_intent(user_input) assert result["intent"] == expected_intent def test_build_tool_params_refund(skill): params = skill.build_tool_params("refund", "12345") assert params == {"api": "refund", "order_id": "12345"} def test_build_tool_params_unknown(skill): params = skill.build_tool_params("unknown", "12345") assert params == {}

3.4 异步 Skill 测试

如果 Skill 的 run 方法是异步的,可以用 pytest-asyncio 标记测试:

# test_skill_async.py import pytest pytestmark = pytest.mark.asyncio async def test_skill_run_with_refund(skill): result = await skill.run("我想申请退款", "12345") assert "mock response" in result

这里的关键是把 MockLLMClient 和 MockToolClient 注入到 Skill 中,测试过程不产生真实模型调用,也不消耗 Token,执行速度非常快。

方案一的核心结论:凡是 Skill 里不依赖模型的逻辑,都应该用 Pytest 做确定性测试。这能让 Skill 的基础质量得到保障,也为上层测试打下基础。

4. 方案二:Agent 运行时集成测试

4.1 集成测试要解决什么问题

单元测试通过只能说明 Skill 内部逻辑正确,但 Agent 系统中还有一个关键环节:Agent 是否能在合适的时机选择这个 Skill,并且把上下文正确传递给它。

很多时候,Skill 单测通过,但放到 Agent 里就失灵。常见原因包括:

  • Agent 的意图识别没有触发该 Skill。
  • Agent 传给 Skill 的上下文格式不对。
  • Skill 执行完没有把结果正确返回给 Agent。
  • 多个 Skill 之间存在路由冲突。

这些问题是单测覆盖不到的,必须通过 Agent 运行时集成测试来验证。

4.2 如何设计 Agent 集成测试

集成测试的思路是:构造一个接近真实的 Agent 运行环境,但把外部依赖(LLM、工具、数据库)替换成可控的 Mock 或 Test Double。

下面以一段简化代码演示。

# agent_harness.py class Agent: def __init__(self, skills: dict, llm_client): self.skills = skills self.llm_client = llm_client async def handle(self, user_input: str): # 简化:根据关键词选择 Skill for name, skill in self.skills.items(): if name in user_input: return await skill.run(user_input) return await self.llm_client.generate_response(user_input)

对应的集成测试如下:

# test_agent_integration.py import pytest pytestmark = pytest.mark.asyncio from agent_harness import Agent from skill import CustomerServiceSkill class MockLLMClient: async def generate_response(self, content): return f"fallback: {content}" class MockToolClient: async def call(self, params): return {"status": "ok", "params": params} @pytest.fixture def agent(): llm_client = MockLLMClient() tool_client = MockToolClient() skill = CustomerServiceSkill(llm_client=llm_client, tool_client=tool_client) skills_map = {"客服": skill} return Agent(skills=skills_map, llm_client=llm_client) async def test_agent_routes_to_skill(agent): result = await agent.handle("客服,我想申请退款") assert "mock response" in result async def test_agent_fallback_to_llm(agent): result = await agent.handle("今天天气怎么样") assert "fallback" in result

4.3 使用记录型 Mock 校验调用顺序

集成测试除了关心返回结果,还要关注 Agent 调用了哪些 Skill、以及调用顺序是否正确。可以使用记录型 Mock 来捕获调用历史:

class RecordingToolClient: def __init__(self): self.calls = [] async def call(self, params): self.calls.append(params) return {"status": "ok", "params": params} async def test_agent_tool_call_order(): tool_client = RecordingToolClient() llm_client = MockLLMClient() skill = CustomerServiceSkill(llm_client=llm_client, tool_client=tool_client) agent = Agent(skills={"客服": skill}, llm_client=llm_client) await agent.handle("客服,我想申请退款") assert len(tool_client.calls) == 1 assert tool_client.calls[0]["api"] == "refund"

这种测试可以确保 Skill 在 Agent 中的调用链路符合预期。需要注意的是,集成测试仍然使用 Mock,因此不会触发真实模型,可以在 CI 中快速执行。

4.4 集成测试与 Harness 的区别

热词里经常出现 “harness 和 agent 区别”。简单理解:Agent 是业务系统的一部分,负责处理用户请求;Harness 是测试装置,负责模拟运行环境、注入依赖、记录调用过程。本文中的 Agent 集成测试实际上就是一个轻量级 Harness。在生产项目中,你可以把 Harness 独立成一个测试模块,专门负责 Agent 编排和调用模拟。

集成测试覆盖的是“链路是否正确”,还不足以回答“结果好不好”。这就要用到第三层方案。

5. 方案三:LLM + LVM 效果评测

5.1 为什么要做效果评测

Pytest 和集成测试都验证了流程,但 Skill 的最终输出是面向用户的自然语言内容。如果生成结果质量差,即使流程全通,用户依然不满意。

效果评测要回答的问题包括:

  • 生成结果是否准确、完整?
  • 是否存在幻觉?
  • 是否遵循了 Skill 中定义的格式规范?
  • 多模态场景下,模型对图片的理解是否正确?
  • 结果是否与预期风格、口径一致?

这类测试无法用 assert 简单判断,因此需要引入“评测集 + 大模型裁判”的机制。

5.2 LLM-as-Judge 的思路

LLM-as-Judge 是用一个大模型作为评审,对被测 Skill 的生成结果进行打分或给出判断结论。这种方案成本适中、扩展性好,适合批量评测。

一个典型的评测流程是:

  1. 准备一批带标准答案或评分标准的测试用例。
  2. 用被测 Skill 生成结果。
  3. 让评审模型根据评分标准对结果打分。
  4. 汇总评分,得到 Skill 的整体效果指标。

下面是一个简化示例:

# evaluator.py import json from openai import OpenAI client = OpenAI() def evaluate_with_llm(skill_output: str, expected: str) -> dict: prompt = f"""请根据以下标准评审 AI 生成结果。 【期望结果】:{expected} 【实际结果】:{skill_output} 请打分(1-10),并给出简短原因。 只返回 JSON:{{"score": 0, "reason": ""}}""" response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, ) return json.loads(response.choices[0].message.content)

使用时要根据你实际接入的模型接口调整。对测试用例集循环执行,即可得到平均分和失败用例列表。

5.3 LVM 在多模态 Skill 评测中的作用

LVM(Large Vision Model,大视觉模型)在本语境下指具备图像理解能力的大模型。当 Skill 涉及截图理解、UI 还原、图片内容识别、图表解读等场景时,传统文本评测无法覆盖视觉信息,必须引入 LVM 作为评测辅助。

举个例子:一个“前端开发 Skill”接收 UI 设计图然后生成页面。此时需要评测以下维度:

  • 页面布局是否和设计图一致。
  • 关键元素(按钮、文字、图片)是否遗漏。
  • 颜色、间距、字体是否合理。

这些维度可以通过 LVM 对设计图和生成页面的截图进行对比评分。具体来说,可以让视觉模型分别描述两张图片的结构和内容,再让文本模型对比差异,生成结构化的评审结论。

需要说明的是,LVM 评测目前仍在快速发展中,不同模型的理解能力差异较大。建议在评测环境中固定视觉模型版本,避免因模型升级导致评测结果波动。另外,本文中的 LVM 特指大视觉模型,和 Linux 中的逻辑卷管理(Logical Volume Manager)不是一个概念,读者不要混淆。

5.4 评测集建设

无论是 LLM 评测还是 LVM 评测,都离不开高质量的评测集。评测集应包含:

  • 典型用例:覆盖 Skill 最常见的调用场景。
  • 边界用例:覆盖输入为空、参数缺失、格式异常等边界情况。
  • 困难用例:覆盖复杂任务、长文本、多轮对话、多模态混合输入。
  • 标准答案或评分标准:用于给模型评审提供参照。

评测集建议以 JSON 文件维护,这样便于版本管理和自动化执行:

[ { "id": 1, "input": "生成一个带登录表单的首页", "expected": "页面应包含用户名输入框、密码输入框和登录按钮", "category": "frontend" }, { "id": 2, "input": "描述这张图片中的图表趋势", "expected": "应准确描述折线图的上升和下降趋势", "category": "multimodal" } ]

效果评测适合在每个版本发布前执行,也可以作为夜间任务定期运行,用来监控模型升级和 prompt 调整对 Skill 质量的影响。

6. 完整实战:一个 Skill 如何跑通三层测试

为了让你更直观地理解三层方案如何协同工作,下面用一个“电商客服退款 Skill”完整走一遍。

6.1 项目结构

skill_demo/ ├── skill.py # Skill 核心实现 ├── agent_harness.py # 简易 Agent 运行时 ├── evaluator.py # LLM 评测逻辑 ├── test_cases.json # 评测集 ├── tests/ │ ├── test_skill.py # 第一层:Pytest 单测 │ ├── test_agent_integration.py # 第二层:集成测试 │ └── test_evaluation.py # 第三层:效果评测

6.2 第一层:运行 Pytest 单测

在 skill.py 和 tests/test_skill.py 完成编写后,执行:

pytest tests/test_skill.py -v

预期输出应当是全部用例通过。这一层验证的是 Skill 内部解析逻辑和参数构建逻辑。

6.3 第二层:运行 Agent 集成测试

执行:

pytest tests/test_agent_integration.py -v

这一层验证 Agent 能否在正确的场景下调用“客服” Skill,同时验证工具调用顺序。

6.4 第三层:运行效果评测

效果评测可以单独执行:

python -m pytest tests/test_evaluation.py -v

test_evaluation.py 的简化实现如下:

# tests/test_evaluation.py import json import pytest from skill import CustomerServiceSkill from evaluator import evaluate_with_llm class MockLLMClient: async def generate_response(self, content): return f"退款申请已受理,订单号:{content}" class MockToolClient: async def call(self, params): return {"status": "ok", "order_id": params["order_id"]} @pytest.mark.asyncio async def test_skill_output_quality(): llm_client = MockLLMClient() tool_client = MockToolClient() skill = CustomerServiceSkill(llm_client=llm_client, tool_client=tool_client) with open("test_cases.json", "r", encoding="utf-8") as f: cases = json.load(f) for case in cases: result = await skill.run(case["input"], "12345") verdict = evaluate_with_llm(result, case["expected"]) assert verdict["score"] >= 7, f"用例 {case['id']} 评分过低:{verdict}"

实际项目中,效果评测通常不会作为阻塞性单测频繁运行,因为调用真实模型会带来成本和延迟。更常见的做法是把评分落到数据库或报表,汇总后人工判断是否发版。

7. 常见问题与排查思路

在 Skill 测试落地过程中,很多团队会遇到相同的问题。下面整理一张排查表:

问题现象常见原因解决思路
Pytest 测试通过但 Agent 不调用 Skill测试只测了函数,没测路由逻辑增加 Agent 集成测试,检查技能路由匹配规则
Agent 调用 Skill 后返回空结果Skill 内部异常被吞掉检查 Skill 的异常处理,确保有日志输出兜底结果
LLM 评测分数不稳定评审模型版本不一致或 prompt 没固定固定评审模型版本,统一评分 prompt 和温度参数
多模态评测结果不可信视觉模型对图像理解能力不足换用更强的 LVM,或增加多模型交叉评审
测试执行太慢每层测试都调真实模型底层用 Mock,只让最上层评测调用真实模型
评测集容易过时没有版本管理和职责分工用 Git 管理评测集,每次变更走评审流程
工具调用报 IO 错误外部服务不稳定或参数异常检查工具服务状态,添加重试机制和超时配置

对于热词中提到的“lvm 分区报老是报 io 错误”,如果你的测试环境中确实存在 Linux 逻辑卷管理相关存储问题,请优先检查磁盘空间、RAID 状态和文件系统挂载参数,但这属于基础设施排查范畴,和 AI Skill 测试无关,不要混在一起处理。

8. 最佳实践与工程建议

8.1 设计阶段就要考虑可测试性

Skill 的设计应遵循“逻辑与模型分离”原则。所有不依赖模型推理的部分都抽成普通函数,并保证输入数据是可序列化的结构。这样一来,Pytest 单测和 Mock 注入都会变得非常容易。

8.2 把测试环境与生产环境隔离

集成测试和效果评测应使用独立的 API Key、独立的工具服务实例,避免对生产环境造成污染。涉及退款、删除、写入等危险操作时,测试环境必须使用模拟数据,并且遵循最小权限原则。

8.3 建立三层测试的自动化流水线

我建议在 CI 流程中做如下配置:

  • 每次提交:运行 Pytest 单测,要求全部通过。
  • 每次发版:运行 Agent 集成测试,保证链路可用。
  • 发版前或夜间:运行 LLM/LVM 效果评测,生成质量报告。

这样既能保证速度,又能覆盖质量。

8.4 效果评测要保存历史数据

效果评测一次性的通过率说明不了问题。建议把每次评测的分值、用例 ID、模型版本、prompt 版本都保存下来,形成趋势数据。当 Skill 效果下降时,可以快速定位是模型升级、prompt 修改还是评测集变更导致的。

8.5 注意 Token 成本与评测节奏

LLM/LVM 评测会消耗大量 Token,尤其当评测集规模较大时。可以按用例类型分层抽样:

  • 典型用例全量评测。
  • 边界用例抽样评测。
  • 困难用例人工评审。

这样可以控制成本,同时保持质量监控的有效性。

8.6 不要陷入“测试万能”的误区

测试能提升质量,但不能替代设计。一个 prompt 混乱、函数职责不明的 Skill,无论测试写得多好,线上体验都会打折扣。建议先在设计阶段把 Skill 的输入输出、调用边界、错误处理定义清楚,再用三层测试方案去守护这些契约。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询