第 20 章 · 测试与评估:crewai test/train
本章目标:掌握
crewai test的运行机制与评分指标、crewai train的人工反馈训练流程,学会搭建回归评估集,并理解在 CI 中跑评估的取舍。
20.1 为什么 LLM 应用需要"评估"而不只是"测试"
传统单元测试断言确定性输出(assert add(1,2) == 3),但 crew 的产出是自然语言——每次运行都可能不同。因此 CrewAI 把质量保障分成三层:
| 层级 | 手段 | 回答的问题 |
|---|---|---|
| 单元测试 | pytest 测自定义工具、输入解析 | 函数逻辑对不对? |
| 运行冒烟 | crewai run / kickoff() 手工跑 | 流程能不能走通? |
| 评估 | crewai test / train | 产出质量是否达标且稳定? |
注意区分
crewai test 不是单元测试框架,它靠 LLM 当裁判给每个任务的产出打分(1–10 分),属于 LLM-as-judge 评估;工具函数的逻辑正确性仍要用第 8 章的方式写真正的单元测试。
20.2 crewai test:多轮运行与打分
在 crewai create crew 生成的工程目录里执行:
# 默认跑 2 轮,裁判模型 gpt-4o-mini
crewai test
# 指定轮数与裁判模型(短参数形式)
crewai test -n 5 -m gpt-4o两个参数:
--n-iterations(简写-n):重复运行的次数,默认 2。多轮的意义在于观察稳定性——单次 9 分可能是运气,5 次平均 9 分才是可靠;--model(简写-m):充当裁判的 OpenAI 模型,默认gpt-4o-mini(目前 test 功能仅支持 OpenAI 系裁判模型)。
旧写法
--n_iterations仍可用但已弃用并从--help隐藏,新代码一律用--n-iterations。
运行结束后会输出类似这样的评分表(分数越高越好):
Tasks Scores (1-10 Higher is better)
| Tasks/Crew/Agents | Run 1 | Run 2 | Avg. Total |
| Task 1 | 9.0 | 9.5 | 9.2 |
| Task 2 | 9.0 | 10.0 | 9.5 |
| Crew | 9.00 | 9.38 | 9.2 |
| Execution Time (s) | 126 | 145 | 135 |三个解读要点:任务分定位哪个环节拖后腿;Crew 总分看整体趋势;执行时间是成本信号——分数持平而时间暴涨时优先查工具调用次数。
也可以用编程方式触发同样的流程:
from my_project.crew import MyProjectCrew
inputs = {"topic": "AI Agent 框架对比"}
try:
# 与 CLI 等价的编程式评估入口
MyProjectCrew().crew().test(
n_iterations=3,
inputs=inputs,
openai_model_name="gpt-4o-mini",
)
except Exception as e:
raise Exception(f"评估执行失败: {e}")20.3 crewai train:用人工反馈校准 Agent
评估发现质量问题后,train 提供一条"人教机"路径:每轮运行中暂停让你对每个任务的产出给出反馈,然后让 Agent 改进,把「初版 → 反馈 → 改进版」记录下来沉淀为后续运行的强制指令。
# 训练 2 轮,结果保存到 trained_agents_data.pkl(默认名)
crewai train -n 2
# 指定保存文件(支持绝对路径)
crewai train -n 3 -f /data/models/marketing_crew.pkl训练时的数据流(官方文档描述):
- 进入训练模式:所有任务自动置
human_input=True、禁用委派; - 每轮迭代记录三件套:
initial_output(初版)、你的human_feedback、improved_output(改进版),写入工作文件training_data.pkl; - 全部轮次结束后,按 Agent 聚合出三样东西并存入
-f指定的文件:suggestions[]:从反馈中提炼的可执行改进指令;quality:0–10 的改进评分;final_summary:面向未来任务的操作清单。
关键机制在于闭环:之后的普通(非训练)运行里,Agent 会自动加载自己 role 名下的 suggestions 并追加为任务提示中的强制性指令——你不需要改任何 Agent 定义就能获得一致的提升:
import os
from crewai import Agent, Task, Crew, Process, LLM
llm = LLM(
model="openai/deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
# 假设已用 crewai train -f marketing.pkl 完成训练,
# 且各 Agent 的 role 与 pkl 中记录一致
writer = Agent(
role="Senior Writer", # role 必须与训练时一致才能命中 suggestions
goal="撰写高质量营销文案",
backstory="十年经验的 B2B 文案专家",
llm=llm,
)
task = Task(
description="为主题 {topic} 写一篇推广短文",
expected_output="300 字以内的中文文案",
agent=writer,
)
crew = Crew(agents=[writer], tasks=[task], process=Process.sequential)
print(crew.kickoff(inputs={"topic": "智能客服"}).raw)
# writer 的提示词中已自动注入 trained_agents_data.pkl 里沉淀的改进指令两个坑
① training_data.pkl 是临时过程文件,真正生效的是 trained_agents_data.pkl(或你 -f 指定的文件),别把它们混淆;② 训练过程依赖终端交互输入反馈,不能在无交互的 CI/容器里直接跑。
20.4 搭建自己的回归评估集
crewai test 的裁判模型打分有随机性,生产团队通常再补一层确定性断言的回归集。思路:准备一组固定输入 + 可程序化验证的期望特征,每次改动后重跑比对:
import os
import re
import pytest
from my_project.crew import MarketingCrew
# 回归评估集:输入 -> 结构化检查器(不比对全文,只断言可测特征)
EVAL_CASES = [
{"topic": "企业微信获客", "must_mention": ["企微"], "max_len": 500},
{"topic": "SaaS 定价页优化", "must_mention": ["定价"], "max_len": 400},
]
@pytest.mark.parametrize("case", EVAL_CASES)
def test_marketing_crew_regression(case):
"""营销 crew 回归评估:关键词覆盖 + 长度约束"""
result = MarketingCrew().crew().kickoff(inputs={"topic": case["topic"]})
text = result.raw
for kw in case["must_mention"]:
assert kw in text, f"产出缺少必要关键词: {kw}"
assert len(text) <= case["max_len"], f"产出超长: {len(text)} > {case['max_len']}"
# 产出必须是完整中文句子而非空转/报错信息
assert re.search(r"[。!?]", text), "产出疑似非正常文本"
def test_crew_cost_budget():
"""成本护栏:一次 kickoff 的 token 消耗不得超过预算"""
result = MarketingCrew().crew().kickoff(inputs={"topic": "邮件营销"})
usage = result.token_usage # total_tokens 属性汇总全 crew 用量
assert usage.total_tokens < 50_000, f"token 超预算: {usage.total_tokens}"设计原则:断言特征而不是全文(关键词、结构、长度、语言、JSON 可解析性);把 usage_metrics/token_usage 变成护栏,防止提示词改动悄悄推高成本。
20.5 CI 中跑评估的取舍
| 方案 | 优点 | 缺点 | 建议 |
|---|---|---|---|
| 只跑单元测试 | 快、便宜、稳定 | 不覆盖产出质量 | 每次 PR 必跑 |
| 单元测试 + 回归集(固定输入) | 能抓结构性退化 | 消耗真实 token,慢 | 合入主分支前跑 |
crewai test -n N(LLM 裁判) | 直接度量质量 | 更贵、裁判有噪声、需 OpenAI key | 夜间定时/发版前跑 |
落地建议:PR 流水线只做 lint + 工具单元测试; nightly job 跑回归集并在分数跌破阈值时告警;crewai test 作为发版前的“验收关卡”,人工查看评分表后再放行。裁判模型的噪声可以通过提高 -n 轮数取均值来抑制,但要预算好费用。
夜间告警脚本的最小实现:
# nightly_eval.py —— cron 每晚执行:跑回归集,分数跌破阈值则非零退出
import subprocess
import sys
THRESHOLD = 8.0
proc = subprocess.run(
["pytest", "tests/test_regression.py", "-q", "--tb=short"],
capture_output=True, text=True,
)
print(proc.stdout)
if proc.returncode != 0:
print("回归集未通过,阻断发版")
sys.exit(1)
# 可选:解析 crewai test 评分表并对比 THRESHOLD,
# 跌破时调用钉钉/Slack webhook 告警本章小结
crewai test -n 轮数 -m 裁判模型多轮运行并以 LLM 打分,输出任务/Crew 两级评分表和耗时;crewai train -n -f通过「初版→人工反馈→改进版」迭代沉淀suggestions到 pkl,之后普通运行自动注入提示词;- 生产级做法是自己搭回归评估集:固定输入 + 特征断言 + token 护栏;
- CI 分层:PR 只跑单元测试,nightly 跑回归集,发版前跑 LLM 裁判评估。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. crewai test 默认使用的裁判模型和轮数是?
2. 关于 crewai train 产生的两类 pkl 文件,说法正确的是?
3. 为什么 crewai train 不能直接放进无人值守的 CI 中执行?
4. 搭建回归评估集时,下列哪种断言方式最不可维护?
🛠️ 动手实践
- 为第 17 章创建的 CLI 项目执行
crewai test -n 3,记录每个任务的平均分与总耗时,找出得分最低的任务并分析原因。 - 按 20.4 节的模式为自己的项目写一个包含至少 3 个用例的回归评估文件(pytest 参数化 + 关键词/长度断言 + token 护栏)。
- 执行一次
crewai train -n 2 -f my_feedback.pkl,在训练中故意给出"太啰嗦,压缩到 200 字以内"这类具体反馈,训练完成后重新运行 crew,观察产出风格是否变化,并用pickle检查 pkl 里的suggestions内容。
下一章我们把 crew 跑起来的每一分钱、每一毫秒都看清楚:第 21 章 · 可观测性与监控集成。