引言
在开发的技术选型的过程中,真正容易引起混淆的是人工智能助手与人工智能代理这两类应用。表面上,这两者常常因称呼上的类似而被误解为同一类应用产品,但实质上,它们基于完全不同的底层架构。这种差异关键在于它们处理任务和交互方式的根本设计理念,影响了它们在实际应用中的功能和效能。首先,人工智能助手的核心功能在于辅助用户完成一些通常需要人工参与的既定任务,其主要作用是替代人工执行特定操作。这一过程依赖于Function Calling技术——大模型调用特定函数的能力,这些函数可以是内置的,也可以是用户自定义的。在执行任务时,大模型会通过分析问题来决定何时以及如何调用这些函数,从而增强其处理特定任务的能力。例如,在电商智能客服案例中,通过给大模型配置查询商品数据库和优惠政策这两个工具(Tools),所构造出来的智能客服能够准确理解并回应用户的具体需求。这种Function_call
的机制使得大模型可以有效利用外部工具或内部功能,从而提升其执行复杂任务的能力。
在处理这类问题时,我们主要依赖于大模型的原生意图识别能力以及单个、多个或并行函数的调用功能。然而,问题也很明显的显现出来了,就是当用户的单次请求中包含多个意图时。例如,用户询问:“你家卖健身手套吗?现在有什么优惠?” 理想的处理流程应如下:
- 首先调用一个工具查询数据库后台,确认是否有该商品。如果没有,直接回复用户。
- 如果商品存在,根据第一个工具的查询结果,再调用第二个工具查询该商品的优惠信息,并计算后回复给用满意度。
但很明显,无论我们怎么努力,都会发现这不是单纯的Function Calling
能够实现的复杂需求。所以才来到了AI Agent的应用领域 – 人工智能代理。
我们可以通过一些简化的比喻来理解Function calling
和AI Agent
这两个概念:想象你正在使用刚刚购买的华为手机,当你想要拍照时,你会打开相机应用。这个相机应用就是一个人工智能助手,它提供了拍照的功能。你通过点击相机图标来调用
这个功能,然后就可以拍照、编辑照片等。在这个比喻中,相机应用就是预定义的函数,而打开相机应用并使用其功能的技术就是Function Calling
。而对于人工智能代理,想象一个机器人管家。这个机器人能够理解你的指令,比如“请打扫客厅”,并且能够执行这个任务。机器人管家就是一个AI Agent,它能够自主地感知环境(比如识别哪些地方是客厅),做出决策(比如决定打扫的顺序和方法),并执行任务(比如使用吸尘器打扫)。在这个比喻中,机器人管家是一个能够自主行动和做出复杂决策的实体,而其背后支撑其做这一系列复杂任务的技术,就是AI Agent
。
总结来说,Function Calling
就像是调用一个具体的功能或工具来帮助你完成特定的任务,而AI Agent
则更像是一个能够独立思考和行动的个体,它可以在没有人类直接指导的情况下完成一系列复杂的任务。所以能够很明显的感觉出,以 AI Agent 为底层架构的应用,其核心是要具备自主决策 + 高效执行的能力。
ReAct Agent基本理论
ReAct Agent 也称为 ReAct
,是一个用于提示大语言模型的框架,它首次在 2022 年 10 月的论文《ReAct:Synergizing Reasoning and Acting in Language Models》中引入,并于2023 年 3 月修订。该框架的开发是为了协同大语言模型中的推理和行动,使它们更加强大、通用和可解释。通过交叉推理和行动,ReAct 使智能体能够动态地在产生想法和特定于任务的行动之间交替。
ReAct 框架有两个过程,由 Reason
和 Act
结合而来。从本质上讲,这种方法的灵感来自于人类如何通过和谐地结合思维和行动来执行任务,就像我们上面“我想去北京旅游”这个真实示例一样。
首先第一部分 Reason,它基于一种推理技术——思想链(CoT), CoT是一种提示工程,通过将输入分解为多个逻辑思维步骤,帮助大语言模型执行推理并解决复杂问题。这使得大模型能够按顺序规划和解决任务的每个部分,从而更准确地获得最终结果,具体包括:
- 分解问题:当面对复杂的任务时,CoT 方法不是通过单个步骤解决它,而是将任务分解为更小的步骤,每个步骤解决不同方面的问题。
- 顺序思维:思维链中的每一步都建立在上一步的结果之上。这样,模型就能从头到尾构造出一条逻辑推理链。
比如,一家商店以 100 元的价格出售产品。如果商店降价20%,然后加价10%,产品的最终价格是多少?
- 步骤 1 — 计算降价20%后的价格:如果原价是100元,商店降价20%,我们计算降价后的价格: 10 x (1–0.2) = 80.
- 步骤 2 — 计算上涨 10% 后的价格:降价后,产品价格为 80 元。现在商店涨价10%:80 x (1 + 0.1) = 88.
- 结论:先降价后加价后,产品最终售价为88元。
但是,在 CoT 提示工程的限定下,大模型仍然会产生幻觉。因为经过长期的使用,大家发现在推理的中间阶段会产生不正确的答案或上下游的传播错误,所以,Google DeepMind 团队开发了 ReAct
的技术来弥补这一点。ReAct 采用的是 思想-行动-观察循环的思路,其中代理根据先前的观察进行推理以决定行动。这个迭代过程使其能够根据其行动的结果来调整和完善其方法。如下图所示:👇
在这个过程中,Question
指的是用户请求的任务或需要解决的问题,Thought
用来确定要采取的行动并向大模型展示如何创建/维护/调整行动计划,Action Input
是用来让大模型与外部环境(例如搜索引擎、维基百科)的实时交互,包括具有预定义范围的API。而Observation
阶段会观察执行操作结果的输出,重复此过程直至任务完成。
由ReAct
思想抽象出来的代理工程,其基本示例如下所示:
prompt = """
You run in a loop of Thought, Action, Observation, Answer.
At the end of the loop you output an Answer
Use Thought to describe your thoughts about the question you have been asked.
Use Action to run one of the actions available to you.
Observation will be the result of running those actions.
Answer will be the result of analysing the Observation
Your available actions are:
calculate:
e.g. calculate: 4 * 7 / 3
Runs a calculation and returns the number - uses Python so be sure to use floating point syntax if necessary
wikipedia:
e.g. wikipedia: Django
Returns a summary from searching Wikipedia
Always look things up on Wikipedia if you have the opportunity to do so.
Example session:
Question: What is the capital of France?
Thought: I should look up France on Wikipedia
Action: wikipedia: France
You should then call the appropriate action and determine the answer from
the result
You then output:
Answer: The capital of France is Paris
"""
如上示例所示:在ReAct
框架下的代理工程描述中,明确的是代理的任务和执行过程。面对不同的场景,其实我们只需要改变的是:1. 代理的身份设定 2. 代理完成任务所需要的工具。代理的身份通常通过system
角色来定义,而所需的工具及其应用则是上一节课中我们重点讨论的Function Calling
中,关于外部工具的定义和使用方法。。只不过,在代理框架下这些工具的应用方法需要进行适当的调整以适应不同的需求。
实例
代理的一个主要组成部分是系统提示词,一般是通过 ‘role’ : ‘system’ 来设定,比如:
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一位专业的人工智能领域的教授,具备50年的教学经验"},
{"role": "user", "content": "请你详细的介绍一下:什么是人工智能?"},
]
)
print(response.choices[0].message.content)
在这个示例中,system
角色被设置为“你是一位专业的人工智能领域的教授,具备50年的教学经验”。这一设定使得大模型能够从一个人工智能教授的角度出发,详尽介绍人工智能的定义、分类、应用、挑战和未来展望。这种详细的介绍反映了教授丰富的知识和对领域的深刻理解。
通过这两个示例可以看出,system
角色设定对大模型的回答有决定性影响。这一机制允许我们开发者或使用者通过改变角色设定来控制大模型的知识范围和行为,使大模型能够适应不同的对话场景和用户需求。这种方法在代理工程中是非常有用的,特别是在需要代理以不同身份进行交互的情况下,可以有效地模拟多种人物角色的行为和专业知识。这种系统提示会直接引导代理推理问题并酌情选择有助于解决问题的外部工具。 那么,我们就应该在系统提示词中,去定义如下所示的完整 AI Agent 自主推理的核心流程:
基于上述流程,要通过代码实现ReAct Agent
,能够非常明确需要做的三项工作是:
- 精心设计代理的完整提示词,并在大模型的
system
角色设置中进行设定,以确保代理的行为和知识与其角色一致。 - 实时将用户的问题作为变量输入,填充到系统提示(System Prompt)中,确保代理能够根据当前的用户需求生成响应。
- 构建并整合所需的工具,使
ReAct Agent
能够完成预定任务,这些工具也应作为变量被嵌入到系统提示中,以便在运行时调用。
接下来,我们就来实现一个基础但功能完整的ReAct Agent
流程。这个AI代理的设计需求是能够实时搜索网络上的信息,并在需要进行数学计算时,调用计算工具。具体使用的工具包括:
- Serper API:利用这个API,代理可以根据给定的关键词执行实时Google搜索,并返回搜索结果中的第一个条目。
- calculate:这个功能通过使用Python的
eval()
函数来解析并计算数学表达式,从而得到数值和互动性。
Step 1. 设计完整的代理工程提示
正如我们上面介绍的 ReAct
原理,其本质是采用了思想-行动-观察
的循环过程来逐步实现复杂任务,那么其系统提示(System Prompt)就可以设计如下:
system_prompt = """
You run in a loop of Thought, Action, Observation, Answer.
At the end of the loop you output an Answer
Use Thought to describe your thoughts about the question you have been asked.
Use Action to run one of the actions available to you.
Observation will be the result of running those actions.
Answer will be the result of analysing the Observation
Your available actions are:
calculate:
e.g. calculate: 4 * 7 / 3
Runs a calculation and returns the number - uses Python so be sure to use floating point syntax if necessary
fetch_real_time_info:
e.g. fetch_real_time_info: Django
Returns a real info from searching SerperAPI
Always look things up on fetch_real_time_info if you have the opportunity to do so.
Example session:
Question: What is the capital of China?
Thought: I should look up on SerperAPI
Action: fetch_real_time_info: What is the capital of China?
PAUSE
You will be called again with this:
Observation: China is a country. The capital is Beijing.
Thought: I think I have found the answer
Action: Beijing.
You should then call the appropriate action and determine the answer from the result
You then output:
Answer: The capital of China is Beijing
Example session
Question: What is the mass of Earth times 2?
Thought: I need to find the mass of Earth on fetch_real_time_info
Action: fetch_real_time_info : mass of earth
PAUSE
You will be called again with this:
Observation: mass of earth is 1,1944×10e25
Thought: I need to multiply this by 2
Action: calculate: 5.972e24 * 2
PAUSE
You will be called again with this:
Observation: 1,1944×10e25
If you have the answer, output it as the Answer.
Answer: The mass of Earth times 2 is 1,1944×10e25.
Now it's your turn:
""".strip()
提示词的第一部分告诉大模型如何通过我们之前看到的流程的标记部分循环处理问题,第二部分描述计算和搜索维基百科的工具操作,最后是一个示例的会话。整体结构非常清晰。
Step 2. 定义工具
import requests
import json
def fetch_real_time_info(query):
# API参数
params = {
'api_key': '0f31d8c556xxxxxxxxxxxd9', # 使用您自己的API密钥
'q': query, # 查询参数,表示要搜索的问题。
'num': 1 # 返回结果的数量设为1,API将返回一个相关的搜索结果。
}
# 发起GET请求到Serper API
api_result = requests.get('https://google.serper.dev/search', params)
# 解析返回的JSON数据
search_data = api_result.json()
# 提取并返回查询到的信息
if search_data["organic"]:
return search_data["organic"][0]["snippet"]
else:
return "没有找到相关结果。"
函数 calculate
接收一个字符串参数 operation,该字符串代表一个数学运算表达式,并使用 Python 的内置函数 eval 来执行这个表达式,然后返回运算的结果。函数的返回类型被指定为 float,意味着期望返回值为浮点数。
def calculate(operation: str) -> float:
return eval(operation)
最后,定义一个名为 available_actions
的字典,用来存储可用的函数引用,用来在后续的Agent 实际执行 Action 时可以根据需要调用对应的功能。
available_actions = {
"fetch_real_time_info": fetch_real_time_info,
"calculate": calculate,
}
Step 3. 开发大模型交互接口
接下来,定义大模型交互逻辑接口。这里我们实现一个聊天机器人的 Python 类,将系统提示(system)与用户(user)或助手的提示(assistant)分开,并在实例化ChatBot时对其进行初始化。 核心逻辑为 __call__
函数负责存储用户消息和聊天机器人的响应,调用execute
来运行代理。完整代码如下所示:
import openai
import re
import httpx
from openai import OpenAI
class ChatBot:
def __init__(self, system=""):
self.system = system
self.messages = []
if self.system:
self.messages.append({"role": "system", "content": system})
def __call__(self, message):
self.messages.append({"role": "user", "content": message})
result = self.execute()
self.messages.append({"role": "assistant", "content": result})
return result
def execute(self):
client = OpenAI()
completion = client.chat.completions.create(model="gpt-4o", messages=self.messages)
return completion.choices[0].message.content
如上所示,这段代码定义了一个ChatBot
的类,用来创建和处理一个基于OpenAI GPT-4
模型的聊天机器人。下面是每个部分的具体解释:
__init__
方法用来接收系统提示(System Prompt),并追加到全局的消息列表中。__call__
方法是Python
类的一个特殊方法, 当对一个类的实例像调用函数一样传递参数并执行时,实际上就是在调用这个类的 call 方法。其内部会 调用execute
方法。- execute 方法实际上就是与
OpenAI
的API进行交互,发送累积的消息历史(包括系统消息、用户消息和之前的回应)到OpenAI的聊天模型,返回最终的响应。
Step 4. 定义代理循环逻辑
从Thought
到 Action
, 最后到 Observation
状态,是一个循环的逻辑,而循环的次数,取决于大模型将用户的原始 Goal
分成了多少个子任务。 所有在这样的逻辑中,我们需要去处理的是:
- 判断大模型当前处于哪一个状态阶段
- 如果停留在
Action
阶段,需要像调用 Function Calling 的过程一样,先执行工具,再将工具的执行结果传递给Obversation
状态阶段。
首先需要明确,需要执行操作的过程是:大模型识别到用户的意图中需要调用工具,那么其停留的阶段一定是在 Action:xxxx : xxxx 阶段,其中第一个 xxx,就是调用的函数名称,第二个 xxxx,就是调用第一个 xxxx 函数时,需要传递的参数。这里就可以通过正则表达式来进行捕捉。如下所示:
# (\w+) 是一个捕获组,匹配一个或多个字母数字字符(包括下划线)。这部分用于捕获命令中指定的动作名称
# (.*) 是另一个捕获组,它匹配冒号之后的任意字符,直到字符串结束。这部分用于捕获命令的参数。
action_re = re.compile('^Action: (\w+): (.*)$')
我们定义了如下的一个 AgentExecutor
函数。该函数实现一个循环,检测状态并使用正则表达式提取当前停留的状态阶段。不断地迭代,直到没有更多的(或者我们已达到最大迭代次数)调用操作,再返回最终的响应。完整代码如下:
action_re = re.compile('^Action: (\w+): (.*)$')
您暂时无权查看此隐藏内容!