教程:https://www.bilibili.com/video/BV1yjz5BLEoY
代码:https://github.com/shangxiang0907/HeiMa-AI-LLM-RAG-Agent-Dev
文章目录
- RAG项目-04、RAG项目-知识库更新服务
-
- 代码截图
- [→提示词:帮我根据图中内容,完善目录@rag-clothing-customer-service 中代码,图中内容仅供参考,你要适当修改](#→提示词:帮我根据图中内容,完善目录@rag-clothing-customer-service 中代码,图中内容仅供参考,你要适当修改)
- ←AI回复
-
- [1. **完善 `config_data.py`**](#1. 完善
config_data.py) - [2. **完善 `knowledge_base.py`**](#2. 完善
knowledge_base.py) - [3. **更新 `app_file_uploader.py`**](#3. 更新
app_file_uploader.py)
- [1. **完善 `config_data.py`**](#1. 完善
- 代码
- [→问AI: "根据配置的最大字符数决定是否分割文本"这段代码是否多余了?](#→问AI: “根据配置的最大字符数决定是否分割文本”这段代码是否多余了?)
- ←AI回复
- [→问AI:@rag-clothing-customer-service/knowledge_base.py:22-27 @bash (6-104) 是不是没帮我加载环境变量,参照这个目录里文件弄下@AI_LLM_RAG_Agent_Dev](#→问AI:@rag-clothing-customer-service/knowledge_base.py:22-27 @bash (6-104) 是不是没帮我加载环境变量,参照这个目录里文件弄下@AI_LLM_RAG_Agent_Dev)
- ←AI回复
- 总结
RAG项目-04、RAG项目-知识库更新服务
代码截图




→提示词:帮我根据图中内容,完善目录@rag-clothing-customer-service 中代码,图中内容仅供参考,你要适当修改




←AI回复
查看现有代码,根据图片内容完善代码。
4 tools called
搜索代码库中的相关依赖和导入信息:
2 tools called
根据图片内容完善代码。先完善 config_data.py,再完善 knowledge_base.py。
2 tools called
完善 knowledge_base.py:
4 tools called
更新 app_file_uploader.py,集成 KnowledgeBaseService:
3 tools called
检查代码是否有错误:
2 tools called
完成所有任务。查看最终文件:
2 tools called
已根据图片内容完善代码。主要修改如下:
1. 完善 config_data.py
- 添加 ChromaDB 配置(
collection_name、persist_directory) - 添加文本分割器配置(
chunk_size、chunk_overlap、separators) - 添加
max_split_char_number配置项
2. 完善 knowledge_base.py
__init__方法 :- 自动创建持久化目录
- 初始化 Chroma 向量数据库(使用 DashScopeEmbeddings)
- 初始化 RecursiveCharacterTextSplitter 文本分割器
upload_by_str方法 :- 实现 MD5 校验,避免重复上传
- 根据配置的最大字符数决定是否分割文本
- 构建包含文件名、创建时间和操作员的元数据
- 将文本块上传到向量数据库
- 保存 MD5 值并返回成功消息
3. 更新 app_file_uploader.py
- 集成
KnowledgeBaseService - 添加文件内容预览功能
- 添加"上传到知识库"按钮和处理状态提示
代码已通过语法检查,无错误。现在可以通过 Streamlit 应用上传文本文件到向量知识库了。
代码
config_data.py
py
md5_path = "./md5.text"
# Chroma
collection_name = "rag"
persist_directory = "./chroma_db"
# splitter
chunk_size = 1000
chunk_overlap = 100
separators = ["\n\n", "\n", ".", "!", "?", ". ", "!", "?", " ", ""]
knowledge_base.py
py
"""
知识库服务类
用于管理文件上传、MD5校验和向量数据库存储
"""
import hashlib
import os
from datetime import datetime
from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
import config_data as config
class KnowledgeBaseService(object):
def __init__(self):
# 如果文件夹不存在则创建,如果存在则跳过
os.makedirs(config.persist_directory, exist_ok=True)
# 向量存储的实例 Chroma向量库对象
self.chroma = Chroma(
collection_name=config.collection_name, # 数据库的表名
embedding_function=DashScopeEmbeddings(model="text-embedding-v4"),
persist_directory=config.persist_directory, # 数据库本地存储文件夹
)
# 文本分割器的对象
self.spliter = RecursiveCharacterTextSplitter(
chunk_size=config.chunk_size, # 分割后的文本段最大长度
chunk_overlap=config.chunk_overlap, # 连续文本段之间的字符重叠数量
separators=config.separators, # 自然段落划分的符号
length_function=len, # 使用Python自带的len函数做长度统计的依据
)
def check_md5(self, md5_str):
"""检查传入的md5字符串是否已经被处理过了"""
if not os.path.exists(config.md5_path):
return False
with open(config.md5_path, 'r', encoding='utf-8') as f:
existing_md5s = f.read().splitlines()
return md5_str in existing_md5s
def save_md5(self, md5_str):
"""将传入的md5字符串,记录到文件内保存"""
# 确保目录存在
os.makedirs(os.path.dirname(config.md5_path) if os.path.dirname(config.md5_path) else '.', exist_ok=True)
with open(config.md5_path, 'a', encoding='utf-8') as f:
f.write(md5_str + '\n')
def get_string_md5(self, str_data):
"""将传入的字符串转换为md5字符串"""
md5_hash = hashlib.md5()
md5_hash.update(str_data.encode('utf-8'))
return md5_hash.hexdigest()
def upload_by_str(self, data: str, filename):
"""将传入的字符串,进行向量化,存入向量数据库中"""
# 先得到传入字符串的md5值
md5_hex = self.get_string_md5(data)
if self.check_md5(md5_hex):
return "[跳过]内容已经存在知识库中"
# 使用文本分割器分割文本(分割器会根据 chunk_size 自动决定是否分割)
knowledge_chunks: list[str] = self.spliter.split_text(data)
# 构建元数据
metadata = {
"source": filename,
"create_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"operator": "小曹",
}
# 内容就加载到向量库中了
# iterable -> list \ tuple
self.chroma.add_texts(
knowledge_chunks,
metadatas=[metadata for _ in knowledge_chunks],
)
# 保存MD5值
self.save_md5(md5_hex)
return "[成功]内容已经成功载入向量库"
app_file_uploader.py
py
"""
基于Streamlit完成WEB网页上传服务
pip install streamlit
"""
import streamlit as st
from knowledge_base import KnowledgeBaseService
# 添加网页标题
st.title("知识库更新服务")
# 初始化知识库服务
if 'kb_service' not in st.session_state:
st.session_state.kb_service = KnowledgeBaseService()
# file_uploader
uploader_file = st.file_uploader(
label="请上传TXT文件",
type=['txt'],
accept_multiple_files=False,
# False表示仅接受一个文件的上传
)
if uploader_file is not None:
# 提取文件的信息
file_name = uploader_file.name
file_type = uploader_file.type
file_size = uploader_file.size / 1024 # KB
st.subheader(f"文件名: {file_name}")
st.write(f"格式: {file_type} | 大小: {file_size:.2f} KB")
# get_value -> bytes -> decode('utf-8')
text = uploader_file.getvalue().decode("utf-8")
# 显示文件内容预览
with st.expander("文件内容预览"):
st.text(text[:1000] + "..." if len(text) > 1000 else text)
# 上传按钮
if st.button("上传到知识库"):
with st.spinner("正在处理文件..."):
result = st.session_state.kb_service.upload_by_str(text, file_name)
st.success(result)
→问AI: "根据配置的最大字符数决定是否分割文本"这段代码是否多余了?

←AI回复
分析这段代码的必要性:
2 tools called
分析这段代码的必要性:
1 tool called
分析这段代码的逻辑问题:
1 tool called
这段代码多余且存在逻辑问题。分析如下:
当前配置:
chunk_size = 1000(文本块最大长度)max_split_char_number = 10000(最大分割字符数)
问题:
RecursiveCharacterTextSplitter会自动处理:文本长度 ≤chunk_size时不分割,>chunk_size时按配置分割。- 当前逻辑:
len(data) > 10000才分割,否则不分割。这会导致 1000-10000 之间的文本不分割,产生过大的块,违背chunk_size的设计。
建议:移除这段判断,直接使用分割器,让它根据 chunk_size 自动决定是否分割。
1 tool called
检查是否还有其他地方使用 max_split_char_number:
2 tools called
已移除多余代码。max_split_char_number 配置项现在未被使用,可考虑删除。保留它也不影响功能。
→问AI:@rag-clothing-customer-service/knowledge_base.py:22-27 @bash (6-104) 是不是没帮我加载环境变量,参照这个目录里文件弄下@AI_LLM_RAG_Agent_Dev

←AI回复
检查相关文件,查看环境变量的加载方式。
3 tools called
查看一个具体示例,了解环境变量的加载方式:
2 tools called
在 knowledge_base.py 中添加环境变量加载逻辑,参照 AI_LLM_RAG_Agent_Dev 目录中的做法。
1 tool called
在 knowledge_base.py 中添加环境变量加载逻辑:
2 tools called
检查是否有 lint 错误:
2 tools called
已修复环境变量加载问题。修改如下:
- 导入
dotenv:添加from dotenv import load_dotenv - 在
__init__方法中加载环境变量 :- 调用
load_dotenv()加载.env文件 - 从环境变量读取
DASHSCOPE_API_KEY或API_KEY(兼容两种命名) - 如果未找到,抛出清晰的错误提示
- 将 API key 设置到
os.environ["DASHSCOPE_API_KEY"],供DashScopeEmbeddings使用
- 调用
修改后的代码与 AI_LLM_RAG_Agent_Dev 目录下的示例保持一致。现在 DashScopeEmbeddings 可以正确读取 API key。
请确保:
- 项目根目录或
rag-clothing-customer-service目录下有.env文件 .env文件中包含DASHSCOPE_API_KEY=你的密钥或API_KEY=你的密钥
重新运行应用应可正常工作。
总结
这段代码确实多余,原因:
-
RecursiveCharacterTextSplitter会根据chunk_size自动决定是否分割:- 文本 ≤ 1000 字符:不分割,返回单个块
- 文本 > 1000 字符:按
chunk_size和separators分割
-
原逻辑会导致 1000-10000 字符的文本不分割,产生过大的块,违背
chunk_size的设计。 -
直接使用分割器更简洁,逻辑由分割器统一处理。
代码已简化,现在直接使用分割器处理所有文本,它会根据 chunk_size 自动决定是否分割。