В этом уроке мы рассмотрим, как реализовать агента, использующего SQL с помощью smolagents.
Начнем с золотого вопроса: почему бы не упростить задачу и не использовать стандартный конвейер преобразования текста в SQL?
Стандартный конвейер преобразования текста в sql является хрупким, поскольку сгенерированный SQL-запрос может быть неверным. Хуже того, запрос может быть неверным, но не вызывать ошибку, а выдавать некорректные/бесполезные результаты, не вызывая тревоги.
👉 Вместо этого агентская система способна критически оценить результаты и решить, нужно ли изменить запрос или нет, что дает ей огромный прирост производительности.
Давайте создадим этого агента! 💪
Сначала мы настроим среду SQL:
Скопировано
из sqlalchemy import (
create_engine,
Метаданные,
Таблица,
Column,
String,
Integer,
Float,
вставка,
проверять,
текст,
)
engine = create_engine("sqlite:///:memory:")
metadata_obj = MetaData()
# создать таблицу города SQL
имя_таблицы = "квитанции"
квитанции = Таблица(
имя_таблицы,
metadata_obj,
Column("receipt_id", Integer, primary_key=True),
Column("имя_клиента", String(16), primary_key=True),
Column("price", Float),
Column("tip", Float),
)
metadata_obj.create_all(engine)
rows = [
{ "receipt_id": 1, "customer_name": "Алан Пейн", "цена": 12.06, "чаевые": 1.20},
{"receipt_id": 2, "имя_клиента": "Алекс Мейсон", "цена": 23.86, "чаевые": 0.24},
{"receipt_id": 3, "имя_клиента": "Вудро Вильсон", "цена": 53.43, "чаевые": 5.43},
{"receipt_id": 4, "имя_клиента": "Маргарет Джеймс", "цена": 21.11, "чаевые": 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 - имя_клиента: VARCHAR(16) - цена: FLOAT - чаевые: FLOAT
Теперь давайте создадим наш инструмент. Для этого необходимо следующее: (читать документ об инструментах подробнее)
- Документная строка с
Args:часть, перечисляющая аргументы. - Введите подсказки как на входе, так и на выходе.
Скопировано
из smolagents импортировать инструмент
@tool
def sql_engine(query: str) -> str:
"""
Позволяет выполнять SQL-запросы к таблице. Возвращает строковое представление результата.
Таблица имеет имя 'receipts'. Ее описание выглядит следующим образом:
Столбцы:
- receipt_id: INTEGER
- имя_клиента: VARCHAR(16)
- цена: FLOAT
- чаевые: FLOAT
Args:
query: Запрос, который необходимо выполнить. Это должен быть правильный SQL.
"""
output = ""
with engine.connect() as con:
rows = con.execute(text(query))
for row in rows:
output += "\n" + str(row)
возврат выводаТеперь давайте создадим агента, использующего этот инструмент.
Мы используем CodeAgentэто основной класс агентов smolagents: агент, который пишет действия в коде и может итерировать предыдущий результат в соответствии с фреймворком ReAct.
Модель - это LLM, на котором работает агентская система. HfApiModel позволяет вызывать LLM с помощью HF's Inference API, либо через Serverless, либо через Dedicated endpoint, но вы также можете использовать любой проприетарный API.
Скопировано
из smolagents import CodeAgent, HfApiModel
агент = CodeAgent(
tools=[sql_engine],
model=HfApiModel("meta-llama/Meta-Llama-3.1-8B-Instruct"),
)
agent.run("Можете ли вы назвать мне имя клиента, который получил самый дорогой чек?")Уровень 2: Объединение таблиц #
Теперь давайте усложним задачу! Мы хотим, чтобы наш агент обрабатывал соединения между несколькими таблицами.
Поэтому давайте создадим вторую таблицу, в которой будут записаны имена официантов для каждого receipt_id!
Скопировано
имя_стола = "официанты"
квитанции = Таблица(
имя_стола,
metadata_obj,
Column("receipt_id", Integer, primary_key=True),
Column("waiter_name", String(16), primary_key=True),
)
metadata_obj.create_all(engine)
строки = [
{ "receipt_id": 1, "waiter_name": "Кори Джонсон"},
{ "receipt_id": 2, "имя_официанта": "Майкл Уоттс"},
{"receipt_id": 3, "waiter_name": "Майкл Уоттс"},
{"receipt_id": 4, "waiter_name": "Маргарет Джеймс"},
]
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
агент = CodeAgent(
tools=[sql_engine],
model=HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct"),
)
agent.run("Какой официант получил больше общих денег от чаевых?")Он работает! Настройка оказалась на удивление простой, не правда ли?
Этот пример завершен! Мы затронули эти понятия:
- Создание новых инструментов.
- Обновление описания инструмента.
- Переход на более сильный LLM помогает агенту рассуждать.
✅ Теперь вы можете построить систему преобразования текста в SQL, о которой всегда мечтали! ✨
