前言

这篇实战直接带你在骁龙X2 Elite笔记本上把本地RAG(检索增强生成)问答环境跑起来。

这套方案的目标很明确:文档不出本机、问答响应可控、开发流程可复现。

我们先从环境搭建和基础验证开始,接着把FAISS检索链路和Prompt策略补齐,形成完整可用的本地知识问答系统。

一. 环境搭建与基础验证

1.1 本文硬件与系统信息

我这边使用的是固定配置:

  • 设备:骁龙X2 Elite开发笔记本
  • 系统:Windows 11
  • 内存:32GB
  • Python:3.10.x
  • Ollama:Windows 版本
  • 模型:qwen2.5:7b

在这里插入图片描述

1.2 整体执行流程

先把整件事的执行路径放出来,后面每一步都照这个走。

  • 环境准备
    确认X2 Elite系统为Windows 11;
    确保获取系统管理员权限,关闭安全软件拦截;
  • 部署Python
    下载并安装Python 3.10;
    安装时务必勾选“Add Python to PATH”选项;
  • 虚拟环境配置
    安装Miniconda包管理工具;
    执行命令创建rag_env独立环境;
  • 部署服务端
    下载并安装Ollama工具;
    首次启动Ollama服务,初始化本地模型服务环境;

打开命令行工具,执行ollama pull qwen2.5:7b,自动下载并部署阿里通义千问2.5 7B模型,完成后即可在本地调用LLM能力。

二. 环境安装步骤

2.1 安装 Python 3.10

直接去Python官网下3.10安装包,安装时一定勾选Addpython.exe toPATH。这一项不勾,后面命令行会一直报找不到python。

安装完成后,执行:

python --version

看到3.10. x就是正常。

2.2 安装 Miniconda 并创建环境

我这边统一用 conda 管理依赖,避免系统 Python 被污染。

conda create - n rag_env python=3.10 - y
conda activate rag_env

然后升级 pip:

python - m pip install --upgrade pip

2.3 安装 Ollama 并拉取模型

安装完 Ollama for Windows 后,先确认服务可用:

ollama --version

接着拉模型:

ollama pull qwen2.5:7b

这个步骤通常会花几分钟到十几分钟,取决于网络。

2.4 安装 RAG 所需 Python 依赖

在 rag_env 环境里执行:

pip install langchain langchain- community faiss- cpu sentence- transformers ollama

为了做基础接口调用测试,再补一个 requests:

pip install requests

三. 基础能力验证

3.1 验证本地模型是否可推理

执行:

ollama run qwen2.5:7b "请用一句话介绍骁龙X2 Elite在端侧AI场景的优势"

只要能稳定返回中文回答,说明模型推理链路没问题。

3.2 验证 RAG 链路的数据流

这里先不追求复杂业务,先把最小可用链路打通:

  1. 准备一份FAQ Markdown文档;
  2. 切分文本;
  3. 生成向量;
  4. 落到 FAISS;
  5. 用户提问后检索 Top- K;
  6. 交给 qwen2.5:7b 生成答案。

流程如下:

在这里插入图片描述

3.3 最小验证脚本

新建 quick_rag_check.py,内容如下:

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings

docs = [
    "X2 Elite支持低功耗端侧推理。",
    "本项目使用Ollama运行qwen2.5:7b模型。",
    "RAG流程包括切分、向量化、检索、生成四个核心步骤。"
]

splitter = RecursiveCharacterTextSplitter(chunk_size=80, chunk_overlap=10)
chunks = splitter.create_documents(docs)

emb = HuggingFaceEmbeddings(model_name="sentence- transformers/all- MiniLM- L6- v2")
db = FAISS.from_documents(chunks, emb)

query = "这个项目的核心流程是什么?"
hits = db.similarity_search(query, k=2)

for i, h in enumerate(hits, 1):
    print(f"Top{i}: {h.page_content}")

运行:

python quick_rag_check.py

只要能输出Top1、Top2命中文本,就说明检索部分已经打通。

到这里,达成了:

  • X2 Elite 本地大模型环境已经可用;
  • RAG 的最小检索链路已经跑通;
  • 关键依赖和排错路径都已经验证。

下面我们直接进入实战:把 Markdown 文档目录批量入库,做可复用的检索+生成脚本,并给出可直接迁移到业务项目的目录结构。

五.文档批量入库

5.1 整体数据流

先看这篇的核心数据流,从文档目录到最终问答输出:
在这里插入图片描述

5.2 准备知识库目录

在项目根目录下新建 knowledge_base/ 文件夹,把需要入库的 Markdown 文件放进去。我这边放了 5 个文件,内容覆盖产品 FAQ、操作手册、常见故障排查。

# 目录结构:
knowledge_base/
├── faq.md
├── install_guide.md
├── troubleshooting.md
├── ...

5.3 编写入库脚本

新建 build_index.py :

import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.schema import Document

KNOWLEDGE_DIR = "./knowledge_base"
INDEX_DIR = "./faiss_index"

def load_markdown_files(directory):
    docs = []
    for filename in os.listdir(directory):
        if filename.endswith(".md"):
            filepath = os.path.join(directory, filename)
            with open(filepath, "r", encoding="utf-8") as f:
                content = f.read()
            docs.append(Document(page_content=content, metadata={"source": filename}))
    return docs

def build_index():
    raw_docs = load_markdown_files(KNOWLEDGE_DIR)
    print(f"加载了 {len(raw_docs)} 个文档")

    splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50,
        separators=["\n## ", "\n### ", "\n\n", "\n", ""]
    )
    chunks = splitter.split_documents(raw_docs)
    print(f"切分为 {len(chunks)} 个片段")

    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2"
    )

    db = FAISS.from_documents(chunks, embeddings)
    db.save_local(INDEX_DIR)
    print(f"FAISS索引已保存到 {INDEX_DIR}")

if __name__ == "__main__":
    build_index()

运行:

python build_index.py

正常输出类似:

加载了 5 个文档
切分为 47 个片段
FAISS索引已保存到 ./faiss_index

5.4 切分参数说明

这里 chunk_size=500 是经过测试的值。太大会导致检索命中不够精准,太小会丢失上下文。chunk_overlap=50 保证相邻片段之间有一定重叠,避免关键信息被切断。

separators 的优先级从高到低:先按二级标题切,再按三级标题,最后按段落。这样能保持文档结构的完整性。

六. 检索生成脚本

6.1 编写问答脚本

新建 rag_query.py :

from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
import ollama

INDEX_DIR = "./faiss_index"

def load_index():
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2"
    )
    db = FAISS.load_local(INDEX_DIR, embeddings, allow_dangerous_deserialization=True)
    return db

def retrieve_and_generate(db, question, top_k=3):
    hits = db.similarity_search(question, k=top_k)

    context_parts = []
    for i, hit in enumerate(hits, 1):
        source = hit.metadata.get("source", "unknown")
        context_parts.append(f"[片段{i}] 来源:{source}] \n{hit.page_content}")

    context = "\n\n".join(context_parts)

    prompt = f"""你是一个技术支持助手。根据以下参考资料回答用户的问题。
如果参考资料中没有相关内容,直接说"抱歉,当前知识库中没有找到相关信息"。

参考资料:
{context}

用户问题:{question}

请用简洁准确的中文回答:
"""

    response = ollama.chat(
        model="qwen2.5:7b",
        messages=[{"role": "user", "content": prompt}]
    )

    return response["message"]["content"], hits

if __name__ == "__main__":
    db = load_index()
    print("知识库已加载,输入问题开始问答(输入q退出)")

    while True:
        question = input("\n你的问题:")
        if question.strip().lower() == "q":
            break

        answer, sources = retrieve_and_generate(db, question)
        print(f"\n回答:{answer}")
        print(f"\n参考来源:{', '.join(set(s.metadata.get('source','') for s in sources))}")

6.2 运行效果

启动问答:

python rag_query.py

实际测试记录:

你的问题:安装失败怎么办?

回答:安装失败时,先检查系统盘剩余空间是否大于10GB,然后确认
Python版本为3.10. x。如果仍然失败,删除已有的conda环境后重新
创建:conda remove - n rag_env - - all,再执行 conda create - n
rag_env python = 3.10 - y。

参考来源:troubleshooting.md,install_guide.md

从提问到返回答案,在X2 Elite上的端到端延迟大约在3-5秒,其中检索部分不到100ms,主要耗时在qwen2.5:7b的生成阶段。

七. Prompt策略优化

7.1 限定回答范围

上面的Prompt里有一句关键话:“如果参考资料中没有相关内容,直接说抱歉”。这一句不能省,否则模型会自由发挥,编造不存在的操作步骤。

7.2 控制输出长度

如果回答太长,在 Prompt 末尾加一句:

请控制回答在200字以内。

实测这对 qwen2.5:7b 的约束效果很好。

7.3 多轮对话扩展

当前脚本是单轮问答。如果要支持多轮,把历史消息列表传给 ollama.chat 的 messages 参数即可:

messages = [
    { "role": "user", "content": "第一轮问题..."},
    { "role": "assistant", "content": "第一轮回答..."},
    { "role": "user", "content": prompt}
]

八. 性能数据

在骁龙 X2 Elite(32GB 内存)上的实测数据:

指标 数值
文档加载(5个MD文件) 0.3秒
向量化(47个片段) 4.2秒
FAISS索引构建 0.1秒
单次检索(Top-3) 80ms
qwen2.5:7b生成(约150字) 3.1秒
端到端问答延迟 3.2秒
内存占用峰值 6.8GB

骁龙X2 Elite上RAG各阶段性能指标:
在这里插入图片描述

数据总结:RAG系统在“检索侧”已达到毫秒级响应标准,性能瓶颈主要集中在“计算侧”的向量化与模型推理阶段,后续优化应聚焦于模型量化与并行计算加速。

  • 极速检索响应
    单次检索(Top-3)仅需80ms,索引构建与文档加载均在毫秒级完成,系统在检索环节展现出极高的吞吐效率。
  • 核心耗时瓶颈
    向量化处理耗时最长达4.2秒,模型生成紧随其后约3.1秒,这是当前影响整体性能的主要计算密集型环节。
  • 端到端体验可控
    完整问答链路延迟控制在3.2秒,在保证检索精准度的前提下,实现了高效的生成与响应平衡。

九. 常见问题

9.1 ollama pull 很慢

这是最常见问题。直接结论:

  • 高峰时段确实慢,换网络最有效;
  • 不要反复中断,断点续传有时候比重拉更快;
  • 先把7B模型跑通,后面再考虑更大参数版本。

9.2 Windows 下 FAISS 安装报错

优先用 faiss- cpu,不要先上GPU版。另外,Python版本固定在3.10,兼容性会明显好很多。

9.3 中文检索命中率一般

这个问题在第一版里很正常,先别急着怀疑模型。先检查三件事:

  1. chunk切分是不是太粗;
  2. 文档里是不是有大量噪声文本;
  3. 查询语句和知识库语料是否同领域。

9.4 FAISS索引加载报安全警告

allow_dangerous_deserialization=True这个参数必须加,否则 LangChain 新版会拒绝加载本地索引。这是因为 FAISS 索引使用了 pickle 序列化,LangChain 默认阻止反序列化以防注入攻击。本地使用场景下加上即可。

9.5 检索结果不相关

先排查这几点:

  1. 检查 chunk_size 是否合理,500 是个不错的起点;
  2. 确认文档内容和查询语句是同一个领域;
  3. 尝试把 top_k 从 3 调到 5,看看是不是相关片段排在了后面。

9.6 生成速度太慢

qwen2.5:7b 在 X2 Elite 上的生成速度大约 8-12 tokens/s。如果觉得慢,换成 qwen2.5:3b 会快很多,但回答质量会下降。根据实际业务需求选择模型大小。

小结

至此,我们在骁龙 X2 Elite 上完成了一个完整的本地 RAG 知识问答系统:既搞定了环境安装和基础链路验证;又实现了文档批量入库、检索生成脚本、Prompt 优化策略;
整套系统完全离线运行,端到端延迟 3 秒左右,实用性没问题。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐