一、概述
核心原理:3个角色组成你的离线私人知识库
我们要搭的系统,就像一个完全装在你电脑里、不联网的私人智库,3个核心角色分工明确,没有任何一个环节需要连外网:
-
「图书管理员」Elasticsearch 9.x:你的私人文档图书馆
-
存储所有文档(会议纪要、报告、制度文件等)
-
自动给每段话生成「语义标签」(向量),不是简单的关键词匹配,而是能识别"这句话是什么意思"的语义编码
-
你问问题时,10毫秒内就能找到所有和问题相关的文档段落,比人工翻找效率提升上千倍
-
-
「标签生成器」multilingual-e5-small 嵌入模型:给文档和问题打语义标签
-
把人类的自然语言,转换成计算机能识别的「语义编码」。比如"API响应慢"和"接口超时",会被转换成相近的编码,实现语义级检索,而非必须匹配完全一致的关键词
-
完全内置在ES中,离线即可运行,无需额外部署服务
-
-
「文案助理」dolphin3.0-qwen2.5-0.5b 大模型:整理答案的私人助理
-
基于ES检索到的相关文档,用通顺的逻辑总结成精准答案,同时标注每句话的来源文件,杜绝凭空编造
-
仅0.5B参数、200M大小,无独立显卡也能流畅运行,完全离线运行,不会将任何文档数据外传
-
全流程闭环
-
把文档放到离线电脑的指定文件夹
-
ES自动存储文档,生成语义标签
-
输入问题,ES快速检索所有相关文档段落
-
把检索到的内容发送给本地大模型,整理成规范答案
-
输出带引文的精准结果,全程无任何数据离开你的设备
二、第一步:环境准备
关键提醒:物理隔离环境无外网,必须先在有外网的设备上,把所有安装包、镜像、模型、代码全部下载完成,再通过U盘/合规介质拷贝到离线环境,缺一不可!
【必下物料清单】(固定版本,避免兼容性问题,全为官方正版地址)
| 物料名称 | 版本要求 | 官方下载/获取方式 | 核心作用 | 离线环境存放路径建议 |
|---|---|---|---|---|
| Docker 离线安装包 | 20.10+ 稳定版 | Linux官方下载页、Windows Desktop离线安装包 | 运行ES和LocalAI的容器环境,避免离线环境依赖冲突 | Linux:/opt/offline-packages/docker/;Windows:D:\offline-packages\docker\ |
| Elasticsearch 9.x Docker镜像 | 9.1.3 稳定版 | 有网环境执行docker pull docker.elastic.co/elasticsearch/elasticsearch:9.1.3,再导出镜像 |
ES核心服务,离线环境无法在线拉取,必须提前导出 | Linux:/opt/offline-packages/images/;Windows:D:\offline-packages\images\ |
| LocalAI CPU版 Docker镜像 | latest-cpu 稳定版 | 有网环境执行docker pull localai/localai:latest-cpu,再导出镜像 |
本地运行大模型的服务,兼容OpenAI接口,离线环境无需额外安装Python依赖 | 同上 |
| multilingual-e5-small 嵌入模型离线包 | ES官方内置版 | Elastic官方模型库下载地址 | ES离线环境无法自动下载模型,必须提前下载手动导入 | Linux:/opt/offline-packages/models/;Windows:D:\offline-packages\models\ |
| dolphin3.0-qwen2.5-0.5b 大模型文件 | Q4_K_M量化版GGUF格式 | Hugging Face官方下载页 | 本地生成答案的大模型,GGUF格式可离线直接加载 | 同上 |
| Python 3.10+ 离线安装包 | 3.10.14 稳定版(兼容性最优) | Python官方下载页 | 运行RAG核心脚本,离线环境无法使用在线安装器 | 同上 |
| Python依赖包离线whl合集 | 对应版本 | 有网环境执行pip download elasticsearch==9.1.0 requests openai -d ./whl-packages |
离线环境无法pip在线安装,必须提前下载whl安装包 | Linux:/opt/offline-packages/whl/;Windows:D:\offline-packages\whl\ |
| RAG离线运行脚本 | 本文优化版 | 复制本文最后的完整脚本,保存为rag_offline.py |
核心业务逻辑,全离线无外部依赖 | 离线环境工作目录,如/opt/rag-offline/ |
【有网环境必做】Docker镜像导出命令
在有网设备上拉取完镜像后,执行以下命令导出镜像文件,拷贝到离线环境:
Bash
# 导出ES 9.1.3镜像
docker save -o es-9.1.3.tar docker.elastic.co/elasticsearch/elasticsearch:9.1.3
# 导出LocalAI CPU镜像
docker save -o localai-cpu.tar localai/localai:latest-cpu
执行完成后,当前目录会生成es-9.1.3.tar和localai-cpu.tar两个文件,必须和其他物料一起拷贝到离线环境。
三、第二步:离线环境基础环境部署
所有操作均在离线环境的服务器/电脑上执行,全程无需联网
3.1 安装Docker(容器运行环境)
原理大白话
Docker就像一个"隔离的盒子",我们把ES和LocalAI分别装在两个独立的盒子里,不会和设备上的其他软件冲突,离线环境无需解决复杂的依赖问题,导入镜像即可直接运行。
Linux离线安装步骤
- 进入Docker安装包存放目录,解压并安装:
Bash
# 进入Docker安装包目录
cd /opt/offline-packages/docker/
# 解压安装包
tar -zxvf docker-20.10.24.tgz
# 将Docker二进制文件复制到系统PATH,实现全局调用
cp docker/* /usr/bin/
- 配置Docker系统服务,实现开机自启:
Bash
# 创建Docker服务配置文件
cat > /etc/systemd/system/docker.service <<EOF
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
- 启动Docker并设置开机自启:
Bash
# 重载系统服务配置
systemctl daemon-reload
# 启动Docker服务
systemctl start docker
# 设置开机自启
systemctl enable docker
- 【成功校验标准】执行
docker version,能正常输出版本号,说明安装成功。
Windows离线安装步骤
直接双击Docker Desktop离线安装包,跟随向导完成安装,安装完成后启动Docker Desktop,右下角Docker图标显示running,说明安装成功。
离线环境常见报错处理
-
报错
permission denied:使用root用户执行,或给当前用户配置sudo权限 -
报错
docker服务启动失败:检查防火墙是否关闭,系统内核版本是否符合Docker要求(3.10+)
3.2 导入离线Docker镜像
原理大白话
我们在有网环境把ES和LocalAI的"完整安装包"导出成了tar文件,现在把这些文件导入到离线环境的Docker中,就像手机安装APK文件一样,无需联网即可完成安装。
操作步骤
Bash
# 进入镜像tar文件存放目录
cd /opt/offline-packages/images/
# 导入ES 9.1.3镜像
docker load -i es-9.1.3.tar
# 导入LocalAI CPU镜像
docker load -i localai-cpu.tar
【成功校验标准】
执行docker images,能看到docker.elastic.co/elasticsearch/elasticsearch:9.1.3和localai/localai:latest-cpu两个镜像,说明导入成功。
3.3 安装Python与离线依赖包
原理大白话
RAG核心脚本基于Python开发,需要安装Python环境和对应的依赖库。离线环境无法使用pip install在线安装,因此我们提前把所有依赖包的离线安装文件(whl格式)下载完成,直接本地安装即可。
操作步骤
-
安装Python 3.10
-
Linux:执行
rpm -ivh python-3.10.14.rpm(对应系统版本),或通过解压包编译安装 -
Windows:双击Python离线安装包,勾选"Add Python to PATH",跟随向导完成安装
-
-
【成功校验Python】执行
python3 --version(Linux)或python --version(Windows),输出版本号3.10.14,说明安装成功 -
离线安装Python依赖包(复制即用)
Bash
# 进入whl包存放目录
cd /opt/offline-packages/whl/
# 本地安装所有依赖包,完全离线,无外网调用
pip install --no-index --find-links=./ elasticsearch requests openai
- 【成功校验依赖】执行
pip list,能看到elasticsearch、requests、openai三个包,说明安装成功。
四、第三步:Elasticsearch 9.x 离线部署
4.1 提前规划:ES离线环境目录
所有数据、配置、模型均存储在宿主机目录,容器重启不会丢失数据,离线环境必须做持久化配置
Bash
# 创建ES工作目录,所有数据均存储在此处
mkdir -p /opt/rag-offline/es/{data,logs,config,models}
# 给目录赋读写权限,ES容器内部用户为1000,必须开放权限否则启动失败
chmod -R 777 /opt/rag-offline/es/
4.2 启动ES 9.x 离线单节点容器
原理大白话
我们通过Docker启动ES,所有配置均适配离线单节点环境,关闭了所有需要联网的功能,开启了安全认证,同时把数据、配置持久化到宿主机,即使容器删除,数据也不会丢失。
启动命令
Bash
docker run -d \
--name es-rag-offline \
--restart=always \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \ # 单节点模式,离线环境无需集群配置
-e "ES_JAVA_OPTS=-Xms2g -Xmx2g" \ # 堆内存设置,建议为设备内存的1/4,最低2G
-e "xpack.security.enabled=true" \ # 开启安全认证,符合内网合规要求
-e "xpack.security.authc.api_key.enabled=true" \ # 开启API密钥认证,脚本调用使用
-e "xpack.ml.enabled=true" \ # 开启机器学习功能,用于加载嵌入模型
-e "xpack.ml.model_repository.file.enabled=true" \ # 开启本地模型导入,离线环境核心配置
-v /opt/rag-offline/es/data:/usr/share/elasticsearch/data \
-v /opt/rag-offline/es/logs:/usr/share/elasticsearch/logs \
-v /opt/rag-offline/es/config:/usr/share/elasticsearch/config \
-v /opt/rag-offline/es/models:/usr/share/elasticsearch/models \ # 挂载离线模型目录
docker.elastic.co/elasticsearch/elasticsearch:9.1.3
Windows环境适配
将目录替换为Windows路径,比如D:\rag-offline\es\data,命令修改为:
Bash
docker run -d ^
--name es-rag-offline ^
--restart=always ^
-p 9200:9200 ^
-p 9300:9300 ^
-e "discovery.type=single-node" ^
-e "ES_JAVA_OPTS=-Xms2g -Xmx2g" ^
-e "xpack.security.enabled=true" ^
-e "xpack.security.authc.api_key.enabled=true" ^
-e "xpack.ml.enabled=true" ^
-e "xpack.ml.model_repository.file.enabled=true" ^
-v D:\rag-offline\es\data:/usr/share/elasticsearch/data ^
-v D:\rag-offline\es\logs:/usr/share/elasticsearch/logs ^
-v D:\rag-offline\es\config:/usr/share/elasticsearch/config ^
-v D:\rag-offline\es\models:/usr/share/elasticsearch/models ^
docker.elastic.co/elasticsearch/elasticsearch:9.1.3
【成功校验标准】
-
执行
docker ps,看到ES容器的STATUS为Up X seconds (healthy),说明启动成功(必须显示healthy,仅Up不代表完全启动) -
执行以下命令,能返回ES的版本信息,说明API可正常访问:
Bash
curl -X GET "http://localhost:9200/" -u "elastic:你的elastic密码"
补充说明:ES首次启动会自动生成elastic用户的密码,执行docker logs es-rag-offline | grep "Password for the elastic user is"即可获取,务必妥善保存。
4.3 离线导入multilingual-e5-small嵌入模型到ES
原理大白话
在线环境中,ES会自动从官网下载该嵌入模型,但离线环境无法联网,因此我们需要提前把模型文件下载好,放到ES的模型目录中,让ES离线加载该模型,才能在无外网的环境下生成语义向量。
操作步骤
-
把提前下载好的
multilingual-e5-small模型压缩包,解压到/opt/rag-offline/es/models/目录,确保解压后的模型文件直接在该目录下 -
给模型文件赋权限:
chmod -R 777 /opt/rag-offline/es/models/ -
重启ES容器,让ES加载本地模型:
docker restart es-rag-offline -
等待ES重启完成(STATUS变为healthy),执行以下命令,验证模型是否加载成功:
Bash
curl -X GET "http://localhost:9200/_ml/trained_models/.multilingual-e5-small" -u "elastic:你的elastic密码"
【成功校验标准】
命令返回模型的详细信息,无报错,说明模型离线加载成功,ES可在离线环境使用该模型生成语义向量。
4.4 生成ES API密钥
操作步骤
Bash
curl -X POST "http://localhost:9200/_security/api_key" \
-u "elastic:你的elastic密码" \
-H "Content-Type: application/json" \
-d '{
"name": "rag-offline-api-key",
"expiration": "10y",
"role_descriptors": {
"rag-admin": {
"cluster": ["all"],
"index": [
{
"names": ["*"],
"privileges": ["all"]
}
]
}
}
}'
【成功校验标准】
返回结果中的encoded字段,就是你的API密钥,务必复制保存,后续脚本需要使用,格式示例:MTk0NVRwa0IxRGRjVmVKSGl2ZFc6dUFaNkZTUHVXRzEzVjdrejQzSUNxZw==
五、第四步:LocalAI+大模型 完全离线部署
5.1 提前规划:LocalAI离线环境目录
Bash
# 创建LocalAI工作目录,存放大模型文件
mkdir -p /opt/rag-offline/localai/models
# 给目录赋读写权限
chmod -R 777 /opt/rag-offline/localai/
5.2 放置大模型文件
把提前下载好的dolphin3.0-qwen2.5-0.5b-instruct.Q4_K_M.gguf文件,拷贝到/opt/rag-offline/localai/models目录中,LocalAI启动时会自动加载该模型,完全离线,无需通过API下载。
5.3 启动LocalAI离线容器
原理大白话
LocalAI就像一个本地的OpenAI服务,我们把大模型文件挂载到容器中,它启动时会自动加载,对外提供和OpenAI完全兼容的API接口,RAG脚本无需修改任何代码,即可调用本地大模型,全程无外网调用。
启动命令
Bash
docker run -d \
--name localai-rag-offline \
--restart=always \
-p 8080:8080 \
-v /opt/rag-offline/localai/models:/models \ # 挂载本地模型目录
-e DEBUG=false \
-e MODELS_PATH=/models \
-e THREADS=4 \ # 改为你的CPU物理核心数,核心越多生成速度越快
-e CONTEXT_SIZE=2048 \ # 上下文窗口,平衡内存占用和长文本支持
-e PRELOAD_MODELS=dolphin3.0-qwen2.5-0.5b-instruct.Q4_K_M.gguf \ # 启动时预加载模型,无需等待调用再加载
localai/localai:latest-cpu
Windows环境适配
将目录替换为Windows路径,比如D:\rag-offline\localai\models,修改为对应Windows格式即可。
【成功校验标准】
-
执行
docker ps,看到LocalAI容器STATUS为Up,说明启动成功 -
执行以下命令,验证模型是否加载成功,可正常生成内容:
Bash
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "dolphin3.0-qwen2.5-0.5b-instruct.Q4_K_M.gguf",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'
- 命令返回大模型生成的回复,无报错,说明LocalAI离线部署成功。
六、第五步:RAG核心配置与离线脚本部署
6.1 原理大白话
这个脚本是整个系统的"总指挥",全程无外网调用,自动完成以下工作:
-
在ES中创建离线推理端点,用于生成语义向量
-
创建知识库索引,用于存储文档和语义标签
-
批量导入指定文件夹中的文档到ES知识库
-
输入问题后,自动在ES中执行语义检索,匹配相关文档
-
把检索到的内容发送给本地大模型,生成带引文的精准答案
-
输出最终结果和性能数据
6.2 准备知识库文档
在离线环境中,创建/opt/rag-offline/dataset目录,把所有txt格式的文档(会议纪要、报告、制度文件等)放入该目录,脚本会自动读取所有txt文件。
6.3 完整离线RAG脚本
把以下脚本保存为/opt/rag-offline/rag_offline.py,仅需修改开头的3个配置项(ES_API_KEY、AI_MODEL_NAME、DATASET_FOLDER),其余内容无需修改。
Python
import os
import time
from elasticsearch import Elasticsearch, helpers
from openai import OpenAI
# ===================== 【仅需修改这里的配置,其他内容无需改动】=====================
# 替换为之前生成的ES API密钥
ES_API_KEY = "你的ES API密钥"
# 替换为放入models目录的大模型文件名,必须和文件名完全一致
AI_MODEL_NAME = "dolphin3.0-qwen2.5-0.5b-instruct.Q4_K_M.gguf"
# 知识库文档存放目录
DATASET_FOLDER = "/opt/rag-offline/dataset"
# ==============================================================================
# 固定配置,离线环境无需修改
ES_URL = "http://localhost:9200"
INDEX_NAME = "offline-rag-knowledge-base"
LOCAL_AI_URL = "http://localhost:8080/v1"
# 初始化客户端,全程无外网调用
es_client = Elasticsearch(ES_URL, api_key=ES_API_KEY)
ai_client = OpenAI(base_url=LOCAL_AI_URL, api_key="sk-offline-rag") # API key填写任意非空字符串即可,无需真实密钥
def check_es_connect():
"""检查ES连接是否正常,离线环境第一步必做"""
try:
if not es_client.ping():
print("❌ 无法连接到Elasticsearch,请检查服务是否启动、API密钥是否正确")
exit(1)
es_info = es_client.info()
print(f"✅ 成功连接Elasticsearch,版本:{es_info['version']['number']}")
return True
except Exception as e:
print(f"❌ ES连接失败,错误信息:{str(e)}")
exit(1)
def setup_embedding_inference():
"""创建离线嵌入推理端点,使用提前导入的e5-small模型,无外网调用"""
inference_id = "offline-e5-small-embedding"
# 先检查推理端点是否已存在,存在则无需重复创建
try:
es_client.inference.get(inference_id=inference_id)
print(f"✅ 嵌入推理端点 {inference_id} 已存在,无需重复创建")
return
except Exception:
pass
# 创建推理端点,完全适配离线环境,无外部调用
try:
es_client.inference.put(
inference_id=inference_id,
task_type="text_embedding",
body={
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".multilingual-e5-small", # 使用提前离线导入的模型
},
"chunking_settings": {
"strategy": "sentence",
"max_chunk_size": 250,
"sentence_overlap": 2
}
}
)
print(f"✅ 嵌入推理端点 {inference_id} 创建成功")
except Exception as e:
print(f"❌ 嵌入推理端点创建失败,错误信息:{str(e)}")
exit(1)
def setup_knowledge_index():
"""创建知识库索引,使用ES9.x原生semantic_text字段,自动处理向量,无需手动编码"""
try:
if es_client.indices.exists(index=INDEX_NAME):
print(f"✅ 知识库索引 {INDEX_NAME} 已存在,无需重复创建")
return False
print(f"📦 正在创建知识库索引 {INDEX_NAME}...")
es_client.indices.create(
index=INDEX_NAME,
body={
# 离线单节点环境专属配置,避免索引黄标,提升性能
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"refresh_interval": "1s"
},
# 索引映射,核心为semantic_text语义字段,自动处理向量生成和检索
"mappings": {
"properties": {
"file_name": {
"type": "text",
"copy_to": "semantic_content" # 把文件名复制到语义字段,支持按文件名检索
},
"file_content": {
"type": "text",
"copy_to": "semantic_content" # 把文件内容复制到语义字段,支持按内容检索
},
"semantic_content": {
"type": "semantic_text",
"inference_id": "offline-e5-small-embedding" # 绑定创建的离线推理端点
}
}
}
}
)
print(f"✅ 知识库索引 {INDEX_NAME} 创建成功")
return True
except Exception as e:
print(f"❌ 知识库索引创建失败,错误信息:{str(e)}")
exit(1)
def load_documents_from_folder():
"""从本地文件夹加载所有txt文档,生成ES批量写入数据,完全离线"""
if not os.path.exists(DATASET_FOLDER):
print(f"❌ 知识库目录 {DATASET_FOLDER} 不存在,请先创建目录并放入txt文档")
exit(1)
file_count = 0
for filename in os.listdir(DATASET_FOLDER):
if filename.endswith(".txt"):
file_path = os.path.join(DATASET_FOLDER, filename)
# 用UTF-8编码读取,避免中文乱码
with open(file_path, "r", encoding="utf-8") as f:
file_content = f.read()
yield {
"_index": INDEX_NAME,
"_source": {
"file_name": filename,
"file_content": file_content
}
}
file_count += 1
if file_count == 0:
print(f"❌ 知识库目录 {DATASET_FOLDER} 中没有找到任何txt文档,请先放入文档")
exit(1)
print(f"📄 找到 {file_count} 个文档,准备导入ES")
def bulk_index_documents():
"""批量把文档导入ES知识库,使用bulk API提升导入速度"""
try:
success_count, failed_count = helpers.bulk(
es_client,
load_documents_from_folder()
)
if failed_count:
print(f"⚠️ {failed_count} 个文档导入失败")
print(f"✅ 成功导入 {success_count} 个文档到知识库")
return success_count
except Exception as e:
print(f"❌ 文档导入失败,错误信息:{str(e)}")
exit(1)
def semantic_search(user_query, top_n=3):
"""ES原生语义检索,自动把用户问题转成向量,匹配最相关的文档,全程离线"""
start_time = time.time()
# 语义检索查询,一行代码完成,无需手动生成向量
search_query = {
"query": {
"semantic": {
"field": "semantic_content",
"query": user_query
}
},
"size": top_n,
"_source": ["file_name", "file_content"]
}
response = es_client.search(index=INDEX_NAME, body=search_query)
search_latency = (time.time() - start_time) * 1000 # 转换为毫秒
return response["hits"]["hits"], search_latency
def call_local_llm(prompt):
"""调用本地离线大模型生成答案,全程无外网调用"""
start_time = time.time()
try:
response = ai_client.chat.completions.create(
model=AI_MODEL_NAME,
messages=[{"role": "user", "content": prompt}],
temperature=0.3, # RAG场景使用低温度,减少幻觉,确保答案仅来自文档
stream=False
)
# 计算耗时和生成速度
llm_latency = (time.time() - start_time) * 1000
answer = response.choices[0].message.content
tokens_per_second = 0
# 计算token生成速度
if hasattr(response, "usage") and response.usage:
completion_tokens = response.usage.completion_tokens
if llm_latency > 0:
tokens_per_second = (completion_tokens / llm_latency) * 1000
return answer, llm_latency, tokens_per_second
except Exception as e:
llm_latency = (time.time() - start_time) * 1000
return f"大模型调用失败,错误信息:{str(e)}", llm_latency, 0
def build_rag_prompt(user_query, search_results):
"""构建RAG提示词,强制大模型仅使用检索到的文档内容,减少幻觉,带引文标注"""
context = ""
citations = []
for idx, hit in enumerate(search_results, 1):
source = hit["_source"]
context += f"[{idx}] 文档名称:{source['file_name']}\n文档内容:{source['file_content']}\n\n"
citations.append(f"[{idx}] {source['file_name']}")
# 中文提示词,适配中文大模型,明确规则减少幻觉
prompt = f"""
请严格基于下面提供的参考文档,回答用户的问题,必须遵守以下规则:
1. 只使用参考文档里的内容回答问题,绝对不能编造参考文档里没有的信息
2. 每引用一句参考文档的内容,必须在后面标注来源,格式为 [1][2] 这样的编号
3. 回答条理清晰,分点说明核心内容,语言简洁准确
4. 如果参考文档里没有和问题相关的内容,直接回答"未在知识库中找到相关信息"
参考文档:
{context}
用户问题:{user_query}
"""
return prompt, citations
if __name__ == "__main__":
print("="*60)
print("🚀 【完全离线版】RAG个人知识库助手 启动")
print("="*60)
# 1. 检查ES连接
print("\n===== 1. 检查Elasticsearch连接 =====")
check_es_connect()
# 2. 创建离线嵌入推理端点
print("\n===== 2. 初始化离线嵌入模型 =====")
setup_embedding_inference()
# 3. 创建知识库索引
print("\n===== 3. 初始化知识库索引 =====")
is_new_index = setup_knowledge_index()
# 4. 新创建的索引自动导入文档
if is_new_index:
print("\n===== 4. 导入知识库文档 =====")
import_count = bulk_index_documents()
if import_count == 0:
exit(1)
time.sleep(1) # 等待索引刷新,确保文档可被检索
# 5. 执行RAG问答
print("\n===== 5. 执行RAG智能问答 =====")
# 此处可修改为你要提问的问题,支持中文
USER_QUESTION = "请总结API存在的性能问题"
print(f"🔍 用户问题:{USER_QUESTION}")
# 语义检索
search_results, search_latency = semantic_search(USER_QUESTION, top_n=3)
if not search_results:
print("❌ 未在知识库中找到相关文档")
exit(0)
print(f"✅ 找到 {len(search_results)} 个相关文档,检索耗时:{search_latency:.0f}ms")
# 构建RAG提示词
rag_prompt, citations = build_rag_prompt(USER_QUESTION, search_results)
# 调用本地大模型生成答案
print(f"\n🤖 正在调用本地大模型 {AI_MODEL_NAME} 生成答案...")
answer, llm_latency, tokens_per_second = call_local_llm(rag_prompt)
# 输出最终结果
print("\n" + "="*60)
print(f"💡 问题:{USER_QUESTION}")
print(f"📝 答案:\n{answer}")
print("\n📚 引用来源:")
for cite in citations:
print(f" {cite}")
print("\n" + "="*60)
print(f"🔍 语义检索耗时:{search_latency:.0f}ms")
print(f"🤖 大模型生成耗时:{llm_latency:.0f}ms | 生成速度:{tokens_per_second:.1f} tokens/s")
print("✅ 离线RAG问答执行完成")
6.4 运行脚本
Bash
# 进入脚本所在目录
cd /opt/rag-offline/
# 运行脚本
python3 rag_offline.py
【成功校验标准】
脚本正常执行,输出最终的答案、引用来源和性能数据,无报错,说明整个离线RAG系统部署成功!
七、离线环境常见报错与终极解决方案
| 报错现象 | 根因分析 | 解决方案 |
|---|---|---|
| ES容器启动后,STATUS一直显示unhealthy | 目录权限不足,ES容器内部用户无法读写宿主机目录 | 执行chmod -R 777 /opt/rag-offline/es/,重启容器 |
| ES模型加载失败,报错model not found | 模型文件未放入正确目录,或权限不足 | 确保模型文件解压到/opt/rag-offline/es/models/目录,赋777权限,重启ES |
| LocalAI调用报错model not found | 大模型文件名和脚本里的AI_MODEL_NAME不一致,或未放入正确目录 | 检查文件名,必须完全一致(包括.gguf后缀),放入/opt/rag-offline/localai/models/目录 |
| 脚本执行报错ES连接失败 | API密钥错误、ES服务未启动、端口被占用 | 检查ES容器是否正常运行、API密钥是否正确、9200端口是否被占用 |
| 中文文档乱码 | 文档非UTF-8编码,或读取时未指定编码 | 用记事本打开文档,另存为UTF-8编码,脚本已指定encoding="utf-8" |
| 大模型生成的答案与文档无关、凭空编造 | 温度设置过高,或提示词无有效约束 | 将temperature改为0.1~0.3,确保提示词明确要求仅使用参考文档内容 |
八、离线环境进阶优化
-
多格式文档支持 :扩展脚本,支持PDF、Word、Excel等格式,通过
PyPDF2、python-docx等库的离线whl包安装,提取文本后导入ES -
Web可视化界面:通过Streamlit离线部署可视化界面,实现文档上传、问答交互、检索结果查看,全程无外网依赖
-
增量文档更新:增加增量导入逻辑,仅导入新增/修改的文档,无需每次全量重新导入
-
多用户权限控制:基于ES的角色权限体系,给不同用户分配不同的知识库访问权限,符合企业内网合规要求
-
大模型性能优化:开启CPU加速库(OpenBLAS/MKL),可提升大模型生成速度30%以上,适配更高配置的服务器