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%以上,适配更高配置的服务器

相关推荐
T06205149 分钟前
【面板数据】地级市及区县人口空心化数据(2000-2024年)
大数据
Aktx20FNz1 小时前
iFlow CLI 完整工作流指南
大数据·elasticsearch·搜索引擎
LaughingZhu2 小时前
Anthropic 收购 Oven 后,Claude Code 用运行时写了一篇护城河文章
大数据·人工智能·经验分享·搜索引擎·语音识别
学习3人组2 小时前
TortoiseGit冲突解决实战上机练习
大数据·elasticsearch·搜索引擎
Ln5x9qZC22 小时前
Flink SQL 元数据持久化实战
大数据·sql·flink
OYpBNTQXi2 小时前
Flink Agents 源码解读 --- (6) --- ActionTask
大数据·flink
A__tao3 小时前
Elasticsearch Mapping 一键生成 Go Struct,支持嵌套解析
elasticsearch·es
中金快讯3 小时前
济民健康医疗服务占比提升至46%!业务结构调整初见成效
大数据·人工智能
lizhihai_993 小时前
股市学习心得-尾盘半小时买入法
大数据
大大大大晴天️4 小时前
Hudi 生产问题排障-乱序Upsert入湖数据丢失
大数据·flink·hudi