10 KiB
目标
在保留现有 RAGFlow 方案的前提下,引入一套 ES/OpenSearch(BM25)为主 的“内部固定资料问答”链路,用配置开关在 ragflow 与 es_bm25 间切换;并将外部实时问题(天气/新闻等)继续沿用项目现有的“搜索增强”流程。
核心诉求:
- 首 token 更快(内部资料命中时尽量做到“检索毫秒级 + LLM首字”)。
- 可上线稳定:有置信度、兜底、同义词、索引治理、可观测。
- 可回滚:配置切回
RAGFlow即可。
现状简述(与你们当前代码对齐)
当前对话入口:POST /system/ragflow/converse_with_chat_assistant(module_admin/controller/ragflow_controller.py)
现有大致流程:
- 移除风格提示词(轻量)
StaticQA静态FAQ(本地相似度)SearchService.classify_intent:判断是否走外部搜索(可能有外部模型调用)SemanticCache:Redis 问答历史缓存(目前实现会扫 keys)- 调用
RAGFlow返回流式答案
对于“首 token 慢”,主要风险点在于:前置链路过长、外部意图识别网络 RTT、缓存扫描、以及 RAGFlow 调用与流式处理的阻塞/日志等。
方案 B:可上线稳定的 ES/BM25 内部知识链路
1) 总体架构(保留 RAGFlow,通过配置切换)
抽象一个内部知识提供方(KB Provider),提供“检索 +(可选)生成”的能力:
KB_PROVIDER=ragflow:保持现状(走 RAGFlow)KB_PROVIDER=es_bm25:内部知识走 ES/OpenSearch(BM25)
外部实时问题(天气/新闻等)继续由现有 SearchService 分流处理,不受 KB_PROVIDER 影响。
建议新增配置(示例,实际以你们 env.py 读取方式为准):
KB_PROVIDER=ragflow|es_bm25(默认 ragflow)KB_ES_URL=http(s)://host:9200或KB_OS_URL=http(s)://host:9200KB_ES_INDEX=kb_chunks_v1KB_ES_USERNAME=...(如开启安全)KB_ES_PASSWORD=...KB_TOP_K=8(检索召回)KB_MIN_SCORE=...(置信度阈值的一个维度)KB_CACHE_TTL=...(检索结果缓存 TTL)
2) 请求路由(兼容你们已有流程)
建议稳定且首 token 友好的顺序:
- 静态FAQ(命中直接回)
- 内部会话缓存(命中直接回)
- 意图识别(外部搜索 vs 内部KB)
- 内部KB:根据
KB_PROVIDER走es_bm25或ragflow - 兜底:内部KB低置信度 →(可配置)fallback 到 ragflow 或通用对话
说明:
- 把“缓存”放到“外部意图识别”前,避免命中时仍需等待外部网络调用。
ES/OpenSearch 索引治理
1) 数据结构(Chunk 化)
内部资料固定,推荐离线预处理:文档 → 分块(chunk)→ 入索引。
每条 chunk 建议字段:
doc_id:文档IDchunk_id:分块IDtitle:标题/章节名(可空)content:正文内容(核心检索字段)source:来源(文件名/系统名)tags:标签(产品/制度/流程/人名/地名等)category:大类(可选)updated_at:更新时间version:索引版本号/数据版本
2) 中文分词与同义词
选择建议:
- OpenSearch:优先使用
analysis-icu+(可选)analysis-smartcn(若可用),并结合自定义同义词。 - Elasticsearch:常见做法是安装 IK 分词(
analysis-ik)或analysis-smartcn。
同义词治理建议:
- 维护一份
synonyms.txt(公司/产品/缩写/别名),例如:康达新材, 康达胶粘剂, 胶水厂区, 园区, 工厂
- 分两层:
- query-time synonyms(更安全,不必全量重建索引)
- index-time synonyms(效果可能更强,但改词需 reindex)
3) Mapping & Query(BM25)
建议用多字段策略:
title:boost 高content:主字段tags/category:过滤与加权
查询 DSL 推荐组合:
multi_match(best_fields或most_fields)match_phrase(对关键短语加分)minimum_should_match(抑制泛匹配)function_score(用字段权重/新鲜度加权)
置信度与兜底策略(上线稳定的关键)
内部资料“固定且可控”,建议对检索结果计算一个可解释的置信度,用于:
- 决定是否直接回答
- 决定是否 fallback 到 ragflow
- 决定是否提示“我不确定/需要更多信息”
可落地的置信度指标(建议先从简单开始):
top1_score:ES 返回的最高分score_gap = top1_score - top2_score:区分度topk_hit_count:有效 chunk 数keyword_coverage:问题关键词在 top chunks 中的覆盖率(你们已有MatchService.calculate_keyword_coverage)
决策建议:
- 高置信:
top1_score >= S1且keyword_coverage >= C1→ 直接走“基于上下文生成”或“抽取式回答” - 中置信:走“生成 + 引用”
- 低置信:
- 若
KB_PROVIDER=es_bm25,则 fallback 到ragflow(可配置开关KB_FALLBACK=ragflow|none) - 或提示用户改写/补充关键词
- 若
缓存与性能策略
建议两级缓存(Redis):
- 检索缓存:
kb:es:retrieval:{hash(question_norm)}→ 存 topK chunks(TTL 10~30min) - 答案缓存:你们已有
SemanticCache(建议把 KEYS 扫描改为 SCAN/MGET 并限制扫描数量)
首 token 优化要点:
- 检索缓存命中时:可立即开始构造 prompt 并流式输出。
- 生成端:
- 若你们用 DeepSeek/OpenAI 兼容接口,尽量使用
stream=True并减少前置串行步骤。
- 若你们用 DeepSeek/OpenAI 兼容接口,尽量使用
观测与回归指标(上线必须)
建议打点并输出到日志/监控:
ttft_ms:Time To First Tokenretrieve_ms:ES 检索耗时llm_first_token_ms:LLM 首 token(若可获得)cache_hit:静态FAQ/语义缓存/检索缓存命中率fallback_rate:es_bm25 → ragflow 的兜底比例no_hit_rate:检索无结果占比
迁移/上线步骤(建议)
- 部署 OpenSearch/ES(见下文)
- 设计索引与分析器(中文分词、同义词)
- 编写离线入库脚本:固定资料 → chunk → bulk ingest
- 新增
es_bm25Provider(只影响内部 KB 路径) - 灰度:
KB_PROVIDER=es_bm25仅在一台实例开启- 对比
ttft_ms、命中率、fallback_rate
- 全量切换;保留随时回滚到
KB_PROVIDER=ragflow
生产环境安装 OpenSearch(推荐)/Elasticsearch 方法
下面给出两种最常见可控方案:Docker(快)与 systemd(稳)。生产建议至少做到:安全(认证/TLS)、资源限制、数据盘、备份与监控。
前置:Linux 内核与系统参数(必须)
- 设置虚拟内存映射(Lucene 需要):
sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf
-
建议关闭 swap(或至少调低 swappiness)。
-
文件句柄与进程限制(systemd/ulimit):
nofile建议 >= 65535memlock尽量 unlimited(视环境)
方案 1:Docker 部署 OpenSearch(最快上手)
适合:你们已有 Docker,想快速上线验证。
- 创建数据目录(挂载到数据盘):
sudo mkdir -p /data/opensearch
sudo chown -R 1000:1000 /data/opensearch
- 启动单节点(示例,生产请开启安全与持久化配置):
docker run -d --name opensearch \
-p 9200:9200 -p 9600:9600 \
-e "discovery.type=single-node" \
-e "OPENSEARCH_JAVA_OPTS=-Xms4g -Xmx4g" \
-e "plugins.security.disabled=false" \
-v /data/opensearch:/usr/share/opensearch/data \
opensearchproject/opensearch:2
- 验证:
curl -k -u admin:admin https://127.0.0.1:9200
生产建议:
- 用 docker-compose 管理,显式配置 admin 密码、证书、网络白名单。
- 至少做反向代理/防火墙,仅允许后端访问 9200。
方案 2:systemd 部署 OpenSearch(更适合生产)
适合:长期稳定运行、要配合 systemd 管理与日志。
- 安装 JDK(OpenSearch 自带 JDK 的版本较多,但建议按官方要求确认):
java -version
- 下载并解压 OpenSearch(示例命令,按你们版本选择):
mkdir -p /opt/opensearch
tar -xf opensearch-2.x.x-linux-x64.tar.gz -C /opt/opensearch --strip-components=1
- 创建专用用户与数据目录:
sudo useradd --system --home /opt/opensearch --shell /usr/sbin/nologin opensearch
sudo mkdir -p /data/opensearch
sudo chown -R opensearch:opensearch /opt/opensearch /data/opensearch
- 配置
opensearch.yml(至少设置数据路径、网络、集群名、安全):
path.data: /data/opensearchnetwork.host: 0.0.0.0(或内网IP)http.port: 9200
- systemd service(示例骨架):
/etc/systemd/system/opensearch.service
[Unit]
Description=OpenSearch
After=network.target
[Service]
Type=simple
User=opensearch
Group=opensearch
WorkingDirectory=/opt/opensearch
ExecStart=/opt/opensearch/bin/opensearch
Restart=always
LimitNOFILE=65535
LimitNPROC=4096
LimitMEMLOCK=infinity
[Install]
WantedBy=multi-user.target
启停:
sudo systemctl daemon-reload
sudo systemctl enable --now opensearch
sudo systemctl status opensearch
Elasticsearch 安装说明(可选)
若你们更倾向 Elasticsearch:
- 官方更推荐包管理(rpm/deb)+ systemd
- 中文分词可用
analysis-ik或analysis-smartcn插件(需要安装插件并重启)
生产同样要保证:vm.max_map_count、nofile、数据盘、认证/TLS(Elastic 8+ 默认安全开启)。
与本项目集成点(开发落点)
建议改动范围(保持接口不变):
- 新增
module_admin/service/kb_es_service.py(或类似命名):封装检索、置信度计算、缓存。 - 新增“LLM 生成器”(可复用你们已有 DeepSeek/OpenAI 客户端,如果已有);支持 SSE 流式输出。
- 在
ragflow_controller内部 KB 分支增加:- 若
KB_PROVIDER=es_bm25:走 ES → 生成 →(低置信度 fallback) - 若
KB_PROVIDER=ragflow:保持现状
- 若
备注:RAGFLOW_* 配置与现有服务保持不变,作为兜底/回滚路径。