更新时间:2024-10-16 GMT+08:00
Agent助手
应用介绍
通过模型对复杂任务的自动拆解与外部工具调用执行能力,通过与用户多轮对话,实现会议室预订场景。
环境准备
- Java 1.8。
- 参考安装章节,完成基础环境准备。
- 盘古大语言模型。
开发实现
- 创建配置文件llm.properties, 正确配置iam、pangu配置项。信息收集请参考准备工作。
# Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. # ################################ GENERIC CONFIG ############################### ## If necessary, you can specify the http proxy configuration. # sdk.proxy.enabled=true # sdk.proxy.url= # sdk.proxy.user= # sdk.proxy.password= ## Generic IAM info. This config is used when the specified IAM is not configured. ## Either user password authentication or AK/SK authentication. # sdk.iam.url= sdk.iam.domain= sdk.iam.user= sdk.iam.password= sdk.iam.project= ## Pangu # Examples: https://{endPoint}/v1/{projectId}/deployments/{deploymentId} ; # sdk.llm.pangu.url=
- 创建代码,示例如下:
/****************************************** 会议室状态查询工具 *****************************************/ import com.huaweicloud.pangu.dev.sdk.api.annotation.AgentTool; import com.huaweicloud.pangu.dev.sdk.api.annotation.AgentToolParam; import com.huaweicloud.pangu.dev.sdk.api.tool.StaticTool; import lombok.Data; @AgentTool(toolId = "meeting_room_status_query", toolDesc = "查询会议室的状态,是否被预定或者正在使用中", toolPrinciple = "请在需要预定会议室之前使用,查询会议室状态判断是否可以预定", inputDesc = "", outPutDesc = "会议室状态") public class MeetingRoomStatusQuery extends StaticTool<MeetingRoomStatusQuery.InputParam, String> { @Override public String run(InputParam input) { switch (input.getMeetingRoom()) { case "A02": return "in use"; case "A03": return "booked"; default: return "available"; } } @Data public static class InputParam { @AgentToolParam(description = "会议开始时间,格式为yyyy-MM-dd HH:mm") private String start; @AgentToolParam(description = "会议结束时间,格式为yyyy-MM-dd HH:mm") private String end; @AgentToolParam(description = "会议室") private String meetingRoom; } } /*********************************************** 会议室预订工具 **********************************************/ import com.huaweicloud.pangu.dev.sdk.api.annotation.AgentTool; import com.huaweicloud.pangu.dev.sdk.api.annotation.AgentToolParam; import com.huaweicloud.pangu.dev.sdk.api.tool.StaticTool; import lombok.Data; @AgentTool(toolId = "reserve_meeting_room", toolDesc = "预定会议室", toolPrinciple = "请在需要预定会议室时调用此工具,预定前需要先查询会议室状态", inputDesc = "会议开始结束时间,会议室", outPutDesc = "预定会议室的结果") public class ReserveMeetingRoom extends StaticTool<ReserveMeetingRoom.InputParam, String> { @Override public String run(InputParam input) { return String.format("%s到%s的%s已预定成功", input.start, input.end, input.meetingRoom); } @Data public static class InputParam { @AgentToolParam(description = "会议开始时间,格式为yyyy-MM-dd HH:mm") private String start; @AgentToolParam(description = "会议结束时间,格式为yyyy-MM-dd HH:mm") private String end; @AgentToolParam(description = "会议室") private String meetingRoom; } } /*********************************************** Agent实现 **********************************************/ import com.huaweicloud.pangu.dev.sdk.agent.AgentAction; import com.huaweicloud.pangu.dev.sdk.agent.AgentSession; import com.huaweicloud.pangu.dev.sdk.agent.AgentSessionStatus; import com.huaweicloud.pangu.dev.sdk.agent.ReactPanguAgent; import com.huaweicloud.pangu.dev.sdk.api.agent.Agent; import com.huaweicloud.pangu.dev.sdk.api.agent.AgentSessionHelper; 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.llms.request.ConversationMessage; import com.huaweicloud.pangu.dev.sdk.api.llms.request.Role; import com.huaweicloud.pangu.dev.sdk.api.tool.Tool; import com.huaweicloud.sdk.demo.test.tools.GetReceipt; import com.huaweicloud.sdk.demo.test.tools.GetReimbursementLimitTool; import com.huaweicloud.sdk.demo.test.tools.GetReimbursementRatio; import com.huaweicloud.sdk.demo.test.tools.GetUserID; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; @Slf4j public class TestAgentCustom { // agent private static Agent panguAgent; // 工具map。在分步骤执行agent场景时,需要调用tool 的run方法来执行tool private static LinkedHashMap<String, Tool> toolMap = new LinkedHashMap(); @BeforeAll public static void initAgent() { 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); Tool meetingRoomQuery = new MeetingRoomStatusQuery(); panguAgent.addTool(meetingRoomQuery); toolMap.put(meetingRoomQuery.getToolId(), meetingRoomQuery); Tool meetingRoomReserve = new ReserveMeetingRoom(); toolMap.put(meetingRoomReserve.getToolId(), meetingRoomReserve); panguAgent.addTool(meetingRoomReserve); } /************************************************************** * 会话信息持久化, 以内存为例。实际生产环境,建议在外部(SQL/Redis)持久化 ***************************************************************/ /** * 在生产环境下,agentSession建议在外部持久化,而不是在内存中 * 如果使用AssistantAPI,华为会提供持久化能力,不需要自行实现 */ private static final Map<String, AgentSession> agentSessionMap = new HashMap<>(); /** * 历史对话消息持久化 */ private static final Map<String, List<ConversationMessage>> agentSessionMessageMap = new HashMap<>(); // 保存session信息 void updateAgentSession(String sessionId, AgentSession agentSession) { agentSessionMap.put(sessionId, agentSession); // 将最近一轮对话信息永久持久化 if (!agentSessionMessageMap.containsKey(sessionId)) { agentSessionMessageMap.put(sessionId, new ArrayList<>()); } agentSessionMessageMap.get(sessionId) .add(agentSession.getMessages().get(agentSession.getMessages().size() - 2)); agentSessionMessageMap.get(sessionId) .add(agentSession.getMessages().get(agentSession.getMessages().size() - 1)); } // 查询session信息 AgentSession getAgentSessionFromMemory(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()); ConversationMessage assistantMessage = ConversationMessage.builder().role(Role.ASSISTANT).build(); agentSession.getMessages().add(assistantMessage); agentSession.setCurrentMessage(assistantMessage); } return agentSession; } void dealMessageByWindowSize(AgentSession agentSession, int windowSize) { if (windowSize <= 1) { return; } int preSize = agentSession.getMessages().size(); if (preSize <= windowSize) { return; } agentSession.setMessages(agentSession.getMessages().subList(preSize - windowSize, preSize)); } /** * 用户会话与session关联关系存储, 生产环境建议在外部持久化 * 如果使用AssistantAPI,华为会提供持久化能力,不需要自行实现 */ private static final Map<String, String> sessionUserMap = new HashMap<>(); @Test void test() { // 用户id String userId = "user01"; // session id相同 String sessionId = UUID.randomUUID().toString(); // 持久化用户会话信息 sessionUserMap.put(sessionId, userId); // 第一轮用户输入, “查询最大报销额度” AgentSession session1st = run(sessionId, "定个2点点的会议"); // 第一轮模型回复: “好的,请问您想预定哪一个会议室?” log.info("助手:" + session1st.getCurrentMessage().getContent()); // 第二轮用户反馈信息, “A01会议室” AgentSession session2nd = run(sessionId, "A01会议室"); // 第二轮模型回复:“已为您预定 A01会议室,时间为2024年5月15日下午2点到4点。” log.info("助手:" + session2nd.getCurrentMessage().getContent()); // 第三轮用户反馈信息, “会议室更换为 AgentSession session3th = run(sessionId, "会议室更换为 // 第三轮回复:” A02会议室在今天下午2点到4点已经被使用了,无法预定。您是否需要更换其他时间或者其他会议室?” log.info("机器人:" + session3th.getCurrentMessage().getContent()); } private AgentSession run(String sessionId, String userMessage) { // 从持久化存储中加载会话上下文 AgentSession agentSession = getAgentSessionFromMemory(sessionId, userMessage); // 取近10条数据给模型 dealMessageByWindowSize(agentSession, 10); agentSession.setAgentSessionStatus(AgentSessionStatus.RUNNING); // 模型规划 agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession)); /** * Agent的状态为FINISHED,为FINISHED,所以不需要调用工具 */ if (agentSession.getAgentSessionStatus() == AgentSessionStatus.FINISHED) { log.info("Agent的状态为{},为{},所以不需要调用工具", agentSession.getAgentSessionStatus(), AgentSessionStatus.FINISHED); AgentSessionHelper.updateAssistantMessage(agentSession, true); // 持久化会话上下文信息 updateAgentSession(sessionId, agentSession); return agentSession; } /** * Agent的状态为RUNNING,不为FINISHED,所以需要调用工具, * 示例:调用的工具为meeting_room_status_query,入参为{"start": "2024-05-07 14:00", * "end": "2024-05-07 16:00", "meetingRoom": "A01"} */ // 最大迭代次数,避免模型规划失败死循环 int maxIteration = 3; int index = 0; // 调用工具, 其中可以扩展用户自定义逻辑 while (index <= maxIteration && agentSession.getAgentSessionStatus() != AgentSessionStatus.FINISHED) { index++; // 从agentSession中取出要调用的工具 final AgentAction currentAction = agentSession.getCurrentAction(); log.info("Agent的状态为{},不为{},所以需要调用工具,调用的工具为{},入参为{}", agentSession.getAgentSessionStatus(), AgentSessionStatus.FINISHED, currentAction.getAction(), currentAction.getActionInput()); // 执行工具 Tool tool = toolMap.get(currentAction.getAction()); Object result = tool.runFromJson(currentAction.getActionInput().toString()); // 获取工具结果后,继续模型推理 AgentSessionHelper.setToolOutput(agentSession, result.toString()); agentSession = panguAgent.runStep(agentSession); log.info(AgentSessionHelper.printPlan(agentSession)); } AgentSessionHelper.updateAssistantMessage(agentSession, true); updateAgentSession(sessionId, agentSession); return agentSession; } }
父主题: 应用实践