brainstate

baigong-ai/Tingji · source

Snapshot 0 · 2026-11-04T12:47:28Z · Python ·

Content fingerprint0fa4f0110009d66a344cb16d0b1ba7e2ca2785ed06854dcf8b28e7ff478ce93a

GitHub description at capture

听记 (Tingji) — 本地会议录音转写与纪要,FunASR + LLM,数据不出本机。Local meeting transcription & minutes, runs entirely offline.

README at capture

[English](README.en.md) | 中文

# 听记 (Tingji)

> 本地会议录音转写与纪要:把录音丢进去,自动得到原文、整理稿和会议纪要。数据全程留在你自己的电脑上。
>
> **English**: Tingji is a local, offline meeting transcription & minutes tool — drop in a recording and get the raw transcript, a cleaned-up version, and structured meeting minutes, with speaker diarization. Nothing leaves your machine. See [README.en.md](README.en.md).

![首页](docs/screenshot-home.png)

基于 [FunASR](https://github.com/modelscope/FunASR)(语音识别 + 说话人分离 + 标点恢复)与大语言模型整理。适合**会议、访谈、讲座**等长音频;跨平台(macOS / Windows WSL2 + GPU),识别无需联网。

## 特性

- **说话人自动区分**:基于 CAM++ 音色聚类,自动分清"谁在说话",可改成真实名字并同步到所有视图与导出;cam++ 偶尔把多人并一人 / 一人拆两人,可**手动合并说话人**或**逐句改归属(含拆出新说话人)**,纯元数据重映射、不动音频不重跑 ASR
- **发言人时间轴**:顶部一条按累计发言时长的比例色条,一眼看出谁说得多;点击定位该说话人第一句,播放时高亮当前说话人
- **逐句时间戳**:点任意句定位音频(不强制播放),播放时当前句自动高亮 + 滚动跟随
- **纪要时间戳回跳**:决议 / 待办 / 待讨论每条可带 `[m:ss]` 时间戳,点击直接跳回音频现场,“这真是会上定的吗”一键核对;整理稿小节标题同样带发言开始时间,导出纪要时时间戳内联保留
- **个人笔记层**:把鼠标移到任意原句上点 ✎ 写自己的批注,笔记锚定句子、独立存储,**重新整理永不覆盖**;总结页顶部「我的笔记」按时间汇总,点击跳回锚定句;也支持不锚定句子的全局笔记;纪要导出可附「个人笔记」一节
- **议题时间轴**:说话人条下方第二条时间轴,把会议切成「背景 / 提出问题 / 讨论 / 结论 / 行动 / 闲聊」的议题段,点击跳到该议题开头、播放时高亮当前议题;LLM 生成初值,人可校正(改名 / 改阶段 / 调边界 / 删段),手动改过后重新整理不会覆盖
- **原文↔整理对照**:左右双栏按时间戳精确关联,hover 高亮、点击跳转音频、播放同步
- **手动校对 + 热词 + 倍速/跳静音**:双击句子纠正识别错误(可顺手改说话人归属、加入热词);音频条支持 0.75×–2× 倍速与"跳静音"(自动跳过句间空隙),校对更快
- **批量上传**:一次拖入多个录音文件批量转录,首页队列逐个显示进度,识别按队列依次进行、转换与整理跨文件并行
- **一键整理**:把口语化的原文整理成通顺文字,再生成结构化会议纪要(概述 / 决议 / 待办 / 待讨论);整理前可选总结模板、填会议背景与常用术语
- **总结模板**:内置普通会议 / 周会 / 访谈采访 / 项目管理,也可自定义(背景、常用词语、总结方向 / 内容 / 框架),不同会议侧重不同
- **实时日志**:进度条 + ASR 设备(GPU 型号)/ 模型 / 分段耗时;超过 15 秒无更新会警告,一眼看出是否卡住
- **LLM 自由选**:本地 Ollama,或任何 OpenAI 兼容 API(GLM / DeepSeek / 通义 / Kimi / OpenAI …)
- **全 Web 配置**:数据目录、LLM、热词、总结模板都在网页里配,不用改文件
- **导出** `.md` / `.docx`(整理版)、`.md` / `.docx`(纪要,只拿四段总结)、`.txt` / `.srt`(原文,可选是否带说话人 / 时间戳)
- **实时流式转写**:会议中直接开麦直播;会议中断线(机器睡眠、标签页挂起、网络抖动)页面自动重连,服务端 10 分钟宽限期内无缝续录,断档处补静音保持时间轴连续;停止后原文与录音**立即**落盘,后台自动二次识别升级为带准确时间戳与说话人分离的精稿,随后进入与上传模式一致的校对 → 整理 → 总结流程;标准模式(内置引擎,全平台开箱即用);增强模式(GPU 引擎)暂不提供,实时页已只保留标准模式
- **历史会议管理**:给会议打标签 + 按标签筛选、对已处理完的会议改名、删除可选「移到回收站」或「完全删除」;回收站里可一键恢复或彻底删除
- **纪要二次编辑**:整理版和会议总结都能手动修改再保存(总结支持四段结构化逐段编辑,也支持纯 markdown)
- **卡死任务恢复**:服务重启导致任务卡住时,详情页点「恢复任务」一键续跑;也可装 cron 每小时自动巡检
- **本地运行**:录音与结果都存在你自己的机器,不上传任何服务器


## 功能一览
![详情页](docs/screenshot-detail.png)
围绕一段录音,听记给你四样东西:


**说话人自动分离 + 发言比例时间轴。** 顶部彩色横条按累计发言时长显示比例,一眼看出谁说得多;点说话人改成真名,改完同步到所有视图和导出。

**逐句时间戳,双击就改。** 点任意句定位音频,播放时当前句自动高亮 + 滚动跟随;识别错了双击直接改,可顺手加进热词,下次更准。
![原文 Tab:当前句高亮 + 双击编辑](docs/screenshot-edit.png)

**四段结构化会议纪要。** 整理完的总结自动拆成「概述 / 决议 / 待办 / 待讨论」,比一锅 Markdown 看着清楚;模型偶尔不吐严格 JSON 时回退成纯文本,不影响用。
![会议总结 Tab](docs/screenshot-summary.png)

**纪要能回现场,笔记不会丢(v0.7)。** 纪要每条决议、待办后面跟一个 `[m:ss]` 时间戳,点一下直接跳回那句话的音频现场;鼠标移到原文任意句上点 ✎ 写批注,笔记存在独立的 `notes.json` 里,重新整理纪要也不会覆盖它;顶部新增议题时间轴,一眼看清这场会怎么走过来的(提出问题 → 讨论 → 结论 → 行动),点任意议题段回到现场,分段不满意可以手动改。

**原文↔整理对照。** 左右双栏按时间戳对齐,hover 高亮、点击跳转音频、播放同步;整理偶尔会改原意,对照能快速核对。
![对照 Tab](docs/screenshot-compare.png)

整理前可配总结模板(普通会议 / 周会 / 访谈采访 / 项目管理,也可自定义),点「开始整理」时会先弹框让你选模板、填会议背景与术语。
![设置 → 总结模板](docs/screenshot-template.png)

处理日志记录各阶段耗时(详情页顶部展示总时长),落盘后重启可查。

## 环境要求

- macOS / Linux / Windows(WSL2)
- Python 3.11,用 [uv](https://github.com/astral-sh/uv) 管理
- ffmpeg(macOS:`brew install ffmpeg`;Linux:`sudo apt install ffmpeg`)
- Git LFS(用于预下载模型)
- 首次启动会下载约 1.3 GB FunASR 模型
- Windows 用户请看 [WSL2 + GPU 部署指南](docs/wsl-deploy.md)

## 快速开始

```bash
git clone https://github.com/baigong-ai/Tingji.git
cd Tingji
cp config.yaml.example config.yaml
cp .env.example .env          # api 模式时填入 LLM_API_KEY

bash scripts/download_models.sh   # 预下载模型(推荐)
./run.sh
```

浏览器打开 `http://127.0.0.1:8000`。

> 启动后会显示局域网地址,同一网络下的其他设备(手机/平板)也能打开使用。

### 常驻后台运行

```bash
./run.sh -d          # 后台运行(脱离终端,PID 写入 logs/tingji.pid,日志写入 logs/tingji.out)
./run.sh --status    # 看是否在跑
./run.sh --stop      # 停止
```

适合长时间挂在 Mac/Linux 上当本地转写服务用。「设置 → 服务」里还能改监听端口/host(改完需重启生效,改之前点「检测端口」,冲突会用 `lsof` 标出占用进程)。

#### 任务中断自动恢复(可选)

服务重启时正在处理的任务会卡在中间状态。除了详情页手动点「恢复任务」,还可以装一个每小时巡检的 cron 自动续跑(实时录音中断的会议,录音已落盘的会自动恢复识别,未能保存的标记为失败):

```bash
crontab -l | { cat; echo '17 * * * * cd /path/to/Tingji && .venv/bin/python scripts/resume_tasks.py >> logs/resume.log 2>&1'; } | crontab -
```

脚本自己从 `config.yaml` 读端口 / SSL / 数据目录,自定义过配置也能直接用。

#### 模型闲置自动卸载

常驻时最吃内存的是 FunASR 那 4 个模型。听记会在**闲置一段时间后把它们从内存里卸掉**,下次来活再自动加载,这样挂着不动时占用大幅下降。

**什么时候触发卸载**(必须同时满足):
1. 模型当前已加载
2. 没有转录在进行(`is_busy=False`)
3. 没有任何任务在排队或跑(pending / converting / asr_running / 整理 / 总结 都不算空闲)
4. 距上次 ASR 活动时间 ≥ 阈值(默认 **30 分钟**,可在「设置 → 服务」改,**保存即生效**不用重启;设 0 = 从不卸载)

后台监视器每 60 秒查一次,所以最坏会多等 60 秒。急着释放可手点「设置 → 服务 → 立即释放模型」。

**两个平台的回收效果**(同一段 `test_cn.wav`,阈值 1 分钟实测):

| 指标 | Mac mini M4 | WSL2 + RTX 4060 Ti |
|---|---|---|
| ASR 完成后 RSS | 1556 MB | 3521 MB |
| 卸载后 RSS | 1275 MB | 1843 MB |
| **RSS 净回收** | ~281 MB(**18%**) | **1678 MB(48%)** |
| GPU 显存回收 | —(无独立显卡) | 2222 → 948 MiB(**57%**) |

**为什么平台差距这么大**:两个独立因素叠加
- **占用端**:WSL 走 CUDA 会把 CUDA runtime / cuDNN / cuBLAS 一坨库 + CUDA context 加载进进程,所以 ASR 时占用比 Mac(MPS,运行时由系统托管)高一倍多。模型本身两台机器一样大,差距在 GPU 后端依赖。
- **回收端**:Linux glibc 的 `free()` 配合 `malloc_trim(0)` 会把页**真正还给操作系统**(外加 `torch.cuda.empty_cache()` 还 GPU 显存);macOS 的 malloc free 之后**不主动还页**,`ps` RSS 几乎不掉,但内存其实空出来了能被本进程复用。所以 Mac 上数字看着不动,实际进程内部已经可用。

判定"卸载有没有生效"别看 Mac 的 RSS 数字,看「设置 → 服务」的模型状态字段,或日志里的 `FunASR models unloaded (idle)`。

## 项目状态(v0.7.1)

核心链路可用:上传 → 识别(带说话人分离 + 时间戳)→ 校对 → 一键整理 + 结构化纪要(条目带时间戳)+ 议题时间轴 + 个人笔记;历史会议支持标签、改名、删除与回收站恢复;实时流式转写(标准模式)。

**v0.7.1 新增(实时保存可靠性)**:
- **会议结束立刻见稿**:实时记录停止或断线后,录音与流式原文**立即**落盘展示;系统在后台继续跑离线二次识别(期间状态显示「识别中」),完成后页面自动刷新为带准确时间戳与说话人分离的精稿,校对 / 整理 / 总结流程不变
- **修复「会议结束后一直显示实时中、页面无内容」**:此前要先等离线二次识别(含模型冷加载数分钟,GPU 停滞时可达十分钟以上)全部跑完才更新会议状态,期间看起来像记录丢失,识别卡死则状态永久卡住;现在状态与耗时识别解耦,秒级落定
- **失败有兜底**:二次识别失败或超时保留流式稿,详情页点「恢复任务」可重跑;服务重启中断的实时会议,录音已落盘的自动从磁盘恢复识别,不再一律报「录音丢失」

**v0.7 新增(会后消费层:追溯、笔记、议题)**:
- **纪要时间戳**:整理稿小节标题带 `[m:ss]` 发言时间锚点,纪要决议 / 待办 / 待讨论条目带可点击时间戳 chip,一键回跳音频现场;导出纪要时时间戳内联保留。模型输出不了带时间的 JSON 时自动降级为普通条目,旧会议数据照常渲染
- **个人笔记层**:原句 hover 出 ✎ 入口写锚定批注(也可写不锚定的全局笔记),独立 `notes.json` 存储,**重新整理不覆盖**;总结页顶部「我的笔记」折叠区按时间汇总、点击跳回锚定句;纪要导出可附「个人笔记」一节(`notes=false` 关闭)
- **议题时间轴**:说话人条下方新增议题分段条(背景 / 提出问题 / 讨论 / 结论 / 行动 / 闲聊 六种阶段配色),点击跳转、播放高亮当前议题、hover 预览该段首句;LLM 生成初值,可手动改名 / 改阶段 / 调边界 / 删段,手动修改后重新整理不再自动覆盖;长会按约 25 分钟窗口分段生成再合并边界;也可在详情页单独生成或重新生成
- **增强模式(GPU sidecar)暂不提供**:实时页移除引擎选择器,只保留标准模式;后端仍拒绝 `sidecar` 选中(直连 API 亦然)

**v0.5 新增(稳定性 + 回收站 + 纪要编辑)**:
- **回收站管理**:首页「回收站」弹窗可查看 / 一键恢复 / 彻底删除(v0.3 的"可找回"补齐了界面)
- **纪要二次编辑**:整理版与会议总结都能手动修改保存;总结支持概述 / 决议 / 待办 / 待讨论四段逐段编辑,也支持纯 markdown
- **卡死任务恢复**:详情页「恢复任务」按钮一键续跑;`scripts/resume_tasks.py` 每小时 cron 自动巡检(自动读 config.yaml 的端口 / SSL / 数据目录)
- **稳定性修复**:重试后任务进度永远停在"排队中"并阻塞模型闲置卸载的 bug、实时转写时标点模型阻塞所有 HTTP 请求、整理中断导致状态卡死、`meta.json` 原子写 + 容错(一个损坏文件不再搞挂整个列表)、meeting_id 路径穿越加固
- **安全**:说话人名注入 XSS 修复;增强模式(GPU sidecar)后端同步禁止选中(后改为暂不提供,见 v0.7)

**v0.4 新增(实时流式转写)**:
- **实时流式转录**:会议中开麦直播,停止后自动落成 `audio_live.wav` + `raw.json`,直接进入「待整理」状态,后续校对 / 整理 / 总结流程与上传模式完全一致
- **标准模式**:内置 FunASR 流式引擎(`paraformer-zh-streaming`),macOS / WSL / Linux 通用,无需额外配置
- **增强模式(GPU 引擎)**:原计划转发到 Fun-ASR-Nano vLLM GPU sidecar(方言 / 口音 / 远场识别更准);因部署门槛高,v0.7 起从前端移除、暂不提供(后端 `app/stream.py` 保留实现)
- **统一入口**:首页「实时记录」标签进入实时页

**v0.3 已完成(历史会议管理)**:
- **标签 + 筛选**:给会议打多个标签,列表上方按标签快速筛选(多选并集),行内标签 chip 点一下也能筛
- **重命名**:已处理完的会议(待整理 / 完成 / 失败)可直接改名
- **删除二选一**:移到回收站(`data/回收站/`,文件保留可找回)/ 完全删除(永久删除);删除前明确显示回收站路径
- **首次引导持久化**:「会议数据将存到…」确认后写入 config.yaml,换浏览器 / 清缓存不再反复弹出

**v0.2 常驻后台能力**:`./run.sh -d` 后台运行;FunASR 模型闲置自动卸载(默认 30 分钟,可配置;Mac 回收 18% / WSL+GPU 回收 48% RSS + 57% 显存);「设置 → 服务」tab(模型状态/释放、端口/host + 冲突检测、闲置阈值)。

**v0.1 已完成**:发言人时间轴、结构化纪要(概述 / 决议 / 待办 / 待讨论 四段 JSON)、总结模板(预设 + 自定义)、整理前会议背景与常用术语、说话人改名同步所有视图与导出、md / txt / srt 导出、实时日志、固定布局。

**v0.6 已完成(离线核心)**:批量上传 + 队列、说话人手动合并 / 逐句拆分(纯元数据重映射)、整理版与纪要的 docx 导出、导出选项(是否带说话人 / 时间戳)、校对增强(倍速 + 跳静音)、pipeline 解锁(多文件转换 / 整理并行)、整理质量保障(假整理回声检测 / 思考开关 / 纠偏 prompt)。

**暂未做**:问纪要(围绕会议内容问答)、纪要组件化(表格 / 分栏 / 流程条)、静态 HTML 页面导出等会后消费层的后续能力。增强模式(GPU sidecar,方言 / 口音 / 远场更准)作为**远期目标**,当前前后端一致禁用。

## 实时转录

除「上传录音再处理」外,听记支持会议中直接开麦实时转写。停止后,录音与流式原文**立即**落盘展示,无需守在页面前等待;系统在后台继续完成带准确时间戳与说话人分离的二次识别,完成后页面自动刷新,随后进入与上传模式一致的校对 → 整理 → 总结流程。会议中连接中断(机器睡眠、标签页挂起、网络抖动)不用慌:页面自动重连,服务端把会话保留 10 分钟宽限期,恢复后无缝续录,断档处补静音保持时间轴连续;超过宽限期未重连,才把已录制内容落盘收尾。

当前只提供一种引擎:

| 模式 | 用户可见名称 | 适用平台 | 硬件要求 |
|---|---|---|---|
| **标准模式** | 内置实时引擎 | macOS / WSL / Linux | Apple Silicon M1+ 或现代 CPU,8GB+ 内存 |

- **标准模式**默认开启,所有平台可用,无需额外配置。
- **增强模式**(GPU 引擎,面向方言 / 口音 / 远场等重场景)暂不提供:需要单独部署 Fun-ASR-Nano vLLM GPU sidecar(`ws://localhost:10095`,仅 WSL/Linux + NVIDIA 独显),门槛较高,v0.7 起实时页已移除该选项;后端仍会拒绝 `sidecar` 选中。实现保留在 `app/stream.py`,未来视需求开放。

### 局域网 HTTPS 访问(实时开麦)

浏览器只允许安全上下文(localhost 或 HTTPS)访问麦克风。若要在局域网其他机器上开麦实时转写:

1. 编辑 `config.yaml`:

```yaml
server:
  ssl:
    enabled: true
```

2. 重启服务,`run.sh` 会自动生成自签名证书到 `certs/cert.pem` 与 `certs/key.pem`。
3. 在局域网设备访问 `https://<服务器IP>:8000/live`。
4. 首次访问浏览器会提示证书不受信任,点击“高级”→“继续前往”即可(iOS Safari 需到 设置 → 通用 → 关于本机 → 证书信任设置 中信任该证书)。

> 自签名证书仅用于局域网,不要上传到公网或提交到 git(`certs/` 已在 `.gitignore` 中)。

## 使用流程

1. **上传录音**:拖入或选择音频文件,填写标题,点「开始转录」
2. **等待识别**:自动完成音频转换 + 语音识别,状态停在「待整理」
3. **校对原文**:在「原文」Tab 双击任意句子纠正错字(可顺手勾选「加入热词」),提升后续准确率
4. **一键整理**:点右上角「开始整理」(可先选总结模板、填会议背景与常用术语),自动生成整理稿与会议纪要

详情页顶部固定两条时间轴(发言人比例条 + 议题分段条)与工具栏(搜索、Tab),只有内容区滚动。四个 Tab:

- **原文**:带说话人配色和时间戳,点击跳转音频;hover 出 ✎ 可给这句写笔记(有笔记的句子带角标)
- **整理版**:去口语化的通顺文字,小节标题带发言开始时间
- **会议总结**:概述 / 决议 / 待办 / 待讨论(结构化四段;条目带可点击时间戳;顶部「我的笔记」折叠区;模型偶尔不吐严格 JSON 时回退成纯文本,不影响使用)
- **原文↔整理对照**:左右双栏按时间戳关联,hover 高亮、点击跳转音频、播放同步跟随

顶部可导出 `.md` / `.txt` / `.srt`;说话人 chip 点击改成真实名字。「重新整理」时会先让你选模板。

## 预下载模型(推荐)

FunASR 需要 4 个模型(ASR / VAD / 标点 / 说话人),共约 1.3 GB。直接启动也能自动下载,但 macOS 上 uv 装的 Python 是临时签名,偶发网络受限。**启动前用 `git clone` 预下载更稳**(git 是 Apple 签名,走系统网络)。

```bash
bash scripts/download_models.sh          # 默认下载到 ./models/
# 或指定路径:
bash scripts/download_models.sh /path/to/models   # 并在 config.yaml 改 asr.cache_dir
```

手动 clone(目录名须严格对应):

```bash
mkdir -p models && cd models && git lfs install
git clone --depth 1 https://www.modelscope.cn/iic/speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch.git paraformer-zh
git clone --depth 1 https://www.modelscope.cn/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch.git fsmn-vad
git clone --depth 1 https://www.modelscope.cn/iic/punc_ct-transformer_zh-cn-common-vocab272727-pytorch.git ct-punc
git clone --depth 1 https://www.modelscope.cn/iic/speech_campplus_sv_zh-cn_16k-common.git campp
```

## 配置

绝大多数配置都能在网页「设置」里完成(数据目录 / LLM / 热词)。需要时也可直接编辑 `config.yaml`:

| 字段 | 说明 |
|---|---|
| `asr.cache_dir` | FunASR 模型缓存目录,默认 `./models` |
| `asr.hub` | `ms`(ModelScope,默认)或 `hf` |
| `asr.idle_unload_minutes` | 模型闲置多久后自动卸载(分钟),默认 30;0 = 从不。可在网页「设置 → 服务」改,即时生效 |
| `llm.mode` | `api` 或 `ollama` |
| `llm.api.*` | OpenAI 兼容 API(base_url / api_key / model) |
| `llm.ollama.*` | 本地 Ollama(base_url / model / think) |
| `llm.polish_chunk_minutes` | 整理分段时长(分钟),默认 6 |
| `server.lan_token` | 默认 `false`。设 `true` 后开启**局域网访问令牌**:非本机(非 127.x)访问 `/api/*` 与 `/ws/*` 需带令牌(首次启动会生成并打印一个带 `?token=…` 的局域网地址,浏览器打开一次即记入本地,之后自动带上)。本机访问不受影响。在 `0.0.0.0` 开放给局域网时建议开启 |

`api_key` 支持 `${LLM_API_KEY}` 占位,从环境变量读取。

### LLM 示例

**Ollama 本地:**
```bash
ollama pull qwen2.5:7b && ollama serve
# config.yaml: llm.mode: ollama
```

**DeepSeek:**
```yaml
llm:
  mode: api
  api: { base_url: "https://api.deepseek.com/v1", api_key: "${LLM_API_KEY}", model: "deepseek-chat" }
```

**通义千问:**
```yaml
llm:
  mode: api
  api: { base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1", api_key: "${LLM_API_KEY}", model: "qwen-plus" }
```

### 模型选择

**本地处理优先**是本项目的基本原则:识别(ASR)全程本地,整理/总结默认也走本地 Ollama,会议内容不出你的机器。在线 API 只是可选增强,适合本地模型力不从心时手动切换。

模型选哪个看你的硬件和偏好,这里不替你选,只列实测过的:

- **本地 Ollama**:`Qwen3:8b`(gguf;WSL + RTX 4060 Ti、Mac mini M4 均实测可用)
- **API(OpenAI 兼容)**:GLM、DeepSeek

思考型模型(Qwen3 系列等)的 thinking 可通过设置页「开启思考模式」开关控制(`ollama.think`,走 Ollama 原生 API 的 `think` 字段):关闭快但小模型整理质量差(实测可能照抄原文),开启更准但更慢。**本地用 Qwen3 整理时建议开启**;思考失控返回空内容时会按失败重试并兜底保留原文。整理稿与原文几乎一致时,详情页会显示告警横幅提示更换模型或开启思考。

## 性能参考

- 81 分钟访谈(GPU:RTX 4060 Ti):识别约 2 分钟(RTF ≈ 0.025),完整流程约 7–8 分钟
- CPU 模式:约音频时长 × 0.25(60 分钟音频 ≈ 15 分钟)
- 支持 60–90 分钟长音频(VAD 自动切片 + 分批推理)

## 已知限制

- 说话人分离基于音色聚类,设备/位置变化可能导致 ID 拆分或合并
- LLM 整理可能轻微改变原意,可用「对照」Tab 核对
- 无用户认证,仅适合本地或可信局域网运行
- 进行中的任务状态存内存,重启服务会丢失(已完成会议数据在磁盘上)

## 项目结构

```
app/
  config.py   配置加载(${ENV_VAR} 展开)
  audio.py    ffmpeg 转换
  asr.py      FunASR AutoModel 封装(GPU 优先)
  stream.py   实时流式引擎(标准 funasr 实现;增强 sidecar 保留但暂不提供)
  llm.py      整理 + 总结(分段 + map-reduce + 条目时间戳)+ 议题分段
  storage.py  data/ 目录 CRUD(会议 / 笔记 / 议题 / 模板 / 日志)
  tasks.py    异步 pipeline + 进度
  main.py     FastAPI 路由
  dns_hosts.py 可选 DNS 覆盖(dns_hosts.txt 存在时生效)
static/       首页 / 详情页 / 实时页(index / meeting / live)
data/         会议数据(gitignore)
models/       FunASR 模型缓存(gitignore)
test/         单元测试 + 冒烟脚本
```

## License

MIT