更新时间: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;
        }
    }

相关文档