1 Commits

Author SHA1 Message Date
bfdc9b00e5 ActionPlanner 流程图制作完毕 2025-11-17 15:11:02 +08:00
119 changed files with 449 additions and 7527 deletions

8
.gitignore vendored
View File

@@ -55,6 +55,8 @@ build/
/data/ /data/
/generated-classes/ /generated-classes/
/.idea/copilot.data.migration.ask2agent.xml /.idea/copilot.data.migration.ask2agent.xml
/Partner-Main/data/ .idea/copilot.data.migration.agent.xml
/AGENTS.md .gitignore
/.serena/ .idea/copilot.data.migration.edit.xml
.gitignore
.idea/copilot.data.migration.ask.xml

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

3
.idea/encodings.xml generated
View File

@@ -3,13 +3,10 @@
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Main/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Main/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-SandboxRunner/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />

15
.idea/misc.xml generated
View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="EntryPointsManager"> <component name="EntryPointsManager">
<list size="15"> <list size="14">
<item index="0" class="java.lang.String" itemvalue="lombok.Data" /> <item index="0" class="java.lang.String" itemvalue="lombok.Data" />
<item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" /> <item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" />
<item index="2" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" /> <item index="2" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
@@ -10,12 +11,11 @@
<item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" /> <item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" />
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" /> <item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentModule" /> <item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentModule" />
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" /> <item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" /> <item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" /> <item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" /> <item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" /> <item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
<item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
</list> </list>
<writeAnnotations> <writeAnnotations>
<writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" /> <writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" />
@@ -27,7 +27,6 @@
<option name="originalFiles"> <option name="originalFiles">
<list> <list>
<option value="$PROJECT_DIR$/pom.xml" /> <option value="$PROJECT_DIR$/pom.xml" />
<option value="$PROJECT_DIR$/PartnerExecutor/pom.xml" />
</list> </list>
</option> </option>
</component> </component>

View File

@@ -23,7 +23,7 @@ public interface ActivateModel {
ModelConfig modelConfig = AgentConfigManager.INSTANCE.loadModelConfig(modelKey()); ModelConfig modelConfig = AgentConfigManager.INSTANCE.loadModelConfig(modelKey());
model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey()) : loadSpecificPrompt(modelKey())); model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey()) : loadSpecificPrompt(modelKey()));
model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel())); model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel()));
setModel(model); ((Module) this).setModel(model);
} }
default void updateModelSettings(ChatClient newChatClient) { default void updateModelSettings(ChatClient newChatClient) {

View File

@@ -1,12 +1,10 @@
package work.slhaf.partner.api.chat; package work.slhaf.partner.api.chat;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.chat.constant.ChatConstant; import work.slhaf.partner.api.chat.constant.ChatConstant;
import work.slhaf.partner.api.chat.pojo.ChatBody; import work.slhaf.partner.api.chat.pojo.ChatBody;
import work.slhaf.partner.api.chat.pojo.ChatResponse; import work.slhaf.partner.api.chat.pojo.ChatResponse;
@@ -15,7 +13,6 @@ import work.slhaf.partner.api.chat.pojo.PrimaryChatResponse;
import java.util.List; import java.util.List;
@Slf4j
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class ChatClient { public class ChatClient {
@@ -37,8 +34,6 @@ public class ChatClient {
public ChatResponse runChat(List<Message> messages) { public ChatResponse runChat(List<Message> messages) {
HttpRequest request = HttpRequest.post(url); HttpRequest request = HttpRequest.post(url);
request.setConnectionTimeout(2000);
request.setReadTimeout(15000);
request.header("Content-Type", "application/json"); request.header("Content-Type", "application/json");
request.header("Authorization", "Bearer " + apikey); request.header("Authorization", "Bearer " + apikey);
@@ -58,26 +53,17 @@ public class ChatClient {
.build(); .build();
} }
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
ChatResponse finalResponse; ChatResponse finalResponse;
try {
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class); PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
finalResponse = ChatResponse.builder() finalResponse = ChatResponse.builder()
.status(ChatConstant.ResponseStatus.SUCCESS) .type(ChatConstant.Response.SUCCESS)
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent()) .message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
.usageBean(primaryChatResponse.getUsage()) .usageBean(primaryChatResponse.getUsage())
.build(); .build();
response.close(); response.close();
} catch (IORuntimeException e) {
log.error("请求超时", e);
finalResponse = ChatResponse.builder()
.message("连接超时")
.status(ChatConstant.ResponseStatus.FAILED)
.usageBean(null)
.build();
}
return finalResponse; return finalResponse;
} }

View File

@@ -8,7 +8,8 @@ public class ChatConstant {
public static final String ASSISTANT = "assistant"; public static final String ASSISTANT = "assistant";
} }
public enum ResponseStatus { public static class Response {
SUCCESS, FAILED public static final String SUCCESS = "success";
public static final String ERROR = "error";
} }
} }

View File

@@ -4,14 +4,13 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import work.slhaf.partner.api.chat.constant.ChatConstant;
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class ChatResponse { public class ChatResponse {
private ChatConstant.ResponseStatus status; private String type;
private String message; private String message;
private PrimaryChatResponse.UsageBean usageBean; private PrimaryChatResponse.UsageBean usageBean;
} }

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>work.slhaf</groupId>
<artifactId>Partner</artifactId>
<version>0.5.0</version>
</parent>
<artifactId>Partner-Common</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.modelcontextprotocol.sdk/mcp -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>0.17.0</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -1,155 +0,0 @@
package work.slhaf.partner.common.mcp;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.server.McpStatelessServerHandler;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpStatelessServerTransport;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
public final class InProcessMcpTransport implements McpClientTransport, McpStatelessServerTransport {
// 每个 transport 只处理一条 inbound 流
private final Sinks.Many<McpSchema.JSONRPCMessage> inbound =
Sinks.many().unicast().onBackpressureBuffer();
private final AtomicBoolean clientConnected = new AtomicBoolean(false);
private final AtomicBoolean serverConnected = new AtomicBoolean(false);
/**
* 对端
*/
private volatile InProcessMcpTransport peer;
private volatile McpStatelessServerHandler serverHandler;
public record Pair(InProcessMcpTransport clientSide, InProcessMcpTransport serverSide) {
}
public static Pair pair() {
InProcessMcpTransport client = new InProcessMcpTransport();
InProcessMcpTransport server = new InProcessMcpTransport();
client.peer = server;
server.peer = client;
return new Pair(client, server);
}
/* ======================================================
* Internal receive: peer.sendMessage -> this.receive
* ====================================================== */
private void receive(McpSchema.JSONRPCMessage message) {
if (inbound.tryEmitNext(message).isFailure()) {
throw new RuntimeException("Failed to receive message: " + message);
}
}
/* ======================================================
* Client → Server sendMessage
* ====================================================== */
@Override
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
InProcessMcpTransport p = this.peer;
if (p == null) {
return Mono.error(new IllegalStateException("Transport is not linked"));
}
return Mono.fromRunnable(() -> p.receive(message));
}
/* ======================================================
* Client connect(handler) 处理 server → client 消息
* ====================================================== */
@Override
public Mono<Void> connect(Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> handler) {
if (!clientConnected.compareAndSet(false, true)) {
return Mono.error(new IllegalStateException("Client already connected"));
}
return inbound.asFlux()
.concatMap(msg ->
handler.apply(Mono.just(msg))
// handler may emit response message → send back to server
.flatMap(resp -> resp != null ? sendMessage(resp) : Mono.empty())
)
.doFinally(sig -> clientConnected.set(false))
.then();
}
@Override
public void setExceptionHandler(Consumer<Throwable> handler) {
McpClientTransport.super.setExceptionHandler(handler);
}
/* ======================================================
* Server: bind stateless handler = process client → server inbound
* ====================================================== */
@Override
public void setMcpHandler(McpStatelessServerHandler handler) {
this.serverHandler = handler;
if (!serverConnected.compareAndSet(false, true)) {
throw new IllegalStateException("Server already connected");
}
// 订阅 client → server 消息
inbound.asFlux()
.concatMap(this::handleServerMessage)
.doFinally(sig -> serverConnected.set(false))
.subscribe();
}
/**
* Server 端处理 JSONRPCMessage
*/
private Mono<Void> handleServerMessage(McpSchema.JSONRPCMessage msg) {
// 创建 transport context简单实现即可
McpTransportContext ctx = key -> null;
if (msg instanceof McpSchema.JSONRPCRequest req) {
return serverHandler.handleRequest(ctx, req)
.flatMap(this::sendMessage);
}
if (msg instanceof McpSchema.JSONRPCNotification noti) {
return serverHandler.handleNotification(ctx, noti);
}
return Mono.empty();
}
/* ======================================================
* other boilerplate
* ====================================================== */
@Override
public void close() {
McpClientTransport.super.close();
}
@Override
public Mono<Void> closeGracefully() {
inbound.tryEmitComplete();
clientConnected.set(false);
serverConnected.set(false);
return Mono.empty();
}
@Override
public <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {
return McpJsonMapper.getDefault().convertValue(data, typeRef);
}
@Override
public List<String> protocolVersions() {
return McpClientTransport.super.protocolVersions();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -26,11 +26,7 @@
<groupId>org.jetbrains.kotlinx</groupId> <groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId> <artifactId>kotlinx-coroutines-core</artifactId>
<version>1.10.2</version> <version>1.10.2</version>
</dependency> <scope>test</scope>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-test</artifactId>
<version>1.10.2</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.nd4j/nd4j-api --> <!-- https://mvnrepository.com/artifact/org.nd4j/nd4j-api -->
<dependency> <dependency>
@@ -48,38 +44,6 @@
<artifactId>tokenizers</artifactId> <artifactId>tokenizers</artifactId>
<version>0.34.0</version> <version>0.34.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.modelcontextprotocol.sdk/mcp -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>0.17.0</version>
</dependency>
<dependency>
<groupId>work.slhaf</groupId>
<artifactId>Partner-Common</artifactId>
<version>0.5.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.20.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.20.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId>
<version>9.2.1</version>
</dependency>
</dependencies> </dependencies>
<properties> <properties>

View File

@@ -12,7 +12,7 @@ public class Main {
.setAgentConfigManager(PartnerAgentConfigManager.class) .setAgentConfigManager(PartnerAgentConfigManager.class)
.setGateway(WebSocketGateway.class) .setGateway(WebSocketGateway.class)
.setAgentExceptionCallback(PartnerExceptionCallback.class) .setAgentExceptionCallback(PartnerExceptionCallback.class)
.addAfterLaunchRunners(VectorClient::load) .addAfterLaunchRunners(() -> VectorClient.load())
.launch(); .launch();
} }
} }

View File

@@ -1,10 +0,0 @@
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";
}
}

View File

@@ -1,14 +0,0 @@
package work.slhaf.partner.common.util;
public class PathUtil {
public static String buildPathStr(String... path) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < path.length; i++) {
str.append(path[i]);
if (i < path.length - 1) {
str.append("/");
}
}
return str.toString();
}
}

View File

@@ -13,11 +13,10 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import static work.slhaf.partner.common.Constant.Path.MEMORY_DATA;
@Slf4j @Slf4j
public abstract class PartnerCore<T extends PartnerCore<T>> extends PersistableObject { public abstract class PartnerCore<T extends PartnerCore<T>> extends PersistableObject {
private static final String STORAGE_DIR = "./data/memory/";
private final String id = ((PartnerAgentConfigManager) AgentConfigManager.INSTANCE).getConfig().getAgentId(); private final String id = ((PartnerAgentConfigManager) AgentConfigManager.INSTANCE).getConfig().getAgentId();
public PartnerCore() throws IOException, ClassNotFoundException { public PartnerCore() throws IOException, ClassNotFoundException {
@@ -54,7 +53,7 @@ public abstract class PartnerCore<T extends PartnerCore<T>> extends PersistableO
public void serialize() throws IOException { public void serialize() throws IOException {
//先写入到临时文件,如果正常写入则覆盖原文件 //先写入到临时文件,如果正常写入则覆盖原文件
Path filePath = getFilePath(id + "-temp"); Path filePath = getFilePath(id + "-temp");
Files.createDirectories(Path.of(MEMORY_DATA)); Files.createDirectories(Path.of(STORAGE_DIR));
try { try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile())); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile()));
oos.writeObject(this); oos.writeObject(this);
@@ -79,12 +78,12 @@ public abstract class PartnerCore<T extends PartnerCore<T>> extends PersistableO
} }
private Path getFilePath(String s) { private Path getFilePath(String s) {
return Paths.get(MEMORY_DATA, s + "-" + getCoreKey() + ".memory"); return Paths.get(STORAGE_DIR, s + "-" + getCoreKey() + ".memory");
} }
private void createStorageDirectory() { private void createStorageDirectory() {
try { try {
Files.createDirectories(Paths.get(MEMORY_DATA)); Files.createDirectories(Paths.get(STORAGE_DIR));
} catch (IOException e) { } catch (IOException e) {
log.error("[{}]创建存储目录失败: {}", getCoreKey(), e.getMessage()); log.error("[{}]创建存储目录失败: {}", getCoreKey(), e.getMessage());
} }

View File

@@ -1,59 +1,26 @@
package work.slhaf.partner.core.action; package work.slhaf.partner.core.action;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
import work.slhaf.partner.api.agent.factory.capability.annotation.Capability; import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
import work.slhaf.partner.core.action.entity.ActionData; import work.slhaf.partner.core.action.entity.CacheAdjustData;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaActionInfo; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.PhaserRecord;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustData;
import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Phaser;
@Capability(value = "action") @Capability(value = "action")
public interface ActionCapability { public interface ActionCapability {
void putPreparedAction(String uuid, MetaActionInfo metaActionInfo);
void putAction(@NonNull ActionData actionData); List<MetaActionInfo> popPreparedAction(String userId);
Set<ActionData> listActions(@Nullable ActionData.ActionStatus actionStatus, @Nullable String source); List<MetaActionInfo> popPendingAction(String userId);
List<ActionData> popPendingAction(String userId); List<MetaActionInfo> listPreparedAction(String userId);
List<ActionData> listPendingAction(String userId); List<MetaActionInfo> listPendingAction(String userId);
void putPendingActions(String userId, ActionData actionData); void putPendingActions(String userId, MetaActionInfo metaActionInfo);
List<String> selectTendencyCache(String input); List<String> selectTendencyCache(String input);
void updateTendencyCache(CacheAdjustData data); void updateTendencyCache(CacheAdjustData data);
ExecutorService getExecutor(ActionCore.ExecutorType type);
PhaserRecord putPhaserRecord(Phaser phaser, ActionData actionData);
void removePhaserRecord(Phaser phaser);
List<PhaserRecord> listPhaserRecords();
PhaserRecord getPhaserRecord(String tendency, String source);
MetaAction loadMetaAction(@NonNull String actionKey);
MetaActionInfo loadMetaActionInfo(@NonNull String actionKey);
Map<String, MetaActionInfo> listAvailableMetaActions();
boolean checkExists(String... actionKeys);
RunnerClient runnerClient();
void handleInterventions(List<MetaIntervention> interventions, ActionData data);
} }

View File

@@ -1,115 +1,91 @@
package work.slhaf.partner.core.action; package work.slhaf.partner.core.action;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import lombok.val; import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
import org.jetbrains.annotations.Nullable;
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore;
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod; import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod;
import work.slhaf.partner.common.vector.VectorClient; import work.slhaf.partner.common.vector.VectorClient;
import work.slhaf.partner.core.PartnerCore; import work.slhaf.partner.core.PartnerCore;
import work.slhaf.partner.core.action.entity.ActionData; import work.slhaf.partner.core.action.entity.ActionCacheData;
import work.slhaf.partner.core.action.entity.MetaAction; import work.slhaf.partner.core.action.entity.CacheAdjustData;
import work.slhaf.partner.core.action.entity.CacheAdjustMetaData;
import work.slhaf.partner.core.action.entity.MetaActionInfo; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.PhaserRecord;
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.ActionDataNotFoundException;
import work.slhaf.partner.core.action.exception.MetaActionNotFoundException;
import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.action.runner.SandboxRunnerClient;
import work.slhaf.partner.module.modules.action.interventor.entity.InterventionType;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.concurrent.*; import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@SuppressWarnings("FieldMayBeFinal") @SuppressWarnings("FieldMayBeFinal")
@CapabilityCore(value = "action") @Capability(value = "action")
@Slf4j @Slf4j
public class ActionCore extends PartnerCore<ActionCore> { public class ActionCore extends PartnerCore<ActionCore> {
/** /**
* 持久行动池 * 对应本次交互即将执行或将要放置在行动池的预备任务因此将以本次交互的uuid为键其起到的作用相当于暂时的模块上下文
*/ */
private CopyOnWriteArraySet<ActionData> actionPool = new CopyOnWriteArraySet<>(); private HashMap<String, List<MetaActionInfo>> preparedActions = new HashMap<>();
/** /**
* 待确认任务以userId区分不同用户因为需要跨请求确认 * 待确认任务以userId区分不同用户因为需要跨请求确认
*/ */
private HashMap<String, List<ActionData>> pendingActions = new HashMap<>(); private HashMap<String, List<MetaActionInfo>> pendingActions = new HashMap<>();
/** /**
* 语义缓存与行为倾向映射 * 语义缓存与行为倾向映射
*/ */
private List<ActionCacheData> actionCache = new ArrayList<>(); private List<ActionCacheData> actionCache = new ArrayList<>();
private final Lock cacheLock = new ReentrantLock(); private Lock cacheLock = new ReentrantLock();
// 由于当前的执行器逻辑实现,平台线程池大小不得小于 2这里规定为最小为 4 private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private final ExecutorService platformExecutor = Executors
.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4));
private final ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
/**
* 已存在的行动程序,键格式为‘<MCP-ServerName>::<Tool-Name>’,值为 MCP Server 通过 Resources 相关渠道传递的行动程序元信息
*/
private final ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
private final List<PhaserRecord> phaserRecords = new ArrayList<>();
private RunnerClient runnerClient;
public ActionCore() throws IOException, ClassNotFoundException { public ActionCore() throws IOException, ClassNotFoundException {
// TODO 通过 AgentConfigManager指定采用何种 runnerClient
runnerClient = new SandboxRunnerClient(existedMetaActions, virtualExecutor);
setupShutdownHook();
}
private void setupShutdownHook() {
// 将执行中的行动状态置为失败
val executingActionSet = listActions(ActionData.ActionStatus.EXECUTING, null);
for (ActionData actionData : executingActionSet) {
actionData.setStatus(ActionData.ActionStatus.FAILED);
actionData.setResult("由于系统中断而失败");
}
} }
@CapabilityMethod @CapabilityMethod
public void putAction(@NonNull ActionData actionData) { public synchronized void putPendingActions(String userId, MetaActionInfo metaActionInfo) {
actionPool.removeIf(data -> data.getUuid().equals(actionData.getUuid())); // 用来应对 ScheduledActionData 的重新排列
actionPool.add(actionData);
}
@CapabilityMethod
public Set<ActionData> listActions(@Nullable ActionData.ActionStatus actionStatus, @Nullable String source) {
return actionPool.stream()
.filter(actionData -> actionStatus == null || actionData.getStatus().equals(actionStatus))
.filter(actionData -> source == null || actionData.getSource().equals(source))
.collect(Collectors.toSet());
}
@CapabilityMethod
public synchronized void putPendingActions(String userId, ActionData actionData) {
pendingActions.computeIfAbsent(userId, k -> { pendingActions.computeIfAbsent(userId, k -> {
List<ActionData> temp = new ArrayList<>(); List<MetaActionInfo> temp = new ArrayList<>();
temp.add(actionData); temp.add(metaActionInfo);
return temp; return temp;
}); });
} }
@CapabilityMethod @CapabilityMethod
public synchronized List<ActionData> popPendingAction(String userId) { public synchronized List<MetaActionInfo> popPendingAction(String userId) {
List<ActionData> infos = pendingActions.get(userId); List<MetaActionInfo> infos = pendingActions.get(userId);
pendingActions.remove(userId); pendingActions.remove(userId);
return infos; return infos;
} }
@CapabilityMethod @CapabilityMethod
public List<ActionData> listPendingAction(String userId) { public synchronized void putPreparedAction(String uuid, MetaActionInfo metaActionInfo) {
preparedActions.computeIfAbsent(uuid, k -> {
List<MetaActionInfo> temp = new ArrayList<>();
temp.add(metaActionInfo);
return temp;
});
}
@CapabilityMethod
public synchronized List<MetaActionInfo> popPreparedAction(String userId) {
List<MetaActionInfo> infos = preparedActions.get(userId);
preparedActions.remove(userId);
return infos;
}
@CapabilityMethod
public List<MetaActionInfo> listPreparedAction(String userId) {
return preparedActions.get(userId);
}
@CapabilityMethod
public List<MetaActionInfo> listPendingAction(String userId) {
return pendingActions.get(userId); return pendingActions.get(userId);
} }
@@ -125,11 +101,10 @@ public class ActionCore extends PartnerCore<ActionCore> {
return null; return null;
} }
VectorClient vectorClient = VectorClient.INSTANCE; VectorClient vectorClient = VectorClient.INSTANCE;
// 计算本次输入的向量 //计算本次输入的向量
float[] vector = vectorClient.compute(input); float[] vector = vectorClient.compute(input);
if (vector == null) if (vector == null) return null;
return null; //与现有缓存比对,将匹配到的收集并返回
// 与现有缓存比对,将匹配到的收集并返回
return actionCache.parallelStream() return actionCache.parallelStream()
.filter(ActionCacheData::isActivated) .filter(ActionCacheData::isActivated)
.filter(data -> { .filter(data -> {
@@ -161,188 +136,9 @@ public class ActionCore extends PartnerCore<ActionCore> {
} }
} }
platformExecutor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient)); executor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient));
platformExecutor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient)); executor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient));
platformExecutor.execute(() -> adjustNotMatchPassed(notMatchPassed, inputVector, input, vectorClient)); executor.execute(() -> adjustNotMatchPassed(notMatchPassed, inputVector, input, vectorClient));
}
@CapabilityMethod
public ExecutorService getExecutor(ExecutorType type) {
return switch (type) {
case VIRTUAL -> virtualExecutor;
case PLATFORM -> platformExecutor;
};
}
@CapabilityMethod
public Map<String, MetaActionInfo> listAvailableActions() {
return existedMetaActions;
}
@CapabilityMethod
public synchronized PhaserRecord putPhaserRecord(Phaser phaser, ActionData actionData) {
PhaserRecord record = new PhaserRecord(phaser, actionData);
phaserRecords.add(record);
return record;
}
@CapabilityMethod
public synchronized void removePhaserRecord(Phaser phaser) {
PhaserRecord remove = null;
for (PhaserRecord record : phaserRecords) {
if (record.phaser().equals(phaser)) {
remove = record;
}
}
if (remove != null) {
phaserRecords.remove(remove);
}
}
@CapabilityMethod
public PhaserRecord getPhaserRecord(String tendency, String source) {
for (PhaserRecord record : phaserRecords) {
ActionData data = record.actionData();
if (data.getTendency().equals(tendency) && data.getSource().equals(source)) {
return record;
}
}
throw new ActionDataNotFoundException("未找到对应的 Phaser 记录: tendency=" + tendency + ", source=" + source);
}
@CapabilityMethod
public MetaAction loadMetaAction(@NonNull String actionKey) {
MetaActionInfo metaActionInfo = existedMetaActions.get(actionKey);
if (metaActionInfo == null) {
throw new MetaActionNotFoundException("未找到对应的行动程序信息" + actionKey);
}
String[] split = actionKey.split("::");
if (split.length < 2) {
throw new MetaActionNotFoundException("未找到对应的行动程序,原因: 传入的 actionKey(" + actionKey + ") 存在异常");
}
return new MetaAction(
split[1],
metaActionInfo.isIo(),
MetaAction.Type.MCP,
split[0]
);
}
@CapabilityMethod
public List<PhaserRecord> listPhaserRecords() {
return phaserRecords;
}
@CapabilityMethod
public MetaActionInfo loadMetaActionInfo(@NonNull String actionKey) {
MetaActionInfo info = existedMetaActions.get(actionKey);
if (info == null) {
throw new MetaActionNotFoundException("未找到对应的行动程序描述信息: " + actionKey);
}
return info;
}
@CapabilityMethod
public boolean checkExists(String... actionKeys) {
return existedMetaActions.keySet().containsAll(Arrays.asList(actionKeys));
}
@CapabilityMethod
public RunnerClient runnerClient() {
return runnerClient;
}
@CapabilityMethod
public void handleInterventions(List<MetaIntervention> interventions, ActionData actionData) {
// 加载数据
if (actionData == null) {
return;
}
// 加锁确保同步
synchronized (actionData.getStatus()) {
applyInterventions(interventions, actionData);
}
}
private void applyInterventions(List<MetaIntervention> interventions, ActionData actionData) {
boolean rebuildCleanTag = false;
interventions.sort(Comparator.comparingInt(MetaIntervention::getOrder));
for (MetaIntervention intervention : interventions) {
List<MetaAction> actions = intervention.getActions()
.stream()
.map(this::loadMetaAction)
.toList();
switch (intervention.getType()) {
case InterventionType.APPEND -> handleAppend(actionData, intervention.getOrder(), actions);
case InterventionType.INSERT -> handleInsert(actionData, intervention.getOrder(), actions);
case InterventionType.DELETE -> handleDelete(actionData, intervention.getOrder(), actions);
case InterventionType.CANCEL -> handleCancel(actionData);
case InterventionType.REBUILD -> {
if (!rebuildCleanTag) {
cleanActionData(actionData);
rebuildCleanTag = true;
}
handleRebuild(actionData, intervention.getOrder(), actions);
}
}
}
}
/**
* 在未进入执行阶段的行动单元组新增新的行动
*/
private void handleAppend(ActionData actionData, int order, List<MetaAction> actions) {
if (order <= actionData.getExecutingStage())
return;
actionData.getActionChain().put(order, actions);
}
/**
* 在未进入执行阶段和正处于行动阶段的行动单元组插入新的行动
*/
private void handleInsert(ActionData actionData, int order, List<MetaAction> actions) {
if (order < actionData.getExecutingStage())
return;
actionData.getActionChain().computeIfAbsent(order, k -> new ArrayList<>()).addAll(actions);
}
private void handleDelete(ActionData actionData, int order, List<MetaAction> actions) {
if (order <= actionData.getExecutingStage())
return;
Map<Integer, List<MetaAction>> actionChain = actionData.getActionChain();
if (actionChain.containsKey(order)) {
actionChain.get(order).removeAll(actions);
if (actionChain.get(order).isEmpty()) {
actionChain.remove(order);
}
}
}
private void handleCancel(ActionData actionData) {
actionData.setStatus(ActionData.ActionStatus.FAILED);
actionData.setResult("行动取消");
}
private void handleRebuild(ActionData actionData, int order, List<MetaAction> actions) {
Map<Integer, List<MetaAction>> actionChain = actionData.getActionChain();
actionChain.put(order, actions);
}
private void cleanActionData(ActionData actionData) {
actionData.getActionChain().clear();
actionData.setExecutingStage(0);
actionData.setStatus(ActionData.ActionStatus.PREPARE);
actionData.getHistory().clear();
} }
/** /**
@@ -352,10 +148,9 @@ public class ActionCore extends PartnerCore<ActionCore> {
* @param inputVector 本次输入内容的语义向量 * @param inputVector 本次输入内容的语义向量
* @param vectorClient 向量客户端 * @param vectorClient 向量客户端
*/ */
private void adjustMatchAndPassed(List<CacheAdjustMetaData> matchAndPassed, float[] inputVector, String input, private void adjustMatchAndPassed(List<CacheAdjustMetaData> matchAndPassed, float[] inputVector, String input, VectorClient vectorClient) {
VectorClient vectorClient) {
matchAndPassed.forEach(adjustData -> { matchAndPassed.forEach(adjustData -> {
// 获取原始缓存条目 //获取原始缓存条目
String tendency = adjustData.getTendency(); String tendency = adjustData.getTendency();
ActionCacheData primaryCacheData = selectCacheData(tendency); ActionCacheData primaryCacheData = selectCacheData(tendency);
if (primaryCacheData == null) { if (primaryCacheData == null) {
@@ -374,7 +169,7 @@ public class ActionCore extends PartnerCore<ActionCore> {
private void adjustMatchNotPassed(List<CacheAdjustMetaData> matchNotPassed, VectorClient vectorClient) { private void adjustMatchNotPassed(List<CacheAdjustMetaData> matchNotPassed, VectorClient vectorClient) {
List<ActionCacheData> toRemove = new ArrayList<>(); List<ActionCacheData> toRemove = new ArrayList<>();
matchNotPassed.forEach(adjustData -> { matchNotPassed.forEach(adjustData -> {
// 获取原始缓存条目 //获取原始缓存条目
String tendency = adjustData.getTendency(); String tendency = adjustData.getTendency();
ActionCacheData primaryCacheData = selectCacheData(tendency); ActionCacheData primaryCacheData = selectCacheData(tendency);
if (primaryCacheData == null) { if (primaryCacheData == null) {
@@ -409,10 +204,9 @@ public class ActionCore extends PartnerCore<ActionCore> {
* @param input 本次输入内容 * @param input 本次输入内容
* @param vectorClient 向量客户端 * @param vectorClient 向量客户端
*/ */
private void adjustNotMatchPassed(List<CacheAdjustMetaData> notMatchPassed, float[] inputVector, String input, private void adjustNotMatchPassed(List<CacheAdjustMetaData> notMatchPassed, float[] inputVector, String input, VectorClient vectorClient) {
VectorClient vectorClient) {
notMatchPassed.forEach(adjustData -> { notMatchPassed.forEach(adjustData -> {
// 获取原始缓存条目 //获取原始缓存条目
String tendency = adjustData.getTendency(); String tendency = adjustData.getTendency();
ActionCacheData primaryCacheData = selectCacheData(tendency); ActionCacheData primaryCacheData = selectCacheData(tendency);
float[] tendencyVector = vectorClient.compute(tendency); float[] tendencyVector = vectorClient.compute(tendency);
@@ -438,9 +232,4 @@ public class ActionCore extends PartnerCore<ActionCore> {
protected String getCoreKey() { protected String getCoreKey() {
return "action-core"; return "action-core";
} }
public enum ExecutorType {
VIRTUAL, PLATFORM
}
} }

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.action.entity.cache; package work.slhaf.partner.core.action.entity;
import lombok.Data; import lombok.Data;
import work.slhaf.partner.common.vector.VectorClient; import work.slhaf.partner.common.vector.VectorClient;

View File

@@ -0,0 +1,11 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
@Data
public class ActionData {
private String key;
private String[] array;
private String reason;
private String description;
}

View File

@@ -1,143 +0,0 @@
package work.slhaf.partner.core.action.entity
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction
import java.time.ZonedDateTime
import java.util.*
/**
* 行动模块传递的行动数据包含行动uuid、倾向、状态、行动链、结果、发起原因、行动描述等信息。
*/
sealed class ActionData {
/**
* 行动ID
*/
val uuid: String = UUID.randomUUID().toString()
/**
* 行动倾向
*/
abstract val tendency: String
/**
* 行动状态
*/
var status: ActionStatus = ActionStatus.PREPARE
/**
* 行动链
*/
abstract val actionChain: MutableMap<Int, MutableList<MetaAction>>
/**
* 行动阶段(当前阶段)
*/
var executingStage: Int = 0
/**
* 行动结果
*/
lateinit var result: String
val history: MutableMap<Int, MutableList<HistoryAction>> = mutableMapOf()
/**
* 修复上下文
*/
val additionalContext: MutableMap<Int, MutableList<String>> = mutableMapOf()
/**
* 行动原因
*/
abstract val reason: String
/**
* 行动描述
*/
abstract val description: String
/**
* 行动来源
*/
abstract val source: String
enum class ActionStatus {
/**
* 执行成功
*/
SUCCESS,
/**
* 执行失败
*/
FAILED,
/**
* 执行中
*/
EXECUTING,
/**
* 暂时中断
*/
INTERRUPTED,
/**
* 预备执行
*/
PREPARE
}
}
/**
* 计划行动数据类,继承自{@link ActionData},扩展了属性{@link ScheduledActionData#type}和{@link ScheduledActionData#scheduleContent},用于标识计划类型(单次还是周期性任务)和计划内容
*/
data class ScheduledActionData(
override val tendency: String,
override val actionChain: MutableMap<Int, MutableList<MetaAction>>,
override val reason: String,
override val description: String,
override val source: String,
val scheduleType: ScheduleType,
val scheduleContent: String,
) : ActionData() {
val scheduleHistories = ArrayList<ScheduleHistory>()
fun recordAndReset() {
val newHistory = ScheduleHistory(ZonedDateTime.now(), result, history.toMap())
scheduleHistories.add(newHistory)
additionalContext.clear()
executingStage = 0
for (entry in actionChain) {
for (action in entry.value) {
action.params.clear()
action.result.reset()
}
}
status = ActionStatus.PREPARE
}
enum class ScheduleType {
CYCLE,
ONCE
}
data class ScheduleHistory(
val endTime: ZonedDateTime,
val result: String,
val history: Map<Int, List<HistoryAction>>
)
}
/**
* 即时行动数据类
*/
data class ImmediateActionData(
override val tendency: String,
override val actionChain: MutableMap<Int, MutableList<MetaAction>>,
override val reason: String,
override val description: String,
override val source: String,
) : ActionData()

View File

@@ -1,10 +0,0 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
@Data
public class ActionFileMetaData {
private String content;
private String name;
private String ext;
}

View File

@@ -0,0 +1,5 @@
package work.slhaf.partner.core.action.entity;
public enum ActionStatus {
SUCCESS, FAILED, EXECUTING, WAITING, PREPARE
}

View File

@@ -1,5 +1,5 @@
package work.slhaf.partner.core.action.entity; package work.slhaf.partner.core.action.entity;
public class McpData { public enum ActionType {
IMMEDIATE, PLANNING
} }

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.action.entity.cache; package work.slhaf.partner.core.action.entity;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.action.entity.cache; package work.slhaf.partner.core.action.entity;
import lombok.Data; import lombok.Data;

View File

@@ -1,15 +0,0 @@
package work.slhaf.partner.core.action.entity;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import java.util.List;
@Data
public class GeneratedData {
private List<String> dependencies;
private String code;
private String codeType;
private boolean serialize;
private JSONObject responseSchema;
}

View File

@@ -0,0 +1,9 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ImmediateActionInfo extends MetaActionInfo{
}

View File

@@ -1,73 +0,0 @@
package work.slhaf.partner.core.action.entity
/**
* 行动链中的单一元素,封装了调用外部行动程序的必要信息与结果容器,可被[work.slhaf.partner.core.action.ActionCapability]执行
*/
data class MetaAction(
/**
* 行动name用于标识行动程序
*/
val name: String,
/**
* 是否IO密集用于决定使用何种线程池
*/
val io: Boolean = false,
/**
* 行动程序类型,可分为 MCP、ORIGIN 两种,前者对应读取到的 MCP Tool、后者对应生成的临时行动程序
*/
val type: Type,
/**
* 当类型为 MCP 时,该字段对应相应 MCP Client 注册时生成的 id;
* 当类型为 ORIGIN 时,该字段对应相应的磁盘路径字符串
*/
val location: String,
) {
/**
* 行动程序可接受的参数,由调用处设置
*/
val params: MutableMap<String, Any> = mutableMapOf()
/**
* 行动结果,包括执行状态和相应内容(执行结果或者错误信息)
*/
val result = Result()
val key: String
/**
* actionKey 将由 location+name 共同定位
*
* @return actionKey
*/
get() = "$location::$name"
class Result {
var status = Status.WAITING
var data: String? = null
fun reset() {
status = Status.WAITING
data = null
}
enum class Status {
SUCCESS,
FAILED,
WAITING
}
}
enum class Type {
/**
* 将调用的 MCP 工具,可包括远程、本地任意服务
*/
MCP,
/**
* 适用于‘临时生成’的行动程序,在生成后根据序列化选项及执行情况,进行持久化
*/
ORIGIN
}
}

View File

@@ -1,25 +1,12 @@
package work.slhaf.partner.core.action.entity; package work.slhaf.partner.core.action.entity;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data; import lombok.Data;
import java.util.List;
import java.util.Map;
@Data @Data
public class MetaActionInfo { public abstract class MetaActionInfo {
private boolean io; protected String uuid;
protected String tendency;
private Map<String, Object> params; protected ActionStatus status;
private String description; protected ActionData actionData;
private List<String> tags; protected String Result;
private List<String> preActions;
private List<String> postActions;
/**
* 是否严格依赖前置行动的成功执行若为true且前置行动失败则不执行该行动后置任务多为触发式。默认即执行。
*/
private boolean strictDependencies;
private JSONObject responseSchema;
} }

View File

@@ -1,33 +0,0 @@
package work.slhaf.partner.core.action.entity;
import work.slhaf.partner.core.action.entity.ActionData.ActionStatus;
import java.util.concurrent.Phaser;
public record PhaserRecord(Phaser phaser, ActionData actionData) {
public void fail() {
actionData.setStatus(ActionStatus.FAILED);
}
/**
* 负责将 ActionData 的状态设置为 INTERRUPTED
* 同时循环检查进行阻塞
*/
public void interrupt() {
actionData.setStatus(ActionStatus.INTERRUPTED);
while (actionData().getStatus() == ActionStatus.INTERRUPTED) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
}
}
/**
* 将状态重新设置为 EXECUTING ,恢复 interrupt 阻塞状态
*/
public void complete() {
actionData().setStatus(ActionStatus.EXECUTING);
}
}

View File

@@ -0,0 +1,15 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ScheduledActionInfo extends MetaActionInfo {
private ScheduledType type;
private String scheduleContent; //如果为周期则对应cron表达式如果为一次性则对应为LocalDateTime字符串
enum ScheduledType {
CYCLE, ONCE
}
}

View File

@@ -1,13 +0,0 @@
package work.slhaf.partner.core.action.exception;
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
public class ActionDataNotFoundException extends AgentRuntimeException {
public ActionDataNotFoundException(String message) {
super(message);
}
public ActionDataNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,13 +0,0 @@
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);
}
}

View File

@@ -1,13 +0,0 @@
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);
}
}

View File

@@ -1,13 +0,0 @@
package work.slhaf.partner.core.action.exception;
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
public class ActionSerializeFailedException extends AgentRuntimeException {
public ActionSerializeFailedException(String message) {
super(message);
}
public ActionSerializeFailedException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,13 +0,0 @@
package work.slhaf.partner.core.action.exception;
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
public class MetaActionNotFoundException extends AgentRuntimeException {
public MetaActionNotFoundException(String message) {
super(message);
}
public MetaActionNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,107 +0,0 @@
package work.slhaf.partner.core.action.runner;
import com.alibaba.fastjson2.JSONObject;
import io.modelcontextprotocol.server.McpStatelessAsyncServer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.jetbrains.annotations.Nullable;
import work.slhaf.partner.core.action.entity.ActionFileMetaData;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaAction.Result;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.exception.ActionInitFailedException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import static work.slhaf.partner.common.Constant.Path.DATA;
import static work.slhaf.partner.common.util.PathUtil.buildPathStr;
/**
* 执行客户端抽象类
* <br/>
* 只负责暴露序列化、执行等相应接口,具体逻辑交给下游实现
* <br/>
* 默认存在两类实现,{@link LocalRunnerClient} 和 {@link SandboxRunnerClient}
* <ol>
* LocalRunnerClient:
* <li>
* 对应本地运行环境,可在本地启动 MCP 客户端将 RunnerClient 暴露的能力接口转发至本地 MCP Client 并执行
* </li>
* SandboxRunnerClient:
* <li>
* 对应沙盒运行环境,该 Client 仅作为沙盒环境的客户端,不持有额外能力,仅保持远端连接已存在行动的内容更新
* </li>
* </ol>
*/
@Slf4j
public abstract class RunnerClient {
protected final String ACTION_PATH;
protected final ConcurrentHashMap<String, MetaActionInfo> existedMetaActions;
protected final ExecutorService executor;
//TODO 仍可提供内部 MCP但调用方式需要结合 AgentContext来获取否则生命周期不合
protected McpStatelessAsyncServer innerMcpServer;
/**
* ActionCore 将注入虚拟线程池
*/
public RunnerClient(ConcurrentHashMap<String, MetaActionInfo> existedMetaActions, ExecutorService executor, @Nullable String baseActionPath) {
this.existedMetaActions = existedMetaActions;
this.executor = executor;
baseActionPath = baseActionPath == null ? DATA : baseActionPath;
this.ACTION_PATH = buildPathStr(baseActionPath, "action");
createPath(ACTION_PATH);
}
/**
* 执行行动程序
*/
public void submit(MetaAction metaAction) {
// 获取已存在行动列表
Result result = metaAction.getResult();
if (!result.getStatus().equals(Result.Status.WAITING)) {
return;
}
RunnerResponse response = doRun(metaAction);
result.setData(response.getData());
result.setStatus(response.isOk() ? Result.Status.SUCCESS : Result.Status.FAILED);
}
protected abstract RunnerResponse doRun(MetaAction metaAction);
public abstract String buildTmpPath(String actionKey, String codeType);
public abstract void tmpSerialize(MetaAction tempAction, String code, String codeType) throws IOException;
public abstract void persistSerialize(MetaActionInfo metaActionInfo, ActionFileMetaData fileMetaData);
protected void createPath(String pathStr) {
val path = Path.of(pathStr);
try {
Files.createDirectory(path);
} catch (IOException e) {
if (!Files.exists(path)) {
throw new ActionInitFailedException("目录创建失败: " + pathStr, e);
}
}
}
/**
* 列出执行环境下的系统依赖情况
*/
public abstract JSONObject listSysDependencies();
@Data
public static class RunnerResponse {
private boolean ok;
private String data;
}
}

View File

@@ -1,57 +0,0 @@
package work.slhaf.partner.core.action.runner;
import com.alibaba.fastjson2.JSONObject;
import work.slhaf.partner.core.action.entity.ActionFileMetaData;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
/**
* 基于 Http 与 WebSocket 的沙盒执行器客户端,负责:
* <ul>
* <li>
* 发送行动单元数据
* </li>
* <li>
* 实时更新获取已存在行动列表
* </li>
* <li>
* 向传入的 MetaAction 回写执行结果
* </li>
* </ul>
*/
public class SandboxRunnerClient extends RunnerClient {
public SandboxRunnerClient(ConcurrentHashMap<String, MetaActionInfo> existedMetaActions, ExecutorService executor) { // 连接沙盒执行器(websocket)
super(existedMetaActions, executor, null);
}
protected RunnerResponse doRun(MetaAction metaAction) {
// 调用沙盒执行器
return null;
}
@Override
public JSONObject listSysDependencies() {
return null;
}
@Override
public String buildTmpPath(String actionKey, String codeType) {
throw new UnsupportedOperationException("Unimplemented method 'buildTmpPath'");
}
@Override
public void tmpSerialize(MetaAction tempAction, String code, String codeType) throws IOException {
throw new UnsupportedOperationException("Unimplemented method 'tmpSerialize'");
}
@Override
public void persistSerialize(MetaActionInfo metaActionInfo, ActionFileMetaData fileMetaData) {
throw new UnsupportedOperationException("Unimplemented method 'persistSerialize'");
}
}

View File

@@ -13,6 +13,7 @@ import java.util.concurrent.locks.Lock;
public interface CognationCapability { public interface CognationCapability {
List<Message> getChatMessages(); List<Message> getChatMessages();
void setChatMessages(List<Message> chatMessages);
void cleanMessage(List<Message> messages); void cleanMessage(List<Message> messages);
Lock getMessageLock(); Lock getMessageLock();
void addMetaMessage(String userId, MetaMessage metaMessage); void addMetaMessage(String userId, MetaMessage metaMessage);

View File

@@ -63,6 +63,11 @@ public class CognationCore extends PartnerCore<CognationCore> {
return currentMemoryId; return currentMemoryId;
} }
@CapabilityMethod
public void setChatMessages(List<Message> chatMessages) {
this.chatMessages = chatMessages;
}
@CapabilityMethod @CapabilityMethod
public void cleanMessage(List<Message> messages) { public void cleanMessage(List<Message> messages) {
messageLock.lock(); messageLock.lock();

View File

@@ -198,9 +198,9 @@ public class MemoryCore extends PartnerCore<MemoryCore> {
//尝试更新缓存 //尝试更新缓存
updateCache(topicPath, memoryResult); updateCache(topicPath, memoryResult);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] selectMemory error: ", getCoreKey(), e); log.error("[CoordinatedManager] selectMemory error: ", e);
log.error("[{}] 路径: {}", getCoreKey(), topicPathStr); log.error("[CoordinatedManager] 路径: {}", topicPathStr);
log.error("[{}] 主题树: {}", getCoreKey(), getTopicTree()); log.error("[CoordinatedManager] 主题树: {}", getTopicTree());
memoryResult = new MemoryResult(); memoryResult = new MemoryResult();
memoryResult.setRelatedMemorySliceResult(new ArrayList<>()); memoryResult.setRelatedMemorySliceResult(new ArrayList<>());
memoryResult.setMemorySliceResult(new CopyOnWriteArrayList<>()); memoryResult.setMemorySliceResult(new CopyOnWriteArrayList<>());
@@ -211,7 +211,7 @@ public class MemoryCore extends PartnerCore<MemoryCore> {
@CapabilityMethod @CapabilityMethod
public void updateActivatedSlices(String userId, List<EvaluatedSlice> memorySlices) { public void updateActivatedSlices(String userId, List<EvaluatedSlice> memorySlices) {
cache.activatedSlices.put(userId, memorySlices); cache.activatedSlices.put(userId, memorySlices);
log.debug("[{}] 已更新激活切片, userId: {}", getCoreKey(), userId); log.debug("[CoordinatedManager] 已更新激活切片, userId: {}", userId);
} }
@CapabilityMethod @CapabilityMethod
@@ -488,7 +488,7 @@ public class MemoryCore extends PartnerCore<MemoryCore> {
return targetParentNode; return targetParentNode;
} }
private void updateCacheCounter(List<String> topicPath) { public void updateCacheCounter(List<String> topicPath) {
ConcurrentHashMap<List<String>, Integer> memoryNodeCacheCounter = cache.memoryNodeCacheCounter; ConcurrentHashMap<List<String>, Integer> memoryNodeCacheCounter = cache.memoryNodeCacheCounter;
if (memoryNodeCacheCounter.containsKey(topicPath)) { if (memoryNodeCacheCounter.containsKey(topicPath)) {
Integer tempCount = memoryNodeCacheCounter.get(topicPath); Integer tempCount = memoryNodeCacheCounter.get(topicPath);

View File

@@ -5,7 +5,7 @@ import lombok.EqualsAndHashCode;
import work.slhaf.partner.api.common.entity.PersistableObject; import work.slhaf.partner.api.common.entity.PersistableObject;
import java.io.Serial; import java.io.Serial;
import java.util.Map; import java.util.HashMap;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@@ -15,5 +15,5 @@ public class AppendPromptData extends PersistableObject {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String moduleName; private String moduleName;
private Map<String, String> appendedPrompt; private HashMap<String,String> appendedPrompt;
} }

View File

@@ -8,7 +8,7 @@ public abstract class PostRunningModule extends AgentRunningModule<PartnerRunnin
@Override @Override
public final void execute(PartnerRunningFlowContext context) { public final void execute(PartnerRunningFlowContext context) {
boolean trigger = context.getModuleContext().getExtraContext().getBoolean("post_process_trigger"); boolean trigger = context.getModuleContext().getExtraContext().getBoolean("post_process_trigger");
if (!trigger && relyOnMessage()) { if (!trigger) {
return; return;
} }
doExecute(context); doExecute(context);
@@ -16,5 +16,4 @@ public abstract class PostRunningModule extends AgentRunningModule<PartnerRunnin
public abstract void doExecute(PartnerRunningFlowContext context); public abstract void doExecute(PartnerRunningFlowContext context);
protected abstract boolean relyOnMessage();
} }

View File

@@ -5,7 +5,7 @@ import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunn
import work.slhaf.partner.module.common.entity.AppendPromptData; import work.slhaf.partner.module.common.entity.AppendPromptData;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext; import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.util.Map; import java.util.HashMap;
/** /**
* 前置模块抽象类 * 前置模块抽象类
@@ -15,7 +15,7 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
private synchronized void setAppendedPrompt(PartnerRunningFlowContext context) { private synchronized void setAppendedPrompt(PartnerRunningFlowContext context) {
AppendPromptData data = new AppendPromptData(); AppendPromptData data = new AppendPromptData();
data.setModuleName(moduleName()); data.setModuleName(moduleName());
Map<String, String> map = getPromptDataMap(context); HashMap<String, String> map = getPromptDataMap(context);
data.setAppendedPrompt(map); data.setAppendedPrompt(map);
context.setAppendedPrompt(data); context.setAppendedPrompt(data);
} }
@@ -24,7 +24,7 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
context.getCoreContext().addActiveModule(moduleName()); context.getCoreContext().addActiveModule(moduleName());
} }
protected abstract Map<String, String> getPromptDataMap(PartnerRunningFlowContext context); protected abstract HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context);
/** /**
* 用于在CoreModule接收到的模块Prompt中标识模块名称 * 用于在CoreModule接收到的模块Prompt中标识模块名称
@@ -40,4 +40,5 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
protected abstract void doExecute(PartnerRunningFlowContext context); protected abstract void doExecute(PartnerRunningFlowContext context);
} }

View File

@@ -1,70 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher;
import lombok.val;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
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.executor.entity.ActionExecutorInput;
import work.slhaf.partner.module.modules.action.dispatcher.scheduler.ActionScheduler;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@AgentModule(name = "action_dispatcher", order = 7)
public class ActionDispatcher extends PostRunningModule {
@InjectCapability
private ActionCapability actionCapability;
@InjectModule
private ActionExecutor actionExecutor;
@InjectModule
private ActionScheduler actionScheduler;
private ExecutorService executor;
@Init
public void init() {
executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
}
@Override
public void doExecute(PartnerRunningFlowContext context) {
// 只需要处理prepared action因为pending action在用户确认后也将变为prepared action
// 将PLANNING action放入时间轮中IMMEDIATE action直接进入并发执行流
// 对于将触发的PLANNING
// action理想做法是将执行工具做成执行链的形式模型的自对话流程、是否通知用户都做成与普通工具同等的通用可选能力避免绑定固定流程
executor.execute(() -> {
String userId = context.getUserId();
val preparedActions = actionCapability.listActions(ActionData.ActionStatus.PREPARE, userId);
// 分类成PLANNING和IMMEDIATE两类
Set<ScheduledActionData> scheduledActions = new HashSet<>();
Set<ImmediateActionData> immediateActions = new HashSet<>();
for (ActionData preparedAction : preparedActions) {
if (preparedAction instanceof ScheduledActionData actionInfo) {
scheduledActions.add(actionInfo);
} else if (preparedAction instanceof ImmediateActionData actionInfo) {
immediateActions.add(actionInfo);
}
}
actionExecutor.execute(new ActionExecutorInput(immediateActions));
actionScheduler.execute(scheduledActions);
});
}
@Override
protected boolean relyOnMessage() {
return false;
}
}

View File

@@ -1,52 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import com.alibaba.fastjson2.JSONObject;
import lombok.val;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.CorrectorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.CorrectorResult;
/**
* 负责在单组行动执行后,根据行动意图与结果检查后续行动是否符合目的,必要时直接调整行动链,或发起自对话请求进行干预
*/
@AgentSubModule
public class ActionCorrector extends AgentRunningSubModule<CorrectorInput, CorrectorResult> implements ActivateModel {
@Override
public CorrectorResult execute(CorrectorInput input) {
val prompt = buildPrompt(input);
val chatResponse = singleChat(prompt);
return JSONObject.parseObject(chatResponse.getMessage(), CorrectorResult.class);
}
private String buildPrompt(CorrectorInput input) {
val prompt = new JSONObject();
prompt.put("[行动来源]", input.getSource());
prompt.put("[行动倾向]", input.getTendency());
prompt.put("[行动描述]", input.getDescription());
prompt.put("[行动原因]", input.getReason());
val messages = prompt.putArray("[近期对话]");
messages.addAll(input.getRecentMessages());
val memory = prompt.putArray("[已激活记忆]");
memory.addAll(input.getActivatedSlices());
val history = prompt.putArray("[已执行情况]");
history.addAll(input.getHistory());
return prompt.toJSONString();
}
@Override
public String modelKey() {
return "action_corrector";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

@@ -1,322 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.ActionData.ActionStatus;
import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.*;
import work.slhaf.partner.module.modules.action.dispatcher.scheduler.ActionScheduler;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@AgentSubModule
public class ActionExecutor extends AgentRunningSubModule<ActionExecutorInput, Void> {
@InjectCapability
private ActionCapability actionCapability;
@InjectCapability
private MemoryCapability memoryCapability;
@InjectCapability
private CognationCapability cognationCapability;
@InjectModule
private ParamsExtractor paramsExtractor;
@InjectModule
private ActionRepairer actionRepairer;
@InjectModule
private ActionCorrector actionCorrector;
@InjectModule
private ActionScheduler actionScheduler;
private ExecutorService virtualExecutor;
private ExecutorService platformExecutor;
private RunnerClient runnerClient;
private final AssemblyHelper assemblyHelper = new AssemblyHelper();
@Init
public void init() {
virtualExecutor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
platformExecutor = actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM);
runnerClient = actionCapability.runnerClient();
}
/**
* 执行行动
*
* @param input ActionExecutor 输入内容
* @return 无返回,执行结果回写至 input 内部携带的 actionData 中
*/
@Override
public Void execute(ActionExecutorInput input) {
val actions = input.getActions();
// 异步执行所有行动
for (ActionData actionData : actions) {
platformExecutor.execute(() -> {
val source = actionData.getSource();
if (actionData.getStatus() != ActionStatus.PREPARE) {
return;
}
val actionChain = actionData.getActionChain();
if (actionChain.isEmpty()) {
actionData.setStatus(ActionStatus.FAILED);
actionData.setResult("行动链为空");
return;
}
// 注册执行中行动
val phaser = new Phaser();
val phaserRecord = actionCapability.putPhaserRecord(phaser, actionData);
actionData.setStatus(ActionStatus.EXECUTING);
// 开始执行
val stageCursor = new Object() {
int stageCount;
boolean executingStageUpdated;
boolean stageCountUpdated;
void init() {
stageCount = 0;
executingStageUpdated = false;
stageCountUpdated = false;
update();
}
void requestAdvance() {
if (!stageCountUpdated) {
stageCount++;
stageCountUpdated = true;
}
if (stageCount < actionChain.size() && !executingStageUpdated) {
update();
executingStageUpdated = true;
}
}
boolean next() {
executingStageUpdated = false;
stageCountUpdated = false;
return stageCount < actionChain.size();
}
void update() {
val orderList = new ArrayList<>(actionChain.keySet());
orderList.sort(Integer::compareTo);
actionData.setExecutingStage(orderList.get(stageCount));
}
};
stageCursor.init();
do {
val metaActions = actionChain.get(actionData.getExecutingStage());
val listeningRecord = executeAndListening(metaActions, phaserRecord, source);
phaser.awaitAdvance(listeningRecord.phase());
// synchronized 同步防止 accepting 循环间、phase guard 判定后发生 stage 推进
// 导致新行动的 phaser 投放阶段错乱无法阻塞的场景
// 该 synchronized 将阶段推进与 accepting 监听 loop 捆绑为互斥的原子事件,避免了细粒度的 phaser 阶段竞态问题
synchronized (listeningRecord.accepting()) {
listeningRecord.accepting().set(false);
// 立即尝试推进,本次推进中,如果前方仍有未执行 stage将执行一次阶段推进
stageCursor.requestAdvance();
}
try {
// 针对行动链进行修正,修正需要传入执行历史、行动目标等内容
// 如果后续运行 corrector 触发频率较高,可考虑增加重试机制
val correctorInput = assemblyHelper.buildCorrectorInput(actionData, source);
val correctorResult = actionCorrector.execute(correctorInput);
actionCapability.handleInterventions(correctorResult.getMetaInterventionList(), actionData);
} catch (Exception ignored) {
}
// 第二次尝试进行阶段推进,本次负责补充上一次在不存在 stage时但 corrector 执行期间发生了 actionChain 的插入事件
// 如果第一次已经推进完毕,本次将会跳过
stageCursor.requestAdvance();
} while (stageCursor.next());
// 结束
actionCapability.removePhaserRecord(phaser);
if (actionData.getStatus() != ActionStatus.FAILED) {
// 如果是 ScheduledActionData, 则重置 ActionData 内容,记录执行历史与最终结果
if (actionData instanceof ScheduledActionData scheduledActionData) {
scheduledActionData.recordAndReset();
actionScheduler.execute(Set.of(scheduledActionData));
} else {
actionData.setStatus(ActionStatus.SUCCESS);
}
}
});
}
return null;
}
private MetaActionsListeningRecord executeAndListening(List<MetaAction> metaActions, PhaserRecord phaserRecord, String source) {
AtomicBoolean accepting = new AtomicBoolean(true);
AtomicInteger cursor = new AtomicInteger();
CountDownLatch latch = new CountDownLatch(1);
val phaser = phaserRecord.phaser();
val phase = phaser.register();
platformExecutor.execute(() -> {
boolean first = true;
while (accepting.get()) {
synchronized (accepting) {
MetaAction next = null;
synchronized (metaActions) {
if (cursor.get() < metaActions.size()) {
next = metaActions.get(cursor.getAndIncrement());
}
}
if (next == null) {
Thread.onSpinWait();
continue;
}
if (phaser.getPhase() != phase) {
metaActions.remove(next);
log.warn("行动阶段已推进,丢弃该行动: {}", next);
continue;
}
ExecutorService executor = next.getIo() ? virtualExecutor : platformExecutor;
executor.execute(buildMataActionTask(next, phaserRecord, source));
if (first) {
phaser.arriveAndDeregister();
latch.countDown();
first = false;
}
}
}
});
try {
// 确保执行一次,防止没来得及注册任务就已经结束
latch.await();
} catch (InterruptedException ignored) {
}
return new MetaActionsListeningRecord(accepting, phase);
}
private Runnable buildMataActionTask(MetaAction metaAction, PhaserRecord phaserRecord, String source) {
val phaser = phaserRecord.phaser();
phaser.register();
return () -> {
val actionKey = metaAction.getKey();
try {
val result = metaAction.getResult();
do {
val actionData = phaserRecord.actionData();
val executingStage = actionData.getExecutingStage();
val historyActionResults = actionData.getHistory().get(executingStage);
val additionalContext = actionData.getAdditionalContext().get(executingStage);
val extractorInput = assemblyHelper.buildExtractorInput(metaAction, source, historyActionResults, additionalContext);
val extractorResult = paramsExtractor.execute(extractorInput);
if (extractorResult.isOk()) {
metaAction.getParams().putAll(extractorResult.getParams());
runnerClient.submit(metaAction);
val historyAction = new HistoryAction(actionKey, actionCapability.loadMetaActionInfo(actionKey).getDescription(), metaAction.getResult().getData());
actionData.getHistory()
.computeIfAbsent(executingStage, integer -> new ArrayList<>())
.add(historyAction);
} else {
val repairerInput = assemblyHelper.buildRepairerInput(historyActionResults, metaAction, source);
val repairerResult = actionRepairer.execute(repairerInput);
switch (repairerResult.getStatus()) {
// 如果本次修复被认为成功,则将补充的信息添加至 additionalContext
case RepairerResult.RepairerStatus.OK -> {
additionalContext.addAll(repairerResult.getFixedData());
result.setStatus(MetaAction.Result.Status.WAITING);
}
// 此处的修复失败来自系统内部的执行失败:其余方式均不可行时将回退至当前分支
case RepairerResult.RepairerStatus.FAILED -> {
result.setStatus(MetaAction.Result.Status.FAILED);
result.setData("行动执行失败");
}
// 此处对应已在 repairer 内发起外部请求,故在此处进行阻塞
case RepairerResult.RepairerStatus.ACQUIRE -> {
phaserRecord.interrupt();
result.setStatus(MetaAction.Result.Status.WAITING);
}
}
}
} while (result.getStatus().equals(MetaAction.Result.Status.WAITING));
} catch (Exception e) {
log.error("Action executing failed: {}", actionKey, e);
} finally {
phaser.arriveAndDeregister();
}
};
}
private record MetaActionsListeningRecord(AtomicBoolean accepting, int phase) {
}
@SuppressWarnings("InnerClassMayBeStatic")
private class AssemblyHelper {
private AssemblyHelper() {
}
private RepairerInput buildRepairerInput(List<HistoryAction> historyActionsResults, MetaAction action, String userId) {
RepairerInput input = new RepairerInput();
MetaActionInfo metaActionInfo = actionCapability.loadMetaActionInfo(action.getKey());
input.setHistoryActionResults(historyActionsResults);
input.setParams(metaActionInfo.getParams());
input.setRecentMessages(cognationCapability.getChatMessages());
input.setActionDescription(metaActionInfo.getDescription());
input.setUserId(userId);
return input;
}
private ExtractorInput buildExtractorInput(MetaAction action, String source, List<HistoryAction> historyActionResults,
List<String> additionalContext) {
ExtractorInput input = new ExtractorInput();
input.setEvaluatedSlices(memoryCapability.getActivatedSlices(source));
input.setRecentMessages(cognationCapability.getChatMessages());
input.setMetaActionInfo(actionCapability.loadMetaActionInfo(action.getKey()));
input.setHistoryActionResults(historyActionResults);
input.setAdditionalContext(additionalContext);
return input;
}
private CorrectorInput buildCorrectorInput(ActionData actionData, String source) {
return CorrectorInput.builder()
.tendency(actionData.getTendency())
.source(actionData.getSource())
.reason(actionData.getReason())
.description(actionData.getDescription())
.history(actionData.getHistory().get(actionData.getExecutingStage()))
.status(actionData.getStatus())
.recentMessages(cognationCapability.getChatMessages())
.activatedSlices(memoryCapability.getActivatedSlices(source))
.build();
}
}
}

View File

@@ -1,228 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore.ExecutorType;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaAction.Result;
import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorResult;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.RepairerInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.RepairerResult;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.RepairerResult.RepairerStatus;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 负责识别行动链的修复
* <ol>
* <li>
* 可通过协调 {@link DynamicActionGenerator} 生成新的行动单元并调用,获取所需的参数信息(必要时持久化);
* </li>
* <li>
* 也可以直接调用已存在的行动程序获取信息;
* </li>
* <li>
* 如果上述都无法满足,将发起自对话借助干预模块进行操作或者借助自对话通道向用户发起沟通请求,该请求的目的一般为行动程序生成/调用指导或者用户侧的信息补充,后续还需要再走一遍参数修复流程
* </li>
* </ol>
*/
@Slf4j
@AgentSubModule
public class ActionRepairer extends AgentRunningSubModule<RepairerInput, RepairerResult> implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
@InjectCapability
private CognationCapability cognationCapability;
@InjectModule
private DynamicActionGenerator dynamicActionGenerator;
private final AssemblyHelper assemblyHelper = new AssemblyHelper();
private RunnerClient runnerClient;
@Init
void init() {
runnerClient = actionCapability.runnerClient();
}
@Override
public RepairerResult execute(RepairerInput data) {
RepairerResult result;
try {
String prompt = assemblyHelper.buildPrompt(data, null);
ChatResponse response = this.singleChat(prompt);
RepairerData repairerData = JSONObject.parseObject(response.getMessage(), RepairerData.class);
result = switch (repairerData.getRepairerType()) {
case ACTION_GENERATION ->
handleActionGeneration(JSONObject.parseObject(repairerData.getData(), GeneratorInput.class));
case ACTION_INVOCATION -> handleActionInvocation(
JSONObject.parseObject(repairerData.getData(), new TypeReference<List<String>>() {
}));
case USER_INTERACTION -> handleUserInteraction(repairerData.getData());
};
if (!repairerData.getRepairerType().equals(RepairerType.USER_INTERACTION)
&& result.getStatus().equals(RepairerResult.RepairerStatus.FAILED)) {
log.warn("常规行动修复失败,将尝试自对话通道");
prompt = assemblyHelper.buildPrompt(data, "常规行动修复失败,请尝试通过自对话通道获取必要的信息以完成行动参数的修复");
response = this.singleChat(prompt);
repairerData = JSONObject.parseObject(response.getMessage(), RepairerData.class);
handleUserInteraction(repairerData.getData());
}
} catch (Exception e) {
result = new RepairerResult();
result.setStatus(RepairerStatus.FAILED);
}
return result;
}
/**
* 负责根据输入内容进行行动单元的参数信息修复
*
* @param generatorInput 生成的行动单元参考内容,最好包含行动单元的执行逻辑
* @return 修复后的行动单元结果
*/
private RepairerResult handleActionGeneration(GeneratorInput generatorInput) {
RepairerResult result = new RepairerResult();
GeneratorResult generatorResult = dynamicActionGenerator.execute(generatorInput);
MetaAction tempAction = generatorResult.getTempAction();
if (tempAction == null) {
result.setStatus(RepairerStatus.FAILED);
return result;
}
runnerClient.submit(tempAction);
// 根据 tempAction 的执行状态设置修复结果
Result actionResult = tempAction.getResult();
if (actionResult.getStatus() != MetaAction.Result.Status.SUCCESS) {
result.setStatus(RepairerStatus.FAILED);
return result;
}
result.setStatus(RepairerStatus.OK);
result.getFixedData().add(actionResult.getData());
return result;
}
/**
* 负责根据输入内容进行行动单元的参数信息修复
*
* @param actionKeys 需要调用的行动单元Key列表
* @return 修复后的行动单元结果
*/
private RepairerResult handleActionInvocation(List<String> actionKeys) {
RepairerResult result = new RepairerResult();
CountDownLatch latch = new CountDownLatch(actionKeys.size());
ExecutorService virtual = actionCapability.getExecutor(ExecutorType.VIRTUAL);
ExecutorService platform = actionCapability.getExecutor(ExecutorType.PLATFORM);
ExecutorService executor;
AtomicInteger failedCount = new AtomicInteger(0);
for (String key : actionKeys) {
MetaAction action = actionCapability.loadMetaAction(key);
executor = action.getIo() ? virtual : platform;
executor.execute(() -> {
try {
runnerClient.submit(action);
result.getFixedData().add(action.getResult().getData());
} catch (Exception e) {
log.error("行动单元执行失败: {}", key, e);
failedCount.incrementAndGet();
} finally {
latch.countDown();
}
});
}
try {
latch.await();
} catch (Exception e) {
log.warn("CountDownLatch 已中断");
}
if (actionKeys.size() - failedCount.get() > 0) {
result.setStatus(RepairerStatus.OK);
} else {
result.setStatus(RepairerStatus.FAILED);
}
return result;
}
private RepairerResult handleUserInteraction(String acquireContent) {
RepairerResult result = new RepairerResult();
result.setStatus(RepairerStatus.ACQUIRE);
// 发送自对话请求
return result;
}
@Override
public String modelKey() {
return "action_repairer";
}
@Override
public boolean withBasicPrompt() {
return false;
}
@SuppressWarnings("InnerClassMayBeStatic")
@Data
private class RepairerData {
private RepairerType repairerType;
private String data;
}
private enum RepairerType {
ACTION_GENERATION,
ACTION_INVOCATION,
USER_INTERACTION
}
@SuppressWarnings("InnerClassMayBeStatic")
private class AssemblyHelper {
private AssemblyHelper() {
}
private String buildPrompt(RepairerInput data, String specialInstruction) {
JSONObject prompt = new JSONObject();
JSONObject actionData = prompt.putObject("[本次行动信息]");
actionData.put("[行动描述]", data.getActionDescription());
JSONObject actionParamsData = actionData.putObject("[行动参数说明]");
actionParamsData.putAll(data.getParams());
JSONArray historyData = prompt.putArray("[历史行动执行结果]");
data.getHistoryActionResults().forEach(historyAction -> {
JSONObject historyItem = new JSONObject();
historyItem.put("[行动Key]", historyAction.actionKey());
historyItem.put("[行动描述]", historyAction.description());
historyItem.put("[行动结果]", historyAction.result());
historyData.add(historyItem);
});
JSONArray messageData = prompt.putArray("[最近消息列表]");
messageData.addAll(data.getRecentMessages());
if (specialInstruction != null) {
prompt.put("[特殊指令]", specialInstruction);
}
return prompt.toString();
}
}
}

View File

@@ -1,91 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import com.alibaba.fastjson2.JSONObject;
import lombok.val;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.common.util.ExtractUtil;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.entity.GeneratedData;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorResult;
/**
* 负责依据输入内容生成可执行的动态行动单元,并选择是否持久化至 SandboxRunner 容器内
*/
@AgentSubModule
public class DynamicActionGenerator extends AgentRunningSubModule<GeneratorInput, GeneratorResult>
implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
private RunnerClient runnerClient;
@Init
void init() {
runnerClient = actionCapability.runnerClient();
}
@Override
public GeneratorResult execute(GeneratorInput input) {
GeneratorResult result = new GeneratorResult();
try {
// 由于 SCRIPT 类型程序都是在 SandboxRunner 内部的磁盘上加载然后执行的,
// 所以此处的输入内容也只需要指定输入参数、临时key、是否持久化即可路径将按照指定规则统一构建不可交给LLM生成
String prompt = buildPrompt(input);
// 响应结果需要包含几个特殊数据: 依赖项、代码内容、是否序列化、响应数据释义
ChatResponse response = this.singleChat(prompt);
GeneratedData generatorData = JSONObject
.parseObject(ExtractUtil.extractJson(response.getMessage()), GeneratedData.class);
val location = runnerClient.buildTmpPath(input.getActionName(), generatorData.getCodeType());
MetaAction tempAction = new MetaAction(
input.getActionName(),
true,
MetaAction.Type.ORIGIN,
location
);
// 将临时行动单元序列化至临时文件夹,并设置程序路径、放置在队列中,等待执行状态变化,并根据序列化选项选择是否补充 MetaActionInfo 并持久序列化
// 通过 ActionCapability 暴露的接口序列化至临时文件夹同时返回Path对象并设置。队列建议交给 SandboxRunner
// 持有,包括监听与序列化线程
runnerClient.tmpSerialize(tempAction, generatorData.getCode(), generatorData.getCodeType());
if (generatorData.isSerialize()) {
waitingSerialize();
}
result.setTempAction(tempAction);
} catch (Exception e) {
result.setTempAction(null);
}
return result;
}
private void waitingSerialize() {
throw new UnsupportedOperationException("Unimplemented method 'waitingSerialize'");
}
private String buildPrompt(GeneratorInput data) {
JSONObject prompt = new JSONObject();
prompt.put("[行动描述]", data.getDescription());
// prompt.putObject("[行动参数]").putAll(data.getParams());
prompt.putObject("[行动参数描述]").putAll(data.getParamsDescription());
return prompt.toString();
}
@Override
public String modelKey() {
return "dynamic_generator";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

@@ -1,75 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.ExtractorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.ExtractorResult;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction;
import java.util.HashMap;
import java.util.List;
/**
* 负责依据输入内容进行行动单元的参数信息提取
*/
@Slf4j
@AgentSubModule
public class ParamsExtractor extends AgentRunningSubModule<ExtractorInput, ExtractorResult> implements ActivateModel {
@Override
public ExtractorResult execute(ExtractorInput input) {
String prompt = buildPrompt(input);
ChatResponse response = this.singleChat(prompt);
ExtractorResult result;
try {
result = JSONObject.parseObject(response.getMessage(), ExtractorResult.class);
} catch (Exception e) {
log.error("ParamsExtractor解析结果失败返回内容{}", response.getMessage(), e);
result = new ExtractorResult();
result.setOk(false);
result.setParams(new HashMap<>());
}
return result;
}
private String buildPrompt(ExtractorInput input) {
JSONObject prompt = new JSONObject();
JSONObject actionData = prompt.putObject("[本次行动信息]");
MetaActionInfo actionInfo = input.getMetaActionInfo();
actionData.put("[行动描述]", actionInfo.getDescription());
actionData.put("[行动参数说明]", actionInfo.getParams());
JSONArray historyData = prompt.putArray("[历史行动执行结果]");
List<HistoryAction> historyActions = input.getHistoryActionResults();
for (HistoryAction historyAction : historyActions) {
JSONObject historyItem = new JSONObject();
historyItem.put("[行动Key]", historyAction.actionKey());
historyItem.put("[行动描述]", historyAction.description());
historyItem.put("[行动结果]", historyAction.result());
historyData.add(historyItem);
}
JSONArray messageData = prompt.putArray("[最近消息列表]");
messageData.addAll(input.getRecentMessages());
return prompt.toString();
}
@Override
public String modelKey() {
return "params_extractor";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

@@ -1,5 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity
import work.slhaf.partner.core.action.entity.ActionData
data class ActionExecutorInput(val actions: Set<ActionData>)

View File

@@ -1,24 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Builder;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import java.util.List;
@Data
@Builder
public class CorrectorInput {
private String tendency;
private String source;
private String reason;
private String description;
private List<HistoryAction> history;
private ActionData.ActionStatus status;
private List<Message> recentMessages;
private List<EvaluatedSlice> activatedSlices;
}

View File

@@ -1,11 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
import java.util.List;
@Data
public class CorrectorResult {
private List<MetaIntervention> metaInterventionList;
}

View File

@@ -1,32 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import java.util.List;
@Data
public class ExtractorInput {
/**
* 目标 MetaActionInfo
*/
private MetaActionInfo metaActionInfo;
/**
* 可参考的记忆切片
*/
private List<EvaluatedSlice> evaluatedSlices;
/**
* 历史行动执行结果
*/
private List<HistoryAction> historyActionResults;
/**
* 最近的消息列表
*/
private List<Message> recentMessages;
/**
* 额外的上下文信息(可来自修复器等)
*/
private List<String> additionalContext;
}

View File

@@ -1,11 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import java.util.Map;
@Data
public class ExtractorResult {
private boolean ok;
private Map<String, Object> params;
}

View File

@@ -1,13 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import java.util.Map;
@Data
public class GeneratorInput {
private String actionName;
private Map<String, Object> params;
private String description;
private Map<String, String> paramsDescription;
}

View File

@@ -1,9 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import work.slhaf.partner.core.action.entity.MetaAction;
@Data
public class GeneratorResult {
private MetaAction tempAction;
}

View File

@@ -1,4 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
public record HistoryAction(String actionKey, String description, String result) {
}

View File

@@ -1,17 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
import java.util.List;
import java.util.Map;
@Data
public class RepairerInput {
private String userId;
private List<Message> recentMessages;
private Map<String, Object> params;
private String actionDescription;
private List<HistoryAction> historyActionResults;
}

View File

@@ -1,30 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import lombok.Data;
import java.util.List;
/**
* 行动修复结果,包含行动状态和修复后的参数
*/
@Data
public class RepairerResult {
private RepairerStatus status;
private List<String> fixedData;
public enum RepairerStatus {
/**
* 成功修复: 携带修复后参数; 此种情况对应 Repairer 通过某种方式获取到了完整的参数(调用额外的行动)
*/
OK,
/**
* 发送了自对话请求干预行动,这类一般是补充信息或者提供行动指导,后续必须再步入修复进程,但需要设置层级
*/
ACQUIRE,
/**
* 修复失败(简单修复、自对话通道均出现错误,正常情况不应该出现)
*/
FAILED
}
}

View File

@@ -1,14 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.exception;
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
public class ActionExecutingFailedException extends AgentRuntimeException {
public ActionExecutingFailedException(String message) {
super(message);
}
public ActionExecutingFailedException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,400 +0,0 @@
package work.slhaf.partner.module.modules.action.dispatcher.scheduler
import com.cronutils.model.CronType
import com.cronutils.model.definition.CronDefinition
import com.cronutils.model.definition.CronDefinitionBuilder
import com.cronutils.model.time.ExecutionTime
import com.cronutils.parser.CronParser
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.slf4j.LoggerFactory
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule
import work.slhaf.partner.api.agent.factory.module.annotation.Init
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule
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.ActionData
import work.slhaf.partner.core.action.entity.ScheduledActionData
import work.slhaf.partner.module.modules.action.dispatcher.executor.ActionExecutor
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.ActionExecutorInput
import java.io.Closeable
import java.time.Duration
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.util.stream.Collectors
import kotlin.jvm.optionals.getOrNull
@AgentSubModule
class ActionScheduler : AgentRunningSubModule<Set<ScheduledActionData>, Void>() {
@InjectCapability
private lateinit var actionCapability: ActionCapability
@InjectModule
private lateinit var actionExecutor: ActionExecutor
private lateinit var timeWheel: TimeWheel
private val schedulerScope =
CoroutineScope(Dispatchers.Default + SupervisorJob() + CoroutineName("ActionScheduler"))
companion object {
private val log = LoggerFactory.getLogger(ActionScheduler::class.java)
}
@Init
fun init() {
val listScheduledActions: () -> Set<ScheduledActionData> = {
actionCapability.listActions(null, null)
.stream()
.filter { it is ScheduledActionData }
.map { it as ScheduledActionData }
.collect(Collectors.toSet())
}
val onTrigger: (Set<ScheduledActionData>) -> Unit = { actionExecutor.execute(ActionExecutorInput(it)) }
timeWheel = TimeWheel(listScheduledActions, onTrigger)
setupShutdownHook()
}
private fun setupShutdownHook() {
Runtime.getRuntime().addShutdownHook(Thread {
timeWheel.close()
schedulerScope.cancel()
})
}
override fun execute(scheduledActionDataSet: Set<ScheduledActionData>?): Void? {
schedulerScope.launch {
scheduledActionDataSet?.run {
for (scheduledActionData in scheduledActionDataSet) {
log.debug("New action to schedule: {}", scheduledActionData)
actionCapability.putAction(scheduledActionData)
timeWheel.schedule(scheduledActionData)
}
}
}
return null
}
private class TimeWheel(
val listScheduledActions: () -> Set<ScheduledActionData>,
val onTrigger: (toTrigger: Set<ScheduledActionData>) -> Unit
) : Closeable {
private val actionsGroupByHour = Array<MutableSet<ScheduledActionData>>(24) { mutableSetOf() }
private val wheel = Array<MutableSet<ScheduledActionData>>(60 * 60) { mutableSetOf() }
private var recordHour: Int = -1
private var recordDay: Int = -1
private val state = MutableStateFlow(WheelState.SLEEPING)
private val wheelActionsLock = Mutex()
private val timeWheelScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("TimeWheel"))
private val cronDefinition: CronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ)
private val cronParser: CronParser = CronParser(cronDefinition)
/**
* 根据 primaryActions 建立时间轮,并只加载当天任务,同时启动 tick 线程
*/
init {
// 启动时间轮
launchWheel()
}
suspend fun schedule(actionData: ScheduledActionData) {
if (actionData.status != ActionData.ActionStatus.PREPARE) {
return
}
checkThenExecute {
val parseToZonedDateTime = parseToZonedDateTime(
actionData.scheduleType,
actionData.scheduleContent,
it
) ?: run {
logFailedStatus(actionData)
return@checkThenExecute
}
log.debug("Action next execution time: {}", parseToZonedDateTime)
val hour = parseToZonedDateTime.hour
actionsGroupByHour[hour].add(actionData)
log.debug("Action scheduled at {}", hour)
if (it.hour == hour) {
val wheelOffset = parseToZonedDateTime.minute * 60 + parseToZonedDateTime.second
wheel[wheelOffset].add(actionData)
state.value = WheelState.ACTIVE
log.debug("Action scheduled at wheel offset {}", wheelOffset)
}
}
}
private fun launchWheel() {
fun collectToTrigger(tick: Int, previousTick: Int, triggerHour: Int): Set<ScheduledActionData>? {
if (tick > previousTick) {
val toTrigger = mutableSetOf<ScheduledActionData>()
for (i in previousTick..tick) {
val bucket = wheel[i]
if (bucket.isNotEmpty()) {
toTrigger.addAll(bucket)
val bucketUuids = bucket.asSequence().map { it.uuid }.toHashSet()
actionsGroupByHour[triggerHour].removeIf { it.uuid in bucketUuids }
bucket.clear() // 避免重复触发
}
}
return toTrigger
}
return null
}
suspend fun CoroutineScope.wheel(launchingTime: ZonedDateTime, primaryTickAdvanceTime: Long) {
val launchingHour = launchingTime.hour
var tick = launchingTime.minute * 60 + launchingTime.second
// 让节拍器从“启动时刻的下一秒”开始(避免立即 step=0
var nextTickNanos = primaryTickAdvanceTime + 1_000_000_000L
while (isActive) {
// 1) 计算落后多少秒:至少 1正常推进也可能 >1追赶
val now0 = System.nanoTime()
val lagNanos = now0 - nextTickNanos
val step = if (lagNanos < 0) 1 else (lagNanos / 1_000_000_000L).toInt() + 1
val previousTick = tick
tick = (tick + step).coerceAtMost(wheel.lastIndex)
// 2) 推进节拍器:按“理论秒”前进 step 次
nextTickNanos += step.toLong() * 1_000_000_000L
var shouldBreak = false
var toTrigger: Set<ScheduledActionData>? = null
checkThenExecute(false) {
if (it.hour != launchingHour) {
shouldBreak = true
toTrigger = collectToTrigger(wheel.lastIndex, previousTick, launchingHour)
log.debug(
"Hour changed, previousTick: {}, tick: {}, toTriggerSize: {}",
previousTick,
tick,
toTrigger?.size
)
return@checkThenExecute
}
toTrigger = collectToTrigger(tick, previousTick, launchingHour)
if (tick >= wheel.lastIndex || actionsGroupByHour[launchingHour].isEmpty()) {
state.value = WheelState.SLEEPING
shouldBreak = true
return@checkThenExecute
}
}
toTrigger?.takeIf { it.isNotEmpty() }?.let {
onTrigger(it)
log.debug("Executing action at hour {} tick {}", launchingHour, tick)
}
if (shouldBreak) {
log.debug("Wheel stopped at tick {}", tick)
break
}
// 3) 精确睡到下一次理论 tick用最新 nanoTime
val now1 = System.nanoTime()
val sleepNanos = nextTickNanos - now1
if (sleepNanos > 0) {
delay(sleepNanos / 1_000_000L) // 毫秒级 delay 足够;剩余 nanos 不必忙等
}
}
}
suspend fun wait(currentTime: ZonedDateTime) {
val nextHour = currentTime.truncatedTo(ChronoUnit.HOURS).plusHours(1)
val seconds = Duration.between(
currentTime, nextHour
).toMillis()
// withTimeoutOrNull 内部已处理 seconds 小于 0 的情况
log.debug("Start waiting {} ms at {}, target time: {}", seconds, currentTime, nextHour)
withTimeoutOrNull(seconds) {
state.first { it == WheelState.ACTIVE }
}
log.debug("Waiting ended at {}", ZonedDateTime.now())
}
timeWheelScope.launch {
while (isActive) {
// 判断是否该步入下一小时
var shouldWait: Boolean? = null
var currentTime: ZonedDateTime? = null
var primaryTickAdvanceTime: Long? = null
checkThenExecute {
currentTime = it
shouldWait = actionsGroupByHour[it.hour].isEmpty()
// 由于 wheel 的启动时间可能存在延迟,而时内推进由 nanoTime 保证不会漏发,
// 正常的时序结束又由 tick 是否触顶、当前时是否存在额外任务触发,
// 而启动时无触发保障,此时一并初始化 tick 推进时间,足以应对 check 与 wheel 间的这段时间间隔
primaryTickAdvanceTime = System.nanoTime()
}
// 如果该时无任务则等待,插入事件可提前唤醒
if (shouldWait!!) {
// 计算距离下一小时的时间,等待
currentTime?.let { wait(it) }
continue
}
// 唤醒进行时间轮循环
wheel(currentTime!!, primaryTickAdvanceTime!!)
}
}
}
suspend fun checkThenExecute(finallyToExecute: Boolean = true, then: (currentTime: ZonedDateTime) -> Unit) =
wheelActionsLock.withLock {
fun loadActions(
source: Set<ScheduledActionData>,
now: ZonedDateTime,
load: (latestExecutingTime: ZonedDateTime, actionData: ScheduledActionData) -> Unit,
repair: () -> Unit
) {
val runLoading = {
for (actionData in source) {
val nextExecutingTime =
parseToZonedDateTime(
actionData.scheduleType,
actionData.scheduleContent,
now
) ?: run {
logFailedStatus(actionData)
continue
}
load(nextExecutingTime, actionData)
}
}
repair()
runLoading()
}
fun loadHourActions(currentTime: ZonedDateTime) {
val load: (ZonedDateTime, ScheduledActionData) -> Unit = { latestExecutionTime, actionData ->
val secondsTime = latestExecutionTime.minute * 60 + latestExecutionTime.second
wheel[secondsTime].add(actionData)
log.debug("Action loaded to hour: {}", actionData)
}
val repair: () -> Unit = {
for (set in wheel) {
set.clear()
}
}
loadActions(actionsGroupByHour[currentTime.hour], currentTime, load, repair)
}
fun loadDayActions(currentTime: ZonedDateTime) {
val load: (ZonedDateTime, ScheduledActionData) -> Unit = { latestExecutingTime, actionData ->
actionsGroupByHour[latestExecutingTime.hour].add(actionData)
log.debug("Action loaded to day: {}", actionData)
}
val repair: () -> Unit = {
for (set in actionsGroupByHour) {
set.clear()
}
}
loadActions(listScheduledActions(), currentTime, load, repair)
}
fun refreshIfNeeded(now: ZonedDateTime) {
val d = now.dayOfMonth
val h = now.hour
if (d != recordDay) {
recordDay = d
recordHour = h
loadDayActions(now)
loadHourActions(now)
} else if (h != recordHour) {
recordHour = h
loadHourActions(now)
}
}
val now = ZonedDateTime.now()
if (finallyToExecute) {
refreshIfNeeded(now)
then(now)
} else {
then(now)
refreshIfNeeded(now)
}
}
private fun parseToZonedDateTime(
scheduleType: ScheduledActionData.ScheduleType,
scheduleContent: String,
now: ZonedDateTime
): ZonedDateTime? {
return when (scheduleType) {
ScheduledActionData.ScheduleType.CYCLE
-> {
val cron = try {
cronParser.parse(scheduleContent).validate()
} catch (_: Exception) {
return null
}
val executionTime = ExecutionTime.forCron(cron)
executionTime.nextExecution(now).getOrNull()
}
ScheduledActionData.ScheduleType.ONCE -> {
val executionTime = try {
ZonedDateTime.parse(scheduleContent)
} catch (_: Exception) {
return null
}
if (executionTime.plusSeconds(1).isBefore(now) || executionTime.dayOfMonth != now.dayOfMonth)
null
else
executionTime
}
}
}
private fun logFailedStatus(actionData: ScheduledActionData) {
log.warn(
"行动未加载uuid: {}, source: {}, tendency: {}, scheduleContent: {}",
actionData.uuid,
actionData.source,
actionData.tendency,
actionData.scheduleContent,
)
}
override fun close() {
timeWheelScope.cancel()
}
private enum class WheelState {
ACTIVE,
SLEEPING,
}
}
}

View File

@@ -1,251 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.val;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.action.entity.PhaserRecord;
import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.module.common.module.PreRunningModule;
import work.slhaf.partner.module.modules.action.interventor.entity.InterventionType;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
import work.slhaf.partner.module.modules.action.interventor.evaluator.InterventionEvaluator;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorInput;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorResult;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorResult.EvaluatedInterventionData;
import work.slhaf.partner.module.modules.action.interventor.recognizer.InterventionRecognizer;
import work.slhaf.partner.module.modules.action.interventor.recognizer.entity.RecognizerInput;
import work.slhaf.partner.module.modules.action.interventor.recognizer.entity.RecognizerResult;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
/**
* 负责识别潜在的行动干预信息,作用于正在进行或已存在的行动池中内容
*/
@AgentModule(name = "action_identifier", order = 2)
public class ActionInterventor extends PreRunningModule implements ActivateModel {
@InjectModule
private InterventionRecognizer interventionRecognizer;
@InjectModule
private InterventionEvaluator interventionEvaluator;
@InjectCapability
private ActionCapability actionCapability;
@InjectCapability
private CognationCapability cognationCapability;
@InjectCapability
private MemoryCapability memoryCapability;
private final AssemblyHelper assemblyHelper = new AssemblyHelper();
private final PromptHelper promptHelper = new PromptHelper();
/**
* 键: 本次调用uuid
* 值本次调用对应的prompt
*/
private final Map<String, Map<String, String>> interventionPrompt = new HashMap<>();
@Override
protected void doExecute(PartnerRunningFlowContext context) {
// 综合当前正在进行的行动链信息、用户交互历史、激活的记忆切片,尝试识别出是否存在行动干预意图
// 首先通过recognizer进行快速意图识别识别成功则步入评估阶段评估成功则直接作用于目标行动链
// 进行快速意图识别时必须结合近期对话与进行中行动链情况
// 干预意图识别
String uuid = context.getUuid();
String userId = context.getUserId();
RecognizerResult recognizerResult = interventionRecognizer
.execute(assemblyHelper.buildRecognizerInput(userId, context.getInput())); // 此处的输入内容携带了所有 PhaserRecord
if (!recognizerResult.isOk()) {
promptHelper.setupNoInterventionPrompt(uuid);
return;
}
// 干预意图评估
EvaluatorResult evaluatorResult = interventionEvaluator
.execute(assemblyHelper.buildEvaluatorInput(recognizerResult, userId));
List<EvaluatedInterventionData> executingDataList = evaluatorResult.getExecutingDataList();
List<EvaluatedInterventionData> preparedDataList = evaluatorResult.getPreparedDataList();
// 意图评估结果处理
if (evaluatorResult.isOk()) {
// 对存在异常ActionKey的评估结果列表进行过滤
invalidActionKeysFilter(executingDataList);
invalidActionKeysFilter(preparedDataList);
// 同步写入prompt异步处理干预行为异步在处理流程中体现
promptHelper.setupInterventionPrompt(uuid, executingDataList, preparedDataList);
handleInterventions(executingDataList, recognizerResult.getExecutingInterventions());
handleInterventions(preparedDataList, recognizerResult.getPreparedInterventions());
} else {
promptHelper.setupInterventionIgnoredPrompt(uuid, executingDataList, preparedDataList);
}
}
private void handleInterventions(List<EvaluatedInterventionData> interventionDataList, Map<String, ActionData> interventionDataMap) {
val executor = actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM);
executor.execute(() -> {
for (EvaluatedInterventionData interventionData : interventionDataList) {
// 此处拿到的为 ActionData 或者 PhaserRecord, 来自 Recognizer 的封装
val data = interventionDataMap.get(interventionData.getTendency());
actionCapability.handleInterventions(interventionData.getMetaInterventionList(), data);
}
});
}
private void invalidActionKeysFilter(List<EvaluatedInterventionData> interventions) {
List<EvaluatedInterventionData> toRemove = new ArrayList<>();
for (EvaluatedInterventionData intervention : interventions) {
List<MetaIntervention> interventionData = intervention.getMetaInterventionList();
List<String> actions = new ArrayList<>();
for (MetaIntervention metaData : interventionData) {
actions.addAll(metaData.getActions());
}
// 如果存在异常行动key则可视为该评估结果存在问题直接忽略该结果
if (!actionCapability.checkExists(actions.toArray(String[]::new))) {
toRemove.add(intervention);
}
// 针对 REBUILD 类型进行特殊校验, REBUILD 类型必须满足所有 MetaIntervention 的类型均为 REBUILD
if (!checkRebuildType(interventionData)) {
toRemove.add(intervention);
}
}
interventions.removeAll(toRemove);
}
private boolean checkRebuildType(List<MetaIntervention> interventionData) {
boolean hasRebuild = false;
for (MetaIntervention meta : interventionData) {
if (meta.getType() == InterventionType.REBUILD) {
hasRebuild = true;
} else if (hasRebuild) {
// 已经存在REBUILD类型但又发现了非REBUILD类型不合法
return false;
}
}
return true;
}
@Override
public String modelKey() {
return "action_identifier";
}
@Override
public boolean withBasicPrompt() {
return false;
}
@Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
return interventionPrompt.remove(context.getUuid());
}
@Override
protected String moduleName() {
return "[行动干预识别模块]";
}
private final class AssemblyHelper {
private AssemblyHelper() {
}
private RecognizerInput buildRecognizerInput(String userId, String input) {
RecognizerInput recognizerInput = new RecognizerInput();
recognizerInput.setInput(input);
recognizerInput.setUserDialogMapStr(memoryCapability.getUserDialogMapStr(userId));
// 参考的对话列表大小或需调整
recognizerInput.setRecentMessages(cognationCapability.getChatMessages());
recognizerInput.setExecutingActions(actionCapability.listPhaserRecords().stream().map(PhaserRecord::actionData).toList());
recognizerInput.setPreparedActions(actionCapability.listActions(ActionData.ActionStatus.PREPARE, userId).stream().toList());
return recognizerInput;
}
private EvaluatorInput buildEvaluatorInput(RecognizerResult recognizerResult, String userId) {
EvaluatorInput input = new EvaluatorInput();
input.setExecutingInterventions(recognizerResult.getExecutingInterventions());
input.setPreparedInterventions(recognizerResult.getPreparedInterventions());
input.setRecentMessages(cognationCapability.getChatMessages());
input.setActivatedSlices(memoryCapability.getActivatedSlices(userId));
return input;
}
}
private final class PromptHelper {
private PromptHelper() {
}
private void setupInterventionIgnoredPrompt(String uuid, List<EvaluatedInterventionData> executingDataList, List<EvaluatedInterventionData> preparedDataList) {
List<EvaluatedInterventionData> total = Stream.concat(executingDataList.stream(), preparedDataList.stream()).toList();
JSONArray reasons = new JSONArray();
for (EvaluatedInterventionData data : total) {
JSONObject reason = reasons.addObject();
reason.put("[干预倾向]", data.getTendency());
reason.put("[未采用原因]", data.getDescription());
}
synchronized (interventionPrompt) {
interventionPrompt.put(uuid, Map.of(
"[识别状态] <是否识别到干预已存在行动的意图>", "识别到,但都未采用",
"[忽略原因] <各个意图被忽略的原因>", reasons.toString(),
"[干预行动] <将对已存在行动做出的行为>", "无行为"));
}
}
private void setupInterventionPrompt(String uuid, List<EvaluatedInterventionData> executingDataList,
List<EvaluatedInterventionData> preparedDataList) {
JSONArray contents = new JSONArray();
List<EvaluatedInterventionData> temp = Stream.concat(executingDataList.stream(), preparedDataList.stream()).toList();
for (EvaluatedInterventionData data : temp) {
if (!data.isOk()) {
continue;
}
String tendency = data.getTendency();
JSONObject newElement = contents.addObject();
newElement.put("[干预倾向]", tendency);
JSONArray changes = newElement.putArray("[行动链变动情况]");
for (MetaIntervention intervention : data.getMetaInterventionList()) {
JSONObject change = changes.addObject();
change.put("[干预类型]", intervention.getType());
change.put("[干预序号]", intervention.getOrder());
change.putArray("[干预内容]").addAll(intervention.getActions());
}
}
synchronized (interventionPrompt) {
interventionPrompt.put(uuid, Map.of(
"[识别状态] <是否识别到干预已存在行动的意图>", "识别到,将采用",
"[干预内容] <将对已存在行动做出的行为>", contents.toString()));
}
}
private void setupNoInterventionPrompt(String uuid) {
interventionPrompt.put(uuid, Map.of(
"[识别状态] <是否识别到干预已存在行动的意图>", "未识别到干预意图",
"[干预行动] <将对已存在行动做出的行为>", "无行动"));
}
}
}

View File

@@ -1,28 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.entity;
public enum InterventionType {
/**
* 追加行动: 追加至指定行动链序列之后才执行
*/
APPEND,
/**
* 插入行动: 指定行动链序列执行过程中即时新增并执行
*/
INSERT,
/**
* 重建行动: 重建指定行动链序列之后的所有行动内容
*/
REBUILD,
/**
* 删除行动: 删除指定行动链序列上的指定行动单元
*/
DELETE,
/**
* 取消行动链: 中断并取消指定行动链的执行
*/
CANCEL
}

View File

@@ -1,21 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.entity;
import lombok.Data;
import java.util.List;
@Data
public class MetaIntervention {
/**
* 干预数据类型
*/
private InterventionType type;
/**
* 干预数据对应的行动链序列
*/
private int order;
/**
* 干预数据所需的行动key列表
*/
private List<String> actions;
}

View File

@@ -1,99 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.evaluator;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore.ExecutorType;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorInput;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorResult;
import work.slhaf.partner.module.modules.action.interventor.evaluator.entity.EvaluatorResult.EvaluatedInterventionData;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
@Slf4j
@AgentSubModule
public class InterventionEvaluator extends AgentRunningSubModule<EvaluatorInput, EvaluatorResult>
implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
/**
* 基于干预意图、记忆切片、交互上下文、已有行动程序综合评估,尝试评估并选取出合适的行动程序,交付给 ActionInterventor
*/
@Override
public EvaluatorResult execute(EvaluatorInput input) {
// 获取必须数据
ExecutorService executor = actionCapability.getExecutor(ExecutorType.VIRTUAL);
Map<String, ActionData> executingInterventions = input.getExecutingInterventions();
Map<String, ActionData> preparedInterventions = input.getPreparedInterventions();
CountDownLatch latch = new CountDownLatch(executingInterventions.size() + preparedInterventions.size());
// 创建结果容器
EvaluatorResult result = new EvaluatorResult();
List<EvaluatedInterventionData> executingDataList = result.getExecutingDataList();
List<EvaluatedInterventionData> preparedDataList = result.getPreparedDataList();
// 并发评估
evaluateIntervention(executingDataList, executingInterventions, input, executor, latch);
evaluateIntervention(preparedDataList, preparedInterventions, input, executor, latch);
try {
latch.await();
} catch (InterruptedException e) {
log.warn("CountDownLatch阻塞已中断");
}
return result;
}
private void evaluateIntervention(List<EvaluatedInterventionData> evaluatedDataList, Map<String, ActionData> interventionMap, EvaluatorInput input, ExecutorService executor, CountDownLatch latch) {
interventionMap.forEach((tendency, actionData) -> executor.execute(() -> {
try {
String prompt = buildPrompt(input.getRecentMessages(), input.getActivatedSlices(), actionData, tendency);
ChatResponse response = this.singleChat(prompt);
EvaluatedInterventionData evaluatedData = JSONObject.parseObject(response.getMessage(),
EvaluatedInterventionData.class);
synchronized (evaluatedDataList) {
evaluatedDataList.add(evaluatedData);
}
} catch (Exception e) {
log.error("干预意图评估出错: {}", tendency, e);
} finally {
latch.countDown();
}
}));
}
private String buildPrompt(List<Message> recentMessages, List<EvaluatedSlice> activatedSlices,
ActionData actionData, String tendency) {
JSONObject json = new JSONObject();
json.put("干预倾向", tendency);
json.putArray("近期对话").addAll(recentMessages);
json.putArray("参考记忆").addAll(activatedSlices);
json.put("将干预的行动", JSONObject.toJSONString(actionData));
return json.toJSONString();
}
@Override
public String modelKey() {
return "intervention_evaluator";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

@@ -1,17 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.evaluator.entity;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import java.util.List;
import java.util.Map;
@Data
public class EvaluatorInput {
private Map<String, ActionData> executingInterventions;
private Map<String, ActionData> preparedInterventions;
private List<EvaluatedSlice> activatedSlices;
private List<Message> recentMessages;
}

View File

@@ -1,33 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.evaluator.entity;
import lombok.Data;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
import java.util.List;
/**
* 干预倾向评估结果包含评估通过的倾向文本、对行动链的行为、指定操作的行动单元key、未通过的原因
*/
@Data
public class EvaluatorResult {
/**
* 是否存在通过的干预倾向
*/
private boolean ok;
private List<EvaluatedInterventionData> executingDataList;
private List<EvaluatedInterventionData> preparedDataList;
@Data
public static class EvaluatedInterventionData {
/**
* 是否通过
*/
private boolean ok;
private String tendency;
/**
* 描述信息(包括通过、失败原因)
*/
private String description;
private List<MetaIntervention> metaInterventionList;
}
}

View File

@@ -1,102 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.recognizer;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.module.modules.action.interventor.recognizer.entity.MetaRecognizerResult;
import work.slhaf.partner.module.modules.action.interventor.recognizer.entity.RecognizerInput;
import work.slhaf.partner.module.modules.action.interventor.recognizer.entity.RecognizerResult;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
@Slf4j
@AgentSubModule
public class InterventionRecognizer extends AgentRunningSubModule<RecognizerInput, RecognizerResult> implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
@Override
public RecognizerResult execute(RecognizerInput input) {
// 获取必须数据
ExecutorService executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
List<ActionData> executingActions = input.getExecutingActions();
List<ActionData> preparedActions = input.getPreparedActions();
CountDownLatch countDownLatch = new CountDownLatch(executingActions.size() + preparedActions.size());
// 创建结果容器
RecognizerResult recognizerResult = new RecognizerResult();
Map<String, ActionData> executingInterventions = recognizerResult.getExecutingInterventions();
Map<String, ActionData> preparedInterventions = recognizerResult.getPreparedInterventions();
// 执行识别操作
recognizeIntervention(executingInterventions, executingActions, executor, input, countDownLatch);
recognizeIntervention(preparedInterventions, preparedActions, executor, input, countDownLatch);
try {
countDownLatch.await();
} catch (InterruptedException e) {
log.warn("CountDownLatch阻塞已中断");
}
return recognizerResult;
}
private void recognizeIntervention(Map<String, ActionData> interventionsMap, List<ActionData> actions, ExecutorService executor, RecognizerInput input, CountDownLatch latch) {
for (ActionData data : actions) {
executor.execute(() -> {
try {
String prompt = buildPrompt(data, input);
ChatResponse response = this.singleChat(prompt);
MetaRecognizerResult result = JSONObject.parseObject(response.getMessage(), MetaRecognizerResult.class);
if (result.isOk()) {
synchronized (interventionsMap) {
interventionsMap.put(result.getIntervention(), data);
}
}
} catch (Exception e) {
log.error("LLM干预意图提取出错", e);
} finally {
latch.countDown();
}
});
}
}
private String buildPrompt(ActionData actionData, RecognizerInput input) {
JSONObject json = new JSONObject();
JSONObject actionInfo = json.putObject("行动信息");
actionInfo.put("行动倾向", actionData.getTendency());
actionInfo.put("行动原因", actionData.getReason());
actionInfo.put("行动描述", actionData.getDescription());
actionInfo.put("行动状态", actionData.getStatus());
actionInfo.put("行动来源", actionData.getSource());
JSONObject interactionInfo = json.putObject("交互信息");
interactionInfo.put("用户输入", input.getInput());
interactionInfo.put("当前对话", input.getRecentMessages());
interactionInfo.put("近期对话", input.getUserDialogMapStr());
return json.toString();
}
@Override
public String modelKey() {
return "intervention_recognizer";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

@@ -1,9 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.recognizer.entity;
import lombok.Data;
@Data
public class MetaRecognizerResult {
private boolean ok;
private String intervention;
}

View File

@@ -1,22 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.recognizer.entity;
import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.entity.ActionData;
import java.util.List;
@Data
public class RecognizerInput {
private String input;
private List<Message> recentMessages;
/**
* 当前用户对应的近两日对话缓存
*/
private String userDialogMapStr;
/**
* 正在执行的行动-Phaser记录列表在Recognizer中结合本次输入并发评估(考虑到不同行动链之间对LLM的影响)
*/
private List<ActionData> executingActions;
private List<ActionData> preparedActions;
}

View File

@@ -1,29 +0,0 @@
package work.slhaf.partner.module.modules.action.interventor.recognizer.entity;
import lombok.Data;
import work.slhaf.partner.core.action.entity.ActionData;
import java.util.HashMap;
import java.util.Map;
@Data
public class RecognizerResult {
private boolean ok;
/**
* <h4>将被干预的‘执行中行动’</h4>
* key: 干预倾向
* <br/>
* value: 干预倾向将作用的行动数据
*/
private Map<String, ActionData> executingInterventions = new HashMap<>();
/**
* <h4>将被干预的‘等待中行动’</h4>
* key: 干预倾向
* <br/>
* value: 干预倾向将作用的行动数据
*/
private Map<String, ActionData> preparedInterventions = new HashMap<>();
}

View File

@@ -1,19 +1,15 @@
package work.slhaf.partner.module.modules.action.planner; package work.slhaf.partner.module.modules.action.planner;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule; import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init; import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule; import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
import work.slhaf.partner.api.chat.pojo.Message; 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.common.vector.VectorClient;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
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; import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.core.perceive.PerceiveCapability; import work.slhaf.partner.core.perceive.PerceiveCapability;
@@ -29,15 +25,15 @@ import work.slhaf.partner.module.modules.action.planner.extractor.entity.Extract
import work.slhaf.partner.module.modules.action.planner.extractor.entity.ExtractorResult; import work.slhaf.partner.module.modules.action.planner.extractor.entity.ExtractorResult;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext; import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* 负责针对本次输入生成基础的行动计划,在主模型传达意愿后,执行行动或者放入计划池 * 负责针对本次输入生成基础的行动计划,在主模型传达意愿后,执行行动或者放入计划池
*/ */
@Slf4j
@AgentModule(name = "action_planner", order = 2) @AgentModule(name = "action_planner", order = 2)
public class ActionPlanner extends PreRunningModule { public class ActionPlanner extends PreRunningModule {
@@ -57,25 +53,21 @@ public class ActionPlanner extends PreRunningModule {
@InjectModule @InjectModule
private ActionConfirmer actionConfirmer; private ActionConfirmer actionConfirmer;
private ExecutorService executor; private InteractionThreadPoolExecutor executor;
private ActionAssemblyHelper assemblyHelper;
private final ActionAssemblyHelper assemblyHelper = new ActionAssemblyHelper();
@Init @Init
public void init() { public void init() {
executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL); executor = InteractionThreadPoolExecutor.getInstance();
assemblyHelper = new ActionAssemblyHelper();
} }
@Override @Override
protected void doExecute(PartnerRunningFlowContext context) { protected void doExecute(PartnerRunningFlowContext context) {
try {
List<Callable<Void>> tasks = new ArrayList<>(); List<Callable<Void>> tasks = new ArrayList<>();
addConfirmTask(tasks, context); addConfirmTask(tasks, context);
addNewActionTask(tasks, context); addNewActionTask(tasks, context);
executor.invokeAll(tasks); executor.invokeAll(tasks);
} catch (Exception e) {
log.error("执行异常", e);
}
} }
/** /**
@@ -92,15 +84,14 @@ public class ActionPlanner extends PreRunningModule {
return null; return null;
} }
EvaluatorInput evaluatorInput = assemblyHelper.buildEvaluatorInput(extractorResult, context.getUserId()); EvaluatorInput evaluatorInput = assemblyHelper.buildEvaluatorInput(extractorResult, context.getUserId());
List<EvaluatorResult> evaluatorResults = actionEvaluator.execute(evaluatorInput); // 并发操作均为访问 List<EvaluatorResult> evaluatorResults = actionEvaluator.execute(evaluatorInput); //并发操作均为访问
putActionData(evaluatorResults, context); setupActionInfo(evaluatorResults, context);
updateTendencyCache(evaluatorResults, context.getInput(), extractorResult);
return null; return null;
}); });
} }
private void updateTendencyCache(List<EvaluatorResult> evaluatorResults, String input, @AfterExecute
ExtractorResult extractorResult) { private void updateTendencyCache(List<EvaluatorResult> evaluatorResults, String input, ExtractorResult extractorResult) {
if (!VectorClient.status) { if (!VectorClient.status) {
return; return;
} }
@@ -132,69 +123,72 @@ public class ActionPlanner extends PreRunningModule {
tasks.add(() -> { tasks.add(() -> {
ConfirmerInput confirmerInput = assemblyHelper.buildConfirmerInput(context); ConfirmerInput confirmerInput = assemblyHelper.buildConfirmerInput(context);
ConfirmerResult result = actionConfirmer.execute(confirmerInput); ConfirmerResult result = actionConfirmer.execute(confirmerInput);
setupConfirmedActionInfo(context, result); setupPendingActionInfo(context, result);
return null; return null;
}); });
} }
private void setupConfirmedActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) { private void setupPendingActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) {
// TODO 需考虑未确认任务的失效或者拒绝时机在action core中实现 //TODO 需考虑未确认任务的失效或者拒绝时机
List<String> uuids = result.getUuids(); List<String> uuids = result.getUuids();
if (uuids == null) { if (uuids == null) {
return; return;
} }
List<ActionData> pendingActions = actionCapability.popPendingAction(context.getUserId()); String contextUuid = context.getUuid();
for (ActionData actionData : pendingActions) { List<MetaActionInfo> pendingActions = actionCapability.popPendingAction(context.getUserId());
if (uuids.contains(actionData.getUuid())) { for (MetaActionInfo actionInfo : pendingActions) {
actionCapability.putAction(actionData); if (uuids.contains(actionInfo.getUuid())) {
actionCapability.putPreparedAction(contextUuid, actionInfo);
} }
} }
} }
private void putActionData(List<EvaluatorResult> evaluatorResults, PartnerRunningFlowContext context) {
private void setupActionInfo(List<EvaluatorResult> evaluatorResults, PartnerRunningFlowContext context) {
for (EvaluatorResult evaluatorResult : evaluatorResults) { for (EvaluatorResult evaluatorResult : evaluatorResults) {
ActionData actionData = assemblyHelper.buildActionData(evaluatorResult, context.getUserId()); MetaActionInfo metaActionInfo = assemblyHelper.buildMetaActionInfo(evaluatorResult);
if (evaluatorResult.isNeedConfirm()) { if (evaluatorResult.isNeedConfirm()) {
actionCapability.putPendingActions(context.getUserId(), actionData); actionCapability.putPendingActions(context.getUserId(), metaActionInfo);
} else { } else {
actionCapability.putAction(actionData); actionCapability.putPreparedAction(context.getUuid(), metaActionInfo);
} }
} }
} }
@Override @Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) { protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
String userId = context.getUserId(); setupPendingActions(map, context.getUserId());
setupPendingActions(map, userId); setupPreparedActions(map, context.getUuid());
setupPreparedActions(map, userId);
return map; return map;
} }
private void setupPendingActions(HashMap<String, String> map, String userId) { private void setupPendingActions(HashMap<String, String> map, String userId) {
List<ActionData> actionData = actionCapability.listPendingAction(userId); List<MetaActionInfo> actionInfos = actionCapability.listPendingAction(userId);
if (actionData == null || actionData.isEmpty()) { if (actionInfos == null || actionInfos.isEmpty()) {
map.put("[待确认行动] <等待用户确认行动信息>", "无待确认行动"); map.put("[待确认行动] <确认行动信息>", "无待确认行动");
return; return;
} }
for (int i = 0; i < actionData.size(); i++) { for (int i = 0; i < actionInfos.size(); i++) {
map.put("[待确认行动 " + (i + 1) + " ] <等待用户确认的行动信息>", generateActionStr(actionData.get(i))); map.put("[待确认行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
} }
} }
private void setupPreparedActions(HashMap<String, String> map, String userId) { private void setupPreparedActions(HashMap<String, String> map, String uuid) {
val preparedActions = actionCapability.listActions(ActionData.ActionStatus.PREPARE, userId).stream().toList(); List<MetaActionInfo> actionInfos = actionCapability.listPreparedAction(uuid);
if (preparedActions.isEmpty()) { if (actionInfos == null || actionInfos.isEmpty()) {
map.put("[预备行动] <预备执行或放入计划池的行动信息>", "无预备行动"); map.put("[预备行动] <预备行动信息>", "无预备行动");
return; return;
} }
for (int i = 0; i < preparedActions.size(); i++) { for (int i = 0; i < actionInfos.size(); i++) {
map.put("[预备行动 " + (i + 1) + " ] <预备执行或放入计划池的行动信息>", generateActionStr(preparedActions.get(i))); map.put("[预备行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
} }
} }
private String generateActionStr(ActionData actionData) { private String generateActionStr(MetaActionInfo metaActionInfo) {
return "<行动倾向>" + " : " + actionData.getTendency() + ActionData actionData = metaActionInfo.getActionData();
return "<行动倾向>" + " : " + metaActionInfo.getTendency() +
"<行动原因>" + " : " + actionData.getReason() + "<行动原因>" + " : " + actionData.getReason() +
"<工具描述>" + " : " + actionData.getDescription(); "<工具描述>" + " : " + actionData.getDescription();
} }
@@ -204,7 +198,7 @@ public class ActionPlanner extends PreRunningModule {
return "[行动模块]"; return "[行动模块]";
} }
private final class ActionAssemblyHelper { private class ActionAssemblyHelper {
private ActionAssemblyHelper() { private ActionAssemblyHelper() {
} }
@@ -231,109 +225,31 @@ public class ActionPlanner extends PreRunningModule {
return input; return input;
} }
private ActionData buildActionData(EvaluatorResult evaluatorResult, String userId) { private MetaActionInfo buildMetaActionInfo(EvaluatorResult evaluatorResult) {
Map<Integer, List<MetaAction>> actionChain = getActionChain(evaluatorResult);
return switch (evaluatorResult.getType()) { return switch (evaluatorResult.getType()) {
case PLANNING -> new ScheduledActionData( case PLANNING -> {
evaluatorResult.getTendency(), ScheduledActionInfo actionInfo = new ScheduledActionInfo();
actionChain, actionInfo.setActionData(evaluatorResult.getActionData());
evaluatorResult.getReason(), actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
evaluatorResult.getDescription(), actionInfo.setStatus(ActionStatus.PREPARE);
userId, actionInfo.setUuid(UUID.randomUUID().toString());
evaluatorResult.getScheduleType(), yield actionInfo;
evaluatorResult.getScheduleContent() }
); case IMMEDIATE -> {
case IMMEDIATE -> new ImmediateActionData( ImmediateActionInfo actionInfo = new ImmediateActionInfo();
evaluatorResult.getTendency(), actionInfo.setActionData(evaluatorResult.getActionData());
actionChain, actionInfo.setStatus(ActionStatus.PREPARE);
evaluatorResult.getReason(), actionInfo.setUuid(UUID.randomUUID().toString());
evaluatorResult.getDescription(), yield actionInfo;
userId }
);
}; };
} }
private @NotNull Map<Integer, List<MetaAction>> getActionChain(EvaluatorResult evaluatorResult) {
Map<Integer, List<MetaAction>> actionChain = new HashMap<>();
Map<Integer, List<String>> primaryActionChain = evaluatorResult.getPrimaryActionChain();
fixDependencies(primaryActionChain);
primaryActionChain.forEach((order, actionKeys) -> {
List<MetaAction> metaActions = actionKeys.stream()
.map(actionKey -> actionCapability.loadMetaAction(actionKey))
.toList();
actionChain.put(order, metaActions);
});
return actionChain;
}
private void fixDependencies(Map<Integer, List<String>> primaryActionChain) {
// 先将 primaryActionChain 的节点序号修正为从1开始依次增大
fixOrder(primaryActionChain);
List<Integer> fixedOrders = new ArrayList<>(primaryActionChain.keySet().stream().toList());
AtomicBoolean fixed = new AtomicBoolean(false);
do {
Set<Integer> tempOrders = new HashSet<>();
fixedOrders.sort(Integer::compareTo);
for (Integer fixedOrder : fixedOrders) {
int lastOrder = fixedOrder - 1;
List<String> actionKeys = primaryActionChain.get(fixedOrder);
for (String actionKey : actionKeys) {
// 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖
MetaActionInfo metaActionInfo = actionCapability.loadMetaActionInfo(actionKey);
List<String> preActions = metaActionInfo.getPreActions();
boolean preActionsExist = preActions != null && !preActions.isEmpty();
if (!preActionsExist) {
continue;
}
if (!metaActionInfo.isStrictDependencies()) {
continue;
}
if (checkDependenciesExist(lastOrder, preActions, primaryActionChain)) {
continue;
}
// 如果存在前置依赖,则将其放置在当前order之前的位置,
// 放置位置优先选择已存在的上一节点,如果不存在(行动链的头节点时)则需要向行动链新增order
// 不需要检查行动链的当前节点的已存在 Action 是否为新 Action 的依赖项,因为这些 Action 实际来自 LLM
// 的评估结果,并非作为依赖项存在
fixed.set(true);
List<String> actionsInChain = primaryActionChain.computeIfAbsent(lastOrder,
list -> new ArrayList<>());
preActions = new ArrayList<>(preActions);
preActions.removeAll(actionsInChain);
actionsInChain.addAll(preActions);
tempOrders.add(lastOrder);
}
}
fixedOrders.clear();
fixedOrders.addAll(tempOrders);
} while (fixed.getAndSet(false));
}
private void fixOrder(Map<Integer, List<String>> primaryActionChain) {
Map<Integer, List<String>> tempChain = new HashMap<>(primaryActionChain);
primaryActionChain.clear();
int chainSize = tempChain.size();
for (int i = 0; i < chainSize; i++) {
primaryActionChain.put(i, tempChain.get(i));
}
}
private boolean checkDependenciesExist(int lastOrder, List<String> preActions,
Map<Integer, List<String>> primaryActionChain) {
if (!primaryActionChain.containsKey(lastOrder)) {
return false;
}
List<String> existActions = primaryActionChain.get(lastOrder);
//noinspection SlowListContainsAll
return existActions.containsAll(preActions);
}
private ConfirmerInput buildConfirmerInput(PartnerRunningFlowContext context) { private ConfirmerInput buildConfirmerInput(PartnerRunningFlowContext context) {
ConfirmerInput confirmerInput = new ConfirmerInput(); ConfirmerInput confirmerInput = new ConfirmerInput();
confirmerInput.setInput(context.getInput()); confirmerInput.setInput(context.getInput());
List<ActionData> pendingActions = actionCapability.listPendingAction(context.getUserId()); List<MetaActionInfo> pendingActions = actionCapability.listPendingAction(context.getUserId());
confirmerInput.setActionData(pendingActions); confirmerInput.setActionInfos(pendingActions);
return confirmerInput; return confirmerInput;
} }
} }

View File

@@ -1,81 +1,16 @@
package work.slhaf.partner.module.modules.action.planner.confirmer; package work.slhaf.partner.module.modules.action.planner.confirmer;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule; import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel; import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule; import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerInput; import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerInput;
import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerResult; import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerResult;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import static work.slhaf.partner.common.util.ExtractUtil.extractJson;
@Slf4j
@AgentSubModule @AgentSubModule
public class ActionConfirmer extends AgentRunningSubModule<ConfirmerInput, ConfirmerResult> implements ActivateModel { public class ActionConfirmer extends AgentRunningSubModule<ConfirmerInput, ConfirmerResult> implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
@Override @Override
public ConfirmerResult execute(ConfirmerInput data) { public ConfirmerResult execute(ConfirmerInput data) {
List<ActionData> actionDataList = data.getActionData(); return null;
ExecutorService executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
CountDownLatch latch = new CountDownLatch(actionDataList.size());
ConfirmerResult result = new ConfirmerResult();
List<String> uuids = result.getUuids();
for (ActionData actionData : actionDataList) {
executor.execute(() -> {
try {
String prompt = buildPrompt(actionData, data.getInput(), data.getRecentMessages());
ChatResponse response = this.singleChat(prompt);
JSONObject tempResult = JSONObject.parseObject(extractJson(response.getMessage()));
if (tempResult.getBoolean("confirmed")) {
actionData.setStatus(ActionData.ActionStatus.PREPARE);
synchronized (uuids) {
uuids.add(actionData.getUuid());
}
}
} finally {
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
log.warn("CountDownLatch阻塞已中断");
}
return result;
}
private String buildPrompt(ActionData data, String input, List<Message> recentMessages) {
JSONObject prompt = new JSONObject();
prompt.put("[用户输入]", input);
JSONObject actionData = prompt.putObject("[行动数据]");
actionData.put("[行动倾向]", data.getTendency());
actionData.put("[行动原因]", data.getReason());
actionData.put("[行动来源]", data.getSource());
actionData.put("[行动描述]", data.getDescription());
JSONArray messageData = prompt.putArray("[近期对话]");
messageData.addAll(recentMessages);
return prompt.toString();
} }
@Override @Override

View File

@@ -2,13 +2,13 @@ package work.slhaf.partner.module.modules.action.planner.confirmer.entity;
import lombok.Data; import lombok.Data;
import work.slhaf.partner.api.chat.pojo.Message; import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.action.entity.ActionData; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import java.util.List; import java.util.List;
@Data @Data
public class ConfirmerInput { public class ConfirmerInput {
private String input; private String input;
private List<ActionData> actionData; private List<MetaActionInfo> actionInfos;
private List<Message> recentMessages; private List<Message> recentMessages;
} }

View File

@@ -2,10 +2,9 @@ package work.slhaf.partner.module.modules.action.planner.confirmer.entity;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Data @Data
public class ConfirmerResult { public class ConfirmerResult {
private List<String> uuids = new ArrayList<>(); private List<String> uuids;
} }

View File

@@ -1,33 +1,24 @@
package work.slhaf.partner.module.modules.action.planner.evaluator; package work.slhaf.partner.module.modules.action.planner.evaluator;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule; import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.factory.module.annotation.Init; import work.slhaf.partner.api.agent.factory.module.annotation.Init;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel; import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule; import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
import work.slhaf.partner.api.chat.pojo.ChatResponse; import work.slhaf.partner.api.chat.pojo.ChatResponse;
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor; import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorBatchInput; import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorBatchInput;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorInput; import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorInput;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult; import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@AgentSubModule @AgentSubModule
public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<EvaluatorResult>> implements ActivateModel { public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<EvaluatorResult>> implements ActivateModel {
@InjectCapability
private ActionCapability actionCapability;
private InteractionThreadPoolExecutor executor; private InteractionThreadPoolExecutor executor;
@Init @Init
@@ -39,7 +30,7 @@ public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<
* 对输入的行为倾向进行评估,并根据评估结果,对缓存做出调整 * 对输入的行为倾向进行评估,并根据评估结果,对缓存做出调整
* *
* @param data 评估输入内容,包含提取/命中缓存的行动倾向、近几条聊天记录,正在生效的记忆切片内容 * @param data 评估输入内容,包含提取/命中缓存的行动倾向、近几条聊天记录,正在生效的记忆切片内容
* @return 评估结果集合 * @return 评估结果集合,包含
*/ */
@Override @Override
public List<EvaluatorResult> execute(EvaluatorInput data) { public List<EvaluatorResult> execute(EvaluatorInput data) {
@@ -48,11 +39,12 @@ public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<
return executor.invokeAllAndReturn(tasks); return executor.invokeAllAndReturn(tasks);
} }
private List<Callable<EvaluatorResult>> getTasks(List<EvaluatorBatchInput> batchInputs) { private List<Callable<EvaluatorResult>> getTasks(List<EvaluatorBatchInput> batchInputs) {
List<Callable<EvaluatorResult>> list = new ArrayList<>(); List<Callable<EvaluatorResult>> list = new ArrayList<>();
for (EvaluatorBatchInput batchInput : batchInputs) { for (EvaluatorBatchInput batchInput : batchInputs) {
list.add(() -> { list.add(() -> {
ChatResponse response = this.singleChat(buildPrompt(batchInput)); ChatResponse response = this.singleChat(JSONObject.toJSONString(batchInput));
EvaluatorResult evaluatorResult = JSONObject.parseObject(response.getMessage(), EvaluatorResult.class); EvaluatorResult evaluatorResult = JSONObject.parseObject(response.getMessage(), EvaluatorResult.class);
evaluatorResult.setTendency(batchInput.getTendency()); evaluatorResult.setTendency(batchInput.getTendency());
return evaluatorResult; return evaluatorResult;
@@ -67,32 +59,11 @@ public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<
EvaluatorBatchInput temp = new EvaluatorBatchInput(); EvaluatorBatchInput temp = new EvaluatorBatchInput();
BeanUtil.copyProperties(data, temp); BeanUtil.copyProperties(data, temp);
temp.setTendency(tendency); temp.setTendency(tendency);
Map<String, String> availableActions = new HashMap<>();
actionCapability.listAvailableMetaActions().forEach((key, info) -> availableActions.put(key, info.getDescription()));
temp.setAvailableActions(availableActions);
list.add(temp); list.add(temp);
} }
return list; return list;
} }
private String buildPrompt(EvaluatorBatchInput batchInput) {
JSONObject prompt = new JSONObject();
prompt.put("[行动倾向]", batchInput.getTendency());
JSONArray memoryData = prompt.putArray("[相关记忆切片]");
for (EvaluatedSlice evaluatedSlice : batchInput.getActivatedSlices()) {
JSONObject memory = memoryData.addObject();
memory.put("[日期]", evaluatedSlice.getDate());
memory.put("[摘要]", evaluatedSlice.getSummary());
}
JSONObject availableActionData = prompt.putObject("[可用行动单元]");
availableActionData.putAll(batchInput.getAvailableActions());
return prompt.toString();
}
@Override @Override
public String modelKey() { public String modelKey() {
return "action_evaluator"; return "action_evaluator";

View File

@@ -5,12 +5,10 @@ import work.slhaf.partner.api.chat.pojo.Message;
import work.slhaf.partner.core.memory.pojo.EvaluatedSlice; import work.slhaf.partner.core.memory.pojo.EvaluatedSlice;
import java.util.List; import java.util.List;
import java.util.Map;
@Data @Data
public class EvaluatorBatchInput { public class EvaluatorBatchInput {
private List<Message> recentMessages; private List<Message> recentMessages;
private List<EvaluatedSlice> activatedSlices; private List<EvaluatedSlice> activatedSlices;
private Map<String, String> availableActions;
private String tendency; private String tendency;
} }

View File

@@ -1,10 +1,8 @@
package work.slhaf.partner.module.modules.action.planner.evaluator.entity; package work.slhaf.partner.module.modules.action.planner.evaluator.entity;
import lombok.Data; import lombok.Data;
import work.slhaf.partner.core.action.entity.ScheduledActionData; import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.action.entity.ActionType;
import java.util.List;
import java.util.Map;
@Data @Data
public class EvaluatorResult { public class EvaluatorResult {
@@ -12,13 +10,6 @@ public class EvaluatorResult {
private boolean needConfirm; private boolean needConfirm;
private ActionType type; private ActionType type;
private String scheduleContent; private String scheduleContent;
private ScheduledActionData.ScheduleType scheduleType; private ActionData actionData;
private Map<Integer, List<String>> primaryActionChain;
private String tendency; private String tendency;
private String reason;
private String description;
public enum ActionType {
IMMEDIATE, PLANNING
}
} }

View File

@@ -0,0 +1,4 @@
package work.slhaf.partner.module.modules.action.scheduler;
public class ActionDispatcher {
}

View File

@@ -0,0 +1,7 @@
package work.slhaf.partner.module.modules.action.scheduler;
/**
* 负责综合前置模块
*/
public class TaskScheduler {
}

View File

@@ -23,7 +23,10 @@ import work.slhaf.partner.module.modules.memory.selector.extractor.entity.Extrac
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext; import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@@ -130,7 +133,7 @@ public class MemorySelector extends PreRunningModule {
} }
@Override @Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) { protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
String userId = context.getUserId(); String userId = context.getUserId();
String dialogMapStr = memoryCapability.getDialogMapStr(); String dialogMapStr = memoryCapability.getDialogMapStr();
@@ -145,7 +148,7 @@ public class MemorySelector extends PreRunningModule {
String sliceStr = memoryCapability.getActivatedSlicesStr(userId); String sliceStr = memoryCapability.getActivatedSlicesStr(userId);
if (sliceStr != null && !sliceStr.isEmpty()) { if (sliceStr != null && !sliceStr.isEmpty()) {
map.put("[记忆切片] <你与最新一条消息的发送者的相关回忆, 不会与[记忆缓存]重复, 如果有重复你也可以指出来>", sliceStr); map.put("[记忆切片] <你与最新一条消息的发送者的相关回忆, 不会与[记忆缓存]重复, 如果有重复你也可以指出来()>", sliceStr);
} }
return map; return map;
} }

View File

@@ -119,11 +119,6 @@ public class MemoryUpdater extends PostRunningModule {
}); });
} }
@Override
protected boolean relyOnMessage() {
return true;
}
private void updateMemory() { private void updateMemory() {
log.debug("[MemoryUpdater] 记忆更新流程开始..."); log.debug("[MemoryUpdater] 记忆更新流程开始...");
tempMessage = new ArrayList<>(cognationCapability.getChatMessages()); tempMessage = new ArrayList<>(cognationCapability.getChatMessages());

View File

@@ -10,7 +10,6 @@ import work.slhaf.partner.module.common.module.PreRunningModule;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext; import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
@Slf4j @Slf4j
@Setter @Setter
@@ -25,7 +24,7 @@ public class PerceiveSelector extends PreRunningModule {
} }
@Override @Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) { protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
User user = perceiveCapability.getUser(context.getUserId()); User user = perceiveCapability.getUser(context.getUserId());
map.put("[关系] <你与最新聊天用户的关系>", user.getRelation()); map.put("[关系] <你与最新聊天用户的关系>", user.getRelation());

View File

@@ -70,11 +70,6 @@ public class PerceiveUpdater extends PostRunningModule {
}); });
} }
@Override
protected boolean relyOnMessage() {
return true;
}
private void runRelationExtractorAction(PartnerRunningFlowContext context, ReentrantLock userLock, User user) { private void runRelationExtractorAction(PartnerRunningFlowContext context, ReentrantLock userLock, User user) {
RelationExtractResult relationExtractResult = relationExtractor.execute(context); RelationExtractResult relationExtractResult = relationExtractor.execute(context);
userLock.lock(); userLock.lock();

View File

@@ -15,7 +15,6 @@ import work.slhaf.partner.runtime.interaction.data.context.subcontext.CoreContex
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@@ -59,7 +58,7 @@ public class PreprocessExecutor extends PreRunningModule {
@Override @Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) { protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
map.put("text", "这部分才是真正的用户输入内容, 就像你之前收到过的输入一样。但...不会是'同一个人'。"); map.put("text", "这部分才是真正的用户输入内容, 就像你之前收到过的输入一样。但...不会是'同一个人'。");
map.put("datetime", "本次用户输入对应的当前时间"); map.put("datetime", "本次用户输入对应的当前时间");

View File

@@ -7,7 +7,7 @@ import work.slhaf.partner.api.agent.runtime.interaction.data.AgentInputData;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
public class PartnerInputData extends AgentInputData { public class PartnerInputData extends AgentInputData {
protected String userNickName; private String userNickName;
protected String platform; private String platform;
protected boolean single; private boolean single;
} }

View File

@@ -1,5 +1,3 @@
package experimental;
import ai.djl.huggingface.tokenizers.Encoding; import ai.djl.huggingface.tokenizers.Encoding;
import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer; import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
import ai.onnxruntime.OnnxTensor; import ai.onnxruntime.OnnxTensor;

View File

@@ -1,5 +1,3 @@
package experimental;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.core.memory.pojo.MemoryResult; import work.slhaf.partner.core.memory.pojo.MemoryResult;

View File

@@ -1,5 +1,3 @@
package experimental
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.lang.String.join import java.lang.String.join
import java.util.regex.Pattern import java.util.regex.Pattern

View File

@@ -1,5 +1,3 @@
package experimental;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import work.slhaf.partner.api.chat.ChatClient; import work.slhaf.partner.api.chat.ChatClient;

View File

@@ -1,5 +1,3 @@
package experimental;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;

View File

@@ -1,41 +0,0 @@
package experimental;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpStatelessServerFeatures;
import io.modelcontextprotocol.server.McpStatelessSyncServer;
import io.modelcontextprotocol.spec.McpSchema;
import org.junit.jupiter.api.Test;
import work.slhaf.partner.common.mcp.InProcessMcpTransport;
import java.util.List;
public class InProcessMcpTransportTest {
@Test
void inProcessMcpTransportTest() {
InProcessMcpTransport.Pair pair = InProcessMcpTransport.pair();
InProcessMcpTransport clientSide = pair.clientSide();
InProcessMcpTransport serverSide = pair.serverSide();
McpStatelessSyncServer server = McpServer.sync(serverSide)
.capabilities(McpSchema.ServerCapabilities.builder().tools(true).build())
.build();
server.addTool(McpStatelessServerFeatures.SyncToolSpecification.builder()
.tool(McpSchema.Tool.builder().name("111").build()).callHandler((mcpTransportContext, callToolRequest) -> {
System.out.println(111);
return McpSchema.CallToolResult.builder().addContent(new McpSchema.TextContent("111")).build();
}).build());
McpSyncClient client = McpClient.sync(clientSide)
.build();
List<McpSchema.Tool> tools = client.listTools().tools();
McpSchema.Tool tool = tools.getFirst();
System.out.println(tool.toString());
McpSchema.CallToolResult callToolResult = client.callTool(McpSchema.CallToolRequest.builder().name(tool.name()).build());
System.out.println(callToolResult.content().toString());
client.close();
server.close();
}
}

View File

@@ -1,17 +0,0 @@
package experimental;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import org.junit.jupiter.api.Test;
public class NetTest {
@Test
void httpTest() {
HttpRequest request = HttpRequest.get("slhaf.work");
request.setConnectionTimeout(2);
request.setReadTimeout(2);
HttpResponse execute = request.execute();
System.out.println(execute.toString());
execute.close();
}
}

View File

@@ -1,48 +0,0 @@
package experimental;
import com.alibaba.fastjson2.JSONObject;
import org.junit.jupiter.api.Test;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.runner.LocalRunnerClient;
import work.slhaf.partner.core.action.runner.RunnerClient;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SystemTest {
@Test
void execTest() {
// exec("pwd");
// exec("ls", "-la");
String r = exec("pip", "st", "--format=freeze");
System.out.println(r);
}
private String exec(String... command) {
StringBuilder s = new StringBuilder();
ProcessBuilder processBuilder = new ProcessBuilder(command);
try {
Process process = processBuilder.start();
java.io.InputStream inputStream = process.getInputStream();
java.util.Scanner scanner = new java.util.Scanner(inputStream).useDelimiter("\\A");
if (scanner.hasNext()) {
s.append(scanner.next());
}
} catch (IOException e) {
e.printStackTrace();
}
return s.toString();
}
@Test
void localRunnerClientTest() {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
RunnerClient client = new LocalRunnerClient(existedMetaActions, executor, null);
JSONObject res = client.listSysDependencies();
System.out.println(res.toString());
}
}

View File

@@ -1,24 +0,0 @@
import sys
def parse_args(argv):
"""
将 --key=value 解析成 dict
"""
params = {}
for arg in argv:
if arg.startswith("--") and "=" in arg:
key, value = arg[2:].split("=", 1)
params[key] = value
return params
def main():
params = parse_args(sys.argv[1:])
name = params.get("name", "World")
print(f"Hello {name}!")
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More