refactor(action-core): return Result for meta action lookups and adapt runtime callers

This commit is contained in:
2026-04-11 18:30:24 +08:00
parent 1b48e955bd
commit 2aae1b1f14
8 changed files with 321 additions and 40 deletions

View File

@@ -8,6 +8,7 @@ import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.intervention.MetaIntervention; import work.slhaf.partner.core.action.entity.intervention.MetaIntervention;
import work.slhaf.partner.core.action.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability; import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability;
import work.slhaf.partner.framework.agent.support.Result;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -23,9 +24,9 @@ public interface ActionCapability {
ExecutorService getExecutor(ActionCore.ExecutorType type); ExecutorService getExecutor(ActionCore.ExecutorType type);
MetaAction loadMetaAction(@NonNull String actionKey); Result<MetaAction> loadMetaAction(@NonNull String actionKey);
MetaActionInfo loadMetaActionInfo(@NonNull String actionKey); Result<MetaActionInfo> loadMetaActionInfo(@NonNull String actionKey);
void registerMetaActions(@NonNull Map<String, MetaActionInfo> metaActions); void registerMetaActions(@NonNull Map<String, MetaActionInfo> metaActions);

View File

@@ -14,12 +14,14 @@ import work.slhaf.partner.core.action.exception.ActionLookupException;
import work.slhaf.partner.core.action.runner.LocalRunnerClient; import work.slhaf.partner.core.action.runner.LocalRunnerClient;
import work.slhaf.partner.core.action.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.framework.agent.config.ConfigCenter; import work.slhaf.partner.framework.agent.config.ConfigCenter;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore; import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore;
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod; import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod;
import work.slhaf.partner.framework.agent.factory.context.Shutdown; import work.slhaf.partner.framework.agent.factory.context.Shutdown;
import work.slhaf.partner.framework.agent.state.State; import work.slhaf.partner.framework.agent.state.State;
import work.slhaf.partner.framework.agent.state.StateSerializable; import work.slhaf.partner.framework.agent.state.StateSerializable;
import work.slhaf.partner.framework.agent.state.StateValue; import work.slhaf.partner.framework.agent.state.StateValue;
import work.slhaf.partner.framework.agent.support.Result;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -114,49 +116,49 @@ public class ActionCore implements StateSerializable {
} }
@CapabilityMethod @CapabilityMethod
public MetaAction loadMetaAction(@NonNull String actionKey) { public Result<MetaAction> loadMetaAction(@NonNull String actionKey) {
MetaActionInfo metaActionInfo = existedMetaActions.get(actionKey); MetaActionInfo metaActionInfo = existedMetaActions.get(actionKey);
if (metaActionInfo == null) { if (metaActionInfo == null) {
throw new ActionLookupException( return Result.failure(new ActionLookupException(
"Meta action info not found for action key: " + actionKey, "Meta action info not found for action key: " + actionKey,
actionKey, actionKey,
"META_ACTION" "META_ACTION"
); ));
} }
String[] split = actionKey.split("::", 2); String[] split = actionKey.split("::", 2);
if (split.length < 2) { if (split.length < 2) {
throw new ActionLookupException( return Result.failure(new ActionLookupException(
"Invalid action key format: " + actionKey, "Invalid action key format: " + actionKey,
actionKey, actionKey,
"META_ACTION" "META_ACTION"
); ));
} }
MetaAction.Type type = switch (split[0]) { MetaAction.Type type = switch (split[0]) {
case BUILTIN_LOCATION -> MetaAction.Type.BUILTIN; case BUILTIN_LOCATION -> MetaAction.Type.BUILTIN;
case ORIGIN_LOCATION -> MetaAction.Type.ORIGIN; case ORIGIN_LOCATION -> MetaAction.Type.ORIGIN;
default -> MetaAction.Type.MCP; default -> MetaAction.Type.MCP;
}; };
return new MetaAction( return Result.success(new MetaAction(
split[1], split[1],
metaActionInfo.getIo(), metaActionInfo.getIo(),
metaActionInfo.getLauncher(), metaActionInfo.getLauncher(),
type, type,
split[0] split[0]
); ));
} }
@CapabilityMethod @CapabilityMethod
public MetaActionInfo loadMetaActionInfo(@NonNull String actionKey) { public Result<MetaActionInfo> loadMetaActionInfo(@NonNull String actionKey) {
MetaActionInfo info = existedMetaActions.get(actionKey); MetaActionInfo info = existedMetaActions.get(actionKey);
if (info == null) { if (info == null) {
throw new ActionLookupException( return Result.failure(new ActionLookupException(
"Meta action description not found for action key: " + actionKey, "Meta action description not found for action key: " + actionKey,
actionKey, actionKey,
"META_ACTION_INFO" "META_ACTION_INFO"
); ));
} }
return info; return Result.success(info);
} }
@CapabilityMethod @CapabilityMethod
@@ -175,7 +177,6 @@ public class ActionCore implements StateSerializable {
if (executableAction == null) { if (executableAction == null) {
return; return;
} }
// 加锁确保同步 // 加锁确保同步
synchronized (executableAction.getStatus()) { synchronized (executableAction.getStatus()) {
applyInterventions(interventions, executableAction); applyInterventions(interventions, executableAction);
@@ -188,10 +189,12 @@ public class ActionCore implements StateSerializable {
interventions.sort(Comparator.comparingInt(MetaIntervention::getOrder)); interventions.sort(Comparator.comparingInt(MetaIntervention::getOrder));
for (MetaIntervention intervention : interventions) { for (MetaIntervention intervention : interventions) {
List<MetaAction> actions = intervention.getActions() Result<List<MetaAction>> actionsResult = resolveInterventionActions(intervention);
.stream() if (actionsResult.isFailure()) {
.map(this::loadMetaAction) reportLookupFailure(actionsResult.exceptionOrNull());
.toList(); continue;
}
List<MetaAction> actions = actionsResult.getOrNull();
switch (intervention.getType()) { switch (intervention.getType()) {
case InterventionType.APPEND -> handleAppend(executableAction, intervention.getOrder(), actions); case InterventionType.APPEND -> handleAppend(executableAction, intervention.getOrder(), actions);
@@ -210,6 +213,27 @@ public class ActionCore implements StateSerializable {
} }
private Result<List<MetaAction>> resolveInterventionActions(MetaIntervention intervention) {
List<MetaAction> actions = new ArrayList<>();
for (String actionKey : intervention.getActions()) {
Result<MetaAction> metaActionResult = loadMetaAction(actionKey);
if (metaActionResult.isFailure()) {
Throwable throwable = metaActionResult.exceptionOrNull();
return Result.failure(throwable == null
? new ActionLookupException("Meta action lookup failed: " + actionKey, actionKey, "META_ACTION")
: throwable);
}
actions.add(metaActionResult.getOrNull());
}
return Result.success(actions);
}
private void reportLookupFailure(Throwable throwable) {
if (throwable instanceof ActionLookupException lookupException) {
ExceptionReporterHandler.INSTANCE.report(lookupException);
}
}
/** /**
* 在未进入执行阶段的行动单元组新增新的行动 * 在未进入执行阶段的行动单元组新增新的行动
*/ */

View File

@@ -102,7 +102,7 @@ sealed class ExecutableAction(
/** /**
* 行动结果 * 行动结果
*/ */
lateinit var result: String var result: String? = null
val history: MutableMap<Int, MutableList<HistoryAction>> = mutableMapOf() val history: MutableMap<Int, MutableList<HistoryAction>> = mutableMapOf()
@@ -142,7 +142,7 @@ sealed class ExecutableAction(
tendency = tendency, tendency = tendency,
actionChainSize = actionChain.size, actionChainSize = actionChain.size,
executingStage = executingStage, executingStage = executingStage,
result = if (::result.isInitialized) result else null, result = result,
history = history.mapValues { (_, value) -> value.toList() }, history = history.mapValues { (_, value) -> value.toList() },
scheduleType = schedulable?.scheduleType, scheduleType = schedulable?.scheduleType,
scheduleContent = schedulable?.scheduleContent, scheduleContent = schedulable?.scheduleContent,
@@ -169,7 +169,7 @@ data class SchedulableExecutableAction @JvmOverloads constructor(
val scheduleHistories = ArrayList<ScheduleHistory>() val scheduleHistories = ArrayList<ScheduleHistory>()
fun recordAndReset() { fun recordAndReset() {
val newHistory = ScheduleHistory(ZonedDateTime.now(), result, history.toMap()) val newHistory = ScheduleHistory(ZonedDateTime.now(), result ?: "null", history.toMap())
scheduleHistories.add(newHistory) scheduleHistories.add(newHistory)
executingStage = 0 executingStage = 0

View File

@@ -15,8 +15,11 @@ import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.ContextBlock;
import work.slhaf.partner.core.cognition.ContextWorkspace; import work.slhaf.partner.core.cognition.ContextWorkspace;
import work.slhaf.partner.framework.agent.exception.AgentException;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent; import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
import work.slhaf.partner.framework.agent.support.Result;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
@@ -291,7 +294,8 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
Set.of(), Set.of(),
true, true,
JSONObject.of( JSONObject.of(
"result", "Intervene status." "ok", "Intervene status",
"result", "Intervene result or failed message."
) )
); );
@@ -305,6 +309,15 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
Integer order = BuiltinActionRegistry.BuiltinActionDefinition.requireInt(params, "order"); Integer order = BuiltinActionRegistry.BuiltinActionDefinition.requireInt(params, "order");
List<String> actions = requireActions(params, type); List<String> actions = requireActions(params, type);
ExecutableAction target = requireTargetAction(targetId); ExecutableAction target = requireTargetAction(targetId);
Result<Void> validationResult = validateActionKeys(actions);
if (validationResult.isFailure()) {
reportFailure(validationResult.exceptionOrNull());
Throwable throwable = validationResult.exceptionOrNull();
return JSONObject.of(
"ok", false,
"result", throwable == null ? "Intervention action validation failed" : throwable.getLocalizedMessage()
).toJSONString();
}
MetaIntervention intervention = new MetaIntervention(); MetaIntervention intervention = new MetaIntervention();
intervention.setType(type); intervention.setType(type);
@@ -383,4 +396,26 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
.orElseThrow(() -> new IllegalArgumentException("未找到对应的 Action: " + targetId)); .orElseThrow(() -> new IllegalArgumentException("未找到对应的 Action: " + targetId));
} }
private Result<Void> validateActionKeys(List<String> actions) {
for (String actionKey : actions) {
Result<work.slhaf.partner.core.action.entity.MetaAction> metaActionResult = actionCapability.loadMetaAction(actionKey);
if (metaActionResult.isFailure()) {
Throwable throwable = metaActionResult.exceptionOrNull();
return Result.failure(throwable == null
? new work.slhaf.partner.core.action.exception.ActionLookupException(
"Meta action lookup failed: " + actionKey,
actionKey,
"META_ACTION"
) : throwable);
}
}
return Result.success(null);
}
private void reportFailure(Throwable throwable) {
if (throwable instanceof AgentException agentException) {
ExceptionReporterHandler.INSTANCE.report(agentException);
}
}
} }

View File

@@ -7,11 +7,14 @@ 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.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.framework.agent.exception.AgentException;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import work.slhaf.partner.framework.agent.factory.context.Shutdown; import work.slhaf.partner.framework.agent.factory.context.Shutdown;
import work.slhaf.partner.framework.agent.support.Result;
import work.slhaf.partner.module.action.executor.entity.*; import work.slhaf.partner.module.action.executor.entity.*;
import java.util.*; import java.util.*;
@@ -311,7 +314,14 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
val executingStage = actionData.getExecutingStage(); val executingStage = actionData.getExecutingStage();
val extractorInput = assemblyHelper.buildExtractorInput(metaAction.getKey(), actionData.getUuid(), actionData.getDescription()); Result<ExtractorInput> extractorInputResult = assemblyHelper.buildExtractorInput(metaAction.getKey(), actionData.getUuid(), actionData.getDescription());
if (extractorInputResult.isFailure()) {
reportFailure(extractorInputResult.exceptionOrNull());
Throwable throwable = extractorInputResult.exceptionOrNull();
failureReason = buildAttemptFailureReason("参数提取失败", throwable == null ? null : throwable.getLocalizedMessage());
break;
}
val extractorInput = extractorInputResult.getOrNull();
ExtractorResult extractorResult = paramsExtractor.execute(extractorInput); ExtractorResult extractorResult = paramsExtractor.execute(extractorInput);
if (extractorResult == null || !extractorResult.isOk()) { if (extractorResult == null || !extractorResult.isOk()) {
@@ -331,7 +341,7 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
} }
if (result.getStatus() == MetaAction.Result.Status.SUCCESS) { if (result.getStatus() == MetaAction.Result.Status.SUCCESS) {
val historyAction = new HistoryAction(actionKey, actionCapability.loadMetaActionInfo(actionKey).getDescription(), result.getData()); val historyAction = new HistoryAction(actionKey, resolveHistoryDescription(actionKey), result.getData());
actionData.getHistory() actionData.getHistory()
.computeIfAbsent(executingStage, integer -> new ArrayList<>()) .computeIfAbsent(executingStage, integer -> new ArrayList<>())
.add(historyAction); .add(historyAction);
@@ -497,6 +507,24 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
} }
} }
private String resolveHistoryDescription(String actionKey) {
Result<MetaActionInfo> metaActionInfoResult = actionCapability.loadMetaActionInfo(actionKey);
if (metaActionInfoResult.isFailure()) {
reportFailure(metaActionInfoResult.exceptionOrNull());
return actionKey;
}
MetaActionInfo metaActionInfo = metaActionInfoResult.getOrNull();
return metaActionInfo == null || metaActionInfo.getDescription().isBlank()
? actionKey
: metaActionInfo.getDescription();
}
private void reportFailure(Throwable throwable) {
if (throwable instanceof AgentException agentException) {
ExceptionReporterHandler.INSTANCE.report(agentException);
}
}
private record MetaActionsListeningRecord(AtomicBoolean accepting, int phase) { private record MetaActionsListeningRecord(AtomicBoolean accepting, int phase) {
} }
@@ -511,12 +539,22 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
private AssemblyHelper() { private AssemblyHelper() {
} }
private ExtractorInput buildExtractorInput(String actionKey, @NotNull String uuid, @NotNull String description) { private Result<ExtractorInput> buildExtractorInput(String actionKey, @NotNull String uuid, @NotNull String description) {
Result<MetaActionInfo> metaActionInfoResult = actionCapability.loadMetaActionInfo(actionKey);
if (metaActionInfoResult.isFailure()) {
Throwable throwable = metaActionInfoResult.exceptionOrNull();
return Result.failure(throwable == null
? new work.slhaf.partner.core.action.exception.ActionLookupException(
"Meta action description not found for action key: " + actionKey,
actionKey,
"META_ACTION_INFO"
) : throwable);
}
ExtractorInput input = new ExtractorInput(); ExtractorInput input = new ExtractorInput();
input.setMetaActionInfo(actionCapability.loadMetaActionInfo(actionKey)); input.setMetaActionInfo(metaActionInfoResult.getOrNull());
input.setTargetActionId(uuid); input.setTargetActionId(uuid);
input.setTargetActionDesc(description); input.setTargetActionDesc(description);
return input; return Result.success(input);
} }
private CorrectorInput buildCorrectorInput(ExecutableAction executableAction) { private CorrectorInput buildCorrectorInput(ExecutableAction executableAction) {
@@ -525,7 +563,7 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
List<CorrectorInput.ActionChainItem> overviewItems = list.stream() List<CorrectorInput.ActionChainItem> overviewItems = list.stream()
.map(metaAction -> new CorrectorInput.ActionChainItem( .map(metaAction -> new CorrectorInput.ActionChainItem(
metaAction.getKey(), metaAction.getKey(),
actionCapability.loadMetaActionInfo(metaAction.getKey()).getDescription(), resolveHistoryDescription(metaAction.getKey()),
metaAction.getResult().getStatus().name().toLowerCase(Locale.ROOT) metaAction.getResult().getStatus().name().toLowerCase(Locale.ROOT)
)) ))
.toList(); .toList();

View File

@@ -11,10 +11,12 @@ import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.CommunicationBlockContent; import work.slhaf.partner.core.cognition.CommunicationBlockContent;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.ContextBlock;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import work.slhaf.partner.framework.agent.support.Result;
import work.slhaf.partner.module.action.executor.ActionExecutor; import work.slhaf.partner.module.action.executor.ActionExecutor;
import work.slhaf.partner.module.action.planner.evaluator.ActionEvaluator; import work.slhaf.partner.module.action.planner.evaluator.ActionEvaluator;
import work.slhaf.partner.module.action.planner.evaluator.entity.EvaluatorInput; import work.slhaf.partner.module.action.planner.evaluator.entity.EvaluatorInput;
@@ -147,6 +149,9 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
continue; continue;
} }
ExecutableAction executableAction = assemblyHelper.buildActionData(evaluatorResult, source); ExecutableAction executableAction = assemblyHelper.buildActionData(evaluatorResult, source);
if (executableAction == null) {
continue;
}
passedActions.add(executableAction); passedActions.add(executableAction);
if (evaluatorResult.isNeedConfirm()) { if (evaluatorResult.isNeedConfirm()) {
registerPendingContextBlock(executableAction, evaluatorResult, input); registerPendingContextBlock(executableAction, evaluatorResult, input);
@@ -346,6 +351,9 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
private ExecutableAction buildActionData(EvaluatorResult evaluatorResult, String userId) { private ExecutableAction buildActionData(EvaluatorResult evaluatorResult, String userId) {
Map<Integer, List<MetaAction>> actionChain = getActionChain(evaluatorResult); Map<Integer, List<MetaAction>> actionChain = getActionChain(evaluatorResult);
if (actionChain == null) {
return null;
}
return switch (evaluatorResult.getType()) { return switch (evaluatorResult.getType()) {
case PLANNING -> new SchedulableExecutableAction( case PLANNING -> new SchedulableExecutableAction(
evaluatorResult.getTendency(), evaluatorResult.getTendency(),
@@ -366,20 +374,28 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
}; };
} }
private @NotNull Map<Integer, List<MetaAction>> getActionChain(EvaluatorResult evaluatorResult) { private Map<Integer, List<MetaAction>> getActionChain(EvaluatorResult evaluatorResult) {
Map<Integer, List<MetaAction>> actionChain = new HashMap<>(); Map<Integer, List<MetaAction>> actionChain = new HashMap<>();
Map<Integer, List<String>> primaryActionChain = evaluatorResult.getPrimaryActionChain(); Map<Integer, List<String>> primaryActionChain = evaluatorResult.getPrimaryActionChain();
fixDependencies(primaryActionChain); if (!fixDependencies(primaryActionChain)) {
primaryActionChain.forEach((order, actionKeys) -> { return null;
List<MetaAction> metaActions = actionKeys.stream() }
.map(actionKey -> actionCapability.loadMetaAction(actionKey)) for (Map.Entry<Integer, List<String>> entry : primaryActionChain.entrySet()) {
.toList(); List<MetaAction> metaActions = new ArrayList<>();
actionChain.put(order, metaActions); for (String actionKey : entry.getValue()) {
}); Result<MetaAction> metaActionResult = actionCapability.loadMetaAction(actionKey);
if (metaActionResult.isFailure()) {
reportFailure(metaActionResult);
return null;
}
metaActions.add(metaActionResult.getOrNull());
}
actionChain.put(entry.getKey(), metaActions);
}
return actionChain; return actionChain;
} }
private void fixDependencies(Map<Integer, List<String>> primaryActionChain) { private boolean fixDependencies(Map<Integer, List<String>> primaryActionChain) {
// 先将 primaryActionChain 的节点序号修正为从1开始依次增大 // 先将 primaryActionChain 的节点序号修正为从1开始依次增大
fixOrder(primaryActionChain); fixOrder(primaryActionChain);
List<Integer> fixedOrders = new ArrayList<>(primaryActionChain.keySet().stream().toList()); List<Integer> fixedOrders = new ArrayList<>(primaryActionChain.keySet().stream().toList());
@@ -392,7 +408,12 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
List<String> actionKeys = primaryActionChain.get(fixedOrder); List<String> actionKeys = primaryActionChain.get(fixedOrder);
for (String actionKey : actionKeys) { for (String actionKey : actionKeys) {
// 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖 // 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖
MetaActionInfo metaActionInfo = actionCapability.loadMetaActionInfo(actionKey); Result<MetaActionInfo> metaActionInfoResult = actionCapability.loadMetaActionInfo(actionKey);
if (metaActionInfoResult.isFailure()) {
reportFailure(metaActionInfoResult);
return false;
}
MetaActionInfo metaActionInfo = metaActionInfoResult.getOrNull();
Set<String> preActions = metaActionInfo.getPreActions(); Set<String> preActions = metaActionInfo.getPreActions();
boolean preActionsExist = preActions.isEmpty(); boolean preActionsExist = preActions.isEmpty();
if (!preActionsExist) { if (!preActionsExist) {
@@ -420,6 +441,13 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
fixedOrders.clear(); fixedOrders.clear();
fixedOrders.addAll(tempOrders); fixedOrders.addAll(tempOrders);
} while (fixed.getAndSet(false)); } while (fixed.getAndSet(false));
return true;
}
private void reportFailure(Result<?> result) {
if (result.exceptionOrNull() instanceof work.slhaf.partner.framework.agent.exception.AgentException agentException) {
ExceptionReporterHandler.INSTANCE.report(agentException);
}
} }
private void fixOrder(Map<Integer, List<String>> primaryActionChain) { private void fixOrder(Map<Integer, List<String>> primaryActionChain) {

View File

@@ -7,6 +7,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDir;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.exception.ActionLookupException;
import work.slhaf.partner.framework.agent.support.Result;
import work.slhaf.partner.module.action.executor.entity.HistoryAction; import work.slhaf.partner.module.action.executor.entity.HistoryAction;
import java.nio.file.Path; import java.nio.file.Path;
@@ -331,4 +333,32 @@ class ActionCoreTest {
assertEquals(1, schedulableAction.getScheduleHistories().size()); assertEquals(1, schedulableAction.getScheduleHistories().size());
assertEquals("cycle-result", schedulableAction.getScheduleHistories().getFirst().getResult()); assertEquals("cycle-result", schedulableAction.getScheduleHistories().getFirst().getResult());
} }
@Test
void shouldReturnResultForMetaActionLookup() {
MetaActionInfo metaActionInfo = new MetaActionInfo(
true,
"python",
Map.of(),
"demo",
Set.of(),
Set.of(),
Set.of(),
false,
JSONObject.of()
);
actionCore.registerMetaActions(Map.of("builtin::demo", metaActionInfo));
Result<MetaAction> success = actionCore.loadMetaAction("builtin::demo");
assertTrue(success.isSuccess());
assertEquals("demo", success.getOrNull().getName());
Result<MetaAction> failure = actionCore.loadMetaAction("builtin::missing");
assertTrue(failure.isFailure());
assertInstanceOf(ActionLookupException.class, failure.exceptionOrNull());
Result<MetaActionInfo> infoFailure = actionCore.loadMetaActionInfo("builtin::missing");
assertTrue(infoFailure.isFailure());
assertInstanceOf(ActionLookupException.class, infoFailure.exceptionOrNull());
}
} }

View File

@@ -9,6 +9,7 @@ import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextWorkspace; import work.slhaf.partner.core.cognition.ContextWorkspace;
import work.slhaf.partner.framework.agent.support.Result;
import work.slhaf.partner.module.action.executor.entity.ExtractorResult; import work.slhaf.partner.module.action.executor.entity.ExtractorResult;
import work.slhaf.partner.module.action.executor.entity.HistoryAction; import work.slhaf.partner.module.action.executor.entity.HistoryAction;
@@ -121,7 +122,7 @@ class ActionExecutorTest {
when(actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM)).thenReturn(platformExecutor); when(actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM)).thenReturn(platformExecutor);
when(actionCapability.runnerClient()).thenReturn(runnerClient); when(actionCapability.runnerClient()).thenReturn(runnerClient);
when(actionCapability.listActions(Action.Status.EXECUTING, null)).thenReturn(Set.of()); when(actionCapability.listActions(Action.Status.EXECUTING, null)).thenReturn(Set.of());
when(actionCapability.loadMetaActionInfo(anyString())).thenReturn(new MetaActionInfo( when(actionCapability.loadMetaActionInfo(anyString())).thenReturn(Result.success(new MetaActionInfo(
false, false,
null, null,
Map.of(), Map.of(),
@@ -131,7 +132,7 @@ class ActionExecutorTest {
Set.of(), Set.of(),
false, false,
new com.alibaba.fastjson2.JSONObject() new com.alibaba.fastjson2.JSONObject()
)); )));
when(cognitionCapability.contextWorkspace()).thenReturn(new ContextWorkspace()); when(cognitionCapability.contextWorkspace()).thenReturn(new ContextWorkspace());
ExtractorResult extractorResult = new ExtractorResult(); ExtractorResult extractorResult = new ExtractorResult();
@@ -186,6 +187,130 @@ class ActionExecutorTest {
assertEquals("rerun-ok", metaAction.getResult().getData()); assertEquals("rerun-ok", metaAction.getResult().getData());
} }
@Test
void shouldMarkMetaActionFailedWhenMetaActionInfoLookupFailsBeforeExtraction() throws Exception {
ActionCapability actionCapability = Mockito.mock(ActionCapability.class);
CognitionCapability cognitionCapability = Mockito.mock(CognitionCapability.class);
ParamsExtractor paramsExtractor = Mockito.mock(ParamsExtractor.class);
ActionCorrector actionCorrector = Mockito.mock(ActionCorrector.class);
ActionCorrectionRecognizer actionCorrectionRecognizer = Mockito.mock(ActionCorrectionRecognizer.class);
RunnerClient runnerClient = Mockito.mock(RunnerClient.class);
ExecutorService virtualExecutor = registerExecutor(Executors.newFixedThreadPool(2));
ExecutorService platformExecutor = registerExecutor(Executors.newFixedThreadPool(2));
when(actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL)).thenReturn(virtualExecutor);
when(actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM)).thenReturn(platformExecutor);
when(actionCapability.runnerClient()).thenReturn(runnerClient);
when(actionCapability.listActions(Action.Status.EXECUTING, null)).thenReturn(Set.of());
when(actionCapability.loadMetaActionInfo(anyString())).thenReturn(Result.failure(new work.slhaf.partner.core.action.exception.ActionLookupException(
"missing",
"builtin::command",
"META_ACTION_INFO"
)));
when(cognitionCapability.contextWorkspace()).thenReturn(new ContextWorkspace());
ActionExecutor actionExecutor = new ActionExecutor();
inject(actionExecutor, "actionCapability", actionCapability);
inject(actionExecutor, "cognitionCapability", cognitionCapability);
inject(actionExecutor, "paramsExtractor", paramsExtractor);
inject(actionExecutor, "actionCorrector", actionCorrector);
inject(actionExecutor, "actionCorrectionRecognizer", actionCorrectionRecognizer);
actionExecutor.init();
MetaAction metaAction = metaAction("command");
ImmediateExecutableAction action = new ImmediateExecutableAction(
"urgent",
actionChain(metaAction),
"reason",
"desc",
"planner",
"lookup-fail-uuid"
);
actionExecutor.execute(action);
waitUntilFinished(action);
verify(paramsExtractor, never()).execute(any());
verify(runnerClient, never()).submit(any(MetaAction.class));
assertEquals(Action.Status.SUCCESS, action.getStatus());
assertEquals(MetaAction.Result.Status.FAILED, metaAction.getResult().getStatus());
assertTrue(metaAction.getResult().getData().contains("参数提取失败"));
assertEquals(metaAction.getResult().getData(), action.getResult());
}
@Test
void shouldFallbackToActionKeyWhenHistoryDescriptionLookupFails() throws Exception {
ActionCapability actionCapability = Mockito.mock(ActionCapability.class);
CognitionCapability cognitionCapability = Mockito.mock(CognitionCapability.class);
ParamsExtractor paramsExtractor = Mockito.mock(ParamsExtractor.class);
ActionCorrector actionCorrector = Mockito.mock(ActionCorrector.class);
ActionCorrectionRecognizer actionCorrectionRecognizer = Mockito.mock(ActionCorrectionRecognizer.class);
RunnerClient runnerClient = Mockito.mock(RunnerClient.class);
ExecutorService virtualExecutor = registerExecutor(Executors.newFixedThreadPool(2));
ExecutorService platformExecutor = registerExecutor(Executors.newFixedThreadPool(2));
when(actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL)).thenReturn(virtualExecutor);
when(actionCapability.getExecutor(ActionCore.ExecutorType.PLATFORM)).thenReturn(platformExecutor);
when(actionCapability.runnerClient()).thenReturn(runnerClient);
when(actionCapability.listActions(Action.Status.EXECUTING, null)).thenReturn(Set.of());
when(cognitionCapability.contextWorkspace()).thenReturn(new ContextWorkspace());
when(actionCapability.loadMetaActionInfo(anyString()))
.thenReturn(Result.success(new MetaActionInfo(
false,
null,
Map.of(),
"demo action",
Set.of(),
Set.of(),
Set.of(),
false,
new com.alibaba.fastjson2.JSONObject()
)))
.thenReturn(Result.failure(new work.slhaf.partner.core.action.exception.ActionLookupException(
"missing desc",
"builtin::command",
"META_ACTION_INFO"
)));
ExtractorResult extractorResult = new ExtractorResult();
extractorResult.setOk(true);
extractorResult.setParams(Map.of("fresh", "value"));
when(paramsExtractor.execute(any())).thenReturn(extractorResult);
doAnswer(invocation -> {
MetaAction metaAction = invocation.getArgument(0);
metaAction.getResult().setStatus(MetaAction.Result.Status.SUCCESS);
metaAction.getResult().setData("history-ok");
return null;
}).when(runnerClient).submit(any(MetaAction.class));
ActionExecutor actionExecutor = new ActionExecutor();
inject(actionExecutor, "actionCapability", actionCapability);
inject(actionExecutor, "cognitionCapability", cognitionCapability);
inject(actionExecutor, "paramsExtractor", paramsExtractor);
inject(actionExecutor, "actionCorrector", actionCorrector);
inject(actionExecutor, "actionCorrectionRecognizer", actionCorrectionRecognizer);
actionExecutor.init();
MetaAction metaAction = metaAction("command");
ImmediateExecutableAction action = new ImmediateExecutableAction(
"urgent",
actionChain(metaAction),
"reason",
"desc",
"planner",
"history-fallback-uuid"
);
actionExecutor.execute(action);
waitUntilFinished(action);
assertEquals(Action.Status.SUCCESS, action.getStatus());
assertEquals("builtin::command", action.getHistory().get(1).getFirst().description());
}
private void waitUntilFinished(ImmediateExecutableAction action) throws InterruptedException { private void waitUntilFinished(ImmediateExecutableAction action) throws InterruptedException {
long deadline = System.currentTimeMillis() + 3000; long deadline = System.currentTimeMillis() + 3000;
while (System.currentTimeMillis() < deadline) { while (System.currentTimeMillis() < deadline) {