「吴恩达Agentic AI模块3」工具使用与模型上下文协议(MCP)
「吴恩达Agentic AI模块3简报」工具使用与模型上下文协议(MCP)
概述
本简报综合分析了大型语言模型(LLM)通过“工具使用”实现代理式(Agentic)能力的核心概念、实现机制和未来趋势。关键洞察如下:
- 工具是LLM能力的延伸:工具是开发者提供给LLM调用的外部函数,使其能够超越预训练知识的限制。这使得LLM能够执行获取实时信息、查询数据库、进行网络搜索和执行复杂计算等任务,从而极大地增强了应用的实用性。
- 代理式决策是核心:工具使用的关键在于LLM能够自主决定是否、何时以及调用哪个工具来完成任务。这种非硬编码的决策过程是实现代理式工作流的基础,其标准流程为:LLM接收提示 -> 决定调用工具 -> 外部代码执行工具并返回结果 -> 结果反馈给LLM -> LLM生成最终输出。
- 实现方式的演进:工具调用的实现已从早期依赖特定提示词(如输出“FUNCTION: function_name”)的繁琐方法,演变为现代LLM原生的、基于特定语法的调用方式。诸如AISuite之类的库通过自动从函数文档字符串生成JSON Schema来描述工具,进一步简化了开发流程,使开发者能更便捷地集成工具。
- 代码执行是最强大的工具之一:为LLM提供一个通用的代码执行工具,比创建无数个特定功能的工具更具灵活性和扩展性。LLM能够通过编写和执行代码来解决复杂的数学、逻辑和数据处理问题。然而,这也带来了严峻的安全风险。最佳实践是在沙盒环境(如Docker)中执行代码,以防止潜在的数据丢失或系统损坏。
- 模型上下文协议(MCP)推动生态标准化:MCP是由Anthropic提出并被广泛采用的一项新标准,旨在解决开发者在集成各类API(如Slack、GitHub)时重复造轮子的问题。它通过定义统一的客户端(应用)与服务端(工具提供者)交互协议,将社区开发工作量从M×N(应用×工具)的复杂性降低到M+N,极大地促进了一个可共享、可复用的工具生态系统的形成。
工具使用的核心概念与价值
工具使用是赋予LLM代理能力、使其能够与外部世界交互并执行具体行动的关键机制。它将LLM从一个单纯的文本生成器转变为一个能够主动解决问题的智能体。
1.1 什么是工具?
在LLM的语境下,“工具”是指开发者编写并提供给模型使用的外部代码函数。当LLM认为需要执行某个操作或获取特定信息时,它不会直接执行代码,而是会请求调用这些预先定义的函数。
- 功能示例:
- 信息获取:
getCurrentTime()获取当前时间,网络搜索工具获取最新资讯。 - 数据操作:
queryDatabase()从销售数据库中查询特定客户信息。 - 计算任务:
calculateInterest()执行精确的金融计算。 - 系统交互:
makeAppointment()在日历中创建会议邀请。
- 信息获取:
1.2 工具使用的代理式决策过程
工具使用的核心特征在于其**代理式(Agentic)**本质,即LLM根据上下文自主决策,而非由开发者硬编码指定何时调用函数。
- 决策自主性:当面对一个问题时,LLM会评估其可用的工具集。
- 如果问题(如“绿茶中有多少咖啡因?”)能直接用其内部知识回答,它将不会调用任何工具。
- 如果问题(如“现在几点了?”)需要外部信息,它会决定调用最合适的工具(如getCurrentTime())。
- 标准工作流程:
- 输入提示:用户向LLM提出请求。
- LLM决策与请求:LLM分析请求,并从可用工具集中选择一个或多个进行调用。它生成一个符合特定格式的请求。
- 工具执行与结果返回:开发者编写的外部代码捕获LLM的请求,执行相应的函数,并将函数的返回值(如具体时间“下午3:20”)获取。
- 结果反馈与最终生成:函数返回值被送回LLM,作为其对话历史的一部分。LLM基于这个新信息,生成最终的、对用户更有帮助的回答。
这一流程可以连续进行,LLM在一个任务中可能会按顺序调用多个工具,例如先通过checkCalendar查找空闲时间,再通过makeAppointment创建日程。
2. 实现工具调用的机制
工具调用的技术实现经历了从手动、繁琐到自动化、标准化的演进。
2.1 早期实现方式:基于提示词的指令
在LLM原生支持工具调用之前,开发者需要通过详细的系统提示(System Prompt)来“教”会模型如何请求工具。
- 机制:开发者在提示词中明确规定一种特殊格式,例如:“如果你需要调用getCurrentTime工具,请输出文本‘FUNCTION: getCurrentTime’。”
- 开发者工作:
- 编写代码来解析LLM的输出文本。
- 使用正则表达式等方式检测是否存在关键词“FUNCTION:”。
- 如果检测到,则提取函数名和参数。
- 手动执行该函数,并将结果拼接回LLM的输入中。
- 缺点:这种方法非常“笨拙”(clunky),且高度依赖于提示词的稳定性和LLM的遵循能力,缺乏鲁棒性。
2.2 现代实现方式:原生语法与自动化
现代主流的LLM(如GPT-4o)经过专门训练,能够理解并使用一种标准化的工具调用语法。开发者不再需要手动设计提示词格式。
- 机制:开发者通过API将工具列表直接提供给LLM。这些工具通常通过一个结构化的JSON Schema来描述。
- JSON Schema的角色:
- 它向LLM清晰地描述了每个工具的功能、名称和所需参数。
- name:函数名,如 getCurrentTime。
- description:功能的详细描述,如“获取指定时区的当前时间”。LLM依靠此描述来判断何时使用该工具。
- parameters:函数所需的输入参数及其类型和描述。
- 自动化库(如AISuite):像AISuite这样的开源库极大地简化了这一过程。开发者只需提供Python函数,库会自动检查函数的文档字符串(docstring)和类型注解,并为其生成符合规范的JSON Schema。一个API调用(如client.chat.completions.create)就能完成从LLM决策、工具调用到结果反馈的整个循环。
3. 代码执行:一种特殊的超级工具
在所有可用的工具中,允许LLM编写并执行代码的工具尤为强大和灵活,它将LLM的计算和逻辑推理能力提升到了新的高度。
3.1 灵活性与强大功能
与其为加、减、乘、除、开方、幂运算等每一个数学操作都创建一个独立的工具,不如提供一个通用的“代码执行器”。
- 可扩展性:LLM可以自行编写代码来解决几乎任何可以通过计算解决的问题,从简单的数学题到复杂的利息计算,无需开发者预先定义每一个具体功能。
- 案例:当被问及“2的平方根是多少?”时,LLM可以生成并请求执行以下Python代码:
import math
result = math.sqrt(2)
- 执行后返回的结果1.4142…被反馈给LLM,使其能够给出精确答案。
3.2 安全风险与最佳实践
允许LLM执行任意代码伴随着巨大的安全风险。
- 真实风险案例:一个代理式编码工具曾错误地决定执行rm *.py命令,删除了一个项目目录下的所有Python文件。尽管该智能体事后“道歉”,但文件已被删除,幸运的是项目有备份。
- 最佳实践:沙盒环境:为了降低数据丢失、敏感信息泄露或系统损坏的风险,所有由LLM生成的代码都应在沙盒(Sandbox) 环境中执行。
- 沙盒技术:像Docker容器或轻量级的E2B等工具可以创建一个与主系统隔离的执行环境。即使代码包含恶意或破坏性命令,其影响也被限制在沙盒内部。
3.3 反思与调试
代码执行工具可以与“反思”(Reflection)机制相结合。如果LLM生成的代码在执行时出错,可以将错误信息(如堆栈跟踪)反馈给LLM,使其能够分析错误原因、修改代码并再次尝试,从而提高任务的成功率。
4. 模型上下文协议(MCP):工具生态的标准化
随着工具使用的普及,开发者社区面临着大量的重复性工作。模型上下文协议(MCP)的出现旨在解决这一问题,推动工具生态的标准化和协作。
4.1 MCP解决的问题
在MCP出现之前,如果多个开发团队都需要在其应用中集成Slack、Google Drive和GitHub等服务,每个团队都必须独立编写用于调用这些服务API的封装函数(工具)。
- M×N 问题:如果有 M 个应用和 N 个工具/数据源,社区总的工作量与 M×N 成正比,造成了巨大的资源浪费。
- MCP的解决方案:MCP提出了一套标准化的协议,让工具的提供方和使用方能够解耦。这使得社区的总工作量转变为 M+N:N 个工具只需被封装一次(成为MCP服务端),就可以被 M 个应用(作为MCP客户端)复用。
4.2 MCP的运作方式
MCP生态系统由客户端和服务端组成:
- MCP客户端(Client):需要访问工具或数据的应用程序。
- MCP服务端(Server):提供对工具或数据源(在MCP文档中称为“资源”)访问的软件封装层。例如,一个GitHub MCP服务端可以提供获取代码仓库文件、列出拉取请求等工具。
4.3 实际应用示例
一个集成了MCP的云桌面应用(客户端)可以连接到GitHub MCP服务端,以完成复杂任务:
- 用户请求:用户请求:“总结一下 AI-Suite 仓库的 readme.md 文件。”
- MCP交互:
- 应用(客户端)向GitHub服务端发送一个标准化的请求:“请从AI-Suite仓库获取readme.md文件。”
- 服务端执行请求,返回文件的全部内容。
- LLM处理:文件内容被送入LLM的上下文,LLM随后生成该文件的摘要。
通过这种方式,应用开发者无需关心如何具体调用GitHub的API,只需使用遵循MCP标准的客户端即可接入整个生态的工具。MCP正在成为构建强大、可扩展的代理式应用的重要标准。
我们通常认为大型语言模型(LLM)是强大的文本生成器,擅长对话和写作。但如果它们能做的远不止于此呢?如果它们能够采取行动、查询数据、完成任务呢?“工具使用”(Tool Use)正是解锁这一能力的关键
「吴恩达Agentic AI 模块3」关于AI如何使用“工具”:五个真相
我们通常认为大型语言模型(LLM)是强大的文本生成器,擅长对话和写作。但如果它们能做的远不止于此呢?如果它们能够采取行动、查询数据、完成任务呢?“工具使用”(Tool Use)正是解锁这一能力的关键,而其工作原理可能比大多数人想象的要更令人惊讶。本文将揭示关于AI工具使用的五个最具影响力的真相。
真相一:LLM实际上不“调用”工具,而是“请求”执行
这是最核心且违反直觉的一个机制:作为一个纯粹的文本生成器,LLM本身无法直接执行任何代码或调用一个外部函数。它完全依赖于开发者编写的应用程序代码来完成整个流程。
实际的工作流是一个由开发者精心设计的、多步骤的协作过程:
- 生成意图:当LLM判断需要使用工具时,它会生成一段特殊格式的文本,比如
FUNCTION: getCurrentTime。这串文本仅仅是表达了它想要使用工具的意图。 - 应用检测:开发者编写的应用程序代码会持续监控LLM的输出,并检测到这个特殊格式的文本请求。
- 应用执行:检测到意图后,应用程序会代替LLM去执行真正的函数(例如,调用Python的
getCurrentTime()函数)。 - 结果反馈:函数执行后返回的结果(比如 “下午3:20”)会被应用程序捕获,并作为新的信息添加回与LLM的对话历史中。
- 最终回答:LLM读取包含新结果的对话历史,然后生成最终给用户的、通顺自然的回答(例如,“现在是下午3:20”)。
因此,这并非模型自身的直接行动,而是模型与外部应用程序之间的一场精心编排的“合作舞蹈”,LLM在其中扮演的是“请求者”的角色,而真正的“执行者”是开发者构建的系统。
真相二:LLM自己决定何时使用工具
现代的工具使用方法之所以强大,在于其“智能代理”(Agentic)的特性。开发者只需向LLM提供一系列可用的工具,而由LLM根据用户的具体问题自主决定是否以及何时使用它们。
这与“硬编码”的逻辑截然不同。在硬编码中,开发者会强制规定在某个步骤必须调用某个函数。但在智能代理模式下:
- 当你给LLM一个
getCurrentTime工具并提问“现在几点了?”,它会判断出需要使用这个工具来获取实时信息。 - 但如果你问的是“绿茶里有多少咖啡因?”,它会正确地判断出
getCurrentTime工具对此问题毫无帮助,于是直接利用其内部知识库进行回答。
这种基于上下文的决策能力,使得系统能够灵活地适应千变万化的用户需求,是其强大功能的根源。
真相三:代码执行是LLM的“超级工具”
与其为LLM创建大量功能单一的特定工具,不如给它一个更强大、更通用的“超级工具”:代码解释器。
想象一下计算器:我们不需要为加、减、乘、除、开方、求幂等每一个运算都创建一个单独的工具。我们可以只提供一个工具——一个Python代码执行环境。
工作流程如下:LLM被指示编写一小段代码来解决问题。应用程序提取并执行这段代码,然后将代码的运行结果反馈给LLM。
例如,对于问题“2的平方根是多少?”,LLM不需要一个专门的 square_root 工具,它可以直接生成并请求执行代码:
import math;
print(math.sqrt(2))
正如吴恩达(Andrew Ng)在分享个人经验时所说:
“有好几次,在我参与的一些智能代理应用项目中,它为了帮我完成各种任务而生成的代码解决方案,其巧妙程度都让我感到非常惊喜和高兴。”
真相四:这个“超级工具”也伴随着真实的风险
允许LLM生成并运行任意代码,无疑是一把双刃剑。第三节中提到的那种能让模型巧妙解决复杂问题的灵活性,也正是其潜在风险的来源——它们是同一枚硬币的两面。
一个令人印象深刻的真实案例是:一个开发团队使用的智能编程代理在执行任务时,错误地生成并执行了命令 remove star.py,这导致项目目录中一个关键的Python文件被删除了。事后,那个代理还“道歉”说:“我犯了个蠢到家的错误。”
为了防范此类风险,最佳实践至关重要:必须在安全的沙箱环境(Sandbox Environment)中运行LLM生成的代码。像Docker或E2B这样的沙箱可以将代码的执行限制在一个隔离的容器内,即使代码出现问题或具有恶意,其造成的任何损害也会被限制在沙箱内部,无法影响到更广泛的系统。
真相五:AI社区正在联手解决“重复造轮子”的问题
在过去,每个开发团队如果想让自己的AI应用连接到Slack、GitHub或Google Drive等常用服务,都必须自己编写一套专属的封装代码和函数。如果社区中有 M 个应用和 N 个工具,开发者总共需要完成 M x N 次的集成工作,这造成了巨大的重复劳动。
为了解决这个“重复造轮子”的痛点,模型上下文协议(Model Context Protocol, MCP)应运而生。MCP最初由Anthropic公司提出,现已被许多其他公司和开发者广泛采用,旨在为工具和数据源创建一个通用的标准接口。
这一标准带来的好处是显而易见的:有了MCP,一个服务(如GitHub)只需要提供一个符合MCP标准的服务器,之后任何遵循MCP标准的客户端(即AI应用)都可以直接使用它的工具。这使得整个社区的工作量从 M * N 锐减到 M + N。
这种标准化的趋势标志着AI生态系统正日趋成熟,让开发者能够更轻松、更高效地构建功能强大的、支持工具的应用程序。
结论
工具使用将大型语言模型从一个被动的信息检索者,提升为了一个能够执行任务、与外部世界交互的主动智能代理。这不仅仅是技术上的进步,更是一种范式的转变。
既然AI已经学会了使用工具,那么在我们的工作和生活中,有哪些曾经不可能完成的任务,现在已经触手可及了呢?
「吴恩达Agentic AI 模块3」代理式人工智能工具使用与MCP学习指南
测验
简答题
请用2-3句话回答以下每个问题,以检验您对核心概念的理解。
- 在大型语言模型(LLM)的背景下,“工具使用”指的是什么?为什么它很重要?
- 当一个工具可用时,LLM是否总会选择使用它?请解释其决策过程。
- 根据“早期时代”的方法,描述LLM使用工具的完整流程。
- 在手动的工具创建方法中,LLM是直接调用函数吗?请解释其背后的机制。
- 像AISuite这样的现代库与旧的手动提示方法相比,是如何简化为LLM提供工具的过程的?
- 在现代工具使用语法中,JSON模式(JSON schema)扮演什么角色?它通常包含哪些信息?
- 为什么说代码执行是LLM一个特别强大的工具?
- 与LLM代码执行相关的主要风险是什么?缓解这种风险的最佳实践是什么?
- 模型上下文协议(MCP)旨在解决开发者社区面临的什么核心问题?
- 什么是MCP客户端和MCP服务器?请举例说明它们如何交互。
测验答案
- 在大型语言模型(LLM)的背景下,“工具使用”指的是什么?为什么它很重要? “工具使用”是指让LLM自行决定何时请求调用一个函数来执行特定动作、收集信息或完成其他任务。这非常重要,因为就像人类使用工具能完成更多事情一样,为LLM提供工具(即函数)能极大地扩展其能力,使其能够完成仅凭自身训练数据无法完成的任务,例如获取当前时间或查询数据库。
- 当一个工具可用时,LLM是否总会选择使用它?请解释其决策过程。 不,LLM并不会总是使用可用的工具。开发者可以将决策权留给LLM,让它根据具体的用户提示来判断是否需要调用工具。例如,如果向一个拥有“获取当前时间”工具的LLM询问绿茶中的咖啡因含量,它会直接生成答案,而不会调用该工具,因为它判断该工具与问题无关。
- 根据“早期时代”的方法,描述LLM使用工具的完整流程。 该流程包括几个步骤:首先,开发者通过提示告知LLM可用工具及其调用格式。当LLM决定使用工具时,它会生成一个特定格式的文本(如FUNCTION: getCurrentTime)作为请求。然后,开发者编写的代码会捕获并解析这个输出,实际执行相应的函数,并将函数返回的结果反馈给LLM,最后LLM利用这个新信息生成最终的答复。
- 在手动的工具创建方法中,LLM是直接调用函数吗?请解释其背后的机制。 不,LLM不直接调用函数。其机制是,LLM通过生成一种特定格式的文本输出来“请求”调用函数。开发者需要编写代码来监视LLM的输出,当检测到这个预定义的格式时,由开发者的代码来负责解析请求、执行相应的函数,并将结果返回给LLM。
- 像AISuite这样的现代库与旧的手动提示方法相比,是如何简化为LLM提供工具的过程的? 现代库(如AISuite)能自动处理将函数描述给LLM的复杂过程。开发者无需手动编写冗长的提示来解释每个工具的用法,库会自动检查函数的文档字符串(docstring),并生成一个结构化的JSON模式。这个模式清晰地告诉LLM函数的名称、功能和参数,从而实现了工具的无缝集成。
- 在现代工具使用语法中,JSON模式(JSON schema)扮演什么角色?它通常包含哪些信息? JSON模式扮演着向LLM详细描述可用工具的角色。它是一个结构化的数据格式,通常包含函数的名称(name)、功能的详细描述(description,通常从代码的文档字符串中提取)以及函数的参数列表(parameters),包括每个参数的类型和用途。这使得LLM能够准确理解何时以及如何调用该工具。
- 为什么说代码执行是LLM一个特别强大的工具? 代码执行之所以强大,是因为它赋予了LLM极大的灵活性和解决复杂问题的能力。相比于为每个数学运算(加、减、平方根等)创建单独的工具,允许LLM编写并执行代码能让它处理几乎无限种类的计算和逻辑任务。这种通用性使得LLM能够用创新的代码解决方案来应对各种复杂的用户请求。
- 与LLM代码执行相关的主要风险是什么?缓解这种风险的最佳实践是什么? 主要风险在于安全性,LLM可能生成任意甚至有害的代码,例如错误地删除文件(如remove star.py)。缓解此风险的最佳实践是在一个安全的沙盒环境(如Docker或E2B)中运行代码。沙盒环境可以隔离代码的执行,从而降低数据丢失、敏感数据泄露或对系统造成损害的风险。
- 模型上下文协议(MCP)旨在解决开发者社区面临的什么核心问题? MCP旨在解决开发者在将各种工具和数据源(如Slack、GitHub、数据库)集成到其LLM应用中时普遍存在的重复劳动问题。在MCP出现之前,每个应用开发者都需要为同一个数据源编写自定义的封装代码,导致社区总工作量呈 M(应用数)乘以 N(工具数)的规模。MCP通过提供一个标准化的集成协议,将工作量减少到 M 加 N 的规模。
- 什么是MCP客户端和MCP服务器?请举例说明它们如何交互。 MCP客户端是需要访问工具或数据的应用程序,而MCP服务器是提供这些工具或数据访问的服务封装层。例如,一个云桌面应用(MCP客户端)在需要获取GitHub仓库信息时,会向GitHub MCP服务器发送请求。服务器接收请求(如“列出最新的拉取请求”),执行相应的操作,并将结果返回给客户端,客户端再将这些信息提供给LLM以生成最终的摘要。
论文题目建议
- 比较并对比“早期时代”手动启用工具使用的方法与使用AISuite等现代库的自动化方法。深入探讨开发者工作流程的演变、底层机制的差异以及对应用开发效率的影响。
- 结合工具使用的概念,讨论大型语言模型中的“代理式”(Agentic)行为。模型能够决定何时使用哪个工具(包括决定不使用任何工具)的能力,是如何促成其代理特质的?
- 深入分析将“代码执行”作为LLM工具的独特优势和重大风险。开发者应如何平衡这一工具的强大功能与保障系统安全性和可靠性的需求?请从技术和实践角度进行论述。
- 阐述模型上下文协议(MCP)的原理及其在构建复杂、多工具LLM应用生态系统中的重要性。该协议如何改变应用开发者(客户端)和服务提供商(服务器)之间的互动模式和开发范式?
- 设想你正在构建一个课程资料中描述的“日历助手代理”。请详细规划你的开发步骤,从定义必要的工具(如检查日历、创建约会),到实现与LLM的交互循环。在你的规划中,请引用全部五个相关模块(工具定义、创建、语法、代码执行、MCP)中的概念。
关键术语词汇表
| 术语 | 定义 |
|---|---|
| 工具使用 (Tool Use) | 允许大型语言模型(LLM)自行决定何时请求调用一个函数,以执行特定动作、收集信息或完成其他任务的过程。 |
| 工具 (Tool) | 提供给LLM的函数或代码,LLM可以请求调用这些函数来扩展其能力。 |
| 代理工作流 (Agentic Workflow) | LLM作为一个代理,自主地规划步骤、决策并使用工具来完成复杂任务的一系列流程。 |
| 代码执行 (Code Execution) | 一种特殊的工具,允许LLM编写代码并请求执行,用于解决数学计算、数据处理等复杂问题,具有高度的灵活性。 |
| 沙盒环境 (Sandbox Environment) | 一个安全的、隔离的执行环境(如Docker),用于运行由LLM生成的代码,以防止潜在的有害操作对系统造成损害。 |
| JSON模式 (JSON Schema) | 一种基于JSON的结构化数据格式,用于向LLM详细描述一个工具(函数),包括其名称、功能描述和参数。 |
| AISuite | 一个开源库,它简化了向LLM提供工具的过程,能够自动从函数的文档字符串生成JSON模式,与OpenAI的语法非常相似。 |
| 模型上下文协议 (MCP) | 由Anthropic提出并被广泛采用的一个标准协议,旨在简化应用程序(客户端)对外部工具和数据源(服务器)的访问,减少开发者的重复集成工作。 |
| MCP客户端 (MCP Client) | 遵循MCP标准的应用程序,它消费(使用)由MCP服务器提供的工具或数据资源。 |
| MCP服务器 (MCP Server) | 遵循MCP标准的服务,它将对数据源(如GitHub, Slack)的访问封装起来,向MCP客户端提供工具或数据资源。 |
模块3:工具使用「Andrew Ng:Agentic AI」
3.1 什么是工具?
在本模块中,你将学习大型语言模型(LLM)的工具使用,这意味着让你的 LLM 决定何时可能需要请求调用一个函数来执行某些操作、收集一些信息或做其他事情。就像我们人类使用工具能比徒手做更多的事情一样,LLM 在获得工具后也能做更多的事情。不过,我们给 LLM 的工具不是锤子、扳手和钳子,而是函数,让它能够请求调用,从而完成更多任务。让我们来看一看。
如果你问一个可能在数月前训练好的 LLM:“现在几点了?” 那个训练好的模型并不知道确切的时间,所以它很可能会回答:“抱歉,我无法获取当前时间。” 但是,如果你编写一个函数并让 LLM 能够访问这个函数,那么它就能给出一个更有用的答案。
当我们让 LLM 调用函数,或者更准确地说,让 LLM 请求调用函数时,这就是我们所说的“工具使用”,而工具就是我们提供给 LLM、可供其请求调用的函数。
具体来说,工具使用是这样工作的。在这个例子中,我将把上一张幻灯片中展示的 getCurrentTime 函数提供给 LLM。当你接着提示它“现在几点了?”时,LLM 可以决定调用 getCurrentTime 函数。该函数将返回当前时间,这个时间随后会作为对话历史的一部分反馈给 LLM,最后 LLM 就可以输出,比如说,“现在是下午3点20分”。
所以,步骤顺序是:首先有输入提示。在这种情况下,LLM 查看可用的工具集(本例中只有一个工具),并决定调用该工具。这个工具是一个函数,它会返回一个值,该值被反馈给 LLM,然后 LLM 最终生成它的输出。
现在,工具使用的一个重要方面是,我们可以让 LLM 自行决定是否使用任何工具。所以在同样的设置下,如果我问它:“绿茶里有多少咖啡因?” LLM 不需要知道当前时间来回答这个问题,所以它可以直接生成答案:“绿茶通常含有这么多咖啡因”,并且它在这样做的时候没有调用 getCurrentTime 函数。
在我的幻灯片中,我将使用 LLM 上方带有虚线框的这个标记,来表示我们正在向 LLM 提供一组工具,供其在认为合适时选择使用。这与你在之前视频中看到的一些例子相反,在那些例子中,我作为开发者硬编码了,例如,在研究代理的某个特定点上总是进行网络搜索。相比之下,getCurrentTime 函数的调用并不是硬编码的,而是由 LLM 自行决定是否要请求调用 getCurrentTime 函数。再次强调,我们将使用这个虚线框标记来表示我们何时向 LLM 提供一个或多个工具,由 LLM 来决定它想调用哪些工具(如果有的话)。
这里还有一些工具使用可能帮助基于 LLM 的应用生成更好答案的例子:
- 网络搜索:如果你问它:“你能找一些在加州山景城附近的意大利餐厅吗?” 如果它有一个网络搜索工具,那么 LLM 可能会选择调用一个网络搜索引擎来查询“加州山景城附近的餐厅”,并使用获取到的结果来生成输出。
- 数据库查询:如果你经营一家零售店,并且希望能够回答像“给我看看购买了白色太阳镜的顾客”这样的问题,如果你的 LLM 被赋予了访问查询数据库工具的权限,那么它可能会在销售表中查找哪些条目是销售了一副白色太阳镜的,然后用这个信息来生成输出。
- 计算:最后,如果你想进行利率计算:“如果我存入500美元,利率为5%,10年后我会得到多少钱?” 如果你恰好有一个利率计算工具,那么它就可以调用利率计算函数来计算出结果。或者,事实证明,你稍后会看到的一种方法是,让 LLM 编写代码,比如写一个这样的数学表达式,然后对它求值,这将是另一种让 LLM 计算出正确答案的方式。
因此,作为开发者,你需要思考你希望应用真正做什么样的事情,然后创建所需的函数或工具,并将它们提供给 LLM,让它能够使用适当的工具来完成任务,比如餐厅推荐器、零售问答系统或金融助手可能需要做的事情。所以,根据你的应用,你可能需要实现并向你的 LLM 提供不同的工具。
到目前为止,我们看过的大多数例子都只向 LLM 提供了一个工具或一个函数。但在许多用例中,你会希望向 LLM 提供多个工具或多个函数,供其选择调用哪一个(或不调用)。例如,如果你正在构建一个日历助手代理,你可能希望它能够满足这样的请求:“请在我的日历上找一个周四的空闲时段,并与 Alice 安排一个约会。”
在这个例子中,我们可能会向 LLM 提供一个用于“安排约会”(即发送日历邀请)的工具或函数,一个用于“检查日历”以查看我何时有空的工具,以及一个用于“删除约会”(如果它想取消现有的日历条目)的工具。因此,给定这些指令,LLM 首先会决定,在可用的不同工具中,它可能应该首先使用“检查日历”。所以它会调用一个 check_calendar 函数,该函数将返回我周四的空闲时间。基于这些信息(它们会被反馈给 LLM),它然后可以决定下一步是选择一个时间段,比如说下午3点,然后调用 make_appointment 函数来向 Alice 发送一个日历邀请,并将其添加到我的日历中。该操作的输出(希望是一个确认日历条目已成功发送的通知)被反馈给 LLM,最后,LLM 可能会告诉我:“你与 Alice 的约会已经安排在周四下午3点。”
能够让你的 LLM 访问工具是一件非常重要的事情。它将使你的应用变得更加强大。在下一个视频中,我们将看一看如何编写函数,如何创建工具,然后将它们提供给你的 LLM。让我们进入下一个视频。
3.2 创建一个工具
一个 LLM 决定调用函数的过程,一开始可能看起来有点神秘,因为 LLM 被训练的目的只是生成输出文本或输出文本的 token。那么这是如何工作的呢?在这个视频中,我想与你一步步地走过,让一个 LLM 能够调用函数的过程到底是什么样的。让我们来看一看。
所以,工具只是 LLM 可以请求执行的代码或函数,就像我们在上一个视频中看到的 getCurrentTime 函数一样。现在,当今领先的 LLM 都被直接训练来使用工具,但我想和你一起走一遍,如果你必须自己编写提示来告诉它何时使用工具,那会是什么样子。这是我们在 LLM 被直接训练来使用工具之前的早期时代必须做的事情。尽管我们现在不再完全这样做,但这希望能让你更好地理解这个过程,我们将在下一个视频中讲解更现代的语法。
如果你已经实现了这个 getCurrentTime 函数,那么为了把这个工具给 LLM,你可能会写一个像这样的提示。你可能会告诉它:“LLM,你可以访问一个名为 getCurrentTime 的工具。要使用它,我希望你打印出以下文本:全大写的 FUNCTION,然后打印出 getCurrentTime。如果我看到这个文本,即全大写的 FUNCTION 和 getCurrentTime,我就知道你想让我为你调用 getCurrentTime 函数。”
当一个用户问:“现在几点了?” LLM 就会意识到它需要调用或请求调用 getCurrentTime 函数。所以 LLM 就会输出它被告知的内容,它会输出“FUNCTION: getCurrentTime”。现在,我必须已经写好代码来检查 LLM 的输出,看是否存在这个全大写的 FUNCTION。如果存在,那么我需要提取出 getCurrentTime 这个参数,来弄清楚 LLM 想调用哪个函数。然后我需要编写代码来实际调用 getCurrentTime 函数,并提取出输出,比如说,是早上8点。然后,是开发者编写的代码,也就是我的代码,必须把早上8点这个时间反馈给 LLM,作为对话历史的一部分。当然,对话历史包括了最初的用户提示、请求是函数调用的事实等等。最后,LLM 在知道了之前发生的一切——用户提问,它请求函数调用,然后我调用了函数并返回了早上8点——之后,最终可以查看所有这些信息并生成最终的回复,即“现在是早上8点”。
所以要明确一点,为了调用一个函数,LLM 并不直接调用函数。相反,它会以一种特定的格式输出一些内容,比如这样,来告诉我需要为 LLM 调用这个函数,然后告诉 LLM 它请求的函数输出了什么。
在这个例子中,我们只给了 LLM 一个函数,但你可以想象,如果我们给了它三四个函数,我们可以告诉它输出全大写的 FUNCTION,然后是它想调用的函数名,甚至可能还有这些函数的一些参数。实际上,现在让我们来看一个稍微复杂一点的例子,其中 getCurrentTime 函数接受一个时区作为参数,用于获取你想要的那个时区的当前时间。
对于第二个例子,我写了一个函数,用于获取指定时区的当前时间,这里的时区是 getCurrentTime 函数的输入参数。所以为了让 LLM 使用这个工具来回答问题,比如“新西兰现在几点了?”(因为我的阿姨在那里,所以在给她打电话之前,我确实会查一下新西兰的时间),为了让 LLM 使用这个工具,你可能会修改系统提示,说:“你可以使用 getCurrentTime 工具来获取特定时区的时间。要使用它,请输出如下内容:getCurrentTime,然后包含时区。” 这是一个简化的提示,在实践中,你可能会在提示中加入更多细节,告诉它函数是什么,如何使用等等。
在这个例子中,LLM 会意识到它需要获取新西兰的时间,所以它会生成这样的输出:“FUNCTION: getCurrentTime(Pacific/Auckland)”。这是新西兰的时区,因为奥克兰是新西兰的一个主要城市。然后,我必须编写代码来搜索 LLM 的输出中是否出现了这个全大写的 FUNCTION,如果出现了,我就需要提取出要调用的函数。最后,我将调用 getCurrentTime 并传入指定的参数,这个参数是由 LLM 生成的,即 Pacific/Auckland,也许返回的是早上4点。然后像往常一样,我把这个信息反馈给 LLM,LLM 输出:“新西兰现在是早上4点。”
总结一下,让 LLM 使用工具的过程如下:
- 提供工具:你必须向 LLM 提供工具,即实现函数,然后告诉 LLM 它是可用的。
- LLM 请求调用:当 LLM 决定调用一个工具时,它会生成一个特定的输出,让你知道你需要为 LLM 调用这个函数。
- 执行并返回结果:然后你调用函数,得到它的输出,把你刚刚调用的函数的输出反馈给 LLM。
- LLM 继续执行:LLM 接着用这个信息来决定下一步做什么,在我们这个视频的例子中,就是直接生成最终的输出,但有时它甚至可能决定下一步是去调用另一个工具,然后这个过程继续。
现在,事实证明,这种全大写 FUNCTION 的语法有点笨拙。这是我们在 LLM 被原生训练或自己知道如何请求调用工具之前所做的事情。对于现代的 LLM,你不需要告诉它输出全大写的 FUNCTION,然后去搜索全大写的 FUNCTION 等等。相反,LLM 被训练来使用一种特定的语法,来非常清楚地请求它何时想要调用一个工具。在下一个视频中,我想与你分享,让 LLM 请求调用工具的现代语法到底是什么样的。让我们进入下一个视频。
3.3 工具语法
让我们来看一看如何编写代码,让你的 LLM 能够调用工具。这是我们旧的、不带时区参数的 getCurrentTime 函数。让我向你展示如何使用 AI Suite 开源库来让你的 LLM 调用工具。顺便说一下,技术上讲,正如你在上一个视频中看到的,LLM 并不调用工具,LLM 只是请求你调用工具。但在构建 agentic 工作流的开发者中,我们许多人偶尔会直接说“LLM 调用工具”,尽管技术上并非如此,但这只是一个更简短的说法。
这里的语法与 OpenAI 调用这些 LLM 的语法非常相似,只不过在这里,我使用的是 AI Suite 库,这是一个我和一些朋友开发的开源包,它使得调用多个 LLM 提供商变得容易。所以代码语法是这样的,如果这对你来说看起来很多,别担心,你会在编码实验中看到更多。但简而言之,这与 OpenAI 的语法非常相似,你写 response = client.chat.completions.create,然后选择模型,在这种情况下,我们使用 OpenAI 的模型 GPT-4o,messages=messages,假设你已经把想传递给 LLM 的消息放进了一个数组里,然后你会写 tools=,后面跟着一个你希望 LLM 能够访问的工具列表。在这种情况下,只有一个工具,就是 getCurrentTime。
然后,别太担心 max_turns 这个参数。包含这个参数是因为,在一个工具调用返回后,LLM 可能会决定调用另一个工具,而在那个工具调用返回后,LLM 可能又会决定调用再一个工具。所以 max_turns 只是一个上限,限制你希望 LLM 在停止之前连续请求工具多少次,以避免可能出现的无限循环。在实践中,除非你的代码在做一些异常雄心勃勃的事情,否则你几乎永远不会达到这个限制。所以我不担心 max_turns 参数,我通常只把它设置为5,但在实践中,它影响不大。
事实证明,使用 AI Suite,getCurrentTime 函数会自动以一种适当的方式被描述给 LLM,使得 LLM 知道何时调用它。所以,你不需要手动编写一个长长的提示来告诉 LLM getCurrentTime 是做什么的,AI Suite 中的这个语法会自动完成。为了让它看起来不那么神秘,它的工作方式是,它实际上会查看与 getCurrentTime 相关联的文档字符串(docstring),也就是 getCurrentTime 中的这些注释,以便弄清楚如何向 LLM 描述这个函数。
所以,为了说明这是如何工作的,这里再次是那个函数,这里是使用 AI Suite 调用 LLM 的代码片段。在幕后,这会创建一个详细描述该函数的 JSON schema。右边的这个东西,就是实际传递给 LLM 的内容。具体来说,它会提取函数名,即 getCurrentTime,然后还有函数的描述,这个描述是从文档字符串中提取出来的,用来告诉 LLM 这个函数是做什么的,这让它能够决定何时调用它。有些 API 要求你手动构建这个 JSON schema,然后再把这个 JSON schema 传递给 LLM,但 AI Suite 包会自动为你完成这个工作。
来看一个稍微复杂一点的例子,如果你有这个更复杂的 getCurrentTime 工具,它还有一个输入的 time_zone 参数,那么 AI Suite 会创建这个更复杂的 JSON schema。和之前一样,它会提取出函数名 getCurrentTime,从文档字符串中提取出描述,然后还会识别出参数是什么,并根据左边这里显示的文档向 LLM 描述它们,这样当它生成调用工具的函数参数时,它就知道参数应该是像 America/New_York 或 Pacific/Auckland 或其他时区这样的东西。
所以,如果你执行左下角的这段代码片段,它将使用 OpenAI 的 GPT-4o 模型,看 LLM 是否想调用这个函数,如果想,它就会调用这个函数,从函数中获取输出,将输出反馈给 LLM,最多这样做五轮,然后返回响应。请注意,如果 LLM 请求调用 getCurrentTime 函数,AI Suite 或这个 client 会为你调用 getCurrentTime,所以你不需要自己显式地去做。所有这些都在你必须编写的这一个函数调用中完成了。需要注意的是,其他一些 LLM 接口的实现中,你必须手动完成那一步,但对于这个特定的包,所有这些都封装在这个 client.chat.completions.create 函数调用中了。
所以,你现在知道了如何让一个 LLM 调用函数了。我希望你在实验中玩得开心,当你为 LLM 提供几个函数,然后 LLM 决定去现实世界中采取行动,去获取更多信息来满足你的请求时,这真的很神奇。如果你以前没有玩过这个,我想你会觉得这非常酷。
事实证明,在所有你可以给 LLM 的工具中,有一个有点特殊,那就是代码执行工具。它被证明非常强大。如果你能告诉一个 LLM:“你可以编写代码,我会有一个工具为你执行那些代码”,因为代码可以做很多事情,所以当我们给一个 LLM 编写和执行代码的灵活性时,这被证明是给予 LLM 的一个极其强大的工具。所以代码执行是特殊的。让我们在下一个视频中讨论 LLM 的代码执行工具。
3.4 代码执行
在我参与过的几个 agentic 应用中,我给了 LLM 编写代码来执行我希望它完成的任务的选项。有好几次,我真的对它为了解决我的各种任务而生成的代码解决方案的巧妙之处感到惊讶和欣喜。所以,如果你不怎么使用代码执行,我想你可能会对它能让你的 LLM 应用做什么感到惊讶和欣喜。让我们来看一看。
让我们以构建一个可以输入数学应用题并为你解答的应用为例。你可能会创建一些工具,用来做加法、减法、乘法和除法。如果有人说:“请计算13.2加18.9”,那么它会触发加法工具,然后给你正确的答案。但如果现在有人输入:“2的平方根是多少?” 嗯,一种方法是为平方根写一个新的工具,但接着可能又需要一个新的东西来做指数运算。实际上,如果你看看你现代科学计算器上的按钮数量,你难道要为每一个按钮以及我们想在数学计算中做的更多事情都创建一个单独的工具吗?
所以,与其试图一个接一个地实现工具,另一种方法是让它编写并执行代码。要告诉 LLM 编写代码,你可能会写一个像这样的提示:“编写代码来解决用户的查询。将你的答案以 Python 代码的形式返回,并用 <execute_python> 和 </execute_python> 标签界定。” 所以,给定一个像“2的平方根是多少”这样的查询,LLM 可能会生成这样的输出。然后你可以使用模式匹配,例如正则表达式,来寻找开始和结束的 execute_python 标签,并提取出中间的代码。所以在这里,你得到了绿色框中显示的两行代码,然后你可以为 LLM 执行这段代码,并得到输出,在这种情况下是 1.4142 等等。最后,这个数值答案被传回给 LLM,它就可以为原始问题写一个格式优美的答案。
你可以用几种不同的方式为 LLM 执行代码。一种是使用 Python 的 exec 函数。这是一个内置的 Python 函数,它会执行你传入的任何代码。这对于让你的 LLM 真正编写代码并让你执行这些代码来说非常强大,尽管这有一些安全隐患,我们稍后会在这个视频中看到。还有一些工具可以让你在更安全的沙箱环境中运行代码。当然,2的平方根是一个相对简单的例子。一个 LLM 也可以准确地编写代码来做,例如,利率计算,并解决比这难得多的数学计算问题。
对这个想法的一个改进,你在我们关于反思的部分中已经有所了解,那就是如果代码执行失败——比如说,LLM 出于某种原因生成的代码不完全正确——那么将那个错误信息传回给 LLM,让它反思并可能修改它的代码,再试一两次。这有时也能让它得到一个更准确的答案。
现在,运行 LLM 生成的任意代码确实有很小的可能会导致不好的事情发生。最近,我的一个团队成员正在使用一个高度 agentic 的编码器,它竟然选择在一个项目目录内执行 rm *.py(删除所有 .py 文件)。所以这实际上是一个真实的例子。最终那个 agentic 编码器确实道歉了,它说:“是的,那确实是对的,那是一个极其愚蠢的错误。” 我想我很高兴这个 agentic 编码器真的很抱歉,但它已经删除了一堆 Python 文件。幸运的是,团队成员在 GitHub 仓库里有备份,所以没有造成真正的损害,但如果这段错误地删除了一堆文件的任意代码在没有备份的情况下被执行了,那就不太好了。
所以,代码执行的最佳实践是在沙箱环境中运行它。在实践中,任何单行代码的风险都不是那么高。所以如果我坦率地说,许多开发者会直接执行来自 LLM 的代码,而不会做太多检查。但如果你想更安全一点,那么最佳实践是创建一个沙箱,这样如果 LLM 生成了糟糕的代码,数据丢失或敏感数据泄露等的风险就会降低。像 Docker 或 E2B 这样的沙箱环境(E2B 是一个轻量级的沙箱环境)可以降低任意代码以损害你的系统或环境的方式被执行的风险。
事实证明,代码执行是如此重要,以至于许多 LLM 的训练者实际上会做一些特殊的工作,来确保代码执行在他们的应用上能良好运行。但我希望,当你把这个作为你可以提供给 LLM 的又一个工具时,能让你的应用变得更加强大。
到目前为止,在我们讨论的内容中,你必须一个一个地创建工具并将它们提供给你的 LLM。事实证明,许多不同的团队正在构建类似的工具,并且不得不做所有这些构建函数并将它们提供给 LLM 的工作。但最近出现了一个名为 MCP(模型上下文协议)的新标准,它使得开发者更容易地获得一大套可供 LLM 使用的工具。这是一个越来越重要的协议,越来越多的团队正在使用它来开发基于 LLM 的应用。让我们在下一个视频中了解一下 MCP。
3.5 MCP
MCP,即模型上下文协议(Model Context Protocol),是 Anthropic 提出的一个标准,但现在已被许多其他公司和许多开发者采纳,作为一种让 LLM 访问更多上下文和更多工具的方式。有许多开发者正在围绕 MCP 生态系统进行开发,因此了解这一点将让你为你的应用获得更多的资源。让我们来看一看。
这是 MCP 试图解决的痛点。如果一个开发者正在编写一个应用,想要与来自 Slack、Google Drive 和 GitHub 的数据集成,或者访问来自 Postgres 数据库的数据,那么他们可能必须编写代码来包装 Slack 的 API,以提供函数给应用;编写代码来包装 Google Drive 的 API,以解析给应用;对于其他这些工具或数据源也类似。然后,在开发者社区中一直发生的情况是,如果一个不同的团队在构建一个不同的应用,那么他们也会自己与 Slack、Google Drive、GitHub 等进行集成。所以许多开发者都在为这些类型的数据源构建自定义的包装器。因此,如果有 M 个应用正在开发,而市面上有 N 个工具,那么社区完成的总工作量就是 M 乘以 N。
MCP 所做的是,为应用获取工具和数据源提出了一个标准,这样社区需要完成的总工作量现在是 M 加 N,而不是 M 乘以 N。MCP 的初始设计非常关注如何为 LLM 提供更多上下文,或者如何获取数据。所以很多初始的工具都是只获取数据的。如果你阅读 MCP 的文档,它将这些称为“资源”(resources)。但 MCP 既提供了对数据的访问,也提供了应用可能想调用的更通用的函数。
事实证明,有许多 MCP 客户端(clients),这些是想要访问工具或数据的应用;也有许多 MCP 服务器(servers),这些通常是软件包装器,它们提供对 Slack、GitHub 或 Google Drive 中数据的访问,或者允许你在这些不同类型的资源上执行操作。所以今天,MCP 客户端(消费工具或资源的应用)和 MCP 服务器(提供工具和资源的服务)的列表正在迅速增长。我希望你会发现构建自己的 MCP 客户端很有用,你的应用也许有一天会成为一个 MCP 客户端。如果你想为其他开发者提供资源,也许你有一天也可以构建自己的 MCP 服务器。
让我给你看一个使用 MCP 客户端的快速例子。这是一个云桌面应用,它已经连接到了一个 GitHub MCP 服务器。所以当我输入这个查询:“总结这个 URL 处的 GitHub 仓库中的 readme.md 文件”(这实际上是一个 AI Suite 的仓库),这个应用(它是一个 MCP 客户端)就会使用 GitHub MCP 服务器,并发出请求:“请从这个仓库的 AI Suite 中获取 readme.md 文件”。然后它得到这个响应,内容很长。所有这些都被反馈到 LLM 的上下文中,然后 LLM 生成这个 markdown 文件的摘要。
现在让我输入另一个请求:“最新的拉取请求(pull requests)是什么?” 这反过来又导致 LLM 使用 MCP 服务器发出一个不同的请求,即“列出拉取请求”。这是 GitHub 的 MCP 服务器提供的另一个工具。所以它发出这个请求,带有仓库名 AI Suite,排序方式为更新时间,列出20个等等。然后它给出这个响应,这个响应被反馈给 LLM,LLM 接着写出这个关于该仓库最新拉取请求的漂亮的文本摘要。
MCP 是一个重要的标准。如果你想了解更多,DeepLearning.ai 也有一个短期课程,专门更深入地讲解 MCP 协议,如果你感兴趣的话,可以在完成本课程后去看看。我希望这个视频能让你简要地了解它为什么有用,以及为什么现在许多开发者都在按照这个标准进行构建。
这把我们带到了关于工具使用的最后一个视频。我希望通过让你的 LLM 访问工具,你能构建出更强大的 agentic 应用。在下一个模块中,我们将讨论评估和错误分析。事实证明,我所看到的,区分那些能非常好地执行 agentic 工作流的人和那些效率不高的团队的事情之一,就是你推动一个规范的评估流程的能力。在下一组视频中,我认为这可能是整个课程中最重要的一个模块,我希望能与你分享一些关于如何使用评估来推动 agentic 工作流开发的最佳实践。期待在下一个模块见到你。
Module 3: Tool Use
3.1 What are tools?
0:00
In this module, you learn about tool use by LLMs, and that means letting your LLM decide when it might want to request to have a function called to take some action, or gather some information, or do something else.
0:12
Just as we as humans can do a lot more with tools than we can with just our bare hands, LLMs too can also do a lot more with access to tools.
0:23
But rather than using hammers and spanners and pliers, when we give tools, that is, functions, for an LLM to request a call, that's what lets it do a lot more.
0:33
Let's take a look.
0:34
If you were to ask an LLM that's been trained maybe many months ago, what time is it right now?
0:40
Well, that trained model does not know exactly what time it is, and so hopefully it responds, sorry, I do not have access to the current time.
0:48
But if you were to write a function and give the LLM access to this function, then that lets it respond with a more useful answer.
0:56
When we let LLMs call functions, or more precisely, let an LLM request to call functions, that's what we mean by tool use, and the tools are just functions that we provide to the LLM that it can request to call.
1:08
In detail, this is how tool use works.
1:11
In this example, I'm going to give the getCurrentTime function that I showed on the previous slide to the LLM.
1:18
When you then prompt it, what time is it, the LLM can decide to call the getCurrentTime function.
1:23
That will return the current time, which is then fed back to the LLM in the conversational history, and finally the LLM can output is, say, 3.20pm.
1:33
So the sequence of steps is, there's the input prompt.
1:36
The LLM, in this case, looks at the set of tools, which is just one tool in this example, but looks at the set of tools available, and it will decide in this example to call the tool.
1:47
The tool is a function that then returns a value, that value is fed back to the LLM, and then finally the LLM generates its output.
1:54
Now, one important aspect of tool use is, we can leave it up to the LLM to decide whether or not to use any of the tools.
2:03
So for the same setup, if I was asking it, how much caffeine is in green tea, the LLM doesn't need to know the current time to answer this, and so it can generate an answer directly,
2:12
green tea typically has this much caffeine, and it does so without invoking the getCurrentTime function.
2:18
In my slides, I'm going to use this notation with this dashed box on top of the LLM to indicate that we're providing a set of tools to the LLM for the LLM to choose to use when it deems appropriate.
2:30
This is as opposed to some examples you saw in earlier videos, where I, as a developer, had hard-coded in, for example, that I will always do a web search at this point in the research agent.
2:41
In contrast, the getCurrentTime function call is not hard-coded in, it's up to the LLM to decide whether or not it wants to request a call to the getCurrentTime function.
2:51
And again, we're going to use this dashed box notation to indicate when we're giving one or more tools to the LLM for the LLM to decide what tools, if any, it wants to call.
3:02
Here are some more examples of when tool use may help an LLM-based app generate better answers.
3:08
If you were to ask it, can you find some Italian restaurants near Mountain View, California?
3:12
If it has a web search tool, then an LLM might elect to call a web search engine for a query, restaurants near Mountain View, California, and use the results that fetches to generate the output.
3:23
Or if you are running a retail store and you want to be able to answer questions like, show me customers who bought white sunglasses, if your LLM is given access to a query database tool, then it might look up the table of sales for what entries had a pair of white sunglasses sold and then use that to then generate the output.
3:44
Finally, if you wanted to do an interest rate calculation, if I were to deposit $500 and after 10 years, an interest rate of 5%, what would I have?
3:53
If you happen to have an interest calculation tool, then it could invoke the interest calculation function to calculate that.
4:01
Or it turns out, one thing you see later is letting an LLM write code, like just write a mathematical expression like this, and then to evaluate it, that would be another way to let an LLM calculate the right answer.
4:16
So as a developer, it'll be up to you to think through what are the sorts of things you want an application to really do, and then to create the functions or the tools that are needed to make them available to the LLM to let it use the appropriate tools to complete the sorts of tasks that maybe a restaurant recommender or a retail question answer or a finance assistant may want to do.
4:39
So depending on your application, you may have to implement and make different tools available to your LLM.
4:46
So far, most of the examples we've gone through made only one tool or one function available to the LLM.
4:52
But there are many use cases where you want to make multiple tools or multiple functions available for the LLM for it to choose which of any to call.
4:59
For example, if you're building a calendar assistant agent, you might then want it to be able to fulfill requests like, please find a free slot on Thursday in my calendar and make an appointment with Alice.
5:11
So in this example, we might make available to the LLM a tool or a function to make an appointment, that is, to send a calendar invite, to check the calendar to see when I might be free, as well as to delete the appointment if it ever wants to cancel an existing calendar entry.
5:26
And so given the set of instructions, the LLM would first decide that of the different tools available, probably the first one it should use is check calendar.
5:35
So call a check calendar function that will return when I am free on Thursday.
5:40
Based on that information, which is fed back to the LLM, it can then decide that the next step is to pick a slot, let's say 3 p.m., and then to call the make appointment function to send a calendar invite to Alice, as well as to add it to my calendar.
5:56
The output of that, which hopefully is a confirmation that the calendar entry was sent out successfully, is fed back to the LLM, and then lastly, the LLM might tell me your appointment is set up with Alice at 3 p.m. Thursday.
6:08
Being able to give your LLM access to tools is a pretty big deal. It will make your applications much more powerful.
6:15
In the next video, we'll take a look at how to write functions, how to create tools to then make them available to your LLM. Let's go on to the next video.
3.2 Creating a tool
0:01
The process of how an LLM decides to call a function maybe seems a little bit mysterious
0:05
initially because an LLM is just trained to generate output text or output text tokens.
0:11
So how does that work? In this video, I'd like to walk through with you step-by-step
0:15
what the process of getting an LLM to be able to get a function called really looks like.
0:21
Let's take a look.
0:22
So tools are just codes or functions that an LLM can request to be executed,
0:27
like this getCurrentTime function that we saw from the previous video.
0:31
Now, today's leading LLMs are all trained directly to use tools,
0:36
but I want to walk through with you what it would look like if you had to write prompts yourself
0:41
to tell it when to use tools, and this is what we had to do in an earlier era
0:46
before LLMs were trained directly to use tools.
0:49
And even though we don't do it exactly this way anymore,
0:51
this will hopefully give you a better understanding of the process,
0:54
and we'll walk through the more modern syntax in the next video.
0:57
If you've implemented this function to getCurrentTime,
1:00
then in order to give this tool to the LLM, you might write a prompt like this.
1:05
You may tell it, LLM, you have access to a tool called getCurrentTime.
1:09
To use it, I want you to print out the following text.
1:12
Print out all caps function and then print out getCurrentTime.
1:15
And if I ever see this text, all caps function and then getCurrentTime,
1:19
that's when I know you want me to call the getCurrentTime function for you.
1:23
When a user asks, what time is it?
1:25
The LLM will then realize it needs to call or request to get called the getCurrentTime function.
1:29
And so the LLM will then output what it was told.
1:32
It'll output all caps function: getCurrentTime.
1:35
Now, I then have to have written code to look at the output of the LLM
1:40
to see if there is this all caps function.
1:42
And if so, then I need to pull out the argument of this getCurrentTime
1:47
to figure out what function the LLM wants to call.
1:49
And then I need to write code to actually call the getCurrentTime function
1:53
and then pull out the output, which is, let's say, 8 a.m.
1:57
And then it is the developer written code, my code,
2:00
that has to take 8 a.m. and feed that time, 8 a.m.,
2:04
back into the LLM as part of this conversational history.
2:07
And the conversational history, of course, includes the initial user prompt,
2:10
the fact that the request is a function call, and so on.
2:13
And lastly, the LLM, knowing what had happened earlier,
2:17
that the user asks a question, requests a function call,
2:19
and then also that I call the function and return 8 a.m.
2:23
Finally, the LLM can look at all this and generate the final response,
2:26
which is, it is 8 a.m.
2:28
So to be clear, in order to call a function,
2:31
the LLM doesn't call the function directly.
2:34
It instead outputs something in a specific format like this
2:38
that tells me that I need to call the function for the LLM
2:41
and then tell the LLM what was the output of the function I requested.
2:45
In this example, we had given the LLM only a single function,
2:49
but you can imagine if we gave it three or four functions,
2:52
we could tell it to output functions in all caps,
2:55
then the name of the function it wants called,
2:57
and maybe even some arguments of these functions.
3:00
In fact, now let's take a look at a slightly more complex example
3:03
where the getCurrentTime function accepts an argument for the time zone
3:08
at which you want the current time.
3:10
For this second example, I've written a function
3:14
that gets the current time in a specified time zone,
3:16
where here the time zone is the input argument
3:19
to the getCurrentTime function.
3:22
So to let the LLM use this tool to answer questions
3:25
like maybe, what time is it in New Zealand?
3:27
Because my answer is there, so before I call her up,
3:29
I do look up what time it is in New Zealand.
3:31
To let the LLM use this tool, you might modify the system prompt
3:35
to say you can use the getCurrentTime tool for a specific time zone.
3:39
To use it, I'll put the following, getCurrentTime,
3:41
and then, you know, include the time zone.
3:43
And this is an abbreviated prompt.
3:45
In practice, you might put more details than this into the prompt
3:48
to tell it what is the function, how to use it, and so on.
3:50
In this example, the LLM will then realize
3:53
it needs to fetch the time in New Zealand,
3:56
and so it will generate output like this,
3:58
function: getCurrentTime Pacific/Auckland.
4:02
This is the New Zealand time zone
4:04
because Auckland is a major city in New Zealand.
4:06
Then I have to write code to search for whether or not
4:10
this function all caps appeared in the LLM output,
4:13
and if so, then I need to pull out the function to call.
4:17
Lastly, I will then call getCurrentTime
4:19
with the specified arguments, which is generated by the LLM,
4:22
which is Pacific/Auckland, and maybe returns is 4 a.m.
4:25
Then as usual, I feed this to the LLM and the LLM outputs.
4:28
It is 4 a.m. in New Zealand.
4:30
To summarize, here's the process for getting LLM to use tools.
4:34
First, you have to provide the tool to the LLM,
4:37
implement the function, and then tell the LLM that it is available.
4:40
When the LLM decides to call a tool,
4:42
it then generates a specific output that lets you know
4:45
that you need to call the function for the LLM.
4:48
Then you call the function, get its output,
4:51
take the output of the function you just called,
4:53
and give that output back to the LLM,
4:55
and the LLM then uses that to go on to whatever it decides to do next,
4:59
which in our examples in this video was to just generate the final output,
5:03
but sometimes it may even decide that the next step
5:05
is to go call yet another tool, and the process continues.
5:09
Now, it turns out that this all-caps function syntax is a little bit clunky.
5:13
This is what we used to do before LLMs were trained natively
5:17
or to know by themselves how to request that tools be called.
5:21
With modern LLMs, you don't need to tell it to output all-caps function,
5:25
then search for all-caps function, and so on.
5:27
Instead, LLMs are trained to use a specific syntax
5:31
to request very clearly when it wants a tool called.
5:34
In the next video, I want to share with you
5:36
what the modern syntax actually looks like
5:38
for letting LLMs request to have tools be called.
5:42
Let's go on to the next video.
3.3 Tool syntax
0:01
Let's take a look at how to write code to have your LLM get tools called.
0:04
Here's our old getCurrentTime function without the time zone argument.
0:09
Let me show you how to use the AI Suite open source library in order to have your LLM call
0:14
tools. By the way, technically, as you saw from the last video, the LLM doesn't call the tool.
0:20
The LLM just requests that you call the tool. But among developers building agentic workflows,
0:25
many of us will occasionally just say the LLM calls the tool, even though it's not technically
0:30
what happens, but because it's just a shorter way to say it.
0:34
This syntax here is very similar to the OpenAI syntax for calling these LLMs, except that here,
0:41
I'm using the AI Suite library, which is an open source package that some friends and I had worked
0:46
on that makes it easy to call multiple LLM providers. So the code syntax, and if this
0:53
looks like a lot to you, don't worry about it. You'll see more of this in the code labs.
0:57
But very briefly, this is very similar to the OpenAI syntax, where you say response equals
1:02
client check, completions create, then select the model, which in this case, we'll use the
1:07
OpenAI model GPT-4o, messages equals messages, assuming you've put into an array here the
1:13
messages you want to pass the LLM, and it will say tools equals, then a list of the tools you want
1:19
the LLM to have access to. And in this case, there's just one tool, which is get current time,
1:23
and then don't worry too much about the max turns parameter. This is included because
1:28
after a tool call returns, the LLM might decide to call another tool, and after that tool call
1:33
returns, the LLM might decide to call yet another tool. So max turns is just a ceiling on how many
1:38
times you want the LLM to request one tool after another before you stop to just break out of a
1:44
possible infinite loop. In practice, you almost never hit this limit unless your code is doing
1:49
something unusually ambitious. So I wouldn't worry about the max turns parameter. I usually just set it to
1:54
five, but in practice, it doesn't matter that much. And it turns out that with AISuite, the function
2:00
get current time is automatically described to the LLM in an appropriate way to enable the LLM to
2:06
know when to call it. So rather than you needing to manually write a long prompt to tell the LLM,
2:12
once get current time, this syntax in AISuite does that automatically. And to make it seem not
2:18
too mysterious, the way it does that, it actually looks at the dot string associated with get current
2:23
time with this comments in get current time in order to figure out how to describe this function
2:30
to the LLM. So to illustrate how this works, here's the function again, and here's the
2:36
snippet of code using AISuite to call the LLM. Behind the scenes, what this will do is create a
2:43
JSON schema that describes the function in detail. And this over here on the right is what is actually
2:50
passed to the LLM. And specifically, it will pull the name of the function, which is get current time,
2:55
and then also a description of the function, which is pulled out from the doc string to tell the LLM
3:01
what this function does, which lets it decide when to call it. There's some APIs which require that
3:06
you manually construct this JSON schema and then pass this JSON schema to the LLM, but the AISuite
3:13
package does this automatically for you. To go through a slightly more complex example, if you
3:18
have this more complex get current time tool that also has an input time zone parameter, then AISuite
3:25
will create this more complex JSON schema where, as before, it pulls out the name of the function,
3:30
which is get current time, pulls out the description from the doc string, and then also identifies
3:35
what are the parameters and describes them to the LLM based on the documentation here shown on the
3:41
left, so that when it's generating the function arguments to call the tool, it knows that it
3:47
should be something like America/New York or Pacific/Auckland or some other time zone.
3:53
And so if you execute this code snippet here on the lower left, it will use the OpenAI
3:59
GPT-4o model, see if the LLM wants the function called, and if so, it'll call the function,
4:04
get the output from the function, feed that back to the LLM, and do that up to a maximum of five
4:10
turns and then return the response. Note that if the LLM requests to call the get current time
4:17
function, AISuite or this client, it will call the get current time for you, so you don't need
4:23
to explicitly do it yourself. All that is done in this single function call that you have to write.
4:29
Just note that there are some other implementations of LLM interfaces where you have to do that step
4:36
manually, but with this particular package, this is all wrapped into this client chat completions
4:41
create function call. So you now know how to get an LLM to call functions, and I hope that you enjoy
4:49
playing with this in the labs, and it's actually really amazing when you provide a few functions
4:54
to LLM and LLM decides to go and take action in the world, go and get more information
4:59
to fulfill your requests. If you haven't played with this before, I think you'll find this to be
5:04
really cool. It turns out that of all the tools you can give an LLM, there's one that's a bit special,
5:10
which is a code execution tool. It turns out to be really powerful. If you can tell an LLM,
5:16
you can write code, and I will have a tool to execute that code for you, because code can do
5:21
a lot of things, and we give an LLM the flexibility to write code and have code executed. That turns
5:28
out to be an incredibly powerful tool to give to LLMs. So code execution is special. Let's go
5:35
on to the next video to talk about the code execution tool for LLMs.
3.4 Code execution
0:05
In a few agentic applications I've worked on, I gave the LLM the option to write code to then
0:06
carry out the task I wanted it to. And I've been a few times now, I've been really surprised and
0:10
delighted by the cleverness of the code solutions it generated in order to solve various tasks for
0:17
me. So if you haven't used code execution much, I think you might be surprised and delighted at
0:23
what this will let your LLM applications do. Let's take a look. Let's take an example of
0:29
building an application that can input math word problems and solve them for you. So you might
0:36
create tools that add numbers, subtract numbers, multiply numbers, and divide numbers. And if
0:40
someone says, please add 13.2 plus 18.9, then it triggers the add tool and then it gets you the
0:46
right answer. But what if someone now types in, what is the square root of two? Well, one thing
0:50
you could do is write a new tool for a square root, but then maybe some new thing is needed to
0:56
carry out exponentiation. And in fact, if you look at the number of buttons on your modern
1:02
scientific calculator, are you going to create a separate tool for every one of these buttons and
1:06
the many more things that we would want to do in math calculation? So instead of trying to implement
1:12
one tool after another, a different approach is to let it write and execute code. To tell the LLM
1:19
to write code, you might write a prompt like this. Write code to solve the user's query. Return your
1:24
answer as Python code delimited with execute Python and closing execute Python tags. So given a query
1:31
like what is the square root of two, the LLM might generate outputs like this. You can then use
1:37
pattern matching, for example, a regular expression to look for the start and end execute Python tags
1:44
and extract the code in between. So here you get these two lines of code shown in the green box,
1:50
and you can then execute this code for the LLM and get the output, in this case, 1.4142 and so on.
1:57
Lastly, this numerical answer is then passed back to the LLM and it can write a nicely formatted
2:04
answer to the original question. There are a few different ways you can carry out the code
2:08
execution step for the LLM. One is to use Python's exec function. This is a built-in Python function
2:15
which will execute whatever code you pass in. And this is very powerful for your LLM to really
2:21
write code and get you to execute that code, although there are some security implications
2:26
which we'll see later in this video. And then there are also some tools that will let you run the code
2:31
in a safer sandbox environment. And of course, square root of two is a relatively simple example.
2:38
An LLM can also accurately write code to, for example, do interest calculations and solve much harder
2:45
math calculations than this. One refinement to this idea, which you sort of saw in our section
2:52
on reflection, is that if code execution fails, so if for some reason the LLM had generated code
2:58
that wasn't quite correct, then passing that error message back to the LLM to let it reflect and
3:04
maybe revise this code and try another one or two times. That can sometimes also allow it to get a
3:10
more accurate answer. Now, running arbitrary code that an LLM generates does have a small chance of
3:17
causing something bad to happen. Recently, one of my team members was using a highly agentic coder
3:24
and it actually chose to remove star.py within a project directory. So this is actually a real
3:30
example. And eventually that agentic coder did apologize. It said, yes, that's actually right,
3:35
that was an incredibly stupid mistake. I guess I was glad that this agentic coder was really sorry,
3:40
but I already deleted a bunch of Python files. Unfortunately, the team member had it backed
3:44
up on GitHub repo, so there was no real harm done, but it would have been not great if this
3:50
arbitrary code, which made the mistake of deleting a bunch of files, had been executed
3:55
without the backup. So the best practice for code execution is to run it inside a sandbox
4:00
environment. In practice, the risk for any single line of code is not that high. So if I'm being
4:07
candid, many developers will execute code from the LLM without too much checking. But if you want to
4:12
be a bit safer, then the best practice is to create a sandbox so that if an LLM generates bad
4:19
code, there's a lower risk of data loss or leakage of sensitive data and so on. So sandbox
4:26
environments like Docker or E2B as a lightweight sandbox environment can reduce the risk of
4:33
arbitrary codes being executed in a way that damages your system or your environment.
4:39
It turns out that code execution is so important that a lot of trainers of LLMs actually do special
4:46
work to make sure that code execution works well on their applications. But I hope that as you add
4:52
this as one more tool for you to potentially offer to LLMs or let you make your applications
4:58
much more powerful. So far and what we've discussed, you have to create tools and make them
5:05
available one at a time to your LLM. It turns out that many different teams are building similar
5:11
tools and having to do all this work of building functions and making them available to the OMs.
5:18
But there is recently a new standard called MCP, Model Context Protocol, that's making it much
5:24
easier for developers to get access to a huge set of tools for LLMs to use. This is an important
5:31
protocol that more and more teams are using to develop LLM based applications. Let's go learn
5:37
about MCP in the next video.
3.5 MCP
0:00
MCP, the Model Context Protocol, was a standard proposed by Anthropic but now adopted by many
0:07
other companies and by many developers as a way to give an LLM access to more context and to
0:13
more tools. There are a lot of developers developing around the MCP ecosystem and so
0:18
learning about this will give you a lot more access to resources for your applications.
0:24
Let's take a look. This is the pain points that MCP attempts to solve. If one developer is writing
0:31
an application that wants to integrate with data from Slack and Google Drive and GitHub or access
0:37
data from a Postgres database, then they might have to write code to wrap around Slack APIs
0:42
to have functions to provide to the application, write code to wrap around Google Drive APIs to
0:47
parse the application, and similarly for these other tools or data sources. Then what has been
0:54
happening in the developer community is if a different team is building a different application,
0:59
then they too will integrate by themselves with Slack and Google Drive and GitHub and so on.
1:04
So many developers were all building custom wrappers around these types of data sources.
1:10
And so if there are M applications being developed and there are N tools out there,
1:16
the total amount of work done by the community was M times N. What MCP did was propose a standard
1:24
for applications to get access to tools and data sources so that the total work that needs to be
1:29
done by the community is now M plus N rather than M times N. The initial design of MCP focused a lot
1:38
on how to give more context to an LLM or how to fetch data. So a lot of the initial tools were
1:44
ones that would just fetch data. And if you read the MCP documentation, that refers to these as
1:51
resources. But MCP gives access to both data as well as the more general functions that an
1:57
application may want to call. And it turns out that there are many MCP clients. These are the
2:04
applications that want access to tools or to data as well as service, which are often the software
2:11
wrappers that then give access to data in Slack or GitHub or Google Drive or allows you to take
2:16
actions at these different types of resources. So today there's a rapidly growing list of MCP
2:23
clients that consume the tools or the resources as well as MCP service that provide the tools and
2:28
the resources. And I hope that you find it useful to build your own MCP client. Your application
2:35
maybe one day will be an MCP client. And if you want to provide resources to other developers,
2:40
maybe you can build your own MCP server someday. Let me show you a quick example of using an MCP
2:46
client. This is a cloud desktop app and it has been connected to a GitHub MCP server. So when
2:54
I enter this query, summarize the readme.md from the GitHub repo at this URL, this is actually
3:00
an AI suite repo. Then this application, which is an MCP client, uses the GitHub MCP server with
3:07
the request, please get the file readme.md from the repo AI suite from this repo. And then it
3:14
gets this response, which is pretty long. All this is then fed back to the LLMs context and the LLM
3:21
then generates the summary of the markdown file. Now let me enter another request, which is let me
3:27
enter what are the latest pull requests. This in turn causes the LLMs to use the MCP server to make
3:36
a different request, to list the pull request. This is another tool provided by GitHub's MCP
3:42
server. And so it makes this request with repo AI suite, sort, going to update it, list 20, and so on.
3:50
And then it gives this response, which is fed back to the LLM and the LLM then writes this nice
3:55
text summary of the latest pull request for this repo. MCP is an important standard. If you want to
4:01
learn more about it, DeepLearning.ai also has a short course that goes much deeper into just the MCP
4:08
protocol that you can check out after finishing the course, if you're interested. I hope this
4:13
video gives you a brief overview of why it's useful and also why many developers are now building to
4:20
this standard. This brings us to the last video on tool use. And I hope that by giving your own access
4:27
to tools, you build and build agentic applications that are much more powerful. In the next module,
4:34
we'll talk about evaluations and error analysis. It turns out that one of the things I've seen
4:41
that distinguishes people that can execute agentic workflows really well versus teams that are not
4:47
as efficient at it is your ability to drive a disciplined evaluation process. In the next
4:55
set of videos, which I think is maybe the most important module of this entire course,
5:00
I hope to share with you some of the best practices of how to use evals to drive
5:05
development of agentic workflows. Look forward to seeing you in the next module.