使用LangChain、Google Maps API和Gradio构建智能旅行行程建议工具(第3部分)

“打造智能旅行行程建议工具:LangChain、Google Maps API和Gradio的结合(第3部分)”

学习如何构建一个可能激发您下一次公路旅行灵感的应用程序

This article is the final one in a three part series where we build a travel itinerary suggester application using OpenAI and Google APIs and display it in a simple UI generated with gradio. In this part, we discuss how to build that UI, putting together the Agent and RouteFinder modules that we built in parts 1 and 2. Just want see the code? Find it here.

1. 第2部分回顾

在这个三部分系列的第二部分中,我们构建了一个使用OpenAI和Google APIs构建旅行行程建议应用程序,并在使用gradio生成的简单UI中展示它们。在本part中,我们讨论如何构建该UI,将我们在第1部分和第2部分中构建的Agent和RouteFinder模块组合在一起。只想看代码?在这里找到它

2. 将地图连接到gradio

gradio是一个出色的库,可以快速构建展示机器学习模型的交互式应用程序。它具有与Matplotlib、Bohkeh和Plotly兼容的gradio.Plot组件(详细信息在这里)。然而,我们在第二部分中生成的地图是使用folium制作的。我们可以用这些其他库之一重新制作它们,但我们不需要这样做。相反,我们可以使用leafmap软件包,它允许我们重用已经有的folium代码,并将其强制转换为gradio可以理解的html格式。详情可以在这里找到。

让我们看一个简单的示例,看看它是如何工作的。首先,我们将创建一个以所需格式输出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接受一个格式为”lat,lon”的坐标字符串和folium地图的缩放级别。它生成地图并将其转换为gradio可以读取的格式。

接下来,让我们构建一个非常简单的gradio界面来显示我们的地图

demo = gr.Blocks()with demo:    gr.Markdown("## Generate a map")    with gr.Row():      with gr.Column():        # 第一列是按钮        coordinates_input = gr.Textbox(value="",label="Your center coordines",lines=1)        zoom_level_input = gr.Dropdown(choices=[1,2,3,4,5,6,7,8,9],label="choose zoom level")        map_button = gr.Button("Generate map")      with gr.Column():        # 第二列是地图        map_output = gr.HTML(label="Travel map")    map_button.click(generate_map, inputs=[coordinates_input,zoom_level_input], outputs=[map_output])# 在笔记本中运行以显示界面demo.queue().launch(debug=True)

在这里,我们使用了Blocks API,它使我们能够灵活地设置应用程序的UI。我们创建了一行组件,有两列。第一列包含三个元素:一个文本框,用户可以输入所需的中心坐标,一个下拉列表框,用于选择缩放级别,并一个名为“生成地图”的按钮,用户需要点击它。

在第二列中,我们有一个map_output,这是一个gradio.HTML()组件,将显示地图html。

然后,我们所需要做的就是定义当map_button被点击时发生的事情。在这种情况下,我们将运行generate_map函数,将从coordinates_inputzoom_input中选择的值传递进去。结果将发送到map_output变量。

运行后,会生成以下用户界面:

使用leafmap和gradio生成的基本地图UI

这个界面虽然没有太多精细或合理布局的地方,但它包含了使用gradio构建地图工具所需的基本元素。

3. 一个简单的旅行顾问用户界面

在我们检查代码之前,让我们先看看旅行顾问gradio应用程序的一些特点。请牢记,gradio提供了大量可用于创建复杂和美观用户界面的组件,而这个旅行顾问界面仍然处于原型阶段。

最终旅行顾问gradio应用程序中所有组件的描述

我们的应用程序基本上有两列。第一列包含一个文本框,供用户输入查询内容,一组单选按钮,允许我们在模型之间切换,以及一个文本框,显示验证检查的输出。

第二列包含地图(使用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)        # 设置旅行顾问(见第二部分)    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():                    # 创建在第一行中的第一列                    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                        )                    # 创建在第一行中的第二列                    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():                    # 在第一行中创建第一列                    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                        )                    # 在第一行中创建第二列                    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上的存储库可以看出,旅行地图代码是使用cookiecutter提供的标准模板进行结构化的,但模板的一些重要部分尚未填写。理想情况下,我们应该包括单元测试和集成测试,并完成存储库的设置,以便使用持续集成/持续交付(CI/CD)的概念。如果此项目在完成POC阶段后继续发展,将来还会添加这些方面。

代码可以通过几种方式在本地运行。如果我们将上面的main函数放入名为driver.py的脚本中,我们应该可以从终端的travel_mapper项目的顶级目录中运行它。如果软件包成功运行,则终端会显示以下消息:

Running on local URL:  http://127.0.0.1:7860

将此网址复制粘贴到Web浏览器中,应在本机上显示出gradio应用程序运行。当然,如果我们真的想要将其部署到Web上(由于API调用所产生的成本,我不建议这样做),还需要更多的步骤,但这超出了这些文章的范围。

驱动程序还可以从名为run.sh的bash脚本中运行,该脚本可以在代码库的user_interface模块中找到。

# Run the UI# run this from the top level directory of the travel mapper projectexport PYTHONPATH=$PYTHONPATH:$(pwd)echo "Starting travel mapper UI"$(pwd)/travel_mapper/user_interface/driver.py

从项目的顶级目录运行时,这也会正确设置PYTHONPATH,以便始终识别项目特定的导入语句。

系列文章到此结束,感谢您一直阅读到最后!请随时在此处https://github.com/rmartinshort/travel_mapper探索完整的代码库。如有任何改进或功能扩展的建议,将不胜感激!