6ae91679e2
- .env.example 嵌入默认改为本地 BGE-M3(EMBEDDING_PROVIDER=local, dim=1024),NAS 全新部署即用 - 新增 app/services/reranker.py:基于 Ollama /api/rerank 的 cross-encoder 重排服务 - retriever 在 chunk 候选阶段接入语义精排,调用失败优雅降级为 RRF 顺序 - docker-compose 拉取 qwen3-reranker:0.6b,OLLAMA_MAX_LOADED_MODELS 提至 3 - .gitignore 排除 .workbuddy/ 与 .trae/(防止误提交项目记忆与 IDE spec) - 新增 reranker 单元与集成测试,全量测试 529 passed
93 lines
3.8 KiB
Python
93 lines
3.8 KiB
Python
from pydantic_settings import BaseSettings
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""应用配置,通过环境变量注入"""
|
||
|
||
# 应用
|
||
app_name: str = "QMDSearch"
|
||
log_level: str = "info"
|
||
|
||
# 嵌入模型
|
||
embedding_provider: str = "local" # openai | local
|
||
openai_api_key: str = ""
|
||
openai_base_url: str = "https://api.openai.com/v1"
|
||
embedding_model: str = "text-embedding-3-small"
|
||
# bge-m3(本地 Ollama 嵌入)维度为 1024;切换 openai provider 时需同步改为 1536
|
||
embedding_dimension: int = 1024
|
||
|
||
# Ollama 本地模型(用于文档三级总结)
|
||
ollama_base_url: str = "http://localhost:11434"
|
||
ollama_model: str = "qwen2.5:1.5b" # 备选: qwen2.5:3b
|
||
ollama_embedding_model: str = "bge-m3" # embedding_provider=local 时使用的嵌入模型
|
||
|
||
# 重排模型(cross-encoder,经 Ollama /api/rerank,对 chunk 候选做最终精排)
|
||
# 关闭时检索链路退化为纯 RRF 融合结果,行为不变
|
||
reranker_enabled: bool = False
|
||
reranker_model: str = "qwen3-reranker:0.6b"
|
||
reranker_timeout: float = 30.0
|
||
|
||
# Qdrant
|
||
qdrant_host: str = "localhost"
|
||
qdrant_port: int = 6333
|
||
|
||
# Redis
|
||
redis_url: str = "redis://localhost:6379/0"
|
||
|
||
# 检索参数
|
||
retrieval_top_k: int = 20 # L2 语义检索召回数
|
||
retrieval_final_k: int = 5 # L3 重排后返回数
|
||
|
||
# 文档入库参数
|
||
summary_min_text_length: int = 500 # 低于此字符数触发 2.5 级回退
|
||
|
||
# 入库异步任务
|
||
ingest_max_concurrency: int = 2 # 入库后台任务并发上限
|
||
ingest_task_ttl_done: int = 86400 # 任务状态 Redis 保留秒数(进行中与已完成,24h)
|
||
ingest_task_ttl_failed: int = 604800 # 失败任务状态 Redis 保留秒数(7 天)
|
||
|
||
# 知识分类(taxonomy)
|
||
taxonomy_path: str = "" # taxonomy JSON 文件路径,为空用内置默认
|
||
classify_confidence_threshold: float = 0.6 # 低于此值归 uncategorized
|
||
classify_max_categories: int = 3 # query 路由命中类目数上限,超过走全库兜底
|
||
|
||
# 分层检索参数
|
||
l1_doc_top_n: int = 10 # L1 层候选文档数
|
||
l2_section_top_n: int = 5 # L2 层候选 section 数
|
||
l3_top_n: int = 10 # L3 层定位数
|
||
sparse_enabled: bool = True # 是否启用稀疏检索
|
||
cache_ttl: int = 300 # Redis 缓存秒数
|
||
|
||
# 分块参数
|
||
chunk_max_chars: int = 800 # chunk 超长二次切分阈值
|
||
|
||
# 认证(JWT)
|
||
jwt_secret_key: str = "" # JWT 签名密钥,为空时启动自动生成(仅开发,生产必填)
|
||
jwt_algorithm: str = "HS256"
|
||
jwt_expire_minutes: int = 1440 # token 有效期(分钟),默认 24 小时
|
||
auth_register_enabled: bool = True # 是否开放 POST /auth/register
|
||
default_admin_username: str = "admin" # 启动时自动创建的默认管理员用户名
|
||
default_admin_password: str = "" # 默认管理员密码,为空则不创建默认管理员
|
||
|
||
# 文件上传
|
||
upload_dir: str = "./uploads" # 原始文件保存目录(相对路径以工作目录为基)
|
||
upload_max_size_mb: int = 20 # 单文件大小上限(MB)
|
||
upload_allowed_extensions: str = (
|
||
".txt,.md,.html,.htm,.pdf,.docx" # 允许上传的扩展名(逗号分隔)
|
||
)
|
||
|
||
# PDF OCR(图片型/扫描件降级,pypdf extract_text 为空时触发)
|
||
pdf_ocr_enabled: bool = (
|
||
True # 是否启用 OCR 降级(关闭则扫描件按"无法提取文本"拒绝入库)
|
||
)
|
||
pdf_ocr_max_pages: int = 30 # 单文件 OCR 页数上限,超过仅前 N 页
|
||
pdf_ocr_dpi: int = 200 # 渲染 DPI(越高越准但越慢,72~300 合理)
|
||
|
||
# 检索结果 AI 总结
|
||
result_summary_max_hits: int = 5 # 参与总结的最大 hit 条数(控制 prompt 长度)
|
||
|
||
model_config = {"env_prefix": "", "case_sensitive": False}
|
||
|
||
|
||
settings = Settings()
|