通过 131 个测试覆盖 4 个维度(路由/总结/生成/格式化)验证 Agent 输出正确性,成本仅 $0.03/次。揭示了 AI Agent 难以通过传统测试发现「输出看着对但内容错」的问题。
Originally published on AIdeazz — cross-posted here with canonical link.
我交付了一个生产级 AI Agent,它有 17% 的概率在静默中失败。这个 Agent 的设计流程是:接收来自 Telegram 的用户请求,将其路由到专门的 Groq 驱动的摘要生成器,然后发送到 Claude 驱动的内容生成器,最后将输出格式化后发送到 WhatsApp。我的单元测试通过了,集成测试通过了,端到端测试(检查最终输出格式)也通过了。问题不在于它是否产生了输出,而在于它产生了什么样的输出。
这个 Agent 的核心任务是:接收一份文档 URL 和一个目标社交媒体平台(来自 Telegram),使用 Groq 对文档进行摘要(为了速度),然后使用 Claude 生成社交媒体帖子(为了细腻度和创意),最后将帖子发送到 WhatsApp。
静默失败发生在第 3 和第 4 步。一位用户上传了一篇研究论文的 PDF。Groq 摘要生成器在某些提示词变体下,会关注"致谢"部分或"参考文献"部分,而不是"摘要"和"方法论"。接收到这份偏差摘要的 Claude 随后会生成一篇关于作者资金来源或相关工作的社交媒体帖子,而不是论文的研究发现。最终的 WhatsApp 消息在结构上看起来是正确的,但内容从根本上就是错的。
这是一类传统软件测试无法捕获的失败。单元测试验证代码逻辑,集成测试验证组件通信,端到端测试验证系统流程和最终输出格式。它们都无法验证 AI Agent 的语义正确性或推理忠实度。这正是需要一个专门的 AI Agent 评估框架的原因。
我的评估框架的第一层关注的是 Agent 处理多样化和挑战性输入的能力。我的 Agent 在 Telegram 和 WhatsApp 上运行,这意味着输入通常是非结构化的、拼写错误的或不完整的。
我构建了 35 个测试用例,覆盖:
Malformed URLs: htps://example.com, example.com, ftp://example.com/doc.pdf。预期:优雅地报错,提示用户提供正确的 URL。
Non-existent URLs: https://nonexistent-domain-12345.com/doc.pdf。预期:网络错误处理,通知用户。
Unsupported document types: https://example.com/image.jpg, https://example.com/video.mp4。预期:"不支持的格式"消息。
Empty messages: 用户什么都没发送。预期:提示输入。
Gibberish: "asdfghjkl;"。预期:"我无法理解"消息。
Language variations: 用西班牙语、法语发送请求(我的 Agent 目前只支持英语)。预期:"仅支持英语"消息。
Long inputs: 直接粘贴一段 10,000 词的文本到 Telegram。预期:截断或"内容过长"消息。
每个测试用例都是一个 JSON 对象,定义输入、预期输出(正则或特定字符串)和通过/失败条件。框架模拟一条 Telegram 消息,将其注入 Agent 的入口点,并捕获最终响应。仅这一层就捕获了 8 个关键失败,涉及 URL 解析和内容获取,这是之前的测试完全没有发现的。例如,像 example.com 这样格式错误的 URL 之前会导致文档获取服务崩溃,造成用户面前的静默失败。现在,它会触发一条特定的"无效 URL"响应。
这是最关键的一层,旨在捕获我之前描述的语义失败。它专注于 Agent 理解和执行复杂指令的能力,特别是在多次 LLM 调用中。
我开发了 50 个测试用例,每个用例包含:
A specific document:(例如,一篇研究论文、一篇新闻文章、一份产品手册)。
A specific prompt/instruction:(例如,"用简单的话为 10 岁孩子总结这个","为投资者提取关键要点","生成一条突出挑战的 LinkedIn 帖子")。
Evaluation criteria:这些不是简单的字符串匹配,而是以下几种方式的组合:
Keyword presence/absence: "必须包含 'AI' 和 'Panama',必须不包含 'Acknowledgements'"。
Semantic similarity: 使用嵌入模型(例如,通过 Oracle OCI Generative AI 服务的 text-embedding-ada-002)将生成的输出与人类撰写的"黄金标准"答案进行比较。余弦相似度低于 0.7 触发失败。
Factuality check: 对于特定的事实提取任务,我使用一个小的微调 LLM(在 Oracle Cloud Infrastructure GPU 实例上运行)作为"评审",将生成的事实与源文档进行对比。这个评审模型的提示词是:"Given the document X, is statement Y true? Answer only 'Yes' or 'No'."
Constraint adherence: "输出必须控制在 280 字符以内","必须使用项目符号"。
以下是一个具体例子:
Document: 一份关于"量子计算进展"的 5 页 PDF。
Prompt: "Generate a tweet summarizing the main breakthrough for a general audience. Keep it under 280 characters. Focus on the practical implications."
Evaluation:
Length check: < 280 chars.
Keyword check: contains "quantum", contains "breakthrough", NOT contains "Schrödinger equation".
Semantic similarity: Compare generated tweet embedding to a human-written tweet embedding (cosine similarity > 0.75).
Factuality: Critic model checks if the "practical implications" mentioned are actually present in the document.
这一层揭示了虽然我的 Groq 摘要生成器速度很快,但有时会过度简化或遗漏关键上下文,导致 Claude 生成误导性的帖子。我调整了 Groq 的系统提示词,明确强调"主要发现及其意义",并添加了一个后处理步骤,根据摘要句子与初始用户查询的嵌入相似度重新排序。
我的 Agent 设计用于对话式接口。这意味着它们需要维护上下文,并在多轮交互中做出适当的响应。
测试场景包括:
Follow-up questions: 用户在摘要之后问"那 X 呢?"预期:Agent 引用之前的摘要并回答 X(如果相关)。
Clarification requests: 如果上下文模糊,Agent 询问"您指的是哪个文档?"
Context switching: 用户先请求对文档 A 进行摘要,然后立即请求为文档 B 生成社交媒体帖子。预期:Agent 正确切换上下文并处理文档 B。
Interrupted flows: 用户开始一个任务,然后发送"stop"或"cancel"。预期:Agent 优雅地终止当前任务。
Error recovery: Agent 遇到错误(例如 API 超时),然后用户重试。预期:Agent 尝试重新启动或引导用户。
这些测试模拟一系列消息,在每一步检查 Agent 的状态。例如,在用户提供文档 URL 后,框架检查 Agent 的内部状态是否正确存储了这个 URL,以便为下一条消息做准备。这一层帮助我优化了状态管理逻辑,从简单的字典迁移到更健壮的、由 Oracle Autonomous Database 支持的基于会话的上下文存储。
虽然与正确性没有直接关系,但性能和成本对生产系统至关重要,尤其是使用 LLM API 时。
测试维度包括:
Latency: 特定操作花费的时间(例如摘要、生成)。我设定了阈值(例如,Groq 摘要 < 2 秒,Claude 生成 < 10 秒)。
Token usage: 每次 LLM 调用的输入/输出 token 数量。这直接影响成本。我监控异常的 token 消耗高峰。
API call count: 每次请求的外部 API 调用次数。意外增加可能表明存在循环或低效的提示词。
Error rates: 返回非 200 状态码的 API 调用百分比。
Total cost per run: 框架汇总 token 使用量和 API 调用次数,然后根据当前提供商费率计算预估成本(Groq: $0.0002/1K tokens,Claude 3 Sonnet: $3/M 输入 tokens,$15/M 输出 tokens)。我的目标是每次完整 Agent 交互 $0.03。
框架在 Oracle Cloud Infrastructure 的始终免费层计算实例上每晚运行这 131 个测试。完整一次运行的總成本(包括 LLM 调用)始终保持在 $0.03 左右。这个低成本使我能够频繁运行,快速捕获回归。性能测试揭示了某些针对 Claude 的复杂提示词将生成时间推到了可接受限度之外,促使我优化提示词结构,并探索为特定任务使用更小的 Claude 模型。
构建这个 AI Agent 评估框架花了我两周时间。这两周我没有在构建新功能,没有在获取新用户。但没有它,我会继续交付一个在相当一部分使用场景中根本性损坏的产品。调试生产问题、失去用户信任和重建功能的成本会远远更高。
我的 131 个测试覆盖四层,提供了一个传统软件测试无法提供的安全网。它们确保我的多 Agent 系统不仅在技术上正常运行,而且在推理上正确、遵守指令、并提供有价值、准确的输出。对于任何进入生产的 AI Agent 来说,这是不可妥协的。
Q: How do you manage the "gold standard" answers for semantic similarity tests?
A: 对于关键路径,我手动为每个测试用例创建 2-3 个"黄金标准"答案。对于不太关键的路径,我使用更强的 LLM(如 Claude 3 Opus)生成参考答案,然后手动审查和批准。
Q: What if the LLM models change or update, invalidating my semantic similarity scores?
A: 这是一个真实存在的风险。我尽可能锁定特定模型版本(例如 claude-3-sonnet-20240229)。当模型更新时,我会重新运行整个评估框架。如果大量语义相似度测试失败,就表明存在模型漂移,我要么调整黄金标准,要么微调我的提示词。
Q: How do you handle the cost of running 131 tests, especially with expensive LLMs?
A: 我通过在框架本身中尽可能使用更便宜、更快的模型(如 Groq)进行摘要和初始路由来优化成本。对于实际的生成测试,我使用生产模型,但保持输入文档和期望输出简洁,以最小化 token 使用量。$0.03/次是一个计算出的平均值,我密切监控它。
Q: What's your strategy for evaluating agents that generate creative content, where there's no single "correct" answer?
A: 对于创意内容,我更依赖约束遵守(例如语气、风格、长度)和负面约束(例如"不得冒犯"、"不得虚构事实")。语义相似度用于检查与提示词的相关性,而不是精确措辞。对创意输出的子集进行人工审查也是必不可少的。
Q: How do you integrate this harness into your CI/CD pipeline on Oracle Cloud?
A: 框架是一个独立的 Python 应用。我使用 Oracle Cloud Infrastructure (OCI) DevOps 来触发每晚运行。结果(通过/失败、延迟、成本)存储在 OCI Object Storage 桶中,并通过 OCI Logging Analytics 仪表板可视化。关键失败会通过 OCI Notifications 触发通知。
— Elena Revicheva · AIdeazz · Portfolio