在大语言模型(LLM)快速发展的今天,如何让模型基于我们自己的私有知识进行准确回答,成为了一个关键问题。检索增强生成(Retrieval-Augmented Generation, RAG)正是解决这一问题的有效范式。本文将通过一段完整的 Python 代码,带你使用 LangChain 框架构建一个本地 RAG 系统,并借此介绍 LLM 应用开发中的核心组件——虽然标题提到“LLMIndex”,但实际在 LangChain 生态中,我们更常使用 FAISS、Chroma 等向量数据库来实现类似功能(注:LLMIndex 是 LlamaIndex 项目的核心概念,而本文使用的是 LangChain)。

📌 说明:本文示例基于 LangChain + 本地运行的 Qwen 模型(通过 http://127.0.0.1:1234/v1 提供 OpenAI 兼容 API),适合希望在本地部署私有知识问答系统的开发者。


一、准备工作

pip install langchain langchain-community langchain-openai python-dotenv faiss-cpu

同时,你需要:

  • 一个本地运行的大模型服务(如 LM StudioOllamavLLM),并开启 OpenAI 兼容 API(端口 1234)。
  • 一份名为 knowledge.txt 的文本知识库文件(UTF-8 编码)。
  • .env 文件中配置 OPENAI_API_KEY(即使使用本地模型,LangChain 仍要求提供 API Key,可设为任意字符串,如 sk-local)。

二、代码解析:构建 RAG 流程

1. 加载环境变量与文档

load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")

loader = TextLoader("knowledge.txt", encoding="utf-8")
documents = loader.load()

这里我们使用 TextLoader 读取本地知识文件。LangChain 支持多种格式(PDF、Word、网页等),可根据需求替换加载器。

2. 文本分块(Chunking)

text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
texts = text_splitter.split_documents(documents)

由于 LLM 有上下文长度限制,需将长文档切分为小段。RecursiveCharacterTextSplitter 按字符递归分割,保留语义连贯性,chunk_overlap 避免信息割裂。

3. 向量化与向量存储

embeddings = OpenAIEmbeddings(
    model="text-embedding-qwen3-embedding-0.6b",
    api_key=api_key,
    base_url="http://127.0.0.1:1234/v1"
)

vectorstore = FAISS.from_documents(texts, embeddings)

关键点来了!我们使用 本地嵌入模型(如 Qwen Embedding)将文本转换为向量,并存入 FAISS(Facebook 开源的高效相似性搜索库)。这一步相当于构建了“知识索引”——类似于 LlamaIndex 中的 VectorStoreIndex

4. 配置本地大语言模型

llm = ChatOpenAI(
    model='qwen2.5-7b-instruct-1m',
    api_key=api_key,
    base_url="http://127.0.0.1:1234/v1"
)

通过 OpenAI 兼容接口调用本地 Qwen 模型,无需联网,保障数据隐私。

5. 构建 RAG 链

retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

template = """使用以下上下文片段来回答问题。
如果你不知道答案,就说你不知道,不要编造答案。

{context}

问题: {question}
有用的回答:"""

prompt = ChatPromptTemplate.from_template(template)

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

qa_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

这是 LangChain 的精髓:声明式链式编程

  • retriever 从向量库中找出最相关的 3 个文本块;
  • format_docs 将检索结果拼接成字符串;
  • prompt 注入上下文和问题;
  • llm 生成答案;
  • StrOutputParser 提取纯文本。

整个流程清晰、模块化,易于调试和扩展。

6. 提问与输出

query = "LangChain 支持哪些功能?"
result = qa_chain.invoke(query)
print("问题:", query)
print("回答:", result)

系统会自动检索 knowledge.txt 中相关内容,并基于上下文生成精准回答。


三、为什么不用 LlamaIndex?

你可能会问:既然提到了 “LLMIndex”,为什么不直接用 LlamaIndex?

  • LlamaIndex 更专注于 数据索引与查询优化,适合复杂知识图谱、多模态索引等场景;
  • LangChain 则是一个 通用 LLM 应用框架,强调链式组合、工具集成(如 Agent、Memory)。

两者并非互斥,甚至可以结合使用。但在简单 RAG 场景下,LangChain + FAISS 已足够高效。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐