Создание интеллектуального рекомендателя для путешествий с использованием LangChain, Google Maps API и Gradio (Часть 3)

Создание интеллектуального путеводителя для путешествий с использованием LangChain, Google Maps API и Gradio (Часть 3)

Узнайте, как создать приложение, которое может вдохновить вашу следующую дорожную поездку

Эта статья является последней в трехчастной серии, в которой мы создаем приложение-помощник для составления путевого листа с использованием OpenAI и Google APIs и отображаем его в простом пользовательском интерфейсе, созданном с помощью Gradio. В этой части мы обсудим, как создать этот пользовательский интерфейс, объединив агентов и модули поиска маршрутов, созданные в первой и второй части. Просто хотите посмотреть код? Найдите его здесь.

1. Резюме второй части

Во второй части этой трехчастной серии мы построили систему, которая принимает разобранный список точек маршрута из набора вызовов LLM (часть 1) и использует API Google Maps и Folium для генерации маршрута между ними и его отображения на интерактивной карте. Напомним, что нашей целью в этом проекте является создание приложения, которое позволяет кому-то легко ввести путевой запрос вроде «Четырехдневная поездка из Берлина в Цюрих, где я попробую местное пиво и еду» и возвращает подробное путевое описание вместе с картой для исследования. Благодаря первой и второй частям у нас есть все необходимые компоненты, теперь мы просто должны объединить их в пользовательский интерфейс, который облегчит использование.

2. Соединение карт с Gradio

Gradio – отличная библиотека для быстрой разработки интерактивных приложений, которая может показывать модели машинного обучения. В ней есть компонент gradio.Plot, который предназначен для работы с Matplotlib, Bohkeh и Plotly (подробнее здесь). Однако карты, которые мы создавали в Части 2, сделаны с помощью folium. Возможно, было бы возможно создать их заново с использованием одной из этих других библиотек, но, к счастью, нам не нужно. Вместо этого мы можем использовать пакет leafmap, который позволяет нам повторно использовать уже имеющийся код folium и преобразовывать его в html-файлы, которые можно понять Gradio. Подробности можно найти здесь.

Давайте рассмотрим простой пример, чтобы узнать, как это работает. Сначала мы создадим функцию, которая выводит html в нужном формате:

import leafmap.foliumap as leafmapimport foliumimport gradio as grdef generate_map(center_coordinates, zoom_level):    coords = center_coordinates.split(",")    lat, lon = float(coords[0]), float(coords[1])    map = leafmap.Map(location=(lat,lon), tiles="Stamen Terrain", zoom_start=zoom_level)    return map.to_gradio()

Здесь функция generate_map принимает строку с координатами в формате “широта,долгота” и уровень масштабирования для folium-карты. Она генерирует карту и преобразует ее в формат, который понятен Gradio.

Затем давайте создадим простой пользовательский интерфейс Gradio для отображения нашей карты:

demo = gr.Blocks()with demo:    gr.Markdown("## Сгенерировать карту")    with gr.Row():      with gr.Column():        # первая колонка для кнопок        coordinates_input = gr.Textbox(value="",label="Ваши центральные координаты",lines=1)        zoom_level_input = gr.Dropdown(choices=[1,2,3,4,5,6,7,8,9],label="выберите уровень масштабирования")        map_button = gr.Button("Сгенерировать карту")      with gr.Column():        # вторая колонка для карты        map_output = gr.HTML(label="Путевая карта")    map_button.click(generate_map, inputs=[coordinates_input,zoom_level_input], outputs=[map_output])# запустите это в блокноте, чтобы отобразить пользовательский интерфейсdemo.queue().launch(debug=True)

Здесь мы используем API Blocks, который дает нам гибкость в настройке пользовательского интерфейса нашего приложения. Мы создаем одну строку компонентов с двумя столбцами. Первый столбец содержит три элемента: текстовое поле, в которое пользователь вводит желаемые центральные координаты, выпадающий список для выбора уровня масштабирования и кнопку «Сгенерировать карту», на которую им нужно нажать.

Во втором столбце у нас есть map_output, который является компонентом gradio.HTML() и будет отображать html-код карты.

Затем все, что нам нужно сделать, это определить, что происходит, когда нажимается map_button. Когда это происходит, мы запустим функцию generate_map, передавая выбранные значения из coordinates_input и zoom_input. Результат будет отправлен в переменную map_output.

При выполнении этого будет создано следующее пользовательское интерфейсное приложение:

Основной интерфейс создания карты с помощью leafmap и gradio

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

3. Простой интерфейс для нашего туристического агента

Давайте сначала рассмотрим некоторые функции приложения gradio для нашего приложения “travel mapper”, прежде чем мы рассмотрим код. Однако имейте в виду, что у gradio есть большое разнообразие доступных компонентов для создания сложных и красивых пользовательских интерфейсов, и этот пользовательский интерфейс travel mapper все еще находится на этапе POC.

Описание всех компонентов в окончательном приложении gradio для travel mapper

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

Вторая колонка содержит карту, созданную с помощью leafmap.folium, и текстовое поле, которое отображает полный текстовый вывод маршрута от вызова LLM. Кнопка “создать карту” находится внизу, за пределами экрана на скриншоте выше.

Код для всего этого является замечательно лаконичным благодаря всей работе, которая выполняется в фоновом режиме gradio.

import gradio as grfrom travel_mapper.TravelMapper import TravelMapperForUI, load_secrets, assert_secretsfrom travel_mapper.user_interface.utils import generate_generic_leafmapfrom travel_mapper.user_interface.constants import EXAMPLE_QUERYdef main():        # загрузка ключей API    secrets = load_secrets()    assert_secrets(secrets)        # установка картографа (см. часть 2)    travel_mapper = TravelMapperForUI(        openai_api_key=secrets["OPENAI_API_KEY"],        google_maps_key=secrets["GOOGLE_MAPS_API_KEY"],        google_palm_api_key=secrets["GOOGLE_PALM_API_KEY"],    )    # построение пользовательского интерфейса в gradio    app = gr.Blocks()      # создание общей карты для отображения при первой загрузке приложения     generic_map = generate_generic_leafmap()    with app:        gr.Markdown("## Создание туристических предложений")                # создание нескольких вкладок        with gr.Tabs():            # создание первой вкладки            with gr.TabItem("Создать с картой"):                # создание строк внутри первой вкладки                with gr.Row():                    # создание первой колонки в строке 1                    with gr.Column():                        text_input_map = gr.Textbox(                            EXAMPLE_QUERY, label="Запрос", lines=4                        )                        radio_map = gr.Radio(                            value="gpt-3.5-turbo",                            choices=["gpt-3.5-turbo", "gpt-4", "models/text-bison-001"],                            label="Модели",                        )                        query_validation_text = gr.Textbox(                            label="Информация о проверке запроса", lines=2                        )                    # создание второй колонки в строке 1                    with gr.Column():                        # место, где появится карта                        map_output = gr.HTML(generic_map, label="Карта")                        # место, где появится предложение по маршруту                        itinerary_output = gr.Textbox(                            value="Здесь появится ваш маршрут",                            label="Предложение по маршруту",                            lines=3,                        )                # кнопка создания карты                map_button = gr.Button("Создать")            # создание второй вкладки            with gr.TabItem("Создать без карты"):                # создание первой строки второй вкладки                with gr.Row():                    # создание первой колонки в строке 1                    with gr.Column():                        text_input_no_map = gr.Textbox(                            value=EXAMPLE_QUERY, label="Запрос", lines=3                        )                        radio_no_map = gr.Radio(                            value="gpt-3.5-turbo",                            choices=["gpt-3.5-turbo", "gpt-4", "models/text-bison-001"],                            label="Модели",                        )                        query_validation_no_map = gr.Textbox(                            label="Информация о проверке запроса", lines=2                        )                    # создание второй колонки в строке 1                    with gr.Column():                        text_output_no_map = gr.Textbox(                            value="Здесь появится ваш маршрут",                            label="Предложение по маршруту",                            lines=3,                        )                # кнопка создания текста                text_button = gr.Button("Создать")        # инструкции о том, что происходит при нажатии кнопок         # обратите внимание на использование метода "generate_with_leafmap" здесь.         map_button.click(            travel_mapper.generate_with_leafmap,            inputs=[text_input_map, radio_map],            outputs=[map_output, itinerary_output, query_validation_text],        )        text_button.click(            travel_mapper.generate_without_leafmap,            inputs=[text_input_no_map, radio_no_map],            outputs=[text_output_no_map, query_validation_no_map],        )        # запуск приложения    app.launch()

4. Создание пакета

Как можно увидеть, изучая репозиторий на GitHub, код travel mapper структурирован с помощью стандартного шаблона cookiecutter, но некоторые важные части шаблона еще не заполнены. Идеальным решением было бы включить модульные и интеграционные тесты, а также завершить настройку репозитория, чтобы использовать концепции непрерывной интеграции/непрерывной доставки (CI/CD). Если проект будет развиваться дальше этой стадии POC, эти аспекты будут добавлены в будущем.

Существует несколько способов запуска кода локально. Если мы поместим функцию main в блок выше в скрипт с названием driver.py, мы сможем запустить его из верхнего уровня проекта travel_mapper из терминала. Если пакет успешно запускается, в терминале должно появиться сообщение, подобное этому

Запуск на локальном URL:  http://127.0.0.1:7860

Скопируйте этот URL и вставьте его в веб-браузер, чтобы отобразить приложение Gradio, работающее локально на вашем компьютере. Конечно, если бы мы хотели развернуть это в Интернете (что я не рекомендую из-за затрат, связанных с вызовами API), потребуется еще несколько шагов, но это выходит за рамки этих статей.

Драйвер также может быть запущен с помощью bash-скрипта с названием run.sh, который можно найти в модуле user_interface кодовой базы.

# Запуск пользовательского интерфейса# запускайте это из верхнего уровня директории проекта travel mapperexport PYTHONPATH=$PYTHONPATH:$(pwd)echo "Запуск travel mapper UI"$(pwd)/travel_mapper/user_interface/driver.py

При запуске из верхнего уровня проекта это также правильно устанавливает PYTHONPATH, чтобы всегда распознавались специфичные для проекта операторы импорта.

Это все для серии, и спасибо за то, что добрались до конца! Пожалуйста, не стесняйтесь изучать полную кодовую базу здесь https://github.com/rmartinshort/travel_mapper. Любые предложения по улучшению или расширению функциональности будут очень оценены!