You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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文档加载失败或分割后未得到有效文本块。

具体修复步骤

  1. 验证文档加载有效性
    在utils.py中加载文档后添加校验,确保能读取到PDF内容:

    documents = load_docs(directory)
    if not documents:
        raise ValueError("Docs目录中未加载到任何PDF文档,请检查目录路径是否正确、文件夹内是否有可读取的PDF文件")
    print(len(documents))
    

    同时确认Docs目录与utils.py在同一层级,且目录内的PDF文件未损坏、能正常打开。

  2. 校验文档分割结果
    文档分割后同样添加空值校验,避免传入空列表给Chroma:

    docs = split_docs(documents)
    if not docs:
        raise ValueError("文档分割后未得到有效文本块,请检查PDF内容是否为空,或调整分割参数(如缩小chunk_size)")
    print(len(docs))
    
  3. 修复向量库创建逻辑
    避免每次启动服务都重复创建向量库,改为先检查已有向量库是否有数据,无数据再重新生成:

    # 替换原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()
    
  4. 修复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()
    
  5. 修复向量库查询方法
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:54:52