对应 Maps to:Stanford CS329Z Engineering AI Agents(Fall 2026),Lecture 10 · Wed Oct 28 · What Data Do Agents Need?(原课 10/26 的客座讲座本讲义不覆盖)。阅读材料:Shankar, Data Flywheels for LLM Applications(2024)/ Tan et al., Large Language Models for Data Annotation and Synthesis: A Survey(EMNLP 2024)。 依据说明:写作时(2026-10-16)这一讲的 slides 还没有公布。本讲依据课表上的主题描述(轨迹、示范与反馈;用于优化的数据 vs 用于评估的数据;数据飞轮;合成数据生成;从人与智能体的交互中收集数据)和两篇阅读材料来写。等 slides 公布后,侧重点可能和这里不同。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture10/(只用 Python 标准库;5 个脚本 + 4 个模块 + 1 个可选脚本;不需要 API key;最慢的脚本约 20 秒) 预计阅读时间:3 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲你应该能够:
- 列出智能体系统里几种数据——轨迹、示范、偏好对、反馈信号、评估用例——各从哪来、用来干什么、用之前要先确认什么。
- 分析一个上线系统的反馈信号:显式和隐式信号各覆盖哪些失败、漏掉哪些失败;为什么”用户没抱怨”不等于成功;为什么”找问题”和”估计水平”要用两种抽样。
- 设计一个数据飞轮(评估 → 监控 → 持续改进):挑哪些轨迹去标注、错误分析怎样驱动修复、评估集和判分标准怎样跟上变化(标准漂移)。
- 用 LLM 生成和标注数据时,知道它常见的偏差(重复、过于干净、分布不对、漏报),会去重、会比较合成数据和真实数据的分布,会把人放在 LLM 最可能错的地方。
- 说清为什么用于优化的数据必须和用于评估的数据分开,识别四种常见的污染(在测试集上优化、反复看评估集、随机切分遇上漂移、近似重复泄漏)。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| 第 9 讲:提示词优化、蒸馏、DPO 各需要什么数据;赢家诅咒 | 从数据里挑最有信息量的部分、标注一致性、IRT(第 11 讲) |
| 第 5 讲 §5.4:追踪(trace)与日志;第 7 讲:记忆投毒、来源标记 | 评估用例的 4 元组和 benchmark 的有效性(第 12 讲) |
| 第 8 讲 §6:失败分类、Cohen’s κ、LLM 评审 | LLM 评审的偏差与校准(第 13 讲) |
0.3 本讲地图 Map
§0.6 开场:上线之后,数据才真正开始
§1 智能体需要什么数据:轨迹、示范、偏好对、反馈、评估用例 ← 代码 01
§2 从人和智能体的交互中收集数据:显式与隐式信号、看不见的失败、谁在说话 ← 代码 02
§3 数据飞轮:评估 → 监控 → 持续改进;评估集会过时;标准漂移 ← 代码 03
§4 合成数据与 LLM 标注:生成、去重、分布差异、LLM 当标注者 ← 代码 04
§5 用于优化的数据 vs 用于评估的数据:四种污染 ← 代码 05
§6 用真实模型试一试 ← 代码 06(可选)
§7 一份数据计划
§8 小结 §9 常见误区 §10 练习 §11 延伸阅读 §12 下一讲预告
0.4 术语 Terms
| 术语 | 本讲里的意思 |
|---|---|
| 轨迹(trace / trajectory) | 一次运行的完整记录:输入、每一步的思考 / 工具调用 / 观察、输出、元数据 |
| 示范(demonstration) | “在这个上下文里应该做这个动作”的例子;可以做 SFT 数据或少样本示范 |
| 偏好对(preference pair) | 同一个输入的两个输出,加上”哪个更好”的判断 |
| 显式 / 隐式反馈 | 用户主动给的评价(点赞 / 点踩、评分)/ 从用户行为里推断的信号(换说法、转人工、回头再找) |
| 审核(audit) | 人(或 LLM)读一条轨迹、按标准判断对错和原因 |
| 数据飞轮(data flywheel) | 用线上数据持续改进系统的循环:评估 → 监控 → 改进 → 再评估 |
| 标准漂移(criteria drift) | 判断”好”的标准随着看到的输出、业务的变化而改变 |
| 合成数据(synthetic data) | 由模型生成的输入(或标注),而不是来自真实用户 |
| 污染 / 泄漏(contamination / leakage) | 评估用的数据(或它的近似重复)进入了优化过程 |
0.5 本讲的代码
代码模拟一个上线 12 周的客服智能体(Orrin Grid,第 3、4 讲的虚构能源公司):
- 请求(
support_world.py):7 类——改期、取消、停电补偿、改地址、账单疑问、要求转人工、(第 5 周起)太阳能计划;每周 400 个,有 15% 的用户说话含糊、带错别字。 - 政策:先查客户、只能改到工作日、日期含糊要问清、取消前要确认、有停电记录才补偿、补偿有上限(第 8 周起从 50 降到 30)、改地址前验证 PIN、要转人工就转、太阳能先查资格。
- 智能体(
support_agent.py):读系统提示里写的规则办事,没写的凭”常识”(有的常识对,有的完全不知道)。第 1 周的提示词写了当时的全部政策——之后没人改它。 - 用户的反应:每类失败让用户”看得见”的程度不同:不知道太阳能计划,用户会困惑、换说法、要求转人工;补偿给多了,用户只会高兴。
提醒:遵守规则的概率、用户反应的概率、标注者的准确率、合成生成器的偏差都是旋钮(各模块开头);轨迹的结构、抽取规则、信号的统计、抽样估计、错误日志与修复的时机、MinHash、最近邻分类都是真的计算。
0.6 开场:上线之后,数据才真正开始
前九讲大部分时候,我们手里有一个固定的数据集:第 3 讲的 96 道题、第 9 讲的 850 张报销单。真实的智能体不是这样:它上线以后,每天产生成百上千条对话;用户的需求在变(新产品)、公司的规定在变(新政策)、“什么算做得好”本身也在变。
第 9 讲的每一种优化方法都在消费数据:GEPA 要带反馈的例子,蒸馏要老师的标签,BetterTogether 要正确的轨迹,DPO 要偏好对,所有方法都要一个没碰过的测试集。这一讲问它们从哪来。Shankar 的文章给了一个框架:数据飞轮——用线上数据持续评估、监控、改进系统;Tan et al. 的综述则系统地整理了用 LLM 来生成和标注数据的做法。
1. 智能体需要什么数据 · What data do agents need
1.1 轨迹:一切数据的原材料
代码 01 让第 1 周的提示词跑第 9 周(太阳能已上线、补偿上限已下调)的 400 条对话。一条轨迹的日志长这样(一张补偿单:用户要 100 美元,智能体按提示词里的旧上限给了 50,而新上限是 30):
{"rid": "R09-0009", "week": 9, "prompt_version": "v1", "signals": {"thumbs": null, "rephrase": false, ...}, "confidence": 0.823, "tokens": 572}
user : Hi, this is Kai Moreno. The power was out for hours. Please credit my account 100 dollars.
agent → get_account(fields="outage_logged")
agent → issue_credit(amount=50)
agent : I've added a 50 dollar credit.
一周 400 条对话的 JSONL 日志约 296 KB、23.6 万个 token。日志里有:用户说了什么、调用了哪些工具和参数、回了什么、用户有什么反应、用了哪个版本的提示词。日志里没有:这次对话对不对、违反了哪条规定、用户真正想要什么。这一周的真实成功率是 68.0%,但光看日志不知道——这些要靠标注。
记日志的几条工程要点(第 5 讲 §5.4 的追踪):记下提示词和模型的版本(之后才能比较)、工具的参数和结果、用户的后续行为;写入前脱敏;用 JSONL 这样可以逐行追加、逐行读的格式,坏行要能跳过。
1.2 从轨迹里能拿到什么
示范(SFT 数据):成功轨迹里的每一个”上下文 → 动作”都是一个样本。关键是”成功”怎么判断:
| 选择方法 | 轨迹 | (上下文 → 动作) 样本 | 其中真失败的轨迹 | 其中”用户不会抱怨”的违规 |
|---|---|---|---|---|
| 全部轨迹 | 400 | 1,261 | 128(32%) | 46 |
| 用户没有任何负面信号 | 316 | 1,081 | 57(18%) | 40 |
| 用户点了赞 | 10 | 34 | 0(0%) | 0 |
| 人工审核判成功(随机抽 40 条审核) | 31 | 108 | 1(3%) | 1 |
“用户没抱怨”的 316 条里有 57 条其实失败了,其中 40 条是用户根本不会抱怨的那几类:补偿超过上限、没有停电记录也给了补偿、没验证 PIN 就改了地址。拿它们做示范,就是在教模型违反政策。点赞的轨迹很干净,但只有 10 条;人工审核过的干净得多,但贵、少。
偏好对:两个版本的提示词(第 1 周的 v1、按第 9 周政策更新的 v2)跑同样的请求,再判断哪个好:
| 谁来判断哪个更好 | 得到的偏好对 | 方向对的 | 方向反的 | 其实一样好 / 一样差 |
|---|---|---|---|---|
| 真实结果(理想情况) | 138 | 138 | 0 | 0 |
| 用户没有负面信号 | 120 | 68 | 8 | 44 |
| 人工审核 | 139 | 126 | 0 | 13 |
用”没有负面信号”当裁判,120 对里只有 68 对方向对:8 对把一个失败了、但用户没吭声的回答当成了更好的,44 对两个回答其实一样好或一样差——是信号的噪声造出来的”偏好”。第 9 讲的 DPO 就是用这样的数据训练的:偏好数据的质量取决于裁判。
1.3 四种数据,四种要求
| 数据 | 从哪来 | 用来 | 先要确认什么 |
|---|---|---|---|
| 示范(轨迹 → SFT 样本) | 成功的轨迹 | 微调、少样本示范、蒸馏 | 真的成功了(包括用户不会抱怨的那些规则) |
| 偏好对 | 同一请求的两个回答 | DPO / RLHF、比较两个版本 | 谁更好的判断是可靠的;两个回答来自同样的输入 |
| 反馈(显式 / 隐式信号) | 用户的行为 | 监控、挑选要审核的轨迹 | 信号覆盖哪些失败、漏掉哪些失败 |
| 评估用例(4 元组) | 标注过的请求 + 环境 + 停止条件 + 判分器 | 衡量系统、比较版本 | 和优化用的数据分开;跟得上流量和政策的变化 |
(“请求、环境、停止条件、判分器”的 4 元组来自 Press 的 benchmark 写作建议,第 12 讲细讲。)
2. 从人和智能体的交互中收集数据 · Collecting data from human–agent interaction
2.1 显式信号和隐式信号
代码 02:第 9 周 400 条对话,真实失败 128 条。每种信号能抓住多少失败?
| 信号 | 出现次数 | 覆盖了多少失败(召回) | 出现时真是失败(精确) |
|---|---|---|---|
| 点踩 | 9 | 7% | 100% |
| 换个说法再问 | 37 | 24% | 84% |
| 要求转人工 | 24 | 19% | 100% |
| 7 天内回头再找 | 41 | 27% | 83% |
| 任何一种负面信号 | 84 | 55% | 85% |
| 置信度 < 0.7 | 56 | 42% | 96% |
显式反馈极其稀疏:点踩 9 次,只覆盖 7% 的失败。隐式信号多得多,但每个都有噪声(成功的对话里也有人换个说法、过几天又来问别的事)。所有负面信号合起来覆盖 55%——信号适合用来挑要看的轨迹,不适合直接当标签。
2.2 看不见的失败
按失败的类型看,用户看得见吗?
| 失败的类型(违反的规则) | 次数 | 有任何负面信号 | |
|---|---|---|---|
| solar(不知道太阳能计划) | 47 | 83% | |
| credit_cap(补偿超过上限) | 40 | 12% | 用户不会抱怨 |
| lookup(没先查客户) | 25 | 72% | |
| misunderstood(听错了) | 19 | 84% | |
| credit_outage(没停电也给补偿) | 5 | 20% | 用户不会抱怨 |
| verify_pin(没验证 PIN) | 4 | 0% | 用户不会抱怨 |
(credit_cap 的 12% 来自同一对话里的其他失败。)最贵的失败——钱、安全、合规——恰恰是用户不会告诉你的。它们只能靠主动审核发现:这是为什么任何只靠用户反馈的改进流程,都会系统性地漏掉一类问题。
2.3 谁在说话
12 周合计,按用户类型看显式反馈:
| 用户类型 | 对话 | 点赞 | 点踩 | 给了反馈的比例 |
|---|---|---|---|---|
| 普通(calm) | 2,910 | 76 | 57 | 4.6% |
| 话多(vocal) | 718 | 49 | 25 | 10.3% |
| 沉默(silent) | 1,172 | 0 | 8 | 0.7% |
“话多”的用户只占 15%,给反馈的比例却是普通用户的两倍多;“沉默”的用户几乎不给。点赞 / (点赞 + 点踩) = 58%,而 12 周的真实成功率是 80%——反馈代表的是愿意说话的那些人,它的比例不是成功率。
2.4 估计 vs 发现:两种抽样
估计第 9 周的真实成功率(68.0%):
| 估计方法 | 看了多少 | 花费(美元) | 估计的成功率 |
|---|---|---|---|
| 1 − 负面信号比例 | 全部 400 条 | 0 | 79.0% |
| 点赞 / (点赞 + 点踩) | 19 次反馈 | 0 | 52.6% |
| 随机抽 40 条人工审核 | 40 条 | 80 | 65.0% [49.5, 77.9] |
| 按信号挑 40 条人工审核 | 40 条 | 80 | 10.0% |
| LLM 评审看全部(提示词里是第 1 周的政策) | 全部 400 条 | 4 | 81.8% |
| LLM 评审看全部(提示词更新到当前政策) | 全部 400 条 | 4 | 72.5% |
- “1 − 负面信号比例”漏掉了看不见的失败,偏高;点赞比例偏得没有方向。
- 随机抽查是无偏的,但 40 条的置信区间有 28 个点宽(第 12 讲会讲需要多少样本)。
- 按信号挑着查严重偏低:挑的就是可疑的——它是找问题的好办法,却不能用来估计水平。
- LLM 评审便宜,但它的判断标准是它提示词里的政策;政策变了没人告诉它,它就看不出新上限下的违规(§3.4 的标准漂移)。
第一条经验:找问题用”按信号挑”(富集失败),量水平用”随机抽”(无偏)。两件事、两种抽样,不要混。
3. 数据飞轮 · Data flywheels
3.1 Shankar 的三步
Shankar 的 Data Flywheels for LLM Applications 的出发点:人的偏好会变、LLM 不一定照指令办事,所以评估不能只做一次,要变成一个持续转动的循环:
- 评估:定义成功的指标。指标要从数据里来,而不是凭空想出来——先读大量真实输出,再决定检查什么(文中的例子:发现输出里总有”delve”这样一看就是 AI 写的词);能用代码检查的用代码,主观的用 LLM 评审,并给评审少量好坏示例来对齐;优先用二元的指标(对 / 错),比 1–5 分更容易对齐、标注更一致;多步的流水线要在每个节点设检查点(分类节点看准确率,写作节点看质量,写代码的节点跑测试);输入也要检查(话题是否支持、是否含敏感信息、是否异常)。
- 监控:让指标跑起来。定期抽样、标注线上数据,标签带时间戳;标准会漂移——新的失败模式出现、模型 API 变了,指标本身要定期复审;让 LLM 评审”动态”起来:检索相关的已标注例子当少样本示范,优先使用人和评审意见不一致的例子(主动学习),近期的标签权重更高。
- 持续改进:闭环。记录输出和指标分数;手动迭代(看低分的簇、分析输入的模式、A/B 测试提示词);自动修复(把低分输出改好、记录理由,改好的轨迹在运行时作为最相似的少样本示范);把”质量”拆成多个指标,标注更一致、评审更容易对齐、也更容易定位是哪一项坏了。
3.2 一个每周转一圈的飞轮
代码 03(flywheel.py)把它写成每周一圈:用当前提示词跑这周的 400 个请求 → 按某种策略挑一些去标注 → 失败按原因记进错误日志(跨周累积)→ 某个原因累计 3 次 → 工程师查政策、把规则写进提示词,下周生效 → 标注过的加进评估集。比较六种策略:
| 策略 | 第1周 | 第5周 | 第8周 | 第9周 | 第12周 | 第 8–12 周平均 | 12 周标注花费(美元) | 修好太阳能 | 修好补偿上限 |
|---|---|---|---|---|---|---|---|---|---|
| 不转飞轮 | 91% | 86% | 71% | 70% | 70% | 71.4% | 0 | 没修 | 没修 |
| 只审核点踩的对话 | 89% | 87% | 83% | 79% | 82% | 81.5% | 128 | 第 5 周 | 没修 |
| 每周随机抽 40 条 | 89% | 84% | 78% | 89% | 89% | 86.8% | 960 | 第 6 周 | 第 8 周 |
| 按信号优先挑 40 条 | 92% | 84% | 80% | 80% | 82% | 81.8% | 960 | 第 5 周 | 没修 |
| 30 条按信号 + 10 条随机 | 90% | 88% | 84% | 89% | 88% | 86.9% | 960 | 第 5 周 | 第 8 周 |
| LLM 评审看全部 + 10 条人工抽查 | 89% | 86% | 82% | 81% | 86% | 85.4% | 287 | 第 5 周 | 第 9 周 |
(“修好”指那一周的错误日志里攒够了证据;修改下一周生效。)
- 不转飞轮:91% → 70%。没有人改提示词,世界却变了。上线不是终点,而是飞轮的起点。
- 只看点踩、按信号挑:能发现用户看得见的问题(太阳能,第 5 周就攒够了证据),但永远发现不了补偿超过上限——用户只会高兴。
- 加上随机抽查(mixed、纯随机)才能发现看不见的失败;mixed 两者兼顾:既能快速找到看得见的问题,又不漏掉看不见的。
- LLM 评审全量便宜(287 美元),但它的政策是团队提示词里写的旧政策,看不出补偿上限的问题;最后靠每周 10 条人工抽查慢慢攒够证据,第 9 周才修好。
3.3 估计也要用对的样本
各策略用自己标注的数据估计成功率(估计 / 真实):
| 策略 | 第 3 周 | 第 6 周 | 第 9 周 | 第 12 周 |
|---|---|---|---|---|
| 每周随机抽 40 条 | 90% / 91% | 80% / 85% | 85% / 89% | 90% / 89% |
| 按信号优先挑 40 条 | 57% / 90% | 55% / 92% | 50% / 80% | 52% / 82% |
| 30 条按信号 + 10 条随机 | 60% / 89% | 60% / 90% | 68% / 89% | 55% / 88% |
| LLM 评审看全部 + 10 条人工抽查 | 86% / 90% | 88% / 89% | 88% / 81% | 84% / 86% |
按信号挑出来的样本估出来的成功率只有真实的一半多——§2.4 说过,它是用来找问题的。mixed 策略如果要报告整体水平,应该只用那 10 条随机样本(或者按抽样概率加权)。
3.4 评估集会过时;标准会漂移
评估集会过时。让一直不改的智能体(第 1 周的提示词)每周在三种评估集上各报告一个数:
| 第几周 | 线上真实 | 第 1 周的请求 + 第 1 周的标准 | 本周的请求 + 第 1 周的标准 | 本周的请求 + 本周的标准 |
|---|---|---|---|---|
| 1 | 93.8% | 91% | 86% | 94% |
| 5 | 85.5% | 87% | 89% | 87% |
| 8 | 72.0% | 93% | 87% | 75% |
| 12 | 68.8% | 93% | 90% | 68% |
第 12 周线上只有 69%,第 1 周建的评估集还报告 93%。就算换成本周的请求,用第 1 周的标准判分也报告 90%——请求要跟上流量,判分标准也要跟上政策。
标准漂移(criteria drift)。Shankar 和合作者在 Who Validates the Validators?(第 11 讲的阅读材料)里观察到:人在给输出打分的过程中,才逐渐明确自己的标准;有些标准依赖于看到的具体输出,没法事先定死。在我们的飞轮里,它表现为 LLM 评审的”政策”有没有更新:
| LLM 评审 | 修好补偿上限 | 第 8–12 周平均 | 第 9 周 估计 / 真实 |
|---|---|---|---|
| 评审的政策 = 团队提示词里写的(政策变了没人告诉它) | 第 9 周 | 85.4% | 88% / 81% |
| 评审的政策 = 公司当前的政策 | 第 8 周 | 87.3% | 88% / 90% |
评审本身也是要维护的数据:它的提示词、它的少样本示例、它对照的政策,都要和业务一起更新。
3.5 标注预算
mixed 策略(3/4 按信号、1/4 随机),每周审核多少条:
| 每周审核几条 | 第 8–12 周平均 | 12 周花费(美元) | 修好太阳能 | 修好补偿上限 |
|---|---|---|---|---|
| 8 | 82.8% | 192 | 第 6 周 | 没修 |
| 20 | 85.0% | 480 | 第 5 周 | 第 9 周 |
| 40 | 86.9% | 960 | 第 5 周 | 第 8 周 |
| 80 | 86.9% | 1,920 | 第 5 周 | 第 8 周 |
预算决定问题被发现得多快;超过某个量之后,多审核只是在重复确认已知的问题。剩下的 10% 左右是”听错了”之类改提示词修不好的失败——那是第 9 讲权重或推理算力的事。
4. 合成数据与 LLM 标注 · Synthetic data and LLM annotation
4.1 用 LLM 生成和标注数据
Tan et al. 的综述把”用 LLM 做数据”分成三个方面:用 LLM 生成标注(让 LLM 给数据打标签、写理由、生成指令和回答、生成成对的偏好)、评估 LLM 生成的标注(怎样判断这些标签和数据的质量)、利用 LLM 生成的标注(拿它们做监督学习、对齐、推理训练等);并讨论了它的局限:偏差、幻觉、和真实分布的差距、以及评估这些数据本身的困难。
为什么要合成数据?真实数据少(新产品刚上线,太阳能请求一共没几条)、贵(人工标注每条 2 美元)、来得慢(要等用户来问)、可能有隐私问题。代码 04 用一个模拟的生成器(data_lab.SyntheticGenerator)演示它的典型问题——它的偏差是我们设定的;06_real_llm.py 可以用真实的 Claude 检验这些偏差是否真的存在。
4.2 重复
直接生成 1,000 条请求:
| 去重方法 | 去掉 | 剩下 |
|---|---|---|
| 完全相同(字符串相等) | 604 | 396 |
| MinHash 相似度 ≥ 0.9 算重复 | 604 | 396 |
| MinHash 相似度 ≥ 0.7 算重复 | 628 | 372 |
| MinHash 相似度 ≥ 0.5 算重复 | 856 | 144 |
| (对照)1,000 条真实请求,相似度 ≥ 0.7 | 397 | 603 |
生成器反复写同一个模板、只换名字和日期——数量多不等于信息多。
MinHash 是近似去重的标准工具:把一段文本切成”词的 3-gram”集合;用 k 个哈希函数,每个函数下取集合里的最小哈希值,得到长度 k 的签名。两个集合的签名里”同一位置相等”的比例,是它们 Jaccard 相似度(交集 / 并集)的无偏估计。签名越长越准:300 对文本上平均绝对误差 k=16 时 0.057,k=64 时 0.027,k=256 时 0.013。签名短、可以分桶(LSH)只比较可能相似的那些,所以能处理几百万条文本。
4.3 分布:难的情形被”洗干净”了
| 特征 | 真实流量 | 直接让 LLM 生成 | 照着真实日志生成 |
|---|---|---|---|
| 意图:改期 / 补偿 / 转人工 | 23% / 19% / 6% | 14% / 14% / 14% | 24% / 18% / 7% |
| 说话含糊 / 有错别字 | 13% | 0% | 15% |
| 改期请求里日期含糊 | 22% | 0% | 23% |
| 改期请求里要求周末 | 23% | 0% | 23% |
| 补偿请求里没有停电记录 | 34% | 0% | 29% |
| 补偿请求里金额 > 50 | 36% | 0% | 32% |
直接生成的数据意图均匀、语言干净、情形”标准”——最容易出错的那些情形恰恰被洗掉了。“照着真实日志生成”(给生成模型几条真实对话当示范、指定用户画像和场景)的比例和真实流量接近得多。
4.4 在合成数据上评估会高估多少
(判分用第 12 周的政策。)
| 智能体 | 真实流量 | 直接生成的合成数据 | 照着日志生成的合成数据 |
|---|---|---|---|
| 只有任务说明、没写任何政策 | 41.8% | 48.1% | 43.3% |
| 第 1 周的提示词 | 68.8% | 71.4% | 71.3% |
| 按第 12 周政策更新的提示词 | 88.5% | 90.8% | 90.2% |
高估多少取决于智能体弱在哪:没写任何政策的智能体在周末、含糊日期、没有停电记录这些情形上更容易错,而这些情形在直接生成的数据里被洗掉了,所以高估了 6 个点;第 1 周的提示词剩下的失败(太阳能、补偿上限)在合成数据里也有,只高估了 2.6 个点。合成数据的盲区和智能体的弱点重合得越多,评估就偏得越多——而你事先不知道它们重不重合。
第二条经验:合成数据最适合补覆盖(新意图、罕见情形、对抗样本),不适合单独拿来评估。要评估,就让它的分布照着真实日志,并且和真实数据一起报告。
4.5 LLM 当标注者
第 12 周、第 1 周提示词的 400 条对话:
| 标注方式 | 和真实结果一致 | 漏掉的失败 | 花费(美元) |
|---|---|---|---|
| 人工审核全部 | 97.0% | 6 | 800 |
| LLM 评审全部 | 89.0% | 30 | 4 |
| LLM 评审全部 + 人工复查它判失败的和随机 20 条(共 124 条) | 91.8% | 33 | 252 |
LLM 便宜两百倍,但漏报多。混合方案的一致率升了,升的部分来自纠正 LLM 的误报;LLM 判”成功”的那些失败人根本没看到,漏掉的失败没有减少(人复查时也会看漏几个)。人要放在 LLM 最可能错的地方——而 LLM 的漏报恰恰藏在它很有把握的”成功”里。更好的做法是在 LLM 判成功的样本里也随机抽查,估计它的漏报率(第 11、13 讲)。
5. 用于优化的数据 vs 用于评估的数据 · Data for optimization vs. evaluation
同一批数据,拿来”改系统”和拿来”量系统”,会互相污染。代码 05 演示四种常见的方式。
5.1 在测试集上优化
把第 9 讲的 GEPA 跑两遍(3 个种子):一遍在验证集上挑候选、在测试集上报告;一遍直接把测试集当成挑候选的集合:
| 做法 | 报告的数字 | 全新数据(1,000 张) | 高估 |
|---|---|---|---|
| 正确:在验证集上挑,在测试集上报告 | 89.8% | 89.3% | +0.5% |
| 污染:把测试集当成挑候选的验证集 | 92.3% | 84.9% | +7.4% |
优化器在哪个集合上挑,哪个集合的分数就不能再当作”没见过的数据”。污染的那一组在全新数据上反而更差:100 张测试单子太少,挑出来的是”恰好在这 100 张上好”的候选。
5.2 反复看同一个评估集
团队一版一版地改提示词,每次在同一个 100 张的评估集上看一眼,留下最好的:
| 试了几个版本 | 挑中的版本在评估集上 | 同一个版本在全新数据上 | 高估 |
|---|---|---|---|
| 1 | 64.0% | 58.9% | +5.1% |
| 5 | 67.0% | 70.2% | −3.2% |
| 20 | 86.0% | 75.0% | +11.0% |
| 80 | 87.0% | 75.1% | +11.9% |
(1 个、5 个版本时的差别主要是 100 张评估集本身的抽样误差。)版本越多,评估集上的最高分里运气越多——第 5、9 讲的赢家诅咒。每一次”看一眼评估集再决定”,都在把评估集变成训练集。要留一个只在最后看一次的测试集;常用的评估集要定期更换。
5.3 时间漂移:随机切分 vs 按时间切分
一个按第 4 周政策写的提示词(不知道太阳能),用两种评估集预测第 9–12 周的表现:
| 评估集 | 其中太阳能请求 | 报告的成功率 |
|---|---|---|
| 第 1–8 周里随机抽 200 条 | 6% | 81.0% |
| 最近一周(第 8 周)抽 200 条 | 12% | 70.5% |
| (真实)第 9–12 周的全部流量 | 13% | 69.8% |
流量在变时,评估集要按时间切(用最近的数据),随机切分会把过去平均进来。同样的道理也适用于训练 / 测试的切分:用过去训练、用未来测试,才模拟得了上线后的情形。
5.4 近似重复泄漏
把评估用例”改写”一下(换个商家名、换个说法)混进训练数据或少样本的检索池,是最隐蔽的污染。代码 05 用一个最近邻分类器(“记忆”的极端情形,相当于动态少样本检索:找最像的已标注例子、抄它的答案)在报销审核任务上演示:
| 检索池 | 条数 | 评估集(150 张) | 全新数据(500 张) | 高估 |
|---|---|---|---|---|
| 600 张独立的标注单子 | 600 | 76.7% | 73.2% | +3.5% |
| 再加上评估用例的改写(每张改写 2 次,+300 条) | 900 | 96.7% | 73.0% | +23.7% |
评估集上的 96.7% 是”抄”来的。和评估集去重能修好它,但去重本身有讲究:
| 和评估集去重:怎样比较 | 去掉的改写(共 300) | 误伤的独立单子(共 600) | 评估集 | 全新数据 |
|---|---|---|---|---|
| 整张单子,阈值 0.6 | 300 | 387 | 64.0% | 65.4% |
| 去掉模板文字,阈值 0.6 | 120 | 0 | 90.0% | 73.2% |
| 去掉模板文字,阈值 0.4 | 276 | 16 | 77.3% | 73.2% |
| 去掉模板文字,阈值 0.3 | 298 | 72 | 75.3% | 71.8% |
- 直接比较整张单子:每张都有的字段名和汇率行让所有单子都”相似”,误伤了 387 张好数据——比较之前先去掉模板。
- 阈值要在”漏掉泄漏”和”误删好数据”之间取舍:0.6 只抓到 120 条改写(评估集仍虚高到 90%),0.4 抓到 276 条、误伤 16 张,0.3 几乎全抓到、误伤 72 张。
第三条经验:合成数据、数据增强、从网上抓来的数据、动态少样本的检索池——进训练(或进提示词)之前,都要和评估集去重。大模型的”测试集污染”(benchmark 的题目出现在预训练数据里)是同一个问题的放大版。
6. 用真实模型试一试 · Try it on a real model
06_real_llm.py(可选,需要 pip install anthropic 和 ANTHROPIC_API_KEY,2 次调用)让真实的 Claude 用两种方式各生成 40 条客服请求——只说要求 / 给 6 条真实的请求当示范——然后用 MinHash 去重、用关键词粗略统计意图、含糊日期、周末、错别字的比例,和真实流量比。
python3 06_real_llm.py
不联网检查脚本本身:python3 06_real_llm.py --toy(用模拟的生成器)。
读结果时想一想:真实模型生成的请求会不会也”过于干净”?给了真实示范之后,错别字和含糊日期出现了吗?意图的比例像不像真实流量(关键词分类很粗糙,只看大的差别)?40 条里有多少近似重复?编写时没有用真实 API 跑过这个脚本,这里不报告数字。
7. 一份数据计划 · A data plan
给一个要上线的智能体写数据计划时,逐条回答:
- 记什么:每条轨迹的输入、每一步(工具和参数)、输出、提示词和模型版本、用户的后续行为;写入前脱敏;保留多久。
- 标什么、谁来标:成功与否 + 失败原因(二元、按规则拆开);人工审核员拿的是当前的政策手册;LLM 评审的提示词和政策一起更新,并定期用人工标注验证它(第 13 讲)。
- 挑什么去标:按信号挑一部分(找问题),随机抽一部分(量水平、发现看不见的失败),两部分分开统计。
- 错误日志:失败按原因累积;够证据就修;修了之后看那一类失败是否真的消失。
- 评估集:和优化用的数据分开;按时间更新(最近的流量);判分标准随政策更新;留一个只在发布前看一次的测试集。
- 合成数据:用来补覆盖(新意图、罕见和对抗情形),照着真实日志生成;进训练或检索池之前和评估集去重。
- 示范和偏好数据:只用确认过的成功轨迹;偏好对的裁判要可靠(审核过的,而不是”用户没抱怨”)。
8. 小结 · Summary
- 轨迹是原材料:日志能记下输入、步骤、输出和用户反应,记不下”对不对”——那要靠标注。示范、偏好对、反馈信号、评估用例各有用途,也各有要求:示范要确认真的成功,偏好对要可靠的裁判,信号要知道它漏掉了什么,评估用例要和优化数据分开、跟得上变化。
- 用户反馈有盲区:显式反馈稀疏、偏向愿意说话的人;隐式信号多但有噪声;最贵的失败(钱、安全、合规)用户不会告诉你。找问题用按信号挑,量水平用随机抽。
- 数据飞轮:评估 → 监控 → 改进,每周转一圈。只靠用户信号的飞轮发现不了看不见的失败;加上随机抽查才行。评估集会过时,判分标准会漂移——评审本身也是要维护的数据。
- 合成数据与 LLM 标注:生成的数据会重复、过于干净、分布不对;照着真实日志生成能缩小差距;在合成数据上评估会高估,高估多少取决于它的盲区和智能体的弱点重不重合。LLM 标注便宜但漏报多,人要放在它最可能错的地方。
- 优化数据和评估数据要分开:在测试集上优化、反复看同一个评估集、随机切分遇上漂移、近似重复泄漏,都会让报告的数字虚高;去重要先去掉模板、再在漏报和误删之间选阈值。
9. 常见误区 · Common pitfalls
- “日志全记下来了,数据就有了。” 日志里没有”对不对”;没有标注的轨迹只是原材料。
- “用户没抱怨,就是成功。” 第 9 周”没有负面信号”的轨迹里 18% 其实失败了,大多是用户不会抱怨的违规(多给钱、没验证身份)。
- “点赞率就是满意度、就是成功率。” 点赞 / (点赞 + 点踩) = 58%,真实成功率 80%;给反馈的主要是话多的那部分用户。
- “把可疑的对话挑出来审核,顺便就知道整体水平了。” 按信号挑的样本估出来的成功率只有真实的一半多。
- “评估集建好了就能一直用。” 第 1 周的评估集在第 12 周报告 93%,线上只有 69%。
- “换上最新的请求,评估集就不会过时。” 判分标准也要更新:用旧政策判新请求还报告 90%。
- “LLM 评审全量标注,又便宜又全面。” 它只认识它提示词里的政策;它的漏报藏在它很有把握的”成功”里。
- “合成数据越多越好。” 1,000 条里 60% 是完全重复;它还把最难的情形洗掉了。
- “在合成数据上测过了,上线也差不多。” 高估多少取决于合成数据的盲区和智能体弱点的重合,你事先不知道。
- “数据增强是好习惯。” 把评估用例改写一下混进检索池,评估集准确率从 76.7% 变成 96.7%,全新数据上一点没涨。
10. 练习 · Exercises
练习 1(选示范):代码 01 里,“用户没有任何负面信号”的轨迹有 316 条,其中 57 条失败。(a) 如果拿这 316 条做 SFT,模型会学到什么坏习惯?(b) 只用人工审核过的 31 条又有什么问题?(c) 设计一个折中方案。
参考答案
(a) 40 条是"用户不会抱怨"的违规:补偿超过上限(按旧上限给 50)、没有停电记录也给补偿、没验证 PIN 就改地址——模型会学到"多给钱、跳过验证"是正常做法;另外 17 条是其他失败(例如不认识太阳能计划时的含糊回复)。(b) 太少,而且随机抽的 31 条大多是简单的请求,覆盖不了罕见情形。(c) 例如:用"没有负面信号"做初筛,再用**程序检查**看得见的规则(issue_credit 的金额是否 ≤ 当前上限、update_address 之前是否有 verify_pin、太阳能请求是否调用了 check_solar_eligibility)过滤掉违规;剩下的再按意图分层抽一部分人工审核,估计残留的错误率;高风险意图(补偿、改地址)只用人工确认过的。练习 2(信号的召回和精确):代码 02 里”任何一种负面信号”出现 84 次、精确 85%、召回 55%,真实失败 128 条。(a) 验证这几个数互相一致。(b) 如果每周只能审核 40 条,全部从有信号的对话里挑,期望能找到多少个失败?随机挑呢?
参考答案
(a) 有信号且真失败 = 84 × 0.85 ≈ 71;71 / 128 ≈ 55%,一致。(b) 从有信号的里挑 40 条,期望 40 × 0.85 = 34 个失败;随机挑 40 条,期望 40 × 128/400 ≈ 12.8 个。按信号挑找到的失败多将近 3 倍——但找到的都是"看得见"的那类,而且不能拿来估计整体成功率。练习 3(估计的置信区间):随机抽 40 条审核,32 条成功。(a) 用正态近似算 95% 置信区间。(b) 要把区间半宽缩到 ±5 个点,大约要抽多少条?(c) 这和每周 400 条的流量比,意味着什么?
参考答案
(a) p̂ = 0.8,标准误 = √(0.8 × 0.2 / 40) ≈ 0.063,95% 区间 ≈ 0.8 ± 0.124,即 [67.6%, 92.4%](代码里用 Wilson 区间,形状稍有不同)。(b) 1.96 × √(0.8 × 0.2 / n) = 0.05 ⇒ n ≈ 246。(c) 要把每周的成功率估到 ±5 个点,几乎要审核一半的流量;实际做法是按月汇总、或者用 LLM 评审全量估计再用少量人工样本校正它的偏差(第 13 讲)。练习 4(为什么按信号挑会偏):用贝叶斯公式解释:按信号挑出来的 40 条里成功率约 50%,而整体成功率 80% 左右。
参考答案
P(成功 | 有信号) = P(有信号 | 成功) · P(成功) / P(有信号)。成功的对话也有少量"误报"信号(例如 3% 换个说法、4% 回头再找),失败的对话有信号的概率高得多(看得见的失败 50% 以上)。整体 80% 成功时,P(有信号) 主要由失败贡献,所以有信号的对话里成功的比例远低于 80%。按信号挑,就是在条件 P(·| 有信号) 下抽样——它回答的是"有信号的对话里有多少失败",不是"所有对话里有多少失败"。如果非要用这批样本估计整体,要按抽样概率加权(逆概率加权),这需要知道每条对话被挑中的概率。练习 5(设计飞轮):代码 03 的飞轮假设”错误日志里一个原因攒够 3 条,工程师就能把规则写对”。(a) 现实中这一步可能怎样出错?(b) 怎样验证一次修复真的生效了?(c) 什么情况下修复会引入新的问题?
参考答案
(a) 3 个例子可能来自同一个特殊情形,归纳出的规则过窄或过宽;工程师不知道政策的细节(例如新上限是否对老客户也适用);修复写进了提示词,但和已有规则冲突,或者让提示词变长、其他规则被遵守得更差(第 9 讲 §1.3)。(b) 在修复之后的流量里,专门统计那一类失败的比例(需要随机样本),并在评估集上回归测试所有其他类别;最好先在一小部分流量上 A/B 测试。(c) 修复过宽(例如为了修补偿上限,写成"拒绝所有补偿请求")、改变了其他意图的行为;或者修复针对的是错误的原因(第 9 讲 §3.3 的归因问题)。所以修复之后要看**所有**类别,而不只是被修的那一类。练习 6(标准漂移):举出你熟悉的一个场景里”判分标准需要随时间更新”的三个原因,并说明每种情况下谁应该负责更新评估。
参考答案(之一)
以客服为例:(1) 政策变了(补偿上限、退货期限)——政策团队变更时同步通知评估的维护者,评估集的判分器版本号跟政策版本绑定;(2) 产品变了(新功能、新套餐)——产品上线流程里加一步"补充评估用例";(3) 用户期望变了(例如过去可以接受的"请稍候"现在被视为差体验),或者团队在看输出时发现了新的失败模式(Shankar 的观察:标准是在看输出的过程中形成的)——由做错误分析的人提出、经评审后加进评分细则。共同点:评估是一份要版本管理、要有人负责的资产。练习 7(合成数据的覆盖):太阳能计划刚上线时,真实请求只有十几条。设计一个用 LLM 生成太阳能相关测试用例的流程,使它尽量覆盖真实会遇到的情形,并说明怎样检查生成的数据。
参考答案(之一)
1) 先列出情形清单(不是让 LLM 自由发挥):有资格 / 没资格、地址不在服务区、已经在计划里、想退出、问价格、说话含糊或有错别字、夹带别的请求("顺便改个地址")、不认识"solar"这个词的说法;2) 每个情形给 LLM 几条真实的请求当示范、指定用户画像(年长用户、着急的用户、非母语用户),各生成若干条;3) 去重(先去掉模板、再 MinHash),并和评估集去重;4) 检查分布:每个情形的数量、错别字和含糊表达的比例、和真实请求的相似度;5) 人工读一个样本,确认情形标签对、请求自然;6) 生成的用例用于**覆盖测试**,报告时和真实请求上的结果分开。练习 8(LLM 标注者的漏报):代码 04 里 LLM 评审漏掉 30 个失败,混合方案还漏掉 33 个。设计一个更好的人机分工,并说明怎样估计最终标签里还剩多少漏报。
参考答案(之一)
在 LLM 判"成功"的样本里也随机抽一部分人工复查(例如 10%),用它估计 LLM 的漏报率 r;对已知 LLM 容易漏的类别(例如需要知道最新政策的补偿上限、需要看工具调用顺序的 PIN 验证)用程序检查或者全部人工复查;人和 LLM 不一致的例子拿去更新 LLM 评审的提示词(少样本示例、政策)。最终的漏报数 ≈ LLM 判成功的条数 × r(只在没被人工复查的那部分上),给出置信区间。这本质上是第 13 讲"用少量人工标签校准 LLM 评审"的做法。练习 9(反复看评估集):代码 05 里,试 80 个版本后评估集高估了 11.9 个点。(a) 解释为什么版本越多高估越多。(b) 列出三种减轻它的做法。
参考答案
(a) 每个版本在评估集上的分数 = 真实水平 + 抽样噪声(100 张时标准差约 4–5 个点)。挑最高分时,挑中的往往是噪声恰好为正的那个;版本越多,最大的噪声越大(大约随 √(2 ln K) 增长)。(b) ① 留一个只在最后看一次的测试集;② 增大评估集(噪声随 1/√n 下降)或者用配对比较(两个版本在同一批例子上比,噪声小得多,第 12 讲);③ 定期更换评估集、限制每个评估集被查看的次数;④ 对"最好的版本"在全新的数据上复测再上线。练习 10(开放题:给你的智能体写一份数据计划):选一个你熟悉或者想做的智能体(例如帮研究生找文献的智能体),按 §7 的七条写一份数据计划,并特别说明:它的”看不见的失败”可能是什么、你准备怎样发现它们。
参考答案(之一)
以文献助手为例:看不见的失败包括引用了不存在的论文(用户不一定去查)、漏掉了最重要的相关工作(用户不知道自己不知道)、把论文的结论说反了(用户相信了)。发现它们:① 程序检查——每个引用都去 DOI / arXiv 查是否存在、标题作者是否匹配;② 随机抽查——每周抽 20 条对话,请领域内的人检查结论是否准确、是否漏了关键文献;③ 构造覆盖测试——对一些有公认综述的问题,检查回答是否覆盖了综述里的核心文献。其余几条:记录检索词、检索结果、引用和用户是否点开了引用;评估集按月更新(新论文不断出现),判分标准里"重要文献"的清单要随领域更新;合成问题用于覆盖不同领域和不同表述,进检索池之前和评估问题去重。11. 延伸阅读 · Further reading
本讲的阅读材料
- S. Shankar, Data Flywheels for LLM Applications, 2024(sh-reya.com 博客)。
- Z. Tan et al., Large Language Models for Data Annotation and Synthesis: A Survey, EMNLP 2024.
相关
- S. Shankar et al., Who Validates the Validators? Aligning LLM-Assisted Evaluation of LLM Outputs with Human Preferences, UIST 2024(EvalGen 与标准漂移;第 11 讲的阅读材料)。
- O. Press, How to Build Good Language Modeling Benchmarks(博客;请求、环境、停止条件、判分器的 4 元组;第 12 讲)。
- A. Z. Broder, On the Resemblance and Containment of Documents, 1997(MinHash 的原始论文)。
- L. Ouyang et al., Training Language Models to Follow Instructions with Human Feedback, NeurIPS 2022(示范数据和偏好数据怎样用于对齐)。
12. 下一讲预告 · Next lecture
第 11 讲 数据选择与质量(11/2)。这一讲说的是”需要什么数据、从哪来”;下一讲问”有了一大堆数据,哪些值得用、怎样保证它们是好的”:怎样找出信息量最大的数据(tinyBenchmarks 用 100 道题估计整个 MMLU 的分数)、过滤与选择的策略、小而精的数据为什么够用(LIMA 的 1,000 条)、标注的实践与一致性、从智能体轨迹里大规模构建训练数据(SWE-smith 从 128 个代码仓库里造出 5 万个任务),以及”谁来验证验证者”(EvalGen)。配套代码会用 IRT 模型挑出最有信息量的评估题、比较几种数据过滤与选择的方法、模拟标注者之间的一致性,并在一个小代码仓库上照着 SWE-smith 的做法自动造出”能让测试失败的 bug”任务。