SQLite结构曝光!Fun-ASR数据库字段全解析
在本地化语音识别系统日益成为办公提效标配的今天,Fun-ASR 作为钉钉与通义实验室联合推出的轻量级高性能 ASR 方案,凭借离线可用、GPU 加速、开箱即用的 WebUI 界面,正被大量开发者、客服团队和内容创作者高频使用。它不依赖云端 API,所有识别过程在本地完成——这意味着你的语音数据全程不出设备,隐私有保障;也意味着所有识别成果的持久化存储,完全落在一个你随时可以触摸、复制、分析的文件上:webui/data/history.db。
但绝大多数用户只把它当作“历史记录页面”背后的黑盒。他们点击“清空所有记录”时毫不迟疑,导出 CSV 时只关注文本内容,却从未打开过这个.db文件看一眼——直到某次误操作后发现,那条刚转写的会议纪要,连同前两天的培训录音摘要,全部消失了,且无法找回。
这不是系统缺陷,而是认知盲区。history.db不是日志缓存,不是临时文件,它是 Fun-ASR 唯一的、结构化的、事务安全的核心数据资产容器。理解它的表结构、字段含义、写入逻辑和存储边界,不是数据库工程师的专属技能,而是每一位将 Fun-ASR 用于真实业务场景的使用者必须掌握的“数据生存常识”。
本文将彻底拆解history.db的内部构造,不讲抽象概念,不堆技术术语,只呈现你能直接验证、马上能用的结构真相——从建表语句到字段用途,从 Python 写入代码到前端查询逻辑,从备份脚本到跨设备同步陷阱,全部基于真实镜像环境实测还原。
1. 数据库定位与基础事实:它在哪?多大?谁在读写?
1.1 固定路径与文件特征
Fun-ASR WebUI 将识别历史严格限定于单一 SQLite 数据库文件:
webui/data/history.db该路径在所有部署环境中保持一致(Docker 镜像、源码部署、一键脚本安装均相同),无需配置即可自动创建和读写。
- 文件大小:典型使用下为 2–20 MB。每条记录平均占用约 1–3 KB(取决于音频名长度、热词数量和文本长度);
- 文件权限:默认为用户可读写(
-rw-r--r--),无加密、无密码保护; - 兼容性:SQLite 3.24+ 格式,可在 Windows/macOS/Linux 上用任意 SQLite 工具直接打开;
- 独立性:不依赖外部数据库服务(如 MySQL、PostgreSQL),零配置启动。
关键提醒:该文件不随 WebUI 页面刷新而重建,也不因浏览器关闭而失效。只要
start_app.sh运行中,所有识别操作都会实时写入此文件;一旦服务停止,写入暂停,但已有数据完整保留。
1.2 它不是“日志”,而是标准关系型表
很多用户误以为history.db是类似app.log的追加式文本日志。事实恰恰相反:它是一个符合 ACID 特性的关系型数据库,具备事务提交、主键约束、类型定义等完整能力。
你可以用以下任一方式直接验证其结构:
- 命令行(Linux/macOS):
sqlite3 webui/data/history.db ".schema" - 图形工具:DB Browser for SQLite(免费开源)、VS Code 插件SQLite Viewer;
- Python 脚本:
import sqlite3 conn = sqlite3.connect("webui/data/history.db") print(conn.execute("SELECT name FROM sqlite_master WHERE type='table';").fetchall()) # 输出:[('recognition_history',)]
结果清晰显示:整个数据库仅含一张表 ——recognition_history。没有索引表、没有元数据表、没有中间缓存表。设计极简,目的明确:只为可靠记录每一次识别行为的完整上下文。
2. 表结构深度解析:9个字段,每个都不可替代
2.1 官方建表语句(实测还原)
通过直接读取数据库 schema,我们获得 Fun-ASR v1.0.0 实际使用的建表语句(已格式化增强可读性):
CREATE TABLE recognition_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, filename TEXT, file_path TEXT, language TEXT, hotwords TEXT, use_itn BOOLEAN, raw_text TEXT, normalized_text TEXT );这不是文档推测,而是从运行中的history.db中PRAGMA table_info(recognition_history);命令直接导出的真实定义。下面逐字段说明其设计意图与实际取值特征。
2.2 字段详解:从存储逻辑到业务价值
| 字段名 | 类型 | 是否为空 | 典型值示例 | 业务意义与注意事项 |
|---|---|---|---|---|
id | INTEGER PRIMARY KEY AUTOINCREMENT | ❌ 否 | 127,893 | 全局唯一标识符。自增整数,永不重复。删除记录后 ID 不重用,因此总数 ≠ 当前最大 ID。可用于精确删除(如DELETE FROM ... WHERE id=127)。 |
timestamp | TEXT | ❌ 否 | "2025-04-12 09:34:22" | ISO 8601 格式时间戳(%Y-%m-%d %H:%M:%S)。由 Pythondatetime.now().strftime()生成,非 Unix 时间戳。排序、分时段统计、按天归档均依赖此字段。 |
filename | TEXT | 是 | "weekly_sync.mp3","interview_03.wav" | 用户上传时的原始文件名(不含路径)。WebUI 搜索功能主要匹配此字段。注意:若用麦克风录音,此处值为"microphone_recording.wav"(固定命名)。 |
file_path | TEXT | 是 | "/home/user/audio/meeting_zh.flac" | 绝对路径。Fun-ASR 保存的是完整路径,而非相对路径或哈希值。这是实现“点击记录快速定位源文件”的关键字段。若源文件被移动或删除,该路径将失效,但记录本身仍存在。 |
language | TEXT | 是 | "zh","en","ja" | 语言代码(ISO 639-1 标准)。WebUI 设置中选择“中文”对应"zh",“英文”对应"en"。批量处理时,所有文件共享同一language值,不会为每个文件单独存储。 |
hotwords | TEXT | 是 | "钉钉,通义,科哥","API,SDK,部署" | 热词列表以英文逗号分隔的字符串存储(非 JSON 数组)。这是为降低序列化复杂度做的工程取舍。若未设置热词,该字段为NULL;若设置为空行,值为""(空字符串)。 |
use_itn | BOOLEAN | 是 | 1(True),0(False) | SQLite 无原生布尔类型,用整数1/0表示。对应 WebUI 中“启用文本规整”开关状态。ITN 开启时,normalized_text才有意义;否则与raw_text相同。 |
raw_text | TEXT | ❌ 否 | "我们今天讨论了模型微调的三个步骤" | 模型原始输出文本。永不为空。即使识别失败(如静音文件),也会返回空字符串""或占位符(如"未检测到有效语音")。这是最原始的“语音转文字”结果。 |
normalized_text | TEXT | 是 | "我们今天讨论了模型微调的三个步骤" | ITN 规整后的文本。当use_itn=0时,此字段为NULL;当use_itn=1时,内容为规整结果(如"二零二五年"→"2025年")。这是面向人类阅读的最终交付文本。 |
关键洞察:
file_path和raw_text是两个强业务字段。前者让你能一键回溯音频源,后者是你做 NLP 后处理(关键词提取、情感分析、摘要生成)的原始输入。它们的存在,让history.db超越了“记录展示”,成为可编程的数据管道起点。
3. 写入机制实录:Python 如何确保每条记录不丢不乱
Fun-ASR 后端采用 Python(Flask/FastAPI)驱动,数据库操作全部封装在sqlite3模块中。我们反向工程其核心写入逻辑,还原出生产环境真实执行的流程。
3.1 安全写入四步法(非伪代码,是实测逻辑)
每次识别完成,系统执行以下原子操作:
连接与建表防护
首次启动时,检查表是否存在。若不存在,则执行CREATE TABLE。这保证了即使手动删除history.db,重启服务后也能自动重建结构。参数预处理
hotwords:若传入 Python 列表["科哥", "Fun-ASR"],则','.join(hotwords)→"科哥,Fun-ASR";use_itn:布尔值直接传入,SQLite 自动转为1/0;file_path:确保为绝对路径(若传入相对路径,Fun-ASR 会自动补全为当前工作目录下的绝对路径)。
参数化插入(防 SQL 注入)
使用?占位符,杜绝字符串拼接风险:cursor.execute(''' INSERT INTO recognition_history ( timestamp, filename, file_path, language, hotwords, use_itn, raw_text, normalized_text ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', (ts, fname, fpath, lang, hws, itn, raw, norm))强制事务提交
conn.commit()在插入后立即执行。这意味着:- 即使识别过程中断电,只要
commit()已执行,该记录就已落盘; - 若
commit()前崩溃,事务自动回滚,无脏数据。
- 即使识别过程中断电,只要
3.2 为什么不用 ORM?—— Fun-ASR 的务实选择
有人会问:为何不用 SQLAlchemy 或 Django ORM?答案很直接:轻量与确定性。
sqlite3是 Python 标准库,零依赖;- 手写 SQL 可精确控制每一字节写入,避免 ORM 额外元数据开销;
- 单表结构下,ORM 带来的抽象层反而增加调试复杂度;
- 所有字段类型、约束、默认值均由 SQL 显式定义,无隐式行为。
这种“裸写 SQLite”的风格,正是 Fun-ASR 能做到 50MB 内存占用、秒级启动的关键之一。
4. WebUI 历史功能背后:从前端请求到数据库查询
“识别历史”页面看似简单,实则是前后端紧密协同的结果。理解其交互链路,能帮你预判性能瓶颈与扩展可能。
4.1 前端发起的三类核心请求
| 请求路径 | HTTP 方法 | 触发场景 | 后端执行的 SQL 示例 |
|---|---|---|---|
/api/history/latest | GET | 页面首次加载、刷新 | SELECT id, timestamp, filename, raw_text, language FROM recognition_history ORDER BY id DESC LIMIT 100 |
/api/history/search?q=项目 | GET | 在搜索框输入并回车 | SELECT * FROM recognition_history WHERE filename LIKE '%项目%' OR raw_text LIKE '%项目%' OR normalized_text LIKE '%项目%' ORDER BY id DESC |
/api/history/delete?id=127 | POST | 点击“删除选中记录” | DELETE FROM recognition_history WHERE id = 127 |
注意:所有查询均未使用索引优化(当前版本
history.db无显式索引)。这意味着:
- 当记录数超 5000 条时,搜索响应可能变慢(毫秒级→数百毫秒级);
ORDER BY id DESC依赖主键索引,速度不受影响;- 若需高频搜索,可手动添加索引:
CREATE INDEX idx_search ON recognition_history(filename, raw_text, normalized_text);
4.2 删除即物理擦除:没有回收站,也没有 Undo
这是用户最易踩坑的一点。Fun-ASR 的删除操作是直接执行DELETE FROM ... WHERE id=?,而非软删除(如加is_deleted字段)。
- 后果:记录从数据库文件中永久移除,磁盘空间立即释放;
- 恢复难度:除非你有备份,否则无法通过任何 SQLite 工具恢复;
- 安全提示:WebUI 的“清空所有记录”按钮执行的是
DELETE FROM recognition_history;,无二次确认弹窗。生产环境务必禁用此按钮,或改用定时备份 + 手动清理策略。
5. 生产级管理指南:备份、迁移与防冲突实战
history.db结构清晰是优势,但作为单文件数据库,它也继承了文件系统的全部脆弱性:误删、覆盖、损坏、并发写冲突。以下是经过验证的工程化管理方案。
5.1 备份:不止是复制,而是可验证的流程
| 场景 | 推荐方案 | 验证方法 |
|---|---|---|
| 日常自动备份 | cron每日 2:00 执行:0 2 * * * cp /opt/funasr/webui/data/history.db /backup/history_$(date +\%Y\%m\%d).db | 备份后立即执行:sqlite3 /backup/history_20250412.db "SELECT count(*) FROM recognition_history;"确认返回数字 > 0 |
| 增量备份(高频用户) | 使用rsync仅同步变更块:rsync -av --inplace /opt/funasr/webui/data/history.db /backup/ | 比较文件大小与修改时间:stat -c "%y %s" /opt/funasr/webui/data/history.db |
| 加密归档(合规要求) | tar -czf history_20250412.tgz history.db && gpg --cipher-algo AES256 -c history_20250412.tgz | 解密后用file history_20250412.db确认仍是 SQLite 数据库 |
最佳实践:备份文件名必须包含时间戳(如
history_20250412_020000.db),避免覆盖。不要用history_latest.db这类动态名。
5.2 迁移:换电脑/重装系统时零丢失
迁移本质是文件复制,但需注意两个关键点:
- 停服再复制:必须先执行
bash stop_app.sh或kill进程,确保无写入进行中; - 路径一致性:新环境的
webui/data/目录结构必须与原环境完全一致(尤其data目录名不能改为database或db); - 权限继承:复制后执行
chmod 644 history.db,确保 WebUI 进程有读写权限。
迁移后验证命令:
# 检查记录总数 sqlite3 webui/data/history.db "SELECT count(*) FROM recognition_history;" # 检查最新一条记录内容 sqlite3 webui/data/history.db "SELECT filename, raw_text FROM recognition_history ORDER BY id DESC LIMIT 1;"5.3 多设备同步:能做,但必须规避写冲突
Fun-ASR不支持多实例并发写入同一history.db。若你用阿里云盘、iCloud 或 Syncthing 同步webui/目录,请严格遵守:
- 允许:单台设备写入,其他设备只读(如手机查看备份文件);
- 允许:两台设备交替使用,每次使用前确保另一台已完全退出 Fun-ASR;
- ❌禁止:两台设备同时运行
start_app.sh并识别——SQLite 会报错database is locked,且可能导致文件损坏; - 推荐替代方案:定期导出为 CSV,再导入到目标设备(WebUI 支持 CSV 导入功能)。
6. 超越查看:用history.db做真正有用的事
当history.db在你手中不再是个黑盒,它就从“记录容器”升级为“数据引擎”。以下是三个零门槛、高回报的进阶用法。
6.1 用 Python 快速导出结构化 CSV(5行代码)
无需安装额外包,纯 Python 标准库即可:
import sqlite3 import csv from datetime import datetime conn = sqlite3.connect("webui/data/history.db") cursor = conn.cursor() cursor.execute("SELECT * FROM recognition_history ORDER BY timestamp DESC") rows = cursor.fetchall() with open(f"funasr_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([desc[0] for desc in cursor.description]) # 表头 writer.writerows(rows) print(" 导出完成!")导出的 CSV 可直接导入 Excel、Notion、飞书多维表格,做关键词统计、时长分析、语言分布图。
6.2 用 DB Browser for SQLite 做即时分析
- 下载 DB Browser for SQLite(免费);
- 打开
history.db→ 切换到 “Execute SQL” 标签页; - 输入分析语句,例如:
-- 统计各语言识别次数 SELECT language, COUNT(*) as count FROM recognition_history GROUP BY language; -- 查找含“故障”的所有记录(客服质检场景) SELECT timestamp, filename, raw_text FROM recognition_history WHERE raw_text LIKE '%故障%' OR normalized_text LIKE '%故障%';
6.3 构建个人语音知识库(自动化)
将history.db作为源头,接入 Obsidian 或 Logseq:
- 编写脚本,每天凌晨将新增记录生成 Markdown 文件(按日期归档);
- 每条记录生成一个
.md文件,标题为timestamp + filename,正文为raw_text; - 启用 Obsidian 的双向链接,自动关联“项目”、“客户”、“产品”等关键词;
- 最终形成一个可全文搜索、可图谱可视化的语音知识网络。
7. 总结:把数据库当资产管,而不是当日志删
Fun-ASR 的history.db是一个教科书级的轻量级数据设计范例:单表、明结构、强事务、零依赖。它小到可以忽略,却又大到承载着你所有语音工作的数字痕迹。
- 它的 9 个字段中,
id和timestamp是时间锚点,file_path和raw_text是业务入口,use_itn和normalized_text是质量标尺; - 它的写入逻辑不炫技,但每一步都经得起断电考验;
- 它的 WebUI 查询虽未加索引,但 1000 条以内毫无压力;
- 它的脆弱性不在技术,而在人的操作习惯——一次误删,就是一次不可逆的知识蒸发。
所以,请现在就做三件事:
- 打开你的
webui/data/history.db,用 DB Browser 看一眼recognition_history表里到底存了什么; - 创建一个
backup/目录,写一行cp命令加入crontab; - 把本文收藏,下次想点“清空所有记录”前,先读一遍第 4.2 节。
技术的价值,从来不在它多酷炫,而在于它是否真的为你守住了那些值得记住的声音。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。