mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-14 09:43:03 +08:00
Compare commits
182 Commits
fix
...
feature/Ac
| Author | SHA1 | Date | |
|---|---|---|---|
| d381a97731 | |||
| 940beb2587 | |||
| 69d9f04f11 | |||
| e2bd9eb0af | |||
| 9ec03c4c95 | |||
| ecbbbc9954 | |||
| a5d26769e8 | |||
| 2db1bdf3e9 | |||
| 656d6b65e3 | |||
| 7c46f1d1ff | |||
| 406b4250aa | |||
| eab3d00fe8 | |||
| d47e9fbf95 | |||
| 4b77f26e7b | |||
| 650f9b27a1 | |||
| 9f479c5f6f | |||
| 227c735667 | |||
| b05b665960 | |||
| 882ec43f2b | |||
| 7cb565fd1b | |||
| 84b96b6645 | |||
| 2169376062 | |||
| 9bff74c8c7 | |||
| 76c9c27532 | |||
| 8524ca6f9f | |||
| 7dd2104689 | |||
| 6ba5784a7f | |||
| cdea8d6322 | |||
| 8ca2b9998d | |||
| d098b28f31 | |||
| 98e4d4cf1b | |||
| 70489e57f7 | |||
| a43c87006e | |||
| be43b7eec6 | |||
| 3bc2ce839a | |||
| fe5a366527 | |||
| 9f724cee5d | |||
| ad58b83020 | |||
| c9b64fec2a | |||
| 0eb4765235 | |||
| 050c39cbc7 | |||
| 08100aea8a | |||
| 2cd0774834 | |||
| 12df938d85 | |||
| 277c0d437f | |||
| 6b861f4b77 | |||
| d33b6617c1 | |||
| a1dcf4a6fa | |||
| 9c38719514 | |||
| 33df0fa017 | |||
| 08bda84471 | |||
| 76da3c29f8 | |||
| 558b589830 | |||
| 80d7c283c5 | |||
| b0bb40c5f0 | |||
| eec8f71096 | |||
| fbd30d1a96 | |||
| 346f925b66 | |||
| 04e8d9e531 | |||
| 63d1552de2 | |||
| 77eb9b92a4 | |||
| a1b4743eeb | |||
| 0768cddd2d | |||
| 75145cc547 | |||
| d1ca1cda7d | |||
| fac6609d6b | |||
| dce8825e58 | |||
| cd641ac8dd | |||
| 5ffdab9e4a | |||
| 830503eee4 | |||
| 96e74ec877 | |||
| 420d51af15 | |||
| 8ead306b7b | |||
| c793851107 | |||
| fb5cabc747 | |||
| c5f6c4e0ae | |||
| 200c0f3f13 | |||
| fdf398b86e | |||
| 774e2b6cd5 | |||
| 837a4c92d1 | |||
| ddd999d47b | |||
| 9694a022c7 | |||
| 31968c7076 | |||
| abec141e4e | |||
| cdb6ae9d01 | |||
| dd8d86d3c4 | |||
| 99b42620d0 | |||
| 70b8335d49 | |||
| 8ca475beeb | |||
| 4f36c0dd2d | |||
| 00993bd763 | |||
| a0bca668cb | |||
| c6118c41b0 | |||
| 872d21170a | |||
| 44ab6cfac8 | |||
| ec30ac1922 | |||
| 74b6d0c653 | |||
| de462866b2 | |||
| 4ea8926363 | |||
| 04c98c7856 | |||
| 0757856187 | |||
| 19ec93f248 | |||
| 5877b9e80d | |||
| 5db0b5fad1 | |||
| 623a86daab | |||
| 64f24d3fc3 | |||
| 3097efe453 | |||
| b58eeffd2f | |||
| 62cec79005 | |||
| 03a5935107 | |||
| 0ecaec0545 | |||
| 74f2c6c950 | |||
| f35a467ebc | |||
| 64b907707a | |||
| a6e33edc7a | |||
| 94ef79c67d | |||
| a222015abb | |||
| 1c562f0e7b | |||
| 89535a6b1c | |||
| 6e90bc8d67 | |||
| 0e741802d1 | |||
| db3435fccf | |||
| e3294ec302 | |||
| bf99e01b51 | |||
| 1bd23b20c4 | |||
| 442dd55686 | |||
| abe5dd5251 | |||
| 1f737c0e29 | |||
| d41074c814 | |||
| 621441601a | |||
| e00d77f076 | |||
| d614ac0b15 | |||
| 592e2604d9 | |||
| dcbd2c6569 | |||
| 476acb0641 | |||
| 88a14f36b2 | |||
| 05d1fff125 | |||
| 49a4c9eb01 | |||
| 9e76c3e7ad | |||
| 9762739138 | |||
| 1f5509c17d | |||
| ed042cfffa | |||
| 128592e23c | |||
| 5ba36ed3e8 | |||
| 4dea948f82 | |||
| dc4074715e | |||
| 225802c1a8 | |||
| e851e33b2e | |||
| cb28a5b068 | |||
| ad58567ada | |||
| 0eee12d685 | |||
| 1e6ff1b30c | |||
| 0413fc281d | |||
| 8a7681ae31 | |||
| 1947f25ed6 | |||
| 488246525f | |||
| 534dcd5ade | |||
| ad58c0cc7c | |||
| d546148d69 | |||
| bf2d5ac707 | |||
| 628234f6e2 | |||
| 4b852e0049 | |||
| 6e3deced77 | |||
| 6a351413a1 | |||
| ad973d4230 | |||
| 1d315a9b62 | |||
| 4e32129b31 | |||
| 3f59719e16 | |||
| c548cceec6 | |||
| b3098310b4 | |||
| f48d559a7b | |||
| 14a57f0be6 | |||
| 264cdb09e5 | |||
| a1520f117b | |||
| c3ca4145b8 | |||
| 746fda1a5e | |||
| ec4fbb7f19 | |||
| f9c3cacfea | |||
| e35e18f3b7 | |||
| 83832d2060 | |||
| 4757425a15 | |||
| 21b3a0e846 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -55,3 +55,6 @@ build/
|
|||||||
/data/
|
/data/
|
||||||
/generated-classes/
|
/generated-classes/
|
||||||
/.idea/copilot.data.migration.ask2agent.xml
|
/.idea/copilot.data.migration.ask2agent.xml
|
||||||
|
/Partner-Main/data/
|
||||||
|
/AGENTS.md
|
||||||
|
/.serena/
|
||||||
|
|||||||
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AgentMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AskMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?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
3
.idea/encodings.xml
generated
@@ -3,10 +3,13 @@
|
|||||||
<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
15
.idea/misc.xml
generated
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="EntryPointsManager">
|
<component name="EntryPointsManager">
|
||||||
<list size="14">
|
<list size="15">
|
||||||
<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" />
|
||||||
@@ -11,11 +10,12 @@
|
|||||||
<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.BeforeExecute" />
|
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" />
|
||||||
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
|
||||||
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
||||||
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
||||||
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
||||||
|
<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,6 +27,7 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -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()));
|
||||||
((Module) this).setModel(model);
|
setModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
default void updateModelSettings(ChatClient newChatClient) {
|
default void updateModelSettings(ChatClient newChatClient) {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
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;
|
||||||
@@ -13,6 +15,7 @@ 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 {
|
||||||
@@ -34,6 +37,8 @@ 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);
|
||||||
|
|
||||||
@@ -53,17 +58,26 @@ public class ChatClient {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
|
|
||||||
ChatResponse finalResponse;
|
ChatResponse finalResponse;
|
||||||
|
|
||||||
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
|
try {
|
||||||
finalResponse = ChatResponse.builder()
|
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
|
||||||
.type(ChatConstant.Response.SUCCESS)
|
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
|
||||||
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
|
finalResponse = ChatResponse.builder()
|
||||||
.usageBean(primaryChatResponse.getUsage())
|
.status(ChatConstant.ResponseStatus.SUCCESS)
|
||||||
.build();
|
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
|
||||||
|
.usageBean(primaryChatResponse.getUsage())
|
||||||
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ public class ChatConstant {
|
|||||||
public static final String ASSISTANT = "assistant";
|
public static final String ASSISTANT = "assistant";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Response {
|
public enum ResponseStatus {
|
||||||
public static final String SUCCESS = "success";
|
SUCCESS, FAILED
|
||||||
public static final String ERROR = "error";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ 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 String type;
|
private ChatConstant.ResponseStatus status;
|
||||||
private String message;
|
private String message;
|
||||||
private PrimaryChatResponse.UsageBean usageBean;
|
private PrimaryChatResponse.UsageBean usageBean;
|
||||||
}
|
}
|
||||||
|
|||||||
28
Partner-Common/pom.xml
Normal file
28
Partner-Common/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
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
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>work.slhaf</groupId>
|
<groupId>work.slhaf</groupId>
|
||||||
@@ -26,7 +26,11 @@
|
|||||||
<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>
|
||||||
<scope>test</scope>
|
</dependency>
|
||||||
|
<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>
|
||||||
@@ -44,6 +48,38 @@
|
|||||||
<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>
|
||||||
@@ -66,7 +102,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<transformers>
|
<transformers>
|
||||||
<transformer
|
<transformer
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
<mainClass>work.slhaf.partner.Main</mainClass>
|
<mainClass>work.slhaf.partner.Main</mainClass>
|
||||||
</transformer>
|
</transformer>
|
||||||
</transformers>
|
</transformers>
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,10 +13,11 @@ 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 {
|
||||||
@@ -53,7 +54,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(STORAGE_DIR));
|
Files.createDirectories(Path.of(MEMORY_DATA));
|
||||||
try {
|
try {
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile()));
|
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile()));
|
||||||
oos.writeObject(this);
|
oos.writeObject(this);
|
||||||
@@ -78,12 +79,12 @@ public abstract class PartnerCore<T extends PartnerCore<T>> extends PersistableO
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Path getFilePath(String s) {
|
private Path getFilePath(String s) {
|
||||||
return Paths.get(STORAGE_DIR, s + "-" + getCoreKey() + ".memory");
|
return Paths.get(MEMORY_DATA, s + "-" + getCoreKey() + ".memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createStorageDirectory() {
|
private void createStorageDirectory() {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(Paths.get(STORAGE_DIR));
|
Files.createDirectories(Paths.get(MEMORY_DATA));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("[{}]创建存储目录失败: {}", getCoreKey(), e.getMessage());
|
log.error("[{}]创建存储目录失败: {}", getCoreKey(), e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,59 @@
|
|||||||
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.CacheAdjustData;
|
import work.slhaf.partner.core.action.entity.ActionData;
|
||||||
|
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);
|
|
||||||
|
|
||||||
List<MetaActionInfo> popPreparedAction(String userId);
|
void putAction(@NonNull ActionData actionData);
|
||||||
|
|
||||||
List<MetaActionInfo> popPendingAction(String userId);
|
Set<ActionData> listActions(@Nullable ActionData.ActionStatus actionStatus, @Nullable String source);
|
||||||
|
|
||||||
List<MetaActionInfo> listPreparedAction(String userId);
|
List<ActionData> popPendingAction(String userId);
|
||||||
|
|
||||||
List<MetaActionInfo> listPendingAction(String userId);
|
List<ActionData> listPendingAction(String userId);
|
||||||
|
|
||||||
void putPendingActions(String userId, MetaActionInfo metaActionInfo);
|
void putPendingActions(String userId, ActionData actionData);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +1,115 @@
|
|||||||
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 work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
|
import lombok.val;
|
||||||
|
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.ActionCacheData;
|
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.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.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.concurrent.*;
|
||||||
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")
|
||||||
@Capability(value = "action")
|
@CapabilityCore(value = "action")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ActionCore extends PartnerCore<ActionCore> {
|
public class ActionCore extends PartnerCore<ActionCore> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对应本次交互即将执行或将要放置在行动池的预备任务,因此将以本次交互的uuid为键,其起到的作用相当于暂时的模块上下文
|
* 持久行动池
|
||||||
*/
|
*/
|
||||||
private HashMap<String, List<MetaActionInfo>> preparedActions = new HashMap<>();
|
private CopyOnWriteArraySet<ActionData> actionPool = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待确认任务,以userId区分不同用户,因为需要跨请求确认
|
* 待确认任务,以userId区分不同用户,因为需要跨请求确认
|
||||||
*/
|
*/
|
||||||
private HashMap<String, List<MetaActionInfo>> pendingActions = new HashMap<>();
|
private HashMap<String, List<ActionData>> pendingActions = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语义缓存与行为倾向映射
|
* 语义缓存与行为倾向映射
|
||||||
*/
|
*/
|
||||||
private List<ActionCacheData> actionCache = new ArrayList<>();
|
private List<ActionCacheData> actionCache = new ArrayList<>();
|
||||||
|
|
||||||
private Lock cacheLock = new ReentrantLock();
|
private final Lock cacheLock = new ReentrantLock();
|
||||||
|
|
||||||
private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
// 由于当前的执行器逻辑实现,平台线程池大小不得小于 2,这里规定为最小为 4
|
||||||
|
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 synchronized void putPendingActions(String userId, MetaActionInfo metaActionInfo) {
|
public void putAction(@NonNull ActionData actionData) {
|
||||||
|
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<MetaActionInfo> temp = new ArrayList<>();
|
List<ActionData> temp = new ArrayList<>();
|
||||||
temp.add(metaActionInfo);
|
temp.add(actionData);
|
||||||
return temp;
|
return temp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public synchronized List<MetaActionInfo> popPendingAction(String userId) {
|
public synchronized List<ActionData> popPendingAction(String userId) {
|
||||||
List<MetaActionInfo> infos = pendingActions.get(userId);
|
List<ActionData> infos = pendingActions.get(userId);
|
||||||
pendingActions.remove(userId);
|
pendingActions.remove(userId);
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public synchronized void putPreparedAction(String uuid, MetaActionInfo metaActionInfo) {
|
public List<ActionData> listPendingAction(String userId) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +125,11 @@ 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) return null;
|
if (vector == null)
|
||||||
//与现有缓存比对,将匹配到的收集并返回
|
return null;
|
||||||
|
// 与现有缓存比对,将匹配到的收集并返回
|
||||||
return actionCache.parallelStream()
|
return actionCache.parallelStream()
|
||||||
.filter(ActionCacheData::isActivated)
|
.filter(ActionCacheData::isActivated)
|
||||||
.filter(data -> {
|
.filter(data -> {
|
||||||
@@ -136,9 +161,188 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient));
|
platformExecutor.execute(() -> adjustMatchAndPassed(matchAndPassed, inputVector, input, vectorClient));
|
||||||
executor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient));
|
platformExecutor.execute(() -> adjustMatchNotPassed(matchNotPassed, vectorClient));
|
||||||
executor.execute(() -> adjustNotMatchPassed(notMatchPassed, inputVector, input, vectorClient));
|
platformExecutor.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,9 +352,10 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
* @param inputVector 本次输入内容的语义向量
|
* @param inputVector 本次输入内容的语义向量
|
||||||
* @param vectorClient 向量客户端
|
* @param vectorClient 向量客户端
|
||||||
*/
|
*/
|
||||||
private void adjustMatchAndPassed(List<CacheAdjustMetaData> matchAndPassed, float[] inputVector, String input, VectorClient vectorClient) {
|
private void adjustMatchAndPassed(List<CacheAdjustMetaData> matchAndPassed, float[] inputVector, String input,
|
||||||
|
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) {
|
||||||
@@ -169,7 +374,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) {
|
||||||
@@ -189,13 +394,13 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
/**
|
/**
|
||||||
* 针对未命中但评估通过的缓存做出调整:
|
* 针对未命中但评估通过的缓存做出调整:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <h3>如果存在缓存条目</h3>
|
* <h3>如果存在缓存条目</h3>
|
||||||
* <li>
|
* <li>
|
||||||
* 若已生效,但此时未匹配到则说明尚未生效或者阈值、向量{@link ActionCacheData#getInputVector()}存在问题,调低阈值,同时带权移动平均
|
* 若已生效,但此时未匹配到则说明尚未生效或者阈值、向量{@link ActionCacheData#getInputVector()}存在问题,调低阈值,同时带权移动平均
|
||||||
* </li>
|
* </li>
|
||||||
* <li>
|
* <li>
|
||||||
* 若未生效,则只增加计数并带权移动平均
|
* 若未生效,则只增加计数并带权移动平均
|
||||||
* </li>
|
* </li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* 如果不存在缓存条目,则新增并填充字段
|
* 如果不存在缓存条目,则新增并填充字段
|
||||||
*
|
*
|
||||||
@@ -204,9 +409,10 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
* @param input 本次输入内容
|
* @param input 本次输入内容
|
||||||
* @param vectorClient 向量客户端
|
* @param vectorClient 向量客户端
|
||||||
*/
|
*/
|
||||||
private void adjustNotMatchPassed(List<CacheAdjustMetaData> notMatchPassed, float[] inputVector, String input, VectorClient vectorClient) {
|
private void adjustNotMatchPassed(List<CacheAdjustMetaData> notMatchPassed, float[] inputVector, String input,
|
||||||
|
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);
|
||||||
@@ -232,4 +438,9 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
protected String getCoreKey() {
|
protected String getCoreKey() {
|
||||||
return "action-core";
|
return "action-core";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ExecutorType {
|
||||||
|
VIRTUAL, PLATFORM
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
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()
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package work.slhaf.partner.core.action.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ActionFileMetaData {
|
||||||
|
private String content;
|
||||||
|
private String name;
|
||||||
|
private String ext;
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
|
||||||
|
|
||||||
public enum ActionStatus {
|
|
||||||
SUCCESS, FAILED, EXECUTING, WAITING, PREPARE
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public class ImmediateActionInfo extends MetaActionInfo{
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
package work.slhaf.partner.core.action.entity;
|
||||||
|
|
||||||
public enum ActionType {
|
public class McpData {
|
||||||
IMMEDIATE, PLANNING
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
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 abstract class MetaActionInfo {
|
public class MetaActionInfo {
|
||||||
protected String uuid;
|
private boolean io;
|
||||||
protected String tendency;
|
|
||||||
protected ActionStatus status;
|
private Map<String, Object> params;
|
||||||
protected ActionData actionData;
|
private String description;
|
||||||
protected String Result;
|
private List<String> tags;
|
||||||
|
|
||||||
|
private List<String> preActions;
|
||||||
|
private List<String> postActions;
|
||||||
|
/**
|
||||||
|
* 是否严格依赖前置行动的成功执行,若为true且前置行动失败则不执行该行动,后置任务多为触发式。默认即执行。
|
||||||
|
*/
|
||||||
|
private boolean strictDependencies;
|
||||||
|
|
||||||
|
private JSONObject responseSchema;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
package work.slhaf.partner.core.action.entity.cache;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import work.slhaf.partner.common.vector.VectorClient;
|
import work.slhaf.partner.common.vector.VectorClient;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
package work.slhaf.partner.core.action.entity.cache;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
package work.slhaf.partner.core.action.entity.cache;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package work.slhaf.partner.core.action.exception;
|
||||||
|
|
||||||
|
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
||||||
|
|
||||||
|
public class ActionInitFailedException extends AgentLaunchFailedException {
|
||||||
|
public ActionInitFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionInitFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package work.slhaf.partner.core.action.exception;
|
||||||
|
|
||||||
|
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
|
||||||
|
|
||||||
|
public class ActionLoadFailedException extends AgentRuntimeException {
|
||||||
|
public ActionLoadFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionLoadFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ 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);
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ 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();
|
||||||
|
|||||||
@@ -198,9 +198,9 @@ public class MemoryCore extends PartnerCore<MemoryCore> {
|
|||||||
//尝试更新缓存
|
//尝试更新缓存
|
||||||
updateCache(topicPath, memoryResult);
|
updateCache(topicPath, memoryResult);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[CoordinatedManager] selectMemory error: ", e);
|
log.error("[{}] selectMemory error: ", getCoreKey(), e);
|
||||||
log.error("[CoordinatedManager] 路径: {}", topicPathStr);
|
log.error("[{}] 路径: {}", getCoreKey(), topicPathStr);
|
||||||
log.error("[CoordinatedManager] 主题树: {}", getTopicTree());
|
log.error("[{}] 主题树: {}", getCoreKey(), 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("[CoordinatedManager] 已更新激活切片, userId: {}", userId);
|
log.debug("[{}] 已更新激活切片, userId: {}", getCoreKey(), userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
@@ -488,7 +488,7 @@ public class MemoryCore extends PartnerCore<MemoryCore> {
|
|||||||
return targetParentNode;
|
return targetParentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCacheCounter(List<String> topicPath) {
|
private 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);
|
||||||
|
|||||||
@@ -82,12 +82,14 @@ public class PerceiveCore extends PartnerCore<PerceiveCore> {
|
|||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public void updateUser(User temp) {
|
public void updateUser(User temp) {
|
||||||
|
usersLock.lock();
|
||||||
User user = getUser(temp.getUuid());
|
User user = getUser(temp.getUuid());
|
||||||
user.setRelation(temp.getRelation());
|
user.setRelation(temp.getRelation());
|
||||||
user.setImpressions(temp.getImpressions());
|
user.setImpressions(temp.getImpressions());
|
||||||
user.setAttitude(temp.getAttitude());
|
user.setAttitude(temp.getAttitude());
|
||||||
user.setStaticMemory(temp.getStaticMemory());
|
user.setStaticMemory(temp.getStaticMemory());
|
||||||
user.updateRelationChange(user.getRelationChange());
|
user.updateRelationChange(user.getRelationChange());
|
||||||
|
usersLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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.HashMap;
|
import java.util.Map;
|
||||||
|
|
||||||
@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 HashMap<String,String> appendedPrompt;
|
private Map<String, String> appendedPrompt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
if (!trigger && relyOnMessage()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
doExecute(context);
|
doExecute(context);
|
||||||
@@ -16,4 +16,5 @@ public abstract class PostRunningModule extends AgentRunningModule<PartnerRunnin
|
|||||||
|
|
||||||
public abstract void doExecute(PartnerRunningFlowContext context);
|
public abstract void doExecute(PartnerRunningFlowContext context);
|
||||||
|
|
||||||
|
protected abstract boolean relyOnMessage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.HashMap;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前置模块抽象类
|
* 前置模块抽象类
|
||||||
@@ -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());
|
||||||
HashMap<String, String> map = getPromptDataMap(context);
|
Map<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 HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context);
|
protected abstract Map<String, String> getPromptDataMap(PartnerRunningFlowContext context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于在CoreModule接收到的模块Prompt中标识模块名称
|
* 用于在CoreModule接收到的模块Prompt中标识模块名称
|
||||||
@@ -40,5 +40,4 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
|
|||||||
|
|
||||||
protected abstract void doExecute(PartnerRunningFlowContext context);
|
protected abstract void doExecute(PartnerRunningFlowContext context);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,322 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
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>)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
|
||||||
|
|
||||||
|
public record HistoryAction(String actionKey, String description, String result) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
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(
|
||||||
|
"[识别状态] <是否识别到干预已存在行动的意图>", "未识别到干预意图",
|
||||||
|
"[干预行动] <将对已存在行动做出的行为>", "无行动"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package work.slhaf.partner.module.modules.action.interventor.entity;
|
||||||
|
|
||||||
|
public enum InterventionType {
|
||||||
|
/**
|
||||||
|
* 追加行动: 追加至指定行动链序列之后才执行
|
||||||
|
*/
|
||||||
|
APPEND,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入行动: 指定行动链序列执行过程中即时新增并执行
|
||||||
|
*/
|
||||||
|
INSERT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重建行动: 重建指定行动链序列之后的所有行动内容
|
||||||
|
*/
|
||||||
|
REBUILD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除行动: 删除指定行动链序列上的指定行动单元
|
||||||
|
*/
|
||||||
|
DELETE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消行动链: 中断并取消指定行动链的执行
|
||||||
|
*/
|
||||||
|
CANCEL
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package work.slhaf.partner.module.modules.action.interventor.recognizer.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MetaRecognizerResult {
|
||||||
|
private boolean ok;
|
||||||
|
private String intervention;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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<>();
|
||||||
|
}
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
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;
|
||||||
@@ -25,15 +29,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.ArrayList;
|
import java.util.*;
|
||||||
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 {
|
||||||
|
|
||||||
@@ -53,21 +57,25 @@ public class ActionPlanner extends PreRunningModule {
|
|||||||
@InjectModule
|
@InjectModule
|
||||||
private ActionConfirmer actionConfirmer;
|
private ActionConfirmer actionConfirmer;
|
||||||
|
|
||||||
private InteractionThreadPoolExecutor executor;
|
private ExecutorService executor;
|
||||||
private ActionAssemblyHelper assemblyHelper;
|
|
||||||
|
private final ActionAssemblyHelper assemblyHelper = new ActionAssemblyHelper();
|
||||||
|
|
||||||
@Init
|
@Init
|
||||||
public void init() {
|
public void init() {
|
||||||
executor = InteractionThreadPoolExecutor.getInstance();
|
executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
|
||||||
assemblyHelper = new ActionAssemblyHelper();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doExecute(PartnerRunningFlowContext context) {
|
protected void doExecute(PartnerRunningFlowContext context) {
|
||||||
List<Callable<Void>> tasks = new ArrayList<>();
|
try {
|
||||||
addConfirmTask(tasks, context);
|
List<Callable<Void>> tasks = new ArrayList<>();
|
||||||
addNewActionTask(tasks, context);
|
addConfirmTask(tasks, context);
|
||||||
executor.invokeAll(tasks);
|
addNewActionTask(tasks, context);
|
||||||
|
executor.invokeAll(tasks);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("执行异常", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,14 +92,15 @@ 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); // 并发操作均为访问
|
||||||
setupActionInfo(evaluatorResults, context);
|
putActionData(evaluatorResults, context);
|
||||||
|
updateTendencyCache(evaluatorResults, context.getInput(), extractorResult);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterExecute
|
private void updateTendencyCache(List<EvaluatorResult> evaluatorResults, String input,
|
||||||
private void updateTendencyCache(List<EvaluatorResult> evaluatorResults, String input, ExtractorResult extractorResult) {
|
ExtractorResult extractorResult) {
|
||||||
if (!VectorClient.status) {
|
if (!VectorClient.status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -123,72 +132,69 @@ 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);
|
||||||
setupPendingActionInfo(context, result);
|
setupConfirmedActionInfo(context, result);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPendingActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) {
|
private void setupConfirmedActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) {
|
||||||
//TODO 需考虑未确认任务的失效或者拒绝时机
|
// TODO 需考虑未确认任务的失效或者拒绝时机,在action core中实现
|
||||||
List<String> uuids = result.getUuids();
|
List<String> uuids = result.getUuids();
|
||||||
if (uuids == null) {
|
if (uuids == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String contextUuid = context.getUuid();
|
List<ActionData> pendingActions = actionCapability.popPendingAction(context.getUserId());
|
||||||
List<MetaActionInfo> pendingActions = actionCapability.popPendingAction(context.getUserId());
|
for (ActionData actionData : pendingActions) {
|
||||||
for (MetaActionInfo actionInfo : pendingActions) {
|
if (uuids.contains(actionData.getUuid())) {
|
||||||
if (uuids.contains(actionInfo.getUuid())) {
|
actionCapability.putAction(actionData);
|
||||||
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) {
|
||||||
MetaActionInfo metaActionInfo = assemblyHelper.buildMetaActionInfo(evaluatorResult);
|
ActionData actionData = assemblyHelper.buildActionData(evaluatorResult, context.getUserId());
|
||||||
if (evaluatorResult.isNeedConfirm()) {
|
if (evaluatorResult.isNeedConfirm()) {
|
||||||
actionCapability.putPendingActions(context.getUserId(), metaActionInfo);
|
actionCapability.putPendingActions(context.getUserId(), actionData);
|
||||||
} else {
|
} else {
|
||||||
actionCapability.putPreparedAction(context.getUuid(), metaActionInfo);
|
actionCapability.putAction(actionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
|
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
|
||||||
HashMap<String, String> map = new HashMap<>();
|
HashMap<String, String> map = new HashMap<>();
|
||||||
setupPendingActions(map, context.getUserId());
|
String userId = context.getUserId();
|
||||||
setupPreparedActions(map, context.getUuid());
|
setupPendingActions(map, userId);
|
||||||
|
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<MetaActionInfo> actionInfos = actionCapability.listPendingAction(userId);
|
List<ActionData> actionData = actionCapability.listPendingAction(userId);
|
||||||
if (actionInfos == null || actionInfos.isEmpty()) {
|
if (actionData == null || actionData.isEmpty()) {
|
||||||
map.put("[待确认行动] <待确认行动信息>", "无待确认行动");
|
map.put("[待确认行动] <等待用户确认的行动信息>", "无待确认行动");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < actionInfos.size(); i++) {
|
for (int i = 0; i < actionData.size(); i++) {
|
||||||
map.put("[待确认行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
|
map.put("[待确认行动 " + (i + 1) + " ] <等待用户确认的行动信息>", generateActionStr(actionData.get(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPreparedActions(HashMap<String, String> map, String uuid) {
|
private void setupPreparedActions(HashMap<String, String> map, String userId) {
|
||||||
List<MetaActionInfo> actionInfos = actionCapability.listPreparedAction(uuid);
|
val preparedActions = actionCapability.listActions(ActionData.ActionStatus.PREPARE, userId).stream().toList();
|
||||||
if (actionInfos == null || actionInfos.isEmpty()) {
|
if (preparedActions.isEmpty()) {
|
||||||
map.put("[预备行动] <预备行动信息>", "无预备行动");
|
map.put("[预备行动] <预备执行或放入计划池的行动信息>", "无预备行动");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < actionInfos.size(); i++) {
|
for (int i = 0; i < preparedActions.size(); i++) {
|
||||||
map.put("[预备行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
|
map.put("[预备行动 " + (i + 1) + " ] <预备执行或放入计划池的行动信息>", generateActionStr(preparedActions.get(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateActionStr(MetaActionInfo metaActionInfo) {
|
private String generateActionStr(ActionData actionData) {
|
||||||
ActionData actionData = metaActionInfo.getActionData();
|
return "<行动倾向>" + " : " + actionData.getTendency() +
|
||||||
return "<行动倾向>" + " : " + metaActionInfo.getTendency() +
|
|
||||||
"<行动原因>" + " : " + actionData.getReason() +
|
"<行动原因>" + " : " + actionData.getReason() +
|
||||||
"<工具描述>" + " : " + actionData.getDescription();
|
"<工具描述>" + " : " + actionData.getDescription();
|
||||||
}
|
}
|
||||||
@@ -198,7 +204,7 @@ public class ActionPlanner extends PreRunningModule {
|
|||||||
return "[行动模块]";
|
return "[行动模块]";
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ActionAssemblyHelper {
|
private final class ActionAssemblyHelper {
|
||||||
private ActionAssemblyHelper() {
|
private ActionAssemblyHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,31 +231,109 @@ public class ActionPlanner extends PreRunningModule {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetaActionInfo buildMetaActionInfo(EvaluatorResult evaluatorResult) {
|
private ActionData buildActionData(EvaluatorResult evaluatorResult, String userId) {
|
||||||
|
Map<Integer, List<MetaAction>> actionChain = getActionChain(evaluatorResult);
|
||||||
return switch (evaluatorResult.getType()) {
|
return switch (evaluatorResult.getType()) {
|
||||||
case PLANNING -> {
|
case PLANNING -> new ScheduledActionData(
|
||||||
ScheduledActionInfo actionInfo = new ScheduledActionInfo();
|
evaluatorResult.getTendency(),
|
||||||
actionInfo.setActionData(evaluatorResult.getActionData());
|
actionChain,
|
||||||
actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
|
evaluatorResult.getReason(),
|
||||||
actionInfo.setStatus(ActionStatus.PREPARE);
|
evaluatorResult.getDescription(),
|
||||||
actionInfo.setUuid(UUID.randomUUID().toString());
|
userId,
|
||||||
yield actionInfo;
|
evaluatorResult.getScheduleType(),
|
||||||
}
|
evaluatorResult.getScheduleContent()
|
||||||
case IMMEDIATE -> {
|
);
|
||||||
ImmediateActionInfo actionInfo = new ImmediateActionInfo();
|
case IMMEDIATE -> new ImmediateActionData(
|
||||||
actionInfo.setActionData(evaluatorResult.getActionData());
|
evaluatorResult.getTendency(),
|
||||||
actionInfo.setStatus(ActionStatus.PREPARE);
|
actionChain,
|
||||||
actionInfo.setUuid(UUID.randomUUID().toString());
|
evaluatorResult.getReason(),
|
||||||
yield actionInfo;
|
evaluatorResult.getDescription(),
|
||||||
}
|
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<MetaActionInfo> pendingActions = actionCapability.listPendingAction(context.getUserId());
|
List<ActionData> pendingActions = actionCapability.listPendingAction(context.getUserId());
|
||||||
confirmerInput.setActionInfos(pendingActions);
|
confirmerInput.setActionData(pendingActions);
|
||||||
return confirmerInput;
|
return confirmerInput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,81 @@
|
|||||||
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) {
|
||||||
return null;
|
List<ActionData> actionDataList = data.getActionData();
|
||||||
|
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
|
||||||
|
|||||||
@@ -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.MetaActionInfo;
|
import work.slhaf.partner.core.action.entity.ActionData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ConfirmerInput {
|
public class ConfirmerInput {
|
||||||
private String input;
|
private String input;
|
||||||
private List<MetaActionInfo> actionInfos;
|
private List<ActionData> actionData;
|
||||||
private List<Message> recentMessages;
|
private List<Message> recentMessages;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ 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;
|
private List<String> uuids = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,33 @@
|
|||||||
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
|
||||||
@@ -30,7 +39,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) {
|
||||||
@@ -39,12 +48,11 @@ 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(JSONObject.toJSONString(batchInput));
|
ChatResponse response = this.singleChat(buildPrompt(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;
|
||||||
@@ -59,11 +67,32 @@ 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";
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
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.ActionData;
|
import work.slhaf.partner.core.action.entity.ScheduledActionData;
|
||||||
import work.slhaf.partner.core.action.entity.ActionType;
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class EvaluatorResult {
|
public class EvaluatorResult {
|
||||||
@@ -10,6 +12,13 @@ public class EvaluatorResult {
|
|||||||
private boolean needConfirm;
|
private boolean needConfirm;
|
||||||
private ActionType type;
|
private ActionType type;
|
||||||
private String scheduleContent;
|
private String scheduleContent;
|
||||||
private ActionData actionData;
|
private ScheduledActionData.ScheduleType scheduleType;
|
||||||
|
private Map<Integer, List<String>> primaryActionChain;
|
||||||
private String tendency;
|
private String tendency;
|
||||||
|
private String reason;
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public enum ActionType {
|
||||||
|
IMMEDIATE, PLANNING
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
package work.slhaf.partner.module.modules.action.scheduler;
|
|
||||||
|
|
||||||
public class ActionDispatcher {
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package work.slhaf.partner.module.modules.action.scheduler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 负责综合前置模块
|
|
||||||
*/
|
|
||||||
public class TaskScheduler {
|
|
||||||
}
|
|
||||||
@@ -23,10 +23,7 @@ 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.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@@ -133,7 +130,7 @@ public class MemorySelector extends PreRunningModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
|
protected Map<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();
|
||||||
@@ -148,7 +145,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ 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());
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ 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
|
||||||
@@ -24,7 +25,7 @@ public class PerceiveSelector extends PreRunningModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
|
protected Map<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());
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ 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();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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
|
||||||
@@ -58,7 +59,7 @@ public class PreprocessExecutor extends PreRunningModule {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
|
protected Map<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", "本次用户输入对应的当前时间");
|
||||||
|
|||||||
@@ -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 {
|
||||||
private String userNickName;
|
protected String userNickName;
|
||||||
private String platform;
|
protected String platform;
|
||||||
private boolean single;
|
protected boolean single;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Partner-Main/src/test/java/experimental/NetTest.java
Normal file
17
Partner-Main/src/test/java/experimental/NetTest.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
48
Partner-Main/src/test/java/experimental/SystemTest.java
Normal file
48
Partner-Main/src/test/java/experimental/SystemTest.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user