Files
rag/docs/生产路径优化计划.md
lacerate551 100d1a06eb init: RAG 知识库服务初始提交
- 后端 API(Flask + Gunicorn)
- RAG 引擎(混合检索 + 云端 Reranker + 引用溯源)
- 文档解析(MinerU + 多格式支持)
- Docker 生产部署配置
- 排除前端项目、敏感配置、模型文件
2026-06-04 17:35:27 +08:00

11 KiB
Raw Blame History

生产 RAG 路径优化计划

原则:小步快跑,每阶段独立可测,打完 commit 验证质量后再推进下一阶段。 如果某阶段导致回答质量下降,直接 git revert 回退到上一个 commit。


Phase 0基线建立 + 评估基础设施

目标:在改动任何代码之前,先有量化基线和自动化评估手段。

现状scripts/evaluate_rag.py 只评估检索层Recall/MRR/nDCG不评估端到端回答质量。data/eval_dataset.json 不存在。

工作内容

  1. 创建 data/eval_dataset.json:从现有知识库中挑选 15-20 个测试问题,覆盖以下类型:

    • 事实查询("XX标准是多少"
    • 列举查询("有哪些禁止情形"
    • 对比查询("A和B的区别"
    • 流程查询("如何申请XX"
    • 跨文档查询(答案涉及多个文件)

    每个问题记录:queryquery_typeexpected_keywords(答案应包含的关键信息点)、relevant_sources(应命中的文件名)

  2. 创建 scripts/eval_e2e.py:端到端评估脚本

    • 启动本地服务(或连接已运行的服务)
    • 对每个测试问题调用 POST /rag
    • 收集回答,用 LLM 评分(相关性 1-5、完整性 1-5、准确性 1-5
    • 同时记录检索层指标Rerank 分数分布、上下文切片数、上下文总字数)
    • 输出 JSON 报告 + 终端汇总
  3. 运行一次基线评估,保存为 data/eval_results/baseline.json

  4. 打 Git taggit tag v1.0-rag-baseline

产出文件

  • data/eval_dataset.json
  • scripts/eval_e2e.py
  • data/eval_results/baseline.json

验收标准:评估脚本能正常运行,基线报告包含每个问题的评分。


Phase 1Rerank 分数传递与上下文过滤

目标:让 Rerank 分数在 chat_routes 的上下文构建中发挥作用,过滤低分切片。

改动范围:仅 api/chat_routes.py,不涉及 engine.py。

具体改动

  1. contexts.append 时已有 score 字段,确认它包含 Rerank 分数(当前 scores 来自 search_result.get('scores'),是 RRF 融合分数还是 Rerank 分数需要确认)

  2. _order_text_contexts_for_prompt 函数中增加分数过滤参数:

    def _order_text_contexts_for_prompt(contexts, query, max_chunks,
                                         min_score=0.0):
        # 过滤低于分数阈值的切片
        if min_score > 0:
            text_contexts = [c for c in text_contexts
                            if c.get('score', 0) >= min_score]
    
  3. generate() 中调用时传入阈值:

    text_contexts = _order_text_contexts_for_prompt(
        contexts, message, MAX_CONTEXT_CHUNKS,
        min_score=RERANK_CONTEXT_MIN_SCORE  # 新增配置项,初始值 0.05
    )
    
  4. config.py 中新增配置:

    RERANK_CONTEXT_MIN_SCORE = 0.05  # 上下文最低 Rerank 分数阈值
    
  5. 在 SSE context_built 事件中增加分数信息DEV 模式),方便调试。

风险:低。min_score 初始设 0.05(几乎不过滤),逐步调高观察效果。

验证方法

  • 运行 eval_e2e.py,对比基线
  • 重点关注:是否有问题因为过滤了切片而回答变差
  • 观察 context_built 事件中 chunk_count 的变化

Git commitfeat(rag): pass rerank scores through to context building with min-score filter


Phase 2Token 预算控制

目标:用字数/token 预算替代纯计数截断,避免上下文过长稀释 LLM 注意力。

改动范围api/chat_routes.py + config.py

具体改动

  1. config.py 新增:

    CONTEXT_MAX_CHARS = 8000      # 上下文最大字符数(约 4000 token
    CONTEXT_SOFT_LIMIT = 6000     # 软限制,超过后只保留高分切片
    
  2. _order_text_contexts_for_prompt 返回后,构建 context_text 时:

    # 按 Rerank 分数降序逐个加入,直到达到 token 预算
    sorted_contexts = sorted(text_contexts,
                             key=lambda c: c.get('score', 0),
                             reverse=True)
    context_parts = []
    total_chars = 0
    for ctx in sorted_contexts:
        doc = ctx.get('doc', '')
        if total_chars + len(doc) > CONTEXT_MAX_CHARS:
            break
        context_parts.append(doc)
        total_chars += len(doc)
    context_text = "\n\n".join(context_parts)
    
  3. 注意:排序后需要保持同一文档切片的连续性。改为先按 (source, section, chunk_index) 分组,再按组内最高分降序排列各组,逐组加入直到预算满。

风险:中。如果预算设太小,可能丢掉关键信息。初始 CONTEXT_MAX_CHARS=8000 比较保守(当前 20 个切片平均约 10000-15000 字)。

验证方法

  • 对比基线的评分变化
  • 关注列举类查询("有哪些禁止情形")是否因为预算截断而漏条
  • 观察 context_built.context_length 分布

Git commitfeat(rag): add character-based token budget for context building


Phase 3上下文扩展精细化

目标Rerank 后的上下文扩展只对高置信度切片执行,避免低分切片引入噪声邻居。

改动范围core/engine.py_expand_contiguous_chunks 及其调用处。

具体改动

  1. MMR 前的扩展(第 573 行)保持不变——它的目的是防止邻居被 MMR 误删

  2. Rerank 后的扩展(第 615 行)增加条件:

    # 只对 Rerank 分数 > EXPANSION_SCORE_THRESHOLD 的切片扩展邻居
    EXPANSION_SCORE_THRESHOLD = 0.3
    
  3. 给扩展进来的邻居切片在 metadata 中记录扩展来源:

    n_meta['_expanded_from_score'] = seed_score  # 种子切片的 Rerank 分数
    
  4. _order_text_contexts_for_prompt 中限制扩展邻居的数量:

    MAX_EXPANDED_NEIGHBORS = 4  # 最多 4 个扩展邻居进入上下文
    

风险:中。如果阈值设太高,一些中等分数的切片不会扩展邻居,可能丢失上下文。初始 0.3 比较宽松。

验证方法

  • 对比 retrieval_debug 事件中第二次 context_expansion 的 before/after 数量
  • 检查是否有回答因为缺少邻居切片而变得不连贯
  • 评估分数不应低于 Phase 2 的结果

Git commitfeat(rag): restrict post-rerank context expansion to high-confidence chunks


Phase 4置信度兜底

目标:当检索质量整体偏低时,在 prompt 中告知 LLM 谨慎回答,减少幻觉。

改动范围api/chat_routes.py(仅 prompt 层面,不改检索逻辑)。

具体改动

  1. 在上下文构建完成后(context_text 已生成),检查 top-3 切片的平均 Rerank 分数:

    top_scores = [ctx.get('score', 0) for ctx in text_contexts[:3]]
    avg_top3_score = sum(top_scores) / len(top_scores) if top_scores else 0
    
  2. 根据平均分数在 prompt 中注入不同指令:

    if avg_top3_score < CONFIDENCE_WARN_THRESHOLD:  # 比如 0.15
        confidence_note = (
            "【重要提示】参考资料与问题的相关性较低。"
            "请仅基于参考资料中明确包含的信息回答,"
            "如果资料不足以回答问题,请直接说明'知识库中未找到直接相关的信息'。"
        )
    elif avg_top3_score < CONFIDENCE_CAUTION_THRESHOLD:  # 比如 0.3
        confidence_note = (
            "参考资料的相关性一般,请优先引用资料中的原文,避免推测。"
        )
    else:
        confidence_note = ""
    
  3. config.py 新增:

    CONFIDENCE_WARN_THRESHOLD = 0.15
    CONFIDENCE_CAUTION_THRESHOLD = 0.30
    
  4. 在 SSE finish 事件中附带 confidence_score 字段,前端可用于提示用户。

风险:低。纯 prompt 层面的改动,不影响检索链路。最坏情况是 LLM 过于保守拒绝回答——可以通过调低阈值解决。

验证方法

  • 构造几个"知识库中确实没有答案"的问题,检查 LLM 是否正确拒绝
  • 正常问题的评分不应下降
  • 观察 finish 事件中的 confidence_score 分布

Git commitfeat(rag): add confidence-based prompt guidance for low-quality retrieval


Phase 5上下文排序优化

目标:对所有查询类型(不仅是列举类)都做同章节聚合排序,保证同一文件同一章节的切片连续排列。

改动范围api/chat_routes.py_order_text_contexts_for_prompt

具体改动

  1. 将当前只对 _is_enum_query 执行的排序逻辑推广为所有查询类型的默认行为

  2. 排序策略:

    • 第一优先级:按 Rerank 分数降序(高分切片先进入上下文)
    • 第二优先级:同一 source + section 的切片按 chunk_index 连续排列
    • 具体做法:先按 (source, section) 分组,组内按 chunk_index 排序,组间按组内最高分降序
  3. 列举类查询保持当前行为不变(作为特例)

风险:低。排序变化不影响切片内容,只影响 LLM 看到的顺序。

验证方法

  • 对比 context_built.chunks_used 的排列顺序
  • 评估分数不应低于 Phase 4

Git commitfeat(rag): apply section-aware context ordering for all query types


Phase 6引用标注改进

目标:提升 _attach_citations 的匹配精度,支持多引用。

改动范围api/chat_routes.py_attach_citations 函数。

具体改动

  1. 动态阈值:短段落(< 50 字)使用更高的 overlap 阈值0.55),长段落保持 0.45

  2. 允许一个段落匹配最多 2 个 chunk如果两个 chunk 的 overlap 分数都超过阈值且差距小于 0.1

  3. 引用标记改为 [ref:chunk_id_1][ref:chunk_id_2],前端 extractCitations 已支持多个 [ref:xxx]

风险:中。多引用可能导致引用列表变长、前端展示变化。

验证方法

  • 检查引用数量是否合理增加(不应翻倍)
  • 引用溯源弹窗的跳转是否仍然正确
  • 评估分数不应下降

Git commitfeat(rag): improve citation matching with dynamic thresholds and multi-citation support


阶段依赖关系

Phase 0 (基线+评估)
  ↓
Phase 1 (Rerank分数传递) ← 风险最低,收益最直接
  ↓
Phase 2 (Token预算) ← 依赖 Phase 1 的分数传递
  ↓
Phase 3 (扩展精细化) ← 依赖 Phase 1 的分数信息
  ↓
Phase 4 (置信度兜底) ← 依赖 Phase 1 的分数信息
  ↓
Phase 5 (上下文排序) ← 独立,可与 Phase 4 互换顺序
  ↓
Phase 6 (引用标注) ← 独立,放在最后因为改动面较大

回退策略

每个 Phase 完成后执行:

# 运行评估
python scripts/eval_e2e.py --output data/eval_results/phase_N.json

# 对比上一阶段
# 如果评分下降 > 5%,回退:
git revert HEAD

# 如果评分持平或提升,打 tag
git tag v1.0-phase-N

预估时间

阶段 代码改动量 预估时间
Phase 0 新建 2 个文件 2-3 小时
Phase 1 改 2 个文件,约 30 行 1 小时
Phase 2 改 2 个文件,约 40 行 1-2 小时
Phase 3 改 1 个文件,约 20 行 1 小时
Phase 4 改 2 个文件,约 25 行 30 分钟
Phase 5 改 1 个文件,约 30 行 1 小时
Phase 6 改 1 个文件,约 40 行 1-2 小时