BrainBank
AI 课堂/应用场景DeepLearningAI

M3 智能体式人工智能 - 邮件助手工作流

2026/8/5 17:04:19

#tool-use#use-cases#lab#email

动手实验:基于一个模拟收件箱 API 构建邮件助手智能体,观察它如何根据自然语言请求选择并串联搜索/读取/发送/删除等工具,并看看当所需工具缺失时会发生什么。

M3 智能体式人工智能 - 邮件助手工作流

1. 简介

1.1 实验概览

在本模块前面的一个视频中,Andrew 展示了一个日历助手的例子,它能使用多个工具来完成复杂任务。在这个非评分实验中,你将探索一个类似的例子——使用一个邮件助手智能体

这个智能体工作流可以完成与邮件管理相关的各种任务,包括发送邮件、按发件人搜索邮件,以及删除邮件。你会给它下达自然语言指令——比如“check unread emails from my boss”(查看我老板发来的未读邮件)或“delete the Happy Hour email”(删除那封 Happy Hour 邮件)——并观察它是如何为你选择正确的工具、完成任务的。

<img src="lab_overview.png" alt="Example of a calendar assistant" width="700"/>

🎯 1.2 学习目标

完成本实验后,你将能够使用 AISuite 将大模型连接到工具,下达自然语言指令,并观察智能体如何选择、执行并验证诸如搜索、发送、删除邮件等多步骤任务。

2. 初始化环境与客户端

与前一个实验一样,现在你将搭建环境。

# ================================
# Imports
# ================================

# --- Third-party ---
from dotenv import load_dotenv
import aisuite as ai
import json

# --- Local / project ---
import utils
import display_functions
import email_tools



# ================================
# Environment & Client
# ================================
load_dotenv()          # Load environment variables from .env
client = ai.Client()   # Initialize AISuite client

3. 模拟邮件服务

3.1 组成部分

本实验使用一个模拟的邮件后端来还原真实的邮件交互场景。 可以把它当作你个人的沙盒收件箱:里面已经预先加载了一些消息,让你可以在不发送真实邮件的情况下进行练习。

你不需要从零开始搭建这个后端,而是会使用一些工具来直接与它交互。在幕后,它使用了:

层级作用
FastAPI暴露 REST 接口
SQLite + SQLAlchemy在本地存储和查询邮件
Pydantic确保输入输出数据有效
AISuite tools连接大模型与该服务的桥梁

3.2 接口

该服务提供了若干模拟常见邮件操作的路由。 稍后,这些路由会被封装成工具,供助手自动使用:

  • POST /send → 发送一封新邮件
  • GET /emails → 列出所有邮件
  • GET /emails/unread → 仅显示未读邮件
  • GET /emails/{id} → 按 ID 获取指定邮件
  • GET /emails/search?q=... → 按关键词搜索邮件
  • GET /emails/filter → 按收件人和/或日期范围筛选
  • PATCH /emails/{id}/read → 将邮件标记为已读
  • PATCH /emails/{id}/unread → 将邮件标记为未读
  • DELETE /emails/{id} → 按 ID 删除邮件
  • GET /reset_database → 将邮件重置为初始状态(用于测试)

💡 核心理念: 这些接口相当于“收件箱的控制面板”。 在接下来的步骤中,你会把它们暴露为大模型可以调用的 Python 函数(工具)——把原始的路由转化为智能体的操作。

3.3 接口测试辅助函数

在把控制权交给大模型之前,你会先自己测试这个后端utils.test_* 系列函数是围绕这些 API 接口的快捷封装。它们让你可以尝试发送、列出、搜索、筛选、标记以及删除等操作,而无需编写原始的 HTTP 请求。

注意: 你可以从顶部菜单进入 File > Open 找到 utils.py 文件

每个辅助函数都有一个清晰、见名知意的名字。在下一个代码单元格中,你可以取消注释你想运行的那一行,查看它的输出,确认邮件服务的行为符合预期。

例如,你可以测试:

  • 发送一封测试邮件
  • 按 ID 获取邮件
  • 列出所有消息
  • 将邮件标记为已读或未读
  • 删除邮件

这一步是你在把控制权交给智能体之前的健全性检查。它能让你确信,这个模拟邮件服务是正常运作的,行为就像真实的邮件服务一样。

👉 要测试这些接口,取消注释你想尝试的那几行,运行该单元格,立即查看结果。

# uncomment the line 'utils.test_*' you want to try
new_email_id = utils.test_send_email()
_ = utils.test_get_email(new_email_id['id'])
#_ = utils.test_list_emails()
#_ = utils.test_filter_emails(recipient="test@example.com")
#_ = utils.test_search_emails("lunch")
#_ = utils.test_unread_emails()
#_ = utils.test_mark_read(new_email_id['id'])
#_ = utils.test_mark_unread(new_email_id['id'])
#_ = utils.test_delete_email(new_email_id['id'])
#_ = utils.reset_database()

4. 邮件智能体的工具层

4.1 为什么需要工具?

既然这些接口已经能正常工作了,下一步就是把它们以名为工具的 Python 函数形式,暴露给大模型。每个工具都封装了一个 REST 路由,把原始的 API 调用转化为智能体可以执行的操作——比如列出、读取、搜索、发送、删除,或切换已读状态。

可以把工具想象成智能体的执行器:你给出一条自然语言指令(“check unread emails from my boss and send a polite reply”,即“查看我老板的未读邮件并回复一封礼貌的回信”),模型会自行判断该调用哪些工具、以及按什么顺序调用,来完成这个任务。

4.2 设计要点

  • 保持工具的 docstring 简短、祈使句式,且针对具体操作。
  • 返回一致、紧凑的 JSON,便于模型对结果进行串联处理。
  • 优先做到每个工具只负责一件事(单一路由、单一效果)。

4.3 可用工具

工具函数操作
list_all_emails()获取所有邮件,按最新排列
list_unread_emails()仅获取未读邮件
search_emails(query)按关键词搜索主题、正文或发件人
filter_emails(...)按收件人和/或日期范围筛选
get_email(email_id)按 ID 获取指定邮件
mark_email_as_read(id)将邮件标记为已读
mark_email_as_unread(id)将邮件标记为未读
send_email(...)发送一封新的(模拟)邮件
delete_email(id)按 ID 删除邮件
search_unread_from_sender(addr)返回来自指定发件人的未读邮件(例如 boss@email.com

注意: 你可以从顶部菜单进入 File > Open 找到 email_tools.py 文件。

例如,测试 list_all_emails() 工具:

    all_emails = email_tools.list_all_emails()

现在,让我们试一试几个连接到模拟接口的工具,确认一切正常工作。

👉 取消注释你想运行的那几行,执行该单元格,查看输出结果。

# Test sending a new email and fetch it by ID
new_email = email_tools.send_email("test@example.com", "Lunch plans", "Shall we meet at noon?")
content_ = email_tools.get_email(new_email['id'])

# Uncomment the ones you want to try:
#content_ = email_tools.list_all_emails()
#content_ = email_tools.list_unread_emails()
#content_ = email_tools.search_emails("lunch")
#content_ = email_tools.filter_emails(recipient="test@example.com")
#content_ = email_tools.mark_email_as_read(new_email['id'])
#content_ = email_tools.mark_email_as_unread(new_email['id'])
#content_ = email_tools.search_unread_from_sender("test@example.com")
#content_ = email_tools.delete_email(new_email['id'])

utils.print_html(content=json.dumps(content_, indent=2), title="Testing the email_tools")

5. 准备智能体提示词

在给邮件助手智能体分配任务之前,你将创建一个名为 build_prompt() 的小型辅助函数。 这个函数会用一段系统风格的前言,把自然语言请求包装起来,从而让大模型:

  • 意识到自己正扮演一个邮件助手智能体的角色
  • 明白自己有权限使用可用的工具
  • 直接执行操作,而无需请求确认(不涉及人工介入)
def build_prompt(request_: str) -> str:
    return f"""
- You are an AI assistant specialized in managing emails.
- You can perform various actions such as listing, searching, filtering, and manipulating emails.
- Use the provided tools to interact with the email system.
- Never ask the user for confirmation before performing an action.
- If needed, my email address is "you@email.com" so you can use it to send emails or perform actions related to my account.

{request_.strip()}
"""

运行下一个单元格,看看上面这个函数是如何将你的原始用户提示词用系统指令包装起来的。 例如:

example_prompt = build_prompt("Delete the Happy Hour email")
utils.print_html(content=example_prompt, title="Example example_prompt")

5.3 重置邮件服务

由于你一直在对这个邮件服务进行各种实验,我们现在把它重置回初始状态。

你可以通过调用清空并刷新这个模拟邮件服务的工具函数来实现这一点:

utils.reset_database()

6. 大模型 + 邮件工具

6.1 场景

到目前为止,你一直在直接与后端打交道。现在,是时候让 大模型接管,成为你的邮件助手智能体了。

举例来说,你可能会这样要求它:

“Check for unread emails from boss@email.com, mark them as read, and send a polite follow-up.”(查看来自 boss@email.com 的未读邮件,将其标记为已读,并发送一封礼貌的跟进邮件。)

6.2 会发生什么

  1. 智能体理解你的指令。
  2. 它选择合适的工具(search_unread_from_sendermark_email_as_readsend_email)。
  3. 它自动执行每一步操作,无需请求确认。

AISuite 负责处理 schema 暴露、参数绑定、执行,以及在各步骤之间传递结果——这样你就可以专注于智能体做了什么,而不必操心如何调用 API。

6.3 运行它

运行下一个单元格,看看智能体是如何编排多个工具来完成你的请求的。你也可以自由地在这个邮件服务上尝试你自己的请求。

需要留意的地方:

  • 一份清晰的工具调用轨迹,展示使用了哪些工具、传入了什么参数。
  • 一条简洁的最终消息,总结所执行的操作(例如“找到 1 封未读邮件,已标记为已读,已发送跟进邮件”)。
# Try your own requests
prompt_ = build_prompt("Check for unread emails from boss@email.com, mark them as read, and send a polite follow-up.")

response = client.chat.completions.create(
    model="openai:gpt-4.1", # LLM
    messages=[{"role": "user", "content": (
        prompt_
    )}],
    tools=[ # list of tools that the LLM can access
        email_tools.search_unread_from_sender,
        email_tools.list_unread_emails,
        email_tools.search_emails,
        email_tools.get_email,
        email_tools.mark_email_as_read,
        email_tools.send_email
    ],
    max_turns=5,
)

display_functions.pretty_print_chat_completion(response)


6.4. 缺失的工具:delete_email

如果你需要的工具没有提供,会发生什么?

比如,试试这条请求:

“Delete alice@work.com’s email.”(删除 alice@work.com 的邮件。)

由于 delete_email 这个工具并未被注册,大模型仍然会尝试给出响应,但它将无法完成这个操作。

<div style="background-color: #ffe4e1; padding: 12px; border-radius: 6px; color: black;"> <h4>🔍 关键洞见</h4> <p style="margin: 0;"> 这凸显了一个重要观点:<b>你所提供的工具,直接决定了智能体能做什么。</b> </p> </div>

可用工具

工具函数操作
list_all_emails()获取所有邮件,按最新排列
list_unread_emails()仅获取未读邮件
search_emails(query)按关键词搜索主题、正文或发件人
filter_emails(...)按收件人和/或日期范围筛选
get_email(email_id)按 ID 获取指定邮件
mark_email_as_read(id)将邮件标记为已读
mark_email_as_unread(id)将邮件标记为未读
send_email(...)发送一封新的(模拟)邮件
delete_email(id)按 ID 删除邮件
search_unread_from_sender(addr)返回来自指定发件人的未读邮件(例如 boss@email.com
# Try with a request that may call an unavailable tool
prompt_ = build_prompt("Delete alice@work.com email")

response = client.chat.completions.create(
    model="openai:o4-mini",
    messages=[{"role": "user", "content": (
        prompt_
    )}],
    tools=[ # list of tools that the LLM can access
        email_tools.search_unread_from_sender,
        email_tools.list_unread_emails,
        email_tools.search_emails,
        email_tools.get_email,
        email_tools.mark_email_as_read,
        email_tools.send_email
    ],
    max_turns=5
)

display_functions.pretty_print_chat_completion(response)

6.4.1 启用 delete_email 后再试一次

如果你需要的工具没有提供,会发生什么?

在实验的上一步中,你给大模型提供了以下工具列表:

    tools=[
        email_tools.search_unread_from_sender,
        email_tools.list_unread_emails,
        email_tools.search_emails,
        email_tools.get_email,
        email_tools.mark_email_as_read,
        email_tools.send_email
    ]

正因为那个列表中提供的是这些工具,智能体无法删除邮件,因为 delete_email 这个工具当时并不可用。

现在,把 delete_email 加入工具列表,重新运行该单元格。这一次,智能体将拥有完成任务所需的一切。

提示:留意调用顺序——在找到目标消息之后,智能体应该会选择 delete_email 来完成这个操作。

prompt_ = build_prompt("Delete alice@work.com email")

response = client.chat.completions.create(
    model="openai:o4-mini",
    messages=[{"role": "user", "content": (
        prompt_
    )}],
    tools=[
        email_tools.search_unread_from_sender,
        email_tools.list_unread_emails,
        email_tools.search_emails,
        email_tools.get_email,
        email_tools.mark_email_as_read,
        email_tools.send_email,
        email_tools.delete_email
    ],
    max_turns=5
)

display_functions.pretty_print_chat_completion(response)

6.5. 定向操作:删除“Happy Hour”邮件

测试收件箱中预先加载了一封标题为 “Happy Hour” 的邮件。你的任务是指示智能体找到这条消息并将其删除。

作为参考,以下是模拟数据集中的相应条目:

{
  "id": 1,
  "sender": "eric@work.com",
  "recipient": "you@email.com",
  "subject": "Happy Hour",
  "body": "We're planning drinks this Friday!",
  "timestamp": "2025-06-13T04:48:59.096908",
  "read": false
}

运行下一个单元格,看看智能体是如何搜索这条消息并将其从收件箱中删除的。

prompt_ = build_prompt("Delete the happy hour email")

response = client.chat.completions.create(
    model="openai:o4-mini",
    messages=[{"role": "user", "content": (
        prompt_
    )}],
    tools=[
        email_tools.search_unread_from_sender,
        email_tools.list_unread_emails,
        email_tools.search_emails,
        email_tools.get_email,
        email_tools.mark_email_as_read,
        email_tools.send_email,
        email_tools.delete_email
    ],
    max_turns=5
)

display_functions.pretty_print_chat_completion(response)

7. 最终总结

  • 在这个非评分实验中,你探索了一个大模型邮件智能体是如何通过工具调用与模拟邮件服务交互的。
  • 工具调用让大模型不再局限于生成文本——使它们能够调用函数(工具),完成多步骤任务。
  • 可用工具的集合决定了智能体能做什么、不能做什么(例如,没有 delete_email,它就无法删除消息)。
  • 清晰的 docstring 和一致的行为,能帮助大模型在每一步都选出正确的工具。
  • AISuite 负责管理交互层:把 Python 函数暴露为工具、接受参数、发起 API 请求,并返回结果。
  • 观察从提示词 → 工具调用 → 输出 → 最终响应的完整工作流,是理解和改进智能体推理与行动方式的关键。
<div style="border:1px solid #22c55e; border-left:6px solid #16a34a; background:#dcfce7; border-radius:6px; padding:14px 16px; color:#064e3b; font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;">

🎉 <strong>恭喜!</strong>

你刚刚引导一个由大模型驱动的<em>邮件助手智能体</em>,把自然语言请求转化为在模拟邮件服务上的具体操作。你看到了工具是如何把普通语言转化为可靠操作的——列出、搜索、标记已读、发送和删除,而你自己完全不需要接触原始的 HTTP 请求。

你了解到,你所暴露的工具决定了智能体真正的能力边界。当某个工具缺失时,智能体可以推理,却无法行动;而当它存在时,智能体就能把多个步骤串联起来、传递参数,并准确交付你所要求的结果。在这个过程中,AISuite 处理了幕后的连接工作:schema 暴露、参数绑定、执行,以及结果传递——这样你就可以专注于想要完成的事情,而不必操心如何把它们连接起来。

掌握了这个工作流之后,你已经准备好设计能够安全、透明地行动、解释自己所做之事,并能从简单提示词扩展到可靠的多步骤自动化的、以任务为中心的智能体,应用到你自己的各类服务中去。🌟

</div>

学习地图

本页是「DeepLearningAI > Agentic AI Lab」的第 4 / 7 页——这是一份真实的 DeepLearning.AI 笔记本(代码 + 讲解文字),而非「Agentic AI」板块中那种模板化内容。顺序上接在「M3 智能体式人工智能 - 将函数转化为工具」之后。完成后可继续阅读「M4 智能体式人工智能 - 为研究工作流添加组件级评估」。代码单元格完全保留了源笔记本中的原样——请在你自己的 Python 环境中按顺序运行它们(它们依赖 utils.py 等本地辅助模块,这里并未包含)。

动手实践——分步指南

搭建好这份笔记本所需的本地依赖(代码单元格中导入的辅助模块,例如 utils.py、display_functions.py,以及文中引用的各个工具模块),然后按照「M3 智能体式人工智能 - 邮件助手工作流」自身的分步讲解,从上到下依次运行每个代码单元格。在进入下一个实验之前,先尝试文中建议的实验(更换模型、修改提示词、提出你自己的请求)。

三大推荐资源

  1. 1
    aisuite (GitHub)

    The unified multi-provider LLM client used throughout these labs for chat completions and tool calling.

    https://github.com/andrewyng/aisuite

  2. 2
    DeepLearning.AI Course Catalog

    The DeepLearning.AI catalog these lab notebooks are drawn from.

    https://www.deeplearning.ai/courses/

链接由 AI 推荐——使用前建议快速核实。