向量检索增强生成(RAG,Retrieval‑Augmented Generation),是弥补大语言模型固有缺陷的关键技术。它可接入私有文档、内部笔记等外部数据源,有效缓解训练数据滞后、知识固化、回答幻觉等问题,输出更为精准可信的内容。Naive RAG 即朴素 RAG,是 RAG 体系中最基础原始的实现方式,也是各类进阶 RAG 技术的底层基石。该方案架构极简、落地门槛低,舍弃复杂优化手段,只保留检索加生成的核心能力。很适合新手学习实践,同时该方式也多用于搭建轻量化知识库问答服务,适配各类简单问答场景。
当前主流 RAG 存在三种典型实现范式,分别为 Naive‑RAG、Advanced‑RAG、Agentic‑RAG。本章将以 Naive‑RAG 为例,介绍其功能实现逻辑,以及调用大模型完成 RAG 检索的完整流程。Naive‑RAG 在处理检索结果时,通常直接复用检索得到的文档片段,不会对召回内容做额外加工与二次处理。
- 完整流程为:用户输入问题后,直接执行语义相似度检索,通过向量相似度打分筛选出匹配度最高的文档块;随后将检索获取的上下文片段传入 LCEL 执行链,与提示词进行拼接,完成向量检索推理,最终交由大模型生成回答。
该方案仅依靠基础语义相似度完成召回,不具备查询改写、结果重排序、文本块优化等高级能力。但其实现逻辑简洁、开发效率高、调试难度低,是入门学习 RAG 技术的首选方案。
环境依赖安装
1、项目运行需依赖文档解析、文本分割、向量数据库、网络请求等核心库,为保证安装速度和稳定性,统一使用清华PyPI镜像源批量安装所有依赖,避免后续开发过程中反复补装库文件。
bash
CMD> pip install -i https://pypi.tuna.tsinghua.edu.cn/simple langchain_text_splitters langchain_chroma chromadb pypdf requests bs4
CMD>
CMD> pip list
Package Version
---------------------------------------- -----------
beautifulsoup4 4.15.0
bs4 0.0.2
chromadb 1.5.9
langchain-chroma 1.1.0
langchain-text-splitters 1.1.2
pypdf 6.16.1
requests 2.34.2
2、大模型知识库检索分为对话大模型和Embedding向量模型两类,二者作用完全不同。对话模型输出自然语言文本(供人阅读),向量模型输出浮点型向量数组(供机器计算相似度),是实现RAG检索生成的核心组件。
本次选用 Qwen3-Embedding-0.6B-GGUF 轻量向量模型,模型存放于阿里魔搭社区,可直接访问链接下载对应文件:
下载完成后,将模型文件直接放入llama.cpp根目录,与后续启动脚本同级,避免因路径错误导致模型加载失败。
3、为避免对话模型和向量模型端口冲突、服务相互干扰,本次采用双端口分离部署方案。11433端口承载对话大模型服务,11434端口承载Embedding向量模型服务,两个服务独立运行、各司其职。
对话大模型服务启动,用于实现自然语言问答、对话交互,加载通用指令微调大模型,上下文窗口设置为4096,适配常规问答场景。
bash
CMD> llama-server.exe -m qwen2.5-1.5b-instruct-q4_k_m.gguf --host 127.0.0.1 --port 11433 -c 4096
init: llama threadpool init, n_threads = 12
load_model: initializing, n_slots = 4, n_ctx_slot = 4096, kv_unified = 'true'
llama_server: model loaded
llama_server: listening on http://127.0.0.1:11433
向量Embedding模型服务启动,核心增加 --embeddings 参数,强制模型以向量编码模式启动,不输出文本,仅输出向量数据。
bash
CMD> llama-server.exe -m Qwen3-Embedding-0.6B-Q8_0.gguf --host 127.0.0.1 --port 11434 -c 4096 --embeddings -b 512 -ub 512
init: llama threadpool init, n_threads = 12
load_model: initializing, n_slots = 4, n_ctx_slot = 4096, kv_unified = 'true'
llama_server: model loaded
llama_server: listening on http://127.0.0.1:11434
文本向量维度测试
Llama.cpp 内置 OpenAI 兼容接口标准,无需部署 OpenAI 服务,本地 GGUF 向量模型即可直接被 langchain_openai.OpenAIEmbeddings 调用,大幅降低本地RAG开发适配成本。
本段代码核心目的:验证向量模型服务可用性、向量维度合法性、文本向量化一致性,是RAG项目前置必测环节。核心区分两个核心向量化方法:
- embed_query:面向用户提问,单条短句向量化,向量特征适配「检索匹配」场景;
- embed_documents:面向知识库切片,批量长文本向量化,向量特征适配「入库存储」场景。
python
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(
model="Qwen3-Embedding-0.6B-Q8_0.gguf",
base_url="http://127.0.0.1:11434/v1",
api_key="dummy"
)
text = "人工智能是什么"
# 用于查询语句,单条文本,适合用户提问向量化
query_vector = embeddings.embed_query(text)
print(f"向量长度:{len(query_vector)}")
print(f"前5个向量:{query_vector[:5]}")
texts = ["大模型", "RAG检索增强生成", "向量数据库"]
# 用于文档切片列表,批量处理知识库文本,适合文档检索向量化
doc_vectors = embeddings.embed_documents(texts)
print(f"文档向量数量:{len(doc_vectors)}")
print(f"文档向量前5个:{doc_vectors[0][:5]}")
print(f"文档向量前5个:{doc_vectors[1][:5]}")
print(f"文档向量前5个:{doc_vectors[2][:5]}")
模型固定输出1024维浮点向量,所有文本的语义信息都会被压缩为统一维度的数值数组。语义相似度越高的文本,向量数值重合度越高、空间距离越近,这是语义检索的底层原理。
bash
CMD> python main.py
向量长度:1024
前5个向量:[-0.021272549405694008, -0.04013322293758392, -0.016011258587241173, -0.1134452372789383, 0.014976831153035164]
文档向量数量:3
文档向量前5个:[0.012190932407975197, -0.04904358834028244, -0.011189550161361694, -0.04492426663637161, -0.02737090364098549]
文档向量前5个:[-0.021237701177597046, -0.0865737572312355, -0.018793350085616112, 0.08651088923215866, 0.02789725735783577]
文档向量前5个:[0.04903004691004753, -0.06314395368099213, -0.010090955533087254, -0.09537919610738754, 0.024128597229719162]
向量数据库存储与语义检索测试
Chroma 是轻量级嵌入式向量数据库,无需独立部署服务、无需额外数据库环境,开箱即用,极其适合本地轻量化RAG项目。核心特性为本地磁盘持久化,通过 persist_directory 参数指定存储目录,向量数据、原始文本、元数据会永久保存,重启项目无需重复向量化。
本段代码实现完整的「文本入库-向量存储-语义召回」流程,不依赖关键词匹配,仅通过向量空间距离计算相似度,支持模糊语义匹配。同时直观暴露 Naive RAG 原生短板:无重排序、无查询优化,单纯相似度检索容易出现匹配偏差。
python
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
embeddings = OpenAIEmbeddings(
model="qwen3-embedding-local.gguf",
base_url="http://127.0.0.1:11434/v1",
api_key="dummy"
)
texts = [
"llama.cpp用来本地运行GGUF大模型,一款轻量级、高性能的本地大模型推理框架,主打低资源消耗,支持CPU、GPU混合推理。",
"RAG检索增强生成是解决大模型知识滞后、幻觉问题的核心技术,通过检索外部知识库,让大模型基于真实私有数据生成答案。",
"RAG技术核心流程分为文档加载、文本切片、向量嵌入存储、相似度检索、大模型生成回答五个核心步骤。"
]
# 初始化Chroma向量数据库,持久化存储到本地文件夹
db = Chroma.from_texts(
texts=texts,
embedding=embeddings,
persist_directory="./my_chroma_db"
)
# 语义相似度检索:查询llama.cpp的功能与用途,返回2条最匹配结果
res = db.similarity_search("llamacpp能干什么?",k=2)
print("相似度检索结果:")
for idx, d in enumerate(res, 1):
print(f"\n检索结果{idx}:{d.page_content}")
本次检索结果看似不准确,是Naive RAG 典型缺陷:简短口语化问句与正式文档文本向量空间匹配度偏低,单纯向量相似度无法精准匹配业务语义。这也是进阶RAG需要引入重排序、查询改写、上下文优化的核心原因。
运行后项目目录自动生成 my_chroma_db 文件夹,即为持久化向量库,删除文件夹即可重置知识库数据。
bash
CMD> python main.py
相似度检索结果:
检索结果1:RAG检索增强生成是解决大模型知识滞后、幻觉问题的核心技术,通过检索外部知识库,让大模型基于真实私有数据生成答案。
检索结果2:RAG技术核心流程分为文档加载、文本切片、向量嵌入存储、相似度检索、大模型生成回答五个核心步骤。
调用LCEL链测试
LCEL(LangChain Expression Language)是LangChain官方链式编程语法,用于极简、标准化组装AI业务流程,替代传统函数嵌套写法,结构清晰、易于调试、支持流式输出。
本段代码实现最简Naive RAG完整链路,覆盖全部核心环节:文档切片、向量库持久化、语义检索、Prompt约束、大模型生成、结果解析。同时引入向量距离分数可视化,分数越小语义相似度越高,可直观调试检索质量。
python
import os
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 全局变量配置
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
CHROMA_PERSIST_DIR = "./my_chroma_database"
# 大模型调用接口
llm = ChatOpenAI(
model="qwen2.5-1.5b-instruct-q4_k_m.gguf",
base_url="http://127.0.0.1:11433/v1",
api_key="dummy",
temperature=0.7,
max_tokens=512,
)
# 向量服务调用接口
embeddings = OpenAIEmbeddings(
model="qwen3-embedding-local.gguf",
base_url="http://127.0.0.1:11434/v1",
api_key="dummy"
)
# 构建向量库 并写入磁盘持久化
def build_vector_store(text_list: list[str]) -> Chroma:
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", "。", ",", " "]
)
all_chunks = []
for text in text_list:
chunks = splitter.split_text(text)
all_chunks.extend(chunks)
db = Chroma.from_texts(
texts=all_chunks,
embedding=embeddings,
persist_directory=CHROMA_PERSIST_DIR
)
print(f"[*] 向量库构建完成,共 {len(all_chunks)} 个文本块,保存到 {CHROMA_PERSIST_DIR}")
return db
# 加载本地已经存在的向量库
def load_vector_store() -> Chroma:
return Chroma(
persist_directory=CHROMA_PERSIST_DIR,
embedding_function=embeddings
)
# 将检索到的文档列表拼接成字符串
def format_docs(docs):
return "\n---\n".join(doc.page_content for doc in docs)
if __name__ == "__main__":
raw_texts = [
"llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。",
"RAG检索增强生成用于知识库问答,先检索文档片段,再交给大模型生成答案。",
"Chroma是轻量级嵌入式向量数据库,可以本地持久化存储向量,不需要额外部署服务。",
"RecursiveCharacterTextSplitter用于把长文档切分成适合向量化的小块。"
]
if os.path.exists(CHROMA_PERSIST_DIR):
print("[+] 检测到本地向量库,直接加载...")
vector_db = load_vector_store()
else:
print("[*] 本地向量库不存在,开始构建...")
vector_db = build_vector_store(raw_texts)
# 配置检索4级文档片段
RETRIEVE_TOP_K = 4
retriever = vector_db.as_retriever(search_kwargs={"k": RETRIEVE_TOP_K})
# 配置RAG链
prompt = ChatPromptTemplate.from_messages([
("system", """你是知识库问答助手,请严格依据提供的上下文回答用户问题。
如果上下文中没有相关信息,直接回答"知识库中未找到相关内容",不要编造信息。
参考上下文:
{context}"""),
("human", "{question}")
])
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 测试用户问题
user_question = "llama.cpp有什么作用?"
# 打印相似度分数
docs_score = vector_db.similarity_search_with_score(user_question, k=4)
print("\n[检索结果 (距离分数 越小越相似)]")
for doc, score in docs_score:
print(f"[分数:{score:.4f}] {doc.page_content}")
answer = rag_chain.invoke(user_question)
retrieved_docs = retriever.invoke(user_question)
context_text = format_docs(retrieved_docs)
print("\n[检索到的上下文]")
print(context_text)
print("\n[最终NaiveRAG回答]")
print(answer)
本次运行充分体现 Naive RAG 的容错能力:虽然第一条检索结果匹配错误,但 Top4 召回集合中包含正确文档,大模型通过整合全部上下文,过滤无效信息、精准输出答案。
核心参数解读:temperature=0.7 平衡回答随机性与准确性,数值越低回答越严谨、越高越灵活;chunk_overlap 文本重叠量,用于避免切片截断丢失上下文信息。
bash
CMD> python main.py
[+] 检测到本地向量库,直接加载...
[检索结果 (距离分数 越小越相似)]
[分数:1.1428] RecursiveCharacterTextSplitter用于把长文档切分成适合向量化的小块。
[分数:1.1689] Chroma是轻量级嵌入式向量数据库,可以本地持久化存储向量,不需要额外部署服务。
[分数:1.2348] llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。
[分数:1.2493] RAG检索增强生成用于知识库问答,先检索文档片段,再交给大模型生成答案。
[检索到的上下文]
RecursiveCharacterTextSplitter用于把长文档切分成适合向量化的小块。
---
Chroma是轻量级嵌入式向量数据库,可以本地持久化存储向量,不需要额外部署服务。
---
llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。
---
RAG检索增强生成用于知识库问答,先检索文档片段,再交给大模型生成答案。
[最终NaiveRAG回答]
llama.cpp用于本地运行GGUF大模型,支持CPU和GPU加速。
调用LCEL增量测试
原生Chroma数据库存在重复添加文档生成冗余数据的问题,容易导致知识库内容重复、检索结果冗余,影响问答精度。因此本节在基础Naive RAG的基础上完成工程化升级,新增三项核心实用能力。
主要包含增量入库与覆盖入库双模式、MMR多样性检索、文档溯源元数据绑定三大能力,同时通过UUID唯一标识解决文档重复问题。MMR多样性检索能够优化基础相似度检索的缺陷,避免召回内容单一、重复,在保障语义匹配度的同时提升检索多样性;为每一个文本切片绑定文档来源元数据,可实现内容快速溯源、权限管控与文档迭代更新,是轻量化RAG项目的核心优化方案。
python
import os
import uuid
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.documents import Document
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
CHROMA_PERSIST_DIR = "./my_chroma_database"
RETRIEVE_TOP_K = 4
llm = ChatOpenAI(
model="qwen2.5-1.5b-instruct-q4_k_m.gguf",
base_url="http://127.0.0.1:11433/v1",
api_key="dummy",
temperature=0.3,
max_tokens=800,
)
embeddings = OpenAIEmbeddings(
model="qwen3-embedding-local.gguf",
base_url="http://127.0.0.1:11434/v1",
api_key="dummy"
)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", "。", ",", " "]
)
prompt = ChatPromptTemplate.from_messages([
("system", """你是知识库问答助手,请严格依据提供的上下文回答用户问题。
如果上下文中没有相关信息,直接回答"知识库中未找到相关内容",不要编造信息。
参考上下文:
{context}"""),
("human", "{question}")
])
def format_docs(docs):
return "\n---\n".join(
f"[来源:{doc.metadata.get('source','未知')}]\n{doc.page_content}"
for doc in docs
)
def build_rag_chain(vector_db: Chroma):
retriever = vector_db.as_retriever(
search_type="mmr", # 开启MMR检索
search_kwargs={
"k": RETRIEVE_TOP_K, # 返回最相关的 4 个文档块
"fetch_k": 10, # 从数据库中获取 10 个候选文档
"lambda_mult":0.3 # 越小越看重相似度,越大看重多样性
}
)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
return rag_chain
# 自定义追加文档函数,避免重复添加相同文档
def add_documents_safe(db, docs):
ids = [str(uuid.uuid4()) for _ in docs]
db.add_documents(docs, ids=ids)
print(f"[+] 本次追加 {len(docs)} 个文本块,当前总数量:{db._collection.count()}")
def get_vector_store(documents, incremental: bool = True):
"""
:param documents: List[Document] 待入库文档
:param incremental: True=增量追加;False=覆盖重建(删除旧库)
:return: Chroma db对象
"""
# 覆盖模式:删除整个库重新构建
if not incremental:
if os.path.exists(CHROMA_PERSIST_DIR):
import shutil
shutil.rmtree(CHROMA_PERSIST_DIR)
print("[*] 删除旧向量库,开启覆盖重建模式")
split_docs = text_splitter.split_documents(documents)
if os.path.exists(CHROMA_PERSIST_DIR):
# 库已存在:增量add_documents
print("[+] 向量库已存在,增量追加新文档")
db = Chroma(persist_directory=CHROMA_PERSIST_DIR, embedding_function=embeddings)
#db.add_documents(split_docs)
#print(f"[+] 本次追加 {len(split_docs)} 个文本块,当前总数量:{db._collection.count()}")
add_documents_safe(db, split_docs)
else:
# 库不存在:初次创建
print("[*] 新建向量库")
db = Chroma.from_documents(
documents=split_docs,
embedding=embeddings,
persist_directory=CHROMA_PERSIST_DIR
)
print(f"[+] 存入 {len(split_docs)} 个文本块")
return db
if __name__ == "__main__":
batch = [
Document(page_content="llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。", metadata={"source":"note1.txt"}),
Document(page_content="RAG检索增强生成用于知识库问答。", metadata={"source":"note1.txt"}),
Document(page_content="Chroma是轻量级向量数据库。", metadata={"source":"note2.txt"}),
Document(page_content="RecursiveCharacterTextSplitter用于文档切分。", metadata={"source":"note2.txt"}),
]
# 构建向量库incremental=True 增量追加文档=False覆盖重建
db = get_vector_store(batch, incremental=True)
print(f"\n向量库总文档数:{db._collection.count()}")
# 打印全部候选+分数
docs_with_score = db.similarity_search_with_score("llama.cpp则作用是啥?", k=10)
print("\n[全部检索结果(分数越小越相似)]")
for doc, score in docs_with_score:
print(f"score={score:.4f} | source={doc.metadata['source']} | {doc.page_content}")
# 构建RAG链
chain = build_rag_chain(db)
# 调用RAG链
ans = chain.invoke("llama.cpp则作用是啥?")
print(f"\n[最终NaiveRAG回答]: {ans}")
MMR检索策略有效平衡相似度与多样性,lambda_mult=0.3 优先保证语义匹配精准度,同时规避结果同质化。输出结果自带文档来源,可快速定位知识库素材出处,适合后续知识库迭代优化。
bash
CMD> python main.py
[*] 新建向量库
[+] 存入 4 个文本块
向量库总文档数:4
[全部检索结果(分数越小越相似)]
score=1.0284 | source=note1.txt | RAG检索增强生成用于知识库问答。
score=1.0645 | source=note1.txt | llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。
score=1.1095 | source=note2.txt | Chroma是轻量级向量数据库。
score=1.1582 | source=note2.txt | RecursiveCharacterTextSplitter用于文档切分。
[最终NaiveRAG回答]: llama.cpp用来本地运行GGUF大模型,支持CPU和GPU加速。
增量PDF读取测试
原生LangChain PDF加载器依赖冗余第三方库,本案例手写轻量化PDF解析工具,仅依赖 pypdf,简洁高效、无多余依赖。支持读取本地PDF可编辑文本,自动按页封装Document对象,记录页码与文件来源元数据。
适配增量/覆盖入库模式,可直接将PDF文档批量向量化入库,实现本地PDF知识库问答。注意:仅支持可复制文本的PDF,扫描图片型PDF无法提取文本。
python
import os
from pypdf import PdfReader
from langchain_core.documents import Document
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
CHROMA_PERSIST_DIR = "./my_chroma_database"
RETRIEVE_TOP_K = 4
def text_splitter():
pass
def get_vector_store(documents, incremental: bool = True):
pass
def build_rag_chain(vector_db):
pass
def my_pdf_loader(pdf_path):
reader = PdfReader(pdf_path)
docs = []
for page_idx, page in enumerate(reader.pages):
text = page.extract_text() or ""
docs.append(Document(
page_content=text,
metadata={"source": pdf_path, "page": page_idx}
))
return docs
if __name__ == "__main__":
pdf_file_path = "./test.pdf"
if not os.path.exists(pdf_file_path):
raise FileNotFoundError(f"PDF文件不存在:{pdf_file_path}")
pdf_docs = my_pdf_loader(pdf_file_path)
print(f"[*] PDF读取完成,共 {len(pdf_docs)} 页")
db = get_vector_store(pdf_docs, incremental=False)
print(f"\n向量库总文档数:{db._collection.count()}")
test_query = "概括一下文章内容"
docs_with_score = db.similarity_search_with_score(test_query, k=10)
chain = build_rag_chain(db)
ans = chain.invoke(test_query)
print(f"\n[最终NaiveRAG回答]: {ans}")
PDF每页文本经过切片拆分,2页PDF最终生成8个文本块,保证切片长度均匀、适配向量模型输入。大模型基于全部PDF语义信息完成概括问答,实现私有PDF文档智能解读能力。
bash
CMD> python main.py
[*] PDF读取完成,共 2 页
[*] 删除旧向量库,开启覆盖重建模式
[*] 新建向量库
[+] 存入 8 个文本块
向量库总文档数:8
[最终NaiveRAG回答]: RAG是一种结合知识库检索与大模型生成的技术。它的工作流程包括文档加载、文本切分、向量化、存入向量数据库、检索、组装Prompt和输出答案。RAG的主要工作流程
包括文档加载、文本切分、向量化、存入向量数据库、检索、组装Prompt和输出答案。RAG的切分块大小不合适会导致噪声多或丢失上下文。RAG的主要工作流程包括文档加载、文
本切分、向量化、存入向量数据库、检索、组装Prompt和输出答案。
增量文本读取测试
针对本地TXT知识库场景,封装全自动文件夹批量加载工具。解决中文编码乱码问题,自动兼容 utf-8/gbk/gb2312 三大主流编码,无需手动适配。支持单层文件夹批量读取,可快速将大量笔记、文档批量入库构建私有知识库。
python
import os
from langchain_core.documents import Document
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
CHROMA_PERSIST_DIR = "./my_chroma_database"
RETRIEVE_TOP_K = 4
def text_splitter():
pass
def get_vector_store(documents, incremental: bool = True):
pass
def build_rag_chain(vector_db):
pass
def load_txt_file(file_path: str) -> list[Document]:
"""读取单个txt文件,自动尝试utf‑8、gbk编码"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件不存在:{file_path}")
text = ""
for enc in ("utf‑8", "gbk", "gb2312"):
try:
with open(file_path, "r", encoding=enc) as f:
text = f.read()
break
except UnicodeDecodeError:
continue
else:
raise RuntimeError(f"{file_path} 编码无法识别,尝试utf‑8/gbk均失败")
return [Document(page_content=text, metadata={"source": file_path})]
def load_any_file(file_path: str) -> list[Document]:
"""通用加载器,仅支持txt"""
ext = os.path.splitext(file_path)[1].lower()
if ext != ".txt":
raise ValueError(f"暂不支持文件类型 {ext},仅支持txt")
return load_txt_file(file_path)
class DirectoryLoader:
"""批量加载文件夹下全部txt文档"""
def __init__(self, directory: str, recursive: bool = False):
self.directory = directory
self.recursive = recursive
self.support_suffix = {".txt"}
def load(self) -> list[Document]:
all_docs = []
if not os.path.isdir(self.directory):
raise NotADirectoryError(f"目录不存在:{self.directory}")
for root, _, files in os.walk(self.directory):
for filename in files:
ext = os.path.splitext(filename)[1].lower()
if ext not in self.support_suffix:
continue
full_path = os.path.join(root, filename)
try:
print(f"\n[*] 正在加载:{full_path}")
docs = load_any_file(full_path)
all_docs.extend(docs)
print(f"[+] {filename} 读取成功")
except Exception as e:
print(f"[!] 跳过 {filename},失败:{str(e)}")
if not self.recursive:
break
return all_docs
if __name__ == "__main__":
doc_dir = "./txt_docs"
loader = DirectoryLoader(directory=doc_dir, recursive=False)
all_docs = loader.load()
if not all_docs:
print("\n[!] 文件夹没有读取到任何有效txt文档,程序退出")
else:
db = get_vector_store(all_docs, incremental=False)
print(f"\n[+] 向量库总文档数:{db._collection.count()}")
chain = build_rag_chain(db)
test_query = "llama.cpp的作用是什么?"
ans = chain.invoke(test_query)
print(f"[最终NaiveRAG回答]: {ans}\n")
批量读取文件夹内所有TXT文档,自动切片、入库、构建向量库,全程自动化。适合批量导入笔记、技术文档、学习资料,快速搭建专属文本知识库。
bash
CMD> python main.py
[*] 正在加载:./txt_docs\111.txt
[+] 111.txt 读取成功
[*] 正在加载:./txt_docs\222.txt
[+] 222.txt 读取成功
[*] 新建向量库
[+] 存入 8 个文本块
[+] 向量库总文档数:8
[最终NaiveRAG回答]: llama.cpp是GGUF格式模型的本地推理库,主要用于在CPU/GPU上本地运行大模型。它支持GGUF量化模型,例如q4_k_m量化版本,可以降低显存占用,适合个人电脑本地部
署。llama.cpp可以启动OpenAI兼容接口,LangChain可以直接调用该接口,不需要特殊适配。
增量网页读取测试
本模块实现无依赖轻量化网页爬虫,不依赖LangChain社区爬虫组件,仅通过 requests+bs4 完成网页抓取与清洗。自动过滤脚本、样式、空行等无效噪声,提纯网页核心文本,适配网页知识库搭建场景。支持批量URL抓取,可快速将技术博客、文档网站内容入库问答。
python
import os
import requests
from bs4 import BeautifulSoup
from langchain_core.documents import Document
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
CHROMA_PERSIST_DIR = "./my_chroma_database"
RETRIEVE_TOP_K = 4
def text_splitter():
pass
def get_vector_store(documents, incremental: bool = True):
pass
def build_rag_chain(vector_db):
pass
def load_web_urls(url_list: list[str], timeout: int = 10) -> list[Document]:
"""
手写网页抓取,仅网页来源,无langchain_community依赖
:param url_list: url列表
:param timeout: 请求超时秒数
:return: List[Document]
"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
}
web_docs = []
for url in url_list:
try:
print(f"\n[*] 正在抓取网页: {url}")
resp = requests.get(url, headers=headers, timeout=timeout)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# 移除脚本、样式标签
for bad_tag in soup(["script", "style", "noscript"]):
bad_tag.decompose()
raw_text = soup.get_text(separator="\n")
lines = [line.strip() for line in raw_text.splitlines() if line.strip()]
clean_text = "\n".join(lines)
doc = Document(
page_content=clean_text,
metadata={"source": url}
)
web_docs.append(doc)
print(f"[+] {url} 抓取成功,文本长度:{len(clean_text)}")
except requests.exceptions.RequestException as e:
print(f"[!] 请求异常 {url} : {str(e)}")
except Exception as e:
print(f"[!] 解析失败 {url} : {str(e)}")
return web_docs
if __name__ == "__main__":
all_docs = []
url_list = [
"https://www.lyshark.com"
]
web_docs = load_web_urls(url_list)
all_docs.extend(web_docs)
if not all_docs:
print("\n[!] 没有加载到任何文档,程序退出")
else:
db = get_vector_store(all_docs, incremental=False)
print(f"\n[+] 向量库总文档数:{db._collection.count()}")
chain = build_rag_chain(db)
test_query = "请概括网页内容"
ans = chain.invoke(test_query)
print(f"[最终NaiveRAG回答]: {ans}\n")
网页原始文本经过降噪清洗、切片拆分,长文本切割为32个适配向量化的文本块,成功构建网页知识库。可实现对任意公开技术网页的内容总结、问答、检索,适合学习资料快速沉淀。
bash
CMD> python main.py
[*] 正在抓取网页: https://www.lyshark.com
[+] https://www.lyshark.com 抓取成功,文本长度:7706
[*] 删除旧向量库,开启覆盖重建模式
[*] 新建向量库
[+] 存入 32 个文本块
[+] 向量库总文档数:32
[最终NaiveRAG回答]: 一个致力于信息安全知识分享的专业平台,平台高度关注前沿攻防技术研究。
CMP扫描。