基于Streamlit+LangChain+ChromaDB构建文档ChatBot遇ID空列表错误
文档问答ChatBot运行错误排查与解决
问题现象
本地运行基于Streamlit、LangChain、ChromaDB和OpenAI构建的文档问答ChatBot时,触发以下错误:
ValueError: Expected IDs to be a non-empty list, got [] Traceback: File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 535, in _run_script exec(code, module.__dict__) File "C:\Users\FCI\Desktop\Happy Sales AI\OpenAI RAG Chatbot\2nd RAG project\main.py", line 13, in <module> from utils import * File "C:\Users\FCI\Desktop\Happy Sales AI\OpenAI RAG Chatbot\2nd RAG project\utils.py", line 43, in <module> vectordb = Chroma.from_documents( File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\langchain_community\vectorstores\chroma.py", line 778, in from_documents return cls.from_texts( File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\langchain_community\vectorstores\chroma.py", line 736, in from_texts chroma_collection.add_texts( File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\langchain_community\vectorstores\chroma.py", line 324, in add_texts self._collection.upsert( File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\chromadb\api\models\Collection.py", line 477, in upsert ) = self._validate_embedding_set( File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\chromadb\api\models\Collection.py", line 545, in _validate_embedding_set valid_ids = validate_ids(maybe_cast_one_to_many_ids(ids)) File "C:\Users\FCI\AppData\Local\Programs\Python\Python310\lib\site-packages\chromadb\api\types.py", line 213, in validate_ids raise ValueError(f"Expected IDs to be a non-empty list, got {ids}")
项目代码
main.py
from langchain_openai import ChatOpenAI from langchain.chains import ConversationChain from langchain.chains.conversation.memory import ConversationBufferWindowMemory from langchain.prompts import ( SystemMessagePromptTemplate, HumanMessagePromptTemplate, ChatPromptTemplate, MessagesPlaceholder ) import streamlit as st from streamlit_chat import message from utils import * st.subheader("Happy Sales Streamlit") if 'responses' not in st.session_state: st.session_state['responses'] = ["How can I assist you?"] if 'requests' not in st.session_state: st.session_state['requests'] = [] llm = ChatOpenAI(model_name="gpt-3.5-turbo", openai_api_key="---------") if 'buffer_memory' not in st.session_state: st.session_state['buffer_memory'] = ConversationBufferWindowMemory(k=3, return_messages=True) system_msg_template = SystemMessagePromptTemplate.from_template(template="""Answer the question as truthfully as possible using the provided context, and if the answer is not contained within the text below, say 'I don't know'""") human_msg_template = HumanMessagePromptTemplate.from_template(template="{input}") prompt_template = ChatPromptTemplate.from_messages([system_msg_template, MessagesPlaceholder(variable_name="history"), human_msg_template]) conversation = ConversationChain(memory=st.session_state.buffer_memory, prompt=prompt_template, llm=llm, verbose=True) # container for chat history response_container = st.container() # container for text box textcontainer = st.container() with textcontainer: query = st.text_input("Query: ", key="input") if query: with st.spinner("typing..."): conversation_string = get_conversation_string() refined_query = query_refiner(conversation_string, query) st.subheader("Refined Query:") st.write(refined_query) context = find_match(refined_query) response = conversation.predict(input=f"Context:\n {context} \n\n Query:\n{query}") st.session_state.requests.append(query) st.session_state.responses.append(response) with response_container: if st.session_state['responses']: for i in range(len(st.session_state['responses'])): message(st.session_state['responses'][i],key=str(i)) if i < len(st.session_state['requests']): message(st.session_state["requests"][i], is_user=True,key=str(i)+ '_user')
utils.py
from sentence_transformers import SentenceTransformer import openai import streamlit as st openai.api_key = "-------------------------" model = SentenceTransformer('all-MiniLM-L6-v2') from langchain_community.document_loaders import PyPDFDirectoryLoader directory = 'Docs' def load_docs(directory): loader = PyPDFDirectoryLoader(directory) documents = loader.load() return documents documents = load_docs(directory) print(len(documents)) from langchain.text_splitter import RecursiveCharacterTextSplitter def split_docs(documents,chunk_size=2000,chunk_overlap=20): text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap) docs = text_splitter.split_documents(documents) return docs docs = split_docs(documents) print(len(docs)) from langchain_community.embeddings import SentenceTransformerEmbeddings embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2") from langchain_community.vectorstores import Chroma import chromadb persist_directory = "chroma_db" vectordb = Chroma.from_documents( documents=docs, embedding=embeddings, persist_directory=persist_directory ) vectordb.persist() def find_match(input): input_em = model.encode(input).tolist() result = vectordb.query(input_em, n_results=2) #includeMetadata=True return result['matches'][0]['metadata']['text']+"\n"+result['matches'][1]['metadata']['text'] def query_refiner(conversation, query): response = openai.chat.completions.create( model="gpt-3.5-turbo", prompt=f"Given the following user query and conversation log, formulate a question that would be the most relevant to provide the user with an answer from a knowledge base.\n\nCONVERSATION LOG: \n{conversation}\n\nQuery: {query}\n\nRefined Query:", temperature=0.7, max_tokens=256, top_p=1, frequency_penalty=0, presence_penalty=0 ) return response['choices'][0]['text'] def get_conversation_string(): conversation_string = "" for i in range(len(st.session_state['responses'])-1): conversation_string += "Human: "+st.session_state['requests'][i] + "\n" conversation_string += "Bot: "+ st.session_state['responses'][i+1] + "\n" return conversation_string
错误原因与解决步骤
核心原因
错误触发的直接原因是Chroma.from_documents方法传入的docs为空列表,导致Chroma无法生成有效ID列表。这通常是因为PDF文档加载失败或分割后未得到有效文本块。
具体修复步骤
验证文档加载有效性
在utils.py中加载文档后添加校验,确保能读取到PDF内容:documents = load_docs(directory) if not documents: raise ValueError("Docs目录中未加载到任何PDF文档,请检查目录路径是否正确、文件夹内是否有可读取的PDF文件") print(len(documents))同时确认
Docs目录与utils.py在同一层级,且目录内的PDF文件未损坏、能正常打开。校验文档分割结果
文档分割后同样添加空值校验,避免传入空列表给Chroma:docs = split_docs(documents) if not docs: raise ValueError("文档分割后未得到有效文本块,请检查PDF内容是否为空,或调整分割参数(如缩小chunk_size)") print(len(docs))修复向量库创建逻辑
避免每次启动服务都重复创建向量库,改为先检查已有向量库是否有数据,无数据再重新生成:# 替换原vectordb创建代码 from chromadb.config import Settings persist_directory = "chroma_db" try: # 尝试加载已有向量库 vectordb = Chroma(persist_directory=persist_directory, embedding_function=embeddings) if vectordb._collection.count() == 0: # 向量库为空则重新创建 vectordb = Chroma.from_documents(documents=docs, embedding=embeddings, persist_directory=persist_directory) vectordb.persist() except Exception as e: # 向量库不存在则创建新的 vectordb = Chroma.from_documents(documents=docs, embedding=embeddings, persist_directory=persist_directory) vectordb.persist()修复OpenAI API调用格式
最新版OpenAI SDK中,chat.completions.create的返回结构和参数格式有变化,修改query_refiner函数:def query_refiner(conversation, query): response = openai.chat.completions.create( model="gpt-3.5-turbo", messages=[ {"role": "user", "content": f"Given the following user query and conversation log, formulate a question that would be the most relevant to provide the user with an answer from a knowledge base.\n\nCONVERSATION LOG: \n{conversation}\n\nQuery: {query}\n\nRefined Query:"} ], temperature=0.7, max_tokens=256, top_p=1, frequency_penalty=0, presence_penalty=0 ) return response.choices[0].message.content.strip()修复向量库查询方法
LangChain的Chroma向量库查询方法使用错误,替换为正确的similarity_search_by_vector:def find_match(input): input_em = model.encode(input).tolist() docs = vectordb.similarity_search_by_vector(input_em, k=2) return docs[0].page_content + "\n" + docs[1].page_content
内容的提问来源于stack exchange,提问作者goldithehero
相关产品推荐
相关产品推荐

