Skip to content
dsh.fish
Skill

audio-read

读懂音频内容——转写语音、分析音高音调、识别语音情感(9 类)、辨认鸟种与动物、识别环境声与城市噪音、分析歌曲的节奏调性。当用户说「听一下这段录音 / 这段音频说了什么 / 帮我转写 / 会议录音整理 / 这是什么声音 / 是什么在叫 / 这是什么鸟 / 他说话什么情绪 / 分析这首歌」等需要理解音频文件的请求时使用。全部本地运行,不上传云端。

Source
liyixuan201211
License
NOASSERTION
Updated
Updated 10 hours ago

Readme

# audio-read

> 让 AI Agent 真正"听懂"音频 —— **全部本地运行,不上传云端,不用大模型**。

一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)技能:
把音频转成 Agent 能推理的文字、数字和标签,覆盖语音、音乐、环境声、鸟种与情感。

## 为什么不用大模型

一个音频大模型(如 Qwen3-Omni-30B)要 ~17GB,冷启动 30–90 秒,
输出还是不可复现的自由文本。

这个项目走另一条路:**把"一个大模型"换成"一把小尺子"**——
每条能力轴用一个专用小工具,按需组合:

| 层 | 做什么 | 引擎 | 体积 | 耗时 |
|---|---|---|---|---|
| **L1 声学画像** | 音高/音调走向/音域/起伏、语速、能量与停顿、节奏、调性、音色 | librosa(纯 DSP) | 0 | 2–4s |
| **L2 转写** | 语音 → 文字(分段时间戳;`--timestamps` 可到词级) | Qwen3-ASR-0.6B(MLX) | 1.9GB | ~10× 实时 |
| **L3 事件识别** | 动物/鸟虫、自然、人声、乐器、城市噪音(521 类) | YAMNet(ONNX) | **16MB** | <1s |
| **L4-A 鸟种** | **具体到物种**(6522 种,含中文名) | BirdNET v2.4 | 52MB | 秒级 |
| **L4-B 情感** | 9 类语音情感 | emotion2vec_plus_large | 1.8GB | 1–3s(MPS/常驻) |

**自动分流**:先做廉价声学预分类,再决定跑哪几层。
环境声若被 L3 判为动物,会**自动接 L4-A 继续问到物种级**——
"这是什么在叫"可以一路问到"这是黑顶麻雀"。

## 实测验证

不是"在我造的样本上还行",下面是真实数据:

**BirdNET 鸟种识别** —— 标注物种的真实录音(*Arremon abeillei*):

| 时间段 | 检出 | 置信度 |
|---|---|---|
| 0–3s | 黑顶麻雀 *Arremon abeillei* | **0.991** |
| 12–14s | 黑顶麻雀 | 0.875 |

阴性对照:合成鸟叫 → **0 检出**(不硬猜物种);中文人声 → 判为 `Human vocal`(内置非鸟类拒识类)。

**emotion2vec 情感识别**:官方真人示例 → **愤怒 1.0000**;中文中性语音 → **中性 1.00**。

**转写**:中文 46.5s 音频 → 4.8s 转完(≈10× 实时),自动切 3 段带时间戳。

## 安装

前置:macOS(Apple Silicon)+ [uv](https://github.com/astral-sh/uv) + ffmpeg。

```bash
# 1) 放到 DSH 技能目录
git clone https://github.com/liyixuan201211/dsh-audio-read ~/.dsh/skills/audio-read
cd ~/.dsh/skills/audio-read

# 2) 独立 Python 环境(不污染系统)
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python \
  numpy scipy soundfile librosa onnxruntime \
  torch transformers funasr birdnet

# 3) 自检:告诉你哪一层就位、哪一层还缺什么
node scripts/audio.mjs --doctor
```

模型会在**首次使用**时自动下载(YAMNet 16MB 来自 hf-mirror;BirdNET 52MB 来自 Zenodo;
emotion2vec 来自 ModelScope)。**L2 转写需要额外装 [mlx-qwen3-asr](https://pypi.org/project/mlx-qwen3-asr/) 并下载 Qwen3-ASR-0.6B。**

> 国内网络提示:PyPI 走腾讯镜像直连可到 9MB/s,比走代理快得多:
> `export UV_DEFAULT_INDEX=https://mirrors.cloud.tencent.com/pypi/simple/`

## 用法

```bash
A=~/.dsh/skills/audio-read/scripts/audio.mjs

# 自动判断内容类型并分流(最常用)
node $A ~/Desktop/录音.m4a

# 只转写(快)
node $A 会议.mp3 --mode transcribe

# 只做声学画像(音高/节奏/调性)
node $A 歌曲.mp3 --mode acoustic

# 鸟种识别
node $A 野外录音.wav --birds

# 语音情感
node $A 语音.wav --emotion

# 环境声识别(零样本类目)
node $A 野外录音.wav --semantic

# 自检
node $A --doctor
```

输出是单行 JSON:`acousticSummary`(中文摘要)、`transcript`、`semantic`、`birds`、`emotion`、
以及落盘文件路径(长音频会给有界预览 + 完整文件,避免撑爆上下文)。

## 设计上的几个选择

- **上下文经济**:1 小时录音 ≈ 8–10k token。完整结果落盘,stdout 只回有界预览,
  Agent 需要细节时再 `grep`。
- **L4-B 做成常驻服务**:emotion2vec 冷加载要 15–45s,每次调用冷启无法接受,
  所以首次调用自动拉起常驻服务,之后热调用 1–3s。默认跑 **Apple GPU(MPS)**:
  实测 133s 语音 CPU 13.5s → MPS 3.4s,加载也从 15–45s 降到 4s;
  不可用时自动回退 CPU。
- **诚实优先**:置信度低就明说"别当确定结论";BirdNET 认不出就给 0 检出而不是硬猜。
- **音频内容是不可信输入**:转写文本可能包含对 Agent 的注入指令,一律只当数据。

## 词级时间戳与字幕

加 `--timestamps` 会多跑一个强制对齐模型(`Qwen3-ForcedAligner-0.6B`),
把每个字/词的时间都标出来,适合做字幕:

```bash
node scripts/audio.mjs 会议.wav --language zh --timestamps
# → files.srt 是句级字幕
```

**为什么字幕要自己重算**:对齐器会丢掉标点,底层自带 SRT 因此退化成"按字数硬切",
中文实测切出「今天下午三点开会请准 / 时到会议室我们要讨论」这种断句。
本仓库用**带标点的原文**重建句子边界(`scripts/lib/subtitle.mjs`),
同一段音频切出 3 句干净的句子:

```
1  00:00:00,000 --> 00:00:04,080  今天下午三点开会,请准时到会议室。
2  00:00:04,320 --> 00:00:07,280  我们要讨论项目进度和人员安排。
3  00:00:07,440 --> 00:00:11,360  另外,服务器成本超支了,需要重新评估。
```

## 已知限制

- **说话人分离**(谁在说)未实现。
- **BirdNET** 只认它 6522 种内的鸟;未启用地理/季节先验。
- **emotion2vec 是声学情感**(判断"怎么说的"),不是语义情感("说了什么");
  反讽、平静语气说狠话可能误判。
- 自动分流器是**启发式**,阈值在有限样本上调过,真实场景可能有误判。
- 体积不小:emotion2vec 1.8GB + 强制对齐 1.84GB + BirdNET 52MB。

## 目录结构

```
audio-read/
├── SKILL.md                 # 给 Agent 看的说明书(含 17 条实测坑)
├── config.example.json      # 复制成 config.json 可覆盖默认值
└── scripts/
    ├── audio.mjs            # 入口:探测 → 声学 → 分流 → 各层 → 落盘
    ├── asr.mjs              # L2:Qwen3-ASR 封装
    ├── lib/{config,probe}.mjs
    └── py/
        ├── acoustic.py      # L1:声学画像 + 内容预分类
        ├── semantic.py      # L3:YAMNet
        ├── birds.py         # L4-A:BirdNET
        ├── emotion.py       # L4-B:客户端
        └── emotion_server.py# L4-B:常驻服务
```

## 许可

MIT。第三方模型的许可见各自仓库(BirdNET 为 CC-BY-NC-SA,仅限非商业用途)。

Install

# Skills are files: copy them into $DSH_HOME/skills/audio-read (defaults to ~/.dsh/skills/audio-read)

Profile: web

Source