diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml
new file mode 100644
index 00000000..4ea72a91
--- /dev/null
+++ b/.idea/copilot.data.migration.agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml
new file mode 100644
index 00000000..7ef04e2e
--- /dev/null
+++ b/.idea/copilot.data.migration.ask.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml
new file mode 100644
index 00000000..8648f940
--- /dev/null
+++ b/.idea/copilot.data.migration.edit.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/common/Constant.java b/Partner-Main/src/main/java/work/slhaf/partner/common/Constant.java
new file mode 100644
index 00000000..0707b3d3
--- /dev/null
+++ b/Partner-Main/src/main/java/work/slhaf/partner/common/Constant.java
@@ -0,0 +1,11 @@
+package work.slhaf.partner.common;
+
+public final class Constant {
+
+ public static final class Path {
+ public static final String DATA = "./data";
+ public static final String MEMORY_DATA = DATA + "/memory";
+ public static final String ACTION_PROGRAM = DATA + "/action";
+ }
+
+}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/PartnerCore.java b/Partner-Main/src/main/java/work/slhaf/partner/core/PartnerCore.java
index eee0652c..9b75d06d 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/PartnerCore.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/PartnerCore.java
@@ -13,10 +13,11 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import static work.slhaf.partner.common.Constant.Path.MEMORY_DATA;
+
@Slf4j
public abstract class PartnerCore> extends PersistableObject {
- private static final String STORAGE_DIR = "./data/memory/";
private final String id = ((PartnerAgentConfigManager) AgentConfigManager.INSTANCE).getConfig().getAgentId();
public PartnerCore() throws IOException, ClassNotFoundException {
@@ -53,7 +54,7 @@ public abstract class PartnerCore> extends PersistableO
public void serialize() throws IOException {
//先写入到临时文件,如果正常写入则覆盖原文件
Path filePath = getFilePath(id + "-temp");
- Files.createDirectories(Path.of(STORAGE_DIR));
+ Files.createDirectories(Path.of(MEMORY_DATA));
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile()));
oos.writeObject(this);
@@ -78,12 +79,12 @@ public abstract class PartnerCore> extends PersistableO
}
private Path getFilePath(String s) {
- return Paths.get(STORAGE_DIR, s + "-" + getCoreKey() + ".memory");
+ return Paths.get(MEMORY_DATA, s + "-" + getCoreKey() + ".memory");
}
private void createStorageDirectory() {
try {
- Files.createDirectories(Paths.get(STORAGE_DIR));
+ Files.createDirectories(Paths.get(MEMORY_DATA));
} catch (IOException e) {
log.error("[{}]创建存储目录失败: {}", getCoreKey(), e.getMessage());
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCapability.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCapability.java
index 03d4f0c2..79003548 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCapability.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCapability.java
@@ -1,24 +1,24 @@
package work.slhaf.partner.core.action;
import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
-import work.slhaf.partner.core.action.entity.ActionInfo;
+import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustData;
import java.util.List;
@Capability(value = "action")
public interface ActionCapability {
- void putPreparedAction(String uuid, ActionInfo actionInfo);
+ void putPreparedAction(String uuid, ActionData actionData);
- List popPreparedAction(String userId);
+ List popPreparedAction(String userId);
- List popPendingAction(String userId);
+ List popPendingAction(String userId);
- List listPreparedAction(String userId);
+ List listPreparedAction(String userId);
- List listPendingAction(String userId);
+ List listPendingAction(String userId);
- void putPendingActions(String userId, ActionInfo actionInfo);
+ void putPendingActions(String userId, ActionData actionData);
List selectTendencyCache(String input);
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCore.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCore.java
index 8dd95d58..9497147b 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCore.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/ActionCore.java
@@ -1,25 +1,35 @@
package work.slhaf.partner.core.action;
import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore;
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod;
import work.slhaf.partner.common.vector.VectorClient;
import work.slhaf.partner.core.PartnerCore;
-import work.slhaf.partner.core.action.entity.ActionInfo;
+import work.slhaf.partner.core.action.entity.ActionData;
+import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.cache.ActionCacheData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustMetaData;
+import work.slhaf.partner.core.action.exception.ActionInitFailedException;
+import work.slhaf.partner.core.action.exception.ActionLoadFailedException;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
-import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
+import static work.slhaf.partner.common.Constant.Path.ACTION_PROGRAM;
+
@SuppressWarnings("FieldMayBeFinal")
@CapabilityCore(value = "action")
@Slf4j
@@ -28,64 +38,68 @@ public class ActionCore extends PartnerCore {
/**
* 对应本次交互即将执行或将要放置在行动池的预备任务,因此将以本次交互的uuid为键,其起到的作用相当于暂时的模块上下文
*/
- private HashMap> preparedActions = new HashMap<>();
+ private HashMap> preparedActions = new HashMap<>();
/**
* 待确认任务,以userId区分不同用户,因为需要跨请求确认
*/
- private HashMap> pendingActions = new HashMap<>();
+ private HashMap> pendingActions = new HashMap<>();
/**
* 语义缓存与行为倾向映射
*/
private List actionCache = new ArrayList<>();
- private Lock cacheLock = new ReentrantLock();
+ private final Lock cacheLock = new ReentrantLock();
- private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ private final ExecutorService platformExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ private final ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
+
+ private final LinkedHashMap existedMetaActions = new LinkedHashMap<>();
public ActionCore() throws IOException, ClassNotFoundException {
+ new ActionWatchService().launch();
}
@CapabilityMethod
- public synchronized void putPendingActions(String userId, ActionInfo actionInfo) {
+ public synchronized void putPendingActions(String userId, ActionData actionData) {
pendingActions.computeIfAbsent(userId, k -> {
- List temp = new ArrayList<>();
- temp.add(actionInfo);
+ List temp = new ArrayList<>();
+ temp.add(actionData);
return temp;
});
}
@CapabilityMethod
- public synchronized List popPendingAction(String userId) {
- List infos = pendingActions.get(userId);
+ public synchronized List popPendingAction(String userId) {
+ List infos = pendingActions.get(userId);
pendingActions.remove(userId);
return infos;
}
@CapabilityMethod
- public synchronized void putPreparedAction(String uuid, ActionInfo actionInfo) {
+ public synchronized void putPreparedAction(String uuid, ActionData actionData) {
preparedActions.computeIfAbsent(uuid, k -> {
- List temp = new ArrayList<>();
- temp.add(actionInfo);
+ List temp = new ArrayList<>();
+ temp.add(actionData);
return temp;
});
}
@CapabilityMethod
- public synchronized List popPreparedAction(String userId) {
- List infos = preparedActions.get(userId);
+ public synchronized List popPreparedAction(String userId) {
+ List infos = preparedActions.get(userId);
preparedActions.remove(userId);
return infos;
}
@CapabilityMethod
- public List listPreparedAction(String userId) {
+ public List listPreparedAction(String userId) {
return preparedActions.get(userId);
}
@CapabilityMethod
- public List listPendingAction(String userId) {
+ public List listPendingAction(String userId) {
return pendingActions.get(userId);
}
@@ -136,9 +150,9 @@ public class ActionCore extends PartnerCore {
}
}
- executor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient));
- executor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient));
- executor.execute(() -> adjustNotMatchPassed(notMatchPassed, inputVector, input, vectorClient));
+ platformExecutor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient));
+ platformExecutor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient));
+ platformExecutor.execute(() -> adjustNotMatchPassed(notMatchPassed, inputVector, input, vectorClient));
}
/**
@@ -148,7 +162,8 @@ public class ActionCore extends PartnerCore {
* @param inputVector 本次输入内容的语义向量
* @param vectorClient 向量客户端
*/
- private void adjustMatchAndPassed(List matchAndPassed, float[] inputVector, String input, VectorClient vectorClient) {
+ private void adjustMatchAndPassed(List matchAndPassed, float[] inputVector, String
+ input, VectorClient vectorClient) {
matchAndPassed.forEach(adjustData -> {
//获取原始缓存条目
String tendency = adjustData.getTendency();
@@ -204,7 +219,8 @@ public class ActionCore extends PartnerCore {
* @param input 本次输入内容
* @param vectorClient 向量客户端
*/
- private void adjustNotMatchPassed(List notMatchPassed, float[] inputVector, String input, VectorClient vectorClient) {
+ private void adjustNotMatchPassed(List notMatchPassed, float[] inputVector, String
+ input, VectorClient vectorClient) {
notMatchPassed.forEach(adjustData -> {
//获取原始缓存条目
String tendency = adjustData.getTendency();
@@ -232,4 +248,169 @@ public class ActionCore extends PartnerCore {
protected String getCoreKey() {
return "action-core";
}
+
+ @SuppressWarnings("unchecked")
+ private class ActionWatchService {
+
+ private HashMap registeredPaths = new HashMap<>();
+
+ public void launch() {
+ Path path = Path.of(ACTION_PROGRAM);
+ scanActions(path.toFile());
+ launchActionDirectoryWatcher(path);
+ }
+
+ private void launchActionDirectoryWatcher(Path path) {
+ WatchService watchService;
+ try {
+ watchService = FileSystems.getDefault().newWatchService();
+ setupShutdownHook(watchService);
+ registerParentToWatch(path, watchService);
+ registerSubToWatch(path, watchService);
+ virtualExecutor.execute(registerWatchTask(path, watchService));
+ } catch (IOException e) {
+ throw new ActionInitFailedException("行动程序目录监听器启动失败", e);
+ }
+ }
+
+ private void setupShutdownHook(WatchService watchService) {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ watchService.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ }
+
+ private Runnable registerWatchTask(Path path, WatchService watchService) {
+ return () -> {
+ log.info("[{}] 行动程序目录监听器已启动", getCoreKey());
+ while (true) {
+ WatchKey key;
+ try {
+ key = watchService.take();
+ List> events = key.pollEvents();
+ for (WatchEvent> e : events) {
+ WatchEvent event = (WatchEvent) e;
+ WatchEvent.Kind kind = event.kind();
+ Path context = event.context();
+ log.info("[{}] 行动程序目录变更事件: {} - {}", getCoreKey(), kind.name(), context.toString());
+ Path thisDir = (Path) key.watchable();
+ //根据事件发生的目录进行分流,分为父目录事件和子程序事件
+ if (thisDir.equals(path)) {
+ handleParentDirEvent(kind, thisDir, context, watchService);
+ } else {
+ handleSubDirEvent(kind, thisDir);
+ }
+ }
+ } catch (InterruptedException e) {
+ log.info("监听线程被中断,准备退出...");
+ Thread.currentThread().interrupt(); // 恢复中断标志
+ break;
+ } catch (ClosedWatchServiceException e) {
+ log.info("WatchService 已关闭,监听线程退出。");
+ break;
+ }
+ }
+ };
+ }
+
+ private void handleSubDirEvent(WatchEvent.Kind kind, Path thisDir) {
+ // path为触发本次行动的文件的路径(当前位于某个action目录下)
+ // 先判定发生的目录前缀是否匹配(action、desc),否则忽略
+ if (kind == StandardWatchEventKinds.ENTRY_CREATE || kind == StandardWatchEventKinds.ENTRY_MODIFY) {
+ // CREATE、MODIFY 事件将触发一次检测,看当前thisDir中action和desc是否都具备,如果通过则尝试加载(put)。
+ boolean complete = checkComplete(thisDir);
+ if (!complete) return;
+ try {
+ MetaActionInfo newActionInfo = new MetaActionInfo(thisDir.toFile());
+ existedMetaActions.put(thisDir.toString(), newActionInfo);
+ } catch (ActionLoadFailedException e) {
+ log.warn("[{}] 行动信息重新加载失败,触发行为: {}", getCoreKey(), kind.name());
+ }
+ } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
+ // DELETE 事件将会把该 MetaActionInfo 从记录中移除
+ existedMetaActions.remove(thisDir.toString());
+ }
+ }
+
+ private boolean checkComplete(Path thisDir) {
+ File[] files = thisDir.toFile().listFiles();
+ if (files == null) {
+ log.error("[{}]当前目录无法访问: [{}]", getCoreKey(), thisDir);
+ return false;
+ }
+ boolean existedAction = false;
+ boolean existedDesc = false;
+ for (File file : files) {
+ String fileName = file.getName();
+ String nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));
+ if (nameWithoutExt.equals("action")) existedAction = true;
+ else if (nameWithoutExt.equals("desc")) existedDesc = true;
+ }
+ return existedAction && existedDesc;
+ }
+
+ private void handleParentDirEvent(WatchEvent.Kind kind, Path thisDir, Path context, WatchService watchService) {
+ Path path = Path.of(thisDir.toString(), context.toString());
+ // MODIFY 事件不进行处理
+ if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
+ try {
+ path.register(watchService,
+ StandardWatchEventKinds.ENTRY_CREATE,
+ StandardWatchEventKinds.ENTRY_DELETE,
+ StandardWatchEventKinds.ENTRY_MODIFY);
+ } catch (IOException e) {
+ log.error("[{}] 新增行动程序目录监听失败: {}", getCoreKey(), path, e);
+ }
+ } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
+ WatchKey remove = registeredPaths.remove(path);
+ remove.cancel();
+ }
+ }
+
+ private void registerSubToWatch(Path path, WatchService watchService) throws IOException {
+ Files.walkFileTree(path, new SimpleFileVisitor<>() {
+ @Override
+ public @NotNull FileVisitResult preVisitDirectory(@NotNull Path dir, @NotNull BasicFileAttributes attrs) throws IOException {
+ if (dir.getFileName().startsWith(".")) return FileVisitResult.CONTINUE;
+ WatchKey key = dir.register(watchService,
+ StandardWatchEventKinds.ENTRY_CREATE,
+ StandardWatchEventKinds.ENTRY_DELETE,
+ StandardWatchEventKinds.ENTRY_MODIFY);
+ registeredPaths.put(dir, key);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ private void registerParentToWatch(Path path, WatchService watchService) throws IOException {
+ WatchKey key = path.register(watchService,
+ StandardWatchEventKinds.ENTRY_CREATE,
+ StandardWatchEventKinds.ENTRY_DELETE,
+ StandardWatchEventKinds.ENTRY_MODIFY);
+ registeredPaths.put(path, key);
+ }
+
+ private void scanActions(File file) {
+ if (!file.exists() || file.isFile()) {
+ throw new ActionInitFailedException("未找到行动程序目录: " + file.getAbsolutePath());
+ }
+ File[] files = file.listFiles();
+ if (files == null) {
+ throw new ActionInitFailedException("目录无法访问: " + file.getAbsolutePath());
+ }
+ for (File f : files) {
+ try {
+ MetaActionInfo actionInfo = new MetaActionInfo(f);
+ existedMetaActions.put(f.getName(), actionInfo);
+ log.info("[{}] 行动程序[{}]已加载", getCoreKey(), actionInfo.getKey());
+ } catch (ActionLoadFailedException e) {
+ log.warn("[{}] 行动程序未加载: {}", getCoreKey(), e.getMessage());
+ }
+ }
+
+ }
+ }
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionInfo.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionData.java
similarity index 54%
rename from Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionInfo.java
rename to Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionData.java
index d3a3318d..18ddcddc 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionInfo.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionData.java
@@ -4,8 +4,11 @@ import lombok.Data;
import java.util.List;
+/**
+ * 行动模块传递的行动数据,包含行动uuid、倾向、状态、行动链、结果、发起原因、行动描述等信息。
+ */
@Data
-public abstract class ActionInfo {
+public abstract class ActionData {
protected String uuid;
protected String tendency;
protected ActionStatus status;
@@ -13,4 +16,8 @@ public abstract class ActionInfo {
protected String Result;
protected String reason;
protected String description;
+
+ public enum ActionStatus {
+ SUCCESS, FAILED, EXECUTING, WAITING, PREPARE
+ }
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionStatus.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionStatus.java
deleted file mode 100644
index 31256076..00000000
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionStatus.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package work.slhaf.partner.core.action.entity;
-
-public enum ActionStatus {
- SUCCESS, FAILED, EXECUTING, WAITING, PREPARE
-}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionType.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionType.java
deleted file mode 100644
index 2354929e..00000000
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ActionType.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package work.slhaf.partner.core.action.entity;
-
-public enum ActionType {
- IMMEDIATE, PLANNING
-}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionInfo.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionData.java
similarity index 55%
rename from Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionInfo.java
rename to Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionData.java
index 5e65d871..388ea0b1 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionInfo.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ImmediateActionData.java
@@ -3,7 +3,10 @@ package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
+/**
+ * 即时行动数据类,继承自{@link ActionData}
+ */
@EqualsAndHashCode(callSuper = true)
@Data
-public class ImmediateActionInfo extends ActionInfo {
+public class ImmediateActionData extends ActionData {
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaAction.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaAction.java
index bdf2451b..349bebb8 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaAction.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaAction.java
@@ -2,25 +2,97 @@ package work.slhaf.partner.core.action.entity;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
+import work.slhaf.partner.common.Constant;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.concurrent.Callable;
+
+import static work.slhaf.partner.common.Constant.Path.ACTION_PROGRAM;
+
+/**
+ * 行动链中的单一元素,实现{@link Runnable}接口,封装了调用外部行动程序的必要信息,可被执行
+ */
@Data
-public class MetaAction implements Comparable {
- //行动key
+public class MetaAction implements Comparable, Callable {
+
+ /**
+ * 行动key,用于标识与定位行动程序
+ */
private String key;
- //行动参数
+ /**
+ * 行动程序可接受的参数,由调用处设置
+ */
private String[] params;
- //行动回应
- private String response;
- //执行顺序,升序排列
+ /**
+ * 行动结果,包括执行状态和相应内容(执行结果或者错误信息)
+ */
+ private Result result;
+ /**
+ * 执行顺序,升序排列
+ */
private int order;
+ /**
+ * 是否IO密集,用于决定使用何种线程池
+ */
private boolean io;
+ /**
+ * 行动程序类型,可分为PLUGIN(jar文件)、SCRIPT(Python程序)、MCP(MCP服务)
+ */
+ private MetaActionType type;
@Override
public int compareTo(@NotNull MetaAction metaAction) {
return this.order - metaAction.order;
}
- private void execute() {
+ @Override
+ public Void call() {
+ File action = loadFromFile();
+ if (!action.exists()) {
+ result = new Result();
+ result.setSuccess(false);
+ result.setData("Action file not found: " + action.getAbsolutePath());
+ return null;
+ }
+ try {
+ switch (type) {
+ case PLUGIN -> executePlugin(action);
+ case MCP -> executeMcp(action);
+ case SCRIPT -> executeScript(action);
+ }
+ } catch (Exception e) {
+ result = new Result();
+ result.setSuccess(false);
+ result.setData(e.getMessage());
+ }
+ return null;
+ }
+
+ private File loadFromFile() {
+ return switch (type) {
+ case PLUGIN -> Path.of(Constant.Path.ACTION_PROGRAM, key, "action.jar").toFile();
+ case SCRIPT -> Path.of(ACTION_PROGRAM, key, "action.py").toFile();
+ case MCP -> Path.of(ACTION_PROGRAM, key, "action.json").toFile();
+ };
+ }
+
+ private void executePlugin(File actionFile) {
}
+
+ private void executeMcp(File actionFile) {
+
+ }
+
+ private void executeScript(File actionFile) {
+
+ }
+
+ @Data
+ public static class Result {
+ private boolean success;
+ private String data;
+ }
+
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionInfo.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionInfo.java
new file mode 100644
index 00000000..c9bbd04d
--- /dev/null
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionInfo.java
@@ -0,0 +1,62 @@
+package work.slhaf.partner.core.action.entity;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson2.JSONObject;
+import lombok.Data;
+import org.apache.commons.io.FileUtils;
+import work.slhaf.partner.core.action.exception.ActionLoadFailedException;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class MetaActionInfo {
+ private String key;
+ private boolean io;
+ private MetaActionType type;
+
+ private Map params;
+ private String description;
+ private List tags;
+
+ private List preActions;
+ private List postActions;
+ /**
+ * 是否严格依赖前置行动的成功执行,若为true且前置行动失败则不执行该行动,后置任务多为触发式。默认即执行。
+ */
+ private boolean strictDependencies;
+
+ private JSONObject responseSchema;
+
+ public MetaActionInfo(File actionDir) {
+ if (actionDir.isFile()) {
+ throw new ActionLoadFailedException("Action directory expected, but file found: " + actionDir.getAbsolutePath());
+ }
+ File[] files = actionDir.listFiles();
+ if (files == null || files.length == 0) {
+ throw new ActionLoadFailedException("Action directory is empty: " + actionDir.getAbsolutePath());
+ }
+ //加载desc.json
+ File desc = Path.of(actionDir.getPath(), "desc.json").toFile();
+ if (!desc.exists() || desc.isDirectory()) {
+ throw new ActionLoadFailedException("Action desc.json not found: " + desc.getAbsolutePath());
+ }
+ try {
+ String s = FileUtils.readFileToString(desc, StandardCharsets.UTF_8);
+ MetaActionInfo temp = JSONObject.parseObject(s, MetaActionInfo.class);
+ BeanUtil.copyProperties(temp, this);
+ } catch (Exception e) {
+ throw new ActionLoadFailedException("Failed to load action desc.json: " + desc.getAbsolutePath(), e);
+ }
+ //进行必要的字段校验和初始化
+ if (type == null) throw new ActionLoadFailedException("Action type missing in desc.json");
+ if (params == null) params = new HashMap<>();
+ if (preActions == null) preActions = new ArrayList<>();
+ if (postActions == null) postActions = new ArrayList<>();
+ }
+}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionType.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionType.java
new file mode 100644
index 00000000..a260141a
--- /dev/null
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/MetaActionType.java
@@ -0,0 +1,5 @@
+package work.slhaf.partner.core.action.entity;
+
+public enum MetaActionType {
+ PLUGIN, MCP, SCRIPT
+}
\ No newline at end of file
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionInfo.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionData.java
similarity index 56%
rename from Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionInfo.java
rename to Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionData.java
index d2f1283b..00eaaaea 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionInfo.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/entity/ScheduledActionData.java
@@ -3,9 +3,12 @@ package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
+/**
+ * 计划行动数据类,继承自{@link ActionData},扩展了属性{@link ScheduledActionData#type}和{@link ScheduledActionData#scheduleContent},用于标识计划类型(单次还是周期性任务)和计划内容
+ */
@EqualsAndHashCode(callSuper = true)
@Data
-public class ScheduledActionInfo extends ActionInfo {
+public class ScheduledActionData extends ActionData {
private ScheduledType type;
private String scheduleContent; //如果为周期,则对应cron表达式,如果为一次性,则对应为LocalDateTime字符串
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionInitFailedException.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionInitFailedException.java
new file mode 100644
index 00000000..4e3e2849
--- /dev/null
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionInitFailedException.java
@@ -0,0 +1,13 @@
+package work.slhaf.partner.core.action.exception;
+
+import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
+
+public class ActionInitFailedException extends AgentLaunchFailedException {
+ public ActionInitFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ActionInitFailedException(String message) {
+ super(message);
+ }
+}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionLoadFailedException.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionLoadFailedException.java
new file mode 100644
index 00000000..7cded2a6
--- /dev/null
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/exception/ActionLoadFailedException.java
@@ -0,0 +1,13 @@
+package work.slhaf.partner.core.action.exception;
+
+import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
+
+public class ActionLoadFailedException extends AgentRuntimeException {
+ public ActionLoadFailedException(String message) {
+ super(message);
+ }
+
+ public ActionLoadFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java b/Partner-Main/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java
index 46924c05..98c6d4b6 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java
@@ -198,9 +198,9 @@ public class MemoryCore extends PartnerCore {
//尝试更新缓存
updateCache(topicPath, memoryResult);
} catch (Exception e) {
- log.error("[CoordinatedManager] selectMemory error: ", e);
- log.error("[CoordinatedManager] 路径: {}", topicPathStr);
- log.error("[CoordinatedManager] 主题树: {}", getTopicTree());
+ log.error("[{}] selectMemory error: ", getCoreKey(), e);
+ log.error("[{}] 路径: {}", getCoreKey(), topicPathStr);
+ log.error("[{}] 主题树: {}", getCoreKey(), getTopicTree());
memoryResult = new MemoryResult();
memoryResult.setRelatedMemorySliceResult(new ArrayList<>());
memoryResult.setMemorySliceResult(new CopyOnWriteArrayList<>());
@@ -211,7 +211,7 @@ public class MemoryCore extends PartnerCore {
@CapabilityMethod
public void updateActivatedSlices(String userId, List memorySlices) {
cache.activatedSlices.put(userId, memorySlices);
- log.debug("[CoordinatedManager] 已更新激活切片, userId: {}", userId);
+ log.debug("[{}] 已更新激活切片, userId: {}", getCoreKey(), userId);
}
@CapabilityMethod
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/ActionDispatcher.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/ActionDispatcher.java
index 8b278967..d07b95f3 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/ActionDispatcher.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/ActionDispatcher.java
@@ -6,9 +6,9 @@ import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
import work.slhaf.partner.core.action.ActionCapability;
-import work.slhaf.partner.core.action.entity.ActionInfo;
-import work.slhaf.partner.core.action.entity.ImmediateActionInfo;
-import work.slhaf.partner.core.action.entity.ScheduledActionInfo;
+import work.slhaf.partner.core.action.entity.ActionData;
+import work.slhaf.partner.core.action.entity.ImmediateActionData;
+import work.slhaf.partner.core.action.entity.ScheduledActionData;
import work.slhaf.partner.module.common.module.PostRunningModule;
import work.slhaf.partner.module.modules.action.dispatcher.executor.ActionExecutor;
import work.slhaf.partner.module.modules.action.dispatcher.scheduler.ActionScheduler;
@@ -42,14 +42,14 @@ public class ActionDispatcher extends PostRunningModule {
//对于将触发的PLANNING action,理想做法是将执行工具做成执行链的形式,模型的自对话流程、是否通知用户都做成与普通工具同等的通用可选能力,避免绑定固定流程
executor.execute(() -> {
String userId = context.getUserId();
- List preparedActions = actionCapability.popPreparedAction(userId);
+ List preparedActions = actionCapability.popPreparedAction(userId);
//分类成PLANNING和IMMEDIATE两类
- List scheduledActions = new ArrayList<>();
- List immediateActions = new ArrayList<>();
- for (ActionInfo preparedAction : preparedActions) {
- if (preparedAction instanceof ScheduledActionInfo actionInfo) {
+ List scheduledActions = new ArrayList<>();
+ List immediateActions = new ArrayList<>();
+ for (ActionData preparedAction : preparedActions) {
+ if (preparedAction instanceof ScheduledActionData actionInfo) {
scheduledActions.add(actionInfo);
- } else if (preparedAction instanceof ImmediateActionInfo actionInfo) {
+ } else if (preparedAction instanceof ImmediateActionData actionInfo) {
immediateActions.add(actionInfo);
}
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/executor/ActionExecutor.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/executor/ActionExecutor.java
index 435b65e7..8f97e60e 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/executor/ActionExecutor.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/executor/ActionExecutor.java
@@ -4,18 +4,18 @@ import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapabili
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.core.action.ActionCapability;
-import work.slhaf.partner.core.action.entity.ImmediateActionInfo;
+import work.slhaf.partner.core.action.entity.ImmediateActionData;
import java.util.List;
@AgentSubModule
-public class ActionExecutor extends AgentRunningSubModule, Void> {
+public class ActionExecutor extends AgentRunningSubModule, Void> {
@InjectCapability
private ActionCapability actionCapability;
@Override
- public Void execute(List immediateActions) {
+ public Void execute(List immediateActions) {
return null;
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/scheduler/ActionScheduler.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/scheduler/ActionScheduler.java
index 3c7d0878..a6fc5b07 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/scheduler/ActionScheduler.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/dispatcher/scheduler/ActionScheduler.java
@@ -2,14 +2,14 @@ package work.slhaf.partner.module.modules.action.dispatcher.scheduler;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
-import work.slhaf.partner.core.action.entity.ScheduledActionInfo;
+import work.slhaf.partner.core.action.entity.ScheduledActionData;
import java.util.List;
@AgentSubModule
-public class ActionScheduler extends AgentRunningSubModule, Void> {
+public class ActionScheduler extends AgentRunningSubModule, Void> {
@Override
- public Void execute(List data) {
+ public Void execute(List data) {
return null;
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/ActionPlanner.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/ActionPlanner.java
index 2629ac05..59088261 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/ActionPlanner.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/ActionPlanner.java
@@ -9,10 +9,9 @@ import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
import work.slhaf.partner.common.vector.VectorClient;
import work.slhaf.partner.core.action.ActionCapability;
-import work.slhaf.partner.core.action.entity.ActionInfo;
-import work.slhaf.partner.core.action.entity.ActionStatus;
-import work.slhaf.partner.core.action.entity.ImmediateActionInfo;
-import work.slhaf.partner.core.action.entity.ScheduledActionInfo;
+import work.slhaf.partner.core.action.entity.ActionData;
+import work.slhaf.partner.core.action.entity.ImmediateActionData;
+import work.slhaf.partner.core.action.entity.ScheduledActionData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustMetaData;
import work.slhaf.partner.core.cognation.CognationCapability;
@@ -140,10 +139,10 @@ public class ActionPlanner extends PreRunningModule {
return;
}
String contextUuid = context.getUuid();
- List pendingActions = actionCapability.popPendingAction(context.getUserId());
- for (ActionInfo actionInfo : pendingActions) {
- if (uuids.contains(actionInfo.getUuid())) {
- actionCapability.putPreparedAction(contextUuid, actionInfo);
+ List pendingActions = actionCapability.popPendingAction(context.getUserId());
+ for (ActionData actionData : pendingActions) {
+ if (uuids.contains(actionData.getUuid())) {
+ actionCapability.putPreparedAction(contextUuid, actionData);
}
}
}
@@ -151,11 +150,11 @@ public class ActionPlanner extends PreRunningModule {
private void setupActionInfo(List evaluatorResults, PartnerRunningFlowContext context) {
for (EvaluatorResult evaluatorResult : evaluatorResults) {
- ActionInfo actionInfo = assemblyHelper.buildMetaActionInfo(evaluatorResult);
+ ActionData actionData = assemblyHelper.buildMetaActionInfo(evaluatorResult);
if (evaluatorResult.isNeedConfirm()) {
- actionCapability.putPendingActions(context.getUserId(), actionInfo);
+ actionCapability.putPendingActions(context.getUserId(), actionData);
} else {
- actionCapability.putPreparedAction(context.getUuid(), actionInfo);
+ actionCapability.putPreparedAction(context.getUuid(), actionData);
}
}
}
@@ -170,31 +169,31 @@ public class ActionPlanner extends PreRunningModule {
}
private void setupPendingActions(HashMap map, String userId) {
- List actionInfos = actionCapability.listPendingAction(userId);
- if (actionInfos == null || actionInfos.isEmpty()) {
+ List actionData = actionCapability.listPendingAction(userId);
+ if (actionData == null || actionData.isEmpty()) {
map.put("[待确认行动] <待确认行动信息>", "无待确认行动");
return;
}
- for (int i = 0; i < actionInfos.size(); i++) {
- map.put("[待确认行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
+ for (int i = 0; i < actionData.size(); i++) {
+ map.put("[待确认行动 " + (i + 1) + " ]", generateActionStr(actionData.get(i)));
}
}
private void setupPreparedActions(HashMap map, String uuid) {
- List actionInfos = actionCapability.listPreparedAction(uuid);
- if (actionInfos == null || actionInfos.isEmpty()) {
+ List actionData = actionCapability.listPreparedAction(uuid);
+ if (actionData == null || actionData.isEmpty()) {
map.put("[预备行动] <预备行动信息>", "无预备行动");
return;
}
- for (int i = 0; i < actionInfos.size(); i++) {
- map.put("[预备行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
+ for (int i = 0; i < actionData.size(); i++) {
+ map.put("[预备行动 " + (i + 1) + " ]", generateActionStr(actionData.get(i)));
}
}
- private String generateActionStr(ActionInfo actionInfo) {
- return "<行动倾向>" + " : " + actionInfo.getTendency() +
- "<行动原因>" + " : " + actionInfo.getReason() +
- "<工具描述>" + " : " + actionInfo.getDescription();
+ private String generateActionStr(ActionData actionData) {
+ return "<行动倾向>" + " : " + actionData.getTendency() +
+ "<行动原因>" + " : " + actionData.getReason() +
+ "<工具描述>" + " : " + actionData.getDescription();
}
@Override
@@ -229,20 +228,20 @@ public class ActionPlanner extends PreRunningModule {
return input;
}
- private ActionInfo buildMetaActionInfo(EvaluatorResult evaluatorResult) {
+ private ActionData buildMetaActionInfo(EvaluatorResult evaluatorResult) {
return switch (evaluatorResult.getType()) {
case PLANNING -> {
- ScheduledActionInfo actionInfo = new ScheduledActionInfo();
+ ScheduledActionData actionInfo = new ScheduledActionData();
actionInfo.setActionChain(evaluatorResult.getActionChain());
actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
- actionInfo.setStatus(ActionStatus.PREPARE);
+ actionInfo.setStatus(ActionData.ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
case IMMEDIATE -> {
- ImmediateActionInfo actionInfo = new ImmediateActionInfo();
+ ImmediateActionData actionInfo = new ImmediateActionData();
actionInfo.setActionChain(evaluatorResult.getActionChain());
- actionInfo.setStatus(ActionStatus.PREPARE);
+ actionInfo.setStatus(ActionData.ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
@@ -252,8 +251,8 @@ public class ActionPlanner extends PreRunningModule {
private ConfirmerInput buildConfirmerInput(PartnerRunningFlowContext context) {
ConfirmerInput confirmerInput = new ConfirmerInput();
confirmerInput.setInput(context.getInput());
- List pendingActions = actionCapability.listPendingAction(context.getUserId());
- confirmerInput.setActionInfos(pendingActions);
+ List pendingActions = actionCapability.listPendingAction(context.getUserId());
+ confirmerInput.setActionData(pendingActions);
return confirmerInput;
}
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/confirmer/entity/ConfirmerInput.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/confirmer/entity/ConfirmerInput.java
index 54f0247c..40b0b5ac 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/confirmer/entity/ConfirmerInput.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/confirmer/entity/ConfirmerInput.java
@@ -2,13 +2,13 @@ package work.slhaf.partner.module.modules.action.planner.confirmer.entity;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
-import work.slhaf.partner.core.action.entity.ActionInfo;
+import work.slhaf.partner.core.action.entity.ActionData;
import java.util.List;
@Data
public class ConfirmerInput {
private String input;
- private List actionInfos;
+ private List actionData;
private List recentMessages;
}
diff --git a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/evaluator/entity/EvaluatorResult.java b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/evaluator/entity/EvaluatorResult.java
index 1f7232b7..76289180 100644
--- a/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/evaluator/entity/EvaluatorResult.java
+++ b/Partner-Main/src/main/java/work/slhaf/partner/module/modules/action/planner/evaluator/entity/EvaluatorResult.java
@@ -1,7 +1,6 @@
package work.slhaf.partner.module.modules.action.planner.evaluator.entity;
import lombok.Data;
-import work.slhaf.partner.core.action.entity.ActionType;
import work.slhaf.partner.core.action.entity.MetaAction;
import java.util.List;
@@ -14,4 +13,8 @@ public class EvaluatorResult {
private String scheduleContent;
private List actionChain;
private String tendency;
+
+ public enum ActionType {
+ IMMEDIATE, PLANNING
+ }
}