引言
在第9天和第10天,我们分别学习了AI的基础入门知识------如何开启与AI的循环对话、如何引入第三方接口。今天来聊聊如何让AI模拟人类做出决策的思维推理过程。
Agent 智能体
- LLM 开发的一种设计模式,让大模型变得更聪明的一种套路
- 通过结合LLM和其他工具或数据源,增强其能力
什么是ReAct?
- ReAct全称:Reasoning and Action
- Reasoning and Action: 思维链 + 外部工具调用
- 构建一个智能体
- 这种思想是让LLM将一个的问题拆分成多个小问题,一步一步解决
准备工作
这里依然选用deepseek的API服务
依赖安装
需要的依赖
- pip install tools
- pip install prompt // 提示词设计
- pip install openai
创建一个llm.py文件
ini
// 若为首次引入,需要在终端输入 pip install openai
import openai from OpenAI
client = OpenAI(
api_key = "放上你的密钥",
base_url="https://api.deepseek.com/v1"
)
创建一个tools.py文件
这个文件专门用来存放调用外部接口的工具函数,这里自定义一个函数模拟外部接口
python
# 工具列表,第三方函数的说明书
tools =[
{
"name": "get_closing_price",
"description": "使用该工具获取指定的股票信息",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称,例如:贵州茅台、青岛啤酒"
}
},
"required": ["name"]
}
}
]
def get_closing_price(name):
if name == "贵州茅台":
return "1488.21"
elif name == "青岛啤酒":
return "67.92"
else:
return "未找到相关信息"
创建一个prompt.py文件
这个文件是一个专门用来设计提示词的文件,也是AI-ReAct的核心,提示词会引导大模型该如何思考、并嵌套了变量,告诉大模型应该做出什么样的行动
ini
# 使用三引号"""创建多行字符串,可以保留字符串中的换行和格式
REACT_PROMPT = """
{instructions}
TOOLS:
------
您可以使用以下工具:
{tools}
使用工具时,请使用以下格式:
``
思考: 我需要使用工具吗? 是
行动: 要采取的行动,必须是[{tool_names}]其中之一
行动输入: 该行动的输入内容
``
然后等待人类使用 Observation 回复您操作的结果。
... (这个思考/行动/行动输入/观察可以重复 N 次)
当您有回应要对人类说,或者不需要使用工具时,您必须使用以下格式:
``
思考: 我需要使用工具吗? 否
最终答案: [在此输入您的回应]
``
开始!
新输入: {input}
"""
创建一个agent.py文件
这是一个主文件
1-20行都是简单的代码,就是导包和定义了一个发送消息的函数
这里主要介绍循环部分,是如何利用提示词模板引导大模型的
python
# 导入所需的库
import json # 用于处理JSON数据
from llm import client # 导入LLM客户端
from prompt import REACT_PROMPT # 导入预设的提示模板
from tools import get_closing_price,tools # 导入工具函数
import re # 导入正则表达式库
# 定义发送消息到LLM的函数
def send_messages(messages):
"""
向LLM发送消息并获取响应
:param messages: 消息列表
:return: LLM的响应
"""
response = client.chat.completions.create(
model="deepseek-chat", # 使用deepseek-chat模型
messages=messages, # 传入消息列表
temperature=0.1, # 设置温度参数,控制生成文本的随机性
)
return response
if __name__ == "__main__":
# 设置助手的角色说明
instructions = "你是一个股票助手,可以回答股票相关的问题"
# 设置用户查询
query = "青岛啤酒和贵州茅台的收盘价哪个贵?"
# 使用模板构建完整的提示
prompt = REACT_PROMPT.format(instructions=instructions,tools=tools,tool_names="get_closing_price",input=query)
# 初始化消息列表
messages = [{"role": "user", "content": prompt}]
# 开始对话循环
while True:
# 发送消息并获取响应
# print("发送消息到模型...", messages)
response = send_messages(messages)
response_text = response.choices[0].message.content
# 打印模型的回复
print("大模型的回复:")
print(response_text)
# 检查是否有最终答案
# 使用正则表达式搜索回复文本中是否包含"Final Answer:",\s*匹配任意空白字符,(.*)捕获冒号后面的所有内容
final_answer_match = re.search(r'最终答案:\s*(.*)', response_text)
if final_answer_match:
# 从正则匹配结果中提取第一个捕获组(括号内匹配到的内容),即"Final Answer:"后面的文本内容
final_answer = final_answer_match.group(1)
print("最终答案:", final_answer)
# 如果有最终答案,结束对话
break
# 将模型的回复添加到消息历史
messages.append(response.choices[0].message)
# 解析模型回复中的动作和参数
action_match = re.search(r'Action:\s*(\w+)', response_text)
action_input_match = re.search(r'Action Input:\s*({.*?}|".*?")', response_text, re.DOTALL) # 非贪婪匹配,匹配到第一个"}"或"""
# 如果成功解析到动作和参数
if action_match and action_input_match:
tool_name = action_match.group(1) # 获取工具名称
params = json.loads(action_input_match.group(1)) # 解析参数
print("工具名称:", tool_name)
print("参数:", params)
if tool_name == "get_closing_price":
observation = get_closing_price(params["name"]) # 调用工具函数
print("调用第三方API结果:", observation)
# 将观察结果添加到消息历史
messages.append({'role': 'user', 'content': f"observation:{observation}"})
AI-ReAct案例的整体工作逻辑
这个案例实现了一个 ReAct(Reasoning and Acting) 模式的AI Agent,它可以:
- 推理:分析问题并决定是否需要使用工具
- 行动:调用外部工具获取信息
- 观察:处理工具返回的结果
- 循环:重复上述过程直到得出最终答案
对话循环部分代码详解
让我逐步分析 agent.py
中的对话循环:
python
while True:
# 1. 发送消息并获取响应
response = send_messages(messages)
response_text = response.choices[0].message.content
# 2. 检查是否有最终答案
final_answer_match = re.search(r'最终答案:\s*(.*)', response_text)
if final_answer_match:
final_answer = final_answer_match.group(1)
print("最终答案:", final_answer)
break # 找到最终答案,退出循环
# 3. 将模型回复添加到消息历史
messages.append(response.choices[0].message)
# 4. 解析模型回复中的动作和参数
action_match = re.search(r'Action:\s*(\w+)', response_text)
action_input_match = re.search(r'Action Input:\s*({.*?}|".*?")', response_text, re.DOTALL)
# 5. 如果解析到动作,执行工具调用
if action_match and action_input_match:
tool_name = action_match.group(1)
params = json.loads(action_input_match.group(1))
if tool_name == "get_closing_price":
observation = get_closing_price(params["name"])
# 将观察结果添加到消息历史
messages.append({'role': 'user', 'content': f"observation:{observation}"})
为什么需要正则表达式?
正则表达式在这里起到了结构化解析的作用:
1. 检测最终答案
python
final_answer_match = re.search(r'最终答案:\s*(.*)', response_text)
- "最终答案"这四个字是我们模板里提供的,大模型会很大可能按照模板进行回复
- 检测AI回复中是否包含"最终答案:"
\s*
匹配任意空白字符(.*)
捕获冒号后面的所有内容- 如果找到,说明AI已经完成推理,可以结束循环
2. 解析工具调用
python
action_match = re.search(r'Action:\s*(\w+)', response_text)
action_input_match = re.search(r'Action Input:\s*({.*?}|".*?")', response_text, re.DOTALL)
- 从AI的回复中提取工具名称和参数
- 第一个正则提取"Action:"后面的工具名
- 第二个正则提取"Action Input:"后面的JSON参数或字符串参数
工作流程示例
以查询"青岛啤酒和贵州茅台的收盘价哪个贵?"为例:
第一轮对话:
css
用户输入 → AI回复:
思考: 我需要使用工具吗? 是
行动: get_closing_price
行动输入: {"name": "青岛啤酒"}
系统处理:
- 正则表达式解析出工具名和参数
- 调用
get_closing_price("青岛啤酒")
- 返回结果"67.92"
- 将结果添加到消息历史
第二轮对话:
makefile
AI收到observation:67.92 → AI回复:
思考: 我需要使用工具吗? 是
行动: get_closing_price
行动输入: {"name": "贵州茅台"}
第三轮对话:
makefile
AI收到observation:1488.21 → AI回复:
思考: 我需要使用工具吗? 否
最终答案: 贵州茅台的收盘价(1488.21)比青岛啤酒(67.92)贵
关键设计特点
- 状态保持 :通过
messages
列表维护完整的对话历史 - 结构化交互:使用预定义的格式让AI按照特定模式回复
- 工具集成:通过正则表达式解析实现工具调用
- 循环推理:直到找到最终答案才结束
这种设计让AI能够像人类一样进行多步推理,在需要时主动获取外部信息,最终给出综合的答案。