..
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00
2026-07-15 17:02:32 +08:00

Speakr - 定制版

基于 murtaza-nasir/speakr 二次开发的语音转录与智能笔记平台。

项目简介

本项目是一个自托管的 AI 语音转录和智能笔记平台,支持:

  • 语音录制与上传 - 浏览器直接录音或上传已有音频文件
  • AI 转录 - 高准确率语音转文字,支持说话人识别
  • 交互式对话 - 对录音提问,获取 AI 回答
  • 智能标签 - 支持带自定义 AI 提示词的标签
  • 安全分享 - 生成安全链接分享录音
  • 草稿功能 - 本地草稿保存与上传
  • 声纹管理 - 自定义说话人声纹识别
  • 标准词映射 - 自动纠正专业术语和人名

项目结构

speakr/
├── app.py                          # 入口文件(启动脚本)
├── src/
│   ├── app.py                      # Flask 应用主逻辑create_app 工厂模式)
│   ├── config.py                   # 统一配置文件(所有环境变量集中管理)
│   ├── logging_config.py           # 日志配置模块(格式化、输出目标、日志级别)
│   ├── flask_ext.py                # Flask 扩展实例集合LoginManager、Bcrypt、Limiter、CSRFProtect
│   ├── clients/                    # 外部服务客户端层
│   │   ├── __init__.py             # 统一导出所有客户端和配置
│   │   ├── database.py             # 数据库客户端db 实例 + 配置 + 初始化
│   │   ├── llm_client.py           # LLM 客户端OpenAI 客户端初始化
│   │   └── voiceprint_client.py    # 声纹客户端VoiceprintClient
│   ├── api/                        # API 路由层Flask 蓝图)
│   │   ├── __init__.py             # 统一蓝图注册函数 register_blueprints()
│   │   ├── template_filters.py     # 模板过滤器注册now、localdatetime
│   │   ├── auth.py                 # 认证路由(登录、注册、账户管理)
│   │   ├── recording.py            # 录音管理CRUD、上传、转录、下载
│   │   ├── share.py                # 分享功能
│   │   ├── tag.py                  # 标签管理
│   │   ├── speaker.py              # 说话人管理
│   │   ├── admin.py                # 管理后台
│   │   ├── search.py               # 搜索和 Inquire
│   │   ├── voiceprint.py           # 声纹管理
│   │   ├── config.py               # 系统配置管理
│   │   ├── hotword.py              # 热词管理
│   │   ├── template.py             # 转录模板
│   │   ├── event.py                # 事件管理
│   │   └── main.py                 # 主页和杂项
│   ├── models/                     # 数据模型层(每个模型独立文件)
│   │   ├── __init__.py             # 统一导出所有模型
│   │   ├── user.py                 # 用户模型
│   │   ├── speaker.py              # 说话人模型
│   │   ├── recording.py            # 录音模型
│   │   ├── tag.py                  # 标签模型
│   │   ├── recording_tag.py        # 录音-标签关联模型
│   │   ├── event.py                # 事件模型
│   │   ├── share.py                # 分享模型
│   │   ├── transcript_chunk.py     # 转录块模型
│   │   ├── transcript_template.py  # 转录模板模型
│   │   ├── inquire_session.py      # 询问会话模型
│   │   ├── voiceprint.py           # 声纹模型
│   │   ├── system_setting.py       # 系统设置模型
│   │   ├── system_config.py        # 系统配置模型
│   │   ├── hotword.py              # 热词模型
│   │   ├── draft_recording.py      # 草稿录音模型
│   │   ├── draft_segment.py        # 草稿片段模型
│   │   └── forms.py                # 表单类(注册、登录)
│   ├── services/                   # 业务逻辑层
│   │   ├── __init__.py             # 统一导出所有服务函数
│   │   ├── llm_service.py          # LLM 服务
│   │   ├── transcription_service.py # 转录服务
│   │   ├── summary_service.py      # 摘要服务
│   │   ├── embedding_service.py    # 嵌入向量服务
│   │   ├── event_service.py        # 事件服务
│   │   ├── document_service.py     # 文档服务
│   │   ├── speaker_service.py      # 说话人服务
│   │   ├── voiceprint_service.py   # 声纹服务(业务逻辑)
│   │   ├── auto_process_service.py # 自动处理服务
│   │   ├── auth_service.py         # 认证服务
│   │   ├── sync_service.py         # 远程数据同步服务
│   │   ├── audio_chunking.py       # 音频分块服务
│   │   └── file_monitor.py         # 文件监控服务
│   ├── utils/                      # 工具函数层
│   │   ├── __init__.py
│   │   ├── markdown.py             # Markdown 转 HTML 工具
│   │   ├── datetime.py             # 时区转换工具
│   │   ├── text_utils.py           # 文本处理工具
│   │   ├── json_utils.py           # JSON 处理工具
│   │   └── standard_word_mappings.py # 标准词映射工具
├── templates/                      # Jinja2 模板
├── static/                         # 静态资源CSS/JS/图片)
├── instance/                       # SQLite 数据库文件
├── uploads/                        # 上传的音频文件
└── .env                            # 环境变量配置

启动方式

前置条件

  • Python 环境(已安装 conda 环境 speakr
  • Nginx 反向代理(必须启动)

启动步骤

# 1. 进入项目根目录speakr 2 目录)
cd "c:\Work\zdht\python-projects\speakr 2"

# 2. 启动 Nginx 反向代理(必须先启动)
#    Nginx 负责将 https://localhost:8083/tool/speakr/ 请求转发到 Flask 后端
#    ⚠️ 注意:不启动 Nginx 将无法访问应用
nginx  # 或根据你的安装方式启动 nginx

# 3. 激活 conda 环境并启动应用
conda activate speakr
python .\speakr\app.py

Flask 后端默认监听 5000 端口Nginx 负责将 https://localhost:8083/tool/speakr/ 请求转发到 http://localhost:5000

访问地址

  • 主页面:https://localhost:8083/tool/speakr/
  • 管理后台:https://localhost:8083/tool/speakr/admin
  • 账户设置:https://localhost:8083/tool/speakr/account

常见错误

问题 原因 解决
页面无法访问 Nginx 未启动 启动 Nginx 反向代理
ModuleNotFoundError: No module named 'xxx' conda 环境未激活 执行 conda activate speakr
数据库文件位置错误 从错误目录启动 确保从 speakr 2/ 目录执行 python .\speakr\app.py

环境配置

编辑 .env 文件,主要配置项:

# ASR 服务配置
USE_ASR_ENDPOINT=true
ASR_BASE_URL=http://10.100.3.22:6688
ASR_DIARIZE=true

# 文本生成模型配置
TEXT_MODEL_BASE_URL=http://10.100.3.22:9800/v1
TEXT_MODEL_API_KEY=EMPTY
TEXT_MODEL_NAME=minimax-m2.5

# 数据库配置
SQLALCHEMY_DATABASE_URI=sqlite:///transcriptions.db
# PostgreSQL 配置示例(切换时只需修改 URI 即可)
# SQLALCHEMY_DATABASE_URI=postgresql://user:password@localhost/speakr
# DB_POOL_SIZE=10
# DB_MAX_OVERFLOW=20
# DB_POOL_TIMEOUT=30
# DB_POOL_RECYCLE=3600

# 外部访问端口Nginx 监听端口)
EXTERNAL_PORT=8083

# 时区配置
TIMEZONE=Asia/Shanghai

重构计划与当前进度

拆分原则

  1. 标记的模块是业务特色,拆分时只改代码组织结构,不改业务逻辑
  2. 拆分顺序:
    • 第一步:抽出 Models最安全纯数据定义
    • 第二步:抽出 Services把 app.py 中的业务逻辑函数提取到独立文件)
    • 第三步:抽出 API 蓝图(把路由按功能域分组注册)
    • 第四步:抽出 Utils工具函数独立化
    • 第五步:模板组件化(可选,优先级较低)
  3. app.py 最终只保留Flask 初始化、数据库初始化、蓝图注册、全局错误处理
  4. 保持向后兼容:拆分过程中所有 API 路径不变,前端无需改动

需要特别注意的业务逻辑

功能 说明 拆分注意事项
声纹管理 VoiceprintClient 类、声纹注册/比对/同步 保持 VoiceprintClient 与外部平台的交互逻辑不变
系统配置 SystemConfig 动态配置读写 保持配置缓存和热加载机制
草稿 API 已有独立蓝图 draft_bp 直接迁移,几乎不需改动
标准词映射 转写后的文本纠正 保持映射规则和应用逻辑
自动处理 定时/触发式自动转写 保持调度逻辑和状态管理
收件箱 录音的收件箱/高亮状态 保持查询和状态切换逻辑
热词 热词管理和应用 保持与转写服务的集成

当前进度

已完成Phase 1 - Models 层抽取

已将 src/app.py 中的 16 个数据模型2 个表单类 从主文件中抽取到独立模块:

状态 任务 说明
src/database.py(已移至 clients 创建独立数据库实例文件,集中管理 SQLAlchemy db 对象Phase 2.5 移至 clients/database.py
src/models/ 每个模型独立文件user.py, speaker.py, recording.py 等 16 个模型)
src/models/forms.py 表单类独立RegistrationForm, LoginForm, password_check
src/utils/markdown.py Markdown 转 HTML 工具函数
src/utils/datetime.py 时区转换工具函数
src/app.py 删除模型定义,改为从 src.models 导入

验证结果:应用可正常启动,数据库文件路径正确(speakr/instance/transcriptions.db

已完成Phase 2 - Services 层抽取

服务文件已全部创建src/services/ 目录下 11 个服务文件app.py 中的旧函数定义已全部删除。

状态 服务文件 包含函数
llm_service.py call_llm_completion, clean_llm_response, format_transcription_for_llm, process_streaming_with_thinking, extract_thinking_content, preprocess_long_transcription, extract_corrected_text, format_api_error_message
transcription_service.py transcribe_audio_asr, transcribe_audio_task, transcribe_single_file, transcribe_with_chunking, extract_audio_from_video, convert_to_wav
summary_service.py generate_title_task, generate_summary_only_task
embedding_service.py get_embedding_model, chunk_transcription, generate_embeddings, serialize_embedding, deserialize_embedding, process_recording_chunks, basic_text_search_chunks, semantic_search_chunks
event_service.py extract_events_from_transcript, generate_ics_content, escape_ical_text
document_service.py process_markdown_to_docx, get_recording_display_title, sanitize_download_filename_component, build_docx_download_filename, set_download_filename_header
speaker_service.py update_speaker_usage, identify_speakers_from_text, identify_unidentified_speakers_from_text
voiceprint_service.py sync_voiceprint_to_platformVoiceprintClient 已移至 clients 层)
auto_process_service.py initialize_file_monitor, get_file_monitor_functions
auth_service.py is_safe_url, auto_login
__init__.py 统一导出所有服务函数

已完成的工作:

  1. 删除了 app.py 中的 22 个重复函数定义,改为从服务层导入
  2. 创建了 src/utils/text_utils.pysrc/utils/json_utils.py,将工具函数移出 app.py
  3. 添加了服务层和工具层的导入
  4. 核心功能测试通过,应用运行正常

已完成Phase 2.5 - Clients 层创建

按照行业规范创建了 src/clients/ 层,统一管理与外部服务的连接客户端:

状态 客户端 说明
database.py 数据库客户端:db 实例 + 配置 + init_database() 初始化逻辑
llm_client.py LLM 客户端OpenAI 客户端初始化client, TEXT_MODEL_NAME, TEXT_MODEL_API_KEY 等)
voiceprint_client.py 声纹客户端VoiceprintClient 类 + voiceprint_client() 延迟初始化
__init__.py 统一导出所有客户端

重构成果:

  • 删除了旧 src/database.py(空实例文件),合并到 clients/database.py
  • 删除了 services/voiceprint_service.py 中的 VoiceprintClient 类,移到 clients/voiceprint_client.py
  • 17 个模型文件 + 6 个服务文件的 db 导入统一改为 from src.clients import db
  • 消除了层层传递 client 参数的问题
  • 外部服务客户端与业务逻辑分离,架构更清晰

已完成Phase 2.6 - 服务层注释中文化

将所有服务层文件中的英文注释和日志消息翻译为中文,提升代码可读性:

文件 翻译内容
clients/llm_client.py URL 注释清理逻辑、客户端初始化逻辑
clients/voiceprint_client.py 声纹客户端注释和日志(新建文件,全中文)
clients/database.py 数据库配置和初始化注释(新建文件,全中文)
services/llm_service.py 思考内容提取、流式处理、API 错误格式化、分段总结等函数的注释和日志
services/summary_service.py 摘要生成全流程的注释和日志(标签提示词、语言要求、系统/用户消息构建等)
services/transcription_service.py 音频提取、ASR 转录、分块转录、Whisper 调用等全流程注释和日志
services/embedding_service.py 文本分块、嵌入向量生成、语义搜索等全流程注释和日志
services/voiceprint_service.py 声纹同步业务逻辑注释

已完成Phase 3 - API 蓝图抽取

src/app.py 中的约 100 个路由定义按功能域分组抽取到独立的 Flask 蓝图中app.py 从约 10000 行精简到约 700 行:

状态 蓝图文件 路由数量 说明
api/auth.py 5 注册、登录、登出、账户管理、修改密码
api/recording.py 24 录音 CRUD、上传、转录、下载、状态管理、ASR 校正
api/share.py 7 分享创建、查看、访问
api/tag.py 6 标签 CRUD、应用到录音
api/speaker.py 5 说话人管理、搜索
api/admin.py 18 管理后台、统计分析、录音管理、配置管理
api/search.py 7 搜索、Inquire 对话、收件箱
api/voiceprint.py 6 声纹注册、管理、同步
api/config.py 6 系统配置读写、分段服务管理
api/hotword.py 2 热词管理
api/template.py 5 转录模板 CRUD
api/event.py 3 事件提取、ICS 导出
api/main.py 5 主页、摘要生成、对话

架构改进:

  • 创建 api/__init__.py 中的 register_blueprints() 函数,统一管理所有蓝图注册和初始化
  • 使用 init_xxx_bp() 延迟注入模式解决循环导入问题limiter、csrf、bcrypt 等全局对象)
  • 所有 API 路径保持不变,前端无需任何修改
  • app.py 仅保留Flask 初始化、配置加载、蓝图注册、全局错误处理、启动逻辑

重构后修复的问题:

问题 原因 解决方案
重定向循环 main.py 中的 index() 路由误加了 @login_required 装饰器,丢失了自动登录逻辑 恢复备份中的 token 验证和自动登录逻辑,移除装饰器
WebSocket 连接失败 getUserConfig 路由缺少 FUNASR_WEBSOCKET_IPSCREEN_PUSH_IP 配置 main.pyget_user_config() 中补充 WebSocket 配置字段
循环导入 bcrypt main.py 在模块级别导入 bcrypt 导致循环依赖 改为在 init_main_bp() 中延迟注入 bcrypt_instance

已完成:统一配置管理

创建 src/config.py 统一配置文件,将所有散落的环境变量读取集中管理:

配置域 包含变量
基础配置 SECRET_KEY, UPLOAD_FOLDER, LOG_LEVEL, MAX_FILE_SIZE_MB
ASR 配置 USE_ASR_ENDPOINT, ASR_BASE_URL, ASR_DIARIZE, ASR_MIN_SPEAKERS, ASR_MAX_SPEAKERS
LLM 配置 TEXT_MODEL_API_KEY, TEXT_MODEL_BASE_URL, TEXT_MODEL_NAME
分段配置 ENABLE_CHUNKING
Inquire 配置 ENABLE_INQUIRE_MODE
数据库配置 SQLALCHEMY_DATABASE_URI, DB_POOL_SIZE, DB_MAX_OVERFLOW, DB_POOL_TIMEOUT, DB_POOL_RECYCLE
外部 API TRANSCRIPTION_BASE_URL, VALID_TOKEN, EMBEDDINGS_AVAILABLE
代理配置 HTTP_PROXY, HTTPS_PROXY

重构成果:

  • clients/llm_client.pyclients/voiceprint_client.pysrc.config 导入配置
  • clients/__init__.py 统一导出所有配置项
  • app.py 通过 from src.clients import ... 统一导入配置,消除散落的环境变量读取
  • 提供 validate_config() 函数在启动时验证关键配置

已完成Phase 3.5 - app.py 清理与同步服务抽取

src/app.py 中残留的约 400 行业务逻辑彻底清理app.py 从约 700 行精简到约 234 行:

状态 任务 说明
删除重复的 preprocess 函数 删除了 preprocess_long_transcription 等 6 个重复定义的函数(已在 llm_service.py 中)
抽取远程同步服务 创建了 services/sync_service.py,将约 180 行同步逻辑从 app.py 移出
清理无用 import 删除了 30+ 个未使用的导入render_template、OpenAI、httpx、subprocess、markdown 等)
注释中文化 将所有英文注释翻译为中文,删除无意义注释
修复函数签名 start_sync_thread() 增加 app 参数,调用处同步更新
优化配置读取 preprocess_long_transcription 等函数直接从 src.config 读取配置,不再通过参数传递

已完成Phase 4 - app.py 工厂模式重构

采用 Flask create_app() 工厂模式,将 src/app.py 从 234 行进一步精简到 60 行,各层职责清晰分离:

新建模块:

文件 职责
src/logging_config.py 日志配置模块
src/flask_ext.py Flask 扩展实例集合login_manager、bcrypt、limiter、csrf
src/services/register.py 服务层统一初始化(音频分段、同步线程、文件监控、表结构迁移)
src/api/template_filters.py 模板过滤器注册

修改模块:

文件 修改内容
src/config.py 新增 get_app_config() 函数,一次性返回所有需写入 app.config 的配置项。新增配置只需改此函数,无需修改 app.py
src/clients/__init__.py 集中管理 chunking_serviceEMBEDDINGS_AVAILABLE 的初始化与导出
src/api/__init__.py 简化 register_blueprints() 函数,直接从 src.flask_ext 导入扩展实例
src/app.py 采用 create_app() 工厂模式,仅负责编排各层初始化顺序

app.py 最终结构60 行):

create_app()
├── 1. configure_logging()         ← src/logging_config.py
├── 2. 创建 Flask 应用实例
├── 3. 加载应用配置                ← src/config.py 的 get_app_config()
├── 4. 配置反向代理支持
├── 5. 初始化 Flask 扩展           ← src/flask_ext.py
├── 6. 初始化数据库                ← src/clients/database.py
├── 7. 注册蓝图                    ← src/api/__init__.py
├── 8. 注册模板过滤器              ← src/api/template_filters.py
└── 9. 初始化服务层                ← src/services/register.py

关键设计决策:

决策 理由
flask_ext.py 独立 Flask 扩展是全局单例,不应放在 clients 或 services 中
get_app_config() 集中管理 新增配置只需改 config.pyapp.py 永远不需要改动
register_blueprints 自行读扩展 蓝图注册最清楚自己需要什么扩展,无需 app.py 中转
init_services 放在最后 服务依赖数据库和蓝图,必须在它们之后初始化

开发规范

API 接口返回格式规范

重要:所有新建/修改的 API 接口必须使用 ApiResponse 工具类统一返回格式。旧接口暂不强制迁移。

工具位置

src/api/api_response.py

使用方式

from src.api.api_response import ApiResponse

# 成功响应(返回数据)
@blueprint.route('/xxx', methods=['POST'])
def create_xxx():
    result = do_something()
    return ApiResponse.success({'xxx': result.to_dict()})

# 成功响应(带提示消息)
    return ApiResponse.success({'xxx': result.to_dict()}, '创建成功')

# 成功响应(自定义状态码,如 201 Created
    return ApiResponse.success({'xxx': result.to_dict()}, '创建成功', status_code=201)

# 错误响应
    if not xxx:
        return ApiResponse.error('资源未找到', 404)

# 错误响应(带详情)
    if not valid:
        return ApiResponse.error('参数校验失败', 400, detail='email 格式不正确')

# 分页响应
    return ApiResponse.paginated(
        items=[item.to_dict() for item in items],
        pagination={
            'page': page,
            'per_page': per_page,
            'total': total,
            'total_pages': total_pages,
            'has_next': has_next,
            'has_prev': has_prev
        }
    )

响应格式约定

成功响应结构:

{
  "success": true,
  "message": "操作成功(可选)",
  "xxx": {...}  // 业务数据直接合并到顶层
}

分页响应结构:

{
  "success": true,
  "items": [...],
  "pagination": {
    "page": 1,
    "total": 100,
    ...
  }
}

错误响应结构:

{
  "error": "用户友好的错误提示"
}

注意事项

  1. data 参数必须是字典类型,列表数据请使用 paginated() 或直接合并到字典中
  2. 前端通过 response.okHTTP 状态码)判断成功/失败,通过 data.error 获取错误信息
  3. 旧接口暂不迁移,新接口和修改的接口必须使用 ApiResponse
  4. 不要在 ApiResponse 中直接传递原始异常信息给用户(str(e)),应转为友好提示

技术栈

  • 后端: Python/Flask + SQLAlchemy
  • 前端: Vue.js 3 + Tailwind CSS
  • AI: OpenAI Whisper (ASR) + Ollama/OpenRouter (LLM)
  • 数据库: SQLite默认/ PostgreSQL支持环境变量切换
  • 部署: Nginx 反向代理 + Flask 开发服务器

依赖安装

conda activate speakr
pip install -r requirements.txt

主要依赖:

  • flask==2.3.3
  • flask-sqlalchemy==3.1.1
  • flask-login==0.6.3
  • flask-wtf==1.2.2
  • flask-bcrypt==1.0.1
  • openai==1.3.0
  • markdown==3.5.1
  • python-docx==1.1.0

许可证

本项目基于 AGPL v3.0 开源协议。