ソースを参照

新增 PlanningTool.java类及相关逻辑

alibct 2 ヶ月 前
コミット
d278b9dafa

+ 146 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/agent/tool/PlanningTool.java

@@ -0,0 +1,146 @@
+package com.pavis.admin.aigc.core.agent.tool;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.TypeReference;
+import com.pavis.admin.aigc.model.resp.AgentPlanResp;
+import com.pavis.admin.aigc.model.resp.AgentPlanStepResp;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.openai.api.OpenAiApi.FunctionTool;
+import org.springframework.ai.tool.function.FunctionToolCallback;
+import org.springframework.ai.tool.metadata.ToolMetadata;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * 计划工具类
+ */
+@Slf4j
+public class PlanningTool implements Function<String, ToolExecuteResult> {
+
+    private AgentPlanResp currentPlan;
+
+    public Long getCurrentPlanId() {
+        return currentPlan != null ? currentPlan.getId() : null;
+    }
+
+    public AgentPlanResp getCurrentPlan() {
+        return currentPlan;
+    }
+
+    private static final String PARAMETERS = """
+            {
+                "type": "object",
+                "properties": {
+                    "command": {
+                        "description": "create a execution plan , Available commands: create",
+                        "enum": [
+                            "create"
+                        ],
+                        "type": "long"
+                    },
+                    "plan_id": {
+                        "description": "Unique identifier for the plan",
+                        "type": "string"
+                    },
+                    "title": {
+                        "description": "Title for the plan",
+                        "type": "string"
+                    },
+                    "steps": {
+                        "description": "List of plan steps",
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    },
+                    "step_index": {
+                        "description": "Index of step to update",
+                        "type": "integer"
+                    },
+                    "step_status": {
+                        "description": "Status to set for step",
+                        "enum": ["not_started", "in_progress", "completed", "blocked"],
+                        "type": "string"
+                    },
+                    "step_notes": {
+                        "description": "Additional notes for step",
+                        "type": "string"
+                    }
+                },
+                "required": ["command"]
+            }
+            """;
+
+    private static final String name = "planning";
+
+    private static final String description = "Planning tool for managing tasks ";
+
+    public FunctionTool getToolDefinition() {
+        return new FunctionTool(new FunctionTool.Function(description, name, PARAMETERS));
+    }
+
+    public FunctionToolCallback getFunctionToolCallback() {
+        return FunctionToolCallback.builder(name, this)
+                .description(description)
+                .inputSchema(PARAMETERS)
+                .inputType(String.class)
+                .toolMetadata(ToolMetadata.builder().returnDirect(true).build())
+                .build();
+    }
+
+    public ToolExecuteResult run(String toolInput) {
+        try {
+            Map<String, Object> input = JSON.parseObject(toolInput, new TypeReference<Map<String, Object>>() {
+            });
+            String command = (String) input.get("command");
+            Long planId = (Long) input.get("plan_id");
+            String title = (String) input.get("title");
+            List<String> steps = JSON.parseObject(JSON.toJSONString(input.get("steps")), new TypeReference<>() {
+            });
+            return switch (command) {
+                case "create" -> createPlan(planId, title, steps);
+                // case "update" -> updatePlan(planId, title, steps);
+                // case "get" -> getPlan(planId);
+                // case "mark_step" -> markStep(planId, stepIndex, stepStatus, stepNotes);
+                // case "delete" -> deletePlan(planId);
+                default -> {
+                    log.info("收到无效的命令: {}", command);
+                    throw new IllegalArgumentException("Invalid command: " + command);
+                }
+            };
+        } catch (Exception e) {
+            log.info("执行计划工具时发生错误", e);
+            return new ToolExecuteResult("Error executing planning tool: " + e.getMessage());
+        }
+    }
+
+    public ToolExecuteResult createPlan(Long planId, String title, List<String> steps) {
+        if (planId == null || title == null || steps == null || steps.isEmpty()) {
+            log.info("创建计划时缺少必要参数: planId={}, title={}, steps={}", planId, title, steps);
+            return new ToolExecuteResult("Required parameters missing");
+        }
+        AgentPlanResp plan = new AgentPlanResp();
+        plan.setId(planId);
+        plan.setTitle(title);
+        List<AgentPlanStepResp> planSteps = new ArrayList<>();
+        // 使用创建并添加步骤
+        for (int i = 0; i < steps.size(); i++) {
+            AgentPlanStepResp planStepResp = new AgentPlanStepResp();
+            planStepResp.setOrder(i);
+            planStepResp.setDescription(steps.get(i));
+            planSteps.add(planStepResp);
+        }
+        plan.setSteps(planSteps);
+        this.currentPlan = plan;
+        return new ToolExecuteResult("Plan created: " + planId + "\n" + plan.getPlanExecutionStateStringFormat(false));
+    }
+
+    @Override
+    public ToolExecuteResult apply(String input) {
+        return run(input);
+    }
+
+}

+ 33 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/agent/tool/ToolExecuteResult.java

@@ -0,0 +1,33 @@
+package com.pavis.admin.aigc.core.agent.tool;
+
+import lombok.Data;
+
+/**
+ * 工具执行结果
+ */
+@Data
+public class ToolExecuteResult {
+
+    public ToolExecuteResult() {
+    }
+
+    public ToolExecuteResult(String output) {
+        this.output = output;
+    }
+
+    public ToolExecuteResult(String output, Boolean interrupted) {
+        this.output = output;
+        this.interrupted = interrupted;
+    }
+
+    /**
+     * 工具返回的内容
+     */
+    private String output;
+
+    /**
+     * 是否中断
+     */
+    private Boolean interrupted;
+
+}

+ 25 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/enums/AgentState.java

@@ -0,0 +1,25 @@
+package com.pavis.admin.aigc.enums;
+
+/**
+ * 智能体执行状态
+ */
+public enum AgentState {
+
+    NOT_STARTED("not_started"),
+    IN_PROGRESS("in_progress"),
+    COMPLETED("completed"),
+    BLOCKED("blocked"),
+    FAILED("failed");
+
+    private final String state;
+
+    AgentState(String state) {
+        this.state = state;
+    }
+
+    @Override
+    public String toString() {
+        return state;
+    }
+
+}

+ 5 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/entity/AgentDO.java

@@ -82,6 +82,11 @@ public class AgentDO extends BaseDO {
      */
     private Integer timeout;
 
+    /**
+     * 运行时状态
+     */
+    private String runtimeStatus;
+
     /**
      * 状态(1:启用;2:禁用)
      */

+ 8 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/query/AgentQuery.java

@@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 
 import top.continew.starter.data.core.annotation.Query;
 import top.continew.starter.data.core.enums.QueryType;
+
 import java.io.Serial;
 import java.io.Serializable;
 import java.time.*;
@@ -79,6 +80,13 @@ public class AgentQuery implements Serializable {
     @Query(type = QueryType.EQ)
     private Integer status;
 
+    /**
+     * 运行时状态
+     */
+    @Schema(description = "运行时状态")
+    @Query(type = QueryType.EQ)
+    private String runtimeStatus;
+
     /**
      * 创建人
      */

+ 8 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/AgentReq.java

@@ -7,6 +7,7 @@ import lombok.Data;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 import org.hibernate.validator.constraints.Length;
+
 import java.io.Serial;
 import java.io.Serializable;
 import java.time.*;
@@ -86,6 +87,13 @@ public class AgentReq implements Serializable {
     @NotNull(message = "状态(1:启用;2:禁用)不能为空")
     private Integer status;
 
+    /**
+     * 运行时状态
+     */
+    @Schema(description = "运行时状态")
+    @NotNull(message = "运行时状态不能为空")
+    private String runtimeStatus;
+
     /**
      * 创建人
      */

+ 75 - 5
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/AgentPlanResp.java

@@ -1,12 +1,15 @@
 package com.pavis.admin.aigc.model.resp;
 
-import lombok.Data;
-
+import com.pavis.admin.common.model.resp.BaseResp;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
 
-import com.pavis.admin.common.model.resp.BaseResp;
 import java.io.Serial;
-import java.time.*;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static com.pavis.admin.aigc.enums.AgentState.COMPLETED;
+import static com.pavis.admin.aigc.enums.AgentState.IN_PROGRESS;
 
 /**
  * 智能体规划记录信息
@@ -94,11 +97,17 @@ public class AgentPlanResp extends BaseResp {
     private Boolean completed;
 
     /**
-     * 执行总结
+     * 计划总结
      */
     @Schema(description = "执行总结")
     private String summary;
 
+    /**
+     * 计划执行步骤
+     */
+    @Schema(description = "计划执行步骤")
+    private List<AgentPlanStepResp> steps;
+
     /**
      * 修改人
      */
@@ -110,4 +119,65 @@ public class AgentPlanResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+    public String getPlanExecutionStateStringFormat(boolean onlyCompletedAndFirstInProgress) {
+        StringBuilder state = new StringBuilder();
+        state.append("全局目标 : ").append("\n").append(title).append(")\n");
+        state.append("- 全局步骤计划:\n");
+        state.append(getStepsExecutionStateStringFormat(onlyCompletedAndFirstInProgress));
+        return state.toString();
+    }
+
+    /**
+     * 获取步骤执行状态的字符串格式
+     *
+     * @param onlyCompletedAndFirstInProgress 当为true时,只输出所有已完成的步骤和第一个进行中的步骤
+     * @return 格式化的步骤执行状态字符串
+     */
+    public String getStepsExecutionStateStringFormat(boolean onlyCompletedAndFirstInProgress) {
+        StringBuilder state = new StringBuilder();
+        boolean foundInProgress = false;
+
+        for (int i = 0; i < steps.size(); i++) {
+            AgentPlanStepResp step = steps.get(i);
+
+            // 如果onlyCompletedAndFirstInProgress为true,则只显示COMPLETED状态的步骤和第一个IN_PROGRESS状态的步骤
+            if (onlyCompletedAndFirstInProgress) {
+                // 如果是COMPLETED状态,始终显示
+                if (step.getAgentRuntimeStatus() == COMPLETED) {
+                    // 什么都不做,继续显示
+                }
+                // 如果是IN_PROGRESS状态,且还没找到其他IN_PROGRESS的步骤
+                else if (step.getAgentRuntimeStatus() == IN_PROGRESS && !foundInProgress) {
+                    foundInProgress = true; // 标记已找到IN_PROGRESS步骤
+                }
+                // 其他所有情况(不是COMPLETED且不是第一个IN_PROGRESS)
+                else {
+                    continue; // 跳过不符合条件的步骤
+                }
+            }
+
+            String symbol = switch (step.getAgentRuntimeStatus()) {
+                case COMPLETED -> "[completed]";
+                case IN_PROGRESS -> "[in_progress]";
+                case BLOCKED -> "[blocked]";
+                case NOT_STARTED -> "[not_started]";
+                default -> "[ ]";
+            };
+            state.append("步骤 ")
+                    .append(i)
+                    .append(": ")
+                    .append(symbol)
+                    .append(" ")
+                    .append(step.getDescription())
+                    .append("\n")
+                    .append("\n");
+            String result = step.getResult();
+            if (result != null && !result.isEmpty()) {
+                state.append("该步骤的执行结果: ").append("\n").append(result).append("\n");
+            }
+
+        }
+        return state.toString();
+    }
 }

+ 14 - 4
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/AgentPlanStepResp.java

@@ -1,12 +1,12 @@
 package com.pavis.admin.aigc.model.resp;
 
-import lombok.Data;
-
+import com.pavis.admin.aigc.enums.AgentState;
+import com.pavis.admin.common.model.resp.BaseResp;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
 
-import com.pavis.admin.common.model.resp.BaseResp;
 import java.io.Serial;
-import java.time.*;
+import java.time.LocalDateTime;
 
 /**
  * 智能体规划步骤记录信息
@@ -75,6 +75,12 @@ public class AgentPlanStepResp extends BaseResp {
     @Schema(description = "本次计划要使用的智能体:[BROWSER_AGENT]")
     private String agentCode;
 
+    /**
+     * 智能体详情
+     */
+    @Schema(description = "本次计划要使用的智能体:[BROWSER_AGENT]")
+    private AgentResp agent;
+
     /**
      * 步骤结果
      */
@@ -92,4 +98,8 @@ public class AgentPlanStepResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+    public AgentState getAgentRuntimeStatus() {
+        return agent == null ? AgentState.NOT_STARTED : AgentState.valueOf(agent.getRuntimeStatus());
+    }
 }

+ 7 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/AgentResp.java

@@ -5,6 +5,7 @@ import lombok.Data;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 import com.pavis.admin.common.model.resp.BaseResp;
+
 import java.io.Serial;
 import java.time.*;
 
@@ -93,6 +94,12 @@ public class AgentResp extends BaseResp {
     @Schema(description = "执行超时时间(秒)")
     private Integer timeout;
 
+    /**
+     * 运行时状态
+     */
+    @Schema(description = "运行时状态")
+    private String runtimeStatus;
+
     /**
      * 状态(1:启用;2:禁用)
      */

+ 1 - 0
pavis-webapi/src/main/resources/db/changelog/mysql/main_table.sql

@@ -805,6 +805,7 @@ CREATE TABLE IF NOT EXISTS `aigc_agent`
     `config`           JSON                         DEFAULT NULL COMMENT '配置参数',
     `max_steps`        INT                          DEFAULT 10 COMMENT '最大步骤数',
     `timeout`          INT                          DEFAULT 60 COMMENT '执行超时时间(秒)',
+    `runtime_status`   VARCHAR(50)                  DEFAULT NULL COMMENT '运行时状态',
     `status`           TINYINT(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)',
     `version`          VARCHAR(20)                  DEFAULT '1.0.0' COMMENT '版本号',
     `create_user`      BIGINT              NOT NULL COMMENT '创建人',