别只看回答像不像:用 Strands Evals 与 AgentCore 评测 Agent Skills

2026-09-23 35 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

Skills 把领域流程、工具使用规范和操作步骤封装成可复用指令,让 Agent 能处理退款审核、故障排查、合规检查等专门任务。但一个回答读起来流畅,并不代表 Agent 选对了 Skill,更不代表它遵循了 Skill 中规定的步骤。

因此,评测 Skill 型 Agent 时,不能只给最终答案打分。更可靠的做法是同时观察 Skill 选择、指令遵循和任务结果,再分别用 Strands Evals 做开发期回归,用 Amazon Bedrock AgentCore Evaluations 承接集成或运行环境中的评测。

把一次 Agent 执行拆成三层证据

一条测试用例至少应回答三个问题:

  1. 选对了吗?
  2. 用户要求退款,Agent 是否选择了 refund_review,而不是通用客服 Skill?
  3. 遇到信息不足时,它是否选择了澄清流程,而不是强行执行?

  4. 照着做了吗?

  5. Skill 要求先校验订单、再检查退款期限,Agent 是否按顺序执行?
  6. Skill 明确要求不得猜测客户身份,Agent 是否请求补充信息?
  7. 某个工具失败后,Agent 是否执行了规定的降级或终止步骤?

  8. 结果可用吗?

  9. 最终结论是否正确?
  10. 回答是否包含必要字段、引用或风险提示?
  11. 是否泄露敏感信息,或者执行了超出权限的操作?

这三层不能互相替代。例如,Agent 可能误选 Skill,却凭语言模型常识碰巧答对;也可能选对 Skill,但跳过身份校验。两种情况都不应被“答案看起来不错”掩盖。

建议为每次运行保留结构化轨迹,而不是只保存聊天文本:

{
  "case_id": "refund-within-window",
  "events": [
    {"type": "skill_selected", "skill": "refund_review"},
    {"type": "instruction_completed", "name": "verify_order"},
    {"type": "instruction_completed", "name": "check_refund_window"}
  ],
  "answer": "订单已验证,并且仍在退款期限内,可以进入退款流程。"
}

这里的事件名是一种可实践的观测约定,不代表 Strands 或 AgentCore 的固定字段。实际接入时,应把框架产生的 trace、hook 或回调记录映射为类似的统一格式。

一个可以直接运行的本地评测器

下面的最小示例只使用 Python 标准库。它演示如何分别计算 Skill 选择、步骤遵循和最终答案三个分数。你可以先用它确定评测数据结构,再将同样的断言迁移到 Strands Evals 或 AgentCore Evaluations。

将以下内容保存为 evaluate_skills.py

from dataclasses import dataclass
from typing import List, Dict, Any


@dataclass
class EvalCase:
    case_id: str
    expected_skill: str
    required_steps: List[str]
    answer_keywords: List[str]


CASES = [
    EvalCase(
        case_id="refund-within-window",
        expected_skill="refund_review",
        required_steps=["verify_order", "check_refund_window"],
        answer_keywords=["退款期限", "可以"],
    ),
    EvalCase(
        case_id="missing-order-id",
        expected_skill="refund_review",
        required_steps=["request_order_id"],
        answer_keywords=["订单号"],
    ),
]

# 示例中的运行记录。生产环境应替换为 Agent 的真实 trace 和回答。
RUNS: Dict[str, Dict[str, Any]] = {
    "refund-within-window": {
        "events": [
            {"type": "skill_selected", "skill": "refund_review"},
            {"type": "instruction_completed", "name": "verify_order"},
            {"type": "instruction_completed", "name": "check_refund_window"},
        ],
        "answer": "订单已验证,仍在退款期限内,可以进入退款流程。",
    },
    "missing-order-id": {
        "events": [
            {"type": "skill_selected", "skill": "refund_review"},
            {"type": "instruction_completed", "name": "request_order_id"},
        ],
        "answer": "请提供订单号,以便继续审核。",
    },
}


def evaluate(case: EvalCase, run: Dict[str, Any]) -> Dict[str, Any]:
    events = run.get("events", [])
    selected = [
        event.get("skill")
        for event in events
        if event.get("type") == "skill_selected"
    ]
    completed = {
        event.get("name")
        for event in events
        if event.get("type") == "instruction_completed"
    }

    selection_score = float(case.expected_skill in selected)
    missing_steps = [s for s in case.required_steps if s not in completed]
    adherence_score = (
        1.0
        if not case.required_steps
        else (len(case.required_steps) - len(missing_steps)) / len(case.required_steps)
    )

    answer = run.get("answer", "")
    missing_keywords = [k for k in case.answer_keywords if k not in answer]
    answer_score = (
        1.0
        if not case.answer_keywords
        else (len(case.answer_keywords) - len(missing_keywords))
        / len(case.answer_keywords)
    )

    # Skill 选错通常比措辞不佳更严重,因此给予更高权重。
    total = 0.45 * selection_score + 0.35 * adherence_score + 0.20 * answer_score

    return {
        "case_id": case.case_id,
        "selection_score": round(selection_score, 2),
        "adherence_score": round(adherence_score, 2),
        "answer_score": round(answer_score, 2),
        "total_score": round(total, 2),
        "missing_steps": missing_steps,
        "missing_keywords": missing_keywords,
        "passed": total >= 0.85 and selection_score == 1.0,
    }


if __name__ == "__main__":
    failed = False
    for case in CASES:
        result = evaluate(case, RUNS[case.case_id])
        print(result)
        failed = failed or not result["passed"]

    raise SystemExit(1 if failed else 0)

运行:

python evaluate_skills.py

这个脚本还适合直接放进 CI:只要有用例失败,进程就返回非零退出码。

python evaluate_skills.py
if [ $? -ne 0 ]; then
  echo "Skill evaluation failed"
  exit 1
fi

真实项目中不要只依赖关键词。关键词适合验证格式和必要信息,但无法可靠判断语义正确性。更稳妥的组合是:

  • 对 Skill ID、工具名、步骤顺序使用确定性断言;
  • 对答案质量使用人工标注、规则或模型评审;
  • 对安全边界使用明确的禁止事件和失败条件;
  • 对延迟、token 和工具调用次数单独设置预算。

Strands Evals 与 AgentCore Evaluations 如何分工

可以把两者放在同一条评测流水线的不同位置,而不是让它们互相替代。

开发和提交阶段,使用 Strands Evals 组织固定数据集与回归断言。每当 Skill 文本、路由规则、模型版本或工具定义变化时,重新运行用例,及时发现:

  • Skill 路由准确率下降;
  • 必需步骤被遗漏;
  • 步骤顺序发生变化;
  • 正常请求被不必要地拒绝;
  • 对抗性输入绕过了 Skill 的限制。

集成和运行阶段,可以用 Amazon Bedrock AgentCore Evaluations 对接 Agent 执行环境中的样本与轨迹,关注更接近真实流量的表现,例如不同输入分布下的成功率、指令遵循率以及版本间差异。

由于来源摘要没有给出具体 SDK 方法和配置字段,下面是一份用于设计评测集的示意 YAML,而不是某个版本可直接提交的官方配置。接入时需要按当前 Strands Evals 或 AgentCore Evaluations 的接口做字段映射:

suite: refund-skill-regression
metrics:
  - skill_selection
  - instruction_adherence
  - answer_quality

cases:
  - id: refund-within-window
    input: "订单 A-100 已签收 3 天,我想退款。"
    expected_skill: refund_review
    required_steps:
      - verify_order
      - check_refund_window
    forbidden_events:
      - issue_refund_before_verification

  - id: missing-order-id
    input: "帮我退款,但我没有提供订单号。"
    expected_skill: refund_review
    required_steps:
      - request_order_id
    forbidden_events:
      - fabricate_order_id
      - issue_refund

关键不是配置文件长什么样,而是让本地测试、托管评测和线上观测共享同一套语义:相同的 Skill 名称、步骤名称、失败原因和版本标识。否则,同一个问题在三套系统中会得到三种无法对齐的统计结果。

上线前应守住的几条线

评测集不要只收集“标准答案题”。至少加入以下样本:

  • 两个 Skill 都可能匹配的模糊请求;
  • 应该拒绝或转人工的越权请求;
  • 缺少关键参数的请求;
  • 工具超时、返回空值或部分失败的情况;
  • 用户试图覆盖 Skill 指令的提示注入;
  • 换一种说法但意图不变的同义输入。

发布门槛也不宜只看平均总分。一个平均 95 分的版本,仍可能在高风险场景中连续跳过身份校验。更合理的门槛包括:

  • 高风险用例的 Skill 选择必须全部正确;
  • 禁止事件出现次数必须为零;
  • 必需步骤遵循率不得低于设定阈值;
  • 新版本不能显著差于当前生产基线;
  • 失败记录必须能够回溯到 Skill、模型、提示词和工具版本。

Skill 让 Agent 的行为更容易复用,也让评测从“答案是否顺眼”推进到“过程是否符合制度”。只有把选择、执行和结果拆开测量,团队才能知道问题究竟出在路由、Skill 指令、模型推理,还是外部工具上,并据此做出可验证的改进。


相关推荐