一、准备工作:环境与依赖配置
目标:搭建开发环境,配置敏感信息与核心依赖,为后续开发奠定基础。
1.1 安装核心依赖
RAG 应用需要三类核心工具:前端交互框架(Chainlit)、RAG 引擎(llama-index)、环境管理工具(python-dotenv)。在终端执行安装命令:
pip install chainlit
检测是否安装成功
chainlit hello
如果报错如下

这个问题最主要的是pydantic的版本问题,解决方案如下
①卸载pydantic
pip uninstall pydantic
②安装pydantic为2.9.2版本
pip install pydantic==2.9.2
③检测
chainlit hello
1.2 配置环境变量(.env文件)
敏感信息(如 API 密钥、认证密码)需通过环境变量管理,避免硬编码。创建.env文件,内容如下:
# .env
CHAINLIT_AUTH_SECRET=your_chainlit_secret # Chainlit认证密钥(可自定义)
KIMI_API_KEY=your_kimi_api_key # 月之暗面模型API密钥
DEEPSEEK_API_KEY=your_deepseek_api_key # DeepSeek模型API密钥(可选)
CHAINLIT_AUTH_SECRET用于 Chainlit 应用的访问认证,第三方模型 API 密钥则是调用 LLM 的凭证
二、模型配置:嵌入模型与 LLM 初始化
目标:配置文本嵌入模型(用于向量检索)和大语言模型(用于生成回答),是 RAG 的 “大脑”。
2.1 嵌入模型配置(embeddings.py)
嵌入模型将文本转为向量,是实现 “检索” 的核心。选择中文支持好的BAAI/bge-small-zh-v1.5模型:
# embeddings.py
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
def get_embed_model():
"""加载本地中文嵌入模型"""
# 模型会自动下载到本地(~/.cache/huggingface/hub)
embed_model = HuggingFaceEmbedding(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={"device": "cpu"}, # 本地运行用CPU,有GPU可改为"cuda"
embed_batch_size=10 # 批量处理文本的大小
)
return embed_model
# 全局实例化,避免重复加载
embed_model = get_embed_model()
①嵌入模型将文档和用户问题转为高维向量,向量相似度越高,文本语义越接近(支撑后续 “找相关文档” 的逻辑)。
②选择bge-small-zh是因为它轻量(适合本地部署)且中文效果好,比英文模型更适配中文知识库
2.2 LLM 配置(llms.py)
LLM 负责基于检索到的文档生成回答,这里适配第三方模型(以月之暗面kimi为例,兼容 OpenAI 接口):
# llms.py
from llama_index.llms.openai import OpenAI
from dotenv import load_dotenv
import os
load_dotenv() # 加载.env文件
def get_llm(model_name: str = "kimi"):
"""获取配置好的LLM实例"""
if model_name == "kimi":
return OpenAI(
model="kimi-k2-0711-preview", # 月之暗面模型名称
api_key=os.getenv("KIMI_API_KEY"),
api_base="https://api.moonshot.cn/v1" # 月之暗面API地址
)
elif model_name == "deepseek":
return OpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
api_base="https://api.deepseek.com/v1"
)
else:
raise ValueError(f"不支持的模型:{model_name}")
# 默认使用kimi模型
llm = get_llm("kimi")
①利用llama_index的OpenAI类适配第三方模型:只要模型提供兼容 OpenAI 的 API(如/v1/chat/completions接口),就能直接复用该类,无需重复开发调用逻辑
②通过model_name参数可快速切换模型,提升灵活性
三、RAG 核心逻辑:索引与聊天引擎(base_rag.py)
目标:实现 “文档→索引→检索→生成” 的核心链路,是 RAG 的 “心脏”。
3.1 文档索引创建(将文档转为可检索的向量)
索引是文档的 “向量化存储库”,创建后可反复复用:
# base_rag.py
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.storage.storage_context import StorageContext
from llama_index.core.persistence import PersistentDirectoryStore
from embeddings import embed_model
import os
INDEX_DIR = "./index" # 索引持久化目录
def create_index(documents=None):
"""
创建或加载文档索引
- 若documents不为空:基于新文档创建索引并保存
- 若documents为空:加载已保存的索引
"""
# 若指定了新文档,先读取文档
if documents:
reader = SimpleDirectoryReader(documents=documents) # 读取传入的文档
docs = reader.load_data()
else:
docs = None
# 配置存储上下文(指定索引保存路径)
storage_context = StorageContext.from_defaults(
persist_dir=INDEX_DIR,
docstore=PersistentDirectoryStore.from_persist_dir(persist_dir=INDEX_DIR)
)
# 若索引目录不存在或有新文档,创建新索引
if not os.path.exists(INDEX_DIR) or documents:
index = VectorStoreIndex.from_documents(
docs,
storage_context=storage_context,
embed_model=embed_model # 使用前面配置的嵌入模型
)
index.storage_context.persist(persist_dir=INDEX_DIR) # 保存索引
else:
# 加载已存在的索引
index = VectorStoreIndex.from_storage(storage_context=storage_context)
return index
①核心逻辑:VectorStoreIndex.from_documents会调用嵌入模型,将文档拆分为片段并转为向量,存储到INDEX_DIR
②增量更新:若用户上传新文件(documents不为空),会基于新文档更新索引,避免重复处理旧文档
3.2 聊天引擎初始化(结合索引、LLM 与对话记忆)
聊天引擎是 “检索 + 生成” 的执行者,需同时处理对话历史和文档检索:
# base_rag.py(续)
from llama_index.core.memory import ChatMemoryBuffer
from llama_index.core.chat_engine import ContextChatEngine
from llms import llm
def create_chat_engine(index):
"""基于索引创建带对话记忆的聊天引擎"""
# 配置对话记忆(保存最近1024 token的对话历史)
memory = ChatMemoryBuffer.from_defaults(token_limit=1024)
# 创建聊天引擎:CONTEXT模式会先检索相关文档,再结合记忆生成回答
chat_engine = ContextChatEngine.from_defaults(
index=index,
memory=memory,
llm=llm, # 使用前面配置的LLM
system_prompt="你是一个基于文档的问答助手,回答必须结合提供的文档内容,不编造信息。"
)
return chat_engine
①ContextChatEngine是 llama-index 专为 RAG 设计的引擎:收到问题后,会先从索引中检索相关文档片段(基于向量相似度),再将 “问题 + 文档片段 + 对话历史” 传给 LLM,确保回答基于文档
②ChatMemoryBuffer用于保存对话历史,避免用户重复提问时需要重新输入上下文(如 “上文提到的 XX 是什么”)
四、前端交互:基于 Chainlit 的用户界面(app_ui.py)
目标:实现用户可视化交互(聊天、文件上传),将 RAG 核心逻辑与用户操作连接起来。
4.1 应用初始化与认证
# app_ui.py
import chainlit as cl
from dotenv import load_dotenv
from base_rag import create_index, create_chat_engine
import os
# 加载环境变量
load_dotenv()
# 1. 认证控制:限制未授权访问
@cl.authorize_callback
def auth_callback(username: str, password: str):
# 简单示例:仅允许admin/admin登录(实际可对接数据库)
return username == "admin" and password == "admin"
# 2. 聊天启动时初始化
@cl.on_chat_start
async def on_chat_start():
# 向用户发送欢迎消息
await cl.Message(content="欢迎使用RAG聊天助手!请上传文档或直接提问~").send()
# 加载已有的索引(若存在)
index = create_index()
# 创建聊天引擎并保存到用户会话(多用户隔离)
chat_engine = create_chat_engine(index)
cl.user_session.set("chat_engine", chat_engine)
①@cl.authorize_callback:Chainlit 提供的认证机制,这里用简单的用户名密码校验,实际部署可替换为 OAuth 或数据库验证
②cl.user_session:用于存储用户专属的聊天引擎,确保多用户同时使用时,各自的知识库和对话历史互不干扰(例如 A 用户上传的文档不会被 B 用户检索到)
4.2 消息处理(文件上传与提问)
# app_ui.py(续)
@cl.on_message
async def on_message(message: cl.Message):
# 从会话中获取当前用户的聊天引擎
chat_engine = cl.user_session.get("chat_engine")
# 处理用户上传的文件(若有)
if message.elements:
documents = []
for element in message.elements:
# 只处理文件/图片(可扩展支持PDF、Markdown等)
if element.type in ["file", "image"]:
# 保存文件到临时路径
file_path = f"./temp/{element.name}"
os.makedirs("./temp", exist_ok=True)
with open(file_path, "wb") as f:
f.write(element.content)
documents.append(file_path)
if documents:
# 基于新文件更新索引
await cl.Message(content="正在处理上传的文档...").send()
index = create_index(documents=documents)
# 更新会话中的聊天引擎
chat_engine = create_chat_engine(index)
cl.user_session.set("chat_engine", chat_engine)
await cl.Message(content="文档处理完成,可以提问啦!").send()
return # 处理完文件后等待用户提问
# 处理用户提问(流式返回回答)
msg = cl.Message(content="") # 初始化空消息用于流式输出
await msg.send()
# 调用聊天引擎处理问题,流式获取结果
response = chat_engine.stream_chat(message.content)
for token in response.response_gen:
msg.content += token
await msg.update() # 实时更新消息内容(打字机效果)
①文件上传流程:用户上传文件后,先保存到本地临时目录,再调用create_index更新索引,最后用新索引重建聊天引擎 —— 确保后续提问能检索到新文档
②提问处理流程:通过stream_chat获取流式回答(逐 token 返回),再用msg.update()实时推送给用户,模拟 ChatGPT 的 “边生成边显示” 效果,提升体验
五、完整流程串联:从用户操作到回答生成
- 启动应用:运行
chainlit run app_ui.py,Chainlit 加载环境变量并启动前端界面。 - 用户登录:通过
admin/admin认证后进入聊天界面。 - 初始化:
on_chat_start加载已有索引,创建带记忆的聊天引擎,存入用户会话。 - 交互循环:
- 若用户上传文件:保存文件→更新索引→重建聊天引擎。
- 若用户提问:聊天引擎先检索索引找相关文档→结合对话历史→调用 LLM 生成回答→流式返回给用户。

795

被折叠的 条评论
为什么被折叠?



