项目工作总结

项目工作总结

项目:中华心法问答系统
整理时间:2026-11-03

一、项目概览

中华心法问答系统是一套面向传统文化问答场景的检索系统:Web 端(Jinja2 + 原生 JS)、uni-app 小程序(H5 / 微信小程序)、Flask + MySQL 后端,支持四种语义向量模型、语音输入与播报、知识库回收站,以及面向运维的部署与打包方案。

维度现状
后端Flask(app-factory + Blueprint),138+ 路由,MySQL 8/9
前端Jinja2 + 原生 JS,设计系统static/css/app.css(宣纸 / 朱砂)
小程序uni-app,8 个页面,H5 与微信小程序双端可构建
知识库1529 条已入库问答
用户18 个(user / admin / superadmin)
向量模型BERT-base(768)、LAUR 混合权重(768,11 档比例)、BGE-large-zh-v1.5(1024)、Conan-embedding-v1(1792)
模型切换异步 + 进度百分比 + 完成后自动刷新;缓存命中约 3–5 秒
语音Web Speech ASR + Vosk 离线兜底;edge-tts + Windows SAPI 兜底
部署腾讯云 + 宝塔 + Nginx + Gunicorn;app / bert / bge / conan 四个部署包
主版本服务http://127.0.0.1:5000
检索实验环境F:\mdqa\xinfa-b,http://127.0.0.1:5001,独立库xinfa_db_b

二、界面统一与小程序

统一全站视觉

  • 以原有 mdqa 模板为参考,建立统一设计令牌:--cinnabar #8C3A1E、--paper #F5F0E8、--card #FFFDF9、--ink #241C18、--line #E2D6C6
  • 统一组件:页头.page-head、卡片.card、按钮.btn、徽标.badge、表格.table-wrap、空状态.empty-state
  • 所有页面继承templates/base.html,统一导航、表单、表格与空状态
  • 整理目录结构,清理历史备份与临时文件

小程序重写

  • 重写 8 个页面:首页、搜索、详情、标签、提交、留言板、个人中心、登录
  • H5 与 mp-weixin 两端构建通过,接口与 Web 端对齐
  • 文档:docs/小程序功能对齐PRD.md、docs/小程序后端接口对齐.md
  • 截图:docs/screenshots/uniapp/*.png

三、语音输入与播报

能力

  • 语音输入:浏览器 Web Speech API 优先,失败时走/api/asr(Vosk 离线模型vosk-model-small-cn-0.22)
  • 语音播报:/api/tts(edge-tts),失败自动降级 Windows SAPI
  • 支持音色、语速设置,并按页面持久化
  • 搜索结果卡片动态生成「朗读」按钮

做过的稳定性修复

问题根因处理
朗读没有声音stop()把audioEl置空保留元素,只清src
动态按钮点不动事件只绑首屏节点改为 document 捕获阶段事件委托
前端修改不生效Jinja 模板缓存 + 静态缓存重启后端、资源加?v=版本号
切换音色/点击闪退语音对象状态竞争播放令牌 + 停止旧实例 + 异常兜底
edge-tts 503 / 0 字节缓存服务偶发失败、半成品写入重试、超时、.part原子替换、最小字节校验

四、云服务器部署

  • 打包脚本scripts/package_for_deploy.ps1:
    • -IncludeVosk带离线语音模型
    • -IncludeBge带 BGE 模型
    • -IncludeConan带 Conan 模型
    • -SkipModel只打代码包
  • 部署配置:deploy/nginx.conf、deploy/xinfa_qa.service、gunicorn_config.py
    • gunicorn:workers=1、gthread、threads=8、timeout=300、bind 127.0.0.1:8000
  • 指南:deploy/部署指南-腾讯云宝塔.md(Python 3.11、MySQL、Nginx、COS、systemd、升级 SQL)
  • requirements.txt的 20 个依赖在 Python 3.11 下校验通过;BGE / Conan 依赖拆分为requirements-bge.txt、requirements-conan.txt

当前部署包:

xinfa_qa_app_20261031_1251.zip 85.1 MB (代码 + 四模式向量缓存) xinfa_qa_bert_20261029_1446.zip 364.5 MB xinfa_qa_bge_20261029_1446.zip 721.6 MB xinfa_qa_conan_20261031_1251.zip 732.0 MB

五、知识库治理

超级管理员删除

  • 搜索结果卡片新增「移入回收」,仅role == 'superadmin'渲染
  • 后端POST /admin/delete-content/<type>/<id>二次校验角色,非超管返回 403
  • 布局:朗读在左、移入回收在右,危险色描边 + 二次确认

软删除与回收站

  • question / video / article增加is_deleted、deleted_at、deleted_by_id
  • 通过 SQLAlchemydo_orm_execute+with_loader_criteria全局过滤,删除后全站不可见
  • 删除问答时从内存向量索引移除并刷新缓存
  • 新增「已删除数据」页面(仅超管):恢复 / 永久删除;恢复时重新加入索引

批量、筛选与操作日志

  • 批量恢复 / 批量永久删除(/admin/deleted-content/batch,单次 ≤200)
  • 按类型、删除时间区间筛选
  • operation_log表记录 delete / restore / purge:操作人、对象、IP、时间
  • 回收站页展示最近 30 条日志
  • 迁移 SQL 写入 README 与部署指南

六、小助手

  • 仅首页 / 问答页加载,默认右下角,可拖拽并本地持久化位置
  • 尺寸随视口自适应(clamp(76, min(7vw, 26vh), 240)),2560 分辨率下不再过小
  • 三态动画:
    • 待机:assistant-anim.webp(stand.gif,71 帧,5.68 秒循环)
    • 搜索后:assistant-search.webp(search.gif,90 帧,7.2 秒循环)
    • 切换用 160ms 淡入淡出
  • 统一画布 420×690、人物高度 618px,切换不跳大小
  • 双击播报「今天有什么想问的」;右键复位位置
  • 透明背景干净(搜索态约 77% 透明,无白边)

七、模型体系与切换体验

默认模型

  • 默认模式改为 BGE(MODEL_MODE默认bge,.env可覆盖)
  • 切换成功后 1.6 秒自动刷新页面,并提示「页面即将刷新…」

按模式缓存

  • 缓存按模式拆分:data/embeddings/vectors_{bert,laur,bge}.npz
  • 指纹包含问题集合、模式、维度、权重、模型路径;数据或配置变化才重建
  • 旧版单文件缓存首次启动自动迁移
  • 切换改为后台线程 + 轮询/api/model_switch_progress,进度示例:
    • 读取问答数据 241/1529 6%
    • 正在生成向量 600/1529(40%)
    • 载入向量缓存… 60%
    • ✅ 已切换到 XXX 模式,页面即将刷新…

LAUR 混合比例

  • 滑块范围BERT 0.0 : 1.0 LAUR至1.0 : 0.0,共 11 档
  • 异步切换 + 同一进度接口,按比例分文件缓存
  • 加速技巧:LAUR 向量 =normalize(BERT 向量 × 权重矩阵),直接由 BERT 缓存推导,3.2–3.8 秒完成(此前约 70 秒)

缓存误判修复

  • 问题:指纹含hybrid_ratio,在 LAUR 下调过比例后切 BGE 会被判缓存失效,重新编码约 12 分钟
  • 修复:BERT / BGE 模式对 0.0–1.0 共 11 个比例逐一比对指纹
  • 结果:LAUR(0.4) ↔ BGE 来回切换 2.8–3.1 秒

Conan-embedding-v1 集成

  • 结构:BERT-large 中文(24 层 / 1024)+ mean pooling + Dense 1024→1792
  • 懒加载(1.24GB,切到 CONAN 才加载),独立缓存vectors_conan.npz(9.7MB)
  • 与其它模式对齐:按钮、进度、自动刷新、失败回滚
  • 首次构建 472 秒;热切换 3.7–5.2 秒

性能对比(1529 条,CPU):

模式维度单条编码单次检索文档吞吐热切换缓存内存
BERT76846.4 ms65 ms13.3 条/s4.33 s4.2 MB963 MB
LAUR76829.8 ms38 ms13.1 条/s3.80 s4.2 MB977 MB
BGE1024399.2 ms198 ms1.9 条/s4.47 s5.6 MB984 MB
Conan1792132.8 ms102 ms3.5 条/s5.19 s9.7 MB2168 MB

C-MTEB(论文 Table 3):Conan72.62,BGE-large-zh-v1.5 官方约 64.5。

详细报告:docs/Conan模型集成与性能对比.md


八、标注与检索优化方案

产出docs/检索与标注优化方案.md:

  • 现状诊断:120 条扰动 query 自检索,BERT Recall@1 0.55、BGE 0.95、Conan 0.92;缺少固定评测集
  • 标注难的四层解法:
    • L0 自动弱标签(LLM 改写问法、答案反推问题、难负例)
    • L1 隐式反馈(点击、停留、换词、无结果)
    • L2 LLM 预标注 + 人工抽检(成本约为全人工的 1/8)
    • L3 主动学习(只标模型最不确定的 100–200 条)
  • 检索四阶段路线:
    • 混合检索(BM25 + 向量 RRF)、多路召回、置信度阈值、去重
    • 查询改写、意图分类、答案分块
    • 重排(bge-reranker)
    • 微调 / 蒸馏 / RAG 摘要
  • A/B/C 方案对比:1 周轻量 / 3 周标准 / 6 周完整

九、检索实验:混合检索与重排

为避免影响线上版本,单独建立了隔离环境:

  • 目录F:\mdqa\xinfa-b(代码副本,模型只读引用主目录)
  • 数据库xinfa_db_b(完整复制,1529 条问答、18 用户)
  • 端口 5001,主版本 5000 不受影响

混合检索

  • BM25:jieba 分词,问题权重 3、关键词 2、答案 1;k1=1.5、b=0.75
  • 向量 Top100 + BM25 Top50 → RRF(k=60,w_dense=1.0,w_bm25=0.7)
  • 近似问题去重(字符 Jaccard ≥0.9)
  • 低置信标记:Top1 相似度 < 0.5
  • /search与/api/search均已接入;开关RETRIEVAL_MODE=hybrid|dense

评测集与脚本

  • scripts/eval/build_eval_set.py:自动生成 300 条扰动 query(截断 / 丢字 / 口语前缀 / 关键词式),正例已知;100 条 test、200 条 dev
  • scripts/eval/run_eval.py:dense / hybrid / hybrid+rerank 对比,指标 Recall@1、Recall@10、MRR、nDCG@10、低置信率、P95 延迟

重排

  • 模型:bge-reranker-base(CrossEncoder,1.06GB,CPU)
  • 对 RRF 融合后的 Top5 精排;只对问题文本打分,5 条约 0.11 秒
  • 开关:RERANK=1、RERANK_TOP_K=5

低置信兜底

  • 后端返回low_confidence
  • 前端提示条:「🔍 这条问题没有找到特别匹配的答案?去提问留言板…」

标注后台

  • /admin/search-eval(管理员 / 超管),导航栏「检索评测」
  • 展示 query + Top6 候选(问题 / 答案 / 相似度 / 正例标记),三档标注(2 相关 / 1 部分 / 0 不相关),保存后自动跳下一条
  • 表relevance_label(query + question 唯一,记录标注人与时间)
  • 接口POST /admin/search-eval/label

评测结果(100 条 test,BGE)

指标densehybridhybrid+rerank(Top5)hybrid+rerank(Top10)
Recall@10.9000.9300.9500.960
Recall@100.9800.9900.9900.990
MRR0.9270.9540.9640.973
nDCG@100.9400.9630.9700.977
P95 延迟328ms502ms755–902ms1844ms

结论:Top5 重排以约 +0.2 秒换 Recall@1 +2pt;Top10 再多 +1pt 但延迟翻倍,默认取 Top5。


十、查询改写实验(未采用)

  • 实现了规则式改写:去口语前缀、口语词归一(啥 / 咋 / 为啥 / 能否)、去语气词、长查询关键词提取、15 组领域同义词扩展
  • 向量检索使用归一化 query;BM25 使用原始 + 归一化 + 关键词 + 同义词四路 RRF
  • 前端显示「💡 已按「xxx」理解你的问题」

评测结果:

指标不改写改写
Recall@1(普通集)0.9500.950
Recall@1(口语集)0.9330.933
MRR(口语集)0.9490.945
P95(口语集)951ms1050ms

结论:规则改写对「BGE + hybrid + rerank」没有增益,同义词扩展还略降 MRR、增加约 100ms。已完整回退该功能(删除模块、还原检索链路、移除接口字段与前端提示、清理相关评测数据),环境恢复到「混合检索 + 重排 + 低置信 + 标注后台」状态。


十一、关键指标

检索(100 条 test)

  • dense:Recall@1 0.900 / MRR 0.927 / nDCG@10 0.940 / P95 328ms
  • hybrid:0.930 / 0.954 / 0.963 / 502ms
  • hybrid + rerank Top5:0.950 / 0.964 / 0.970 / 755–902ms
  • BERT 基线下 hybrid 提升更明显:Recall@1 0.570 → 0.760(+19pt)

模型切换

操作耗时
BERT → LAUR(缓存)5.8s
LAUR → BGE(缓存)2.8–4.2s
BGE → Conan(缓存)3.7–5.2s
LAUR 比例切换(缓存 / 推导)3.2–3.8s
首次构建 BERT / LAUR / BGE / Conan77s / 148s / 753s / 472s

向量缓存

文件大小说明
vectors_bert.npz4.2MB768 维
vectors_bge.npz5.6MB1024 维
vectors_laur_*.npz各 4.2MB11 档比例(0.0–1.0,步长 0.1)
vectors_conan.npz9.7MB1792 维

vectors_laur_0.00.npz显示 0MB,实际为 20KB 高度可压缩文件,内容完整。


十二、产物清单

核心代码模块

路径作用
app/modules/qa/__init__.py搜索、问答、回收站、删除、历史等主路由
app/modules/admin/__init__.py模型切换、比例、配置、评测路由
app/modules/api/__init__.pyH5 / 小程序 API
app/qasystem.py四模型加载、缓存、异步切换、比例推导
app/modules/voice/、app/modules/asr/TTS / ASR
static/js/voice.js语音输入与朗读
static/js/assistant.js小助手三态、拖拽、双击语音
templates/deleted_content.html已删除数据(回收站)
models.pyQuestion / PendingQuestion / OperationLog / EvalQuery 等

检索实验新增

路径作用
app/modules/retrieval/__init__.pyBM25 + RRF + 去重 + 重排 + 低置信
templates/search_eval.html标注后台
scripts/eval/build_eval_set.py生成 300 条评测集
scripts/eval/run_eval.pydense / hybrid / rerank 跑分
B-方案说明.md检索实验全记录

文档

  • README.md(界面、小程序、语音、部署、检索、模型)
  • deploy/部署指南-腾讯云宝塔.md
  • docs/项目技术栈与团队协作指南.md
  • docs/小程序功能对齐PRD.md、docs/小程序后端接口对齐.md
  • docs/Conan模型集成与性能对比.md
  • docs/检索与标注优化方案.md
  • docs/项目工作总结.md

十三、当前运行状态

服务地址说明
主版本http://127.0.0.1:5000稳定版本,检索实验期间未改动
检索实验http://127.0.0.1:5001混合检索 + 重排 + 低置信 + 标注后台
实验数据库xinfa_db_b与主库隔离
模型主目录models/(只读)BERT / BGE / Conan / reranker

实验数据:300 条评测 query(100 test / 200 dev)、1 条人工标注、33 条检索日志。

健康检查:

GET / 200 POST /search 200(hybrid) POST /api/search 200(mode=hybrid、reranked=true) GET /admin/search-eval 200(管理员)

十四、经验与坑

类别问题结论
编码PowerShell 写中文变 GBK 乱码统一 UTF-8(写入工具 / Python),脚本尽量 ASCII
文件系统exFAT 原子写报EISDIR新建文件先建空文件再写入
Flask改模板 / 静态后不生效Jinja 模板缓存需重启后端;静态加?v=
SQLAlchemy新增deleted_by_id导致submitted_by外键歧义显式foreign_keys=[submitted_by_id]
SQLAlchemy列名query覆盖Model.query改为query_text
软删除多处查询需要过滤do_orm_execute+with_loader_criteria全局过滤
缓存指纹含hybrid_ratio导致 BGE 误重建非混合模式对比例容错匹配
缓存中断写入产生 0 字节缓存启动校验大小 / 维度,失败自动重建
重排FlagReranker与 transformers 5 不兼容改用sentence-transformers.CrossEncoder
重排打分返回 ndarray,长度判断失效list(scores)归一化
重排答案全文拼入 pair 太慢只对问题文本打分,Top5 精排
数据测试期误删 1 条真实问答(1530→1529)永久删除需更严格二次确认;定期备份
进程重启偶发端口占用先按端口杀进程、确认释放后再启动

十五、后续建议

  1. 标注 → 训练重排:先用标注后台标 50–100 条,再用relevance_label训练或蒸馏小重排模型。
  2. LLM 查询改写:仅在低置信 / 长查询时触发,避免全量 +100ms 开销。
  3. 检索方案投产:把retrieval模块合并回主版本,加开关灰度,重排只对首屏 Top5 生效。
  4. 长答案分块:对 500 字以上答案切块检索再聚合,提升细粒度问题命中。
  5. 数据安全:MySQL 每日备份 + 回收站保留策略 + 永久删除二次确认(输入标题 / 密码)。
  6. 监控:检索 P95、低置信率、模型切换耗时、缓存命中率接入看板。

十六、截图索引

主题截图
首页 / 登录 / 个人中心docs/screenshots/{home,login,profile}.png
审核 / 评分 / CSV 导入docs/screenshots/{review,rating_stats,csv_import}.png
小程序 8 页docs/screenshots/uniapp/*.png
语音docs/screenshots/voice/*.png
删除 / 回收站docs/screenshots/ui/{superadmin-delete,deleted-page,recycle-page}.png
小助手docs/screenshots/ui/{assistant-idle,assistant-search-pose}.png
模型切换 / 比例docs/screenshots/ui/{switch-progress,ratio-progress,conan-active}.png
检索实验F:\mdqa\xinfa-b\docs\screenshots\b\{search-eval,low-conf,rewrite-hint}.png