授权声明: 本文基于九天Hector的原创课程资料创作,已获得其正式授权。
原课程出处:九天Hector的B站主页,感谢九天Hector为学习者带来的宝贵知识。
请尊重原创,转载或引用时,请标明来源。
全文共4000余字,预计阅读时间约20~35分钟 | 满满干货(附代码),建议收藏!
本文目标:提供一个结合OpenAI的Chat模型和Google API来开发智能邮件应用程序的详细构建流程
一、介绍
大模型应用开发从谷歌云入手进行学习和尝试,是门槛最低、效率最高的方法。谷歌云上不仅有成百上千的各类应用的API,而且拥有统一的API制式和权限管理方法,调用过程非常方便,一个账号进行一次项目认证即可使用海量的API,外加详细完整的开发者文档,更是极大程度降低了上手使用API的门槛。
同时,谷歌云是功能完备的应用开发平台,如果不仅是尝试使用API进行前期的探索,而是希望真正意义上的完成企业级应用开发,也完全可以在谷歌云上进行。谷歌云不仅提供了完整的在线应用开发与发布流程,而且提供了(相对)廉价、稳定的云服务,开发者开发的应用程序可以直接在云端运行,并享受谷歌云提供的一整套应急、维护流程,以及实时可视化监控页面。
一个真实存在的问题是:国内还是存在访问限制,需要使用魔法
本文介绍一下如何将Gmail API接入Chat Completion模型,编写一个智能收发邮件的AI应用程序,起到一个抛砖引玉的作用。
二、Gmail API的OAuth授权
要调用谷歌的API,第一步是要进行授权。具体谷歌云Google Cloud与谷歌云API库的介绍,及如何完成Gmail API的OAuth授权过程,如果您了解的话可以跳过这一步,直接进入下面代码内容,如不清楚如何操作的,请看下面链接:
Gmail API的OAuth授权过程的成功实现非常重要,使用谷歌云的其他API来构建更加复杂的AI应用程序,其中大多数都需要进行OAuth授权,如果已经完成了授权,则该凭证也是可以应用于其他API调用的,完成授权也是进行AI应用程序开发的前提。
三、借助Gmail API构建智能邮件收发应用程序
3.1 在Chat Completion模型中添加查阅邮件功能
先尝试将Gmail API接入Chat Completion模型中。在这三篇文章中:
OpenAI开发系列(十一):Function calling功能的实际应用流程与案例解析
OpenAI开发系列(十二):Function calling功能的流程优化与多轮对话实现
OpenAI开发系列(十三):利用Function calling功能开发基于大模型的实时天气查询助手
已经跑通了优化后的Function calling功能执行流程,对于开发一个AI应用程序,在确定了基本功能实现的目标之后,总共分四步进行:
- 按照需求编写外部功能函数并完成功能测试,确保函数可以正确运行
- 验证大语言模型是否具备解读外部函数结果和准确翻译外部函数参数的能力
- 将函数带入
AutoFunctionGenerator
封装类中,自动生成一系列功能函数的 JSON Schema 描述。 - 调用
ChatConversation
封装类,激活Function Calling功能,并输出最终推理结果
按照上述过程,要基于大模型实现基于Gmail API收发邮件的AI小程序,具体实现过程如下:
- Step 1:测试外部函数功能可行性
当完成Gmail API的OAuth授权后,先测试能否通过调用Gmail API查阅最近的一封邮件信息,包括发件人、日期和邮件内容,代码如下:
def get_gmail_service(token_file):
"""
从本地文件中加载授权凭据,并返回Gmail API客户端。
参数:
- token_file (str): 包含授权凭据的本地文件名。
返回:
- service: Gmail API客户端。
"""
# 从本地文件加载授权凭据
creds = Credentials.from_authorized_user_file(token_file)
# 创建并返回Gmail API客户端
service = build('gmail', 'v1', credentials=creds)
return service
def print_latest_email_info(service):
"""
获取并打印用户的最新一封邮件的信息。
参数:
- service: Gmail API客户端。
返回:
无。
"""
# 获取最新一封邮件的ID
results = service.users().messages().list(userId='me', maxResults=1).execute()
messages = results.get('messages', [])
# 遍历邮件列表(这里只有一封邮件)
for message in messages:
# 获取邮件详细信息
msg = service.users().messages().get(userId='me', id=message['id']).execute()
# 获取邮件头部信息
headers = msg['payload']['headers']
# 初始化发件人和日期字段
From, Date = "", ""
# 提取发件人和日期信息
for h in headers:
name = h['name']
if name.lower() == 'from':
From = h['value']
if name.lower() == 'date':
Date = h['value']
# 判断邮件是否有多个部分(例如正文和附件)
if 'parts' in msg['payload']:
part = msg['payload']['parts'][0]
data = part['body']["data"] if part['mimeType'] == 'text/plain' else msg['payload']['body']["data"]
else:
data = msg['payload']['body']["data"]
# 将数据从base64格式解码为文本
data = data.replace("-", "+").replace("_", "/")
decoded_data = base64.b64decode(data)
str_text = str(decoded_data, "utf-8")
# 将解码后的数据转换为email.message.Message对象
msg_str = email.message_from_string(str_text)
# 如果邮件有多个部分,只取第一个部分(通常是正文)
if msg_str.is_multipart():
text = msg_str.get_payload()[0]
else:
text = msg_str.get_payload()
# 打印邮件信息
print('From: {}'.format(From[:8]))
print('Date: {}'.format(Date))
print('Content: {}'.format(text))
看下输出结果:
# 示例使用
if __name__ == "__main__":
# 获取Gmail API客户端
service = get_gmail_service('token.json')
# 获取并打印最新一封邮件的信息
print_latest_email_info(service)
看下邮箱状态:
上述代码的核心流程是通过service.users().messages().list(userId=‘me’, maxResults=1).execute()获取了最近一封邮件的信息,并最终保留在msg中。
更多关于Gmail API返回结果信息可参考Gmail API官网功能介绍:https://developers.google.com/gmail/api/guides
- Step 2:验证模型能否解读Gmail API返回结果
Gmail API 在不处理的情况下,返回的结果是这样的:
尝试直接让Chat Completion模型解读msg结果,测试其解读邮件内容的能力,代码如下:
import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
response = openai.ChatCompletion.create(
model="gpt-4-0613",
messages=[
{"role": "system", "content": "我现在有一封邮件是通过Gmail API获取到的,在我Gmail邮箱中,最新的一封邮件内容如下:%s" % msg},
{"role": "user", "content": "请问一下,这封邮件是谁发发送给我的,内容是什么?"}
]
)
response.choices[0].message['content']
看下输出结果:
所以这也就意味着:使用Chat Completion模型来解析Gmail邮件内容是一个有效且可行的方案,因为该模型能准确地理解邮件中包含的信息。
- Step 3:创建外部功能函数
按照Step 1中的代码流程,创建一个能够返回最近一封邮件信息的msg对象,并输出为JSON格式,函数如下:
def fetch_latest_gmail_content(userId):
"""
查询指定用户ID的Gmail邮箱中的最后一封邮件信息。
参数:
userId (str): 必填参数。表示需要查询的Gmail用户ID。注意,如果查询自己的邮箱,\
userId需设置为'me'。
返回:
str: 包含最后一封邮件全部信息的JSON格式字符串。该对象由Gmail API创建并返回。\
如果查询失败,返回包含错误信息的JSON格式字符串。
"""
# 从本地文件中加载凭据
creds = Credentials.from_authorized_user_file('token.json')
# 创建 Gmail API 客户端
service = build('gmail', 'v1', credentials=creds)
# 列出用户的一封最新邮件
results = service.users().messages().list(userId=userId, maxResults=1).execute()
messages = results.get('messages', [])
# 遍历邮件
for message in messages:
# 获取邮件的详细信息
msg = service.users().messages().get(userId='me', id=message['id']).execute()
# 只处理最新的邮件(列表中的第一个)
if messages:
# 获取邮件的详细信息
msg = service.users().messages().get(userId=userId, id=messages[0]['id']).execute()
return json.dumps(msg)
else:
return json.dumps({"error": "No messages found."})
- Step 4:构建功能函数的JSON Schema对象
直接使用封装好的AutoFunctionGenerator
类来为fetch_latest_gmail_content
生成相应的JSON Schema对象。
functions_list = [fetch_latest_gmail_content]
generator = AutoFunctionGenerator(functions_list)
function_descriptions = generator.auto_generate()
看一下执行结果:
- Step 5:测试大模型是否能识别外部功能函数库
直接调用测试Chat Completion模型能否顺利创建满足格式的参数:
response = openai.ChatCompletion.create(
model="gpt-4-0613",
messages=[{"role": "user", "content": '请帮我查下我Gmail邮箱中最后一封邮件信息'}],
functions=functions,
function_call="auto",
)
看下结果:
- Step 4:验证对话效果
最后来测试下对话效果,还是使用优化好的messages自动拼接流程ChatConversation
封装类和多轮对话函数chat_with_assistant
。
先测试不带入外部函数仓库时:
再测试带入外部功能函数时:
3.2 在Chat Completion模型中添加发送邮件功能
- Step 1:重新获取授权
在谷歌云API中,一个API类别可能有多个功能,每个功能可能需要单独的授权。这主要通过设置SCOPES
变量来实现,该变量是一个包含多个URL的列表。每个URL代表一个特定的API功能或权限。例如,以gmail.send
结尾的URL表示发送邮件的权限,而以gmail.readonly
结尾的URL则表示只有阅读邮件的权限。
目前生成的token.json
仅包含阅读邮件的API授权,而没有包括发送邮件的权限。因此,首先需要更新API权限,以获得Gmail发送邮件的授权。可以使用之前用于获取授权的相同代码,但需在授权过程中添加发送邮件的权限。将新的授权文件保存为本地的token_send.json
。
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
flow = InstalledAppFlow.from_client_secrets_file(
# 此处替换你下载的OAuth授权命名文件
'credentials-web1.json', SCOPES)
# 此处替换你自己设置的端口
creds = flow.run_local_server(port=9090, access_type='offline', prompt='consent')
with open('token_send.json', 'w') as token:
token.write(creds.to_json())
如果这里不清楚怎么授权的,看Gmail API的OAuth授权部分
具体不同的API功能对应可以参考官方说明:https://developers.google.com/gmail/api/auth/scopes?hl=zh_CN
也可以一次性获得包含多个权限的授权文件,此时SCOPES可以按照如下方式进行定义,此时授权文件可以同时允许执行文件阅读和发送:
SCOPES = ['https://www.googleapis.com/auth/gmail.send','https://www.googleapis.com/auth/gmail.readonly']
- Step 2:测试发送功能
完成授权之后,即可使用Gmail的发送邮件功能了,将授权文件改为token_send.json,具体代码实现流程如下:
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from email.mime.text import MIMEText
import base64
import json
def send_email_via_gmail_api(to, subject, message_text, creds_file='token_send.json'):
"""
使用 Gmail API 发送邮件。
参数:
- to (str): 接收者的邮箱地址。
- subject (str): 邮件的主题。
- message_text (str): 邮件的正文内容。
- creds_file (str): 存储 Gmail API 凭据的本地 JSON 文件名。
返回:
- dict: 如果发送成功,返回一个包含发送邮件的 ID 和状态的字典。如果发送失败,返回 None。
"""
def create_message(to, subject, message_text):
"""创建一个 MIME 邮件对象"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = 'me' # 'me' 表示授权用户自己
message['subject'] = subject
raw_message = base64.urlsafe_b64encode(message.as_string().encode('utf-8')).decode('utf-8')
return {
'raw': raw_message
}
def send_message(service, user_id, message):
"""使用 Gmail API 发送邮件"""
try:
sent_message = service.users().messages().send(userId=user_id, body=message).execute()
print(f'Message Id: {sent_message["id"]}')
return sent_message
except Exception as e:
print(f'An error occurred: {e}')
return None
# 从本地 JSON 文件加载 Gmail API 凭据
creds = Credentials.from_authorized_user_file(creds_file)
# 创建 Gmail API 客户端
service = build('gmail', 'v1', credentials=creds)
# 创建邮件消息
message = create_message(to, subject, message_text)
# 发送邮件消息
result = send_message(service, 'me', message)
return json.dumps(result)
看下函数的测试结果:
# 示例用法
to_address = 'snowball950123@gmail.com'
subject = '测试'
message_text = '测试 Gmail API 的邮件发送功能'
result = send_email_via_gmail_api(to_address, subject, message_text)
print(f"发送结果: {result}")
看下邮箱是否收到:
- Step 3:封装发送邮件的功能函数
def send_email(to, subject, message_text):
"""
借助Gmail API创建并发送邮件函数
:param to: 必要参数,字符串类型,用于表示邮件发送的目标邮箱地址;
:param subject: 必要参数,字符串类型,表示邮件主题;
:param message_text: 必要参数,字符串类型,表示邮件全部正文;
:return:返回发送结果字典,若成功发送,则返回包含邮件ID和发送状态的字典。
"""
creds_file='token_send.json'
def create_message(to, subject, message_text):
"""创建一个MIME邮件"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = 'me'
message['subject'] = subject
raw_message = base64.urlsafe_b64encode(message.as_string().encode('utf-8')).decode('utf-8')
return {
'raw': raw_message
}
def send_message(service, user_id, message):
"""发送邮件"""
try:
sent_message = service.users().messages().send(userId=user_id, body=message).execute()
print(f'Message Id: {sent_message["id"]}')
return sent_message
except Exception as e:
print(f'An error occurred: {e}')
return None
# 从本地文件中加载凭据
creds = Credentials.from_authorized_user_file(creds_file)
# 创建 Gmail API 客户端
service = build('gmail', 'v1', credentials=creds)
message = create_message(to, subject, message_text)
res = send_message(service, 'me', message)
return json.dumps(res)
- Step 4:测试下functions参数
functions_list = [send_gmail]
generator = AutoFunctionGenerator(functions_list)
function_descriptions = generator.auto_generate()
看下生成的JSON Schema对象描述:
- Step 5:测试Chat Completion是否能正确解析
response = openai.ChatCompletion.create(
model="gpt-4-0613",
messages=[{"role": "user", "content": '我想发送一个Gmail邮件,主要内容是:让小陈明天早上9点半来我办公室开会,商量一下我的100亿该怎么花'}],
functions=functions,
function_call="auto"
)
response
看下返回结果:
模型会根据语义创建邮件的主题和内容
- Step 6:多轮对话验证
当不调用外部功能函数时:
chat_with_assistant()
看下对话过程:
当调用send_email外部功能函数时:
functions_list = [send_email]
chat_with_assistant(functions_list=functions_list)
看一下对话过程:
然后在邮箱中查看查收的邮件:
至此,就将邮件发送功能也完整集成到Chat Completion模型当中了。
3.3 在Chat模型中同时调用邮件发送和查询功能
在分别跑通了邮件发送功能之后,可以尝试在一个对话中同时调用这两个功能。毕竟大多数AI应用都是围绕某一方面应用的多功能集合,例如对于一个智能收发邮件助手的AI应用来说,收件和发件肯定是最基本的功能需求。
要在一个Chat模型中集成收件和发件两方面功能,只需要在functions_list同时添加get_latest_email和send_email即可,代码如下:
functions_list = [fetch_latest_gmail_content, send_email]
chat_with_assistant(functions_list=functions_list)
看下结果:
至此,就完成了在一个Chat Completion对话中同时调用发件和收件功能。
四、总结
随着Chat Completion模型集成的外部工具API越来越丰富,其智能化的应用范围也逐渐扩大。不过,需要清楚的认识到:集成这些外部工具API并不是一件简单的事情。这不仅需要获取各种API的访问权限,还需要具备相应的编程和API调用知识。例如,仅仅为了开发一个能够智能收发邮件的应用,就需要投入大量时间去理解API授权和函数编写,所以如何利用大模型来协助开发者完成这些中间过程,是一个非常有价值的研究和探索方向。
最后,感谢您阅读这篇文章!如果您觉得有所收获,别忘了点赞、收藏并关注我,这是我持续创作的动力。您有任何问题或建议,都可以在评论区留言,我会尽力回答并接受您的反馈。如果您希望了解某个特定主题,也欢迎告诉我,我会乐于创作与之相关的文章。谢谢您的支持,期待与您共同成长!