核心内容摘要
Qwen-Image-2512-Pixel-Art-LoRA开发者案例:基于Gradio构建可扩展像素艺术SaaS原型
用Python分析Fun-ASR的history.db提取全部记录Fun-ASR作为一款面向本地部署的语音识别系统其WebUI界面简洁直观但真正沉淀业务价值的往往不是界面上一闪而过的识别结果而是那些被默默存入webui/data/history.db中的每一条历史记录。
这个SQLite数据库文件虽小却完整保存了每一次识别的上下文音频来源、语言设置、热词配置、原始文本与规整后文本——它是一份结构清晰、可编程访问的语音操作日志。
很多用户只把Fun-ASR当作“即用即走”的工具直到某次误点“清空所有记录”或重装系统后发现两周的会议转写全没了才意识到那个.db文件才是你语音资产的唯一副本。
本文不讲如何部署模型、不调参数、不比准确率而是聚焦一个务实问题如何用Python安全、稳定、可复用地读取并导出全部识别记录你会学到一套轻量级但生产可用的数据提取方案——无需修改Fun-ASR源码不依赖WebAPI不启动服务仅靠标准库就能把history.db变成你的结构化数据源。
理解history.db不是日志是结构化数据库Fun-ASR将所有识别历史统一存入单个SQLite数据库路径固定为webui/data/history.db。
它不是文本日志也不是临时缓存而是一个符合ACID特性的关系型数据库文件。
这意味着数据写入时自动事务提交断电也不会丢记录支持标准SQL查询可按时间、关键词、语言等任意字段筛选表结构稳定v
1.
0至今未变更兼容性好可直接用Python内置sqlite3模块读取零第三方依赖。
只要Fun-ASR运行过至少一次识别该文件就已存在且可读。
你不需要登录WebUI甚至不需要启动服务只需找到这个文件就能开始分析。
小提示在Linux/macOS中可通过find /path/to/funasr -name history.db快速定位Windows用户可在资源管理器中搜索history.db注意检查是否在webui\data\子目录下反斜杠路径。
数据表结构详解一张表九个关键字段Fun-ASR使用单一数据表recognition_history存储全部记录建表语句如下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 );我们逐字段说明其实际含义与使用价值
1 核心标识字段id自增整数主键全局唯一。
它是记录的“身份证号”也是WebUI中“查看详情”“删除记录”功能的依据。
timestampISO格式时间字符串如
14:23:10。
精确到秒可用于按时间排序、统计日频次、绘制趋势图。
2 音频上下文字段filename上传时的原始文件名如team_meeting_
mp3不含路径便于快速识别来源。
file_path音频在服务器上的绝对或相对路径如/home/user/audio/team_meeting_
mp3。
这是追溯原始音频的关键线索尤其当批量处理多个目录时。
3 识别配置字段language识别所用语种代码常见值为zh中文、en英文、ja日文。
可用于分语言统计识别量或质量。
hotwords热词列表以逗号分隔的字符串如钉钉,通义,Fun-ASR。
若未设置则为空字符串。
可用于分析哪些专业术语被高频强化。
use_itn布尔值SQLite中存为0/1表示是否启用智能文本规整。
值为1代表启用了ITN输出更规范如“二零二五年”→“2025年”。
4 文本内容字段raw_text模型原始输出保留口语化表达、重复词、语气词等。
适合做语音错误分析、ASR模型诊断。
normalized_text经ITN规整后的文本更接近书面语可读性强适合导入知识库、生成摘要、做NLP下游任务。
实用建议日常使用中优先查看normalized_text做模型效果对比或调试时重点分析raw_text与normalized_text的差异。
Python提取全流程从连接到导出下面提供一套完整、健壮、可直接运行的Python脚本用于安全读取并导出全部记录。
代码基于标准库sqlite3兼容Python
7无需安装额外包。
1 基础连接与查询函数import sqlite3 import os from datetime import datetime from typing import List, Dict, Optional def connect_to_history_db(db_path: str) - Optional[sqlite
Connection]: 安全连接history.db验证文件存在且可读 返回数据库连接对象失败返回None if not os.path.exists(db_path): print(f❌ 错误数据库文件不存在 — {db_path}) return None if not os.access(db_path, os.R_OK): print(f❌ 错误无读取权限 — {db_path}) return None try: conn sqlite
connect(db_path) # 验证表是否存在 cursor conn.cursor() cursor.execute(SELECT name FROM sqlite_master WHERE typetable AND namerecognition_history;) if not cursor.fetchone(): print(f❌ 错误数据库中缺少表 recognition_history) conn.close() return None return conn except Exception as e: print(f❌ 连接数据库失败{e}) return None def fetch_all_records(conn: sqlite
Connection) - List[Dict]: 查询全部记录返回字典列表字段名与数据库一致 cursor conn.cursor() cursor.execute( SELECT id, timestamp, filename, file_path, language, hotwords, use_itn, raw_text, normalized_text FROM recognition_history ORDER BY id ASC ) columns [desc[0] for desc in cursor.description] rows cursor.fetchall() records [] for row in rows: record dict(zip(columns, row)) # 将use_itn转换为Python布尔值 record[use_itn] bool(record[use_itn]) records.append(record) return records
2 导出为CSV最通用的交换格式CSV格式兼容Excel、Google Sheets、数据库导入、BI工具是跨平台共享的首选。
import csv def export_to_csv(records: List[Dict], output_path: str): 将记录列表导出为CSV文件 if not records: print( 提示无记录可导出) return # 定义CSV字段顺序按数据库字段但调整为更易读的顺序 fieldnames [ id, timestamp, filename, file_path, language, hotwords, use_itn, raw_text, normalized_text ] try: with open(output_path, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() for record in records: # CSV不支持布尔值转为字符串 record_copy record.copy() record_copy[use_itn] True if record_copy[use_itn] else False writer.writerow(record_copy) print(f 成功导出 {len(records)} 条记录至{output_path}) except Exception as e: print(f❌ 导出CSV失败{e}) # 使用示例 if __name__ __main__: DB_PATH webui/data/history.db # 替换为你的实际路径 OUTPUT_CSV ffunasr_history_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv conn connect_to_history_db(DB_PATH) if conn: records fetch_all_records(conn) print(f 共读取 {len(records)} 条识别记录) export_to_csv(records, OUTPUT_CSV) conn.close()注意encodingutf-8-sig确保Excel能正确识别中文-sig前缀添加BOM头避免乱码。
3 导出为JSON适合程序集成与API对接JSON格式保留原始数据类型如布尔值、null更适合后续Python、JavaScript程序解析。
import json def export_to_json(records: List[Dict], output_path: str): 将记录列表导出为JSON文件保持数据类型 try: with open(output_path, w, encodingutf-
as f: json.dump(records, f, ensure_asciiFalse, indent
print(f 成功导出 {len(records)} 条记录至{output_path}) except Exception as e: print(f❌ 导出JSON失败{e}) # 在主程序中添加调用 # export_to_json(records, ffunasr_history_{datetime.now().strftime(%Y%m%d_%H%M%S)}.json)
4 按条件筛选只导出你需要的部分实际工作中你可能只想导出某段时间、某种语言或含特定关键词的记录。
以下函数支持灵活过滤def filter_records( records: List[Dict], start_time: str None, end_time: str None, language: str None, keyword_in_text: str None ) - List[Dict]: 按条件筛选记录 start_time/end_time 格式
00:00:00 filtered records.copy() if start_time: filtered [r for r in filtered if r[timestamp] start_time] if end_time: filtered [r for r in filtered if r[timestamp] end_time] if language: filtered [r for r in filtered if r[language] language] if keyword_in_text: kw keyword_in_text.lower() filtered [ r for r in filtered if kw in r.get(raw_text, ).lower() or kw in r.get(normalized_text, ).lower() ] return filtered # 使用示例导出今天所有中文记录 # today datetime.now().strftime(%Y-%m-%d) # today_records filter_records( # records, # start_timef{today} 00:00:00, # end_timef{today} 23:59:59, # languagezh # ) # export_to_csv(today_records, today_chinese.csv)
进阶技巧让数据真正为你所用导出只是第一步。
当你拥有了结构化数据就可以解锁更多实用场景。
1 快速统计一眼看清使用情况from collections import Counter def quick_stats(records: List[Dict]): 打印基础统计信息 if not records: return print(\n 快速统计报告) print(- *
print(f总记录数{len(records)}) print(f最早记录{records[0][timestamp]}) print(f最新记录{records[-1][timestamp]}) # 按语言统计 lang_counter Counter(r[language] for r in records) print(f\n按语言分布{dict(lang_counter)}) # ITN启用率 itn_enabled sum(1 for r in records if r[use_itn]) print(fITN启用率{itn_enabled}/{len(records)} ({itn_enabled/len(records)*100:.1f}%)) # 平均文本长度 norm_lens [len(r[normalized_text]) for r in records if r[normalized_text]] if norm_lens: print(f平均规整文本长度{sum(norm_lens)//len(norm_lens)} 字) # 调用 # quick_stats(records)
2 关键词提取发现高频讨论主题利用jieba需pip install jieba对normalized_text做简单分词快速发现会议或客服对话中的核心话题import jieba from collections import Counter def extract_top_keywords(records: List[Dict], top_k: int
: 从规整文本中提取高频关键词中文 all_words [] for r in records: text r.get(normalized_text, ) if text: # 过滤停用词简单版去掉标点、单字、常见虚词 words jieba.lcut(text) words [w.strip() for w in words if len(w.strip()) 1] all_words.extend(words) counter Counter(all_words) print(f\n 高频关键词前{top_k}) for word, count in counter.most_common(top_k): print(f {word} — {count} 次) # 调用需先安装jieba # extract_top_keywords(records)
3 自动备份脚本防误删的最后一道防线将上述逻辑封装为定时备份脚本每天凌晨自动执行#!/bin/bash # backup_history.sh DATE$(date %Y%m%d_%H%M%S) DB_PATH/path/to/funasr/webui/data/history.db BACKUP_DIR/backup/funasr mkdir -p $BACKUP_DIR cp $DB_PATH $BACKUP_DIR/history_$DATE.db echo Backup done: history_$DATE.db配合crontabLinux/macOS# 每天凌晨2:00执行 0 2 * * * /path/to/backup_history.sh
5.
常见问题与避坑指南Q1脚本运行报错“No such table: recognition_history”A确认数据库路径正确且Fun-ASR确实已执行过至少一次识别首次运行不会自动建表。
可手动检查sqlite3 webui/data/history.db .tables # 应输出recognition_historyQ2导出CSV后Excel打开全是乱码A务必使用encodingutf-8-sig注意-sig这是Windows Excel识别UTF-8的必要前缀。
Q3file_path字段为空无法定位原始音频A这是Fun-ASR的设计行为——当通过麦克风录音时file_path为空只有上传本地文件时才填写。
建议在批量处理时统一使用绝对路径上传便于溯源。
Q4想合并多个Fun-ASR实例的历史记录ASQLite支持ATTACH命令。
在新数据库中执行ATTACH instance
db AS db1; ATTACH instance
db AS db2; INSERT INTO recognition_history SELECT * FROM db
recognition_history; INSERT INTO recognition_history SELECT * FROM db
recognition_history;Q5能否实时监听新记录并自动触发动作A可以。
SQLite不支持原生触发器监听但可通过轮询id最大值实现last_id 0 while True: current_max get_max_id_from_db() # SELECT MAX(id) FROM ... if current_max last_id: new_records fetch_records_since(last_id
process_new_records(new_records) # 如发通知、存ES last_id current_max time.sleep(
# 每30秒检查一次
6.
总结把语音数据变成你的数字资产Fun-ASR的history.db不是一个需要敬畏的黑箱而是一份设计清晰、接口开放、易于编程的数据源。
通过本文提供的Python脚本你可以零依赖提取全部记录仅用sqlite3不改一行Fun-ASR代码按需导出结构化数据CSV供业务分析JSON供程序集成灵活筛选与统计按时间、语言、关键词快速定位目标记录构建自动化护城河定时备份、异常告警、跨实例合并让语音资产真正可控。
技术的价值不在于它多炫酷而在于它是否让你对数据拥有确定性。
当你能随时说出“上周三下午三点的客户咨询内容是什么”并一键导出原文你就已经把语音识别从一个功能升级为一种工作方式。
现在就打开终端运行第一行python extract_history.py吧。
你的语音数据值得被认真对待。
--- **