更新时间:2024-08-29 GMT+08:00
分享

运行Agent

在给出的示例中,Agent中预置了2个工具,分别为:

meeting_room_status_query:查询会议室的状态,是否被预定或者正在使用中。

reserve_meeting_room:预定会议室。

  • 单轮执行:
    调用run接口运行一个Agent:
    panguAgent.run("帮我定个今天下午3点到8点的A02会议室");
    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会议室。请准时参加会议。"
         */
    }

相关文档