在传统的意义上,RAG 主要是从文档中检索用户想要的数据,从而提高大模型的能力,减少幻觉问题。今天,我们从另一个维度介绍RAG,RAG不从文档中获取数据,而是从MySQL数据库检索数据。我们可以使用LangChain SQL Agent结合聊天历史信息构建一个多层RAG聊天机器人。
一、架构
整体架构,如上图所示。主要层将使用带有基本链的聊天记录来生成一个新的和改进的查询,然后将其传递给第二层。在这里,我们使用 SQL Agent直接在 MySQL 数据库上运行查询并获取所需的数据。最后,这个检索到的上下文与提示一起传递到 LLM , SQL Agent可以查看您的聊天记录。
主要层的功能允许我们将聊天记录用作上下文,这样当数据传递给 LLM时,就带有与我们的查询相关的背景知识。我们还在初始提示中要求改进查询的整体情况,以便更轻松地为 SQL Agent进行选择。
self.chat_llm = ChatOpenAI(
openai_api_key=settings.get("langchain.openai_api_key"),
model= llm_model_name,
temperature=0.2,
verbose=True,
model_kwargs={"response_format": {"type": "json_object"}},
)
self.memory = ConversationBufferMemory(
memory_key = "chat_history",
input_key = "question",
return_messages = True
)
template = """
You are a query improvement bot that will use the chat_history provided to improved the user's query.
Return your response in the following JSON format:
{{
"question": "Your response"
}}
If the user's query lacks context, you will use the chat_history to build it.
If the chat_history is not relevant to the question mentioned, forward the same question forward
Question: {question}
Chat History: {chat_history}
Context: {context}
"""
self.prompt = PromptTemplate(
template = template,
input_variables = ["question", "chat_history", "context"]
)
在上面的代码块中,使用 LangChain 的 ChatOpenAI 函数启动我们的LLM代码块。之后,使用 ConversationBufferMemory 实例化内存。最后,使用 PromptTemplate 设置提示,该提示适配于我们的用例。
def get_improved_query(
self,
query,
chat_history: list[SessionMessageBase]):
self.chain = load_qa_chain(
llm = self.chat_llm,
chain_type = "stuff",
memory = self.memory,
prompt = self.prompt,
verbose = True
)
self.memory.clear() print("chat_history", chat_history)
for message in chat_history:
self.memory.chat_memory.add_user_message(message.query)
self.memory.chat_memory.add_ai_message(str(message.response))
response = self.chain.run(
input_documents=[],
question=query
)
print("response", response)
return response
在下一段代码中,将使用上一个代码块中定义的变量创建 QA 链,将消息添加到聊天记录中,然后运行链以获取改进的查询。请注意,input_documents是故意留空的。
二、SQL Agent
在第二层,SQL Agent首先获取到用户的问题,然后要求 LLM 根据用户的问题创建 SQL 查询,使用内置函数在MySQL数据库上运行查询。最后,将来自数据库的响应数据与原始问题再次发送给LLM。这是一种新型的 RAG 检索器,可以轻松连接到您的数据库,从而在您的数据和聊天机器人之间轻松无缝地连接。
self.db = SQLDatabase.from_uri(database_url)
self.chat_llm = ChatOpenAI(
openai_api_key="OpenAI-Key",
model= llm_model_name,
temperature=0,
verbose=True,
model_kwargs={"response_format": {"type": "json_object"}},
)
在这里,我们启动了多个变量,从使用 SQLDatabase.from_uri() 开始,它接受数据库 URL。接下来,我们像以前实例化LLM一样,但这次我们添加了一个名为 model_kwargs 的特殊参数,并将响应格式设置为 JSON,以便我们更容易获取数据并解析它。
self.system = """You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct MySQL query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 3 results.
You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for the relevant columns given the question.
You have access to tools for interacting with the database.
Only use the given tools. Only use the information returned by the tools to construct your final answer.
Only use the results of the given SQL query to generate your final answer and return that.
You MUST double check your query before executing it. If you get an error while executing a query then you should stop!
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.
Only create an SQL statement ONCE!"""
self.prompt = ChatPromptTemplate.from_messages(
[("system", self.system), ("human", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad")]
)
SQL Agent的提示略有不同。在这里,我们使用的是 ChatPromptTemplate,如果你真的研究它,你会看到它是如何专门编写的,用于创建和运行 SQL 查询。
def create_sql_agent(self):
return create_sql_agent(
llm=self.chat_llm,
db=self.db,
prompt=self.prompt,
agent_type="openai-tools",
verbose=True,
)
create_sql_agent函数创建了SQL 代理,它包含了LLM、数据库、提示,agent_type必须是“openai-tools”。
improved_query = json.loads(BaseAgent.get_improved_query(query, chat_history))
response = self.agent.invoke({"input": improved_query.get("question")})
这是代码的最后一部分,它将首先运行第一层以获取改进的查询,然后我们使用该改进的查询来获得最终响应。
三、总结
这个解决方案的关键要点是,它是根据我的用例DIY 构建的。我相信LangChain社区迟早会找到一个解决方案,他们可以满足与SQL代理的聊天记录。就目前而言,如果您希望将聊天记录与您自己的聊天机器人合并,可以直接查询到您的数据库中,这对您非常有帮助。