推进 ActionPlanner: 新增行动确认机制,将与原‘提取-评估’流程并发执行; 将繁杂的装配逻辑封装在内部类ActionAssemblyHelper

# Conflicts:
#	Partner-Main/src/main/java/work/slhaf/partner/core/cache/CacheCapability.java
#	Partner-Main/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java
#	Partner-Main/src/main/java/work/slhaf/partner/module/modules/memory/selector/MemorySelector.java
This commit is contained in:
2025-10-14 22:35:43 +08:00
parent 2d052442b1
commit 8c43d6594f
16 changed files with 295 additions and 90 deletions

View File

@@ -1,14 +1,11 @@
package work.slhaf.partner.common.thread;
import lombok.Getter;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Getter
public class InteractionThreadPoolExecutor {
private static InteractionThreadPoolExecutor interactionThreadPoolExecutor;

View File

@@ -3,9 +3,19 @@ package work.slhaf.partner.core.action;
import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import java.util.List;
@Capability(value = "action")
public interface ActionCapability {
void putPreparedAction(String uuid, MetaActionInfo metaActionInfo);
MetaActionInfo getPreparedAction(String userId);
List<MetaActionInfo> popPreparedAction(String userId);
List<MetaActionInfo> popPendingAction(String userId);
List<MetaActionInfo> listPreparedAction(String userId);
List<MetaActionInfo> listPendingAction(String userId);
void putPendingActions(String userId, MetaActionInfo metaActionInfo);
}

View File

@@ -8,28 +8,71 @@ import work.slhaf.partner.core.PartnerCore;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Setter
@Getter
@Capability(value = "action")
public class ActionCore extends PartnerCore<ActionCore> {
private HashMap<String, MetaActionInfo> preparedActions = new HashMap<>();
/**
* 对应本次交互即将执行或将要放置在行动池的预备任务因此将以本次交互的uuid为键其起到的作用相当于暂时的模块上下文
*/
private HashMap<String, List<MetaActionInfo>> preparedActions = new HashMap<>();
/**
* 待确认任务以userId区分不同用户因为需要跨请求确认
*/
private HashMap<String, List<MetaActionInfo>> pendingActions = new HashMap<>();
//TODO 添加语义缓存,可借由简单向量模型,设想以向量结果为键、行动倾向为值
public ActionCore() throws IOException, ClassNotFoundException {
}
@CapabilityMethod
public synchronized void putPreparedAction(String uuid, MetaActionInfo metaActionInfo) {
preparedActions.put(uuid, metaActionInfo);
public synchronized void putPendingActions(String userId, MetaActionInfo metaActionInfo) {
pendingActions.computeIfAbsent(userId, k -> {
List<MetaActionInfo> temp = new ArrayList<>();
temp.add(metaActionInfo);
return temp;
});
}
@CapabilityMethod
public MetaActionInfo getPreparedAction(String userId){
public synchronized List<MetaActionInfo> popPendingAction(String userId) {
List<MetaActionInfo> infos = pendingActions.get(userId);
pendingActions.remove(userId);
return infos;
}
@CapabilityMethod
public synchronized void putPreparedAction(String uuid, MetaActionInfo metaActionInfo) {
preparedActions.computeIfAbsent(uuid, k -> {
List<MetaActionInfo> temp = new ArrayList<>();
temp.add(metaActionInfo);
return temp;
});
}
@CapabilityMethod
public synchronized List<MetaActionInfo> popPreparedAction(String userId) {
List<MetaActionInfo> infos = preparedActions.get(userId);
preparedActions.remove(userId);
return infos;
}
@CapabilityMethod
public List<MetaActionInfo> listPreparedAction(String userId) {
return preparedActions.get(userId);
}
@CapabilityMethod
public List<MetaActionInfo> listPendingAction(String userId) {
return pendingActions.get(userId);
}
@Override
protected String getCoreKey() {
return "action-core";

View File

@@ -2,10 +2,9 @@ package work.slhaf.partner.core.action.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public abstract class MetaActionInfo {
protected String uuid;
protected String tendency;
protected ActionStatus status;
protected ActionData actionData;

View File

@@ -16,7 +16,7 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
private synchronized void setAppendedPrompt(PartnerRunningFlowContext context) {
AppendPromptData data = new AppendPromptData();
data.setModuleName(moduleName());
HashMap<String, String> map = getPromptDataMap(context.getUserId());
HashMap<String, String> map = getPromptDataMap(context);
data.setAppendedPrompt(map);
context.setAppendedPrompt(data);
}
@@ -25,7 +25,7 @@ public abstract class PreRunningModule extends AgentRunningModule<PartnerRunning
context.getCoreContext().addActiveModule(moduleName());
}
protected abstract HashMap<String, String> getPromptDataMap(String userId);
protected abstract HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context);
/**
* 用于在CoreModule接收到的模块Prompt中标识模块名称

View File

@@ -2,17 +2,19 @@ package work.slhaf.partner.module.modules.action.planner;
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.api.chat.pojo.Message;
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.entity.ActionStatus;
import work.slhaf.partner.core.action.entity.ImmediateActionInfo;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.ScheduledActionInfo;
import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.cache.CacheCapability;
import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.core.perceive.PerceiveCapability;
import work.slhaf.partner.module.common.module.PreRunningModule;
import work.slhaf.partner.module.modules.action.planner.confirmer.ActionConfirmer;
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.evaluator.ActionEvaluator;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorInput;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult;
@@ -21,15 +23,16 @@ 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.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
/**
* 负责针对本次输入生成基础的行动计划,在主模型传达意愿后,执行行动或者放入计划池
*/
@AgentModule(name = "task_planner", order = 2)
@AgentModule(name = "action_planner", order = 2)
public class ActionPlanner extends PreRunningModule {
@InjectCapability
@@ -45,48 +48,137 @@ public class ActionPlanner extends PreRunningModule {
private ActionEvaluator actionEvaluator;
@InjectModule
private ActionExtractor actionExtractor;
@InjectModule
private ActionConfirmer actionConfirmer;
private InteractionThreadPoolExecutor executor;
private ActionAssemblyHelper assemblyHelper;
@Init
public void init() {
executor = InteractionThreadPoolExecutor.getInstance();
assemblyHelper = new ActionAssemblyHelper();
}
@Override
protected void doExecute(PartnerRunningFlowContext context) {
ExtractorInput extractorInput = getExtractorInput(context);
List<Callable<Void>> tasks = new ArrayList<>();
addConfirmTask(tasks, context);
addNewActionTask(tasks, context);
executor.invokeAll(tasks);
}
/**
* 新的提取与评估任务
*
* @param tasks 并发任务列表
* @param context 流程上下文
*/
private void addNewActionTask(List<Callable<Void>> tasks, PartnerRunningFlowContext context) {
tasks.add(() -> {
ExtractorInput extractorInput = assemblyHelper.buildExtractorInput(context);
ExtractorResult extractorResult = actionExtractor.execute(extractorInput);
if (!extractorResult.isAction()){
if (extractorResult.getTendencies().isEmpty()) {
return null;
}
EvaluatorInput evaluatorInput = assemblyHelper.buildEvaluatorInput(extractorResult, context.getUserId());
List<EvaluatorResult> evaluatorResults = actionEvaluator.execute(evaluatorInput);
setupPreparedActionInfo(evaluatorResults, context);
return null;
});
}
/**
* 待确认行动的判断任务
*
* @param tasks 并发任务列表
* @param context 流程上下文
*/
private void addConfirmTask(List<Callable<Void>> tasks, PartnerRunningFlowContext context) {
tasks.add(() -> {
ConfirmerInput confirmerInput = assemblyHelper.buildConfirmerInput(context);
ConfirmerResult result = actionConfirmer.execute(confirmerInput);
setupPendingActionInfo(context, result);
return null;
});
}
private void setupPendingActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) {
//TODO 需考虑未确认任务的失效或者拒绝时机
List<String> uuids = result.getUuids();
if (uuids == null) {
return;
}
EvaluatorInput evaluatorInput = getEvaluatorInput(extractorResult, context.getUserId());
EvaluatorResult evaluatorResult = actionEvaluator.execute(evaluatorInput);
setupPreparedActionInfo(evaluatorResult, context.getUuid());
String contextUuid = context.getUuid();
List<MetaActionInfo> pendingActions = actionCapability.popPendingAction(context.getUserId());
for (MetaActionInfo actionInfo : pendingActions) {
if (uuids.contains(actionInfo.getUuid())) {
actionCapability.putPreparedAction(contextUuid, actionInfo);
}
}
}
private void setupPreparedActionInfo(EvaluatorResult evaluatorResult, String uuid) {
MetaActionInfo metaActionInfo = switch (evaluatorResult.getType()) {
case PLANNING -> {
ScheduledActionInfo actionInfo = new ScheduledActionInfo();
actionInfo.setActionData(evaluatorResult.getActionData());
actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
actionInfo.setStatus(ActionStatus.PREPARE);
yield actionInfo;
private void setupPreparedActionInfo(List<EvaluatorResult> evaluatorResults, PartnerRunningFlowContext context) {
for (EvaluatorResult evaluatorResult : evaluatorResults) {
if (evaluatorResult.isNeedConfirm()) {
MetaActionInfo metaActionInfo = assemblyHelper.buildMetaActionInfo(evaluatorResult);
actionCapability.putPendingActions(context.getUserId(), metaActionInfo);
} else {
MetaActionInfo metaActionInfo = assemblyHelper.buildMetaActionInfo(evaluatorResult);
actionCapability.putPreparedAction(context.getUuid(), metaActionInfo);
}
case IMMEDIATE -> {
ImmediateActionInfo actionInfo = new ImmediateActionInfo();
actionInfo.setActionData(evaluatorResult.getActionData());
actionInfo.setStatus(ActionStatus.PREPARE);
yield actionInfo;
}
};
actionCapability.putPreparedAction(uuid, metaActionInfo);
}
private EvaluatorInput getEvaluatorInput(ExtractorResult extractorResult, String userId) {
EvaluatorInput input = new EvaluatorInput();
input.setTendency(extractorResult.getTendency());
input.setUser(perceiveCapability.getUser(userId));
input.setRecentMessages(cognationCapability.getChatMessages());
input.setActivatedSlices(cacheCapability.getActivatedSlices(userId));
return input;
@Override
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>();
setupPendingActions(map, context.getUserId());
setupPreparedActions(map, context.getUuid());
return map;
}
private ExtractorInput getExtractorInput(PartnerRunningFlowContext context) {
private void setupPendingActions(HashMap<String, String> map, String userId) {
List<MetaActionInfo> actionInfos = actionCapability.listPendingAction(userId);
if (actionInfos == null || actionInfos.isEmpty()) {
map.put("[待确认行动] <待确认行动信息>", "无待确认行动");
return;
}
for (int i = 0; i < actionInfos.size(); i++) {
map.put("[待确认行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
}
}
private void setupPreparedActions(HashMap<String, String> map, String uuid) {
List<MetaActionInfo> actionInfos = actionCapability.listPreparedAction(uuid);
if (actionInfos == null || actionInfos.isEmpty()) {
map.put("[预备行动] <预备行动信息>", "无预备行动");
return;
}
for (int i = 0; i < actionInfos.size(); i++) {
map.put("[预备行动 " + (i + 1) + " ]", generateActionStr(actionInfos.get(i)));
}
}
private String generateActionStr(MetaActionInfo metaActionInfo) {
ActionData actionData = metaActionInfo.getActionData();
return "<行动倾向>" + " : " + metaActionInfo.getTendency() +
"<行动原因>" + " : " + actionData.getReason() +
"<工具描述>" + " : " + actionData.getDescription();
}
@Override
protected String moduleName() {
return "[行动模块]";
}
private class ActionAssemblyHelper {
private ActionAssemblyHelper() {
}
private ExtractorInput buildExtractorInput(PartnerRunningFlowContext context) {
ExtractorInput input = new ExtractorInput();
input.setInput(context.getInput());
List<Message> chatMessages = cognationCapability.getChatMessages();
@@ -100,29 +192,41 @@ public class ActionPlanner extends PreRunningModule {
return input;
}
@Override
protected HashMap<String, String> getPromptDataMap(String userId) {
MetaActionInfo actionInfo = actionCapability.getPreparedAction(userId);
HashMap<String, String> map = new HashMap<>();
if (actionInfo == null){
map.put("[行动状态] <是否存在行动>", "");
return map;
}
map.put("[行动确认原因] <生成行动的原因>", actionInfo.getActionData().getReason());
if (actionInfo instanceof ImmediateActionInfo) {
map.put("[行动类型] <将执行的行动类型,分为即时行动与计划行动>", "即时");
map.put("[行动倾向] <你将要执行的动作>", actionInfo.getTendency());
map.put("[行动工具] <本次行动将要调用的工具>", actionInfo.getActionData().getKey() + ": " + actionInfo.getActionData().getDescription());
} else {
ScheduledActionInfo info = (ScheduledActionInfo) actionInfo;
map.put("[行动类型] <将执行的行动类型,分为即时行动与计划行动>", "计划");
map.put("[计划内容] <生成的计划行动的内容主要是计划时间的DateTime值或者CRON表达式>", info.getScheduleContent());
}
return map;
public EvaluatorInput buildEvaluatorInput(ExtractorResult extractorResult, String userId) {
EvaluatorInput input = new EvaluatorInput();
input.setTendencies(extractorResult.getTendencies());
input.setUser(perceiveCapability.getUser(userId));
input.setRecentMessages(cognationCapability.getChatMessages());
input.setActivatedSlices(cacheCapability.getActivatedSlices(userId));
return input;
}
@Override
protected String moduleName() {
return "[行动模块]";
public MetaActionInfo buildMetaActionInfo(EvaluatorResult evaluatorResult) {
return switch (evaluatorResult.getType()) {
case PLANNING -> {
ScheduledActionInfo actionInfo = new ScheduledActionInfo();
actionInfo.setActionData(evaluatorResult.getActionData());
actionInfo.setScheduleContent(evaluatorResult.getScheduleContent());
actionInfo.setStatus(ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
case IMMEDIATE -> {
ImmediateActionInfo actionInfo = new ImmediateActionInfo();
actionInfo.setActionData(evaluatorResult.getActionData());
actionInfo.setStatus(ActionStatus.PREPARE);
actionInfo.setUuid(UUID.randomUUID().toString());
yield actionInfo;
}
};
}
public ConfirmerInput buildConfirmerInput(PartnerRunningFlowContext context) {
ConfirmerInput confirmerInput = new ConfirmerInput();
confirmerInput.setInput(context.getInput());
List<MetaActionInfo> pendingActions = actionCapability.listPendingAction(context.getUserId());
confirmerInput.setActionInfos(pendingActions);
return confirmerInput;
}
}
}

View File

@@ -0,0 +1,25 @@
package work.slhaf.partner.module.modules.action.planner.confirmer;
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.planner.confirmer.entity.ConfirmerInput;
import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerResult;
@AgentSubModule
public class ActionConfirmer extends AgentRunningSubModule<ConfirmerInput, ConfirmerResult> implements ActivateModel {
@Override
public ConfirmerResult execute(ConfirmerInput data) {
return null;
}
@Override
public String modelKey() {
return "action-confirmer";
}
@Override
public boolean withBasicPrompt() {
return false;
}
}

View File

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

View File

@@ -0,0 +1,10 @@
package work.slhaf.partner.module.modules.action.planner.confirmer.entity;
import lombok.Data;
import java.util.List;
@Data
public class ConfirmerResult {
private List<String> uuids;
}

View File

@@ -6,11 +6,13 @@ import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunn
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorInput;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult;
import java.util.List;
@AgentSubModule
public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, EvaluatorResult> implements ActivateModel {
public class ActionEvaluator extends AgentRunningSubModule<EvaluatorInput, List<EvaluatorResult>> implements ActivateModel {
@Override
public EvaluatorResult execute(EvaluatorInput data) {
public List<EvaluatorResult> execute(EvaluatorInput data) {
return null;
}

View File

@@ -12,5 +12,5 @@ public class EvaluatorInput {
private List<Message> recentMessages;
private User user;
private List<EvaluatedSlice> activatedSlices;
private String tendency;
private List<String> tendencies;
}

View File

@@ -4,11 +4,10 @@ import lombok.Data;
import work.slhaf.partner.core.action.entity.ActionData;
import work.slhaf.partner.core.action.entity.ActionType;
import java.time.LocalDateTime;
@Data
public class EvaluatorResult {
private boolean ok;
private boolean needConfirm;
private ActionType type;
private String scheduleContent;
private ActionData actionData;

View File

@@ -11,6 +11,7 @@ public class ActionExtractor extends AgentRunningSubModule<ExtractorInput, Extra
@Override
public ExtractorResult execute(ExtractorInput data) {
//TODO 添加语义缓存判断
return null;
}

View File

@@ -2,8 +2,9 @@ package work.slhaf.partner.module.modules.action.planner.extractor.entity;
import lombok.Data;
import java.util.List;
@Data
public class ExtractorResult {
private boolean action;
private String tendency;
private List<String> tendencies;
}

View File

@@ -24,9 +24,9 @@ public class PerceiveSelector extends PreRunningModule {
}
@Override
protected HashMap<String, String> getPromptDataMap(String userId) {
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>();
User user = perceiveCapability.getUser(userId);
User user = perceiveCapability.getUser(context.getUserId());
map.put("[关系] <你与最新聊天用户的关系>", user.getRelation());
map.put("[态度] <你对于最新聊天用户的态度>", user.getAttitude().toString());
map.put("[印象] <你对于最新聊天用户的印象>", user.getImpressions().toString());

View File

@@ -60,7 +60,7 @@ public class PreprocessExecutor extends PreRunningModule {
@Override
protected HashMap<String, String> getPromptDataMap(String userId) {
protected HashMap<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>();
map.put("text", "这部分才是真正的用户输入内容, 就像你之前收到过的输入一样。但...不会是'同一个人'。");
map.put("datetime", "本次用户输入对应的当前时间");