录音逐字稿清洗
安装方法
- 先问使用者用的是什么 AI 工具,选项按这个顺序列:1. WorkBuddy 2. Claude Code 3. Codex 4. 其他(用户直接说工具名),回答记作 X
- 只安装给 X,不安装或链接其他任何工具:
-
X 是 WorkBuddy:把本技能下载后放进
~/.workbuddy/skills/(WorkBuddy 的技能目录;skills CLI 不支持 WorkBuddy,由你直接放置) -
X 是其他工具:
npx -y --registry=https://registry.npmmirror.com skills add https://modelscope.cn/skills/sunny31221/data-cleaning-buheliwa -a X -y(不带
-a会安装给本机所有检测到的工具,禁止;--registry为 npmmirror 镜像,保证国内不翻墙也能拉到 CLI) -
X 不在上述范围时,按该工具自身的技能机制安装,装完确认 X 能识别本技能即可
-
- 装完后询问是否现在使用本技能
两条最高原则
- 能自己判的,自己判:凡有判据可依的判断(决策树、术语表、映射表、清洗规则、命名规范),AI 必须自己裁决,不把判断推给用户确认。把能自己判的问题交给用户,既耗用户精力,又浪费双方时间。
- 批量清洗不打断:批量场景下用户没精力逐条确认。所有需要用户知情的项,汇入一份批次报告;用户不回复即视为通过。用户精力只留给真正重要的判断。
「不违」与「唯循」的分野:不违 = 遇到不确定就列确认单,把责任推出去;唯循 = 取最安全默认先处理完,把结果和理由如实写入报告。
前置条件
逐字稿要放在 AI第二大脑/03_知识库/02_沟通录音/ 下,文件名以 -待清洗 结尾(如 20260917-选题会-甲乙-待清洗.md)。清完去掉后缀即为正式稿。
可以帮你做什么
一条链路:预检 → 群像检测 → 两层清洗(2a 脚本 + 2b 语义)→ 说话人识别管线 → 术语/映射回写 → 去后缀 → 7 道闸门验证,三级裁决 + 批次报告制全程零打断。详见下方工作流程。
不适用场景
- 已清洗文件(frontmatter 含 type/summary/tags)→ 不重复处理
- 非逐字稿格式(纯笔记、公众号文章)→ 走其他管道
- 外部人物/路人发言为主 → 按 Step 1 判定直接关闭
- 文件名没有
-待清洗后缀 → 说明不是待洗稿,不处理
快速参考
| 操作 | 做法 |
|------|------|
| 洗一篇 | 告诉 Skill 文件名或路径(必须是 *-待清洗.md) |
| 批量洗 | "批量洗 N 篇"(上限 3 篇/批,超出拒绝) |
| 查看待清洗 | find 03_知识库/02_沟通录音/ -name "*-待清洗.md" |
| 群像模式 | Step 1 自动检测:实际在场人数 > ASR 通道数 |
| 查看批次报告 | 读 03_知识库/02_沟通录音/清洗报告-YYYYMMDD.md(每批一份) |
| 预览变更 | "预览清洗 <文件名>" 输出 diff 不实际修改 |
| 脚本入口 | helpers.py <cmd> — 所有确定性操作统一入口 |
| helpers.py 命令 | 用途 | 步骤 |
|------|------|------|
| extract-date <f> | 从文件名提取日期 | Step 0 |
| validate-filename <f> | 检查命名规范 | Step 0 |
| clean-transcript <f> | 2a 机械清洗 | Step 2a |
| timestamps | 生成 created / id | Step 4 |
| verify-all <f> | 一键 7 道闸门 | Verify |
| append-glossary <正> <误> <释义> <分类> [来源] | 追加术语(自动查重 + 四列留痕) | Step 5 |
| append-mapping <事件> <编号> <姓名> | 追加说话人映射(自动查重) | Step 5 |
| report-init <项目> [date] | 初始化批次报告(幂等,stdout 输出路径) | Step 7 |
| report-add <报告> <来源> <位置> <问题> <AI处理> <理由> | 追加报告项(自动编号) | Step 7 |
| report-finalize <报告> | 生成统计块(幂等) | Step 7 |
三级裁决制(核心)
所有清洗判断分三级,只有 L3 进报告,且全程不打断用户:
| 层 | 判什么 | 判据 | 谁判 | 行为 | 用户可见 | |---|---|---|---|---|---| | L1 | 确定性操作 | 脚本规则 | 脚本(helpers.py) | 直接执行 | 仅计数 | | L2 | 判断性操作 | 决策树/术语表/映射表/规则文件 | AI | 直接执行 + 写清洗备注 | 批次报告统计 | | L3 | 影响信息且无判据 | 见下方判据 | AI 取最安全默认 | 保留原文 + 记入报告 | 报告一条,沉默即通过 |
L3 判据(同时满足两条才进报告):
- 该判断会改变文本传递的信息
- AI 无判据可依(查完决策树、术语表、映射表、上下文后仍无法定夺)
L2 直接裁决、不进报告的情形(即使"不确定",也自己定,不问用户):
| 情形 | 裁决 | 备注 | |------|------|------| | 口语词删留不确定 | 保留原文 | 写进清洗备注(保留原文不伤信息) | | ASR 变体不确定 | 保留原文 | 写进清洗备注 | | 说话人信号不足 | 保留编号 + 理由 | 安全默认:不改归属 | | 外部人物/路人 | 直接关闭 | 不写任何清单 | | 命名事件提炼 | AI 按内容 + ≤12 字规则直接定 | 判据充足 | | 是吧/对吧不确定 | 保留整句 | 决策树第三条:默认保留 |
L3 示例(进报告,AI 已按最安全默认处理):
- 数字疑似转录错误(铁律不改值;报告让用户知情)
- 一句话删与不删直接影响结论,且无判据(默认保留,报告说明)
- 疑似系统性 ASR 错误波及多处(AI 判断需人知情)
批次报告制(沉默即通过)
每批清洗结束,AI 用脚本生成一份报告,不逐条弹确认:
- 位置:
03_知识库/02_沟通录音/清洗报告-YYYYMMDD.md(同日多批复用同一文件) - 内容:报告项表(来源/位置/问题/AI 处理/理由)+ 统计块
- 规则:用户不回复 = 全部通过。要改哪条,回复「改 #编号」;确认无误回复「通过」
- 报告是知情文件,不是待办清单:AI 已按最安全默认处理完,不是等用户拍板才动
- 用户回复「改 #N」后:AI 修正对应处 → 回写参考文件(Step 5)→ 在报告中标注已改
报告寿期:报告是过程材料不是资产——用户回复完、或事后对账完成,就可以归档或删掉,不必长期堆在沟通录音目录里。
- 报告的价值有两段:① 用户回复「改 #N」的窗口期 ② 事后对账
- 删档时必须同步处置指向它的指针:按先例标注「已删档,要点见本板 + plan 编号」,不得留死链
- 判据:报告是过程材料不是资产;不删则无限堆积(2026-09-15 前清洗报告全库积了 9 份、一份未删)
工作流程
Step 0: 预检
find 03_知识库/02_沟通录音/ -name "*-待清洗.md" | sort
- 扫描
03_知识库/02_沟通录音/下所有待清洗的逐字稿 - 读每个文件 frontmatter:有
type+summary+tags→ 跳过(已清洗) - 文件只有
source字段(分类阶段写入)→ 待清洗,继续 python3 <本技能目录>/scripts/helpers.py extract-date <file>→ YYYY-MM-DDpython3 <本技能目录>/scripts/helpers.py validate-filename <file>→ 检查命名规范- 超长文件(> 1500 行)→ 标记单独处理,走大文件模式
归属:录音稿统一放在 03_知识库/02_沟通录音/ 下,无需 AI 判定。
Step 1: 群像检测 + 外部判定
- AI:统计对话中实际在场人数 vs ASR 标注的说话人通道数
- 人数 > 通道数 → 触发群像 → 加载 group-mode.md
- 标准模式 → 继续 Step 2a
外部讲座判定(命中任一信号 → speakers: ['外部讲座'],人物不加术语表):
- 单人讲座,内容为政策/技术/法规宣讲
- 关键词含"培训""授课""讲座""一号文件""政策解读"
- 说话人自称体制内身份("我们厅里""我们局里""市农业农村局")
- 多人但均为同一培训活动学员/讲师
Step 2a: 脚本机械清洗(L1,确定性操作)
python3 <本技能目录>/scripts/helpers.py clean-transcript <file>
脚本自动执行(不消耗 AI 判断,不询问用户):
- 说话人标签归一化:
Speaker N→说话人 N(纯格式规整,只要有就归一) - 说话人标签替换为真名(🔴 2026-09-14 用户拍板:正文用真名):由文件名推导事件名(
YYYYMMDD-事件-…,去掉日期前缀与末尾人物段),在03_知识库/02_沟通录音/_speaker-mappings.md里该事件已有映射时,把行首「说话人 N 」就地换成真名,时间戳与行尾空格逐字保留。未映射的编号一律保留、不猜;文件名无日期前缀则整体不查映射。姓名取「(」前主名(张三(老张)→张三)。映射表两种行首格式(说话人N/ 裸N)都认 - 术语纠错:从
03_知识库/02_沟通录音/_术语表.md解析正则模式 → ASR 变体替换为正确写法(长匹配优先) - 口水词初筛:仅删除 100% 确定的填充(单独出现的 嗯/呃/啊/哦/诶/唔/唉/啧 在行首)
- 输出:中间文件(
-stage2a.md)+ 统计报告(替换/纠错/删除各多少处)
分工:2a 只做「映射已知」的替换(确定性 → 脚本干)。新录音的映射要靠内容锚点识别(判断性 → Step 3 由 AI 判定),判定后经
append-mapping回写,下次同事件即由 2a 自动完成。 废止:2026-09-13 曾有一条「正文保留「说话人 N」编号」的注释,其来历是旧实现匹配**1:**格式(全库不存在)导致替换从未生效、speaker_fixes恒为 0,当时未修代码而把现状记成了约定。该注释已删,行为已由回归用例 9/10 守住。
Step 2b: AI 语义清洗(L2,判断性操作)
在 2a 输出上逐段读 + 逐句判断。加载 cleaning-rules.md(127 行,全量)。
术语替换:Step 2a 已做大部分 ASR 纠错,AI 遇到残留变体时 grep "<关键词>" 03_知识库/02_沟通录音/_术语表.md 搜索确认,不全量加载。
必须做的事:
-
是吧/对吧决策树(详见 cleaning-rules.md 第一节,严格按三步判定:连续标记→整组删、无信息→整句删、有信息→整句留。禁止截断)
-
上下文判断(L2 直接裁决,不问用户):
- 口语填充词(就是说/那个/反正/怎么说呢/那种/这样子/的话)→ 逐条判断,不确定 → 保留原文 + 备注
- 主观表达(我觉得/可能/大概/好像/应该)→ 不删
- 说话人特有口语习惯 → 不删(删了反而失真)
-
数字保护铁律:不修改任何数字、日期、金额、百分比、数量的值。疑似转录错误 → 保留原文 + 进批次报告(L3)
-
术语替换:顺序严格为 人名→地名→概念(长匹配优先)
-
清洗备注:在输出文件末尾加
## 清洗备注段落,列出关键决策:- 是吧/对吧判定摘要(删除 X 处 / 保留 Y 处,各举一例)
- 保留项说明(哪些口语词/ASR 变体因不确定而保留)
- 说话人识别依据(匹配来源 + 置信度)
Step 3: 说话人处理
结构化查找管线(严格按序,禁止跳过直接猜测):
grep "<对话事件名>" 03_知识库/02_沟通录音/_speaker-mappings.md→ 只加载该对话的映射行(3-5 行),不全量加载- 已有确认映射 → 此处无需再动手:Step 2a 的 1b 步已在机械层替换完正文标签(🔴 2026-09-14 起正文用真名),只需核对替换结果与计数
- 新录音无匹配(2a 未替换,正文仍是「说话人 N」) → 先读
03_知识库/02_沟通录音/_speaker-profiles.md的 快速匹配索引锁定候选人 → 只读候选人的完整画像做特征匹配。判定成立后:在 2b 里把正文标签就地换成真名(保留时间戳与行尾空格),再经append-mapping回写映射表,下次同事件即由 2a 自动完成 - 特征匹配仍不确定 → L2 裁决:保留编号 + 标注不确定理由,不进报告(安全默认,等待以后有映射再回写)
- 群像模式 → 加载 group-mode.md,保留编号,产出「在场人物块」
- 禁止行为:从对话内容猜测说话人身份(如"说话人1谈到某个项目→就断定是某人")
多说话人交叉验证(2+ 已知说话人、或同一对话分多段录制时,必须执行):
- 定位内容锚点:为每位说话人找 ≥2 个不可辩驳的标记:只有此人知道/会说的信息。
- 信息锚点:谁提了自己的经历/项目/关系?("我同学老张"→据此锁定说话人,"我的访谈提纲"→框架制作者)
- 行为锚点:谁在主导访谈/提问/收尾?("好的那就这样"→收尾者)
- 关系锚点:谁提到了谁?(谁提到了其他人、谁被指为某件事的当事人)
- 禁止性格推断:不以"此人语气理性/感性"作为判定依据。性格是猜测,锚点是事实。
- 一处错必反查整段:发现单个标签错误时,必须追问"这一段的其他标签是否也反了?"多说话人场景中两人标签系统性互换是高频错误,禁止只修被指出的那一处。
说话人归属的 L3 判定:归属改变会改变文本传递的信息(谁说了什么),且管线走完仍拿不准时 → 保留编号(安全默认)+ 进批次报告,理由写清"管线走完仍不确定,建议后续按映射表回写"。
Step 4: 产出与验证
# 1. 确定性字段
python3 <本技能目录>/scripts/helpers.py timestamps # → created / id
python3 <本技能目录>/scripts/helpers.py extract-date <file> # → YYYY-MM-DD
# 2. 项目归属:从文件路径读取(位置即决策)
# 文件在 03_知识库/02_沟通录音/ 下,归属即该目录
# 3. AI:写入完整 YAML frontmatter(规范见 references/frontmatter-standard.md)
# 必填: date created id type source summary tags word_count
# 清洗特有: speakers cleaning_stats
# 4. 一键验证
python3 <本技能目录>/scripts/helpers.py verify-all <file> # 7 道闸门
frontmatter 字段约束(L1 清洗必填,完整定义见 references/frontmatter-standard.md):
id:YYYYMMDD-HHMMSS(helpers.py timestamps产出)tags:1-4 个,严禁超过 4 个type:必须使用映射表值(调研访谈|分享会|工作讨论|外部讲座|群像讨论|项目策略|方法论展示|战略反思|知识传授|合作洽谈|产品技术汇报)source:完整相对路径含目录层级,指向原始录音文件speakers:已确认说话人列表。外部讲座 =['外部讲座']cleaning_stats:两层操作计数
cleaning_stats:
"2a": "speaker_mappings:3, term_fixes:12, filler_removed:8"
"2b": "shiba_fillers_deleted:5, shiba_fillers_kept:3, uncertain_kept:2"
🔴 「2a」段必须照抄脚本输出,不得手写(2026-09-13 P-4 起):clean-transcript 跑完会在
stdout 打印一段可整段粘贴的 cleaning_stats,那是真实替换处数(perl 内 $r ne $& 计数,
回归用例 6 守着)。AI 只负责按实际语义操作填「2b」段。
⚠️ 存量不可信:2026-09-13 之前落盘的
cleaning_stats.2a.term_fixes为人工填写, 已实测失真(某篇写2而实际替换 9 处并附带损伤),不可作为 C-1「替换数异常」信号依据。 需要真实计数时,对该篇重跑一次clean-transcript取脚本输出。
Step 5: 回写参考文件
清洗完成后,用 helpers.py 回写(自动查重,不消耗 AI token):
# 新 ASR 变体 → 追加到术语表(自动查重+合并变体+写「加入日期/来源」列)
python3 <本技能目录>/scripts/helpers.py append-glossary <正确写法> <ASR变体> <释义> <分类> [来源标记]
# 分类: 人名 | 地名/组织 | 项目/概念
# 来源标记(可选,默认「AI 清洗回写」):用户确认 | AI 清洗回写
# → 术语表第 4 列写入 `YYYY-MM-DD 来源标记`,供出问题时追溯是谁加的(B-2 变更留痕)
# 新说话人映射 → 追加到 speaker-mappings(自动查重)
python3 <本技能目录>/scripts/helpers.py append-mapping <对话事件> <编号> <真实姓名>
- 报告:AI 仅汇总本批次新增 X 条术语、M 条映射(数值来自 helpers.py 输出)
Step 6: 完成标记
# 去掉 -待清洗 后缀,标记为正式文件
mv "03_知识库/02_沟通录音/<文件名>-待清洗.md" "03_知识库/02_沟通录音/<文件名>.md"
- 源文件留在原处,清洗不移动、不删除它
- 去后缀 = 清洗完成,文件成为沟通录音目录下的正式稿
Step 7: 批次报告(每批一份,不打断)
本批全部文件清洗完成后执行:
# 1. 初始化(幂等:同日多批复用同一文件)
REPORT=$(python3 <本技能目录>/scripts/helpers.py report-init <项目>)
# 2. 追加报告项(只有 L3 才加;参数含空格时整体加引号)
python3 <本技能目录>/scripts/helpers.py report-add "$REPORT" <来源文件> <位置> <问题> <AI处理> <理由>
# 3. 收尾统计(幂等)
python3 <本技能目录>/scripts/helpers.py report-finalize "$REPORT"
- 无 L3 项时也执行 init + finalize,报告为空项 + 统计 0 条,告知用户"本批零报告项"
- 报告生成后,在回复里一句话告知用户报告位置,等待回复「通过」或「改 #N」,不逐条追问
Verify: 管道闸门(强制执行)
产出后逐项检查,不通过不进下一阶段:
python3 <本技能目录>/scripts/helpers.py verify-all <file>
| # | 检查项 | 不通过时 |
|---|--------|---------|
| 1 | frontmatter 必填字段完整(含 cleaning_stats) | 补全后重新验证 |
| 2 | id 格式 YYYYMMDD-HHMMSS | 修正 id 字段 |
| 3 | tags 数量 ≤ 4 | 删减到 4 个以内 |
| 4 | type 在映射表中 | 修正 type 值 |
| 5 | source 完整路径 + 链路可追溯 | 补全完整路径 |
| 6 | 术语表 / speaker-mappings 已回写(有新增时) | 执行 Step 5 回写 |
| 7 | filename 符合命名规范 + -待清洗 后缀已去除 | 修正文件名或执行 Step 6 |
✅ PASS 全部通过。任一项失败 → 标注 ⚠️ verify-failed,AI 报告修复方案。仅验证失败时才加载 references/pipeline-gates.md 查阅 verify-chain 逻辑和失败处理流程。
关键原则
- 自我承担 — 凡有判据的判断(决策树/术语表/映射表/规则),AI 自己裁决,禁止推给用户确认
- 情境共融 — 批量清洗零打断:L3 项进批次报告,用户不回复即视为通过
- 数字保护铁律 — 不修改任何数字、日期、金额、百分比的值
- 是吧/对吧决策树 — 连续标记→整组删、无信息→整句删、有信息→整句留。禁止截断
- 不确定不改 — 疑似 ASR 错误保留原文 + 备注(L2),保留原文不伤信息
- 不编造说话人 — 无音频对照不猜测归属,结构化管线走完仍不确定则保留编号+理由
- 位置即决策 — 项目归属由文件所在目录决定
- 术语表自增长 — 每次清洗后回写新发现(迭代收敛:前 30 篇量大,50 篇后稳定)
- 先 2a 后 2b — 脚本做完确定性的,AI 做完需要判断的,两层接力不是二选一
- 脚本不手动 — 确定性操作一律走 helpers.py,AI 不手工做
- 分类先于清洗 — 新文件必须先经分类路由,清洗只处理
*-待清洗.md - 清洗必留备注 — 每篇产出末尾写
## 清洗备注,记录关键决策和理由 - 锚点优于印象 — 多说话人场景用内容锚点交叉验证,禁止性格推断。发现一处标签错误必反查整段是否有系统性互换
- 报告不追问 — 批次报告生成后一句话告知位置,不逐条追问;用户回复「改 #N」才动手改
批量限制
| 维度 | 规则 | |------|------| | 上限 | 3 篇/批(硬限制,超出必须明确拒绝。上下文约束,与确认方式无关) | | 超长文件 | 单篇 > 1500 行单独处理 | | 共享上下文 | 术语表 + 清洗规则 + speaker-mappings 只加载一次 | | 并行策略 | 3 篇独立处理,不设金丝雀抽检 | | 报告频率 | 每批一份报告,批次结束统一生成,不逐篇生成 |
与分类技能的衔接
详见 (见 references/ 下的规则文件)(按需加载)。核心规则:分类阶段写 source 字段+路由到项目+加 -待清洗 后缀;清洗阶段从文件路径读项目归属(位置即决策)、补全全部 frontmatter、完成后去后缀。
常见错误
详见 references/troubleshooting.md(按需加载:仅清洗出错或 verify 失败时查阅)。高频错误速查:
| 错误 | 正确 |
|------|------|
| 把能自己判的推给用户确认 | 三级裁决:L1 脚本判、L2 AI 判、只有 L3 进报告(原则 1) |
| 逐条弹确认打断批量清洗 | 批次报告制:每批一份,沉默即通过(原则 2) |
| 口语词不确定就进报告 | L2 保留原文 + 备注即可,不进报告 |
| 说话人信号不足就进报告 | L2 保留编号 + 理由;归属影响信息时才 L3 |
| 是吧/对吧截断留残字 | 整句删或整句留,禁止截断 |
| 批量超 3 篇不拒绝 | 硬拒绝 + 告知上限 |
| 术语替换乱序 | 人名→地名→概念,长匹配优先 |
| 说话人从内容猜测 | 管线走完仍不确定→保留编号+理由 |
| 映射表没有该事件就自行猜真名 | 保留编号 + 判定成立后经 append-mapping 回写,下次 2a 自动替换 |
| 替换真名时删掉时间戳或行尾空格 | 只换行首标签,时间戳与行尾空格逐字保留(2a 已自动做,2b 补做时同理) |
| 多说话人标签系统性互换 | 一处错反查整段,用锚点交叉验证 |
| 产出无清洗备注 | Step 2b 强制写 ## 清洗备注 |
| 报告生成后逐条追问用户 | 一句话告知报告位置,等待「通过」或「改 #N」 |
| AI 手工 mv/拼路径/写报告 | 一律走 helpers.py(含 report-init/add/finalize) |
参考来源
| 文件 | 内容 | 何时读 | |------|------|--------| | references/frontmatter-standard.md | YAML 字段定义、type 映射表、word_count 标准 | Step 4 写 frontmatter 时 | | references/pipeline-gates.md | 质量闸门定义 | Verify 时 | | cleaning-rules.md | 口水词清单、术语替换顺序、数字保护细则、是吧/对吧决策树 | Step 2b 全量加载(127行) | | group-mode.md | 群像模式完整流程+自查清单(65行) | Step 1 触发群像时 |
用户数据(存在项目里,不在技能目录。每个人各攒各的,技能升级覆盖不到):
| 文件 | 内容 | 何时读 |
|------|------|--------|
| 03_知识库/02_沟通录音/_术语表.md | 三列格式:正确写法 / 常见变体 / 释义 | Step 2b grep 搜索,Step 5 回写时加载 |
| 03_知识库/02_沟通录音/_speaker-mappings.md | 已确认说话人映射表 | grep 对话名搜索,不全量加载 |
| 03_知识库/02_沟通录音/_speaker-profiles.md | 核心人物身份信号 + 语言信号 | mappings 无匹配时:先读快速匹配索引锁定候选人,再只读候选人画像。禁止全量加载 |
三份的模板在技能目录 references/ 下,首次运行时脚本自动复制一份到项目里;攒词条、攒映射都攒在项目那一份上。
Scan to join WeChat group