代理 Agent
Agent 最初出现在强化学习任务中,智能体拥有状态空间和动作空间,每执行一个 action都需要通过状态空间和激励来决定下一个 action。而大模型代理使用类似的思想,利用大模型的自然语言分析能力根据当前对话梳理达到目标需要执行的策略,然后一边执行操作一边根据反馈分析下一个操作,最后达到目标,生成总结输出。Agent 是目前认为最能够将大模型实际落地的应用方向,总的来说 Agent 其实就是提示词工程,通过更好的提示词来激发模型的能力。核心思想是使用语言模型(LLM)作为推理的大脑,以制定解决问题的计划、借助工具实施动作。以开源项目 langchain 为例,一个 agents 框架由几个关键组件如下:
Agent:制定计划和思考下一步需要采取的行动。
Tools:解决问题的工具。一个toolkit通常包含3-5个工具。
AgentExecutor:AgentExecutor是agent的运行时环境。这是实际调用agent并执行其选择的动作的部分。
工具+执行器
我们为了从搜索引擎获取我们想要的信息需要下面几个步骤:
打开浏览器 => 输入搜索问题 => 浏览搜索结果 => 思考内容的真实性准确性 => 继续搜索分析 => 完成信息总结。那么使用 LLM + Agent 就很好做这个任务,只需要将需要搜索的内容(目标)以及浏览器(工具)交给 LLM ,利用其自然语言理解能力就能获取最终结果。
复杂任务 ReAct 范式
大模型与 Agent 的结合目前比较有效的是 ReAct 范式(Reson+Act)由谷歌 2022年10月的论文《ReAct: Synergizing Reasoning and Acting in Language Models》提出。ReAct = Reasoning(协同推理) + Acting(行动),简单来说就是每个执行步骤都思考一番并优化下一步执行直到目标结果。
可以参考 LangChain 中封装的 AgentTypes,对不同类型的 Agent 有不同的提示模板。Types | ️ LangChain ReAct 是其中之一:
- OpenAI functions => prompt = hub.pull("hwchase17/openai-functions-agent") 工具调用允许模型检测何时应调用一个或多个工具,并使用应传递给这些工具的输入进行响应。
- OpenAI tools => prompt = hub.pull("hwchase17/openai-tools-agent") 可以检测何时应调用一个或多个 函数,并响应应传递给函数的输入。
- XML Agent => prompt = hub.pull("hwchase17/xml-agent-convo")
- JSON Chat Agent => prompt = hub.pull("hwchase17/react-chat-json") 该代理使用 JSON 来格式化其输出,旨在支持聊天模型。
- Structured chat => prompt = hub.pull("hwchase17/structured-chat-agent") 结构化聊天代理能够使用多输入工具。
- ReAct => prompt = hub.pull("hwchase17/react") ReAct模式的代理,对工具的重复应用直到获取结果
- Self-ask with search => prompt = hub.pull("hwchase17/self-ask-with-search") 搜索代理进行自我询问。
以 React 为例:
------------------------
Answer the following questions as best you can. You have access to the following tools:
{tools}
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, should be one of [{tool_names}]
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: {input}
Thought:{agent_scratchpad}
------------------------
现有的技术路线
利用大模型+各种小模型的思想。使用 LLM 作为控制器来管理 AI 模型,它首先应用LLM来理解用户请求并将其分解成小任务,然后将这些任务分配给不同的功能模型来完成,最后再次使用LLM将结果汇总为最终输出。以此和传统的各种任务结合起来。按照业务场景 Agent 分类:
语音助手型AI Agent:这种类型的大模型AI Agent可以通过语音指令与用户进行沟通和互动。它们能够理解自然语言、识别语音指令,并能够执行各种任务,比如回答问题、提供信息、安排会议等。语音助手型AI Agent可以大大提高用.户体验,简化用户操作,提高工作效率。
聊天机器人型AI Agent:聊天机器人型AI Agent是- -种能够通过文本消息与用户进行对话的大模型AI Agent。它们通常被用于客户服务、在线支持、销售等领域,能够回答用户的问题、提供帮助、解决问题。聊天机器人型AI Agent可以24/7不间断地为用户提供服务,节省人力成本,提高客户满意度。
数据分析型AI Agent:这种类型的大模型AI Agent专注于处理和分析大规模数据,从中提取有用的信息和见解。数据分析型AI Agent可以帮助企业做出更明智的决策、发现潜在的商机、优化运营流程。它们通常与数据科学家、分析师等团队成员合作,共同完成数据分析任务。
自动化流程型AI Agent:自动化流程型AI Agent旨在自动化重复、繁琐的工作流程,提高生产效率和工作质量。它们可以代替人工完成机械性的任务,比如数据录入、文件整理、流程监控等。自动化流程型AI Agent可以帮助企业节省时间、减少错误率、提高效率。
智能决策型AI Agent:智能决策型AI Agent是一种能够根据数据和规则做出决策的大型人工智能程序。它们可以分析各种信息、权衡利弊、预测结果,帮助企业管理层做出更加科学、合理的决策。智能决策型AI Agent在风险管理、投资决策、市场营销等方面有着广泛的应用。
情感识别型AI Agent:情感识别型AI Agent是-种能够识别和理解人类情感的大型AI程序。它们可以分析文本、语音、图像等数据,识别出其中的情感色彩,从而更好地理解用户的需求和情感状态。情感识别型AI Agent可以被用于情感。
测试 LLM 的 Agent 能力
自从 Agent 概念出现以来,国内应用雨后春笋涌现,十个AI应用里边,五个办公Agent,三个AIGC,还有两个是数字人。各大开源 llm 的发布也紧跟 Agent 更新,从对话模型扩展到工具调用,很多项目已经不需要使用 LangChain 外部工具来实现 Agent 任务了。下面测试清华去年 10 月发布的 ChatLMG3-b6 是较少能够在消费级显卡甚至移动端设备运行的中文开源 llm 之一。
https://github.com/THUDM/ChatGLM3
项目中有原生支持的 Agent Demo 以及 LanChain Agent Demo,做简要代码说明。
1.openai_api_demo.py:
定义并加载工具,源码提供天气获取工具,描述工具的作用和参数:
{
'model': 'chatglm3',
'messages': [
{'role': 'user', 'content': '帮我查询北京的天气怎么样'}
],
'stream':
True,
'tools': {
'random_number_generator':
{
'name': 'random_number_generator',
'description': 'Generates a random number x, s.t. range[0] <= x < range[1]',
'parameters': [
{'name': 'seed', 'description': 'The random seed used by the generator', 'type': 'int', 'required': True},
{'name': 'range', 'description': 'The range of the generated numbers', 'type': 'tuple[int, int]', 'required': True}]
},
'get_weather': {
'name': 'get_weather',
'description': 'Get the current weather for `city_name`',
'parameters': [
{'name': 'city_name', 'description': 'The name of the city to be queried', 'type': 'str', 'required': True}]
}
}
}
LLM针对工具调用模式的回答结构是稳定的格式如 get_weatherpython tool_call(city_name='北京')
,展示了天气的获取,llm 会搜索可用的工具来达到目标:
然后根据工具名称和参数进行函数的调用:
register_tool用于注册函数,它接受一个可调用对象 func 作为参数。该函数将 func 注册为一个工具,并返回 func 本身。
dispatch_tool用于执行函数,它接受一个函数名和函数参数,返回函数的返回。
2.langchain_demo
LangChain 可以任意组装想要的链,自带一些现有的工具也支持开发自己的工具。ChatGLM3 依赖中已经包含了 langchain 了,简要分析一下源码:
langchain_demo
|--tools
|----|--Calculator.py 定义了一个执行计算的工具
|----|--DistanceConversion.py 定义了一个单位转换工具
|----|--Weather.py 查询指定位置天气的工具,心知天气网申请 apikey
|--ChatGLM3.py 集成自 LLM 导入 transformers 模块、修改device=cuda在这里面修改
|--main.py 入口函数,演示使用 langchain 库来调用不同的工具进行 Agent 任务
main.py 中定义了4中操作方式,前三个是自定义工具、最后一个是 langchain 提供的工具:
*使用 Calculator 工具执行简单的算术计算,如输入 "34 * 34"。
*使用 Weather 工具回答与天气相关的查询,考虑到对话历史,比如输入 "厦门比北京热吗?",并提供了与之前对话相关的历史消息。
*使用 DistanceConverter 工具将距离在不同单位之间进行转换,例如输入 "how many meters in 30 km?"。
*使用 Arxiv 工具搜索科学文章,例如输入 "Describe the paper about GLM 130B"。
开发一个销售数据分析代理
下面开发一个销售数据分析的 Chain,如本文封面图片,需要提供4个工具,然后利用 LLM 的分析推理能力一步一步调用工具链最终完成我们的任务,使用 Java 开发,各个模块如下:
1.提供销售数据检索工具。
2.提供数据分析工具,提供滑动平均、关联挖掘、回归、异常检测等各种算法交给模型选择。
3.提供ppt生成的工具。
4.提供邮件发送的工具。
5.llm模块调用本地 ChatGLM3-6b。
完整测试代码如下:
package tool.llm;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* @desc : ChatGLM3 Agent Demo
* @auth : tyf
* @date : 2024-04-19 22:27:43
*/
public class ChatGLM3Agent {
public static String llm_host = "http://0.0.0.0:8000";
// 提示模版
public static String prompt = "{\"model\":\"chatglm3-6b\",\"messages\":$messages,\"tool_choice\":\"auto\",\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"dataSearch\",\"description\":\"用于搜索指定时间内的销售数据,工具需要输入时间范围起始时间。\",\"parameters\":[{\"name\":\"start\",\"description\":\"搜索指定时间内的销售数据的开始时间戳\",\"type\":\"lang\",\"required\":true},{\"name\":\"end\",\"description\":\"搜索指定时间内的销售数据的结束时间戳\",\"type\":\"lang\",\"required\":true}]}},{\"type\":\"function\",\"function\":{\"name\":\"dataAnalysis\",\"description\":\"用于销售数据的数据分析,工具需要输入分析算法,目前支持滑动平均、聚类、异常分析三种分析算法。\",\"parameters\":[{\"name\":\"type\",\"description\":\"分析算法,目前支持滑动平均、聚类、异常分析三种分析算法。\",\"type\":\"string\",\"required\":true}]}},{\"type\":\"function\",\"function\":{\"name\":\"dataSummary\",\"description\":\"用于销售数据分析结果的汇总,工具需要输入销售数据的分析结果,生成PPT文件到本地。\",\"parameters\":[{\"name\":\"data\",\"description\":\"销售数据分析结果\",\"type\":\"string\",\"required\":true}]}},{\"type\":\"function\",\"function\":{\"name\":\"dataReport\",\"description\":\"用于邮件发送\"}}]}";
// 保存历史回话
public static JSONArray messages = new JSONArray();
public static void chatTest(String content) throws Exception{
System.out.println("TangYuFan => "+content);
String data =
"{\n" +
" \"model\": \"chatglm3-6b\",\n" +
" \"messages\": [\n" +
" {\n" +
" \"role\": \"system\",\n" +
" \"content\": \"你是一名AI大模型资深专家\"\n" +
" },\n" +
" {\n" +
" \"role\": \"user\",\n" +
" \"content\": \""+content+"\"\n" +
" }\n" +
" ]\n" +
"}";
llmAnswer(data);
}
// agent 模版、参考 chatglm3 添加 system 和 user 身份
public static String promptTemplate1(String task){
String system = task.split(",")[0];
String user = task.split(",")[1];
messages.add(JSONObject.parseObject("{\"role\":\"system\",\"content\":\""+system+"\"}"));
messages.add(JSONObject.parseObject("{\"role\":\"user\",\"content\":\""+user+"\"}"));
String request = prompt.replace("$messages",messages.toString());
return request;
}
// agent 模版、参考 chatglm3 添加 observation 观察者身份
public static String promptTemplate2(String tooled){
messages.add(JSONObject.parseObject("{\"role\":\"function\",\"content\":\""+tooled+"\"}"));
String request = prompt.replace("$messages",messages.toString());
return request;
}
// agent 模版、参考 chatglm3 添加 assistant 助手身份
public static String promptTemplate3(String response,String userInput){
String question = JSONObject.parseObject(JSONObject.parseObject(response).getJSONArray("choices").get(0).toString()).getJSONObject("message").getString("content");
messages.add(JSONObject.parseObject("{\"role\":\"assistant\",\"content\":\""+question+"\"}"));
System.out.println("TangYuFan => " + userInput);
messages.add(JSONObject.parseObject("{\"role\":\"user\",\"content\":\""+userInput+"\"}"));
String request = prompt.replace("$messages",messages.toString());
return request;
}
// 工具调用入口
public static String exeTool(String llmAnswer){
// llm 的回复
Object choices_0 = JSONObject.parseObject(llmAnswer).getJSONArray("choices").get(0);
// 获取需要执行的工具
JSONObject tool = JSONObject.parseObject(choices_0.toString()).getJSONObject("message").getJSONObject("function_call");
String name = tool.getString("name");
String arguments = tool.getString("arguments");
// 调用 chain 中定义的函数
if("dataSearch".contains(name)){
return dataSearch(arguments);
}
else if("dataAnalysis".contains(name)){
return dataAnalysis(arguments);
}
else if("dataSummary".contains(name)){
return dataSummary(arguments);
}
else if("dataReport".contains(name)){
return dataReport(arguments);
}
return "未知错误!";
}
// 数据搜索
public static String dataSearch(String input){
System.out.println("执行:dataSearch,参数:"+input);
return "数据搜索完成!";
}
// 数据分析
public static String dataAnalysis(String input){
System.out.println("执行:dataAnalysis,参数:"+input);
return "数据分析完成!";
}
// 分析汇总
public static String dataSummary(String input){
System.out.println("执行:dataSummary,参数:"+input);
return "数据汇总完成!";
}
// 发送邮件
public static String dataReport(String input){
System.out.println("执行:dataReport,参数:"+input);
return "邮件发送完成!";
}
// 询问 llm 并获取回答
public static String llmAnswer(String request) throws Exception{
HttpPost httpPost = new HttpPost(llm_host+"/v1/chat/completions");
httpPost.addHeader("Content-Type", "application/json;charset=utf-8");
httpPost.setEntity(new StringEntity(request.toString(), StandardCharsets.UTF_8));
CloseableHttpResponse response = HttpClients.createDefault().execute(httpPost);
HttpEntity resEntity = response.getEntity();
String resp = EntityUtils.toString(resEntity,"utf-8");
return resp;
}
// 解析 llm 回答判断是否有函数调用
public static boolean ntoolCallCheck(String response){
Object choices_0 = JSONObject.parseObject(response).getJSONArray("choices").get(0);
JSONObject tool = JSONObject.parseObject(choices_0.toString()).getJSONObject("message").getJSONObject("function_call");
return response!=null&&response.contains("ntool_call")&&tool!=null;
}
// 解析 llm 回答判断任务是否已经执行完成
public static boolean overCheck(String response){
return false;
}
// 解析 llm 回答判断是否是 llm 反问的问题
public static boolean questionCheck(String response){
Object choices_0 = JSONObject.parseObject(response).getJSONArray("choices").get(0);
JSONObject tool = JSONObject.parseObject(choices_0.toString()).getJSONObject("message").getJSONObject("function_call");
return tool==null;
}
// 任务执行入口
public static void exeTask(String task) throws Exception{
System.out.println("TangYuFan => "+task);
// llm询问参数
String request = promptTemplate1(task);
String response = llmAnswer(request);
// 调用 chain
for (int i = 1; i < 30; i++) {
// 有工具调用则调用工具
if(ntoolCallCheck(response)){
String tooled = exeTool(response);
// 如果工具执行错误则提前退出(可以优化让llm继续思考重复执行 TODO)
if(tooled==null){
break;
}
response = llmAnswer(promptTemplate2(tooled));
}
// llm 判断任务已经执行完成
else if(overCheck(response)){
System.out.println("llm判断任务执行完成!");
}
// 处理 llm 的回复
else if(questionCheck(response)){
// 键盘输入
Scanner scanner = new Scanner(System.in);
String userInput = scanner.nextLine();
if(userInput.contains("exit")){
System.exit(0);
}
response = llmAnswer(promptTemplate3(response,userInput));
}
}
}
public static void main(String[] args) throws Exception{
chatTest("你好!");
String task = "你是一个销售数据分析师,请帮我搜索并分析本月销售数据并汇总PPT并发送邮件给我。";
exeTask(task);
}
}
需要注意的地方:
1、工具调用的响应结构并不稳定、模型对 Agent 任务有针对性训练,需要优化 Prompt 以及替换更大参数量模型避免模型已读乱回。
2、另外一个问题是复杂的 Agent 任务通常对应很长的 chain,需要在某个环节执行错误时对 llm 进行矫正(让他尝试重复执行、从头执行等等)。
测试结果:
版权归原作者 0x13 所有, 如有侵权,请联系我们删除。