news 2026/10/3 4:36:59

Agent测试方法论:从不确定性到可测性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent测试方法论:从不确定性到可测性

接手这个Agent项目的第一周,我对着需求文档发了半天呆。做过Web测试的人应该懂那种感觉:平时我们测的是按钮、接口、页面跳转,而这次摆在我面前的是一套会“自己思考”的系统——用户输入一句话,它能自己决定调哪个工具、按什么顺序、怎么组织回答。传统测试那套“输入固定、输出可断言”的玩法,在这里基本失效了。

更让人难受的是,开发那边一开始也不知道该怎么配合测试。需求描述只有“让Agent能帮用户完成报销审批”“支持查询销售数据”这种粒度,既没有明确的验收标准,也没有“什么算答对”的定义。我们团队的技术负责人半开玩笑地说了一句:“测试别着急,等Agent跑通了再介入也不迟。”但我知道,如果真的等开发完再介入,这个项目大概率会在上线前陷入无尽的“人肉回归”——因为大模型输出的不确定性,会让每一次改动都像推倒重来。

所以这篇笔记我想认真聊聊:在一个Agent项目里,测试到底是怎么一步步从边缘走向核心的。这篇不是讲理论,而是我在实际项目里踩过、填过的坑,希望给同样在做Agent测试、或者团队里还没想清楚测试该怎么介入的朋友一些参考。

1. 接手Agent项目的第一周:先别急着写用例

我刚接手项目时,最常见的冲动是“赶紧找几个用户问题跑一遍,看看Agent答得对不对”。但真跑起来就发现问题了:同一个问题问三遍,答案可能三遍都不一样;有时Agent会绕一大圈去调无关工具;有时干脆拒绝执行。如果这时候就开始写用例,写出来的全是一堆“结果不确定”的用例,根本无法支撑长期回归。

1.1 和开发对齐的第一件事:把“不可测”变成“可测”

我做的第一件事不是写测试用例,而是拉着开发和产品经理开了一个“可测性对齐会”。主题只有一个:Agent系统的哪些部分是代码逻辑,哪些部分是模型行为,哪些是外部依赖。快速梳理下来,一个典型的Agent请求链路可以拆成这样:

  • 用户输入进入编排层,Agent决定调用哪个工具、传入什么参数;
  • 编排层把工具结果返回给模型,模型基于结果生成最终回答;
  • 整个过程涉及Prompt模板、工具定义、结果解析、异常处理、日志记录。

这里有个关键认知:Agent项目的测试,不能只盯着“模型的回答”这一个输出点。模型产生的是“会话回复”,而工具调用、参数生成、状态流转、权限判断这些环节,大部分是确定性逻辑,它们完全可以按传统测试的方式来断言。把能确定的东西先测住,剩下大模型那部分才能专心对付。

1.2 定义“正确”的样子:搭建评判体系

对齐完边界之后,紧接着要做的是定义“什么样的输出算正确”。这一步听起来虚,但实际上是整个测试工作能不能落地的分水岭。

我们当时把Agent的输出拆成了四个评价维度,并且跟产品经理逐条确认了每个维度的口径:

  • 任务完成度:用户的目的有没有被满足,比如“查一下上月销售额”是否真的返回了对应数据;
  • 工具使用正确性:调用的工具是否合理,参数是否正确、是否缺少必要字段;
  • 回答规范性:格式、语气、是否包含必要的数字单位、来源标注;
  • 安全与边界:是否拒绝越权操作、是否编造不存在的功能、是否泄露敏感信息。

这四个维度不是凭空定的,而是来自当时线上用户反馈里最常见的四类问题:任务没完成、工具调错、答案敷衍、以及“一本正经地胡说八道”。把用户痛点转成评判维度,后面所有测试用例的断言都有了依据。

1.3 任务分级:不同复杂度的任务采用不同测试策略

维度定好之后,我又和产品一起把所有计划支持的场景按复杂度分了个级,这个分级直接决定了后面测试投入的优先级:

级别场景示例测试策略
L1 简单规则任务查询天气、计算器、查快递全量自动化回归,断言相对固定
L2 业务查询任务查销量、导报表、查审批进度自动化回归 + 关键字段断言 + 少量人工抽检
L3 多步操作任务跨部门报表汇总、申请审批、异常流转半自动化脚本 + 黄金数据集比对 + 上线前人工评审
L4 高风险操作删除数据、支付、外部下单强制人工确认,自动化只做前置校验和结果回读

这个分级最大的价值在于:它让测试组知道哪些场景要用“确定性断言”去卡,哪些场景必须保留“人工判断”的入口。不是所有Agent回答都能用代码断言,硬用反而会让测试集变成形式主义。

2. 测试分层视角的重构:确定性逻辑和大模型行为分开测

在传统Web项目里,测试金字塔一般是UI、接口、单元逐层展开的。Agent项目表面看也有一套“界面”(聊天窗口),但实际上完全不是一个逻辑。如果拿测Web的思路去测Agent,很容易出现两种情况:要么天天在聊天框里人肉点点点,要么写一堆脆弱到一改Prompt就全红的UI自动化脚本。

2.1 传统测试三层模型在Agent项目里哪里失效了

传统UI层测的是页面元素的交互,而Agent的“UI”是自然语言对话。同样的问法在不同时间可能得到不同的表述,页面自动化断言文本很容易误报。接口层测的是请求和响应结构,但Agent的响应是模型生成的,字段结构虽然稳定,内容语义却千变万化。所以Agent项目不能沿用“UI、接口、单元”的三层包抄,而要改成按“确定性边界”来分层。

我自己的做法是把整个系统切成两半来测:

  • 确定性逻辑层:Agent的编排、工具选择、参数解析、会话管理、鉴权、工具执行结果处理;
  • 模型行为层:Prompt的阅读理解、任务规划、语气风格、面对模糊输入时的决策。

确定性的部分用pytest直接做自动化,模型行为的部分用“样本集 + 评分函数 + 人工抽检”的方式来管。两层分开之后,测试用例的稳定性一下子高了很多。

2.2 确定性部分:工具调用参数、状态流转、权限校验

这里我可以用一个我们项目的真实场景来说明。我们有个“销售数据查询”工具,用户说“查一下上个月华东区的销售额”,Agent需要调用一个报表服务接口。整个调用过程里面,Agent要生成类似这样的工具调用参数:

{ "tool": "sales_report", "params": { "region": "华东", "date_from": "2024-10-01", "date_to": "2024-10-31" } }

对于这一层,我要验的是:

  • 用户说“上个月”,Agent是否翻译成了正确的起止日期;
  • “华东区”是否被正确映射到区域编码;
  • 如果用户说“去年同期”,是否识别为去年同一时间段;
  • 在Agent误生成缺失参数或错误时间格式时,编排层是否能拦截并触发追问。

这些全是确定性的断言,跟大模型的“口才”没有关系,可以用非常稳的自动化用例覆盖。只要工具调用参数正确、编排逻辑健壮,Agent哪怕回答得啰嗦一点,任务也是成功的。

2.3 非确定性部分:大模型输出的评价维度

而模型“怎么回答”的部分,我基本不写死文本断言。除非是产品硬性要求“必须以X开头”,否则任何针对完整句子的相等比较都会在两天后变红。我们改成评价式断言:

  • 是否包含了关键数字(比如销售额“1234.5万”不能丢);
  • 是否夹带了来源或单位,保证可追溯;
  • 是否在知识不足时明确说“不知道”,而不是编造;
  • 是否保持了礼貌口径,没有出现负面表述。

这类断言背后是“语义是否合格”的判断,而不是“字符串是否相等”的判断。为了方便落地,我把每一个评价维度都做成可勾选的检查项,由评分器跑完自动给出分数。人工只需要抽检低分样本,不用全量看。

3. Prompt测试落地:从“人肉看效果”到自动化回归

Prompt测试是整个Agent项目里最容易被忽视、但也最值得投入的部分。很多团队的做法是:开发调Prompt的时候,拿几个例子试一下,“看起来不错就上”。可一旦Prompt被反复调整,前两周看着很好的效果,可能只是一些样例过拟合的结果。换一批真实问题,立刻打回原形。

3.1 把Prompt测试当成数据驱动测试来做

我在这里最核心的经验是:Prompt测试不测Prompt本身,而是测“Prompt在不同输入下的行为”。也就是说,Prompt是逻辑,输入输出对是数据,测试就是把这些数据喂进去看输出是否满足评分规则。

我维护了一套“行为测试集”,以JSON文件存放,每个样本包含:

{ "id": "TC-SALES-034", "input": "查一下上个月华南区的销售额,只要总数", "expected_tools": ["sales_report"], "expected_params": { "region": "华南", "date_from": "2024-10-01", "date_to": "2024-10-31" }, "expected_answer_contains": ["销售额"], "forbidden_keywords": ["猜测", "可能"] }

这套JSON本身就是测试用例库,跟代码一样纳入版本管理。每次开发改了Prompt,我把这套数据跑一遍,马上就能看出哪些行为变了、是变更好了还是变更差了。这套机制落地之后,我们从“靠感觉调Prompt”变成了“看回归报告调Prompt”,研发那边的反馈是:终于不用怕改Prompt改出隐蔽问题了。

3.2 用pytest快速搭起Prompt回归框架

自动化框架我直接选用了pytest,原因很简单:团队里其他项目的接口测试就在用它,复用成本最低,而且它对数据驱动支持得很好。核心结构不复杂,每个人都能上手:

import pytest import json CASES = json.load(open("agent_test_cases.json", encoding="utf-8")) def run_agent(input_text): # 调用被测Agent的入口,返回工具调用和最终答案 ... def assert_tool_called(trace, expected_tool): assert any(item["tool"] == expected_tool for item in trace) def assert_params(trace, expected_params): # 找到对应工具调用,校验关键参数 ... @pytest.mark.parametrize("case", CASES, ids=lambda c: c["id"]) def test_agent_behavior(case): trace, answer = run_agent(case["input"]) assert_tool_called(trace, case["expected_tools"]) assert_params(trace, case["expected_params"]) for keyword in case["expected_answer_contains"]: assert keyword in answer for keyword in case["forbidden_keywords"]: assert keyword not in answer

这个框架跑起来之后,每天CI都会执行。跑挂了的用例会自动拉出“当时的输入、工具调用轨迹、最终答案”,开发一眼就能定位是编排问题还是Prompt问题。这里我想特别强调:框架本身不值钱,值钱的是那个不断生长的测试数据集。

3.3 评分函数才是Prompt测试的灵魂

但纯pytest的逻辑断言解决不了“语义对不对”的问题。比如Agent这次回答“上月华东区销售额为1234.5万,较前月增长5%”,如果我断言“销售额”三个字在不在答案里,能过;但会不会数字张冠李戴?会不会“增长5%”是编的?这些没法靠关键词断言查出来。

所以我们引入了一个“评分器”组件,跑完Agent后自动给回答打分。具体做法是:先让Agent跑出答案,再把这个答案连同原始问题、工具结果、参考预期一起发给一个评审模型,让它按我们预设的四维标准打分。分数低于阈值(比如0.7)的用例自动进入“待人工复看”列表,由测试人员每周集中处理一次。

这个方案体验下来有两个好处:一是把人工从“全量看聊天记录”里解放出来,只需要盯低分样本;二是积累的“低分原因”反过来会变成下一轮Prompt优化的方向,形成闭环。

4. 工具调用与编排链路的验证:全链路mock与失败注入

如果说Prompt测试管的是Agent“想得对不对”,那工具调用和编排链路管的就是Agent“做得稳不稳”。这也是Agent项目里翻车最频繁的地方。用户在聊天里一句话,背后可能牵涉两三个工具调用、多个外部服务,任何一个环节出了问题,最终答案都可能错。

4.1 工具注册表与参数契约检查

我们的Agent框架里有一个工具注册表,所有可被调用的工具都定义了名称、描述、入参结构。测试介入时,第一个基础动作就是把注册表里所有工具逐一过一遍,校验三个点:

  • 工具描述是否清晰,模型能不能根据描述做出正确选择;
  • 入参是否定义完整,有没有必填参数缺失;
  • 返回结构是否稳定,编排层解析结果时会不会因为字段结构变动而崩溃。

这个环节我们写了自动化脚本,每次框架或工具版本变更后自动执行。这里有个很典型的坑:开发在新建工具时,把返回结构里的字段名改了个大小写,结果Agent解析失败,直接导致用户问题无法完成。如果没有契约检查,这种失误很难在开发自测阶段暴露。

4.2 外部服务mock的边界要划清楚

Agent所在的业务流程里,通常要调企业内部的订单系统、报表系统、审批流、IM通知等外部服务。测试环境很难完整搭建一套真实系统,所以mock是必须的。但mock的边界如果划不清晰,测试很可能变成“自己和自己玩”。

我的做法是:按照“对Agent行为的观察价值”来决定mock粒度。如果被测点是Agent的工具选择与参数生成,那外部服务全部mock掉,返回固定结构的数据即可。如果被测点是Agent针对工具返回结果的推理能力,那么mock数据要设计成包含各种边界情况的样本——空结果、多记录、异常数据、字段缺失。

举个例子,用户问“查一下库存”,我mock一个工具返回“库存为0”。这时候正确的Agent行为应该是说“该商品暂无库存”,而不是傻乎乎地报一个0或者胡编一个仓库位置。这些边界样本需要测试提前跟业务方确认口径,做成固定样例,每次版本迭代反复验证。

4.3 失败注入:让Agent学会“体面地失败”

外部服务一定会失败。网络超时、接口报错、数据格式异常,这些在传统接口测试里测的是“报错信息是否友好”,但在Agent项目里测的是“Agent能不能从失败里恢复”。

我们当时专门做了一轮失败注入专项:

  • 让报表接口随机返回500,看Agent是否会尝试重试;
  • 让工具返回超时异常,看Agent是否会放弃任务并且向用户说清楚原因;
  • 让某一步工具返回空数据,看Agent是否会编造数字还是诚实说“未查询到”。

结果发现,Agent在遇到工具异常时经常有两种错误表现:一种是彻底卡住反复调用同一个工具,形成死循环;另一种是假装任务完成,在回答里写“查询成功”,实际上什么数据都没有。这两个问题都是通过失败注入暴露的,修复方式分别是增加最大重试次数和增加“未获取到数据”的路径提示。如果没有这一轮测试,这些问题不到线上根本浮不出来。

5. 并发、会话隔离与幂等:Agent上线前的稳定性功课

Agent项目在功能层面跑顺之后,紧接着就是稳定性。很多人以为Agent系统的并发问题跟普通Web服务一样——多上几台机器、挂个负载均衡就扛住了。实际上,Agent因为“有状态会话”和“多次工具调用”的存在,并发测试的复杂度比普通接口高一大截。

5.1 有状态会话造成的并发陷阱

我们的Agent是带记忆的,同一个会话里Agent能记住用户前面说过的话。这样设计用户体验好,但也带来了并发问题:如果用户在A设备开了会话,又在B设备问了另一个问题,Agent能否正确区分两个会话的上下文?如果同一会话里用户连续发了两个指令,系统会不会把两个任务的上下文混在一起?

这些场景我整理成专门的测试用例去覆盖,重点验证的是“会话隔离”:

  • 两个不同会话拥有独立记忆,互不串扰;
  • 同一会话的并发请求按顺序处理,不产生覆盖写;
  • 会话上下文不会因为工具调用而丢失或错乱。

当时真的测出过一个问题:两个用户同时唤起查询任务时,A用户拿到的报表数据被B用户的查询条件覆盖了。根因是工具实例不小心被设计成了全局单例,本来以为是小概率事件,结果并发一压就现了原形。

5.2 幂等性验证:同一个任务执行两次会怎样

Agent工具调用涉及外部系统时,幂等性问题比传统接口更隐蔽。比如用户点了一次“提交报销单”,Agent提交成功了,但响应超时导致界面重试,这时如果Agent又调了一次报销提交接口,就会重复提交,产生两笔报销单。

所以我在做自动化集成测试时,专门设计了一个幂等用例组:让同一输入、同一会话ID的任务被执行两遍,观察外部系统是否产生两条数据。这里的核心是:Agent系统必须在框架层面为工具调用生成唯一的调用ID,外部服务根据这个ID做去重。测试的工作就是验证“重复请求不会造成副作用”。

5.3 超时与重试策略的测试点

Agent一次任务里可能包含多个串行工具调用,总耗时天然比普通接口长。测试超时和重试策略时,要特别关注几个点:

  • 超时阈值设在哪里合适,既不能太短导致正常任务被误判失败,也不能太长导致用户等待过久;
  • 重试次数是否有限制,失败后是否退避,避免死循环;
  • 重试是否会在外部服务已经处理成功的情况下重复提交。

我在压测环境里做过一次模拟:外部报表接口在30%的概率下延迟3秒。Agent系统原来的超时设的是2秒,结果导致大量任务被误判失败,用户体感极差。后来把超时调到4秒,并辅以一次重试,整体成功率才回到95%以上。这些参数需要测试用数据说话,不能拍脑袋。

6. 回归数据集的建设:让测试越做越轻松

Agent项目上线之后,测试工作并没有变少,反而更多了。因为模型在更新、Prompt在频繁调优、工具在不断增加,每一次改动都可能引入回归。如果没有一套能持续生长的测试数据体系,测试组永远在“救火”。

6.1 线上采集真实输入,转化成回归样本

最能发现问题的永远是真实用户。我们上线后做的第一件事,就是把线上用户输入(脱敏后)全部沉淀下来,按语义聚合成高频场景,再挑出有代表性的问题补充到回归数据集里。这些样本混杂着各种口语化说法、错别字、不完整句子,比测试工程师自己拍脑袋写的用例真实得多。

举个具体的例子:用户会说“查下上个月卖的怎么样”,也可能说“给我看看上个月华东的销量数据”,还可能说“上个月业绩咋样”。这三句话在字面上完全不同,但期望工具调用是一样的。把这些变体放进回归集里,就能持续检验Agent对语义泛化的能力。

6.2 数据回流机制:用户反馈是最好的测试用例

我们还做了一个“用户反馈转用例”的流水线:用户会给回答打低分或投诉,这些带着明确信号的数据,每周由测试人员做一次梳理,筛选出属于Agent本身问题的,直接转成回归样本。这条链路跑顺之后,每次版本迭代前都会先跑一遍“近期反馈集”,确保之前用户踩过的坑不会改着改着又冒出来。

这里我特别想提醒:那一类“低质量但不算错”的回答,比如答案不够简洁、没有给出关键数字、绕了很多话,是很难被离线自动检测出来的,只能靠用户反馈来捕捉。所以数据回流机制不是锦上添花,而是Agent质量闭环里的核心环节。

6.3 测试门禁与发布流程的结合

最后一步是把上面所有工作固化成门禁。我们的发布流程里设了三个关卡:

  • 第一关:工具调用链路自动回归全部通过,覆盖注册表校验、参数契约、失败注入用例;
  • 第二关:Prompt行为测试集通过率达到95%以上,低分样本无人复看确认风险;
  • 第三关:并发与幂等抽测无问题,关键路径在压测环境下达到预期指标。

任何一次Prompt改动或Agent框架升级,都必须过这三道门禁才能合并发布。这个机制落地之后,团队最大的变化是:大家终于敢频繁调Prompt了。以前调一次怕一次,现在有回归数据和门禁兜底,改动效率反而大大提升。

我在这个项目里最深的体会是:Agent的测试工作从来不应该是开发的“下游”,而应该跟需求分析同步启动。测试人员前期的“可测性对齐”“评判体系建立”“样本集沉淀”,这三件事做得越扎实,后面开发迭代就越顺畅。等到项目跑稳了,你会发现测试手里最值钱的资产不是测试用例本身,而是那套从线上反馈里不断生长的数据集。它让每一次Prompt优化都有据可依,也让每一个版本的发布都更有底气。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 4:36:50

SOC曲线标定实战指南:从OCV测量到BMS固件集成

1. 为什么一张SOC曲线图,能决定电池管理系统(BMS)的生死?我第一次在车企BMS团队做现场调试时,遇到过一个至今想起来还后背发凉的案例:一辆刚下线的纯电物流车,在交付前最后一次满电静置测试中&a…

作者头像 李华
网站建设 2026/10/3 4:36:39

局域网离线VibeCoding:Claude Code与Codex内网部署全攻略

如果你和我一样,在一家对数据安全卡得很严的研发团队里工作,每天和“代码能不能出网”“这台机器能不能装客户端”较劲,同时又特别想让 Claude Code 和 Codex 这类 AI 编程工具真正帮上忙,那“局域网离线 VibeCoding”这条路你迟早…

作者头像 李华
网站建设 2026/10/3 4:35:52

强化学习路径规划实战:从仿真到实机部署的完整工程指南

简介:本资源是一套基于深度强化学习的动态多智能体路径规划与碰撞规避完整实现方案,面向机器人导航、自动驾驶仿真及AI算法研究者,解决高密度行人环境中智能体协同避障建模难、泛化性弱等核心问题。压缩包共26个文件,含5个核心Pyt…

作者头像 李华
网站建设 2026/10/3 4:34:29

三进制量化实战:27B模型在16GB显卡上的llama.cpp部署与双格式对比

1. 为什么27B模型能在16GB显卡上跑起来1.1 三进制量化的核心逻辑第一次看到“16GB显卡装下27B”这个说法,我的反应和大多数人一样:这不可能。按照FP16精度来算,27B参数的模型光权重就要占掉54GB显存,就算用INT4量化,也…

作者头像 李华
网站建设 2026/10/3 4:34:20

EF Core全局查询筛选器与并发控制实战:从软删除到多租户隔离

做 EF Core 这几年,有两样东西我是后知后觉才真正吃透的,一个是全局查询筛选器,一个是并发控制。这两者在面试题里几乎必问,在实际项目里也处处是坑。先说个我自己的真实翻车经历:早期项目所有业务表都有IsDeleted软删…

作者头像 李华
网站建设 2026/10/3 4:33:46

DDR3带宽计算全解析:从时钟与预取机制到实测验证

搞DDR3带宽计算这活儿,看着就是个公式,实际上坑不少。很多做硬件调试、嵌入式开发的朋友,一上来就拿着“带宽频率位宽”去套,结果算出来的理论值和示波器实测对不上,甚至差出一大截。问题往往不出在乘法上,…

作者头像 李华