主题
第 8 章 · eval 驱动开发
pixiu 锚点:
pixiu/eval/(runner + cases + 测试)、docs/lab/E1(EDD 闭环)、docs/lab/E4(eval 卫生) 关键案例:W2-5(画像 20%→100%)、W3-3E/W3-5E(自主决策 100% pass)
开篇:先写 eval,再写功能
测试驱动开发(TDD)你大概听过:先写测试,再写代码。eval 驱动开发(EDD)是它在 agent 时代的版本:在写任何 agent 功能之前,先写好评测用例。
完整闭环是:先写 eval 定义成功标准 → 开发直到 eval 通过 → eval 沉淀为回归基线。
为什么 agent 特别需要这个?因为 agent 的"成功"是模糊的——不像 add(1,2) 有确定答案。如果不先写 eval 把"成功"钉死,你开发时会陷入"感觉好像好一点了"的主观泥潭,永远不知道算不算完成。eval 把主观的"好"变成客观的"通过率"。
pixiu 在 pixiu/eval/ 里建了完整的 eval 基础设施(这是它从零建的,课题 E1),这一章讲它怎么用、以及踩过的坑。
EDD 在金融叙事里"分维度"
我在 lab 里专门验证了 EDD 在 pixiu 的适用性(课题 E1)。结论很有意思:EDD 不是"适用或不适用",而是分维度适用。
pixiu 的 signal 场景有两层输出:
- 结构化维度——SignalContext 的字段(scene_type、signal、action_level、投票一致性)。这些是确定性的,能用代码断言判。EDD 在这一层直接有效:我注入两种退化(路由错成 general、非法 signal 值 MAYBE),eval 都准确报红。这部分比传统 TDD 还直接。
- 叙事维度——LLM 把结构化数据"翻译"成人话。这一层没法用代码断言,只能用 LLM 裁判(第 9 章)。它引入了结构化场景没有的负担:真实 LLM 调用(要 key、要网络、要花钱)+ 裁判的概率性(第 7 章)+ 输入 mock 成本。
这个分维度发现修正了一个认知:很多人把 EDD 当成一个统一可行的方法,但一旦输出涉及 LLM 叙事,EDD 就裂成了两半——结构化那半照搬 TDD,叙事那半需要一整套新支撑(裁判、统计、工程卫生)。
eval 能抓退化(这是它最值钱的地方)
eval 写出来不是摆设,它最值钱的能力是抓退化。
pixiu 的 eval 用例里,我故意注入错误,看 eval 会不会报红:
- 注入①:把场景路由从正确的
signal改成general→ eval 报红 - 注入②:把 signal 值改成非法的
MAYBE→ eval 报红
两次退化都被 eval 抓住。这意味着以后任何人改代码不小心搞坏了 signal 逻辑,eval 会立刻报红——它成了你的回归基线。
eval 不是写完就扔的验收单,是钉在地上的回归网。 每次改动后跑一遍,退化无所遁形。
eval 驱动修复:量化闭环
更有说服力的是,eval 不只是事后检验,还能驱动你发现并修复问题。pixiu 有几个完整的量化闭环:
W2-5(画像注入):我用 eval 测画像相关问答,发现 pass_rate 只有 20%。用 eval 定位到是 context 缺口(第 5 章详述),修复后 pass_rate 拉到 100%。
W3-3E(自主多策略决策):测 agent 能否自主用 compare_strategies 回答"海康适合什么策略",结果 100% pass(3/3,mean 0.97)——验证了第 3 章的工具粒度设计成立。
W3-5E(自主参数扫描):测 agent 能否自主扫参数回答"布林带 window 怎么选",同样 100% pass(3/3,mean 0.97)。
注意这些 eval 的共同点:它们测的不是"功能能不能跑",而是"agent 在自然提问下能不能自主做对决策"。 这种 eval 才是 agent 时代真正有价值的——它测的是 agent 的行为,不是代码的执行。
E4:eval 本身的工程卫生
eval 会抓 bug,但 eval 自己也会引入 bug。课题 E4 专门讲这个——eval 工程卫生。
一个经典坑(旧手记提过):eval 的"队列污染"——上个用例的残留状态污染了下个用例,单跑能过、连跑就挂。土办法是 sleep(1s) 等它消化。
pixiu 的 E4 把这个坑挖深了一层。我 grep 了 pixiu 全代码,发现 ~20 处模块级可变状态,分成 4 类:
| 类 | 例子 | eval 风险 |
|---|---|---|
| 单例缓存 | _config、_agent、_scheduler | 首次创建后缓存,eval 改不了 |
| 数据缓存 | _trading_dates、_download_cooldowns | 首次计算后缓存,跨用例残留 |
| 运行时字典 | _agent_running、_event_subscribers | 跨用例残留 |
| 标志位 | _setup_done、_dotenv_loaded | 一次性初始化,二次跑行为变 |
这 4 类就是比数据库更隐蔽的"队列"。 旧手记的"队列污染"只指了一个具体的消息队列;pixiu 的实证显示,真实项目里这种"隐藏队列"是一类——分散在各处、极易在 eval reset 时漏掉。
E4 给出的修正:
- 用 grep 系统识别(
global语句 + 顶层赋值)所有模块级可变状态,不能只列显式资源。 - 分类——不同类 reset 方式不同。
- sleep 是治标——正确做法是 reset 状态本身,不是等它消化。
- mock 隔离是合法绕过——pixiu 当前的 mock eval 绕过了这些全局状态,代价是不测真实路径。这是个有意识的取舍。
诚实地说,E4 的假设可证伪点触发了——pixiu 当前的 mock eval 绕开了这些全局状态,所以当前无污染。但 E4 没白做:它把这 20 处"隐藏队列"全 mapped 出来了,等 eval 扩展到真实路径时,这就是 reset checklist。这种"假设被打脸但价值不减"的诚实处理,本身就是工程纪律。
eval 框架本身也要测
旧手记说"eval 框架本身也需要测试"。pixiu 照做了——tests/test_eval_runner.py 测 eval 框架自己:正向用例、抓退化、裁判 mock、端到端加载。你的尺子准不准,得先校准尺子本身。
一个元方法:可证伪假设
讲完具体技术,我想提一个 pixiu lab 体系里我觉得最值钱的元方法。
你看 E1-E4 这几个课题,它们都遵循同一个套路:
- 手记原命题——引用旧手记的某个观点
- 可证伪假设——"我赌 X 成立,如果实验显示 Y 则假设被打脸"
- 实验设计——载体、变量、度量
- 数据与观察——只记事实
- 结论——证实 / 部分证实 / 证伪,三选一,不许"差不多"
- 对手记的修正——修正版命题 + 理由
这不是写论文,这是把"agent 工程的经验之谈"变成可证伪、可复现的实验。pixiu 的 lab 就是用这套方法,把旧手记里那些"我觉得"的观点,一个个拿出来检验、修正、落地。
这才是 eval 驱动开发的最高形态——不只是测代码,是用 eval 检验你的方法论本身。
这一章的工具:EDD 自检
- [ ] 你写 agent 功能前,先写 eval 了吗?还是写完才想到测?
- [ ] 你的 eval 能抓退化吗?(注入一个错误,看它报不报红)
- [ ] 你的 eval 测的是"代码能跑"还是"agent 行为做对"?
- [ ] 你的 eval 有没有"隐藏队列"污染?(grep 你的全局可变状态)
- [ ] 你的 eval 框架本身有测试吗?
小结
eval 驱动开发,是把 agent 从"感觉能用"推向"可靠可用"的关键一跃。
pixiu 的经验是:EDD 在结构化维度直接有效,在叙事维度需要统计(第 7 章)和裁判(第 9 章)支撑;eval 最值钱的能力是抓退化;而 eval 自己也要讲工程卫生——那些隐藏的全局状态,是比数据库更刁钻的污染源。
下一章,我们收一下 eval 的最后一个话题——打分的边界:什么时候该信 LLM 裁判,什么时候不该。
下一章
第 9 章 · 打分的边界 —— 一张"指标类型 → 打分手段"的决策表。