import os
from qdrant_client import QdrantClient
from langchain.vectorstores import Qdrant
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import create_retrieval_chain
from langchain.schema import HumanMessage, SystemMessage, AIMessage
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain.callbacks import get_openai_callback
class ConversationChatManagement():
def _get_existing_vector_store(self, collection_identity):
qdrant_host = os.environ.get('QDRANT_HOST', 'qdrant-host-set')
new_client = QdrantClient(host=qdrant_host, port=6333)
embeddings=OpenAIEmbeddings()
return Qdrant(
client=new_client,
collection_name=collection_identity,
embeddings=embeddings,
)
def _create_prompt_template(self):
SYSTEM_TEMPLATE = """
Answer the user's questions based on the below context and message history.
If the context doesn't contain any relevant information to the question, don't make something up and just say "I don't know":
<context>
{context}
</context>
"""
messages = [
(
"system",
SYSTEM_TEMPLATE,
),
MessagesPlaceholder(variable_name="messages")
]
return ChatPromptTemplate.from_messages(messages)
def chat_conversation(self, message_content: str, collection_identity, target_conversation, llm_client: ChatOpenAI):
# Basically, a list of `HumanMessage, SystemMessage, AIMessage` objects.
# Representing the conversation history fetched from database..
history_messages = self._fetch_conversation_history(target_conversation)
prompt_template = self._create_prompt_template()
document_chain = create_stuff_documents_chain(llm_client, prompt_template)
def parse_retriever_input(params: Dict):
return params["messages"][-1].content
# Add vector store into document chain to become retrieval chain.
existing_vector_store = self._get_existing_vector_store(collection_identity)
retriever = existing_vector_store.as_retriever()
retrieval_chain = RunnablePassthrough.assign(
context=parse_retriever_input | retriever,
).assign(
answer=document_chain,
)
docs = retriever.invoke(message_content)
messages = history_messages + [HumanMessage(content=message_content)]
message_stream = retrieval_chain.stream({
"context": docs,
"messages": messages
})
代码主要参考自here
对于大型文档,它将返回有关最大上下文长度的错误:
openai.BadRequestError: Error code: 400 - {'error': {'message': "This model's maximum context length is 8192 tokens. However, your messages resulted in 8367 tokens. Please reduce the length of the messages.", 'type': 'invalid_request_error', 'param': 'messages', 'code': 'context_length_exceeded'}}
我正在使用
、langchain===0.1.9
和openai===1.5.0
。langchain-community===0.0.24
目前,升级或降级不是一个可行的选择,因为这可能会导致项目中出现太多重大更改,而这些更改可能无法及时解决。
ReduceDocumentsChain
我看到的最好的参考是
ReduceDocumentsChain
的示例(标题中的链接)。
但是,问题是示例代码使用了大量已弃用的、不起作用的代码,这些代码在我正在使用的版本中不起作用:
OpenAI
已在 0.0.10
中弃用。当我尝试使用我的版本的示例时,这会导致代码库损坏。LLMChain
已在 0.1.17
中弃用;尝试用 OpenAI
替换 ChatOpenAI
的输入对象会导致其损坏。诚然,即使这个示例适用于我的版本,我也对如何将消息历史记录附加到其中感到困惑......
ConversationalRetrievalChain
包含一个我认为我可以使用的
max_tokens_limit
。但它已被弃用,并且需要 question_generator: LLMChain
,这也已被弃用... (回想一下 LLMChain
不适用于 ChatOpenAI
)
正如文档所建议的,create_history_aware_retriever
与create_retrieval_chain
似乎没有提供任何类型的功能/参数来限制令牌长度。
诚然,我是
LangChain
API 的新手。显然就我而言,问题在于我如何将文档/文本插入 Qdrant 商店:
doc = [
Document(
page_content=file_contents # This was the ENTIRE document worth of content in string, not good lol
)
]
vector_ids = qdrant_collection.add_documents(doc)
我将整个文档直接插入到 Qdrant 矢量存储中,而没有将其拆分。
这导致检索文档搜索包含整个文档,这很容易超出令牌限制。
CharacterTextSplitter
来对文本进行分块来修复它,然后再将其存储到 Qdrant 矢量存储中。
def _get_chunked_text(text):
text_splitter = CharacterTextSplitter(
separator='\n',
chunk_size=1000,
chunk_overlap=150,
length_function=len
)
return text_splitter.split_text(text)
chunked_text = _get_chunked_text(file_contents)
vector_ids = qdrant_collection.add_texts(chunked_text)
虽然这个“解决方案”对我来说实际上并没有回答尝试限制上下文窗口上的令牌大小的原始问题。
因此我不会将我的解决方案标记为正确的解决方案,并保持这个问题开放......