使用OpenAI的函数调用功能改变数据管道:使用PostgreSQL和FastAPI实现电子邮件发送工作流程
介绍
人工智能的激动人心的世界通过OpenAI最新的大型语言模型(LLM)中引入的函数调用功能又迈出了一步。这个新功能增强了人与人工智能之间的互动,将其从简单的问答格式转变为更具动态性和活跃性的对话。
但这些函数调用功能究竟是什么?它们的核心是,它们允许LLM在对话过程中根据输入指令调用预定义的函数。这可以是任何事情,从发送电子邮件到根据对话的上下文从数据库中获取数据。使用函数调用功能的好处和应用是广泛的。它显着增加了人工智能在各种应用中的动态实用性,从客户服务到我们构建数据管道的方式。
想象一下一个客户服务人工智能,它可以回答问题并执行诸如预约、将信息发送到电子邮件地址或实时更新客户详细信息等操作。或者考虑一个数据管道,LLM代理可以根据命令获取、更新和操作数据。
在接下来的章节中,我们将进一步探讨这些应用,并提供一步一步的指南,介绍如何通过构建一个电子邮件发送管道来利用这个新的功能。
代码如往常一样在我的Github上可用。
理解新的函数调用功能并将其与LangChain进行比较
我们想要了解OpenAI新引入的函数调用功能为人工智能竞赛带来了什么。为此,让我们了解它与市场上其他工具和框架(如LangChain)的区别。我们在本系列文章的第一篇文章中已经介绍了LangChain。它是一个用于开发基于人工智能的应用程序的流行框架。函数调用功能和LangChain为桌子带来了独特的优势和能力,旨在使人工智能更易用、更通用和更动态。
我们已经知道函数调用功能为人工智能应用程序增加了额外的交互层。它使模型能够在对话中调用预定义的函数,增强LLM的动态性和反应性。这个新功能有可能简化向人工智能应用程序添加功能的过程。开发人员需要定义这些函数,然后模型可以根据上下文在对话中执行它们。这里的主要优点是它与OpenAI模型的直接集成,这有助于开发人员快速设置、更低的学习曲线,因为他们熟悉OpenAI生态系统。
另一方面,LangChain提供了一个全面而通用的框架,旨在开发更复杂、数据感知和代理人人工智能应用程序。它允许语言模型与其环境进行交互,并根据高级指令做出决策。它的模块提供了抽象和标准接口,用于构建应用程序,包括模型、提示、内存、索引、链、代理和回调。
LangChain的方法有助于构建应用程序,其中LLM可以在不同的交互中保持其状态,对不同的实用程序进行序列和链式调用,甚至与外部数据源进行交互。我们可以将这些看作是函数调用功能的类固醇。因此,它特别适用于开发人员构建复杂的、多步骤的应用程序,利用语言模型。增加的复杂性的缺点是它创造了一个更陡峭的学习曲线,以完全使用它。
用例-转换数据管道
在我看来,新的LLM函数调用功能在数据管道中是最令人兴奋的应用领域之一。数据管道是收集、处理和分发数据的任何数据驱动组织的关键组成部分。通常,这些过程是静态的、预定义的,并需要手动干预进行任何更改或更新。这就是我们上面讨论的LLM的动态行为所创造的机会。
传统上,数据库查询需要特定的查询语言(如SQL)的知识。有了LLM调用函数、服务和数据库的能力,用户可以在对话中轻松检索数据,而无需明确的查询公式。LLM可以将用户的请求翻译成数据库查询,获取数据,并以用户友好的格式返回,所有这些都是实时的。这个功能可以在组织内不同角色之间民主化数据访问。
另一个可能会改变的方面是数据转换。在分析之前,通常需要进行单独的数据清洗和处理步骤。通过根据用户的指令交互式地执行数据清洗和操作任务,LLM可以简化这个过程。此外,通过在对话期间操作实时数据,可以进行更多探索性和迭代式数据分析。
第三个用例是数据监控。它涉及定期检查数据管道中数据的准确性和一致性。使用LLMs,监控任务可以变得更加交互式和高效。例如,LLM可以在对话期间提醒用户数据不一致,并立即采取纠正措施。
最后,LLMs还可以自动创建和分发数据报告。用户可以指示LLM基于特定条件生成报告,LLM可以获取数据,创建报告,甚至将其发送给相关收件人。
使用OpenAI函数调用功能构建电子邮件发送管道
我们的目标是创建一个向用户发送电子邮件的管道。虽然这听起来很简单,但这个过程的美妙之处在于LLM控制的不同组件之间的相互作用。AI模型不仅仅生成电子邮件正文;它动态地与数据库交互,检索用户信息,组合出上下文适当的电子邮件,然后指示服务发送电子邮件。
我们的管道由三个主要组件组成:PostgreSQL、FastAPI和OpenAI LLM。我们使用PostgreSQL存储用户数据。这些数据包括用户姓名和与之关联的电子邮件地址。它作为我们的用户信息的真实来源。FastAPI是用Python构建API的现代、高性能Web框架。我们使用FastAPI服务模拟发送电子邮件的过程。当服务收到发送电子邮件的请求时,它将返回一个响应,确认已发送电子邮件。LLM作为整个过程的协调器。它控制着对话,根据上下文确定必要的行动,与PostgreSQL数据库交互以获取用户信息,构建电子邮件消息,并指示FastAPI服务发送电子邮件。
实现PostgreSQL数据库
我们管道的第一个组件是PostgreSQL数据库,我们在其中存储用户数据。使用Docker可以轻松、可复制地设置PostgreSQL实例,Docker是一个允许我们将数据库环境容器化和隔离的平台。
要设置PostgreSQL Docker容器,首先需要安装Docker。安装后,可以拉取PostgreSQL镜像并将其作为容器运行。我们将容器的端口5432映射到主机的端口5432以访问数据库。在生产环境中,请将密码设置为环境变量,不要像下面的命令那样直接设置密码。我们这样做是为了加快我们的进程。
docker run --name user_db -e POSTGRES_PASSWORD=testpass -p 5432:5432 -d postgres
有了我们的PostgreSQL实例运行,我们现在可以创建一个数据库和一个表来存储我们的用户数据。我们将使用一个初始化脚本来创建一个带有用户名和电子邮件列的users
表,并使用一些虚拟数据填充它。将此脚本放置在一个目录中,然后将该目录映射到容器中的/docker-entrypoint-initdb.d
目录中。PostgreSQL在启动时执行在该目录中找到的任何脚本。下面是脚本(user_init.sql
)的内容:
CREATE DATABASE user_database;\c user_database;CREATE TABLE users ( username VARCHAR(50), email VARCHAR(50));INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com'), ('user2', 'user2@example.com'), ('user3', 'user3@example.com'), ... ('user10', 'user10@example.com');
LLM能够理解SQL命令,可以用于查询PostgreSQL数据库。例如,如果您要求LLM发送电子邮件给user10
,LLM可以制定以下查询:
SELECT email FROM users WHERE username=’user10';
这使它可以从users
表中获取user10
的电子邮件地址。LLM随后可以使用此电子邮件地址指示FastAPI服务发送电子邮件。
在下一部分中,我们将指导您实现发送电子邮件的FastAPI服务。
创建FastAPI电子邮件服务
我们的第二个组件是一个FastAPI服务。该服务将模拟发送电子邮件的过程。它是一个简单的API,接收包含收件人姓名、电子邮件和电子邮件正文的POST请求。它将返回确认已发送电子邮件的响应。我们将再次使用Docker来确保我们的服务是隔离和可重复的。
首先,您需要安装Docker(如果尚未安装)。然后,为您的FastAPI服务创建一个新目录并进入该目录。在此处,创建一个新的Python文件(例如,main.py
),并添加以下代码:
from fastapi import FastAPIfrom pydantic import BaseModelapp = FastAPI()class User(BaseModel): name: str email: str body: str@app.post("/send_email")async def send_email(user: User): return { "message": f"电子邮件已成功发送至{user.name},电子邮件地址为{user.email}。电子邮件正文:\n\n{user.body}" }
这段代码定义了一个包含单个端点/send_email/
的FastAPI应用程序。该端点接受POST请求并期望包含收件人姓名、电子邮件和电子邮件正文的JSON正文。
接下来,在相同的目录中创建一个Dockerfile文件,其内容如下:
FROM python:3.9-slim-busterWORKDIR /appADD . /appRUN pip install --no-cache-dir fastapi uvicornEXPOSE 1000CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "1000"]
此Dockerfile指示Docker基于python:3.9-slim-buster
镜像创建一个镜像,该镜像是运行python应用程序的轻量级镜像。然后,将我们的main.py
文件复制到镜像中的/app/
目录中。
您可以使用以下命令构建Docker镜像:
docker build -t fastapi_email_service .
然后使用以下命令运行它:
docker run -d -p 1000:1000 fastapi_email_service
LLM使用POST请求与FastAPI服务交互。当LLM决定发送电子邮件时,它生成对send_email
函数的函数调用。这个函数调用的参数包含姓名、电子邮件和电子邮件正文。
我们的Python脚本处理函数调用,提取函数参数并使用它们发送POST请求到FastAPI服务。FastAPI服务响应一个消息,指示电子邮件已成功发送。
现在,我们拥有了整个管道的所有组件。下一部分将把所有内容联系在一起,说明LLM如何编排与PostgreSQL数据库和FastAPI服务的交互以发送电子邮件。
与OpenAI LLM集成
我们管道的最后一部分是OpenAI LLM集成。LLM作为编排器,解释我们的命令,查询用户信息的数据库并指示FastAPI服务发送电子邮件。
我们的脚本使用OpenAI的API来进行基于聊天的完成与LLM。每个完成请求由一系列消息和可选的函数规范列表组成,模型可以调用这些规范。我们从用户消息开始对话,为助手提供提示。
这是我们用于向API发送请求的chat_completion_request
函数:
@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))def chat_completion_request(messages, functions=None, model=GPT_MODEL): headers = { "Content-Type": "application/json", "Authorization": "Bearer " + openai.api_key, } json_data = {"model": model, "messages": messages} if functions is not None: json_data.update({"functions": functions}) response = requests.post( "https://api.openai.com/v1/chat/completions", headers=headers, json=json_data, ) return response
我们使用Chat
类来管理对话历史记录。它具有将新消息添加到历史记录并显示整个对话的方法:
class Chat: def __init__(self): self.conversation_history = [] def add_prompt(self, role, content): message = {"role": role, "content": content} self.conversation_history.append(message) def display_conversation(self): for message in self.conversation_history: print(f"{message['role']}: {message['content']}")
在我们的用例中,LLM需要与我们的PostgreSQL数据库和FastAPI服务进行交互。我们定义这些函数并将它们包含在我们的完成请求中。以下是我们如何定义sql_query_email
和send_email
函数:
functions = [ { "name": "send_email", "description": "发送新电子邮件", "parameters": { "type": "object", "properties": { "to": { "type": "string", "description": "电子邮件的目标地址。", }, "name": { "type": "string", "description": "将收到电子邮件的人的姓名。", }, "body": { "type": "string", "description": "邮件正文。", }, }, "required": ["to", "name", "body"], }, }, { "name": "sql_query_email", "description": "查询用户邮件的SQL查询", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "获取用户电子邮件的查询。", }, }, "required": ["query"], }, },]
当我们发出完成请求时,LLM会响应其预期的操作。如果响应包含函数调用,则我们的脚本会执行该函数。例如,如果LLM决定调用sql_query_email
函数,我们的脚本将从数据库中检索用户的电子邮件,然后将结果添加到对话历史记录中。当调用send_email
函数时,我们的脚本将使用FastAPI服务发送电子邮件。
我们的脚本的主循环检查LLM响应中的函数调用并相应地采取行动:
chat = Chat()chat.add_prompt("user", "向user10发送一封电子邮件,告诉他需要支付月度订阅费。")result_query = ''for i in range(2): chat_response = chat_completion_request( chat.conversation_history, functions=functions ) response_content = chat_response.json()['choices'][0]['message'] if 'function_call' in response_content: if response_content['function_call']['name'] == 'send_email': res = json.loads(response_content['function_call']['arguments']) send_email(res['name'], res['to'], res['body']) break elif response_content['function_call']['name'] == 'sql_query_email': result_query = query_db(json.loads(response_content['function_call']['arguments'])['query']) chat.add_prompt('user', str(result_query)) else: chat.add_prompt('assistant', response_content['content'])
当我们运行脚本时,我们得到以下输出:
{ "message": "电子邮件已成功发送到用户10,其电子邮件为user10@example.com。", "电子邮件正文": "\n\n亲爱的用户10,\n\n这是一个提醒,您的月度订阅费已到期。请尽快支付费用以确保服务不中断。感谢您的合作。\n\n最好的问候,\n您的订阅服务团队"}
让我们分解一下我们得到这个输出的过程。我们的提示是“向user10发送一封电子邮件,告诉他需要支付月度订阅费。”请注意,我们的消息中没有关于user10
电子邮件的信息。LLM识别了缺失的信息,并理解到我们的query_email
函数将允许它查询数据库以获取该用户的电子邮件。获取电子邮件后,它再次做对了两件事:首先,它应该生成电子邮件的正文,其次,它应该调用send_email
函数来使用FastAPI电子邮件服务触发电子邮件。
结论
本文通过实现一个案例研究,探讨了函数调用特性,其中LLM协调了涉及PostgreSQL数据库和FastAPI电子邮件服务的管道。LLM成功地完成了从数据库中检索用户的电子邮件并指示电子邮件服务发送个性化消息的任务,所有这些都是对单个提示的响应。
函数调用在AI模型中的影响可能是巨大的,为自动化和流程优化开辟了新的可能性。数据管道可以从静态和工程重型变成动态实体,使非技术用户可以使用自然语言快速获取最新数据。
大语言模型编年史:探索NLP前沿
本文属于“大语言模型编年史:探索NLP前沿”系列文章,这是一系列新的每周文章,探讨如何利用大模型的力量完成各种NLP任务。通过深入研究这些尖端技术,我们旨在赋予开发人员、研究人员和爱好者利用NLP的潜力,开启新的可能性。
目前已发布的文章:
- 使用ChatGPT总结最新的Spotify发布
- 使用FAISS和Sentence Transformers在规模上掌握语义搜索:索引数百万个文档,推理时间超快
- 解锁音频数据的力量:使用Whisper、WhisperX和PyAnnotate进行高级转录和辨识
- Whisper JAX vs PyTorch:揭开关于GPU上ASR性能的真相
- Vosk用于高效的企业级语音识别:评估和实施指南
- 测试支持1162种语言的大规模多语言语音(MMS)模型
- 利用Falcon 40B模型,最强大的开源LLM
保持联系:LinkedIn