Appearance
第五阶段:LlamaIndex 企业知识库(读 PDF / Word,注明出处)
上一阶段我们手写了 RAG。真实业务里的文档是 PDF、Word,需要解析、切分、建索引、注明出处——这些 LlamaIndex 都帮我们封装好了。这一节完成项目 1:企业知识库问答。
一、安装
bash
pip install llama-index llama-index-llms-openai-like llama-index-embeddings-openai-like llama-index-readers-filellama-index:核心框架llama-index-llms-openai-like:接入 OpenAI 兼容的 LLM(我们的中转平台)llama-index-embeddings-openai-like:接入 OpenAI 兼容的 Embedding(本地 LM Studio)llama-index-readers-file:PDF / Word 解析插件,必须装! 缺了它 PDF 会被当成纯文本读,结果全是乱码(见 踩坑记录)。
把要问答的文档(PDF、Word、txt 等)放进项目下的 data/ 文件夹。
二、05_kb.py 完整代码
python
import os
from dotenv import load_dotenv
from llama_index.core import (Settings, SimpleDirectoryReader, StorageContext,
VectorStoreIndex, load_index_from_storage)
from llama_index.core.node_parser import SentenceSplitter # 文本切分器
from llama_index.llms.openai_like import OpenAILike # OpenAI 兼容 LLM
from llama_index.embeddings.openai_like import OpenAILikeEmbedding # OpenAI 兼容 Embedding
load_dotenv() # 读取 .env 配置
# ===== 1. 全局设置:告诉 LlamaIndex 用哪个 LLM、哪个 Embedding、怎么切分 =====
Settings.llm = OpenAILike(
model=os.getenv("LLM_MODEL"), # 例如 deepseek-flash
api_base=os.getenv("LLM_BASE_URL"), # 中转平台地址
api_key=os.getenv("LLM_API_KEY"),
is_chat_model=True, # 使用 Chat 接口(必须设,否则走旧的补全接口)
temperature=0, # 问答要稳定、忠实于资料
)
Settings.embed_model = OpenAILikeEmbedding(
model_name="bge-m3", # 以 LM Studio 中显示的模型名为准
api_base="http://localhost:1234/v1", # 本地 LM Studio
api_key="lm-studio", # 本地服务不校验,随便填
embed_batch_size=64, # 每批送 64 段文字去算向量,加快速度
)
# 每段约 512 个 token,相邻段重叠 50 个 token,避免一句话被切断后丢失上下文
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)
STORAGE = "./kb_storage" # 索引保存目录
# ===== 2. 建索引或加载已有索引 =====
if os.path.exists(STORAGE):
# 已经建过:直接从磁盘加载,省去重新解析和计算向量
storage_context = StorageContext.from_defaults(persist_dir=STORAGE)
index = load_index_from_storage(storage_context)
print("已加载现有索引")
else:
# 第一次运行:读取 data 目录下的所有文档
documents = SimpleDirectoryReader("./data").load_data()
for doc in documents:
# 打印每个文档(PDF 每页一个)的文件名、字数和前 300 字,检查解析是否正常
print(doc.metadata.get("file_name"), len(doc.text), "字")
print(doc.text[:300])
print("-" * 40)
# 切分 → 计算向量 → 建索引,show_progress 显示进度条
index = VectorStoreIndex.from_documents(documents, show_progress=True)
index.storage_context.persist(persist_dir=STORAGE) # 保存到磁盘
print("索引已建立并保存")
# ===== 3. 创建问答引擎:每次检索最相关的 3 段 =====
query_engine = index.as_query_engine(similarity_top_k=3)
# ===== 4. 循环提问 =====
while True:
q = input("\n问题:")
if q == "exit":
break
response = query_engine.query(q)
print("【回答】", response)
print("【出处】")
for node in response.source_nodes: # 检索到的原文片段
meta = node.metadata
print(f" {meta.get('file_name')} 第{meta.get('page_label')}页 "
f"相似度 {node.score:.3f}:{node.get_content()[:80]}")三、.gitignore
文档可能涉及版权或隐私,索引里也含有原文,都不要上传:
bash
echo data/ >> .gitignore
echo kb_storage/ >> .gitignore
git add .
git commit -m "05 LlamaIndex 企业知识库"
git push四、实测结果(真实运行数据)
测试文档:折叠键盘说明书.pdf,1 页、1263 字,被切成 3 段,约 2 秒建完索引。
问:怎么配对
打开电源开关(ON),红灯亮 10 秒后熄灭;按 Fn+A / S / D 打开 BT1 / BT2 / BT3 通道,点按 Connect 键;手机搜索配对名 BOW;连接成功后指示灯熄灭。
出处:折叠键盘说明书.pdf 第 1 页,相似度 0.558 / 0.484。
问:怎么保修
按三包规定:签收次日起 7 日内可退货或换货;8~30 日内可换货或维修;12 个月内免费维修。并给出了所需资料和服务热线。
回答带上了文件名和页码,用户可以回原文核对——这就是企业知识库“可追溯”的价值。
五、要点
- RAG 效果差,常常是文档解析出了问题,而不是模型不行。 建索引前先看打印出来的字数和前 300 字是否正常。
- 换了文档后必须删除旧索引,否则程序会直接加载
kb_storage,仍然检索旧内容(PowerShell):
powershell
Remove-Item -Recurse -Force kb_storage- 扫描版 PDF、缺少解析插件等问题见 踩坑记录。
下一节预告
进入项目 2:客服 / 工单 Agent,学习 Function Calling——让模型自己决定调用查订单、建工单等工具。
👉 下一阶段:Function Calling 工具调用