写在前面,直接使用dify吧,rag可视化分块,混合检索,有界面还是可以调接口,也可以使用agnet,公司落地dify后效果挺好的
- 知识库手动搭建完整代码如下
- 调试可以使用
127.0.0.1/docs
进行调试
# 考勤问答系统优化版(单文件)
import chromadb
from llama_index.core import (
VectorStoreIndex,
StorageContext,
SimpleDirectoryReader,
Settings,
PromptTemplate,
get_response_synthesizer # 新增导入
)
from llama_index.core.node_parser import SentenceSplitter
# 修改 BM25Retriever 的导入方式
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
import nest_asyncio
nest_asyncio.apply() # Add this right after imports
# ============== 配置参数 ==============
CONFIG = {
"data_path": r"D:\pythoncode\rag_study\data\福立盟考勤制度.pdf", # 文档路径
"chunk_size": 350, # 适合中文的块大小
"chunk_overlap": 50, # 块重叠量
"separators": ["。", "!", "?", "\n"], # 中文分句符
"similarity_top_k": 3, # 向量检索数量
"bm25_top_k": 2, # 关键词检索数量
"chroma_host": "localhost",
"chroma_port": 8000,
"collection_name": "flm_attendance",
"llm_model": "deepseek-r1:1.5b",
"embed_model": "milkey/dmeta-embedding-zh:f16",
"hash_path": r"D:\pythoncode\rag_study\data\processed_hashes.txt", # 新增哈希存储路径
"nodes_cache": r"D:\pythoncode\rag_study\data\cached_nodes.pkl" # 新增节点缓存路径
}
# =====================================
# ============== 新增工具函数 ==============
import hashlib
import pickle
def compute_file_hash(file_path):
"""计算文件内容的哈希值"""
hasher = hashlib.md5()
with open(file_path, 'rb') as f:
while chunk := f.read(8192):
hasher.update(chunk)
return hasher.hexdigest()
def load_processed_hash(hash_path):
"""加载已处理的哈希值"""
try:
with open(hash_path, 'r') as f:
return f.read().strip()
except FileNotFoundError:
return None
def save_processed_hash(hash_path, hash_value):
"""保存当前处理的哈希值"""
with open(hash_path, 'w') as f:
f.write(hash_value)
def save_nodes_cache(cache_path, nodes):
"""持久化节点数据"""
with open(cache_path, 'wb') as f:
pickle.dump(nodes, f)
def load_nodes_cache(cache_path):
"""加载缓存的节点数据"""
with open(cache_path, 'rb') as f:
return pickle.load(f)
def initialize_query_engine():
"""初始化查询引擎"""
# 1. 模型设置
Settings.llm = Ollama(model=CONFIG["llm_model"], streaming=True) # 流式模式
Settings.embed_model = OllamaEmbedding(model_name=CONFIG["embed_model"])
# 3. 初始化ChromaDB
chroma_client = chromadb.HttpClient(
host=CONFIG["chroma_host"],
port=CONFIG["chroma_port"]
)
# 判断集合是否存在
collection_exists = CONFIG["collection_name"] in chroma_client.list_collections()
# ============== 新增哈希校验逻辑 ==============
current_hash = compute_file_hash(CONFIG["data_path"])
processed_hash = load_processed_hash(CONFIG["hash_path"])
need_rebuild = not collection_exists or current_hash != processed_hash
# ============================================
if need_rebuild:
# 2. 文档加载与中文分块
reader = SimpleDirectoryReader(input_files=[CONFIG["data_path"]])
documents = reader.load_data()
node_parser = SentenceSplitter(
chunk_size=CONFIG["chunk_size"],
chunk_overlap=CONFIG["chunk_overlap"],
separator="\n",
paragraph_separator="\n\n"
)
nodes = node_parser.get_nodes_from_documents(documents, show_progress=True)
# 创建/更新集合
if collection_exists:
chroma_client.delete_collection(CONFIG["collection_name"])
collection = chroma_client.create_collection(CONFIG["collection_name"])
# ============== 新增缓存保存 ==============
save_processed_hash(CONFIG["hash_path"], current_hash)
save_nodes_cache(CONFIG["nodes_cache"], nodes)
# =========================================
vector_store = ChromaVectorStore(chroma_collection=collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 4. 构建索引
index = VectorStoreIndex(
nodes,
storage_context=storage_context,
show_progress=True
)
else:
# 复用现有集合
collection = chroma_client.get_collection(CONFIG["collection_name"])
vector_store = ChromaVectorStore(chroma_collection=collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 直接加载已有索引
index = VectorStoreIndex.from_vector_store(
vector_store,
storage_context=storage_context
)
# ============== 加载缓存节点 ==============
nodes = load_nodes_cache(CONFIG["nodes_cache"])
# =========================================
# 5. 混合检索配置
vector_retriever = VectorIndexRetriever(
index=index,
similarity_top_k=CONFIG["similarity_top_k"]
)
bm25_retriever = BM25Retriever.from_defaults(
nodes=nodes,
similarity_top_k=CONFIG["bm25_top_k"]
)
# 6. 融合检索器--混合检索模式
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.retrievers.fusion_retriever import FUSION_MODES
retriever = QueryFusionRetriever(
retrievers=[vector_retriever, bm25_retriever],
similarity_top_k=CONFIG["similarity_top_k"],
num_queries=2, # 扩展查询次数 生成两个变体查询 如 查询 请假 可以生成变体查询“如何申请请假”,“休假的步骤”
# mode="reciprocal_rerank", # 混合排序策略,
mode=FUSION_MODES.RECIPROCAL_RANK # 混合排序策略,
)
# 7. 定制化回答模板
global qa_prompt
qa_prompt = PromptTemplate(
"请用简洁的中文回答以下问题,并引用制度条款:\n"
"上下文信息:\n{context_str}\n" # 此处插入检索到的相关文本片段
"问题:{query_str}\n" # 此处插入用户原始问题
"答案:" # LLM 从这里开始生成回答
)
# 8. 构建查询引擎
from llama_index.core.response_synthesizers.type import ResponseMode
return RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=get_response_synthesizer(
text_qa_template=qa_prompt,
# response_mode="compact" # 压缩响应模式
response_mode=ResponseMode.COMPACT, # 简化数据让所有数据在一个窗口中显示出来
streaming=True # 流式
)
)
# 在文件顶部添加以下导入
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
import uvicorn
from typing import AsyncGenerator
import asyncio
# 在 CONFIG 配置后添加Web服务初始化
# 在FastAPI初始化后添加lifespan处理器替换旧的on_event方式
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时初始化
global query_engine
query_engine = initialize_query_engine()
yield
# 关闭时清理资源
pass
app = FastAPI(lifespan=lifespan) # 替换原来的 app = FastAPI()
# 删除旧的 @app.on_event("startup") 代码块
# 配置跨域中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 全局查询引擎实例
query_engine = None
# 在CONFIG配置后添加全局上下文存储
chat_history = []
async def stream_response(query: str) -> AsyncGenerator[str, None]:
streaming_response = query_engine.query(query)
yield " " # 发送初始空数据
# 添加调试信息
print(f"\n检索到的节点数: {len(streaming_response.source_nodes or [])}")
if streaming_response.source_nodes:
print("前2个相关节点:")
for i, node in enumerate(streaming_response.source_nodes[:2]):
print(f"[{i}] {node.text[:100]}...")
# 流式输出
for token in streaming_response.response_gen:
yield token
await asyncio.sleep(0.01)
@app.post("/chat")
async def chat_with_llm(message: str):
global query_engine, chat_history,qa_prompt
try:
# 直接使用全局query_engine
knowledge_response = await asyncio.to_thread(query_engine.query, message)
context_str = "\n".join([node.text for node in knowledge_response.source_nodes[:3]])
# 从chat_history生成对话历史
chat_context = "\n".join([f"用户:{msg[0]}\nAI:{msg[1]}" for msg in chat_history[-3:]]) if chat_history else "无历史对话"
# 使用标准变量名构建prompt
full_prompt = qa_prompt.format(
context_str=f"对话历史:\n{chat_context}\n\n相关制度条款:\n{context_str}",
query_str=message
)
# 获取流式响应
async def generate():
yield " "
response = Settings.llm.complete(full_prompt)
for token in response.text:
yield token
await asyncio.sleep(0.01)
chat_history.append((message, response.text))
return StreamingResponse(
generate(),
media_type="text/event-stream"
)
except Exception as e:
return {"error": str(e)}
# 保留原有ask_question接口
@app.get("/ask")
async def ask_question(query: str):
return StreamingResponse(
stream_response(query),
media_type="text/event-stream"
)
# 修改文件底部的主程序启动代码
if __name__ == "__main__":
# 再启动FastAPI服务
uvicorn.run(
"017多人问答:app",
host="127.0.0.1",
port=8001,
workers=1, # 调试时先用单进程
reload=False
)
一、 如何下载模型参考下列文章和视频
https://testerhome.com/articles/41474
https://www.bilibili.com/video/BV1QyFoeuE3e/?spm_id_from=333.1387.favlist.content.click&vd_source=13dd0c4a82a579a2d8aef2ac9bf547bc
网友关于此书的练习代码 可以直接获取连接
-
启动模型命令
ollama run deepseek-r1:1.5b
-
列出本地已下载的 AI 模型
ollama list
参考书籍及视频
- 《基于大模型的RAG应用开发与优化——构建企业级LLM应用 (严灿平) 》
- https://space.bilibili.com/396453622/favlist?fid=3479234322&ftype=create
二、 如何搭建公司的知识库
大概流程:数据准备→预处理→向量化存储→模型集成→构建检索和生成系统→权限和界面开发→测试优化→部署维护
0、环境搭建
运行代码时及得关闭`喂屁嗯`软件如,避免不必要的报错
- ollama : 官网下载 参考第一章节内容
- 大模型: ollama run deepseek-r1:1.5b
- 嵌入模型 : ollama pull milkey/dmeta-embedding-zh:f16
- miniconda 安装 官网
- 向量库: chroma
- llama-index 的安装
- PostgreSQL 与 pgvector 安装
开启调试程序之前需要在终端(如pycharm下的terminal)启动
- ollama
ollama run deepseek-r1:1.5b
- chromadb(默认的8000端口 不要被占用)
chroma run --path ./chroma_db
3 milkey/dmeta-embedding-zh:f16 是否正常工作测试图 测试图如下,工具使用的是postman
import requests
import json
url = "http://127.0.0.1:11434/api/embeddings"
payload = json.dumps({
"model": "milkey/dmeta-embedding-zh:f16",
"prompt": "Here is an article about llamas..."
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
4 打开 Anaconda Prompt
输入conda create -n rag python=3.12.1
下载相关库包 rag是虚拟环境得名称
选择conda环境 选择虚拟环境里的解释器
5 激活名为 rag 的虚拟运行环境
conda activate rag
pip install chromadb
win10安装可能会出现 error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
问题请参考下面方法
在 Windows 系统上安装 chromadb
时出现编译错误(如缺少 Microsoft Visual C++ 14.0),通常是因为依赖的 C++ 扩展需要本地编译环境。以下是针对此问题的具体解决方案:
1. 安装 Microsoft Visual C++ 构建工具
这是最直接的解决方法,因为 chromadb
的依赖项(如 hnswlib
)需要 C++ 编译器才能编译。
步骤:
- 访问微软官方下载页面:Microsoft C++ Build Tools。
- 下载并运行安装程序,选择 “使用 C++ 的桌面开发” 工作负载。
- 在右侧的 “安装详细信息” 中勾选:
- MSVC v142 - VS 2019 C++ x64/x86 生成工具
- Windows 10 SDK(或最新版本)
- C++ CMake 工具(可选,但推荐)
- 完成安装后重启电脑,确保环境变量生效。
测试
import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
resp=client.heartbeat()
print(resp) # 如果返回正常(一串纳秒数字),那么说明 Chroma 工作正常
7
pip install llama-index
pip install llama-index-embeddings-ollama
pip install llama-index-llms-ollama
pip install llama-index-vector-stores-chroma
pip install --upgrade llama-index-core==0.12.19 chromadb==0.6.3
- 从网络加载数据需要安装的包
pip install llama-index-readers-web
二、初步认识
1 .第一个rag项目
- 如果下面这个脚本可以运行成功说明环境配置没有问题了
#first_rag.py
# 记得关闭未批嗯
import chromadb
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.core import SimpleDirectoryReader, Settings
from llama_index.core.node_parser import SentenceSplitter
# 标准导入方式(推荐)
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
# 设置模型
Settings.llm = Ollama(model="deepseek-r1:1.5b",request_timeout=)
Settings.embed_model = \
OllamaEmbedding(model_name="milkey/dmeta-embedding-zh:f16")
# ---加载与读取文档---
reader = SimpleDirectoryReader(input_files=["../data/福立盟考勤制度.pdf"])
documents = reader.load_data() # 返回一个Document对象列表,每个对象包含文件内容及元数据 pdf一页一个documents
# 维度 Document Node
# 内容范围 完整原始数据 分割后的语义块(已经向量化了)
# ---加载与读取文档结束---
# ---分割文档---
node_parser = SentenceSplitter(chunk_size=500,
chunk_overlap=20) # (chunk_size 500字符/块,chunk_overlap=20 20字符重叠 重叠的意思是保留前一个句子末尾的20个字符)
nodes = node_parser.get_nodes_from_documents(documents,
show_progress=True) # get_nodes_from_documents文本预处理除PDF中的乱码、错误换行; show_progress=False - 静默模式开关关闭进度条显示
# ---分割文档结束---
# ---准备向量存储---
chroma = chromadb.HttpClient(host="localhost", port=8000) # 这里的port与启动chroma 指定的端口有关 chroma run --path /db_path --port 9000 不指定port默认8000
# chroma.delete_collection(name="ragdb") # 若不存在则会报错
collection = chroma.get_or_create_collection(name="ragdb",
metadata={"hnsw:space": "cosine"}) # get_or_create_collection 存在即获取,不存在则创建 # hnsw:指代分层可导航小世界算法(Hierarchical Navigable Small World) space:定义向量空间的相似度度量方式 cosine表示使用余弦相似度计算向量间的相关性
vector_store = ChromaVectorStore(chroma_collection=collection) # 封装以下 实现数据库的增删改查 操作
# ---准备向量存储结束---
# 准备向量存储索引
storage_context = StorageContext.from_defaults(vector_store=vector_store) # 存储组件集成 指明向量存到哪里 vector_store=vector_store 用你指定的向量数据库(如 ChromaDB)替换默认的内存存储
index = VectorStoreIndex(nodes, storage_context=storage_context) # 将node文本向量化并存入ChromaDB 并建立向量索引结构
# 构造查询引擎
query_engine = index.as_query_engine() # 将索引(index)转换为一个问答引擎,赋予其理解自然语言问题并生成答案的能力 问题转换为向量——>检索器(余弦相似度)->相似度最高的几个进行拼装-->使用大模型回答问题
while True:
user_input = input("问题:")
if user_input.lower() == "exit":
break
response = query_engine.query(user_input)
print("AI 助手:", response.response)
2 跟踪与调试RAG应用
pip install langfuse
去https://langfuse.com/注册个账号得到下面的东西
在first_rag.py
增加代码
# --- langfusedebug设置 ---
from llama_index.core.callbacks import CallbackManager
from langfuse.llama_index import LlamaIndexCallbackHandler
import os
os.environ["LANGFUSE_SECRET_KEY"] = "sk-xxx-9a"
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-l-xx-e247"
os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com"
# 构造 Langfuse 平台的回调类
langfuse_callback_handler = LlamaIndexCallbackHandler()
# 设置到全局的 callback_manager
Settings.callback_manager = CallbackManager([langfuse_callback_handler])
......
if user_input.lower() == "exit":
# 程序退出之前注意缓存,将缓存的跟踪信息发送到 Langfuse Server 端
langfuse_callback_handler.flush()
# 在break上方增加
break
# --- ---
就可以在https://us.cloud.langfuse.com查看日志
3 compete(文本预测)与chat(对话)接口
维度 | 作用 |
---|---|
complete | 单次对话,无对话历史:每次请求都是独立上下文 不可设置system 指令 |
chat | 可多次对话,可携带上下文历史 可设置system 指令 |
from llama_index.core.llms import ChatMessage
# from llama_index.llms.openai import OpenAI
from llama_index.llms.ollama import Ollama
"""compete(持文本预测)与chat(对话)接口"""
llm = Ollama(model='deepseek-r1:1.5b', request_timeout=180)
# 测试complete接口
resp = llm.complete("白居易是") # 无对话历史:每次请求都是独立上下文
print(resp)
# 测试chat接口
messages = [
ChatMessage(
role="user", content="推荐成都的美食"
),
]
resp1 = llm.chat(messages)
print(resp1)
# 第二轮(携带历史)
messages.append(ChatMessage(role="assistant", content=resp1))
messages.append(ChatMessage(role="user", content="要适合带孩子吃的"))
resp2 = llm.chat(messages) # 模型会理解上下文推荐亲子餐厅
print(resp2)
4 三种角色
角色类型 | 作用 | 使用场景示例 |
---|---|---|
system | 设置对话的全局指令/背景知识(模型行为控制) | 定义回答风格、领域限制、输出格式要求 |
user | 代表用户的输入内容 | 用户的问题或指令 |
assistant | 代表模型的回复内容(可用于提供对话历史) | 多轮对话时携带历史回复 |
from llama_index.core.llms import ChatMessage
# from llama_index.llms.openai import OpenAI
from llama_index.llms.ollama import Ollama
"""compete(持文本预测)与chat(对话)接口"""
llm = Ollama(model='deepseek-r1:1.5b', request_timeout=180)
messages = [
ChatMessage(
role="system", content="你是一位专业的公司助理,模仿鲁迅的文风进行回答"
),
ChatMessage(
role="user", content="婚假有几天"
),
]
resp1 = llm.chat(messages)
print(resp1)
# 第二轮(携带历史)
messages.append(ChatMessage(role="assistant", content=resp1))
messages.append(ChatMessage(role="user", content="男员工陪产假有几天"))
resp2 = llm.chat(messages) # 模型会理解上下文推荐亲子餐厅
print(resp2)
"""
system的几个作用
1. 安全限制
ChatMessage(
role="system",
content="你拒绝回答任何与政治相关的问题,遇到此类问题统一回复:'根据安全策略,我无法回答该问题'"
)
2. 领域聚焦
ChatMessage(
role="system",
content="你是一个法律咨询助手,专门解答《民法典》相关问题,引用法律条文时需注明具体条款"
)
3. 输出控制
ChatMessage(
role="system",
content="用列表形式回答,每个条目不超过15字,最后用emoji总结"
)
"""
5 prompt 提示词
- 提示词模版默认是英文的,因此可以换成中文的
- 主要是
get_prompts
和update_prompts
两个方法的使用
import chromadb
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.core import SimpleDirectoryReader, Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.core.callbacks import (
CallbackManager,
LlamaDebugHandler,
CBEventType
)
from pprint import pprint
from llama_index.core import PromptTemplate
"""
查看模板与更新模板
"""
# 设置模型
Settings.llm = Ollama(model="deepseek-r1:1.5b")
Settings.embed_model = \
OllamaEmbedding(model_name="milkey/dmeta-embedding-zh:f16")
# --- 构造日志信息---
llama_debug = LlamaDebugHandler(print_trace_on_end=True) # 要在每次事件结束时立即打印出简单的跟踪信息
callback_manager = CallbackManager([llama_debug])
Settings.callback_manager = callback_manager
# --- 构造日志信息结束---
# 加载与读取文档
reader = SimpleDirectoryReader(input_files=["../data/福立盟考勤制度.pdf"])
documents = reader.load_data()
# 分割文档
node_parser = SentenceSplitter(chunk_size=500, chunk_overlap=20)
nodes = node_parser.get_nodes_from_documents(documents, show_progress=False, callback_manager=callback_manager)
# 准备向量存储
chroma = chromadb.HttpClient(host="localhost", port=8000)
chroma.delete_collection(name="ragdb")
collection = chroma.get_or_create_collection(name="ragdb", metadata={"hnsw:space": "cosine"})
vector_store = ChromaVectorStore(chroma_collection=collection)
# 准备向量存储索引
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex(nodes, storage_context=storage_context)
# 构造查询引擎
query_engine = index.as_query_engine()
# ---获得默认prompt模板 开始---
prompts_dict = query_engine.get_prompts() # get_prompts 获得其默认模板 存在多个模板
pprint(prompts_dict.keys())
pprint(prompts_dict["response_synthesizer:text_qa_template"].get_template())
# ---获得默认prompt模板 结束 ---
# --- 更新默认prompt模板 开始---
my_qa_prompt_tmpl_str = (
"以下是上下文信息。\n"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"根据上下文信息回答问题,不要依赖预置知识,不要编造。\n"
"问题: {query_str}\n"
"回答: "
)
my_qa_prompt_tmpl = PromptTemplate(my_qa_prompt_tmpl_str) # 转成相应的类型
query_engine.update_prompts(
{"response_synthesizer:text_qa_template": my_qa_prompt_tmpl}
)
prompts_dict = query_engine.get_prompts()
pprint(prompts_dict["response_synthesizer:text_qa_template"].get_template())
# --- 更新默认prompt模板 结束---
while True:
user_input = input("问题:")
if user_input.lower() == "exit":
break
response = query_engine.query(user_input)
pprint(llama_debug.get_event_pairs(CBEventType.QUERY))
print("AI助手:", response.response)
三、具体学习
1.document 和node
一页PDF就是一个`document`,一个`document`根据分块策略不同分成一个或多个`node`
Document(文档)和Node(节点)是构建RAG(检索增强生成)应用的核心数据结构,其核心逻辑与功能如下:
一、Document:原始数据容器
-
功能定义
- Document是承载任何数据源(如PDF、API响应、数据库查询结果)的基础单元,既支持手动创建,也可通过
SimpleDirectoryReader
等工具自动加载。 - 核心属性:
- 文本内容:存储原始数据的主体文本。
- 元数据(
metadata
):以字典形式存储附加信息(如文件名、分类标签),供后续索引和查询使用。 - ID标识(
doc_id
):唯一标识文档,支持增量更新索引。
- Document是承载任何数据源(如PDF、API响应、数据库查询结果)的基础单元,既支持手动创建,也可通过
-
创建方式
- 自动加载:通过数据连接器(如
SimpleDirectoryReader
)批量读取文件并生成Document集合。 - 手动创建:直接实例化
Document
对象,并自定义文本和元数据。
- 自动加载:通过数据连接器(如
二、Node:语义分块单元
-
功能定义
- Node表示Document经过分块处理后的最小语义单元(如段落、句子),Node是Document经过分块处理后的最小语义单元,代表文本块、图像或其他模态数据块,是索引和检索的基本单位。
- 核心特性:
- 继承元数据:默认继承父Document的全局元数据(如文件名)。
- 局部信息:可添加块级元数据(如段落位置、关键词),增强检索精度。
-
生成方式
- 自动解析:使用
NodeParser
(如SimpleNodeParser
)按预设规则(如固定块大小、重叠窗口)分割Document。 - 手动定义:直接构造Node对象,实现定制化分块逻辑。
- 自动解析:使用
三、Document与Node的关联逻辑
-
数据流转
- Document作为原始数据入口,通过分块转换为Node集合,形成可索引的语义单元。例如,一篇PDF论文(Document)可拆解为多个段落(Node)。
- 元数据继承:Node默认继承Document的全局元数据,同时支持添加局部元数据(如
section_title
)。
-
索引与检索
- 索引阶段:Node经向量化嵌入后存入数据库(如Chroma、Faiss),其元数据用于辅助相似性计算。
- 查询阶段:检索器召回相关Node,LLM结合其内容和元数据生成回答。
-
动态更新
- 通过
doc_id
追踪Document变更,仅更新关联的Node索引,避免全量重建。
- 通过
- 优化技巧
- 分块策略:调整块大小和重叠窗口平衡语义完整性与检索粒度。
- 元数据过滤:在查询时通过
metadata_filters
限定范围(如仅搜索特定类别文档)。
—### 总结
Document与Node的设计体现了LlamaIndex对数据处理与检索效率的平衡:
- Document作为原始数据的入口,统一管理多源异构数据;
- Node通过分块和元数据继承实现高效语义检索。
四、 名词解释
- AGI:
通用人工智能(Artificial General Intelligence),是指具有高效的学习和泛化能力、能够根据所处的复杂动态环境自主产生并完成任务的通用人工智能体,具备自主的感知、认知、决策、学习、执行和社会协作等能力,且符合人类情感、伦理与道德观念 [1]。其研究发展通常涉及众多学科领域,包括计算机科学、认知科学、心理学、神经科学等.(这个是AI的终极目标)
- Hugging Face( https://huggingface.co/):
Hugging Face Hub 允许用户上传、分享、管理 AI 模型、数据集和代码。你可以 Fork 其他人的模型,就像在 GitHub 上 Fork 代码一样。总结:Hugging Face 作为 AI 领域的 GitHub/Docker Hub
- 神经网络
- 前向传播
- RGA
RAG(Retrieval-Augmented Generation)是一种结合信息检索与生成式语言模型的方法,旨在提升知识问答的精准性和有效性。最简单的理解,可以认为是给大模型外挂了一个知识库。 - ollama
Ollama 是一个开源的人工智能平台,旨在为开发者提供更便捷的方式来构建和部署基于大型语言模型(LLMs,Large Language Models)的应用。它的目标是提供一种高效、简洁且低成本的方式来运行和集成语言模型,允许用户在本地环境中高效地使用 AI总结;像 运行 Docker 容器,但 Ollama 聚焦于 AI 模型的运行,而不涉及其他复杂的部署步骤
- Agent(智能体):最重要的思考模型,一堆流程、工具和api,具有意图识别(词槽 还有反问用户生成模型时确实的东西 如点外卖 会追问你喜好和价格)、意图转移等功能
总结:如查询今天订单总价格 他就知道去调用哪个api了
- 词向量
词向量(Word Embedding)是一种将词语映射到高维向量空间的技术。通过这种方式,每个词都被表示为一个固定长度的向量(通常是几十到几百维)。这些向量能够捕捉词与词之间的语义关系,使得相似意义的词在向量空间中彼此靠得更近。例如,“王”和“皇帝”在词向量空间中会比“王”和“狗”更接近。 - 向量库
向量库(Vector Database):向量库是用于存储和管理高维向量的数据库。由于现代AI任务中,经常需要处理大量的向量数据,特别是词向量、句向量、图像特征向量等,传统的关系型数据库并不适合存储和高效地查询这些数据。因此,向量数据库被开发出来,以便对这些高维数据进行快速的插入、存储、检索和相似度计算 - 余弦相似度
余弦相似度用来衡量两个词向量之间的相似度,计算的是它们的角度大小。对于两个词语,如果它们的词向量之间的夹角较小(即余弦相似度接近1),那么这两个词语的语义较为相近。比如,“猫”和“狗”的词向量之间的余弦相似度通常较高,因为它们都是常见的宠物。
词向量、 向量库与余弦相似度之间得关系
举个实际例子,假设我们有一个搜索引擎,当用户输入一个查询词时:
这个查询词首先被转换成词向量(比如 “猫” → 词向量A)。
然后,在向量库中检索所有词的向量,并计算它们与词向量A的余弦相似度。
最后,返回那些与查询词“猫”最相似的词(比如“狗”,“宠物”)。
所以,这三者结合在一起,构成了一个有效的文本检索、推荐或理解的基础。
-
微调(fine-tune)
1.全量微调:一般不做,容易让模型变得更傻
2.高效微调:具体来说,LORA微调并不直接调整原始模型的所有参数,而是通过在某些层中插入低秩的适配器(Adapter)层来进行训练。
LORA(Low-Rank Adaptation)微调是一种参数高效的微调方法,旨在通过引入低秩矩阵来减少微调时需要调整的参数数量,从而显著降低显存和计算资源的消耗。
高效微调主要用于以下四个方面:
2.1 对话风格微调:高效微调可以用于根据特定需求调整模型的对话风格。例如,针对客服系统、虚拟助理等场景,模型可以通过微调来适应不同的语气、礼貌程度或回答方式,从而在与用户互动时提供更符合要求的对话体验。通过微调少量的参数(例如对话生成的策略情感表达等),可以使模型表现出更具针对性和个性化的风格。
2.2 知识灌注:知识灌注是指将外部知识或领域特定的信息快速集成到已有的预训练模型中。通过高效微调,模型可以更好地学习新领域的专有知识,而无需重新从头开始训练。例如,对于法律、医疗等专业领域,可以使用少量的标注数据对预训练模型进行微调,帮助模型理解特定行业的术语、规则和知识,进而提升专业领域的问答能力。
2.3 推理能力提升:高效微调还可以用于提升大模型的推理能力,尤其是在处理更复杂推理任务时。通过微调,模型能够更加高效地理解长文本、推理隐含信息,或者从数据中提取逻辑关系,进而在多轮推理任务中提供更准确的答案。这种微调方式可以帮助模型在解答复杂问题时,提高推理准确性并减少错误。
2.4 Agent能力(Functioncalling能力)提升: 在多任务协作或功能调用场景中,高效微调能够显著提升模型的Agent能力,使得模型能够有效地与其他系统进行交互、调用外部API或执行特定任务。通过针对性微调,模型可以学会更精准的功能调用策略、参数解析和操作指令,从而在自动化服务、智能助手或机器人控制等领域表现得更加高效和智能。 -
COT数据集
CoT 数据集(Chain-of-Thought Dataset)通常指的是与 Chain-of-Thought (CoT) 方法相关的数据集,CoT 是一种用于改进大型语言模型推理能力的技术。在CoT方法中,模型在推理过程中不仅给出最终答案,还会生成中间推理步骤或思路,从而提升模型在复杂任务中的表现。CoT 数据集则是通过模拟这种推理过程,生成带有详细推理链的数据。
unsolth
- 多路召回()
- jieba分词:大佬牛逼已投币收藏,之前做项目时一直疑惑bm25为啥效果这么差,原来是没有用jieba分词评论区现身说法
- bm25:BM25(Best Match 25) 是一种基于统计学的信息检索算法,用于衡量查询(Query)与文档(Document)的相关性。其核心思想是:(我看有项目使用要注意使用)
- 向量召回 文本召回
- 使用rrf进行重排序 两种召回方式后进行重排序 得到更加精准的结果