llamaindex rag示例项目
项目结构
llamaindex-rag-example/
├── data/ # 存放待处理的原始文档(如PDF/TXT/Markdown等)
├── storage_exp/ # 持久化存储向量索引的目录(自动生成)
├── 01_build.py # 构建并持久化向量索引的脚本
└── 02_query.py # 加载索引并进行问答查询的脚本
PDF文件转Markdown
#如果直接用 pdf版解析会很差,可以用mineru转化为md文件,但是测试 api 速度也不快, 可以直接在官网将文档转化下载,时间最快,还可以进行调整 官网:https://mineru.net/
01_build.py 构建索引
import os
import time
import torch
from pathlib import Path
from llama_index.core import VectorStoreIndex, Settings, Document
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SimpleNodeParser
# 配置
device = "cpu"
#如果直接用 pdf版解析会很差,可以用mineru转化为md文件,但是测试 api 速度也不快, 可以直接在官网将文档转化下载,时间最快,还可以进行调整https://mineru.net/
markdown_path = "./data/test_manual.md"
persist_dir = "./storage_exp"
# 创建目录
os.makedirs(persist_dir, exist_ok=True)
os.makedirs("./models", exist_ok=True)
# 2. 配置全局 Embedding(本地 Qwen3 模型)
EMBEDDING_PATH = "/Users/t-mac/.cache/huggingface/hub/models--Qwen--Qwen3-Embedding-0.6B/snapshots/c54f2e6e80b2d7b7de06f51cec4959f6b3e03418"
Settings.embed_model = HuggingFaceEmbedding(
model_name=EMBEDDING_PATH,
trust_remote_code=True
)
# 读取 Markdown 文件
print(f"读取 Markdown: {markdown_path}")
with open(markdown_path, "r", encoding="utf-8") as f:
markdown_content = f.read()
print(f"文档长度: {len(markdown_content)} 字符")
# 创建 Document
doc = Document(
text=markdown_content,
metadata={
"source": Path(markdown_path).name,
"file_path": str(Path(markdown_path).absolute()),
}
)
# 切分文档
print("切分文档...")
parser = SimpleNodeParser(chunk_size=512, chunk_overlap=50)
nodes = parser.get_nodes_from_documents([doc])
print(f"生成 {len(nodes)} 个节点")
# 构建索引
print("构建索引...")
start_time = time.time()
index = VectorStoreIndex(nodes)
build_time = time.time() - start_time
print(f"索引构建完成,耗时 {build_time:.2f} 秒")
# 保存索引
print(f"保存索引到 {persist_dir}...")
index.storage_context.persist(persist_dir=persist_dir)
# 统计
index_size = 0
for root, dirs, files in os.walk(persist_dir):
for file in files:
index_size += os.path.getsize(os.path.join(root, file))
print("\n" + "="*50)
print("构建完成!")
print("="*50)
print(f"文档长度: {len(markdown_content):,} 字符")
print(f"节点数量: {len(nodes)}")
print(f"索引大小: {index_size / 1024:.1f} KB")
print(f"总耗时: {build_time:.2f} 秒")
print("="*50)
02_query.py
import os
import sys
import time
from llama_index.core import StorageContext, load_index_from_storage, Settings
from llama_index.llms.openai import OpenAI
from llama_index.llms.openai_like import OpenAILike
from llama_index.core.prompts import PromptTemplate
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.retrievers.bm25 import BM25Retriever
# 导入依赖
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
QIANWEN_KEY = os.getenv("QIANWEN_API_KEY")
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# 配置
device = "mps"
persist_dir = "./storage_exp"
lm_studio_url = "http://localhost:1234/v1"
# 2. 配置全局 Embedding(本地 Qwen3 模型)
EMBEDDING_PATH = "/Users/t-mac/.cache/huggingface/hub/models--Qwen--Qwen3-Embedding-0.6B/snapshots/c54f2e6e80b2d7b7de06f51cec4959f6b3e03418"
Settings.embed_model = HuggingFaceEmbedding(
model_name=EMBEDDING_PATH,
trust_remote_code=True
)
# 1. 配置全局 LLM(通义千问)
Settings.llm = OpenAILike(
model="qwen3.5-flash",
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=QIANWEN_KEY,
is_chat_model=True,
temperature=0.1
)
# 重排序器
# 中文专用重排模型
# reranker = SentenceTransformerRerank(
# model="BAAI/bge-reranker-base",
# top_n=5
# )
# 提示词模板
PROMPT = PromptTemplate(
"""你是一个专业的 HR 助手,请基于以下员工手册内容回答问题。
{context_str}
问题:{query_str}
回答:"""
)
# 加载索引
print("加载索引...")
storage_context = StorageContext.from_defaults(persist_dir=persist_dir)
index = load_index_from_storage(storage_context)
print(f"索引加载成功,共 {len(index.docstore.docs)} 个节点")
def hybrid_search(query, top_k=5):
"""混合检索"""
# 向量检索
vector_retriever = index.as_retriever(similarity_top_k=10)
vector_nodes = vector_retriever.retrieve(query)
# BM25 检索
all_nodes = []
for doc_id in index.docstore.docs.keys():
doc = index.docstore.get_document(doc_id)
if hasattr(doc, 'nodes'):
all_nodes.extend(doc.nodes)
else:
all_nodes.append(doc)
bm25_retriever = BM25Retriever.from_defaults(
nodes=all_nodes,
similarity_top_k=10,
)
bm25_nodes = bm25_retriever.retrieve(query)
# 合并去重
seen = set()
merged = []
for node in vector_nodes + bm25_nodes:
if node.node.node_id not in seen:
seen.add(node.node.node_id)
merged.append(node)
return merged[:top_k]
# # 重排序
# reranked = reranker.postprocess_nodes(merged, query_str=query)
# return reranked[:top_k]
def generate_answer(query, nodes):
"""生成回答"""
from llama_index.core import get_response_synthesizer
synthesizer = get_response_synthesizer(text_qa_template=PROMPT)
return synthesizer.synthesize(query, nodes=nodes)
def query(question):
"""执行查询"""
nodes = hybrid_search(question)
response = generate_answer(question, nodes)
return response.response
if __name__ == "__main__":
question = "员工违约的条件有哪些?"
print(f"\n问题: {question}")
print("-" * 40)
start = time.time()
answer = query(question)
elapsed = time.time() - start
print(f"回答: {answer}")
print("-" * 40)
print(f"耗时: {elapsed:.2f} 秒")
测试结果
/opt/miniconda3/envs/langchain-dev/bin/python /Users/t-mac/workspace/Python/python_code/24-LlamaIndex/projects/llamaindex-rag-example/02_query.py
W0522 23:40:21.752000 33319 site-packages/torch/distributed/elastic/multiprocessing/redirects.py:35] NOTE: Redirects are currently not supported in MacOs.
W0522 23:40:21.774000 33319 site-packages/torch/utils/_pytree.py:630] <enum 'KernelPreference'> is an Enum subclass and is now natively supported by torch.compile as an opaque value type. Calling register_constant() on Enum subclasses is deprecated and will be an error in a future release.
W0522 23:40:21.793000 33319 site-packages/torch/utils/_pytree.py:630] <enum 'ScaleCalculationMode'> is an Enum subclass and is now natively supported by torch.compile as an opaque value type. Calling register_constant() on Enum subclasses is deprecated and will be an error in a future release.
Loading weights: 100%|██████████| 310/310 [00:00<00:00, 8048.99it/s]
加载索引...
索引加载成功,共 98 个节点
问题: 员工违约的条件有哪些?
----------------------------------------
回答: 你好!我是你的专业 HR 助手。根据提供的《员工手册》内容,虽然手册中主要使用“违纪”、“严重违纪”及“解除劳动合同”等术语,但以下情况均属于员工违反公司规章制度或劳动合同约定的条件(即通常理解的“违约”情形)。
以下是基于文档整理的员工违纪及违规的具体条件分类:
### 1. 考勤与出勤违规
* **迟到早退:** 连续 30 天内迟到或早退三次及以上(10.1.1);试用期内迟到早退累计达三次或以上(4.5)。
* **旷工:**
* 未经请假或请假未获批准擅自不到岗(3.1)。
* 请假期满未续假或续假未批逾期不归(3.3.2)。
* 特殊原因未在当日上班 1 小时内请假,或事后 3 个工作日内未补办手续(3.3.3)。
* 不服从工作调动、工时制度调整拒不执行导致未正常出勤(3.3.4)。
* **后果阈值:** 连续旷工 2 天及以上,或一年内累计旷工 3 天及以上(10.1.2);连续旷工 3 天及以上,或全年累计旷工 7 天及以上(3.2)。
* **打卡异常:** 委托他人打卡或替他人打卡(10.1.3);连续 30 天累计 6 次以上未打卡且无法说明原因或提供证明(1.4 / 10.1.4)。
* **试用期出勤率:** 试用期实际出勤日/应出勤日低于 85%(4.5)。
### 2. 诚信与入职信息违规
* **虚假信息:** 伪造学历、工作经历、证书,简历、入职登记信息、健康状况等与真实情况不符(4.6)。
* **隐瞒欺骗:** 有欺骗、隐瞒行为(4.6)。
* **价值观问题:** 诚信等价值观存在问题,无论服务年限长短,均可能导致立即解除劳动合同(1.3)。
* **竞业限制:** 入职本公司将违反竞业限制义务(4.15)。
### 3. 行为规范与安全违规
* **扰乱秩序:** 发起、参与离岗、怠工、罢工、示威,或鼓动他人参与(10.2.1)。
* **骚扰行为:** 侮辱、诽谤、辱骂、造谣、威胁、恐吓他人(10.2.2)。
* **现场管理:** 将无关人员带入工作现场,情节严重或造成损害(10.2.3)。
* **安全隐患:** 在库房等高风险区域内吸烟,情节严重或造成损害(10.2.4)。
* **资产损害:** 损坏或遗失公司财物,浪费公司资产,情节严重或造成损害(10.2.5 / 10.2.6)。
### 4. 法律、健康与个人资质违规
* **刑事犯罪:** 被判处有期徒刑尚在服刑(4.8);通缉在案(4.10);实施违反国家法律法规的行为(如加入邪教、恐怖组织、酗酒、吸毒、赌博、嫖娼等)(4.14)。
* **权利状态:** 被剥夺公民权利者(4.9)。
* **年龄与健康:** 未满 16 周岁(4.11);传染性疾病病原携带者或患有其他严重疾病(4.12);患有精神病(4.13)。
* **债务问题:** 拖欠他人重大债务或进入法院执行程序未偿还债务者(4.16)。
* **过往记录:** 曾经被公司辞退或未经批准擅自离职者(4.7)。
### 5. 试用期特殊解除条件
* **处分记录:** 试用期期间受记过及以上处分者(4)。
* **综合表现:** 上述考勤、诚信、法律等条款在试用期内触犯者(4.5 - 4.16)。
### 6. 管理与连带责任
* **监管不力:** 管理者对员工违纪行为存在监管不力、失察、包庇、纵容或教唆等情形的,主管也将受到处罚(9.6)。
* **兜底条款:** 其他任何与前述违纪行为严重性相当的行为,或违反部门规定的行为,情节较重或造成一般损害的(5)。
**总结:**
根据手册第 10 条,若员工出现上述**严重违纪行为**,公司将对其进行**辞退(解除劳动合同)**。其中涉及诚信价值观问题、刑事犯罪、严重旷工等行为,公司有权立即解除合同且不支付经济补偿。
希望以上整理能帮助您准确理解员工手册中的相关规定。如有具体案例需要分析,欢迎随时提问。
----------------------------------------
耗时: 37.51 秒