当前位置:   article > 正文

总结不同方案实现-LLM数据库查询-即Text2SQL

text2sql

文本到SQL模型的设计通常具有挑战性,因为它们需要在不同的数据库上工作,并考虑各种用户文本输入和数据库结构。由于文本到SQL任务的复杂性,对其性能的全面评估需要考虑多种场景。随着大规模语言模型越来越受到关注,它们已成为自然语言处理中的重要组成部分。随着预训练模型规模的增长,它们的使用也在逐渐发生变化。本文主要谈论大语言模型背景下,如何与数据库交互-即利用大模型生成sql,并提升sql的准确性和复杂性。

随着NLP的发展,

1 准备:使用的数据库

本文研究基于ClickHouse,当然可以选其他数据库进行连接。

  1. CH_HOST = 'http://localhost:8123' # default address
  2. def get_clickhouse_data(query, host = CH_HOST, connection_timeout = 1500):
  3. r = requests.post(host, params = {'query': query},
  4. timeout = connection_timeout)
  5. return r.text

这段代码主要设置了与数据库连接的函数get_clickhouse_data(query),用于与数据库交互。我们将把这个函数的结果传递给LLM。因此,获取任何输出 DB 返回都是可以的,无论它是否是错误。LLM就能妥善处理。

使用到的表结构如下:我们只需要几个表来表示基本电子商务产品的数据模型。我们将使用用户列表 ( ecommerce.users) 及其会话 ( ecommerce.sessions)。

会话 ( ecommerce.sessions)表如下:

用户列表 ( ecommerce.users) 如下

 现在基于已有的两个表,接下来将使用LLM与之交互。

2 现有技术

LLM 代理的核心思想是使用 LLM 作为推理引擎来定义要采取的操作集。在经典方法中,我们对一系列操作进行硬编码,但对于代理,我们为模型提供工具和任务,并让她决定如何实现它们。

事实上,这就是人类智能的运作方式:我们将内心的声音推理与面向任务的行动结合起来。假设您需要做饭。你将使用推理来定义一个计划(“客人将在 30 分钟内到来,我只有时间煮意大利面”),调整它(“本已经成为素食主义者,我应该为他点一些东西”)或决定委托哪些任务相当于外部工具(“意大利面没有了,我需要让我的伙伴去买”)。同时,你将通过行动来使用一些工具(向伙伴寻求帮助或使用搅拌机)或获取一些信息(在互联网上查找你需要煮意大利面多少分钟才​​能使其有嚼劲)。因此,对LLM使用类似的方法是合理的,因为它适用于人类(毫无疑问是 AGI)。

自 ReAct 以来,LLM 代理有很多不同的方法。它们的不同之处在于用于设置模型推理的提示、我们如何定义工具、输出格式、处理有关中间步骤的内存等。以下是一些流行的方法:

  • OpenAI functions
  • AutoGPT
  • BabyAGI
  • Plan-and-execute agent

在Text2SQL领域,本文将利用上述方法综合测试,对文本生成SQL进行实验和总结。

 

3 第一个版本-构建代理:主要使用函数

LLM代理的核心组成部分是:

  • 提示指导模型的推理。
  • 模型可以使用的工具。
  • 记忆—一种将先前迭代传递给模型的机制。

对于第一个版本的 LLM 代理,我们将使用 OpenAI 函数作为框架来构建代理。 

3.1 定义工具

让我们从定义机器人的工具开始。让我们想想我们的LLM数据分析师可能需要哪些信息才能回答问题:

  • 表列表 - 我们可以将其放在系统提示符中,以便模型对我们拥有的数据有一定的了解,并且不需要每次都为其执行工具
  • 表的列列表,以便模型可以理解数据模式
  • 表中列的顶部值,以便模型可以查找过滤器的值
  • SQL查询执行的结果才能得到实际的数据。

 为了在LangChain中定义工具,我们需要使用@tool装饰器来实现函数。我们将使用 Pydantic 指定每个函数的参数模式,以便模型知道要传递给函数的内容。

下面的代码定义了三个工具:execute_sqlget_table_columnsget_table_column_distr

  1. from langchain.agents import tool
  2. from pydantic import BaseModel, Field
  3. from typing import Optional
  4. class SQLQuery(BaseModel):
  5. query: str = Field(description="SQL query to execute")
  6. @tool(args_schema = SQLQuery)
  7. def execute_sql(query: str) -> str:
  8. """Returns the result of SQL query execution"""
  9. return get_clickhouse_data(query)
  10. # 主要功能:查询数据库表信息。不同数据库的系统表不一致,需要根据不同数据库进行设置。
  11. class SQLTable(BaseModel):
  12. database: str = Field(description="Database name")
  13. table: str = Field(description="Table name")
  14. @tool(args_schema = SQLTable)
  15. def get_table_columns(database: str, table: str) -> str:
  16. """Returns list of table column names and types in JSON"""
  17. q = '''
  18. select name, type
  19. from system.columns
  20. where database = '{database}'
  21. and table = '{table}'
  22. format TabSeparatedWithNames
  23. '''.format(database = database, table = table)
  24. return str(get_clickhouse_df(q).to_dict('records'))
  25. # 主要功能:查询表结构:列信息。
  26. class SQLTableColumn(BaseModel):
  27. database: str = Field(description="Database name")
  28. table: str = Field(description="Table name")
  29. column: str = Field(description="Column name")
  30. n: Optional[int] = Field(description="Number of rows, default limit 10")
  31. @tool(args_schema = SQLTableColumn)
  32. def get_table_column_distr(database: str, table: str, column: str, n:int = 10) -> str:
  33. """Returns top n values for the column in JSON"""
  34. q = '''
  35. select {column}, count(1) as count
  36. from {database}.{table}
  37. group by 1
  38. order by 2 desc
  39. limit {n}
  40. format TabSeparatedWithNames
  41. '''.format(database = database, table = table, column = column, n = n)
  42. return str(list(get_clickhouse_df(q)[column].values))

我们将使用 OpenAI 函数并需要转换我们的工具。另外,我将我们的工具包保存在字典中。执行工具来获取观察结果时会很方便。

  1. from langchain.tools.render import format_tool_to_openai_function
  2. # converting tools into OpenAI functions
  3. sql_functions = list(map(format_tool_to_openai_function,
  4. [execute_sql, get_table_columns, get_table_column_distr]))
  5. # saving tools into a dictionary for the future
  6. sql_tools = {
  7. 'execute_sql': execute_sql,
  8. 'get_table_columns': get_table_columns,
  9. 'get_table_column_distr': get_table_column_distr
  10. }

 3.2 定义Chain

我们已经为该模型创建了工具。现在,我们需要定义Chain。我们将使用最新的 GPT 4 Turbo,它也经过了微调以与这些功能一起使用。让我们初始化一个聊天模型。

  1. from langchain.chat_models import ChatOpenAI
  2. llm = ChatOpenAI(temperature=0.1, model = 'gpt-4-1106-preview')\
  3. .bind(functions = sql_functions)

 下一步是定义由系统消息和用户问题组成的提示。我们还需要MessagesPlaceholder为模型将使用的观察列表设置一个位置。

  1. from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
  2. system_message = '''
  3. You are working as a product analyst for the e-commerce company.
  4. Your work is very important, since your product team makes decisions based on the data you provide. So, you are extremely accurate with the numbers you provided.
  5. If you're not sure about the details of the request, you don't provide the answer and ask follow-up questions to have a clear understanding.
  6. You are very helpful and try your best to answer the questions.
  7. All the data is stored in SQL Database. Here is the list of tables (in the format <database>.<table>) with descriptions:
  8. - ecommerce.users - information about the customers, one row - one customer
  9. - ecommerce.sessions - information about the sessions customers made on our web site, one row - one session
  10. '''
  11. analyst_prompt = ChatPromptTemplate.from_messages(
  12. [
  13. ("system", system_message),
  14. ("user", "{question}"),
  15. MessagesPlaceholder(variable_name="agent_scratchpad"),
  16. ]
  17. )

 

我们已将数据库中的表列表添加到提示中,以便模型至少对我们的数据库信息有一些了解。

我们已具备所有构建模块,并已准备好建立Chain。输入参数是用户消息和中间步骤(先前的消息、函数调用和观察)。将输入参数传递给提示符,用于format_to_openai_function_messages将它们转换为预期的格式。然后,我们将所有内容传递给 LLM,最后,使用输出解析器OpenAIFunctionsAgentOutputParser

以下是代码结构:

  1. from langchain.agents.format_scratchpad import format_to_openai_function_messages
  2. from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
  3. analyst_agent = (
  4. {
  5. "question": lambda x: x["question"],
  6. "agent_scratchpad": lambda x: format_to_openai_function_messages(x["intermediate_steps"]),
  7. }
  8. | analyst_prompt
  9. | llm
  10. | OpenAIFunctionsAgentOutputParser()
  11. )

3.3 开始执行

我们已经定义了我们的主要Chain。让我们尝试调用它。

  1. analyst_agent.invoke({"question": "How many active customers from the United Kingdom do we have?",
  2. "intermediate_steps": []})
  3. # AgentActionMessageLog(
  4. # tool='execute_sql',
  5. # tool_input={'query': "SELECT COUNT(DISTINCT user_id) AS active_customers_uk FROM ecommerce.sessions WHERE country = 'United Kingdom' AND active = TRUE"},
  6. # log='\nInvoking: `execute_sql` with `{\'query\': "SELECT COUNT(DISTINCT user_id) AS active_customers_uk FROM ecommerce.sessions WHERE country = \'United Kingdom\' AND active = TRUE"}`\n\n\n',
  7. # message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{"query":"SELECT COUNT(DISTINCT user_id) AS active_customers_uk FROM ecommerce.sessions WHERE country = \'United Kingdom\' AND active = TRUE"}', 'name': 'execute_sql'}})]
  8. # )

如果我们查看tool_input,我们可以看到模型想要执行以下查询

  1. SELECT COUNT(DISTINCT user_id) AS active_customers_uk
  2. FROM ecommerce.sessions
  3. WHERE country = 'United Kingdom' AND active = TRUE

该查询看起来不错,但使用了错误的列名:active而不是is_active. 看看 LLM 是否能够从这个错误中恢复并返回结果将会很有趣。

  • 我们可以手动一步步执行,但是自动化会更方便。
  • 如果AgentActionMessageLog返回对象,我们需要调用一个工具,将观察添加到agent_scratchpad,并再次调用该链。
  • 如果我们得到了AgentFinish对象,我们就可以终止执行,因为我们有了最终的答案。

 我还将在十次迭代后添加一个中断,以避免潜在的无限循环。代码如下:

  1. from langchain_core.agents import AgentFinish
  2. # setting initial parameters
  3. question = "How many active customers from the United Kingdom do we have?"
  4. intermediate_steps = []
  5. num_iters = 0
  6. while True:
  7. # breaking if there were more than 10 iterations
  8. if num_iters >= 10:
  9. break
  10. # invoking the agent chain
  11. output = analyst_agent.invoke(
  12. {
  13. "question": question,
  14. "intermediate_steps": intermediate_steps,
  15. }
  16. )
  17. num_iters += 1
  18. # returning the final result if we got the AgentFinish object
  19. if isinstance(output, AgentFinish):
  20. model_output = output.return_values["output"]
  21. break
  22. # calling tool and adding observation to the scratchpad otherwise
  23. else:
  24. print(f'Executing tool: {output.tool}, arguments: {output.tool_input}')
  25. observation = sql_tools[output.tool](output.tool_input)
  26. print(f'Observation: {observation}')
  27. print()
  28. intermediate_steps.append((output, observation))
  29. print('Model output:', model_output)

我在输出中添加了一些工具使用情况的日志记录,以查看执行情况。此外,您始终可以使用 LangChain 调试模式来查看所有调用。

执行的结果是,我们得到了以下输出。

  1. Executing tool: execute_sql, arguments: {'query': "SELECT COUNT(*) AS active_customers_uk FROM ecommerce.users WHERE country = 'United Kingdom' AND active = TRUE"}
  2. Observation: Code: 47. DB::Exception: Missing columns: 'active'
  3. while processing query: 'SELECT count() AS active_customers_uk
  4. FROM ecommerce.users WHERE (country = 'United Kingdom') AND (active = true)',
  5. required columns: 'country' 'active', maybe you meant: 'country'.
  6. (UNKNOWN_IDENTIFIER) (version 23.12.1.414 (official build))
  7. Executing tool: get_table_columns, arguments: {'database': 'ecommerce', 'table': 'users'}
  8. Observation: [{'name': 'user_id', 'type': 'UInt64'}, {'name': 'country', 'type': 'String'},
  9. {'name': 'is_active', 'type': 'UInt8'}, {'name': 'age', 'type': 'UInt64'}]
  10. Executing tool: execute_sql, arguments: {'query': "SELECT COUNT(*) AS active_customers_uk FROM ecommerce.users WHERE country = 'United Kingdom' AND is_active = 1"}
  11. Observation: 111469
  12. Model output: We have 111,469 active customers from the United Kingdom.

因此,模型尝试执行 SQL,但收到错误:没有列active。然后,它决定查看表模式,相应地更正查询,并得到结果。

这是一个相当不错的表现。我自己也有同样的行为方式。我通常首先尝试回忆或猜测列名,只有在第一次尝试失败时才检查文档。

第二个版本-研究ReAct 代理

我们可以利用模块化框架的优势:我们只需要更改一个参数agent = AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION

代码如下所示:

  1. system_message = system_message + '''\n您可以访问以下工具:'''
  2. agent_kwargs = {
  3. "prefix" : system_message
  4. }
  5. Analyst_agent_react = initialize_agent(
  6. llm = ChatOpenAI(温度= 0.1 , model = 'gpt-4-1106-preview ' )、
  7. agent = AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION、
  8. tools = [execute_sql、get_table_columns、get_table_column_distr]、
  9. agent_kwargs = agent_kwargs、
  10. verbose = True
  11. max_iterations = 10
  12. early_stopping_method = 'generate'
  13. )
  14. Analyst_agent_react.run( "我们有多少来自英国的活跃客户?" )

尽管模型遵循不同的路径(从理解表模式开始,然后执行 SQL),但它得到了相同的结果。

 

5 第三个版本:计划与执行代理

此方案灵感来自 BabyAGI 框架和论文“Plan-and-Solve Prompting”,该代理遵循“计划和执行”方法。这种方法的特点是代理首先尝试计划接下来的步骤,然后执行它们。

该方法有两个组成部分:

  • Planner——一个常规的大型语言模型,其主要目标——只是为了推理和计划
  • 执行者——行动代理,一个LLM,拥有一套可用于行动的工具。

 这种方法的优点是可以进行分离:一个模型专注于计划(推理),而另一个模型专注于执行(行动)。它更加模块化,并且有可能您可以使用针对您的特定任务进行微调的更小、更便宜的模型。然而,这种方法也会生成更多的 LLM 调用,因此如果我们使用 ChatGPT,成本会更高。

代码如下:

  1. Analyst_agent_plan_and_execute = PlanAndExecute(
  2. planner=planner,
  3. executor=executor
  4. )
  5. Analyst_agent_plan_and_execute.run( "我们有多少来自英国的活跃客户?" )from langchain_experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner
  6. model = ChatOpenAI(temperature=0.1, model = 'gpt-4-1106-preview')
  7. planner = load_chat_planner(model)
  8. executor = load_agent_executor(model,
  9. tools = [execute_sql, get_table_columns, get_table_column_distr],
  10. verbose=True)
  11. Analyst_agent_plan_and_execute = PlanAndExecute(
  12. planner=planner,
  13. executor=executor
  14. )
  15. Analyst_agent_plan_and_execute.run( "我们有多少来自英国的活跃客户?" )

调用返回错误:RateLimitError: Error code: 429 — {'error': {'message': 'Request too large for gpt-4–1106-preview in organization on tokens_usage_based per min: Limit 150000, Requested 235832.', 'type': 'tokens_usage_based', 'param': None, 'code': 'rate_limit_exceeded'}}

这是一个很好的例子,说明了代理将问题过于复杂化并涉及太多细节的情况。它查询了表中所有数据进行分析。

6 第四个版本:BabyAGI 代理与工具

这种方法使用检索,因此我们需要建立向量存储和嵌入模型。我使用开源轻量级Chroma进行存储和 OpenAI 嵌入。

  1. from langchain.embeddings.openai import OpenAIEmbeddings
  2. from langchain.vectorstores import Chroma
  3. embedding = OpenAIEmbeddings()
  4. persist_directory = 'vector_store'
  5. vectordb = Chroma(
  6. persist_directory=persist_directory,
  7. embedding_function=embedding
  8. )

首先,我们将创建一个 TO-DO 链,稍后将其用作执行器的工具。

  1. from langchain.chains import LLMChain
  2. from langchain.prompts import PromptTemplate
  3. todo_prompt_message = '''
  4. You are a planner who is an expert at coming up with a todo list for
  5. a given objective. Come up with a todo list for this objective: {objective}
  6. '''
  7. todo_prompt = PromptTemplate.from_template(todo_prompt_message)
  8. todo_chain = LLMChain(llm=OpenAI(temperature=0.1,
  9. model = 'gpt-4-1106-preview'), prompt=todo_prompt)

然后,我们将创建相应的工具和用于提示的代理,代码如下。

  1. from langchain.agents import AgentExecutor, Tool, ZeroShotAgent
  2. from langchain.prompts import PromptTemplate
  3. tools = [
  4. execute_sql,
  5. get_table_columns,
  6. get_table_column_distr,
  7. Tool(
  8. name="TODO",
  9. func=todo_chain.run,
  10. description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!",
  11. )
  12. ]
  13. prefix = """
  14. You are an AI who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.
  15. You are asked questions related to analytics for e-commerce product.
  16. Your work is very important, since your product team makes decisions based on the data you provide. So, you are extremely accurate with the numbers you provided.
  17. If you're not sure about the details of the request, you don't provide the answer and ask follow-up questions to have a clear understanding.
  18. You are very helpful and try your best to answer the questions.
  19. All the data is stored in SQL Database. Here is the list of tables (in the format <database>.<table>) with descriptions:
  20. - ecommerce.users - information about the customers, one row - one customer
  21. - ecommerce.sessions - information about the sessions customers made on our web site, one row - one session
  22. """
  23. suffix = """Question: {task}
  24. {agent_scratchpad}"""
  25. prompt = ZeroShotAgent.create_prompt(
  26. tools,
  27. prefix=prefix,
  28. suffix=suffix,
  29. input_variables=["objective", "task", "context", "agent_scratchpad"],
  30. )
  31. llm = OpenAI(temperature=0.1)
  32. llm_chain = LLMChain(llm=llm, prompt=prompt)
  33. tool_names = [tool.name for tool in tools]
  34. analyst_agent_babyagi = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names)
  35. analyst_agent_babyagi_executor = AgentExecutor.from_agent_and_tools(
  36. agent=analyst_agent_babyagi, tools=tools, verbose=True
  37. )

最后一步是定义 BabyAGI 执行器并运行它。

  1. from langchain_experimental.autonomous_agents import BabyAGI
  2. baby_agi = BabyAGI.from_llm(
  3. llm=llm,
  4. vectorstore=vectordb,
  5. task_execution_chain=analyst_agent_babyagi_executor,
  6. verbose=True,
  7. max_iterations=10
  8. )
  9. baby_agi("Find, how many active customers from the United Kingdom we have.")

结果发现:

该模型决定不使用 TO-DO 函数创建待办事项列表,而是跳转到查询 SQL。但是,第一个查询不正确。该模型尝试恢复并调用get_table_columns函数来获取列名称,但未能遵循架构。它们无法遵循结构。

7 第五个版本:带工具的 AutoGPT 代理

同样,我们需要为中间步骤设置向量存储。此处省略代码,同上。

在这种情况下,我们再次无法为模型指定任何提示。让我们尝试在没有任何具体指导的情况下使用它。但让我们添加该get_tables工具,以便模型可以看到所有可用的表。我希望它能帮助模型编写正确的 SQL 查询。

  1. @tool()
  2. def get_tables() -> str:
  3. """Returns list of tables in the format <database>.<table>"""
  4. return ['ecommerce.users', 'ecommerce.sessions']

让我们创建一个 AutoGPT 代理,它就像一个函数调用一样简单。然后,让我们执行它并看看它是如何工作的。

  1. from langchain_experimental.autonomous_agents import AutoGPT
  2. analyst_agent_autogpt = AutoGPT.from_llm_and_tools(
  3. ai_name="Harry",
  4. ai_role="Assistant",
  5. tools= [execute_sql, get_table_columns,
  6. get_table_column_distr, get_tables],
  7. llm=ChatOpenAI(temperature=0.1, model = 'gpt-4-1106-preview'),
  8. memory=vectordb.as_retriever(),
  9. )
  10. analyst_agent_autogpt.chain.verbose = True
  11. analyst_agent_autogpt.run(["Find how many active customers from the United Kingdom we have."])

该模型能够得出正确的答案:“来自英国的活跃客户数量为 111469。”

阅读提示很有趣,因为我们使用了默认提示。您可以通过 访问它analyst_agent_autogpt.chain.prompt

可以看到这个模型通过向量存储具有长期记忆。并且具有由思想、理由、计划、批评、总结等组成的综合推理。让我们看看模型的推理(只过滤了模型的响应)。所以,它遵循了与之前成功代理相同的方式。

  1. {
  2. "thoughts": {
  3. "text": "To find the number of active customers from the United Kingdom, I need to identify the relevant database and table that contains customer information. Once I have that, I can look for columns that might indicate customer activity and country. The first step is to list all the available tables.",
  4. "reasoning": "Understanding the structure of the database is essential before I can execute a query to find the active customers. Listing tables is a logical first step to identify where customer data might be stored.",
  5. "plan": "- Use the get_tables command to list all available tables.\n- Identify the table that likely contains customer information.\n- Use get_table_columns to understand the structure of the customer table.\n- Execute an SQL query to count active UK customers.",
  6. "criticism": "I need to ensure that I'm efficient with the commands I use, aiming to gather the necessary information in as few steps as possible.",
  7. "speak": "I am now going to list all the available tables to find where customer information is stored."
  8. },
  9. "command": {
  10. "name": "get_tables",
  11. "args": {}
  12. }
  13. }
  14. {
  15. "thoughts": {
  16. "text": "The 'ecommerce.users' table is likely to contain information about the customers. To find the number of active customers from the United Kingdom, I need to understand the structure of this table, specifically looking for columns that might indicate whether a customer is active and their country.",
  17. "reasoning": "Knowing the structure of the 'users' table will help me determine which columns to use in an SQL query to count active UK customers.",
  18. "plan": "- Use the get_table_columns command to get the structure of the 'ecommerce.users' table.\n- Identify columns that indicate customer activity and country.\n- Formulate an SQL query to count active UK customers based on the identified columns.",
  19. "criticism": "I must continue to be efficient with my commands, ensuring that each step brings me closer to the goal without unnecessary actions.",
  20. "speak": "I will now retrieve the structure of the 'ecommerce.users' table to identify the relevant columns for active customers and their country."
  21. },
  22. "command": {
  23. "name": "get_table_columns",
  24. "args": {
  25. "database": "ecommerce",
  26. "table": "users"
  27. }
  28. }
  29. }
  30. {
  31. "thoughts": {
  32. "text": "With the structure of the 'ecommerce.users' table known, I can see that there are columns for 'country' and 'is_active', which are necessary to identify active customers from the United Kingdom.",
  33. "reasoning": "The 'country' column will allow me to filter customers by their country, and the 'is_active' column will allow me to filter customers by their activity status. With this information, I can construct an SQL query to count the number of active customers from the UK.",
  34. "plan": "- Execute an SQL query using the 'execute_sql' command to count the number of active customers from the United Kingdom.\n- The query will count rows where 'country' is 'United Kingdom' and 'is_active' is true (1).",
  35. "criticism": "I need to ensure that the SQL query is correctly formulated to retrieve the exact information needed without any errors.",
  36. "speak": "I am now ready to execute an SQL query to count the number of active customers from the United Kingdom."
  37. },
  38. "command": {
  39. "name": "execute_sql",
  40. "args": {
  41. "query": "SELECT COUNT(*) FROM ecommerce.users WHERE country = 'United Kingdom' AND is_active = 1"
  42. }
  43. }
  44. }
  45. {
  46. "thoughts": {
  47. "text": "I have successfully executed the SQL query and obtained the number of active customers from the United Kingdom, which is 111,469.",
  48. "reasoning": "The goal was to find the number of active customers from the UK, and the SQL query has provided this information.",
  49. "plan": "- Since the goal has been achieved, the next step is to use the 'finish' command to signal that I have completed all my objectives.",
  50. "criticism": "There is no need for further action or commands as the objective has been met.",
  51. "speak": "I have found the number of active customers from the United Kingdom and am ready to finish the task."
  52. },
  53. "command": {
  54. "name": "finish",
  55. "args": {
  56. "response": "The number of active customers from the United Kingdom is 111,469."
  57. }
  58. }
  59. }

总体而言,AutoGPT 看起来是一种可行的方法。不过,仍然认为目前构建代理最可靠的方法是通过 OpenAI 函数。因为AutoGPT过程是不可控的。

8 第六个版本:LangChain 自己的 SQL 代理实现—— SQLDatabaseChain

以下是使用LangChain自己模块的方法:

  1. uri = 'clickhouse+native://localhost/ecommerce'
  2. db = SQLDatabase.from_uri(uri)
  3. toolkit = SQLDatabaseToolkit(db=db, llm=OpenAI(temperature=0))
  4. agent_executor = create_sql_agent(
  5. llm=ChatOpenAI(temperature=0.1, model = 'gpt-4-1106-preview'),
  6. toolkit=toolkit,
  7. verbose=True,
  8. agent_type=AgentType.OPENAI_FUNCTIONS
  9. )
  10. agent_executor.run("How many active customers from the United Kingdom do we have?")

很顺利地得到答案:

  1. > Entering new AgentExecutor chain...
  2. Invoking: `sql_db_list_tables` with ``
  3. sessions, users
  4. Invoking: `sql_db_schema` with `users`
  5. CREATE TABLE users (
  6. user_id UInt64,
  7. country String,
  8. is_active UInt8,
  9. age UInt64
  10. ) ENGINE = Log
  11. /*
  12. 3 rows from users table:
  13. user_id country is_active age
  14. 1000001 United Kingdom 0 70
  15. 1000002 France 1 87
  16. 1000003 France 1 88
  17. */
  18. Invoking: `sql_db_query` with `SELECT COUNT(*) FROM users WHERE country = 'United Kingdom' AND is_active = 1`
  19. [(111469,)]We have 111,469 active customers from the United Kingdom.
  20. > Finished chain.
  21. 'We have 111,469 active customers from the United Kingdom.'

其实这里langchain中两种方式,第一种构建chain,执行chain.run(question)。第二种是toolkit = SQLDatabaseToolkit(db=db, llm=llm) 然后构建Agent使用toolkit方法。这里是直接使用的第二种。

langchain文档中关于sql_agent的详细解释:

https://api.python.langchain.com/en/latest/agent_toolkits/langchain_community.agent_toolkits.sql.base.create_sql_agent.html?highlight=create_sql_agent#langchain_community.agent_toolkits.sql.base.create_sql_agent

9 总结

本文探究如何创建不同类型的代理实现text2sql。我们已经实现了一个由 LLM 支持的代理,可以完全从头开始使用 SQL 数据库。然后,我们利用高级 LangChain 工具通过几个函数调用来实现相同的结果。我们可以添加数据库Agent作为 LLM 支持的分析师的工具。这将是我们的一项技能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/608710
推荐阅读
相关标签
  

闽ICP备14008679号