数据飞轮与合成数据

状态:✅ 已补齐(2026-08-30)
一句话定义:把线上反馈变成 可训练/可评测数据 ,形成「用 → 标 → 评 → 改(提示/检索/微调)」飞轮;合成数据加速冷启动。

大纲

  1. 飞轮环节与责任人
  2. 日志 → 黄金集 → 回归
  3. 合成数据:用途与污染风险
  4. 与 SFT / DPO / 偏好数据的衔接
  5. 端到端落地示例

已有相关文档(先读这些)

学习要点(读后应能回答)

  • 什么反馈值得进训练集,什么只配进评测集?→ 见 2.4
  • 合成数据何时会放大偏见?→ 见 3.3
  • 方法论怎么排进四周的排期?→ 见

一、飞轮环节与责任人

数据飞轮 /ˈdeɪtə ˈfaɪwiːl/ (Data Flywheel,数据飞轮)的本质:产品运行产生的数据反哺产品,形成越用越好的闭环。LLM 应用的飞轮有四个环节:

flowchart LR
    A[用<br/>线上请求与反馈] --> B[标<br/>筛选 · 标注 · 入库]
    B --> C[评<br/>黄金集 · 回归 · 指标]
    C --> D[改<br/>提示 / 检索 / 微调]
    D --> A

1.1 四个环节的分工

环节 做什么 责任人 关键产物
记录请求、响应、用户显式/隐式反馈 工程师(埋点)+ 产品(定义反馈语义) 结构化日志
采样、脱敏、标注质量标签 标注团队 + 领域专家(抽检仲裁) 带标签数据集
维护黄金集、跑回归、看指标 算法工程师 黄金集 + 评测报告
改提示词 / 检索策略 / 触发微调 算法 + 工程 新版本 + 变更记录

1.2 工程要点

  • 反馈语义先定义:点赞/点踩、复制、重试(retry)、会话放弃各代表什么,由产品给出解释,再由工程落埋点——顺序反了,日志就是废数据;
  • 闭环周期要短:飞轮转速取决于「发现坏 case → 上线修复」的周期。目标以周为单位,而不是以季度为单位;
  • 每个改动可归因:评测集版本、提示词版本、模型版本三者绑定记录,否则改进无法复现。

1.3 贯穿案例:电商客服「小智」

后续所有示例都基于这个虚构但真实感足够的场景:

设定
业务 电商售前咨询 + 售后工单机器人
流量 日均 2 万次请求
现状 v1 版本(RAG + 中档模型)已上线,无数据回流
目标 8 周内把黄金集通过率从 82% 提到 85%+

第一步是产品定义反馈语义(先于埋点),每种反馈的「证据强度」不同:

反馈动作 产品定义 证据强度
点「转人工」 机器人没解决问题 ★★★(最强坏例信号)
点踩 答案错误或没用 ★★★
重新提问(retry) 可能对首答不满意 ★★(需标注确认)
复制答案 答案被采纳走 ★(好例信号)
点赞 明确满意

对应的埋点事件结构:

interface FeedbackEvent {
  requestId: string;
  sessionId: string;
  userId: string;          // 脱敏后的 ID,不存原始账号
  modelVersion: string;    // 如 "v1.3.0"
  promptVersion: string;   // 如 "cs-sys-p42",改提示词必须换版本号
  feedbackType: "like" | "dislike" | "copy" | "transfer" | "retry";
  latencyMs: number;
  retrievedDocIds: string[]; // RAG 命中的文档,定位"检索没命中"类坏例
  createdAt: Date;
}

二、日志 → 黄金集 → 回归

2.1 从日志到数据集的流水线

  1. 采样:全量日志太贵,按「坏例优先」分层采样——差评、重试、人工接管、兜底话术命中的请求优先入选;
  2. 脱敏:先做 PII /ˌpiː aɪ ˈaɪ/ ( Personally Identifiable Information ,个人身份信息)检测打码,再进数据集;
  3. 标注:给每个样本打质量标签(好 / 坏 / 有条件好),坏例补注「坏在哪」(事实错 / 格式崩 / 拒答错误 / 检索没命中);
  4. 入库:带版本号入库,记录来源、标注人、标注时间。

落地示例:坏例优先的加权采样(Node.js + TS):

interface SampledCase {
  requestId: string;
  priority: number;
}

const WEIGHTS: Record<FeedbackEvent["feedbackType"], number> = {
  transfer: 10,
  dislike: 5,
  retry: 3,
  copy: 1,
  like: 1,
};

// 坏例优先分层采样:按反馈权重加权随机抽 500 条,再混入 5% 无反馈请求做基线
function sample(events: FeedbackEvent[], quota = 500): SampledCase[] {
  return events
    .filter((e) => !hasPII(e)) // 先过 PII 检测,脏数据直接丢弃
    .map((e) => ({
      requestId: e.requestId,
      priority: WEIGHTS[e.feedbackType] * Math.random(), // 加权随机,避免全是同类
    }))
    .sort((a, b) => b.priority - a.priority)
    .slice(0, quota);
}

标注后的分流结果长这样(真实样子,不是抽象分类):

requestId 原始反馈 标注结论 坏因 去向
rq-8101 转人工 检索没命中退货政策文档 训练集(补正确答案后)
rq-8102 点踩 事实错:承诺了不支持的「7 天无理由」 训练集(偏好对,见 4.2)
rq-8103 复制 训练集候选 + 黄金集候选
rq-8104 retry 有条件好 首答啰嗦,追问后才简洁 训练集(偏好对:简洁版 > 啰嗦版)
rq-8105 无反馈 命中兜底话术「抱歉我不明白」 评测集红线组(禁兜底场景)

2.2 黄金集

黄金集 /ˈɡoʊldən set/ (Golden Set,黄金评测集)是人工确认过标准答案或质量等级的小规模精选集,通常几百到几千条。它是整个飞轮的「度量衡」:

  • 覆盖核心业务场景与高价值长尾,而不是随机抽样;
  • 有明确判分标准:可自动判(精确匹配 / 断言)的进自动评测,只能人工/模型判的单独分组;
  • 禁止混入训练集——黄金集一旦被训练污染,所有指标都会虚高;
  • 定期由领域专家审订,但频繁改动会让版本间指标不可比,改动要发版本号。

落地示例:一条黄金集样例的完整形态(YAML /ˈjæməl/ ,一种配置文件格式):

id: gs-cs-0007
version: v2026.08.3
scenario: 售后-退货政策
question: "买了个电饭煲用了10天能退货吗?"
reference: |
  可以。电器类目支持 15 天无理由退货,需商品完好……
  (依据:《电器类目售后政策》第 3 条)
checks:
  - type: contains        # 自动判:答案必须出现关键事实
    value: "15 天"
  - type: not_contains    # 自动判:不得出现历史坏答案的错误承诺
    value: "7 天"
  - type: llm_judge       # 模型判:语义层面打分
    rubric: 须引用政策来源,且不得承诺政策外条款
source: rq-8103            # 可追溯到来源日志

要点:checks 里自动判(字符串断言)与 llm_judge(模型评)分层——能自动判的绝不交给模型,减少评测噪声。

2.3 回归

回归测试 /rɪˈɡreʃn test/ (Regression Test,回归测试):每次改动(提示词、检索参数、模型版本)上线前,在黄金集上重跑并对比指标。要点:

  • 改进目标场景的指标时,盯住其他场景指标是否回退(跷跷板效应);
  • 指标波动超过噪声阈值才认定有效——评测模型打分本身有随机性,先用同一集跑两次估出噪声带;
  • 回归报告进变更记录,与版本绑定。

落地示例:噪声带测定与回归判定(Node.js + TS):

interface RunResult {
  version: string;
  passRate: number; // 黄金集通过率
}

// 同一版本连跑 3 次,估出评测噪声带
function noiseBand(runs: RunResult[]): number {
  const rates = runs.map((r) => r.passRate);
  return Math.max(...rates) - Math.min(...rates);
}

// 新版提升超过噪声带 1.5 倍才认定真实有效
function isRealImprovement(baseline: number, candidate: number, band: number): boolean {
  return candidate - baseline > band * 1.5;
}

代入「小智」的真实数字:

  • v1.3 同版连跑三次:82.0% / 82.3% / 82.6% → 噪声带 0.6pp,判定门槛 = 0.9pp;
  • v1.4(改了退货政策的检索策略)跑出 84.8% → 提升 2.8pp > 0.9pp,认定有效
  • 对照组:另一场景「物流查询」从 88.1% 掉到 85.9% → 跷跷板回退,回归失败,不发全量

工具上不必自研评测框架: promptfoo 类开源工具原生支持 YAML 用例 + 断言 + 模型评分,把上面的 checks 直接翻译成它的配置即可接入 CI。

2.4 分流判据:进训练集还是评测集

这是文首第一个学习问题,判据一句话:反映「目标行为」的样本进训练集,衡量「行为底线」的样本进评测集

样本类型 去向 理由
高质量问答(确认正确、有代表性) 训练集 教模型「该怎么做」
差评样本 + 修正答案 训练集(成对) 构成偏好对比,教「哪种更好」
黄金集候选(判分标准明确) 评测集 只能考,不能练
涉及安全红线的拒答案例 评测集(红线组) 底线必须每次考试,不能只靠学过
极端长尾、标注拿不准 弃置或仲裁池 噪声样本既教坏训练也污染评测

经验法则:评测集考验稳定性,训练集改变行为。拿不准时先只进评测集——错误进训练集的代价(学坏)远大于错误进评测集(考官变难)。

三、合成数据:用途与污染风险

合成数据 /sɪnˈθetɪk ˈdaɪtə/ (Synthetic Data,合成数据):用模型生成的数据(或程序构造的数据)代替/补充人工数据。

3.1 用途

场景 做法
冷启动 没有线上日志时,用强模型按业务场景批量生成指令-回答对,先训出可用版本
偏好对构造 同一 prompt 采样多个回答,用强模型或规则排序,生成成对偏好数据(如 Self-Instruct /self ˈɪnstrʌkt/ 思路的延伸)
边界与红线扩充 程序化生成攻击样本、格式极端样例,补足真实日志中的稀缺分布
数据增广 同一事实改写为多种问法,提升鲁棒性

3.2 合成数据的生成原则

  1. 种子真实:从真实业务日志/文档出发生成,不要让模型凭空想象场景;
  2. 多样优先:prompt 层面控制话题、长度、角色、格式的多样性,防止生成分布塌缩到几种模板;
  3. 质量过滤:生成后过一遍质量关(规则校验 + 强模型打分),宁缺毋滥;
  4. 留出真实集:永远保留一批纯人工/纯线上数据作为最终评测的「真实分布锚点」,合成指标只能参考。

落地示例:用多样性轴组合做冷启动生成(Node.js + TS,OpenAI 协议):

import OpenAI from "openai";

const client = new OpenAI();

interface Axis {
  name: string;
  values: string[];
}

// 多样性轴:每条样本从各轴随机取值组合,从源头防模板化
const AXES: Axis[] = [
  { name: "场景", values: ["退货", "换货", "开发票", "查物流", "改地址"] },
  { name: "情绪", values: ["平静", "着急", "愤怒", "含糊其辞"] },
  { name: "表达", values: ["口语", "书面", "多错别字", "多轮追问"] },
  { name: "身份", values: ["新用户", "会员", "代下单的家属"] },
];

function pick<T>(arr: T[]): T {
  return arr[Math.floor(Math.random() * arr.length)];
}

async function genQuestion(): Promise<string> {
  const combo = AXES.map((a) => `${a.name}=${pick(a.values)}`).join(",");
  const resp = await client.chat.completions.create({
    model: "gpt-4o",
    messages: [
      {
        role: "system",
        content: "你是电商客服语料构造器。根据给定组合生成一条用户真实提问,只输出问题本身,不要回答。",
      },
      { role: "user", content: `生成一条用户问题:${combo}` },
    ],
  });
  return resp.choices[0].message.content ?? "";
}

生成的 2000 条里,三道过滤关通常只剩 1000~1500 条可用:

interface SynthSample {
  q: string;
  a: string;
  judgeScore: number; // 强模型 1-5 打分
}

async function pass(s: SynthSample): Promise<boolean> {
  // 第 1 关:规则(便宜,先跑)
  if (s.a.length < 20 || /作为一个AI|很抱歉/.test(s.a)) return false;
  // 第 2 关:查重(向量相似度 > 0.92 视为模板重复)
  if (await tooSimilar(s.q, existingCorpus)) return false;
  // 第 3 关:强模型打分,>= 4 分才留
  return s.judgeScore >= 4;
}

3.3 污染与偏见放大:何时发生

这是文首第二个学习问题。合成数据放大偏见与污染的四个典型时机:

  1. 自我循环(self-consumption):用模型 A 的输出训练模型 B,再用 B 的输出训练 C——尾部分布逐代丢失,多样性单调下降,最终 模型坍缩 /ˈmɒdl kəˈlæps/ (Model Collapse,模型坍缩);
  2. 种子带偏:生成种子里某类人群/某类问法占绝对多数,生成会放大该倾斜(模型倾向于把多数模式推得更极端),下游在少数分布上表现恶化;
  3. 评测集泄漏:合成数据与黄金集同源(同模型、同场景、同模板)时,评测变成「考自己做过的题」,指标虚高,飞轮失去导航能力;
  4. 风格锁定:合成数据全是同一个生成模型的文风,训练后模型输出千篇一律,真实用户偏好反而下降。

判定口诀:合成数据只用于「扩大数量与覆盖」,不用于「定义对错标准」。对错永远来自人工确认的真实样本(黄金集)。

四、与 SFT / DPO / 偏好数据的衔接

飞轮产出最终要落到训练范式上,三种衔接方式:

4.1 三个数据形态

数据形态 长什么样 训什么
指令对 (prompt, 好回答) SFT /ˌes ef ˈtiː/ ( Supervised Fine-Tuning ,监督微调)
偏好对 (prompt, 好回答, 差回答) DPO /ˌdiː piː ˈoʊ/ ( Direct Preference Optimization ,直接偏好优化)
排序列表 (prompt, 多个回答 + 排序) RLHF /ˌɑːr el eɪtʃ ef/ ( Reinforcement Learning from Human Feedback ,基于人类反馈的强化学习)奖励模型

4.2 飞轮数据如何变成训练数据

  • 好例 → SFT:线上确认的高质量问答,按训练格式整理(含 system、工具调用轨迹要完整);
  • 好差对 → DPO:同一请求的「用户采纳版本 vs 修订前版本」「差评样本 vs 修正样本」天然成对,是 DPO 数据最廉价的来源;
  • 标注排序 → RM /ˌɑːr ˈem/ ( Reward Model ,奖励模型):标注团队对同请求多回答排序,训练奖励模型,适合关键场景做 RLHF;
  • 合成 → 冷启动 SFT 为主:合成数据质量不可控,优先只用于 SFT(有标准答案约束),避免直接做偏好对(排序本身可能继承生成模型的偏见)。

落地示例:rq-8102(点踩案例)如何变成一条 DPO 数据——rejected 直接取线上 v1 的原始错误回答,chosen 取客服修正后的答案,不造数据,改数据

{
  "prompt": "电饭煲用了10天,可以以\"不想要了\"为由退货吗?",
  "chosen": "可以。电器类目支持15天无理由退货,\"不想要了\"属于有效理由,需保持商品完好,依据《电器类目售后政策》第3条。",
  "rejected": "可以哦,收货7天内不想要了都可以退的呢~"
}

配套说明字段(进训练框架前展开成列):source: rq-8102failure_mode: 事实错-政策期限annotator: 客服-王某。累积 300~500 对这样的数据,足够做一次 LoRA /ˈlɔːrə/ ( Low-Rank Adaptation ,低秩适配)轻量 DPO:

# LLaMA-Factory 训练配置(节选)
stage: dpo
model_name_or_path: Qwen/Qwen2.5-7B-Instruct
dataset: cs_dpo_v1        # 上面那批偏好对
lora_rank: 16
learning_rate: 5.0e-6
num_train_epochs: 2

4.3 迭代节奏

  1. 冷启动:合成数据 + 少量人工 → SFT v1;
  2. 上线:埋点收集反馈,跑通「日志 → 标注」;
  3. 每周:差评样本修复成偏好对,攒够一批做 DPO;
  4. 每月:黄金集审订扩容,回归全量指标,决定是否触发更大动作(换基座模型、大规模微调)。

五、端到端落地示例:四周迭代

把前面所有零件拼成「小智」的四周排期——人少(1 算法 + 1 工程 + 1 名义兼职标注)也能转起来的最小版本:

动作 产出 用到的工具
W1 多样性轴生成 2000 条 → 三道过滤剩 1200 条 → SFT 训出 v1(LoRA) 可上线的基础版 OpenAI 协议生成 + LLaMA-Factory
W2 反馈埋点上线;黄金集 v1(300 条,每条含 YAML 判分) 日志表 + gs-cs-v1 ClickHouse + promptfoo
W3 第一次人工标注:加权采样 500 条 → 标出 80 个坏例 → 修复成 60 对偏好数据 cs_dpo_v1 数据集 Label Studio
W4 DPO 训练 v1.1 → 黄金集回归(82.4% → 84.8%,过噪声带门槛)→ 灰度 10% 流量 v1.1 灰度上线 LLaMA-Factory + promptfoo

工具栈速查(全部有开源实现):

环节 工具 备注
日志存储 ClickHouse / PostgreSQL 按天分区,反馈事件与请求绑定
标注平台 Label Studio、Argilla 坏因打标 + 偏好排序都支持
评测回归 promptfoo、DeepEval、Ragas(RAG 场景) 接 CI,每次改提示词自动跑
训练 LLaMA-Factory(SFT/DPO/LoRA 一条龙)、TRL 单卡 A10 也能跑 7B 级 LoRA
调度 自写 TS 脚本 + 定时任务(GitHub Actions / cron) 采样、过滤、对账都是批处理脚本

四周之后的常态节奏回到 4.3:每周一批偏好数据,每月一次黄金集审订——飞轮转起来的标志是:修复一个坏例的成本从「改代码」降到「攒一条数据」

自检清单

  • 反馈埋点有明确的语义定义,产品与工程达成一致;
  • 黄金集与训练集物理隔离,黄金集有版本号;
  • 每次改动跑回归,噪声带已测定;
  • 合成数据有真实种子、多样性控制、质量过滤三道关;
  • 保留纯真实数据的最终评测锚点;
  • 飞轮闭环周期以周为单位。

本文缩写

缩写 音标 全拼 中文
DPO /ˌdiː piː ˈoʊ/ Direct Preference Optimization 直接偏好优化
LoRA /ˈlɔːrə/ Low-Rank Adaptation 低秩适配
PII /ˌpiː aɪ ˈaɪ/ Personally Identifiable Information 个人身份信息
RLHF /ˌɑːr el eɪtʃ ef/ Reinforcement Learning from Human Feedback 基于人类反馈的强化学习
RM /ˌɑːr ˈem/ Reward Model 奖励模型
SFT /ˌes ef ˈtiː/ Supervised Fine-Tuning 监督微调
YAML /ˈjæməl/ YAML Ain't Markup Language 一种配置文件格式

参考资料