Files
rag/config.py
lacerate551 ee5295cc97 fix(parser): PDF/DOCX 切片层级结构修复与 MinerU V2 解析增强
核心修复:
- _post_process_chunks: 用 _buffer_has_body 标志替代 buffer.text_level=0,
  标题+正文合并后保留 text_level 不置零,修复层级信息丢失
- heading_rules numeric_level2: 正则从 ^\d+\.\d+[\.、\s] 改为
  ^\d+\.\d+(?!\.\d),修复无空格标题如"2.1运行调度"无法匹配
- V2 title handler: heading_rules 优先(模式匹配可靠),VLM 仅兜底
  (VLM 常给所有标题 level=1)

MinerU V2 解析增强:
- 两轮 TOC 过滤:多行目录块检测 + 孤立标题/单字符残留清理
- 封面 logo 过滤、重复标题去重
- chart VLM 描述和 Markdown 数据表提取
- ChromaDB metadata 新增 text_level/bbox/table_type/sub_type 字段

配置调整:
- CLUSTER_SECTION_PREFIX_LEVELS 1→2(两级 section 聚类更精确)
- MINERU_LOCAL_BACKEND 默认改为 pipeline

文档:
- RAG检索流程逻辑.md 更新 MinerUChunk 字段、ChromaDB metadata、
  MinerU 解析策略等章节
- 新增 RAG引用跳转-优化计划.md、云端MinerU输出分析与优化方案.md
2026-06-19 23:56:13 +08:00

289 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RAG 知识库服务配置
# ================================
# 敏感信息通过环境变量注入,默认值为空字符串
import os
# 加载 .env 文件(敏感信息不写入代码,通过 .env 注入)
try:
from dotenv import load_dotenv
load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"))
except ImportError:
pass # python-dotenv 未安装时静默跳过
# ==============================================================================
# 一、API 密钥与模型
# ==============================================================================
# 通义千问 LLM 服务
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY", "")
DASHSCOPE_BASE_URL = os.getenv("DASHSCOPE_BASE_URL", "https://token-plan-cn.xiaomimimo.com/v1")
DASHSCOPE_MODEL = os.getenv("DASHSCOPE_MODEL", "mimo-v2.5") # 文本生成模型
RAG_CHAT_MODEL = os.getenv("RAG_CHAT_MODEL", "mimo-v2.5") # RAG 对话模型
INTENT_MODEL = os.getenv("INTENT_MODEL", "mimo-v2.5") # 意图分析模型
VLM_MODEL = os.getenv("VLM_MODEL", "mimo-v2.5") # 视觉语言模型(图片描述)
# 兼容旧变量名(逐步迁移到 DASHSCOPE_* 命名)
API_KEY = DASHSCOPE_API_KEY
BASE_URL = DASHSCOPE_BASE_URL
MODEL = DASHSCOPE_MODEL
# ==============================================================================
# 二、环境与功能开关
# ==============================================================================
APP_ENV = os.getenv("APP_ENV", "dev") # dev / prod
IS_DEV = APP_ENV == "dev"
IS_PROD = APP_ENV == "prod"
# 开发模式开关(控制 mock token 登录、模拟用户等开发功能)
# 默认开启,生产环境需在 .env 中设置 DEV_MODE=false
DEV_MODE = os.getenv("DEV_MODE", "true").lower() != "false"
# 开发/生产环境自动切换
ENABLE_SESSION = IS_DEV # 会话存储(仅开发环境)
ENABLE_FEEDBACK = True # 反馈系统
# 扩展功能(手动开启)
ENABLE_WEB_SEARCH = False # 网络搜索(需 SERPER_API_KEY
# ==============================================================================
# 三、路径配置
# ==============================================================================
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
MODELS_DIR = os.path.join(PROJECT_ROOT, "models")
EMBEDDING_MODEL_PATH = os.path.join(MODELS_DIR, "bge-base-zh-v1.5")
RERANK_MODEL_PATH = os.path.join(MODELS_DIR, "bge-reranker-base")
_vector_store_path = os.path.join(PROJECT_ROOT, "knowledge", "vector_store")
CHROMA_DB_PATH = os.path.join(_vector_store_path, "chroma")
DOCUMENTS_PATH = os.path.join(PROJECT_ROOT, "documents")
BM25_INDEXES_PATH = os.path.join(_vector_store_path, "bm25")
# 文件存储类型: local / smb / s3 / http
STORAGE_TYPE = os.getenv("STORAGE_TYPE", "local")
# SMB/CIFS 配置
STORAGE_SMB_HOST = os.getenv("STORAGE_SMB_HOST", "")
STORAGE_SMB_SHARE = os.getenv("STORAGE_SMB_SHARE", "")
STORAGE_SMB_USERNAME = os.getenv("STORAGE_SMB_USERNAME", "")
STORAGE_SMB_PASSWORD = os.getenv("STORAGE_SMB_PASSWORD", "")
STORAGE_SMB_DOMAIN = os.getenv("STORAGE_SMB_DOMAIN", "")
STORAGE_SMB_BASE_PATH = os.getenv("STORAGE_SMB_BASE_PATH", "")
# S3 配置
STORAGE_S3_ENDPOINT = os.getenv("STORAGE_S3_ENDPOINT", "")
STORAGE_S3_BUCKET = os.getenv("STORAGE_S3_BUCKET", "")
STORAGE_S3_ACCESS_KEY = os.getenv("STORAGE_S3_ACCESS_KEY", "")
STORAGE_S3_SECRET_KEY = os.getenv("STORAGE_S3_SECRET_KEY", "")
STORAGE_S3_REGION = os.getenv("STORAGE_S3_REGION", "us-east-1")
# HTTP 文件服务配置
STORAGE_HTTP_BASE_URL = os.getenv("STORAGE_HTTP_BASE_URL", "")
STORAGE_HTTP_TOKEN = os.getenv("STORAGE_HTTP_TOKEN", "")
STORAGE_HTTP_TIMEOUT = int(os.getenv("STORAGE_HTTP_TIMEOUT", "60"))
# ==============================================================================
# 四、设备配置GPU / CPU
# ==============================================================================
EMBEDDING_DEVICE = os.getenv("EMBEDDING_DEVICE", os.getenv("DEVICE", "auto"))
RERANK_DEVICE = os.getenv("RERANK_DEVICE", os.getenv("DEVICE", "auto"))
# ==============================================================================
# 五、LLM 参数
# ==============================================================================
# ----- 通用问答 -----
LLM_TEMPERATURE = 0.7 # 生成温度0=确定性1=随机性)
LLM_MAX_TOKENS = 3000 # 最大输出 token 数
# ----- 意图分析(轻量、确定性高)-----
# INTENT_MODEL 在顶部「一、API 密钥与模型」中统一配置
INTENT_TEMPERATURE = 0.1
INTENT_MAX_TOKENS = 4096 # 推理模型思维链消耗大量 token2048 偶发截断导致意图分析失败
INTENT_HISTORY_WINDOW = 6 # 分析时取最近几条历史消息
# ==============================================================================
# 六、检索参数
# ==============================================================================
# ----- 混合检索 -----
USE_MULTI_KB = True # 多向量库模式
USE_HYBRID_SEARCH = True # 向量 + BM25 混合检索
VECTOR_WEIGHT = 0.5 # 向量检索权重(仅在 USE_MULTI_KB=False 时生效;动态 RRF 启用时被覆盖)
BM25_WEIGHT = 0.5 # BM25 检索权重(同上)
RAG_SEARCH_TOP_K = 30 # 最终返回结果数(不小于 MMR_TOP_K避免截断 MMR 输出)
RAG_SEARCH_CANDIDATES = 100 # [死代码] 候选池大小 — 实际由 RERANK_CANDIDATES 控制,此值未传递给 engine
RECALL_MULTIPLIER = 3 # 候选池最小倍数 = top_k * 此值
# ----- 重排序 -----
USE_RERANK = True # 是否启用重排序
RERANK_CANDIDATES = 20 # 送入重排序的候选数
RERANK_TOP_K = 15 # 重排序后保留数
RERANK_USE_ONNX = os.getenv("RERANK_USE_ONNX", "true").lower() == "true"
RERANK_CONTEXT_MIN_SCORE = 0.05 # Phase 1Rerank 分数低于此值的切片不送入 LLM
# ----- 云端 RerankerDashScope API-----
# RERANK_BACKEND: "local"=本地模型(CPU/GPU)"cloud"=云端API"fallback"=优先云端、失败回退本地
RERANK_BACKEND = os.getenv("RERANK_BACKEND", "local")
RERANK_CLOUD_MODEL = os.getenv("RERANK_CLOUD_MODEL", "xop3qwen8breranker")
RERANK_CLOUD_API_KEY = os.getenv("RERANK_CLOUD_API_KEY", DASHSCOPE_API_KEY)
RERANK_CLOUD_BASE_URL = os.getenv("RERANK_CLOUD_BASE_URL", "https://maas-api.cn-huabei-1.xf-yun.com/v1/rerank")
RERANK_CLOUD_TIMEOUT = int(os.getenv("RERANK_CLOUD_TIMEOUT", "15")) # 云端请求超时(秒)
# ----- RRF 融合 -----
RRF_K = 60 # RRF 常数(越大越平滑)
DYNAMIC_RRF_ENABLED = True # 根据查询类型动态调整向量/BM25 权重
# ----- MMR 多样性去重 -----
MMR_ENABLED = True
MMR_USE_EMBEDDING = os.getenv("MMR_USE_EMBEDDING", "false").lower() == "true" # True=语义向量False=文本相似度(快,使用 jieba 词级 Jaccard
MMR_TOP_K = 30 # MMR 处理后保留数
MMR_LAMBDA = 0.5 # 相关性 vs 多样性权衡0=纯多样1=纯相关)
# ----- 查询扩展 -----
QUERY_EXPANSION_ENABLED = True
QUERY_EXPANSION_THRESHOLD = 0.8 # 扩展词相似度阈值
# ----- 章节过滤 -----
SECTION_FILTER_ENABLED = True # 查询提到章节时优先匹配对应切片
# ==============================================================================
# 七、上下文构建
# ==============================================================================
MAX_CONTEXT_CHUNKS = 20 # 送给 LLM 的最大文本切片数
CONTEXT_MAX_CHARS = 8000 # Phase 2上下文最大字符数约 4000 token
CONTEXT_SOFT_LIMIT = 6000 # Phase 2软限制超过后只接受高分切片组
MAX_SOURCES_RETURNED = 10 # 返回给前端的最大来源数
MAX_HISTORY_ROUNDS = 10 # 对话历史最大轮数
IMAGE_CONTEXT_HISTORY = 4 # 图片上下文取最近几轮历史
DIRECT_CONTEXT_MAX_CHARS = 2000 # 直接回答模式上下文截断字符数
# ==============================================================================
# 八、FAQ 与黑名单
# ==============================================================================
# FAQ 召回与权重
FAQ_RECALL_TOP_K = 3 # FAQ 集合单独召回数
FAQ_BOOST_AMOUNT = 0.1 # FAQ 命中时距离减少量(提升排名)
# FAQ 时间衰减(防止过期 FAQ 长期霸榜)
FAQ_DECAY_MONTHS = 6 # 超过此月数开始衰减
FAQ_DECAY_RATE = 0.01 # 每超一个月的距离惩罚
FAQ_DECAY_MAX = 0.1 # 最大衰减惩罚
# 黑名单(负反馈过滤)
BLACKLIST_MIN_DISLIKES = 3 # 差评达到此数量进入黑名单
BLACKLIST_CACHE_TTL = 300 # 黑名单缓存刷新间隔(秒)
# ==============================================================================
# 九、缓存配置
# ==============================================================================
# 查询结果缓存
QUERY_CACHE_ENABLED = True
QUERY_CACHE_SIZE = 500
QUERY_CACHE_TTL = 3600 # 秒
# Embedding 缓存
EMBEDDING_CACHE_ENABLED = True
EMBEDDING_CACHE_SIZE = 2000
EMBEDDING_CACHE_TTL = 86400
# Rerank 缓存
RERANK_CACHE_ENABLED = True
RERANK_CACHE_SIZE = 1000
RERANK_CACHE_TTL = 3600
# 语义缓存(相似查询复用结果)
SEMANTIC_CACHE_ENABLED = True
SEMANTIC_CACHE_THRESHOLD = 0.92 # 相似度阈值
# 缓存写入最低置信度
# 注意ChromaDB cosine distance 范围 [0,2]score = 1 - dist
# 当前 embedding 模型的 cosine similarity 普遍在 0.03-0.06 之间
# 搜索管线已通过 rerank 过滤低质量结果,此处不再额外限制
CACHE_MIN_SCORE = 0.0
# LLM 调用预算(当前 llm_budget 模块未集成到主流程,以下配置暂不生效)
MAX_LLM_CALLS_PER_QUERY = 2
MAX_QUERY_REWRITES = 1
# ==============================================================================
# 十、文档解析
# ==============================================================================
# MinerU 解析器
MINERU_DEVICE_MODE = os.getenv("MINERU_DEVICE_MODE", "cpu") # cpu / cuda
# MinerU 在线 API优先使用解析效果更好
MINERU_API_TOKEN = os.getenv("MINERU_API_TOKEN", "") # 在 https://mineru.net/apiManage/token 申请
MINERU_API_URL = os.getenv("MINERU_API_URL", "https://mineru.net/api/v4/extract/task")
MINERU_PREFER_ONLINE = os.getenv("MINERU_PREFER_ONLINE", "true").lower() == "true" # 优先使用在线 API
# MinerU 解析模式(本地 + 云端统一配置)
MINERU_MODEL_VERSION = os.getenv("MINERU_MODEL_VERSION", "vlm") # 云端解析模型: pipeline(快速推荐) / vlm(高精度慢) / MinerU-HTML
MINERU_LOCAL_BACKEND = os.getenv("MINERU_LOCAL_BACKEND", "pipeline") # 本地解析后端: pipeline(快速备选) / vlm-auto-engine / hybrid-auto-engine
MINERU_ONLINE_TIMEOUT = int(os.getenv("MINERU_ONLINE_TIMEOUT", "300")) # 云端解析轮询超时(秒),大文档/VLM 模式建议 600+
# 分块参数
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 100
MIN_CHUNK_SIZE = 200
MAX_CHUNK_SIZE = 1200
# 自适应 TopK根据置信度动态调整返回数
ADAPTIVE_TOPK_ENABLED = True
ADAPTIVE_LOW_CONFIDENCE = 0.5
ADAPTIVE_HIGH_CONFIDENCE = 0.8
ADAPTIVE_EXPAND_RATIO = 2.0
ADAPTIVE_SHRINK_RATIO = 0.5
ADAPTIVE_MIN_TOPK = 15
ADAPTIVE_MAX_TOPK = 20
# 连续切片完整性保护(枚举/条款/清单类问题)
CONTEXT_EXPANSION_ENABLED = True
CONTEXT_EXPANSION_BEFORE = 1
CONTEXT_EXPANSION_AFTER = 8
CONTEXT_EXPANSION_MAX_CHUNKS = 50
EXPANSION_SCORE_THRESHOLD = 0.3 # Phase 3Rerank 分数低于此值的切片不扩展邻居
MAX_EXPANDED_NEIGHBORS = 8 # Phase 3每个种子切片最多扩展的邻居数
CONFIDENCE_WARN_THRESHOLD = 0.15 # Phase 4top-3 均分低于此值时,提示 LLM 谨慎回答
CONFIDENCE_CAUTION_THRESHOLD = 0.30 # Phase 4top-3 均分低于此值时,提示 LLM 优先引用原文
ENUM_QUERY_DISABLE_TOPK_SHRINK = True
ENUM_QUERY_MMR_LAMBDA = 0.85
# ----- 章节聚类救援Section-Cluster Rescue-----
# 当同一 section 下多个切片text+table同时出现在候选集中
# 即使单个切片 CrossEncoder 分数很低,也视为强信号进行提升/救援。
SECTION_CLUSTER_BOOST_ENABLED = True # 引擎层聚类提升rerank 后、扩展前)
SECTION_CLUSTER_RESCUE_ENABLED = True # 路由层聚类救援min_score 过滤前)
BM25_DIVERGENCE_RESCUE_ENABLED = True # 路由层BM25-CrossEncoder 分歧检测救援
BM25_DIVERGENCE_MAX_RANK = 3 # 仅救援 BM25 rank <= 此值的切片top-3 是强信号)
CLUSTER_MIN_MEMBERS = 3 # 触发聚类的最小切片数
CLUSTER_MIN_TYPES = 2 # 触发聚类的最小类型多样性text+table=2
CLUSTER_SEED_FLOOR = 0.35 # 引擎层聚类提升后的最低分数(略高于 EXPANSION_SCORE_THRESHOLD=0.3
CLUSTER_RESCUE_FLOOR = 0.06 # 路由层救援保底分数(略高于 RERANK_CONTEXT_MIN_SCORE=0.05
CLUSTER_MAX_BOOST_PER_SECTION = 8 # 引擎层:每个 section 最大提升切片数
CLUSTER_MAX_SECTIONS = 3 # 全局最大提升/救援 section 数
CLUSTER_MAX_RESCUE_PER_SECTION = 6 # 路由层:每个 section 最大救援切片数
CLUSTER_SECTION_PREFIX_LEVELS = 2 # section_path 归一化保留的层级数(按章节前两级分组,提升聚类精确度)
# ==============================================================================
# 十一、可选功能配置
# ==============================================================================
# 网络搜索(需 Serper API
SERPER_API_KEY = os.getenv("SERPER_API_KEY", "")
# ==============================================================================
# 工具函数
# ==============================================================================
def get_llm_client():
"""获取 LLM 客户端实例"""
from openai import OpenAI
return OpenAI(api_key=DASHSCOPE_API_KEY, base_url=DASHSCOPE_BASE_URL)