«Вау!» или почему это первый локальный AI-агент, который делает всё сам

Представьте: вы вводите одно-единственное описание задачи, а искусственный интеллект полностью сам пишет нужный код, сохраняет его в файл, автоматически устанавливает необходимые зависимости (если они требуются) и сразу же запускает полученный скрипт!И всё это в локальном окружении, без отправки вашего кода в чужое «облако». Если происходит ошибка, AI-агент может сам понять, что пошло не так, и скорректировать запрос к модели, пытаясь устранить проблему. Это действительно впечатляет!

Как и любая экспериментальная технология, этот AI-агент далёк от идеала и может допускать ошибки:

  • Возможны ложные срабатывания или ситуации, когда агент не сможет правильно исправить ошибку и будет бесконечно переформулировать запрос.
  • Код, который генерирует агент, может иногда не работать так, как вы ожидаете. Иногда могут появляться логические ошибки или несоответствия с вашим окружением.

Важно понимать, что это пока бета-версия и не гарантирует 100% стабильной работы. В некоторых случаях придётся вручную вмешиваться и корректировать результат. Однако даже сейчас это демонстрирует, насколько далеко продвинулась автоматизация разработки.

  1. Python 3.8+Убедитесь, что у вас установлена современная версия Python. Проверить можно командой: python --version

     2. LLM-сервер Ollama (или другой вариант локального/удалённого LLM-сервера)

  • В примере код настроен на сервер по адресу http://localhost:11434/v1.
  • Если вы используете Ollama, запустите Ollama-сервер локально. Убедитесь, что он принимает запросы на нужном порту (по умолчанию 11434).
  • Если вы хотите использовать другой LLM-сервис, замените адрес http://localhost:11434/v1 на соответствующий.

     3. qwen_agent (или аналогичный пакет для взаимодействия с вашей LLM)

  • Установите через PyPI:  pip install qwen-agent

Сохраните этот код в файл, например, agent.py:

import json
import os
import subprocess
from qwen_agent.llm import get_chat_model

def reformulate_request_agent(user_request, error_description=None):
    """
    Функция для переформулирования запроса к модели.
    Принимает:
    - user_request: исходный текст запроса от пользователя.
    - error_description: сообщение об ошибке, если код не запустился.
    
    Возвращает:
    - Переформулированный текст запроса для повторной генерации кода.
    """
    llm = get_chat_model({
        'model': 'qwen2.5-coder:14b',        # Название модели
        'model_server': 'http://localhost:11434/v1',  # Адрес вашего Ollama или другого LLM-сервера
        'api_key': 'EMPTY',                 # При необходимости указать ключ
    })

    # Формируем сообщение для системы
    base_message = user_request
    if error_description:
        base_message += f" Было получено сообщение об ошибке: {error_description}."

    messages = [
        {
            'role': 'system', 
            'content': (
                "You are an assistant that reformulates user requests to ensure they are suitable "
                "for generating Python code. Your reformulated request should be clear and precise."
            )
        },
        {'role': 'user', 'content': base_message}
    ]

    # Здесь модель генерирует ответ (переформулированный запрос).
    responses = []
    for responses in llm.chat(messages=messages, stream=True):
        pass

    # Возвращаем итоговое переформулирование
    return responses[0]['content']


def save_and_run_code(code, name_file, dependencies=None):
    """
    Функция, которая:
    1. Сохраняет переданный Python-код в файл.
    2. Устанавливает зависимости (если указано).
    3. Запускает созданный файл и возвращает результат (или ошибку).

    :param code: Строка с Python-кодом.
    :param name_file: Имя файла для сохранения кода.
    :param dependencies: Строка со списком зависимостей (через пробел).
    :return: Словарь формата {"success": bool, "output" или "error": str}
    """
    # Приводим код к нужному формату (вдруг пришли эскейп-символы '\n' или '\t')
    formatted_code = code.replace("\\n", "\n").replace("\\t", "\t")

    # 1. Сохраняем код в файл
    with open(name_file, "w", encoding="utf-8") as file:
        file.write(formatted_code)
    print(f"Код сохранён в файл: {name_file}")

    # 2. Установка зависимостей (если они есть)
    if dependencies:
        print(f"Установка зависимостей: {dependencies}")
        try:
            # pip install <dependencies>
            subprocess.run(["pip", "install"] + dependencies.split(), check=True, capture_output=True, text=True)
            print("Зависимости успешно установлены.")
        except subprocess.CalledProcessError as e:
            error_message = f"Ошибка при установке зависимостей: {e.stderr}"
            print(error_message)
            return {"success": False, "error": error_message}

    # 3. Запуск кода
    try:
        result = subprocess.run(["python", name_file], capture_output=True, text=True)
        if result.returncode != 0:
            # Если код вернулся с ошибкой (не 0)
            return {"success": False, "error": result.stderr}
        # Если всё ок, возвращаем результат
        print(f"Результат выполнения файла {name_file}:\n{result.stdout}")
        return {"success": True, "output": result.stdout}
    except Exception as e:
        print(f"Ошибка при запуске файла {name_file}: {e}")
        return {"success": False, "error": str(e)}


def reformulate_request(error_description):
    """
    Простейшая функция, которая формирует новый запрос с учётом ошибки.
    """
    return f"Исправь ошибку: {error_description}"


def test():
    """
    Основная функция, показывающая пример работы агента.

    Шаги:
    1. Получаем от пользователя запрос (описание задачи).
    2. Формируем сообщения для LLM, где указываем, что мы хотим получить готовый Python-код.
    3. Модель пытается сгенерировать код, вызывая функцию save_and_run_code.
    4. Если возникает ошибка, у пользователя запрашивается обновлённый запрос (либо автоматически переформулируется).
    5. Повторяем, пока не будет получен корректный результат.

    Важно: Поскольку агент ещё в бета-версии, иногда могут возникать циклические попытки исправить ошибку.
    Если видите, что он слишком долго не может разобраться, прервите выполнение и откорректируйте код вручную.
    """
    # Подключаем модель (указываем сервер и имя модели)
    llm = get_chat_model({
        'model': 'qwen2.5-coder:14b',          # Название модели
        'model_server': 'http://localhost:11434/v1',  # Адрес Ollama или другого LLM-сервера
        'api_key': 'EMPTY',
    })

    # Пример запроса (вместо input можно просто задать строку)
    req_user = str(input("Введите ваше описание кода: "))
    req_user2 = req_user

    while True:
        # Печатаем запрос, чтобы видеть, что отправляем
        print("Текущий запрос к модели:", req_user)

        # Формируем сообщения для LLM
        messages = [
            {
                'role': 'system', 
                'content': (
                    "You are an assistant that generates Python code based on user descriptions. "
                    "In addition to generating the code, you are responsible for providing the file name "
                    "where the code will be saved and a list of dependencies required to execute the code. "
                    "Ensure the output is executable and properly formatted."
                )
            },
            {'role': 'user', 'content': req_user}
        ]

        # Описываем функцию, которую модель может вызвать (function call)
        functions = [{
            'name': 'save_and_run_code',
            'description': 'Generates Python code, saves it to a specified file, installs dependencies, and executes it.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'code': {
                        'type': 'string',
                        'description': 'Python code',
                    },
                    'name_file': {
                        'type': 'string',
                        'description': 'file name for saving the code',
                    },
                    'dependencies': {
                        'type': 'string',
                        'description': 'A space-separated list of dependencies to install before running the code',
                    },
                },
                'required': ['code', 'name_file'],
            },
        }]

        # Отправляем запрос модели с описанием функций
        responses = []
        for responses in llm.chat(messages=messages, functions=functions, stream=True):
            pass

        # Добавляем ответ модели в историю сообщений
        messages.extend(responses)  
        last_response = messages[-1]

        # Если модель хочет вызвать функцию (function_call), то вызываем её
        if last_response.get('function_call', None):
            try:
                available_functions = {
                    'save_and_run_code': save_and_run_code,
                }
                function_name = last_response['function_call']['name']
                function_to_call = available_functions[function_name]

                # Аргументы для функции
                function_args = json.loads(last_response['function_call']['arguments'])

                # Вызываем функцию (сохраняем, устанавливаем зависимости, запускаем код)
                function_response = function_to_call(
                    code=function_args.get('code'),
                    name_file=function_args.get('name_file'),
                    dependencies=function_args.get('dependencies'),
                )

                # Проверяем результат (успех или ошибка)
                if function_response.get("success"):
                    print("Код выполнен успешно!")
                    print(f"Результат: {function_response['output']}")
                    break
                else:
                    error_description = function_response.get("error")
                    print(f"Обнаружена ошибка: {error_description}")
                    # Можно попросить пользователя уточнить, или переформулировать запрос автоматически
                    req_user = reformulate_request_agent(req_user, error_description)

            except:
                # Если произошла ошибка, пробуем переформулировать
                print('Ошибка в процессе вызова функции.')
                req_user = reformulate_request_agent(req_user)

        else:
            # Модель не смогла сгенерировать корректный function_call
            print("Модель не смогла сгенерировать функцию. Попробуем переформулировать запрос.")
            req_user = reformulate_request_agent(req_user2, "Модель не сформировала корректный запрос.")

if __name__ == '__main__':
    test()

Как запустить: python agent.py

После этого в консоли появится приглашение: «Введите ваше описание кода:».Например, можно ввести: Напиши программу, которая выводит числа от 1 до 5.

Агент попытается сгенерировать код, сохранить его, установить зависимости (если указаны) и выполнить.

В качестве наглядного примера можно попросить нашего AI-агента сгенерировать код, рисующий треугольник Серпинского (часто называют его пирамидой) на Python с помощью модуля turtle. Агент не всегда справляется с первого раза: могут возникать ошибки, опечатки или неверные вызовы функций. Однако после нескольких уточнений он предложил следующий рабочий код:

import turtle

def sierpinski(degree, points):
    """
    Рекурсивная функция, которая рисует треугольник Серпинского
    в зависимости от глубины рекурсии degree и координат точек points.
    """
    colormap = ['blue', 'red', 'green', 'white', 'yellow', 'violet']
    draw_triangle(points, colormap[degree % 6])

    if degree > 0:
        sierpinski(degree - 1,
                   {
                       'left': get_mid(points['left'], points['top']),
                       'right': get_mid(points['right'], points['top']),
                       'top': points['top']
                   })
        sierpinski(degree - 1,
                   {
                       'left': points['left'],
                       'right': get_mid(points['left'], points['right']),
                       'top': get_mid(points['left'], points['right'])
                   })
        sierpinski(degree - 1,
                   {
                       'left': get_mid(points['right'], points['left']),
                       'right': points['right'],
                       'top': get_mid(points['right'], points['top'])
                   })

def draw_triangle(points, color):
    """
    Рисует закрашенный треугольник по координатам в словаре points.
    """
    turtle.fillcolor(color)
    turtle.up()
    turtle.goto(points['left'])
    turtle.down()
    turtle.begin_fill()
    turtle.goto(points['right'])
    turtle.goto(points['top'])
    turtle.goto(points['left'])
    turtle.end_fill()

def get_mid(p1, p2):
    """
    Возвращает координату середины между двумя точками (p1 и p2).
    """
    return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)

# Настраиваем окно turtle
wn = turtle.Screen()
wn.bgcolor('black')

# Определяем три основные точки (вершины) большого треугольника
points = {
    'left': (-300, -150),
    'right': (300, -150),
    'top': (0, 300)
}

# Рисуем треугольник Серпинского глубины 5
sierpinski(5, points)

# Скрываем «черепашку» и ждём закрытия окна
turtle.hideturtle()
wn.exitonclick()
  1. Основная функция sierpinski(degree, points):Рисует треугольник на каждом уровне рекурсии.Если degree > 0, вызывает себя же три раза с новыми координатами («подтреугольниками») меньшего размера.
  2. Рисует треугольник на каждом уровне рекурсии.
  3. Если degree > 0, вызывает себя же три раза с новыми координатами («подтреугольниками») меньшего размера.
  4. Функция draw_triangle(points, color):Получает словарь с тремя координатами ('left', 'right', 'top') и закрашивает треугольник соответствующим цветом.
  5. Получает словарь с тремя координатами ('left', 'right', 'top') и закрашивает треугольник соответствующим цветом.
  6. Функция get_mid(p1, p2):Возвращает точку — середину отрезка между p1 и p2.
  7. Возвращает точку — середину отрезка между p1 и p2.
  8. Настройка окна turtle.Screen(), выбор цвета фона, и установка базовых координат для большого треугольника.
  9. Вызов sierpinski(5, points) даёт глубину рекурсии (количество уровней «вырезания») — чем больше глубина, тем детальнее рисунок, но дольше отрисовка.
  • Сначала агент мог сгенерировать неполный или некорректный код, где не были объявлены переменные или функции, либо пропускались важные части логики.
  • После того как агенту сообщили об ошибке (или он сам нашёл ошибку при запуске), он переформулировалзапрос и скорректировал свой код.
  • В результате спустя несколько итераций был получен рабочий пример.

Это отлично иллюстрирует плюсы и минусы использования бета-версии AI-агента:

  • Плюс: агент может выполнить большую часть рутины и «вспомнить» синтаксис, отрисовку, рекурсию и т.д.
  • Минус: при возникновении ошибки не всегда сразу понимает, как её исправить; иногда требуется вмешательство человека, который точно укажет на проблему.

Таким образом, наш AI-агент хоть и не совершенен, но уже сейчас помогает экономить время и упрощает процесс создания кода — даже для построения таких любопытных фигур, как треугольник (или пирамида) Серпинского с помощью библиотеки turtle.

  • Полная автоматизация: AI-агент работает локально и выполняет все шаги — от генерации кода до его запуска.
  • Экономия времени и сил: Вам не нужно переключаться между редактором кода, терминалом и браузером для поиска зависимостей — агент берёт это на себя.
  • Бета-версия: Важно помнить, что система пока сыровата и не всегда корректно обрабатывает ошибки. Может возникнуть ситуация, когда она «зациклится» в попытках исправить код.
  • Готовая основа для расширения: Хотите дополнить проверки, добавить тесты или реализовать поддержку других языков? Всё это возможно в рамках той же архитектуры, где модель взаимодействует с функциями Python.

Приятного использования!

Комментарии 0

Авторизуйтесь чтобы оставить комментарий