Workflow Evals with Mocked Tools
Nikola Balic (@nibzard)· emerging
问题
单元测试、代码检查器和类型检查器可验证单个组件,但无法对Agent工作流进行端到端测试。即便所有底层组件均无问题,也很容易编写出生效不佳的prompt。
你需要验证prompt与工具作为一个系统能否协同高效运转。
方案
实现工作流评估(模拟):使用模拟工具测试完整的Agent工作流
核心组件(借鉴Sierra方案):
1. 双工具实现
每个工具都提供「真实版」和「模拟版」两种实现
# 真实实现——调用真实API
def search_knowledge_base_true(query: str) -> str:
return kb_api.search(query)
# 模拟实现——返回静态/测试数据
def search_knowledge_base_mock(query: str) -> str:
return TEST_KB_RESULTS.get(query, DEFAULT_RESULT)
2. 模拟配置
每个评估需定义以下内容:
- 初始Prompt:Agent接收的初始指令
- 元数据:评估可用的场景context
- 评估标准:成功/失败的判定规则
evals:
- name: slack_reaction_jira_workflow
initial_prompt: "给这条Slack消息里的JIRA工单添加一个笑脸反应"
metadata:
situation: "包含JIRA链接的Slack消息"
expected_tools:
- slack_get_message
- jira_get_ticket
- slack_add_reaction
evaluation_criteria:
objective:
- tools_called: ["slack_get_message", "jira_get_ticket", "slack_add_reaction"]
- tools_not_called: ["slack_send_message"]
subjective:
- agent_judge: "回复有用且准确"
3. 双重评估标准
客观标准:
- 实际调用的工具列表
- 未调用的工具列表
- 对话中添加的标签/状态(若适用)
主观标准:
- Agent作为判定者的评估(例如:“回复是否友好?”)
- LLM对定性结果的评估
4. CI/CD集成
每次提交拉取请求(PR)时自动运行评估
# GitHub Actions 工作流
触发条件:pull_request
步骤:
- 执行:python scripts/run_agent_evals.py
# 将评估结果作为PR评论发布
# 评估执行流程
1. 加载评估配置
2. 将所有工具替换为模拟实现
3. 结合初始Prompt与元数据运行Agent
4. 跟踪Agent调用的工具
5. 对照客观标准(工具使用情况)完成评估
6. 启动Agent判定环节处理主观标准
7. 生成包含详情的通过/失败报告
如何使用
最佳适用场景:
- 工具存在副作用的Agent工作流(如API、数据库)
- 需验证工作流的CI/CD流水线
- Prompt工程与优化
- Agent行为变更的回归测试
实现方案:
1. 为工具创建模拟层:
class MockToolRegistry:
def __init__(self, mode: str = "mock"):
self.mode = mode
def get_tool(self, tool_name: str):
if self.mode == "mock":
return self.mocks[tool_name]
return self.real_tools[tool_name]
# 注册模拟实现
mocks = {
"slack_send_message": mock_slack_send_message,
"jira_create_ticket": mock_jira_create_ticket,
# ...
}
2. 定义评估用例:
evals = [
{
"name": "login_support_flow",
"prompt": "用户无法登录,请提供帮助",
"expected_tools": ["user_lookup", "password_reset"],
"forbidden_tools": ["account_delete"],
"subjective_criteria": "回复共情且有帮助"
},
# ... 更多评估用例
]
3. 运行并评估:
def run_eval(eval_config):
# 使用模拟工具运行Agent
result = agent.run(
prompt=eval_config["prompt"],
tools=mock_registry
)
# 检查客观标准
tools_called = result.tools_used
passed = all(t in tools_called for t in eval_config["expected_tools"])
passed &= all(t not in tools_called for t in eval_config["forbidden_tools"])
# 检查主观标准
if passed:
judge_prompt = f"""
请评估以下Agent回复:{result.response}
评估标准:{eval_config['subjective_criteria']}
是否通过?
"""
passed = llm_evaluator(judge_prompt) == "PASS"
return {"passed": passed, "details": result}
4. 与CI/CD集成:
# .github/workflows/agent_evals.yml
name: Agent 评估
on: pull_request
jobs:
evals:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: python scripts/run_evals.py --format github
- uses: actions/github-script@v6
with:
script: |
const results = require('./eval_results.json');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: formatResults(results)
});
处理非确定性问题: 文章指出,受非确定性影响,评估效果"远未达到预期":
- 强信号:所有用例全部通过或全部失败
- 弱信号:结果好坏参半
- 缓解方案:重试失败的评估(例如:"三次尝试中至少通过一次")
权衡
优点
- 端到端验证:将prompt与工具作为一个完整系统协同测试
- 快速反馈:在回归问题进入生产环境前提前发现
- 安全测试:通过模拟(Mocked)工具避免测试过程中的副作用
- 明确评估标准:涵盖客观(工具调用)与主观(质量)两类衡量维度
- CI/CD集成:在每一个PR上自动执行验证
缺点
- 非确定性:LLM的输出可变性导致不稳定测试(Flaky Tests)频发
- 模拟维护成本:需持续保持模拟工具与真实工具的行为同步
- Prompt驱动的脆弱性:相比代码驱动的工作流,依赖prompt的工作流稳定性更差
- 不适合作为阻塞性校验:因结果可变性,难以作为CI门控使用
- 调优开销:需持续调整prompt与模拟响应
- 信号有限:通过/失败混合的结果仅能提供模糊的指导意见
运营挑战
“目前这套方案运作尚可,但远未达到预期……当所有测试全失败或全通过时,信号非常明确,但大多数测试结果处于中间模糊地带。” “我们依赖prompt驱动而非代码驱动的工作流,这引入了大量非确定性问题,除非对prompt和模拟进行调优,否则我找不到解决办法。”
改进策略
- 重试逻辑:采用“三次尝试中至少通过一次”的机制降低测试不稳定性
- 优化Prompt:让评估用prompt更精准、更具确定性
- 优化模拟(Mocks):提升模拟响应的真实度
- 用代码替代Prompt:将复杂工作流从prompt驱动迁移至代码驱动
- 指导性校验而非阻塞性校验:用于上下文参考,而非作为CI门控
参考文献
关键词:
围绕智能代理的验证与优化展开,涵盖内部代理工作流的评估验证方法、塞拉平台的模拟测试方案,以及执行后测试的停止钩子自动续行模式、基于工作流的智能代理强化微调技术。
直译:
- 构建内部代理:用于验证工作流的评估方法 - 威尔·拉尔森(2025)
- 塞拉平台:面向代理测试的模拟方法
- 相关内容:停止钩子自动续行模式 - 执行后测试
- 相关内容:智能代理强化微调 - 基于代理工作流的训练
来源摘要
正在获取来源并生成中文摘要…