你刚上线一个智能客服 Agent,头两周用户好评率 85%,第三周突然跌到 60%。你翻日志发现是新加的退款流程把对话搞乱了——但你改完代码后,怎么确认它真的修好了?下次再改,又怎么保证不踩同一个坑?
靠实时流量看指标是必要的,但实时数据本身在变:用户问题分布变了、业务规则变了、甚至模型版本变了。要判断"Agent 到底有没有进步",你还需要一块不动的水准尺——一套版本化的离线测试基准。Amazon Bedrock AgentCore 的数据集管理功能,就是帮你把这块水准尺从散乱的 JSON 文件变成可追踪、可回滚、可对比的工程资产。
离线基准不能随便改
很多团队做 Agent 评估的方式是:在某个目录下放一堆测试用例 JSON,跑一遍看结果,觉得不对就手动改几条用例。这有几个隐性问题:
- 基准漂移:你改了测试用例,这次跑出来的分数和上次就不在一个坐标系里了。"上周 72 分,这周 80 分"听起来像进步,但可能只是你把难题删了。
- 不可回溯:没有版本记录,三个月前那套用例长什么样已经没人记得。出了回归问题,你连"当时测了什么"都查不到。
- 协作冲突:多人各自加用例、改预期,合并时谁的标准为准?
Bedrock AgentCore 把测试用例当作数据集来管理,核心思路和软件工程里的版本化测试 fixture 一致:基准本身也需要被版本化、被评审、被锁定。
数据集管理怎么运作
在 AgentCore 里,一个评估数据集就是一个有版本的对象。每个版本包含一组测试用例(输入 + 预期行为描述),版本一旦创建就不可变。你可以:
- 创建新版本:新增用例或修改用例后,生成一个新版本,老版本不受影响。
- 锁定基准版本:日常回归测试始终跑同一个版本,确保分数可比。
- 按版本对比:用 v3 基准跑旧模型和用 v3 基准跑新模型,差异才是真实进步;用 v3 基准跑新模型和用 v4 基准跑新模型,差异反映的是基准本身的变更。
这套机制把"Agent 变了"和"测试变了"两条线彻底分开,你终于能回答那个关键问题:这次改动到底让 Agent 变好了还是变坏了?
实战:从零搭建版本化测试基准
下面用一个完整的 Python 示例演示如何用 Bedrock AgentCore SDK 创建评估数据集、添加用例、发布版本,并跑一轮回归测试。假设你已经有 AWS 凭证配置好,且安装了 boto3。
以下 API 调用基于 Bedrock AgentCore 当前公开概念推演,具体字段名以 AWS 官方文档为准。运行前请确认你所在 Region 已开放 AgentCore 服务。
1. 创建数据集并写入第一批用例
import boto3
import json
from datetime import datetime
client = boto3.client("bedrock-agentcore", region_name="us-east-1")
# 创建评估数据集
dataset_name = "customer-service-agent-baseline"
create_resp = client.create_evaluation_dataset(
name=dataset_name,
description="客服 Agent 离线回归基准——覆盖退款、改地址、查物流等核心场景",
)
dataset_id = create_resp["datasetId"]
print(f"数据集已创建: {dataset_id}")
# 写入第一批测试用例
test_cases = [
{
"testCaseId": "refund-standard",
"input": {
"userMessage": "我昨天买的鞋子想退款,订单号 CS-20240512-007",
},
"expectedBehavior": {
"intent": "refund_request",
"mustMention": ["退款流程", "预计到账时间"],
"mustNotMention": ["换货"],
"finalState": "refund_initiated",
},
},
{
"testCaseId": "address-change-with-refund",
"input": {
"userMessage": "我要改收货地址,但这个订单我已经申请退款了",
},
"expectedBehavior": {
"intent": "address_change_with_conflict",
"mustMention": ["退款进行中无法改地址", "建议取消退款后再改"],
"mustNotMention": ["直接改地址成功"],
"finalState": "conflict_explained",
},
},
{
"testCaseId": "shipping-status",
"input": {
"userMessage": "订单 CS-20240510-003 到哪了",
},
"expectedBehavior": {
"intent": "shipping_query",
"mustMention": ["当前物流状态", "预计送达日期"],
"mustNotMention": [],
"finalState": "status_provided",
},
},
]
for tc in test_cases:
client.put_test_case(
datasetId=dataset_id,
testCase=tc,
)
print(f"已写入 {len(test_cases)} 条测试用例")
2. 发布第一个基准版本
# 锁定当前用例集合为 v1 版本——之后不可修改
version_resp = client.create_dataset_version(
datasetId=dataset_id,
versionLabel="v1",
description="初始基准:3 条核心场景,2024-05-15 发布",
)
version_id = version_resp["versionId"]
print(f"基准版本已锁定: v1 → {version_id}")
从这一刻起,v1 的内容就冻住了。哪怕你后来往数据集里加了 50 条新用例,跑回归时仍然可以指定 v1,分数永远在同一个坐标系里。
3. 用基准版本跑回归评估
# 对指定 Agent 跑 v1 基准的回归测试
eval_resp = client.start_evaluation_run(
evaluationDatasetId=dataset_id,
datasetVersionId=version_id,
agentId="my-cs-agent",
agentAliasId="prod-v2", # 当前生产别名
evaluationConfig={
"outputFormat": "detailed", # 每条用例的逐项判定
},
)
run_id = eval_resp["runId"]
print(f"评估已启动: run={run_id}")
# 等待完成并拉取结果(简化示意,实际应轮询或用 EventBridge)
import time
time.sleep(30) # 等待评估完成——生产代码请用 waiter
result = client.get_evaluation_run_results(runId=run_id)
# 打印每条用例的通过情况
for case_result in result["testCaseResults"]:
tc_id = case_result["testCaseId"]
passed = case_result["overallPassed"]
details = case_result["criteriaResults"]
print(f" {tc_id}: {'✅ PASS' if passed else '❌ FAIL'}")
for d in details:
print(f" - {d['criterion']}: {d['passed']} | {d['reason']}")
4. 基准升级:加用例、发新版本、对比分数
# 业务新增了"会员积分查询"场景,追加用例
new_case = {
"testCaseId": "points-query",
"input": {
"userMessage": "我账户里还有多少积分可以用",
},
"expectedBehavior": {
"intent": "points_balance_query",
"mustMention": ["当前积分余额", "积分有效期"],
"mustNotMention": [],
"finalState": "points_displayed",
},
}
client.put_test_case(datasetId=dataset_id, testCase=new_case)
# 发布 v2 基准
v2_resp = client.create_dataset_version(
datasetId=dataset_id,
versionLabel="v2",
description="新增积分查询场景,共 4 条用例",
)
v2_version_id = v2_resp["versionId"]
# 用同一个 Agent 分别跑 v1 和 v2,对比分数差异
v1_run = client.start_evaluation_run(
evaluationDatasetId=dataset_id,
datasetVersionId=version_id, # v1
agentId="my-cs-agent",
agentAliasId="prod-v3", # 新版本 Agent
)
v2_run = client.start_evaluation_run(
evaluationDatasetId=dataset_id,
datasetVersionId=v2_version_id, # v2
agentId="my-cs-agent",
agentAliasId="prod-v3",
)
# 两个 run 完成后对比:
# v1 分数 vs v2 分数 → 基准扩大的影响
# v1 分数(prod-v2) vs v1 分数(prod-v3) → Agent 改动的真实效果
这套流程的关键在于:每次对比都明确标注了"用的是哪个基准版本"和"跑的是哪个 Agent 版本",四象限对比一目了然。
落地建议与取舍
| 决策点 | 建议 | 理由 |
|---|---|---|
| 基准版本多久发一次 | 业务场景有结构性变化时再发新版本,不要每周发 | 频繁发版本会让"基准漂移"重新出现,失去离线锚点意义 |
| 用例数量 | 起步 5-10 条覆盖核心路径,逐步扩展到边界场景 | 太少无法捕捉回归,太多维护成本飙升 |
| 预期行为怎么写 | 用 mustMention / mustNotMention / finalState 等结构化字段,不要写自然语言段落 |
结构化字段可以自动判定,自然语言需要人工审 |
| 线上信号怎么结合 | 线上异常对话自动回流到数据集草稿区,人工审核后再入基准 | 避免噪声用例直接污染基准 |
| 老版本保留多久 | 至少保留最近 3 个版本,关键里程碑版本永久保留 | 回溯历史对比时需要老基准 |
几个常见陷阱值得注意:
- 不要把线上 A/B 测试的结论直接等同于离线基准结论。线上流量分布偏移时,离线基准才是"控制变量"。
- 不要在基准版本里混入"只针对新模型"的用例。基准是衡量所有 Agent 版本的同一把尺子,用例应该描述业务需求,而不是描述某个模型的能力边界。
- 基准用例的预期行为要经过业务方评审。技术团队写预期容易偏向"模型能答出什么",业务方更关注"用户需要听到什么"。
把测试用例从散文件升级为版本化数据集,本质上是在给 Agent 的进化过程装一个里程表。没有里程表,你只知道车在跑;有了里程表,你才知道它到底跑了多远、方向对不对。Bedrock AgentCore 的数据集管理把这件事从手工操作变成了可自动化、可审计的工程流程——下一步,就该把"发新基准版本"和"发新 Agent 版本"放进同一个 CI 管线里了。