logo
6

使用promptulate框架&百度大模型 V4探究AI agent模式

大模型 LLM 提供了强大的 NLP 及文本生成能力,但是对复杂任务的处理就没有那么得心应手了,基于这个实际需求就诞生了 AI agent 模式,我先后接触并详细研究了 2 个开源的 AI agent 框架,分别是大名鼎鼎的 LangChain 和国产的 promptulate,今天就重点说说 promptulate,虽然他在不断的迭代升级阶段,但是他的架构思想还是非常复杂 AI agent 核心理念的。
要想用好 LLM 这两个工具必须掌握,一个是 prompt(提示词),一个是 AI agent,而且 prompt 是基础,是能否让AI agent精确分解复杂任务的关键,prompt 起到 AI agent 和 LLM 对话的翻译器。
AI agent 就是从一个任务出发,通过和 LLM 的多次交互,把这个任务逐步分解为多个子任务,再把这些子任务交付给不同的模块完成,有的模块是已经有第三方提供的能力,有些就需要开发者自行完成,这就给应用开发提供了很多便利的条件。AI agent 就类似一个指挥调度中心,借助于 LLM 这个 AI 来进行辅助决策。
多任务协同说起来简单,但是让人直观理解还是要费些思量的,我从事多年微服务架构的开发,所以我认为 AI agent 的架构类似于微服务架构,都是将复杂任务拆解为多个可以协同的简单任务来综合完成,我最喜欢用的例子就是计算一个四则运算的复杂表达式,下面就用这个例子详细介绍 AIagent 的架构思想和如何使用promptulate 及百度新发布的 V4 LLM 来实现。
问题:使用 LLM 计算类似 a0+a1*a2-a3+a4/a5的四则运算结果
思考:
规则描述,遵循基本的四则运算规则,即先乘除,后加减,相同优先级时从左到右
任务分解,按照规则将表达式拆解为若干个独立的+,-,*,/运算,分别定义加法、减法、乘法、除法运算,再按照计算的优先顺序多次运用这些独立的算法,知道完成所有计算
为什么要自行开发计算模块,LLM 严格说是语言概率模型,本身不具备计算能力,所以涉及的计算和逻辑最好利用现有的技术成果或者自行开发,下面就是一个简单的示例:同样的四则运算技术问题,文心一言给出的答案并不正确!
第一步的乘法就错了
正确的答案
支持系统选型:
LLM 选择百度大模型ErnieBot-V4
AI agent 框架选择 promptulate
前期准备:
  
  
  
  
  
  
在百度智能云开通千帆大模型的开发者账号,申请文心大模型4.0的邀测权限,如果没有也可以开通 3.5 版本的权限,使用方法一致
安装 promptulate
pip install promptulate
完整代码:

使用promptulate框架开发LLM agent

from promptulate.llms import ErnieBot
import os
from promptulate.agents import ToolAgent
from promptulate.tools import define_tool
from promptulate.utils.logger import enable_log
from promptulate.hook import Hook
import time
os.environ["ERNIE_API_KEY"] = "zQ3fMzn?????H7QeclgDQm"
os.environ["ERNIE_API_SECRET"] = "MDW1?????GEKESRA8qTpxwfs"
@Hook.on_tool_create(hook_type="instance")
def handle_tool_create(*args, **kwargs):
print("math tool component create")
@Hook.on_tool_start(hook_type="instance")
def handle_tool_start(*args, **kwargs):
prompt = args[0]
print(f"math tool instance hook start, user prompt: {prompt}")
@Hook.on_tool_result(hook_type="instance")
def handle_tool_result(**kwargs):
result = kwargs["result"]
print(f"math tool component result: {result}")

识别字符串中的分隔符,然后再按照分隔符隔离数据

def split_str(query: str):

判断 query 中是否包含+,-,*,/字符,如果包含,则判断包含那个

if '+' in query:
datas=query.split('+')
elif '-' in query:
datas=query.split('-')
elif '*' in query:
datas=query.split('*')
elif '/' in query:
datas=query.split('/')
elif ',' in query:
datas=query.split(',')

去掉datas中每个数据的前后'号和空格

datas=[d.strip().strip("'") for d in datas]

把 datas 里边的字符串转化为 float

datas=[float(d) for d in datas]
return datas

自定义加法工具

def addition(query:str):
if not query:
return -1
if query.startswith('[') and query.endswith(']'):

去掉query的第1个字符和最后一个字符

query = query[1:-1]
datas=split_str(query)
if len(datas) < 2:
return -1
return datas[0]+datas[1]

自定义减法工具

def subtraction(query:str):
if not query:
return -1
datas=split_str(query)
if len(datas) < 2:
return -1
return datas[0]-datas[1]

自定义除法工具

def division(query:str):
if not query:
return -1
datas=split_str(query)
if len(datas) < 2:
return -1
return datas[0]/datas[1]

自定义乘法工具

def multiplication(query:str):
if not query:
return -1
datas=split_str(query)
if len(datas) < 2:
return -1
return datas[0]*datas[1]

以时间戳命名保存文件

def save_file(query:str):
if not query:
return -1

文件名称为当前时间戳,扩展名为txt

fileName='./data/'+str(int(time.time()))+'.txt'

将query保存到文件中

with open(fileName, 'w') as f:
f.write(query)
return fileName
def main3():

定义使用那个LLM

llm = ErnieBot(model="ernie-bot-4")

定义外挂的工具集

tools = [
define_tool(name="addition", description="2个数的和或者字符串中出现符号+", callback=addition),
define_tool(name="subtraction", description="2个数的差或者字符串中出现符号-", callback=subtraction),
define_tool(name="multiplication", description="2个数的积或者字符串中出现符号*", callback=multiplication),
define_tool(name="division", description="2个数的商或者字符串中出现符号/", callback=division),
define_tool(name="save_file", description="保存文件", callback=save_file),
]

定义hook集

hooks = [handle_tool_create, handle_tool_start, handle_tool_result]

定义agent

agent=ToolAgent(tools=tools,llm=llm,hooks=hooks)

使用agent执行复杂任务

result=agent.run("计算23+8712-878+123121-1233/234+123,将最终结果保持到文件中。")
print(result)
if name == "main":
enable_log()
main3()
备注:
因为百度文心一言大模型 4.0 还在测试阶段,所以还没有集成到 promptulate 框架里边,如果想使用就直接修改 promptulate 框架的 config.py 里边的配置,将百度api 的 URL 改成V4 版本的即可,如下所示:
prompt 的关键性对照:
这段代码编写完成后,运行的效果并不理想,LLM 对任务的理解非常不准,就开始怀疑给 AI agent 布置任务的时候描述的不是很准确,导致了任务拆解不合理,就多次对这部分的提示词进行了必要的优化,最后发现好的提示词,效果就很好,下面给出示例。
result=agent.run("按照四则运算的规则,先计算乘除,再进行加减,计算23+8712-878+123121-1233/234+123,将最终结果保持到文件中。")
也就是要明确告诉 AI 的思考规则,这里就提醒使用 AI agent 框架的时候也是要关注提示词的。
运行结果(可能每次的过程不完全相同):
Answer the following questions as best you can.
You have access to the following tools:
addition: 2个数的和或者字符串中出现符号+
subtraction: 2个数的差或者字符串中出现符号-
multiplication: 2个数的积或者字符串中出现符号*
division: 2个数的商或者字符串中出现符号/
save_file: 保存文件
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, must be one of [addition, subtraction, multiplication, division, save_file]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: 按照四则运算的规则,先计算乘除,再进行加减,计算878+123*121-1233/234+123,将最终结果保持到文件中。
Thought:
根据四则运算的规则,需要先计算乘法和除法,然后再进行加法和减法。因此,我们需要先计算123乘以121和1233除以234,然后将结果与其他数相加或相减,最后将结果保存到文件中。
Action: multiplication
Action Input: 123, 121
Observation: 14883.0
Thought: 接下来需要计算1233除以234。
Action: division
Action Input: 1233, 234
Observation: 5.269230769230769
Thought: 现在我们已经得到了乘法和除法的结果,可以进行加法和减法运算。
Action: addition
Action Input: 878, 14883.0
Observation: 15761.0
Thought: 接下来需要减去1233除以234的结果。
Action: subtraction
Action Input: 15761.0, 5.269230769230769
Observation: 15755.73076923077
Thought: 最后加上123。
Action: addition
Action Input: 15755.73076923077, 123
Observation: 15878.73076923077
Thought: 最终结果为15878.73076923077,需要将结果保存到文件中。
Action: save_file
Action Input: "result.txt", "15878.73076923077"
Observation: ./data/1697782411.txt
Thought: 最终结果已经保存到文件中,任务完成。
Observation: None
Thought: I now know the final answer
Final Answer: None
[DEBUG] 2023-10-20 14:13:35 [pne hook] hooks <('Agent', 'on_agent_result')> mounted_obj call hook: <[BaseHookSchema(hook_name='on_agent_result', callback=<function StdOutHook.handle_agent_result at 0x7f9c19f02280>, component_type='Agent')]>
[Agent Result] None
Agent End.
None
评论
用户头像