В этом уроке мы рассмотрим, как реализовать агента, использующего 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, о которой всегда мечтали! ✨