
构建 AI 航班推荐应用程序的分步指南
OpenAI 最近发布了一系列令人兴奋的更新。**其中最引人注目的功能是“函数调用”,它大幅提升了 gpt-4 和 gpt-3.5-turbo 模型的 API 能力。**通过这个功能,用户可以描述函数,并根据输入获取关于这些函数调用的结构化输出,满足之前需要借助外部方法(如 ReACT)才能实现的需求。使用函数调用,用户可以创建聊天机器人,并与自定义工具进行无缝交互。
现在,我们的聊天机器人应用程序可以利用互联网上可执行的“函数”输出来增强对用户查询的响应能力。此外,如果这些功能或 API 提供有价值且推荐度高,则可以通过发布聊天机器人应用程序并与用户进行更加自然交互而赚取佣金,比传统互联网搜索更具商业价值。

在本文中,我将向你介绍如何开发一个由 OpenAI 函数调用、Langchain、Chainlit 和 Tequila API 支持的聊天机器人。该机器人是一个基于 AI 的商业化应用程序,专门为航班推荐而设计。
函数调用的工作原理
在我们深入研究聊天机器人应用程序之前,有必要了解函数调用的概念。语言模型输出作为生成周期后期阶段的输入存在一些限制。确保语言模型能够选择适当的函数并正确使用参数进行调用是具有挑战性的。例如,在某些情况下,最终答案可能需要调用一个通过严格的 HTTP GET 字符串格式从在线服务请求信息的函数。
过去,我们依靠 Langchain 的 ReACT 机制以及代理和工具来解析每个输出,并确保遵循正确结构以运行适当的工具。然而,借助 OpenAI 最近引入的功能,他们直接对模型进行微调,使其能够内部理解外部工具和函数的格式和含义。这样一来,他们就可以生成基于 JSON 格式的函数指令,并无需额外提示或工程工作,在运行时能够无缝地执行相应工具。
现在让我们看看它是如何实际操作的。
假设我们有一个函数可以通过API访问并下载给定日期最便宜航班数据。该函数定义如下:
def get_flight_info(fly_from, fly_to, date):
flight = {"flight_no": "AA1234", "price": 100, "depart_time": date+" 17:23:00"}
return flight
在这个假函数中,fly_from和 fly_to分别表示出发机场和到达机场,date表示搜索日期。返回的 flight对象包含最便宜航班的航班号、价格和出发时间等基本信息。
现在我们来构建 gpt-3.5-turbo-0613模型的提示,其中包括我们需要的新功能。
prompt = [{"role": "system", "content": "You are a helpful assistant. Answer as complete as possible."}]
prompt.append({"role": "user", "content": "Show me the cheapest flight from Berlin to New York in 23 Jul"})
functions = [
{
"name": "get_flight_info",
"description": "Get the info of the cheapest flight for a given date",
"parameters": {
"type": "object",
"properties": {
"fly_from": {
"type": "string",
"description": "the 3-digit code for departure airport"
},
"fly_to": {
"type": "string",
"description": "the 3-digit code for arrival airport"
},
"date": {
"type": "string",
"description": "the dd/mm/yyyy format date for flight search"
},
},
"required": ["fly_from", "fly_to", "date"]
}
}
]
completion=openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages = prompt,
functions = functions
)
在这次新的更新中,我们对 OpenAI API 进行了改进。与之前使用 API 的方式相比,我们添加了一个名为 functions 的新部分。该部分包含函数的名称、描述和参数,以便向模型提供正确的函数用法。
现在,如果我们执行打印输出:
message=completion.choices[0].message.function_call
print(message)
模型生成 JSON 格式文本:
{'name': 'get_flight_info', 'arguments': '{\n "fly_from": "BER",\n "fly_to": "JFK",\n "date": "23/07/2023"\n}'}
在输出中,我们选择了刚刚定义的用于生成搜索结果的函数。我们通过将柏林转换为 BER并填充到 fly_from参数中,将纽约的 JFK填充到 fly_to参数中,并在日期格式中添加 date来附加正确的参数。
有些人可能会问接下来怎么办?
如果你熟悉 Python,并且考虑到这样的结构,你可以轻松地内联执行它以获取你正在查询的结果。
import ast
data_dict = message.to_dict()
# Extract the function name and arguments from the dictionary.
function_name = data_dict['name']
arguments = ast.literal_eval(data_dict['arguments'])
# Get a reference to the function object.
function = globals()[function_name]
# Call the function with the arguments.
result = function(**arguments)
print(result)
使用 GPT 模型提供的三个参数,我们直接执行了预定义的假函数 get_flight_info。从打印结果可以看出,它成功地被调用了。
{'flight_no': 'AA1234', 'price': 100, 'depart_time': '23/07/2023 17:23:00'}
除了函数输出外,OpenAI 的聊天完成 API 还进行了更新,并添加了一个名为 function 的新角色。该角色允许开发人员将函数输出反馈回提示中,以便通过语言模型获取文本摘要。
prompt.append({"role": "function", "name": "get_flight_info", "content": arguments})
completion=openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages = prompt,
functions = functions
)
message = completion.choices[0].message.content
print(message)
输出:
2023 年 7 月 23 日从 柏林 飞往 纽约 最便宜的航班为 AA1234 航班。该航班的价格为 100 美元。出发时间定于2023年7月23日17:23:00。
现在,我们已经了解了 OpenAI 的“函数调用”功能的工作原理。接下来,我们将使用具备“函数调用”更新的 Langchain 和商业化 API 来构建完整的网络聊天机器人应用程序。
航班推荐聊天机器人
首先,让我解释一下为什么此类聊天机器人有可能赚钱。
Kiwi API
**Kiwi.com 是一个在线旅行预订平台,旅行者可以在一个地方搜索和预订航班、火车、巴士和租车。**它于 2012 年在捷克共和国成立,并不断发展壮大,为世界各地的客户提供服务。与 booking.com 等类似的旅行预订网站相比,Kiwi 还提供了名为 Tequila 的合作伙伴解决方案,使企业用户能够将 Kiwi 的内容嵌入到自己的商业应用程序中,并从中赚取佣金。Tequila API 提供了一组适合定制需求的搜索、预订和管理功能。

如果我们能够使用这些 API 和 AI 高级推荐来构建聊天机器人应用程序,那么每笔通过聊天机器人进行的预订交易都会给我们带来3% 的佣金收益。
你可以在此处找到 Taquila 合作伙伴计划的详细信息。https://tequila.kiwi.com/portal/docs/user_guides/kiwi_com_affiliate_program

请注意,在本文中所述聊天机器人仅是技术演示如何将 API 集成到其中以实现简单商业化。要真正开展业务活动,则需要添加更多功能、UI 设计和促销策略。
现在,我们将使用 Kiwi API 定义一个 Python 函数,根据参数提供的搜索条件获取航班列表。
a) API 密钥
为了调用 Kiwi API,你必须拥有 Tequila 帐户才能找到你的 API 密钥。注册帐户后,你可以在 API 文档中找到你的密钥。 https://tequila.kiwi.com/portal/docs/tequila_api/search_api

b) get_flight_in_period()
**搜索函数接受的输入参数包括:fly_from(出发机场)、fly_to(到达机场)、date_from(起始日期)和 date_to(结束日期),以及 sort 参数用于指定排序标准,例如价格、日期或持续时间。**然后,该函数会向 Tequila v2 API 端点发送带有这些参数的 HTTP 请求,并返回一个按照 sort 指定顺序排列的包含 10 条航班信息的列表。
import requests
def get_flight_in_period(fly_from, fly_to, date_from, date_to, sort):
headers = {"apikey": "{Your_API_Key}"}
BASE_URL = f'https://api.tequila.kiwi.com/v2/search?fly_from={fly_from}&fly_to={fly_to}&date_from={date_from}&date_to={date_to}&sort={sort}&limit=10&curr=USD'
response = requests.get(BASE_URL, headers=headers)
response = response.json()
flights = []
for flight in response['data']:
price = flight['price']
duration = flight['duration']['departure']/3600
availability = flight['availability']
routes = flight['route']
detail=''
for idx, route in enumerate(routes):
detail += f"<Stop-{idx}>: {route['airline']}{route['flight_no']}, {route['cityFrom']}/{route['flyFrom']} to {route['cityTo']}/{route['flyTo']}, {route['local_departure']} to {route['local_arrival']} \r\n"
flight_info = {"price":price, "duration": duration, "availability": availability, "route": detail}
flights.append(flight_info)
return flights
输入 API 密钥后调用它来进行快速测试。
flights = get_flight_in_period('BER','NYC', '01/08/2023', '07/08/2023', 'price')
如果你收到类似的内容,Kiwi API 就可以在你的环境中运行。
[{'price': 596, 'duration': 8.916666666666666, 'availability': {'seats': None},
'route': '<Stop-0>: N0601, Berlin/BER to New York/JFK, 2023-08-02T19:05:00.000Z
to 2023-08-02T22:00:00.000Z \r\n'}, {'price': 596, 'duration': 8.916666666666666,
'availability': {'seats': None}, 'route': '<Stop-0>: N0601, Berlin/BER to New York/JFK,
2023-08-04T19:05:00.000Z to 2023-08-04T22:00:00.000Z \r\n'},
....
]
设计概述
该聊天机器人的使用场景非常简单。**它允许用户请求航班列表的推荐,并提供一系列条件,如日期、地点、价格或飞行持续时间。响应将包含票价、总时长、可用座位以及路线信息(包括航班号、位置和时间)。**为了帮助你的用户开始预订,请在每个搜索结果的底部粘贴你的 Kiwi 联属链接。
以下是 8 月第二周从巴黎 (CDG) 飞往 纽约 (JFK) 的 3 个航班选择,飞行时间少于 8.25 小时:
航班 1:
价格:$522
时长:8.25 小时
路线: 巴黎/戴高乐机场至纽约/肯尼迪机场
出发: 2023年8月14日 20:00
抵达时间:2023年8月14日 22:15航班 2:
价格:$559
时长:8.25 小时
路线: 巴黎/戴高乐机场至纽约/肯尼迪机场
出发: 2023年8月10日 20:00
抵达时间:2023年8月10日 22:15航班 3:
价格:$559
时长:8.25 小时
路线: 巴黎/戴高乐机场至纽约/肯尼迪机场
出发:2023年8月9日 20:00
抵达时间:2023年8月9日 22:15点击此链接即可完成你的预订。
该设计的框架图

在这个应用程序设计中,我们使用了一种新型代理 OPENAI_FUNCTIONS。它能够自动执行构造工具中的函数调用过程,并利用外部函数 get_flight_in_period() 的输出来生成最终答案。
Langchain OpenAI 功能代理
Langchain 中的 OpenAI Functions Agent 旨在与 gpt-3.5-turbo-0613 和 gpt-4–0613 等模型配合使用,自动完成从工具描述到 JSON 生成再到函数调用的任务。请确保你已安装或升级至最新版本的 Langchain 软件包。
要进行安装或升级,请运行以下命令:
!pip install langchain --upgrade
a) 工具创建
创建工具时,请按照正常的 Langchain 流程进行操作。你需要定义一个名为 GetflightInPeriodTool 的类,该类继承自 BaseTool,并在其中注册 name、description、_run()和 _arun()方法。此外,还需使用GetflightInPeriodCheckInput()函数对输入参数进行格式和存在性检查。
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from typing import Optional, Type
class GetflightInPeriodCheckInput(BaseModel):
fly_from: str = Field(..., description="the 3-digit code for departure airport")
fly_to: str = Field(..., description="the 3-digit code for arrival airport")
date_from: str = Field(..., description="the dd/mm/yyyy format of start date for the range of search")
date_to: str = Field(..., description="the dd/mm/yyyy format of end date for the range of search")
sort: str = Field(..., description="the catagory for low-to-high sorting, only support 'price', 'duration', 'date'")
class GetflightInPeriodTool(BaseTool):
name = "get_flight_in_period"
description = """Useful when you need to search the flights info. You can sort the result by "sort" argument.
if there is no year, you need to use 2023 for search.
Try to understand the parameters of every flight
---
At the end of your response, include these sentences:‘Click on this link: https://www.kiwi.com/ to make a reservation.’
"""
def _run(self, fly_from: str, fly_to: str, date_from: str, date_to: str, sort: str):
get_flight_in_period_response = get_flight_in_period(fly_from, fly_to, date_from, date_to, sort)
return get_flight_in_period_response
def _arun(self, fly_from: str, fly_to: str, date_from: str, date_to: str, sort: str):
raise NotImplementedError("This tool does not support async")
args_schema: Optional[Type[BaseModel]] = GetflightInPeriodCheckInput
在 description提示词结尾附带自己的推荐链接:
"""
---
At the end of your response, include these sentences:‘Click on this link: https://www.kiwi.com/ to make a reservation.’
"""
b) 代理创建
在成功创建了一个可以从 Kiwi API 请求航班信息的工具之后,下一步是简单地使用它来创建一个 Langchain 代理。
你只需导入 Langchain 方法并提供你的 OpenAI API 密钥即可。
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
import os
os.environ["OPENAI_API_KEY"] = "{Your_API_Key}"
将 GetflightInPeriodTool() 函数添加到 tools 列表中,并使用 ChatOpenAI() 创建一个基于 gpt-3.5-turbo-0613 模型的语言模型。请注意,我们需要选择 OPENAI_FUNCTIONS 作为代理类型,以启用函数调用功能。
tools = [GetflightInPeriodTool()]
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo-0613")
open_ai_agent = initialize_agent(tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True)
如果你想查看 OpenAI 格式的函数结构(如我们上一个演示中所示),有一个名为 format_tool_to_openai_function 的实用函数非常适合你。它可以将 Langchain Tool 对象转换为 OpenAI 的函数格式。
from langchain.tools import format_tool_to_openai_function
functions = [format_tool_to_openai_function(t) for t in tools]
print(functions[0])
用于 Web UI 的 Chainlit
最后一步是将所有的后端逻辑嵌入到 Web UI 中,以提供给用户一个漂亮的聊天机器人界面和友好的交互。我会继续使用 Chainlit 来进行我的演示,因为它对于聊天机器人开发非常方便。
安装包:
!pip install chainlit
将代理初始化代码包装到 Chainlit 装饰器 cl.langchain_factory 和处理函数 agent()中。请注意,最新版本的 Chainlit 需要强制参数 use_async 才能启用代理的异步运行。在此应用程序中,我们禁用了异步功能。
@cl.langchain_factory(use_async=False)
def agent():
tools = [GetflightInPeriodTool()]
functions = [format_tool_to_openai_function(t) for t in tools]
print(functions[0])
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo-0613")
open_ai_agent = initialize_agent(tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True)
return open_ai_agent
就这样?是的,仅此而已。
就像我们对所有 Chainlit 应用程序所做的那样,在终端中输入以下命令后:
!chainlit run flight_chatbot.py

你的浏览器将自动打开一个新选项卡,显示你的聊天机器人。你也可以手动在默认地址 localhost:8000上打开它。如需了解更多 Chainlit 的使用,请查看其官方文档:docs.chainlit.io。

输入此条查询:
I want to travel to New York from Berlin in the first week of Aug, please list me 3 cheap flights but with time less than 20 hours


翻译为中文进行查询:
我想在八月的第一周从柏林到纽约旅行,请给我列出三个价格便宜且飞行时间少于20小时的航班

如果你在聊天机器人的回复中附加一条相关预订链接,例如包含会员 ID 的预订链接,那么当用户完成机票预订流程后,你就可以开始赚取佣金。
完整代码分享在这里:
import requests
from langchain.tools import BaseTool
from typing import Optional, Type
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
from pydantic import BaseModel, Field
from langchain.tools import format_tool_to_openai_function
import os
os.environ["OPENAI_API_KEY"] = "{Your_OpenAI_API_Key}"
import chainlit as cl
def get_flight_in_period(fly_from, fly_to, date_from, date_to, sort):
print(f'function calling: {fly_from}, {fly_to}, {date_from}, {date_to}, {sort}' )
headers = {"apikey": "{Your_Kiwi_API_Key}"}
BASE_URL = f'https://api.tequila.kiwi.com/v2/search?fly_from={fly_from}&fly_to={fly_to}&date_from={date_from}&date_to={date_to}&sort={sort}&limit=10&curr=USD'
response = requests.get(BASE_URL, headers=headers)
response = response.json()
flights = []
for flight in response['data']:
price = flight['price']
duration = flight['duration']['departure']/3600
availability = flight['availability']
routes = flight['route']
detail=''
for idx, route in enumerate(routes):
detail += f"<Stop-{idx}>: {route['airline']}{route['flight_no']}, {route['cityFrom']}/{route['flyFrom']} to {route['cityTo']}/{route['flyTo']}, {route['local_departure']} to {route['local_arrival']} \r\n"
flight_info = {"price":price, "duration": duration, "availability": availability, "route": detail}
flights.append(flight_info)
return flights
class GetflightInPeriodCheckInput(BaseModel):
fly_from: str = Field(..., description="the 3-digit code for departure airport")
fly_to: str = Field(..., description="the 3-digit code for arrival airport")
date_from: str = Field(..., description="the dd/mm/yyyy format of start date for the range of search")
date_to: str = Field(..., description="the dd/mm/yyyy format of end date for the range of search")
sort: str = Field(..., description="the category for low-to-high sorting, only support 'price', 'duration', 'date'")
class GetflightInPeriodTool(BaseTool):
name = "get_flight_in_period"
description = """Useful when you need to search the flights info. You can sort the result by "sort" argument.
You need to consider 2023 for default year of search.
Try to understand the parameters of every flight
---
At the end of your response, include these sentences:‘Click on this link: https://www.kiwi.com/ to make a reservation.’
"""
def _run(self, fly_from: str, fly_to: str, date_from: str, date_to: str, sort: str):
get_flight_in_period_response = get_flight_in_period(fly_from, fly_to, date_from, date_to, sort)
return get_flight_in_period_response
def _arun(self, fly_from: str, fly_to: str, date_from: str, date_to: str, sort: str):
raise NotImplementedError("This tool does not support async")
args_schema: Optional[Type[BaseModel]] = GetflightInPeriodCheckInput
@cl.langchain_factory(use_async=False)
def agent():
tools = [GetflightInPeriodTool()]
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo-0613")
open_ai_agent = initialize_agent(tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True)
return open_ai_agent
最后的话
总结起来,我希望这篇文章能给大家带来一些启示。虽然这篇文章是我翻译自国外大佬的作品,我自己对编程有一定的基础,并成功运行了这个程序,但我并没有足够的能力去将它发展为一个完全商业化的产品。这里,我特别希望那些有深厚编程技能的朋友们能仔细研究这个项目,并探讨其商业化的可能性。
如果你对此有兴趣,我非常欢迎一起交流探讨。一起发掘这个项目的潜力,并探索 AI 和编程如何帮助我们解决现实问题,创造新的商业机会。

参考链接: