Как создать ИИ-агента для кодирования с помощью LangGraph и LangChain?

Что такое ИИ-агент для кодирования и зачем он нужен?

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

Обзор LangChain и LangGraph: основные понятия и преимущества для разработки агентов

LangChain – это фреймворк, предназначенный для упрощения разработки приложений на основе LLM. Он предоставляет инструменты для работы с различными моделями, хранилищами данных и другими компонентами, необходимыми для создания сложных ИИ-систем. LangChain абстрагирует многие низкоуровневые детали, позволяя разработчикам сосредоточиться на логике приложения.

LangGraph – это расширение LangChain, которое позволяет создавать более сложные и управляемые агенты, представляя их как графы состояний. В графе состояний каждый узел представляет определенное состояние агента (например, ожидание запроса, генерация кода, тестирование), а ребра определяют переходы между этими состояниями. Это позволяет создавать более гибкие и надежные системы, которые могут адаптироваться к различным ситуациям и обрабатывать ошибки.

Преимущества использования LangChain и LangGraph для разработки ИИ-агентов для кодирования:

  • Простота разработки: Фреймворки предоставляют готовые компоненты и абстракции, упрощающие создание и интеграцию различных частей агента.
  • Гибкость: LangGraph позволяет создавать агентов с сложной логикой и адаптивным поведением.
  • Надежность: Графы состояний обеспечивают более четкий контроль над процессом выполнения и позволяют эффективно обрабатывать ошибки.
  • Масштабируемость: Фреймворки позволяют легко масштабировать агента для обработки большего количества задач.

Архитектура ИИ-агента для кодирования: компоненты и взаимодействие

Типичный ИИ-агент для кодирования состоит из следующих компонентов:

  1. LLM (Language Model): Ядро агента, отвечающее за генерацию кода, анализ запросов и принятие решений.
  2. Инструменты: Набор функций, предоставляющих агенту доступ к внешним ресурсам, таким как файловая система, компилятор, интерпретатор, системы контроля версий.
  3. Память: Компонент, хранящий историю взаимодействия и контекст, позволяющий агенту принимать более обоснованные решения.
  4. Граф состояний (LangGraph): Определяет логику работы агента, управляя переходами между различными состояниями в зависимости от входных данных и результатов работы.

Взаимодействие между компонентами происходит следующим образом:

  1. Агент получает запрос от пользователя.
  2. LLM анализирует запрос и определяет, какие инструменты необходимо использовать.
  3. Агент использует выбранные инструменты для выполнения запроса.
  4. Результаты выполнения инструментов сохраняются в памяти.
  5. LLM использует память и результаты выполнения инструментов для принятия решения о следующем шаге (например, генерация кода, тестирование, исправление ошибок).
  6. Процесс повторяется до тех пор, пока не будет достигнут желаемый результат.

Настройка окружения и установка необходимых библиотек

Установка Python и создание виртуального окружения

Убедитесь, что у вас установлен Python 3.8 или выше. Рекомендуется использовать виртуальное окружение для изоляции зависимостей проекта:

python3 -m venv .venv
source .venv/bin/activate  # Linux/macOS
# .venv\Scripts\activate  # Windows

Установка LangChain, LangGraph и других зависимостей

Установите необходимые библиотеки с помощью pip:

pip install langchain langgraph openai beautifulsoup4 chromadb requests

Здесь beautifulsoup4, chromadb, и requests — примеры дополнительных библиотек, которые могут потребоваться в зависимости от конкретных инструментов, которые вы планируете использовать (например, для парсинга веб-страниц, векторного хранения данных, или выполнения HTTP-запросов).

Настройка API ключей для доступа к моделям LLM (например, OpenAI)

Для доступа к моделям LLM, таким как OpenAI, необходимо получить API ключ и установить его в качестве переменной окружения:

export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"

Или, можно указать ключ непосредственно в коде (не рекомендуется для production):

import os

os.environ['OPENAI_API_KEY'] = 'YOUR_OPENAI_API_KEY'

Создание базового ИИ-агента с LangChain

Определение инструментов для кодирования (например, выполнение кода, доступ к файловой системе)

Начнем с простого инструмента — выполнения Python кода. Для безопасности не рекомендуется использовать eval или exec в production. Вместо этого, можно использовать ast.literal_eval для более безопасной оценки выражений или ограничить выполнение кода песочницей:

import subprocess
from typing import Dict


def execute_python_code(code: str) -> str:
    """Executes Python code and returns the output."""
    try:
        result = subprocess.run(
            ['python3', '-c', code],
            capture_output=True,  # Capture stdout and stderr
            text=True,           # Decode output as text
            timeout=10           # Limit execution time
        )

        if result.returncode == 0:
            return result.stdout
        else:
            return f"Error: {result.stderr}"
    except subprocess.TimeoutExpired:
        return "Timeout: Code execution exceeded the time limit."
    except Exception as e:
        return f"Exception: {str(e)}"


TOOL_DESCRIPTIONS = {
    "execute_python_code": "Executes Python code and returns the output. Input should be a valid Python code string."
}

TOOLS = {
    "execute_python_code": execute_python_code
}


def get_tool_by_name(tool_name: str):
    return TOOLS.get(tool_name)
Реклама

Создание LLM (Language Model) и определение логики агента

Используем ChatOpenAI для создания языковой модели:

from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder
from langchain.tools.render import format_tool_to_openai
from langchain.agents.format_scratchpad import format_to_openai_function_messages

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

tools = [format_tool_to_openai(tool) for tool_name, tool in TOOLS.items()]

system_prompt = """You are a helpful AI coding assistant.  You can use tools to help you.
{tools}"""

prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
    ("user", "{input}")
])

llm_with_tools = llm.bind(
    functions=tools,
    function_call="auto",
)

def agent_logic(input):
    prompt_value = prompt.format_messages(
        input=input["input"],
        tools=tools,
        agent_scratchpad=format_to_openai_function_messages(input["intermediate_steps'])
    )
    return llm_with_tools.invoke(prompt_value)

Интеграция инструментов и LLM с помощью LangChain: Agent Executor

from langchain.agents import AgentExecutor

def run_agent(user_input):
    intermediate_steps = []
    next_input = {
        "input": user_input,
        "intermediate_steps": intermediate_steps
    }

    for _ in range(5): # limit loop runs
        output = agent_logic(next_input)
        if output.tool_calls:
            tool_name = output.tool_calls[0].function.name
            tool_to_use = get_tool_by_name(tool_name)

            if tool_to_use is None:
                response = f"Unknown tool: {tool_name}"
            else:
                tool_input = output.tool_calls[0].function.arguments
                response = tool_to_use(tool_input)

            intermediate_steps.append((output, response))
            next_input = {
                "input": user_input,
                "intermediate_steps": intermediate_steps
            }
        else:
            return output.content

    return "Agent failed to converge."

Тестирование базового агента: простые задачи кодирования

user_prompt = "Напиши Python код, который вычисляет сумму чисел от 1 до 10."
result = run_agent(user_prompt)
print(result)

Улучшение агента с помощью LangGraph: добавление логики и состояний

Введение в графы состояний LangGraph: узлы и ребра

LangGraph позволяет представить логику агента в виде графа состояний. Узлы графа представляют состояния агента, а ребра – переходы между этими состояниями. Каждый переход может быть обусловлен определенным условием, что позволяет агенту адаптироваться к различным ситуациям.

Определение состояний агента: ожидание запроса, генерация кода, тестирование, исправление ошибок

Для агента кодирования можно выделить следующие состояния:

  • start: Начальное состояние, ожидание запроса пользователя.
  • code_generation: Генерация кода на основе запроса.
  • code_execution: Выполнение сгенерированного кода.
  • evaluation: Оценка результатов выполнения кода.
  • improvement: Исправление ошибок и улучшение кода.
  • end: Конечное состояние, код сгенерирован и протестирован.

Создание графа состояний для управления процессом кодирования

from langgraph.graph import StateGraph, END
from typing import TypedDict, Dict, Any

class AgentState(TypedDict):
    input: str
    intermediate_steps: list[tuple[Any, str]]
    messages: list

def create_graph(agent_logic_func):
    graph = StateGraph(AgentState)
    graph.add_node("start", lambda state: state['input'])
    graph.add_node("agent", agent_logic_func)
    graph.add_node("tool_execution", execute_tool)
    graph.add_node("evaluation", evaluate_code_execution)

    graph.set_entry_point("start")

    graph.add_edge("start", "agent")

    graph.add_conditional_edges(
        "agent",
        should_continue,
        {
            "continue": "tool_execution",
            "end": END
        }
    )

    graph.add_edge("tool_execution", "evaluation")
    graph.add_edge("evaluation", "agent")

    return graph

Реализация переходов между состояниями: логика принятия решений на основе LLM

Реализация переходов между состояниями включает в себя:

  • Функцию should_continue, определяющую, нужно ли продолжать выполнение (использовать инструменты) или завершить работу.
  • Функцию execute_tool, которая выполняет выбранный инструмент и возвращает результат.
  • Функцию evaluate_code_execution, оценивающую результат выполнения кода и определяющую, нужно ли его улучшить.

Пример функции should_continue:

def should_continue(state):
    messages = state['messages']
    if messages[-1].tool_calls:
        return "continue"
    else:
        return "end"

Тестирование улучшенного агента: более сложные задачи и обработка ошибок

Протестируйте агента на задачах, требующих использования нескольких инструментов и итеративного улучшения кода. Обратите внимание на обработку ошибок и способность агента адаптироваться к изменяющимся условиям.

Продвинутые техники и оптимизация

Использование памяти для отслеживания контекста и улучшения результатов

Используйте память (например, ConversationBufferMemory из LangChain) для хранения истории взаимодействия с пользователем и промежуточных результатов. Это позволит агенту принимать более обоснованные решения и избегать повторения ошибок.

Интеграция с системами контроля версий (Git) для управления кодом

Интегрируйте агента с Git для автоматического сохранения изменений в коде и возможности отката к предыдущим версиям. Это особенно важно для сложных проектов, требующих совместной работы нескольких разработчиков.

Оптимизация производительности: кеширование, параллельное выполнение задач

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

Мониторинг и отладка агента: использование логов и визуализации графа состояний

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


Добавить комментарий