在本教程中,我们将了解如何使用 smolagents
.
让我们从最重要的问题开始:为什么不保持简单,使用标准的文本到 SQL 管道?
标准的文本到 SQL 管道很脆弱,因为生成的 SQL 查询可能不正确。更糟糕的是,查询可能不正确,但不会引发错误,反而会提供一些不正确/无用的输出,而不会引发警报。
👉 相反,代理系统能够严格检查输出结果,并决定是否需要更改查询,从而大大提高性能。
让我们建立这个代理!💪
首先,我们设置 SQL 环境:
复制的
从 sqlalchemy 导入 ( create_engine、 元数据 表、 列 字符串 整数 浮点数、 插入 检查 文本、 ) engine = create_engine("sqlite:///:memory:") metadata_obj = MetaData() # 创建城市 SQL 表 table_name = "收据" receipts = Table( table_name、 metadata_obj.Column("receipt_id", Integer, primary_key=True) Column("receipt_id", Integer, primary_key=True)、 Column("customer_name", String(16), primary_key=True)、 Column("price", Float)、 列("小费",浮动)、 ) metadata_obj.create_all(engine) 行 = [ {"receipt_id":1, "customer_name":"Alan Payne", "price":12.06, "tip":1.20}, {"receipt_id":2, "customer_name":"Alex Mason", "价格":23.86, "tip":0.24}, {"receipt_id":3, "customer_name":"Woodrow Wilson", "价格":53.43, "tip":5.43}, {"receipt_id":4, "customer_name":"Margaret James", "price":21.11, "tip":1.00}, ] for row in rows: stmt = insert(receipts).values(**row) with engine.begin() as connection: cursor = connection.execute(stmt)
建立我们的代理 #
现在,让我们用工具检索我们的 SQL 表。
代理系统会将工具的描述属性嵌入 LLM 的提示中:为 LLM 提供如何使用工具的信息。这就是我们要描述 SQL 表的地方。
复制的
inspector = inspect(engine) columns_info = [(col["name"], col["type"]) for col in inspector.get_columns("receipts")] 列_信息 table_description = "Columns:\n" + "\n".join([f" - {name}: {col_type}" for name, col_type in columns_info]) print(table_description)
复制的
列: - receipt_id:INTEGER - customer_name: VARCHAR(16) - 价格FLOAT - 小费:FLOAT
现在,让我们来构建我们的工具。它需要具备以下条件:读取 工具文档 了解更多详情)
- 带有
参数
部分列出参数。 - 输入和输出均有类型提示。
复制的
从 smolagents 导入工具 @tool def sql_engine(query: str) -> str: """ 允许对表执行 SQL 查询。返回结果的字符串表示。 表名为 "收据"。其描述如下 列: - receipt_id:INTEGER - customer_name: VARCHAR(16) - 价格FLOAT - 小费:FLOAT 参数: query:要执行的查询。这应该是正确的 SQL。 """ output = "" with engine.connect() as con: rows = con.execute(text(query)) for row in rows: output += "\n" + str(row) 返回输出
现在,让我们创建一个利用这一工具的代理。
我们使用 代码代理
是 smolagents 的主要代理类:它是一个用代码编写操作的代理,可以根据 ReAct 框架迭代之前的输出。
模型是为代理系统提供动力的 LLM。HfApiModel 允许您使用 HF 的推理 API(通过无服务器端点或专用端点)调用 LLM,但您也可以使用任何专有 API。
复制的
from smolagents import CodeAgent, HfApiModel agent = CodeAgent( tools=[sql_engine]、 model=HfApiModel("meta-llama/Meta-Llama-3.1-8B-Instruct")、 ) agent.run("Can you give me the name of the client who got the most expensive receipt?")
第 2 级:表连接 #
现在,让我们来增加挑战性!我们希望代理能处理多个表之间的连接。
因此,我们再制作一个表,记录每个收据 ID 的服务员姓名!
复制的
table_name = "服务员" 收据 = 表( table_name、 metadata_obj.Column("receipt_id", Integer, primary_key=True) Column("receipt_id", Integer, primary_key=True)、 Column("waiter_name", String(16), primary_key=True)、 ) metadata_obj.create_all(engine) rows = [ {"receipt_id":1, "waiter_name":"Corey Johnson"}、 {"receipt_id":2, "waiter_name":"Michael Watts"}、 {"收据编号":3, "waiter_name":"Michael Watts"}、 {"收据编号":4, "waiter_name":"Margaret James"}、 ] for row in rows: stmt = insert(receipts).values(**row) with engine.begin() as connection: cursor = connection.execute(stmt)
因为我们更改了表格,所以要更新 SQLExecutorTool
与该表的描述一致,以便让 LLM 正确利用该表中的信息。
复制的
updated_description = """允许对表执行 SQL 查询。请注意,该工具的输出是执行输出的字符串表示。 它可以使用以下表格:""" inspector = inspect(engine) for table in ["receipts", "waiters"]: columns_info = [(col["name"], col["type"]) for col in inspector.get_columns(table)] 列信息 table_description = f "表'{table}':/n"。 table_description += "Columns:\n" + "\n".join([f" - {name}: {col_type}" for name, col_type in columns_info]) updated_description += "\n\n" + table_description print(updated_description)
由于这个请求比前一个更难,我们将切换 LLM 引擎,使用功能更强大的 Qwen/Qwen2.5-Coder-32B-Instruct!
复制的
sql_engine.description = updated_description agent = CodeAgent( tools=[sql_engine]、 model=HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct")、 ) agent.run("Which waiter got more total money from tips?")
它可以直接工作!设置出乎意料的简单,不是吗?
这个例子已经完成!我们已经触及了这些概念:
- 开发新工具。
- 更新工具说明。
- 改用更强的 LLM 有助于代理推理。
✅ 现在,你可以去构建你梦寐以求的文本到 SQL 系统了!✨