Как стать автором
Обновить
182.58
Raft
AI решения для бизнеса

Замена Langchain, как OpenAI Agents SDK справляется с глубоким поиском?

Уровень сложностиПростой
Время на прочтение9 мин
Количество просмотров2K

Агенты супер багованы. В своих проектах в компании мы заметили, что Langchain стал уж слишком баговым. В мультиагентных системах агенты зачастую циклятся, так как не понимают, когда они выполнили финальное действие, не вызывают друг друга когда надо, или же просто возвращают данные в битом формате JSON. Короче говоря, создать агентную систему стало не так то просто, и мы даже стали задумываться об упрощении систем, избавляясь от кучи агентов. И вот неделю назад OpenAI обновили SDK для создания агентов, а еще выкатили доступ к новым тулзам по API. Ну и я пошел тестить.

Я Арсений, Data Scientist в Raft и сегодня я расскажу вам про OpenAI либу для создания агентов от OpenAI.

Мультиагенты

Мультиагентые системы супер важны, поскольку это позволяет не размазывать контекст у моделей, на основе которых они сделаны, делая ее работу более стабильной. Нельзя зафигачить супер большой системный промпт в одного агента и ждать что он отработает во всех кейсах идеально. Важно делать задачи для агентов максимально прямолинейными и атомарными, для лучшей производительности, а общий результат должен зависеть от взаимодействия агентов.

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

Помимо этого, логирование и дебаг агентов — не супер удобная штука. Можно вручную настроить логирование в Grafana и записывать туда вывод агентов, но, исходя из моего опыта, это не очень удобно для чтения и требует дополнительного настраивания Grafana.

OpenAI Agents SDK

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

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

Агент ресёрчер (типа deep search в GPT).
Что он делает: Анализирует то, что ввёл пользователь.

  1. Задаёт уточняющие вопросы при необходимости.

  2. Анализирует файлы и интернет на предмет данных по материалу.

  3. Выдаёт полный отчёт со ссылками на материалы.

В качестве файла я выбрал данные одного крупнейшего Агро производителя удобрений и семян. В файле всего 340 страниц и его вес примерно 50 МБ. Задавать вопросы будем соответственно про удобрения.

Итак, пойдем по порядку, и начнем с первого пункта

Шаг 1. Анализ вопроса пользователя

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

from agents import Agent, Runner

query_analyzer_agent = Agent(
    name="QueryAnalyzer",
    instructions="""
    Ты агент, который анализирует запросы пользователей. Тебе задают вопросы ожидая,
    в дальнейшем ответа с использованием файлов и интернета. 
    Твоя задача - понять запрос, определить его тематику и структуру.
    Выдели ключевые аспекты, названия, аттрибуты, определи, нужен ли уточняющий вопрос.
    Не злоупотребляй уточняющими вопросами. Если ты смог достать ключевую информацию,
    не спрашивай у пользователя ещё раз.
    """
)

runner = Runner()
analysis_result = await runner.run(
    query_analyzer_agent, 
    f"Проанализируй следующий запрос пользователя и определи, требуются ли уточнения: '{query}'"
    "Если уточнения требуются, сформулируй конкретный вопрос. "
    "Если уточнения не требуются, просто напиши 'Уточнения не требуются'."
)

Runner запускает рабочий процесс агента. Агент будет выполняться в цикле до тех пор, пока не будет сгенерирован окончательный результат.

Цикл выполняется следующим образом:

  1. Агент вызывается с определенным заданием. В примере выше в задание подставляется пользовательский запрос:

    "Проанализируй следующий запрос пользователя и определи, требуются ли уточнения: '{query}'
    Если уточнения требуются, сформулируй конкретный вопрос.
    Если уточнения не требуются, просто напиши 'Уточнения не требуются'.

  2. Если есть конечный результат (т.е. агент выдает что-то типа agent.output_type), цикл завершается.

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

  4. В противном случае вызываются инструменты (если таковые имеются) и повторно запускается цикл.

При создании агента, можно также выбирать модель, и то через какое API будет реализовываться обращение к модели.

  • Рекомендуется использовать новую версию Responses API, например так

    model=OpenAIResponsesModel(model="gpt-4o", openai_client=AsyncOpenAI())

  • Но и через  OpenAIChatCompletionsModel, который вызывает OpenAI APIs Chat Completions API

    model= OpenAIChatCompletionsModel(model="gpt-4o", openai_client=AsyncOpenAI())

Используется по дефолту новая версия Responses API и gpt-4o-2024-08-06.

Шаг 2. FileSearch

Будем использовать встроенную в SDK FileSearchTool() (подробнее тут). Для того чтобы искать по файлам нужно сначала создать хранилище файлов и загрузить их туда. Это можно сделать либо через сайт, либо используя API.

На сайте:

  • Создаем векторное хранилище

  • Добавляем нужные файлы

  • Для использования нужно сохранить vector_id, чтобы затем передать его в WebSearchTool()

API:

from openai import OpenAI

client = OpenAI()

# Создаем файл
local_file_path = "path"
with open(local_file_path, "rb") as file_content:
  file_response = client.files.create(
    file=file_content,
    purpose="file-search"
  )

# Создаем хранилище
vector_store = client.vector_stores.create(
    name="knowledge_base"
)

# Добавляем файл в хранилище
client.vector_stores.files.create(
    vector_store_id=vector_store.id,
    file_id=file_id
)

Теперь можем создать агента поиска по файлу. Для этого используем vector_store.id и FileSearchTool() Можно задать количество файлов, которое использовать как подходящие для поиска ответа. В моем хранилище только один файл, поэтому я использую только его один.

from agents import Agent, FileSearchTool, Runner

file_search_tool = FileSearchTool(
    vector_store_ids=[vector_store.id],
    max_num_results=1
)

file_agent = Agent(
    name="FileSearcher",
    instructions="""
    Ты агент, специализирующийся на поиске информации в загруженных файлах.
    Используй имеющиеся у тебя инструменты для поиска информации в загруженных файлах.
    Предоставляй полную и точную информацию из файлов, цитируя источник.
    Это самый важный этап анализа, так как информация из файла имеет приоритет.
    Если информация в файле не найдена, четко укажи это.
    """,
    tools=[file_search_tool]
)
runner = Runner()
file_result = await runner.run(
    file_agent, 
    f"""Найди информацию по запросу: '{query}' в загруженном файле.
    Очень важно найти максимально релевантную информацию."""
)

Цены и ограничения:

  • Цена использования $2.50 за 1000 запросов и хранилище стоит $0.10/GB/day, но первый Гб бесплатно. 

  • Проекты не должны превышать 100GB для всех файлов.

  • Максимум хранилища -- 10k файлов.

  • Размер одного файла не более 512MB (примерно 5M токенов)

Шаг 3. WebSearch

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

Итак сначала создаем тулзу для поиска WebSearchTool(). Указываем размер контекста поиска, что напрямую влияет на итоговую цену, а так же опционально можем настроить локацию поиска

"user_location": {"type": "approximate", "country": "GB", "city": "London", "region": "London"}

Затем создаем самого агента и передаем ему инструкции и тулзу. После чего уже его можем запускать.

from agents import Agent, WebSearchTool, Runner

web_search_tool = WebSearchTool(
    search_context_size="medium"
)

web_agent = Agent(
    name="WebSearcher",
    instructions="""
    Ты агент, специализирующийся на поиске информации в интернете.
    Используй инструменты поиска в интернете для нахождения актуальной информации,
    которая дополняет или расширяет информацию, найденную в файле.
    Предоставляй полную и точную информацию из результатов поиска, указывая источники.
    """,
    tools=[web_search_tool]
)

web_result = await runner.run(
    web_agent, 
    f"""Найди актуальную информацию по запросу: '{query}' в интернете.
    В файле была найдена следующая информация: '{file_result.final_output}'
    Дополни или расширь эту информацию своими находками."""
)

Цены и ограничения:

  • Плата за токены: Стоимость зависит от количества токенов, затраченных на обработку запроса и генерацию ответа. Начинается от 25$ за 1000 обращений для GPT-4o-mini-search. Точные тарифы указаны на странице цен.

  • Зависимость от размера контекста: Выбор search_context_size влияет на стоимость — больший контекст увеличивает расходы. Подробности доступны в разделе цен на сайте OpenAI.

Шаг 4. Итоговый Ответ

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

report_agent = Agent(
    name="ReportGenerator",
    instructions="Ты агент, специализирующийся на формировании итоговых отчетов."
    "Твоя задача - обобщать результаты поиска из файлов и интернета."
    "Структурируй информацию логически, выделяй ключевые моменты."
    "Избегай крупных Заголовков."
    "Приоритезируй информацию из файла, затем дополняй её данными из веб-поиска."
    "Обязательно указывай источники информации (ссылки на файлы и веб-страницы)."
)

file_info = file_result.final_output
report_prompt = f"""
  Сформируй полный и структурированный отчет на основе следующих данных:
  
  ЗАПРОС ПОЛЬЗОВАТЕЛЯ: {query}
  
  ИНФОРМАЦИЯ ИЗ ФАЙЛА (ПРИОРИТЕТНАЯ):
  {file_info}
  
  ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ИЗ ИНТЕРНЕТА:
  {web_result.final_output}
  
  Обязательно укажи все источники информации. Структурируй отчет логически.
  Информация из файла должна быть представлена в первую очередь, а затем дополнена данными из интернета.
"""

report_result = await runner.run(report_agent, report_prompt)

Логирование

Отлично, рабочий пайплайн готов!

Запустить его можно streamlit run app.py

Однако создать агентов мало, важно уметь дебажить и логировать их работу. В OpenAI все схвачено, они берут это на себя.

Итак давайте запустим с тестовым запросом 'Технические характеристики удобрения Вайбранс' и перед просмотром ответа пройдем все шаги подряд через логи.

На влкадке Logs можно посмотреть все обращения агентов по API. Реализовано куча фильтров:

  • Тип API: ChatCompletion или Response.

  • Название Модели.

  • Дата обращения.

  • Метаданные.

  • Вызываемые в запросах тулзы.

    Логи нашего запроса
    Логи нашего запроса

Видим пошагово все вызовы агентов. Можно ткнуть на каждый вызов и посмотреть детали.

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

Tracing

Помимо общего логирования можно настроить трассировку на конкретные процессы. Это позволяет отслеживать взаимодействие агентов и корректность работы всего пайплайна. Сделать это можно просто следующим образом:

from agents import trace

# указываем название Workflow -- Поисковый запрос
with trace("Поисковой запрос"):
  # весь код вызова агентов ниже

Теперь каждый поисковой запрос можно будет так же отследить на вкладке Traces с указанием Workflow.

Проваливаясь внутрь можно посмотреть более детально работу каждого из агентов, время работы, потраченные токены. Из трассировки видно, что больше всего времени занимает WebSearch, порядка 15 секунд, поскольку мы поставили "medium" размер контекста поиска.

Поиск по файлу и другие агенты отрабатывают очень быстро и четко, несколько миллисекунд.

Итоговый результат

На выходе мы получили полный ресерч с данными из файла и интернета за меньше чем 20 секунд.

Результат модели:

Информация о технических характеристиках удобрения "Вайбранс" организована по следующим категориям:

Технические характеристики из файла:

  • Активные вещества: седаксан 50 г/л, мефеноксам 37,5 г/л, флудиоксонил 25 г/л.

  • Формулировка: концентрат суспензии.

  • Фасовка: канистра 5 л / упаковка 4 × 5 л.

  • Срок годности: 3 года со дня изготовления.

  • Класс опасности: 3-й класс.

Дополнительная информация из интернета:

Вайбранс Топ Используется для картофеля.

  • Действующие вещества: тиаметоксам 262,5 г/л, седаксан 25 г/л, флудиоксонил 25 г/л.

  • Форма: концентрат суспензии (КС).

  • Упаковка: канистра 5 л / упаковка 4×5 л.

  • Класс опасности: 3-й класс.

  • Норма расхода: 0,4–0,7 л/т клубней.

  • Спектр действия: колорадский жук, ризоктониоз.

Источник: syngenta.ru

Вайбранс Трио Для озимой и яровой пшеницы, ячменя.

  • Действующие вещества: седаксан 25 г/л, флудиоксонил 25 г/л, тебуконазол 10 г/л.

  • Форма: концентрат суспензии (КС).

  • Спектр действия: корневая гниль, пыльная головня.

  • Норма расхода: 1,5–2,0 л/т семян.

Источник: pr-agro.ru

"Вайбранс" — это линейка протравителей, предназначенных для защиты семян различных культур от широкого спектра болезней и вредителей. Каждый из препаратов имеет свои уникальные характеристики и целевое применение.

Вывод

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

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

В общем надеюсь применить OpenAI Agents SDK уже в реальном проекте и рассказать вам о результатах! До встречи!

P.S.: итоговый код я обернул в Streamlit и получилось небольшое веб-приложение. Код тут.

Теги:
Хабы:
+12
Комментарии3

Публикации

Информация

Сайт
ai.raftds.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
Евгений Кокуйкин