refactor(ActionPlanner): switch pending confirmation flow to PendingActionRecord with decision parsing and reminder/expire lifecycle scheduling

This commit is contained in:
2026-03-08 14:40:53 +08:00
parent b256af0f58
commit 4bb83f86a8
8 changed files with 376 additions and 57 deletions

View File

@@ -3,10 +3,7 @@ package work.slhaf.partner.core.action;
import lombok.NonNull; import lombok.NonNull;
import org.jetbrains.annotations.Nullable; 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.ExecutableAction; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.MetaAction;
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.entity.cache.CacheAdjustData;
import work.slhaf.partner.core.action.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention; import work.slhaf.partner.module.modules.action.interventor.entity.MetaIntervention;
@@ -24,11 +21,19 @@ public interface ActionCapability {
Set<ExecutableAction> listActions(@Nullable ExecutableAction.Status status, @Nullable String source); Set<ExecutableAction> listActions(@Nullable ExecutableAction.Status status, @Nullable String source);
List<ExecutableAction> popPendingAction(String userId); PendingActionRecord createPendingAction(String userId, ExecutableAction executableAction, long ttlMillis, long reminderBeforeMillis);
List<ExecutableAction> listPendingAction(String userId); List<PendingActionRecord> listActivePendingActions(String userId);
void putPendingActions(String userId, ExecutableAction executableAction); PendingActionRecord resolvePendingDecision(String userId, String pendingId, PendingActionRecord.Decision decision, String reason);
boolean markPendingReminded(String pendingId);
PendingActionRecord expirePendingIfWaiting(String pendingId);
void bindPendingLifecycleActions(String pendingId, StateAction reminderAction, StateAction expireAction);
void cancelPendingLifecycleActions(String pendingId);
List<String> selectTendencyCache(String input); List<String> selectTendencyCache(String input);

View File

@@ -8,10 +8,7 @@ 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.ExecutableAction; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.MetaAction;
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.ActionCacheData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustData; import work.slhaf.partner.core.action.entity.cache.CacheAdjustData;
import work.slhaf.partner.core.action.entity.cache.CacheAdjustMetaData; import work.slhaf.partner.core.action.entity.cache.CacheAdjustMetaData;
@@ -51,7 +48,8 @@ public class ActionCore extends PartnerCore<ActionCore> {
/** /**
* 待确认任务以userId区分不同用户因为需要跨请求确认 * 待确认任务以userId区分不同用户因为需要跨请求确认
*/ */
private HashMap<String, List<ExecutableAction>> pendingActions = new HashMap<>(); private final HashMap<String, List<PendingActionRecord>> pendingActions = new HashMap<>();
private final HashMap<String, PendingLifecycleActions> pendingLifecycleActions = new HashMap<>();
/** /**
* 语义缓存与行为倾向映射 * 语义缓存与行为倾向映射
*/ */
@@ -88,24 +86,129 @@ public class ActionCore extends PartnerCore<ActionCore> {
} }
@CapabilityMethod @CapabilityMethod
public synchronized void putPendingActions(String userId, ExecutableAction executableAction) { public synchronized PendingActionRecord createPendingAction(String userId, ExecutableAction executableAction,
pendingActions.computeIfAbsent(userId, k -> { long ttlMillis, long reminderBeforeMillis) {
List<ExecutableAction> temp = new ArrayList<>(); long now = System.currentTimeMillis();
temp.add(executableAction); long safeTtl = Math.max(ttlMillis, 1000L);
return temp; long safeReminderBefore = Math.max(0L, Math.min(reminderBeforeMillis, safeTtl - 1000L));
});
PendingActionRecord record = new PendingActionRecord();
record.setUserId(userId);
record.setExecutableAction(executableAction);
record.setCreatedAt(now);
record.setExpireAt(now + safeTtl);
record.setRemindAt(record.getExpireAt() - safeReminderBefore);
record.setStatus(PendingActionRecord.Status.WAITING_CONFIRM);
pendingActions.computeIfAbsent(userId, k -> new ArrayList<>()).add(record);
return record;
} }
@CapabilityMethod @CapabilityMethod
public synchronized List<ExecutableAction> popPendingAction(String userId) { public synchronized List<PendingActionRecord> listActivePendingActions(String userId) {
List<ExecutableAction> infos = pendingActions.get(userId); List<PendingActionRecord> records = pendingActions.get(userId);
pendingActions.remove(userId); if (records == null || records.isEmpty()) {
return infos; return List.of();
}
return records.stream()
.filter(record -> record.getStatus() == PendingActionRecord.Status.WAITING_CONFIRM
|| record.getStatus() == PendingActionRecord.Status.REMINDER_SENT)
.toList();
} }
@CapabilityMethod @CapabilityMethod
public List<ExecutableAction> listPendingAction(String userId) { public synchronized PendingActionRecord resolvePendingDecision(String userId, String pendingId,
return pendingActions.get(userId); PendingActionRecord.Decision decision, String reason) {
PendingActionRecord record = findPendingByUserAndId(userId, pendingId);
if (record == null) {
return null;
}
PendingActionRecord.Status status = record.getStatus();
boolean active = status == PendingActionRecord.Status.WAITING_CONFIRM
|| status == PendingActionRecord.Status.REMINDER_SENT;
if (!active) {
return record;
}
if (decision == PendingActionRecord.Decision.CONFIRM) {
record.setStatus(PendingActionRecord.Status.CONFIRMED);
record.setDecisionAt(System.currentTimeMillis());
record.setDecisionReason(reason);
cancelPendingLifecycleActions(pendingId);
return record;
}
if (decision == PendingActionRecord.Decision.REJECT) {
record.setStatus(PendingActionRecord.Status.REJECTED);
record.setDecisionAt(System.currentTimeMillis());
record.setDecisionReason(reason);
ExecutableAction executableAction = record.getExecutableAction();
executableAction.setStatus(ExecutableAction.Status.FAILED);
executableAction.setResult("行动被拒绝");
cancelPendingLifecycleActions(pendingId);
return record;
}
record.setDecisionReason(reason);
return record;
}
@CapabilityMethod
public synchronized boolean markPendingReminded(String pendingId) {
PendingActionRecord record = findPendingById(pendingId);
if (record == null) {
return false;
}
if (record.getStatus() != PendingActionRecord.Status.WAITING_CONFIRM) {
return false;
}
record.setStatus(PendingActionRecord.Status.REMINDER_SENT);
record.setReminded(true);
return true;
}
@CapabilityMethod
public synchronized PendingActionRecord expirePendingIfWaiting(String pendingId) {
PendingActionRecord record = findPendingById(pendingId);
if (record == null) {
return null;
}
PendingActionRecord.Status status = record.getStatus();
if (status != PendingActionRecord.Status.WAITING_CONFIRM
&& status != PendingActionRecord.Status.REMINDER_SENT) {
return null;
}
record.setStatus(PendingActionRecord.Status.EXPIRED);
record.setDecisionAt(System.currentTimeMillis());
record.setDecisionReason("等待确认超时");
cancelPendingLifecycleActions(pendingId);
return record;
}
@CapabilityMethod
public synchronized void bindPendingLifecycleActions(String pendingId, StateAction reminderAction, StateAction expireAction) {
PendingLifecycleActions old = pendingLifecycleActions.put(
pendingId, new PendingLifecycleActions(reminderAction, expireAction)
);
if (old != null) {
old.reminderAction.setEnabled(false);
old.expireAction.setEnabled(false);
}
PendingActionRecord record = findPendingById(pendingId);
if (record != null) {
record.setReminderActionId(reminderAction.getUuid());
record.setExpireActionId(expireAction.getUuid());
}
}
@CapabilityMethod
public synchronized void cancelPendingLifecycleActions(String pendingId) {
PendingLifecycleActions actions = pendingLifecycleActions.remove(pendingId);
if (actions == null) {
return;
}
actions.reminderAction.setEnabled(false);
actions.expireAction.setEnabled(false);
} }
/** /**
@@ -434,6 +537,33 @@ public class ActionCore extends PartnerCore<ActionCore> {
return "action-core"; return "action-core";
} }
private PendingActionRecord findPendingByUserAndId(String userId, String pendingId) {
List<PendingActionRecord> records = pendingActions.get(userId);
if (records == null) {
return null;
}
for (PendingActionRecord record : records) {
if (record.getPendingId().equals(pendingId)) {
return record;
}
}
return null;
}
private PendingActionRecord findPendingById(String pendingId) {
for (List<PendingActionRecord> records : pendingActions.values()) {
for (PendingActionRecord record : records) {
if (record.getPendingId().equals(pendingId)) {
return record;
}
}
}
return null;
}
private record PendingLifecycleActions(StateAction reminderAction, StateAction expireAction) {
}
public enum ExecutorType { public enum ExecutorType {
VIRTUAL, PLATFORM VIRTUAL, PLATFORM
} }

View File

@@ -0,0 +1,39 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import java.util.UUID;
@Data
public class PendingActionRecord {
private final String pendingId = UUID.randomUUID().toString();
private String userId;
private ExecutableAction executableAction;
private Status status = Status.WAITING_CONFIRM;
private long createdAt;
private long remindAt;
private long expireAt;
private boolean reminded;
private long decisionAt;
private String decisionReason;
private String reminderActionId;
private String expireActionId;
public enum Status {
WAITING_CONFIRM,
REMINDER_SENT,
CONFIRMED,
REJECTED,
EXPIRED
}
public enum Decision {
CONFIRM,
REJECT,
HOLD
}
}

View File

@@ -1,5 +1,6 @@
package work.slhaf.partner.module.modules.action.planner; package work.slhaf.partner.module.modules.action.planner;
import kotlin.Unit;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; 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;
@@ -20,6 +21,7 @@ import work.slhaf.partner.module.modules.action.executor.ActionExecutor;
import work.slhaf.partner.module.modules.action.planner.confirmer.ActionConfirmer; 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.ConfirmerInput;
import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerResult; import work.slhaf.partner.module.modules.action.planner.confirmer.entity.ConfirmerResult;
import work.slhaf.partner.module.modules.action.planner.confirmer.entity.PendingDecisionItem;
import work.slhaf.partner.module.modules.action.planner.evaluator.ActionEvaluator; 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.EvaluatorInput;
import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult; import work.slhaf.partner.module.modules.action.planner.evaluator.entity.EvaluatorResult;
@@ -29,6 +31,9 @@ import work.slhaf.partner.module.modules.action.planner.extractor.entity.Extract
import work.slhaf.partner.module.modules.action.scheduler.ActionScheduler; import work.slhaf.partner.module.modules.action.scheduler.ActionScheduler;
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext; import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -39,6 +44,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/ */
public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract { public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
private static final long PENDING_TTL_MILLIS = 30 * 60 * 1000L;
private static final long PENDING_REMINDER_ADVANCE_MILLIS = 5 * 60 * 1000L;
private final ActionAssemblyHelper assemblyHelper = new ActionAssemblyHelper(); private final ActionAssemblyHelper assemblyHelper = new ActionAssemblyHelper();
@InjectCapability @InjectCapability
@@ -139,22 +147,23 @@ public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
} }
private void setupConfirmedActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) { private void setupConfirmedActionInfo(PartnerRunningFlowContext context, ConfirmerResult result) {
// TODO 需考虑未确认任务的失效或者拒绝时机在action core中实现 List<PendingDecisionItem> decisions = result.getDecisions();
List<String> uuids = result.getUuids(); if (decisions == null || decisions.isEmpty()) {
if (uuids == null) {
return; return;
} }
List<ExecutableAction> pendingActions = actionCapability.popPendingAction(context.getSource()); for (PendingDecisionItem decisionItem : decisions) {
for (ExecutableAction executableAction : pendingActions) { PendingActionRecord pendingAction = actionCapability.resolvePendingDecision(
// put action into ActionCore to record context.getSource(),
if (uuids.contains(executableAction.getUuid())) { decisionItem.getPendingId(),
actionCapability.putAction(executableAction); decisionItem.getDecision(),
decisionItem.getReason()
);
if (pendingAction == null) {
continue;
} }
// execute or schedule it immediately if (decisionItem.getDecision() == PendingActionRecord.Decision.CONFIRM
switch (executableAction) { && pendingAction.getStatus() == PendingActionRecord.Status.CONFIRMED) {
case SchedulableExecutableAction action -> actionScheduler.schedule(action); executeOrSchedule(pendingAction.getExecutableAction());
case ImmediateExecutableAction action -> actionExecutor.execute(action);
default -> log.error("unknown executable action type: {}", executableAction.getClass().getSimpleName());
} }
} }
} }
@@ -163,13 +172,87 @@ public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
for (EvaluatorResult evaluatorResult : evaluatorResults) { for (EvaluatorResult evaluatorResult : evaluatorResults) {
ExecutableAction executableAction = assemblyHelper.buildActionData(evaluatorResult, context.getSource()); ExecutableAction executableAction = assemblyHelper.buildActionData(evaluatorResult, context.getSource());
if (evaluatorResult.isNeedConfirm()) { if (evaluatorResult.isNeedConfirm()) {
actionCapability.putPendingActions(context.getSource(), executableAction); PendingActionRecord pendingAction = actionCapability.createPendingAction(
context.getSource(),
executableAction,
PENDING_TTL_MILLIS,
PENDING_REMINDER_ADVANCE_MILLIS
);
schedulePendingLifecycleActions(pendingAction);
} else { } else {
actionCapability.putAction(executableAction); actionCapability.putAction(executableAction);
} }
} }
} }
private void schedulePendingLifecycleActions(PendingActionRecord pendingAction) {
StateAction reminderAction = buildPendingReminderAction(pendingAction);
StateAction expireAction = buildPendingExpireAction(pendingAction);
actionCapability.bindPendingLifecycleActions(pendingAction.getPendingId(), reminderAction, expireAction);
actionScheduler.schedule(reminderAction);
actionScheduler.schedule(expireAction);
}
private StateAction buildPendingReminderAction(PendingActionRecord pendingAction) {
return new StateAction(
pendingAction.getUserId(),
"pending-action-reminder:" + pendingAction.getPendingId(),
"待确认行动提醒",
Schedulable.ScheduleType.ONCE,
asScheduleContent(pendingAction.getRemindAt()),
new StateAction.Trigger.Call(() -> {
handlePendingReminder(pendingAction.getPendingId());
return Unit.INSTANCE;
})
);
}
private StateAction buildPendingExpireAction(PendingActionRecord pendingAction) {
return new StateAction(
pendingAction.getUserId(),
"pending-action-expire:" + pendingAction.getPendingId(),
"待确认行动失效",
Schedulable.ScheduleType.ONCE,
asScheduleContent(pendingAction.getExpireAt()),
new StateAction.Trigger.Call(() -> {
handlePendingExpire(pendingAction.getPendingId());
return Unit.INSTANCE;
})
);
}
private void handlePendingReminder(String pendingId) {
boolean marked = actionCapability.markPendingReminded(pendingId);
if (!marked) {
return;
}
try {
// TODO target 指定行为待补充; 主动回复链路待补充
cognationCapability.initiateTurn("系统提醒存在待确认行动即将过期请确认是否继续执行。pendingId=" + pendingId);
} catch (Exception e) {
log.warn("触发待确认行动提醒失败, pendingId: {}", pendingId, e);
}
}
private void handlePendingExpire(String pendingId) {
actionCapability.expirePendingIfWaiting(pendingId);
}
private String asScheduleContent(long targetTimeMillis) {
long now = System.currentTimeMillis();
long safeTarget = Math.max(targetTimeMillis, now + 1000L);
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(safeTarget), ZoneId.systemDefault()).toString();
}
private void executeOrSchedule(ExecutableAction executableAction) {
actionCapability.putAction(executableAction);
switch (executableAction) {
case SchedulableExecutableAction action -> actionScheduler.schedule(action);
case ImmediateExecutableAction action -> actionExecutor.execute(action);
default -> log.error("unknown executable action type: {}", executableAction.getClass().getSimpleName());
}
}
@Override @Override
protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) { protected Map<String, String> getPromptDataMap(PartnerRunningFlowContext context) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
@@ -180,13 +263,16 @@ public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
} }
private void setupPendingActions(HashMap<String, String> map, String userId) { private void setupPendingActions(HashMap<String, String> map, String userId) {
List<ExecutableAction> executableActionData = actionCapability.listPendingAction(userId); List<PendingActionRecord> pendingActions = actionCapability.listActivePendingActions(userId);
if (executableActionData == null || executableActionData.isEmpty()) { if (pendingActions.isEmpty()) {
map.put("[待确认行动] <等待用户确认的行动信息>", "无待确认行动"); map.put("[待确认行动] <等待用户确认的行动信息>", "无待确认行动");
return; return;
} }
for (int i = 0; i < executableActionData.size(); i++) { for (int i = 0; i < pendingActions.size(); i++) {
map.put("[待确认行动 " + (i + 1) + " ] <等待用户确认的行动信息>", generateActionStr(executableActionData.get(i))); map.put(
"[待确认行动 " + (i + 1) + " ] <等待用户确认的行动信息>",
generateActionStr(pendingActions.get(i).getExecutableAction())
);
} }
} }
@@ -344,8 +430,9 @@ public class ActionPlanner extends PreRunningAbstractAgentModuleAbstract {
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<ExecutableAction> pendingActions = actionCapability.listPendingAction(context.getSource()); List<PendingActionRecord> pendingActions = actionCapability.listActivePendingActions(context.getSource());
confirmerInput.setExecutableActionData(pendingActions); confirmerInput.setRecentMessages(cognationCapability.snapshotChatMessages());
confirmerInput.setPendingActions(pendingActions);
return confirmerInput; return confirmerInput;
} }
} }

View File

@@ -10,8 +10,10 @@ import work.slhaf.partner.api.chat.pojo.Message;
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.ActionCore;
import work.slhaf.partner.core.action.entity.ExecutableAction; import work.slhaf.partner.core.action.entity.ExecutableAction;
import work.slhaf.partner.core.action.entity.PendingActionRecord;
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 work.slhaf.partner.module.modules.action.planner.confirmer.entity.PendingDecisionItem;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@@ -25,22 +27,33 @@ public class ActionConfirmer extends AbstractAgentModule.Sub<ConfirmerInput, Con
@Override @Override
public ConfirmerResult execute(ConfirmerInput data) { public ConfirmerResult execute(ConfirmerInput data) {
List<ExecutableAction> executableActionList = data.getExecutableActionData(); List<PendingActionRecord> pendingActions = data.getPendingActions();
if (pendingActions == null || pendingActions.isEmpty()) {
return new ConfirmerResult();
}
ExecutorService executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL); ExecutorService executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
CountDownLatch latch = new CountDownLatch(executableActionList.size()); CountDownLatch latch = new CountDownLatch(pendingActions.size());
ConfirmerResult result = new ConfirmerResult(); ConfirmerResult result = new ConfirmerResult();
List<String> uuids = result.getUuids(); List<PendingDecisionItem> decisions = result.getDecisions();
for (ExecutableAction executableAction : executableActionList) { for (PendingActionRecord pendingAction : pendingActions) {
executor.execute(() -> { executor.execute(() -> {
try { try {
ExecutableAction executableAction = pendingAction.getExecutableAction();
String prompt = buildPrompt(executableAction, data.getInput(), data.getRecentMessages()); String prompt = buildPrompt(executableAction, data.getInput(), data.getRecentMessages());
ChatResponse response = this.singleChat(prompt); ChatResponse response = this.singleChat(prompt);
JSONObject tempResult = JSONObject.parseObject(extractJson(response.getMessage())); JSONObject tempResult = JSONObject.parseObject(extractJson(response.getMessage()));
if (tempResult.getBoolean("confirmed")) { PendingActionRecord.Decision decision = parseDecision(tempResult);
executableAction.setStatus(ExecutableAction.Status.PREPARE); String reason = tempResult == null ? null : tempResult.getString("reason");
synchronized (uuids) { synchronized (decisions) {
uuids.add(executableAction.getUuid()); decisions.add(new PendingDecisionItem(pendingAction.getPendingId(), decision, reason));
} }
} catch (Exception e) {
synchronized (decisions) {
decisions.add(new PendingDecisionItem(
pendingAction.getPendingId(),
PendingActionRecord.Decision.HOLD,
"确认解析失败: " + e.getLocalizedMessage()
));
} }
} finally { } finally {
latch.countDown(); latch.countDown();
@@ -55,6 +68,30 @@ public class ActionConfirmer extends AbstractAgentModule.Sub<ConfirmerInput, Con
return result; return result;
} }
private PendingActionRecord.Decision parseDecision(JSONObject tempResult) {
if (tempResult == null) {
return PendingActionRecord.Decision.HOLD;
}
String decisionText = tempResult.getString("decision");
if (decisionText != null) {
String upperDecision = decisionText.toUpperCase();
if (upperDecision.contains("CONFIRM")) {
return PendingActionRecord.Decision.CONFIRM;
}
if (upperDecision.contains("REJECT")) {
return PendingActionRecord.Decision.REJECT;
}
if (upperDecision.contains("HOLD")) {
return PendingActionRecord.Decision.HOLD;
}
}
Boolean confirmed = tempResult.getBoolean("confirmed");
if (Boolean.TRUE.equals(confirmed)) {
return PendingActionRecord.Decision.CONFIRM;
}
return PendingActionRecord.Decision.HOLD;
}
private String buildPrompt(ExecutableAction data, String input, List<Message> recentMessages) { private String buildPrompt(ExecutableAction data, String input, List<Message> recentMessages) {
JSONObject prompt = new JSONObject(); JSONObject prompt = new JSONObject();
prompt.put("[用户输入]", input); prompt.put("[用户输入]", input);
@@ -63,8 +100,14 @@ public class ActionConfirmer extends AbstractAgentModule.Sub<ConfirmerInput, Con
actionData.put("[行动原因]", data.getReason()); actionData.put("[行动原因]", data.getReason());
actionData.put("[行动来源]", data.getSource()); actionData.put("[行动来源]", data.getSource());
actionData.put("[行动描述]", data.getDescription()); actionData.put("[行动描述]", data.getDescription());
JSONArray decisionEnums = prompt.putArray("[决策选项]");
decisionEnums.add("CONFIRM");
decisionEnums.add("REJECT");
decisionEnums.add("HOLD");
JSONArray messageData = prompt.putArray("[近期对话]"); JSONArray messageData = prompt.putArray("[近期对话]");
if (recentMessages != null) {
messageData.addAll(recentMessages); messageData.addAll(recentMessages);
}
return prompt.toString(); return prompt.toString();
} }

View File

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

View File

@@ -7,5 +7,5 @@ import java.util.List;
@Data @Data
public class ConfirmerResult { public class ConfirmerResult {
private List<String> uuids = new ArrayList<>(); private List<PendingDecisionItem> decisions = new ArrayList<>();
} }

View File

@@ -0,0 +1,15 @@
package work.slhaf.partner.module.modules.action.planner.confirmer.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import work.slhaf.partner.core.action.entity.PendingActionRecord;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PendingDecisionItem {
private String pendingId;
private PendingActionRecord.Decision decision;
private String reason;
}