运行Agent
在给出的示例中,Agent中预置了2个工具,分别为:
meeting_room_status_query:查询会议室的状态,是否被预定或者正在使用中。
reserve_meeting_room:预定会议室。
- 单轮执行:
Agent的运行时会进行自我迭代,并且选择合适的工具,在日志中打印最终的执行结果:
用户: 帮我定个今天下午3点到8点的A02会议室 助手: A02会议室在今天下午3点到8点已经被预定了。是否需要为您预定其他时间段或者其他会议室? - 步骤1: 思考:好的,我需要先查询A02会议室今天下午3点到8点的预定状态。使用meeting_room_status_query工具进行查询。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-07 15:00\", \"end\": \"2024-05-07 20:00\", \"meetingRoom\": \"A02\"}" 工具返回:in use - 步骤2 答复:"A02会议室在今天下午3点到8点已经被预定了。是否需要为您预定其他时间段或者其他会议室?"
- 多轮执行
List<ConversationMessage> messages = new ArrayList<>(); messages.add(ConversationMessage.builder().role(Role.USER).content("定个2点-4点的会议,A01会议室").build()); messages.add( ConversationMessage.builder().role(Role.ASSISTANT).content("已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。").build()); messages.add(ConversationMessage.builder().role(Role.USER).content("再定一个明天8点到9点的会议室").build()); panguAgent.run(messages);
运行结果示例:用户: 定个2点-4点的会议,A01会议室 助手: 已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。 用户: 再定一个明天8点到9点的会议室 助手: 已为您预定 A01会议室,时间为2024年5月8日早上8点到9点。 - 步骤1: 思考:好的,让我先查询一下 A01会议室在2024年5月8日8点到9点的状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-08 08:00\", \"end\": \"2024-05-08 09:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在2024年5月8日8点到9点是空闲的,可以预定。现在为您预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-08 08:00\", \"end\": \"2024-05-08 09:00\", \"meetingRoom\": \"A01\"}" 工具返回:2024-05-08 08:00到2024-05-08 09:00的A01已预定成功 - 步骤3 答复:"已为您预定 A01会议室,时间为2024年5月8日早上8点到9点。 "
- 多轮执行增强
上述的例子中实际运行时只提供给模型多轮的对话,并没有提供工具执行的过程,有概率会出现模型不实际调用工具的情况。为了让大模型的效果更好,可以传入agentSession。
AgentSession agentSession = AgentSessionHelper.initAgentSession("定个2点-4点的会议,A01会议室"); agentSession = panguAgent.run(agentSession); agentSession.getMessages() .add(ConversationMessage.builder().role(Role.USER).content("再定一个明天8点到9点的会议室").build()); panguAgent.run(agentSession);
第一次运行Agent,panguAgent.run(agentSession)结果:用户: 定个2点-4点的会议,A01会议室 助手: 已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。 - 步骤1: 思考:好的,我需要先查询 A01会议室在今天下午2点到4点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-07 14:00\", \"end\": \"2024-05-07 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在今天下午2点到4点是空闲的。现在我将为您预定 A01会议室。调用 reserve_meeting_room 工具进行会议室预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-07 14:00\", \"end\": \"2024-05-07 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:2024-05-07 14:00到2024-05-07 16:00的A01已预定成功 - 步骤3 答复:"已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。 "
第二次运行Agent,panguAgent.run(agentSession)结果:用户: 定个2点-4点的会议,A01会议室 助手: 已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。 - 步骤1: 思考:好的,我需要先查询 A01会议室在今天下午2点到4点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-07 14:00\", \"end\": \"2024-05-07 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在今天下午2点到4点是空闲的。现在我将为您预定 A01会议室。调用 reserve_meeting_room 工具进行会议室预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-07 14:00\", \"end\": \"2024-05-07 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:2024-05-07 14:00到2024-05-07 16:00的A01已预定成功 - 步骤3 答复:"已为您预定 A01会议室,时间为2024年5月7日下午2点到4点。 " 用户: 再定一个明天8点到9点的会议室 助手: 已为您预定 A01会议室,时间为2024年5月8日早上8点到9点。 - 步骤1: 思考:好的,我需要先查询会议室在明天早上8点到9点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-08 08:00\", \"end\": \"2024-05-08 09:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在明天早上8点到9点是空闲的。现在我将为您预定 A01会议室。调用 reserve_meeting_room 工具进行会议室预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-08 08:00\", \"end\": \"2024-05-08 09:00\", \"meetingRoom\": \"A01\"}" 工具返回:2024-05-08 08:00到2024-05-08 09:00的A01已预定成功 - 步骤3 答复:"已为您预定 A01会议室,时间为2024年5月8日早上8点到9点。 "
在第二次运行Agent时,包含第一次运行的所有工具调用细节。
agentSession相当于Agent的会话Memory。一般情况下,需要将agentSession对象在外部持久化,在每一轮会话传入agentSession对象中的sessionId,下面的示例代码用一个map对象模拟外部的持久化:/** * 在生产环境下,agentSession建议在外部持久化,而不是在内存中 * 如果使用AssistantAPI,华为会提供持久化能力,不需要自行实现 */ private static final Map<String, AgentSession> agentSessionMap = new HashMap<>(); @Test void test() { String sessionId = "sessionId_1"; panguAgent.run(getAgentSession(sessionId, "定个2点-4点的会议,A01会议室")); panguAgent.run(getAgentSession(sessionId, "再定一个明天8点到9点的会议室")); } AgentSession getAgentSession(String sessionId, String userMessage) { AgentSession agentSession = agentSessionMap.get(sessionId); if (agentSession == null) { agentSession = AgentSessionHelper.initAgentSession(userMessage); agentSession.setSessionId(sessionId); agentSessionMap.put(sessionId, agentSession); } else { agentSession.getMessages() .add(ConversationMessage.builder().role(Role.USER).content(userMessage).build()); } return agentSession; }
- 单步执行
有时并不希望Agent完全自主执行,在某些关键节点,让用户先进行确认,确认后再执行,或者用户对模型的结果有异议或者想法有变化,想对当前结果进行更改。此时可以单步运行Agent:
/** * 单步执行Agent,提供干预能力 * * @param agentSession 包括初始状态,以及执行步骤间的agentSession,可以使用AgentSessionHelper类辅助处理 * @return Agent执行的结果 */ AgentSession runStep(AgentSession agentSession);
完整示例如下:AgentSession agentSession = AgentSessionHelper.initAgentSession("定个2点-4点的会议"); agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession)); log.info("Agent status = {}, is {}, do not call tool", agentSession.getAgentSessionStatus(), AgentSessionStatus.FINISHED); AgentSessionHelper.updateAssistantMessage(agentSession, true); // 用户多轮对话 AgentSessionHelper.addUserMessage(agentSession, "A01会议室"); agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession)); // 从agentSession中取出要调用的工具 final AgentAction currentAction = agentSession.getCurrentAction(); final String action = currentAction.getActionTools().get(0).getAction(); final Object actionInput = currentAction.getActionTools().get(0).getActionInput(); log.info("Agent status = {}, not {}, call tool {}, input = {}", agentSession.getAgentSessionStatus(), AgentSessionStatus.FINISHED, action, actionInput); // 调用工具,这里为调用示例,由于已经给出tool_id和调用参数,实际调用可以有多种形式 final Tool tool = panguAgent.getTool(action); final Object result = tool.runFromJson(actionInput.toString()); // 提交工具调用结果,并继续执行 AgentSessionHelper.setFirstToolOutput(agentSession, result.toString()); agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession)); // 如果对工具调用参数有调整,可以进行反馈 AgentSessionHelper.setUserFeedback(agentSession, "会议室更换为A02"); agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession));
上述例子中,第一次printPlan打印的结果为:用户: 定个2点-4点的会议 助手: - 步骤1: 思考:好的,请问您想预定哪一个会议室?
由于缺少参数,因此模型开始追问,此时构造了用户消息,回答了模型追问的问题。可以通过AgentSession的状态来判断是否是追问闲聊,或是发起工具调用,此时AgentSession的状态为FINISHED,所以为追问或闲聊。打印的日志为:Agent status = FINISHED, is FINISHED, do not call tool
第二次printPlan打印的结果为:用户: 定个2点-4点的会议 助手: 好的,请问您想预定哪一个会议室? - 步骤1: 思考:好的,请问您想预定哪一个会议室? 用户: A01会议室 助手: - 步骤1: 思考:请稍等,我需要先查询 A01会议室在今天下午2点到4点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A01\"}"
此时,由于采用了单步调用,因此不会自动执行工具,打印的日志为:Agent status = RUNNING, not FINISHED, call tool meeting_room_status_query, input = {"start": "2024-05-11 14:00", "end": "2024-05-11 16:00", "meetingRoom": "A01"}
同样的,可以通过AgentSession的status判断此时是否需要调用工具,如果需要调用工具,可以从currentAction中获取需要调用的工具信息。
当提交了工具调用结果后,第三次printPlan打印的结果为:用户: 定个2点-4点的会议 助手: 好的,请问您想预定哪一个会议室? - 步骤1: 思考:好的,请问您想预定哪一个会议室? 用户: A01会议室 助手: - 步骤1: 思考:请稍等,我需要先查询 A01会议室在今天下午2点到4点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在今天下午2点到4点是空闲的。现在为您预定 A01会议室。调用 reserve_meeting_room 工具进行会议室预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A01\"}"
可以看到,Agent又向下执行了一步,此时需要提交reserve_meeting_room的调用结果,提交之后,执行预期结束。为了说明用户反馈的功能,选择了调用setUserFeedback,进行反馈,第四次printPlan打印的结果为:用户: 定个2点-4点的会议 助手: 好的,请问您想预定哪一个会议室? - 步骤1: 思考:好的,请问您想预定哪一个会议室? 用户: A01会议室 助手: - 步骤1: 思考:请稍等,我需要先查询 A01会议室在今天下午2点到4点的状态。现在调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A01\"}" 工具返回:available - 步骤2: 思考: A01会议室在今天下午2点到4点是空闲的。现在为您预定 A01会议室。调用 reserve_meeting_room 工具进行会议室预定。 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A01\"}" 用户反馈:会议室更换为A02 - 步骤3: 思考:好的,我将为您更换为 A02会议室。现在查询 A02会议室在今天下午2点到4点的状态。调用 meeting_room_status_query 工具查询会议室状态。 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-11 14:00\", \"end\": \"2024-05-11 16:00\", \"meetingRoom\": \"A02\"}"
可以看到,Agent继续向下执行了一步,会议室改为了A02,需要重新查询A02的会议室状态。
- Agent结果总结
有时需要Agent对之前的思考、行动回答用户的最终问题,如果对模型返回的结果不满意,可以选择使用AgentSessionSkill解决:
final AgentSession agentSession = agent.run("我的数学成绩和语文成绩分别是多少"); final LLM llm = LLMs.of(LLMs.PANGU, LLMConfig.builder() .llmModuleConfig(LLMModuleConfig.builder() .url( "https://pangu.cn-southwest-2.myhuaweicloud.com/v1/infers/N2-基础模型的调用地址") .build()) .build()); final AgentSessionSkill agentSessionSkill = new AgentSessionSkill(llm); System.out.println("最终结果: " + agentSessionSkill.summary(agentSession));
运行结果示例:用户: 我的数学成绩和语文成绩分别是多少 助手:您的语文成绩也是99分 - 步骤1: 思考:好的,我需要调用 queryScore 工具来查询您的数学成绩。 行动:使用工具[queryScore],传入参数"{\"subjectName\":\"数学\"}" 工具返回:99 - 步骤2: 思考:好的,我需要调用 queryScore 工具来查询您的语文成绩。 行动:使用工具[queryScore],传入参数"{\"subjectName\":\"语文\"}" 工具返回:99 - 步骤3 思考:您的语文成绩也是99分。
最终结果: 您的数学成绩是99分,您的语文成绩也是99分。
AgentSessionSkill使用的大模型建议为N2-基础模型或者其他同等类型的模型。
- 多Agent组合
有时需要多个Agent配合完成任务,可以通过子Agent作为tool方式实现:
// 定义子Agent(tool形式) import com.huaweicloud.pangu.dev.sdk.agent.ReactPanguAgent; import com.huaweicloud.pangu.dev.sdk.api.agent.Agent; import com.huaweicloud.pangu.dev.sdk.api.annotation.AgentTool; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMModuleConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMParamConfig; import com.huaweicloud.pangu.dev.sdk.api.tool.StaticTool; import java.text.SimpleDateFormat; import java.util.Date; @AgentTool(toolId = "meeting_agent", toolDesc = "预定会议Agent", toolPrinciple = "请在需要预定会议室时调用此工具", inputDesc = "预定会议室请求", outPutDesc = "预定会议室的结果") public class MeetingAgent extends StaticTool<String, String> { private static Agent panguAgent; public MeetingAgent() { final String customSystemPrompt = "你是一个会议室预定助手,今天的日期是" + new SimpleDateFormat("yyyy年MM月dd日").format(new Date()); final LLMConfig config = LLMConfig.builder() .llmParamConfig(LLMParamConfig.builder().temperature(0.01).withPrompt(true).build()) .llmModuleConfig(LLMModuleConfig.builder().systemPrompt(customSystemPrompt).build()) .build(); panguAgent = new ReactPanguAgent(LLMs.of(LLMs.PANGU, config)); panguAgent.setMaxIterations(5); panguAgent.addTool(new MeetingRoomStatusQuery()); panguAgent.addTool(new ReserveMeetingRoom()); } @Override public String run(String input) { return panguAgent.run(input).getCurrentMessage().getContent(); } } @Test void test6() { final LLMConfig config = LLMConfig.builder() .llmParamConfig(LLMParamConfig.builder().temperature(0.01).withPrompt(true).build()) .build(); // Agent嵌套,将预定会议室Agent作为一个子Agent添加到agent中 Agent agent = new ReactPanguAgent(LLMs.of(LLMs.PANGU, config)); agent.addTool(new MeetingAgent()); agent.run("帮我定个今天下午3点到8点的A05会议室"); // 子agent是实际做事的agent /** * 用户: 预定今天下午3点到8点的A05会议室 * 助手: 已为您预定2024年05月10日下午3点到8点的A05会议室。请准时参加会议。 * - 步骤1: * 思考:好的,我需要先查询A05会议室在今天下午3点到8点的状态。如果会议室可用,我将为您预定。现在我将调用meeting_room_status_query工具查询会议室状态。 * 行动:使用工具[meeting_room_status_query],传入参数"{\"start\": \"2024-05-10 15:00\", \"end\": \"2024-05-10 20:00\", * \"meetingRoom\": \"A05\"}" * 工具返回:available * - 步骤2: * 思考:A05会议室今天下午3点到8点是可用的。接下来我将调用reserve_meeting_room工具为您预定A05会议室。 * 行动:使用工具[reserve_meeting_room],传入参数"{\"start\": \"2024-05-10 15:00\", \"end\": \"2024-05-10 20:00\", * \"meetingRoom\": \"A05\"}" * 工具返回:2024-05-10 15:00到2024-05-10 20:00的A05已预定成功 * - 步骤3 * 答复:"已为您预定2024年05月10日下午3点到8点的A05会议室。请准时参加会议。" */ // agent只负责分发任务给子agent /** * 用户: 帮我定个今天下午3点到8点的A05会议室 * 助手: 已为您预定2024年05月10日下午3点到8点的A05会议室。请准时参加会议。 * - 步骤1: * 思考:好的,我需要调用meeting_agent工具来预定会议室。该工具需要一个字符串类型的必须参数arg。现在我将调用meeting_agent工具预定会议室。 * 行动:使用工具[meeting_agent],传入参数"{\"arg\": \"预定今天下午3点到8点的A05会议室\"}" * 工具返回:已为您预定2024年05月10日下午3点到8点的A05会议室。请准时参加会议。 * - 步骤2 * 答复:"已为您预定2024年05月10日下午3点到8点的A05会议室。请准时参加会议。" */ }