Elasticsearch 9.x 本地RAG个人知识库实操

一、概述

核心原理:3个角色组成你的离线私人知识库

我们要搭的系统,就像一个完全装在你电脑里、不联网的私人智库,3个核心角色分工明确,没有任何一个环节需要连外网:

  1. 「图书管理员」Elasticsearch 9.x:你的私人文档图书馆

    • 存储所有文档(会议纪要、报告、制度文件等)

    • 自动给每段话生成「语义标签」(向量),不是简单的关键词匹配,而是能识别"这句话是什么意思"的语义编码

    • 你问问题时,10毫秒内就能找到所有和问题相关的文档段落,比人工翻找效率提升上千倍

  2. 「标签生成器」multilingual-e5-small 嵌入模型:给文档和问题打语义标签

    • 把人类的自然语言,转换成计算机能识别的「语义编码」。比如"API响应慢"和"接口超时",会被转换成相近的编码,实现语义级检索,而非必须匹配完全一致的关键词

    • 完全内置在ES中,离线即可运行,无需额外部署服务

  3. 「文案助理」dolphin3.0-qwen2.5-0.5b 大模型:整理答案的私人助理

    • 基于ES检索到的相关文档,用通顺的逻辑总结成精准答案,同时标注每句话的来源文件,杜绝凭空编造

    • 仅0.5B参数、200M大小,无独立显卡也能流畅运行,完全离线运行,不会将任何文档数据外传

全流程闭环

  1. 把文档放到离线电脑的指定文件夹

  2. ES自动存储文档,生成语义标签

  3. 输入问题,ES快速检索所有相关文档段落

  4. 把检索到的内容发送给本地大模型,整理成规范答案

  5. 输出带引文的精准结果,全程无任何数据离开你的设备


二、第一步:环境准备

关键提醒:物理隔离环境无外网,必须先在有外网的设备上,把所有安装包、镜像、模型、代码全部下载完成,再通过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.tarlocalai-cpu.tar两个文件,必须和其他物料一起拷贝到离线环境。


三、第二步:离线环境基础环境部署

所有操作均在离线环境的服务器/电脑上执行,全程无需联网

3.1 安装Docker(容器运行环境)

原理大白话

Docker就像一个"隔离的盒子",我们把ES和LocalAI分别装在两个独立的盒子里,不会和设备上的其他软件冲突,离线环境无需解决复杂的依赖问题,导入镜像即可直接运行。

Linux离线安装步骤
  1. 进入Docker安装包存放目录,解压并安装:
Bash 复制代码
# 进入Docker安装包目录
cd /opt/offline-packages/docker/
# 解压安装包
tar -zxvf docker-20.10.24.tgz
# 将Docker二进制文件复制到系统PATH,实现全局调用
cp docker/* /usr/bin/
  1. 配置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
  1. 启动Docker并设置开机自启:
Bash 复制代码
# 重载系统服务配置
systemctl daemon-reload
# 启动Docker服务
systemctl start docker
# 设置开机自启
systemctl enable docker
  1. 【成功校验标准】执行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.3localai/localai:latest-cpu两个镜像,说明导入成功。

3.3 安装Python与离线依赖包

原理大白话

RAG核心脚本基于Python开发,需要安装Python环境和对应的依赖库。离线环境无法使用pip install在线安装,因此我们提前把所有依赖包的离线安装文件(whl格式)下载完成,直接本地安装即可。

操作步骤
  1. 安装Python 3.10

    • Linux:执行rpm -ivh python-3.10.14.rpm(对应系统版本),或通过解压包编译安装

    • Windows:双击Python离线安装包,勾选"Add Python to PATH",跟随向导完成安装

  2. 【成功校验Python】执行python3 --version(Linux)或python --version(Windows),输出版本号3.10.14,说明安装成功

  3. 离线安装Python依赖包(复制即用)

Bash 复制代码
# 进入whl包存放目录
cd /opt/offline-packages/whl/
# 本地安装所有依赖包,完全离线,无外网调用
pip install --no-index --find-links=./ elasticsearch requests openai
  1. 【成功校验依赖】执行pip list,能看到elasticsearchrequestsopenai三个包,说明安装成功。

四、第三步: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
【成功校验标准】
  1. 执行docker ps,看到ES容器的STATUS为Up X seconds (healthy),说明启动成功(必须显示healthy,仅Up不代表完全启动)

  2. 执行以下命令,能返回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离线加载该模型,才能在无外网的环境下生成语义向量。

操作步骤
  1. 把提前下载好的multilingual-e5-small模型压缩包,解压到/opt/rag-offline/es/models/目录,确保解压后的模型文件直接在该目录下

  2. 给模型文件赋权限:chmod -R 777 /opt/rag-offline/es/models/

  3. 重启ES容器,让ES加载本地模型:docker restart es-rag-offline

  4. 等待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格式即可。

【成功校验标准】
  1. 执行docker ps,看到LocalAI容器STATUS为Up,说明启动成功

  2. 执行以下命令,验证模型是否加载成功,可正常生成内容:

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
  }'
  1. 命令返回大模型生成的回复,无报错,说明LocalAI离线部署成功。

六、第五步:RAG核心配置与离线脚本部署

6.1 原理大白话

这个脚本是整个系统的"总指挥",全程无外网调用,自动完成以下工作:

  1. 在ES中创建离线推理端点,用于生成语义向量

  2. 创建知识库索引,用于存储文档和语义标签

  3. 批量导入指定文件夹中的文档到ES知识库

  4. 输入问题后,自动在ES中执行语义检索,匹配相关文档

  5. 把检索到的内容发送给本地大模型,生成带引文的精准答案

  6. 输出最终结果和性能数据

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,确保提示词明确要求仅使用参考文档内容

八、离线环境进阶优化

  1. 多格式文档支持 :扩展脚本,支持PDF、Word、Excel等格式,通过PyPDF2python-docx等库的离线whl包安装,提取文本后导入ES

  2. Web可视化界面:通过Streamlit离线部署可视化界面,实现文档上传、问答交互、检索结果查看,全程无外网依赖

  3. 增量文档更新:增加增量导入逻辑,仅导入新增/修改的文档,无需每次全量重新导入

  4. 多用户权限控制:基于ES的角色权限体系,给不同用户分配不同的知识库访问权限,符合企业内网合规要求

  5. 大模型性能优化:开启CPU加速库(OpenBLAS/MKL),可提升大模型生成速度30%以上,适配更高配置的服务器

相关推荐
一只鹿鹿鹿8 小时前
智慧水利一体化建设方案
大数据·运维·开发语言·数据库·物联网
Elastic 中国社区官方博客10 小时前
使用 Elastic 进行网络监控:统一网络可观测性
大数据·开发语言·网络·人工智能·elasticsearch·搜索引擎·全文检索
海兰11 小时前
Elasticsearch 9.x 借助神经模型优化中文文本分析
大数据·elasticsearch·搜索引擎
海兰12 小时前
ES9.x 银行场景:银行卡可疑交易风控工作流示例
java·elasticsearch·搜索引擎
500佰14 小时前
Hive常见故障多案例FAQ宝典 --项目总结(宝典一)
大数据·linux·数据仓库·hive·hadoop·云计算·运维开发
老陈头聊SEO14 小时前
深度解析长尾关键词与SEO优化提升效果的有效策略
其他·搜索引擎·seo优化
家的尚尚签15 小时前
高定木作企业实践:案例分享与成果展示
大数据·人工智能·python