如何在SQL问答中进行查询验证
或许SQL链或代理中最容易出错的部分就是编写有效且安全的SQL查询。在本指南中,我们将介绍一些策略来验证我们的查询并处理无效查询。本文内容包括:
- 添加“查询验证器”步骤到查询生成过程中;
- 通过提示工程来减少错误发生的概率。
引言
在SQL问答系统中,生成的SQL查询往往会因为各种原因导致错误,这不仅影响用户体验,还可能带来数据安全问题。为了提升系统的可靠性和安全性,我们需要对生成的SQL查询进行验证和处理。本文将介绍如何在SQL问答流程中添加查询验证步骤,并探讨如何通过提示工程来减少错误。
主要内容
环境设置
首先,获取所需的包并设置环境变量:
%pip install --upgrade --quiet langchain langchain-community langchain-openai
以下示例将使用带有Chinook数据库的SQLite连接。按照这些安装步骤创建Chinook.db文件,并将其放在与此notebook相同的目录中:
- 保存文件为
Chinook_Sqlite.sql - 运行
sqlite3 Chinook.db - 运行
.read Chinook_Sqlite.sql - 测试
SELECT * FROM Artist LIMIT 10;
现在Chinook.db已经在我们的目录中,我们可以使用SQLAlchemy驱动的SQLDatabase类来与之交互:
from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
print(db.dialect)
print(db.get_usable_table_names())
print(db.run("SELECT * FROM Artist LIMIT 10;"))
查询验证器
或许最简单的策略就是让模型自身检查原始查询中的常见错误。假设我们有如下SQL查询链:
from langchain.chains import create_sql_query_chain
from langchain_community.api_keys import get_key
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(api_key=get_key(api_name="openai"))
chain = create_sql_query_chain(llm, db)
我们希望验证它的输出。我们可以通过添加一个第二提示和模型调用来扩展链:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
system = """Double check the user's {dialect} query for common mistakes, including:
- Using NOT IN with NULL values
- Using UNION when UNION ALL should have been used
- Using BETWEEN for exclusive ranges
- Data type mismatch in predicates
- Properly quoting identifiers
- Using the correct number of arguments for functions
- Casting to the correct data type
- Using the proper columns for joins
If there are any of the above mistakes, rewrite the query.
If there are no mistakes, just reproduce the original query with no further commentary.
Output the final SQL query only."""
prompt = ChatPromptTemplate.from_messages(
[("system", system), ("human", "{query}")]
).partial(dialect=db.dialect)
validation_chain = prompt | llm | StrOutputParser()
full_chain = {"query": chain} | validation_chain
我们可以通过如下示例来测试这个完整的链:
query = full_chain.invoke(
{
"question": "What's the average Invoice from an American customer whose Fax is missing since 2003 but before 2010"
}
)
print(query)
# 使用API代理服务提高访问稳定性
db.run(query)
'[(6.632999999999998,)]'
显然,这种方法的缺点在于我们需要进行两次模型调用来生成查询。为了克服这个问题,我们可以尝试在一次模型调用中生成查询和检查查询:
system = """You are a {dialect} expert. Given an input question, create a syntactically correct {dialect} query to run.
...
First draft: <<FIRST_DRAFT_QUERY>>
Final answer: <<FINAL_ANSWER_QUERY>>"""
prompt = ChatPromptTemplate.from_messages(
[("system", system), ("human", "{input}")]
).partial(dialect=db.dialect)
def parse_final_answer(output: str) -> str:
return output.split("Final answer: ")[1]
chain = create_sql_query_chain(llm, db, prompt=prompt) | parse_final_answer
测试这个单步链:
query = chain.invoke(
{
"question": "What's the average Invoice from an American customer whose Fax is missing since 2003 but before 2010"
}
)
print(query)
# 使用API代理服务提高访问稳定性
db.run(query)
'[(6.632999999999998,)]'
人工审核
在某些情况下,我们的数据足够敏感,不希望在没有人工批准的情况下执行SQL查询。可以参考Tool use: Human-in-the-loop页面,了解如何在任何工具、链或代理中添加人工审核。
错误处理
模型在某些时候会犯错并生成无效的SQL查询,或者数据库会出现问题,或者模型API会宕机。我们需要为链和代理添加一些错误处理行为,以便在这些情况下优雅地失败,并可能自动恢复。可以参考Tool use: Error handling页面,了解有关工具错误处理的更多信息。
总结和进一步学习资源
通过本文的介绍,我们学习了如何在SQL问答系统中进行查询验证,并通过提示工程来减少错误的发生。希望这些策略能够帮助你构建更加安全和可靠的SQL问答系统。如果你想深入了解相关主题,以下资源可能会对你有所帮助:
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---