refactor(action): redefine ExtractorResult structure to support openai sdk better, and make failed meta-action executing should be recorded as history normally

This commit is contained in:
2026-04-16 23:27:46 +08:00
parent 281984bb05
commit 503afecbe2
7 changed files with 86 additions and 20 deletions

View File

@@ -1,6 +1,7 @@
<component name="ProjectDictionaryState"> <component name="ProjectDictionaryState">
<dictionary name="project"> <dictionary name="project">
<words> <words>
<w>openai</w>
<w>zuper</w> <w>zuper</w>
</words> </words>
</dictionary> </dictionary>

View File

@@ -112,6 +112,12 @@ public class BuiltinActionRegistry extends AbstractAgentModule.Standalone {
if (value instanceof Number number) { if (value instanceof Number number) {
return number.intValue(); return number.intValue();
} }
try {
if (value instanceof String string) {
return Integer.parseInt(string);
}
} catch (NumberFormatException ignored) {
}
throw new IllegalArgumentException("参数 " + key + " 必须为整数"); throw new IllegalArgumentException("参数 " + key + " 必须为整数");
} }
@@ -123,6 +129,12 @@ public class BuiltinActionRegistry extends AbstractAgentModule.Standalone {
if (value instanceof Number n) { if (value instanceof Number n) {
return n.intValue(); return n.intValue();
} }
try {
if (value instanceof String string) {
return Integer.parseInt(string);
}
} catch (NumberFormatException ignored) {
}
throw new IllegalArgumentException("参数 " + key + " 必须为整数"); throw new IllegalArgumentException("参数 " + key + " 必须为整数");
} }

View File

@@ -304,13 +304,13 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
private void executeMetaActionWithRetry(MetaAction metaAction, ExecutableAction actionData) { private void executeMetaActionWithRetry(MetaAction metaAction, ExecutableAction actionData) {
AtomicReference<String> failureReason = new AtomicReference<>("参数提取失败"); AtomicReference<String> failureReason = new AtomicReference<>("参数提取失败");
val actionKey = metaAction.getKey(); val actionKey = metaAction.getKey();
int executingStage = actionData.getExecutingStage();
boolean succeeded = false;
for (int attempt = 1; attempt <= MAX_EXTRACTOR_ATTEMPTS; attempt++) { for (int attempt = 1; attempt <= MAX_EXTRACTOR_ATTEMPTS; attempt++) {
val result = metaAction.getResult(); val result = metaAction.getResult();
result.reset(); result.reset();
metaAction.getParams().clear(); metaAction.getParams().clear();
val executingStage = actionData.getExecutingStage();
Result<ExtractorInput> extractorInputResult = assemblyHelper.buildExtractorInput(metaAction.getKey(), actionData.getUuid(), actionData.getDescription()); Result<ExtractorInput> extractorInputResult = assemblyHelper.buildExtractorInput(metaAction.getKey(), actionData.getUuid(), actionData.getDescription());
AgentRuntimeException exception = extractorInputResult.exceptionOrNull(); AgentRuntimeException exception = extractorInputResult.exceptionOrNull();
if (exception != null) { if (exception != null) {
@@ -331,7 +331,7 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
failureReason.set(buildAttemptFailureReason("参数提取失败", null)); failureReason.set(buildAttemptFailureReason("参数提取失败", null));
continue; continue;
} }
metaAction.getParams().putAll(extractorResult.getParams()); metaAction.getParams().putAll(toMetaActionParams(extractorResult.getParams()));
try { try {
runnerClient.submit(metaAction); runnerClient.submit(metaAction);
@@ -341,18 +341,47 @@ 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, resolveHistoryDescription(actionKey), result.getData()); succeeded = true;
actionData.getHistory() break;
.computeIfAbsent(executingStage, integer -> new ArrayList<>())
.add(historyAction);
return;
} }
failureReason.set(buildAttemptFailureReason("行动执行失败", result.getData())); failureReason.set(buildAttemptFailureReason("行动执行失败", result.getData()));
} }
if (!succeeded) {
metaAction.getResult().setStatus(MetaAction.Result.Status.FAILED); metaAction.getResult().setStatus(MetaAction.Result.Status.FAILED);
metaAction.getResult().setData(failureReason.get()); metaAction.getResult().setData(failureReason.get());
} }
appendHistoryAction(actionData, executingStage, metaAction);
}
private Map<String, Object> toMetaActionParams(List<ExtractorResult.ParamEntry> params) {
if (params == null || params.isEmpty()) {
return Map.of();
}
Map<String, Object> converted = new LinkedHashMap<>();
for (ExtractorResult.ParamEntry entry : params) {
if (entry == null) {
continue;
}
String name = entry.getName();
if (name == null || name.isBlank()) {
continue;
}
converted.put(name, entry.getValue());
}
return converted;
}
private void appendHistoryAction(ExecutableAction actionData, int executingStage, MetaAction metaAction) {
HistoryAction historyAction = new HistoryAction(
metaAction.getKey(),
resolveHistoryDescription(metaAction.getKey()),
metaAction.getResult().getData()
);
actionData.getHistory()
.computeIfAbsent(executingStage, integer -> new ArrayList<>())
.add(historyAction);
}
private RecognizerTaskRecord startRecognizerIfNeeded(ExecutableAction executableAction, Phaser phaser) { private RecognizerTaskRecord startRecognizerIfNeeded(ExecutableAction executableAction, Phaser phaser) {
if (!shouldRunCorrectionRecognizer(executableAction)) { if (!shouldRunCorrectionRecognizer(executableAction)) {

View File

@@ -239,7 +239,7 @@ class ExecutingActionBlockManager {
private @NotNull BlockContent buildActionStageAbstractBlock(ExecutableActionSnapshot snapshot, String blockName, String event) { private @NotNull BlockContent buildActionStageAbstractBlock(ExecutableActionSnapshot snapshot, String blockName, String event) {
int settledStage = snapshot.getExecutingStage(); int settledStage = snapshot.getExecutingStage();
List<HistoryAction> history = snapshot.getHistory().get(settledStage); List<HistoryAction> history = resolveStageHistory(snapshot, settledStage);
return new ActionBlockContent(blockName, SOURCE) { return new ActionBlockContent(blockName, SOURCE) {
@Override @Override
@@ -252,7 +252,7 @@ class ExecutingActionBlockManager {
private @NotNull BlockContent buildActionStageCompactBlock(ExecutableActionSnapshot snapshot, String blockName, String emittedAt, String event) { private @NotNull BlockContent buildActionStageCompactBlock(ExecutableActionSnapshot snapshot, String blockName, String emittedAt, String event) {
int settledStage = snapshot.getExecutingStage(); int settledStage = snapshot.getExecutingStage();
List<HistoryAction> history = snapshot.getHistory().get(settledStage); List<HistoryAction> history = resolveStageHistory(snapshot, settledStage);
return new ActionBlockContent(blockName, SOURCE) { return new ActionBlockContent(blockName, SOURCE) {
@Override @Override
@@ -267,7 +267,7 @@ class ExecutingActionBlockManager {
private @NotNull BlockContent buildActionStageFullBlock(ExecutableActionSnapshot snapshot, String blockName, String emittedAt, String event) { private @NotNull BlockContent buildActionStageFullBlock(ExecutableActionSnapshot snapshot, String blockName, String emittedAt, String event) {
int settledStage = snapshot.getExecutingStage(); int settledStage = snapshot.getExecutingStage();
List<HistoryAction> history = snapshot.getHistory().get(settledStage); List<HistoryAction> history = resolveStageHistory(snapshot, settledStage);
return new ActionBlockContent(blockName, SOURCE) { return new ActionBlockContent(blockName, SOURCE) {
@Override @Override
@@ -312,6 +312,11 @@ class ExecutingActionBlockManager {
)); ));
} }
private List<HistoryAction> resolveStageHistory(ExecutableActionSnapshot snapshot, int settledStage) {
List<HistoryAction> history = snapshot.getHistory().get(settledStage);
return history == null ? List.of() : history;
}
private @NotNull BlockContent buildActionCorrectionAbstractBlock(ExecutableActionSnapshot snapshot, List<MetaIntervention> interventions, String blockName, String event) { private @NotNull BlockContent buildActionCorrectionAbstractBlock(ExecutableActionSnapshot snapshot, List<MetaIntervention> interventions, String blockName, String event) {
return new ActionBlockContent(blockName, SOURCE) { return new ActionBlockContent(blockName, SOURCE) {
@Override @Override

View File

@@ -39,9 +39,9 @@ public class ParamsExtractor extends AbstractAgentModule.Sub<ExtractorInput, Res
return new TaskBlock() { return new TaskBlock() {
@Override @Override
protected void fillXml(@NotNull Document document, @NotNull Element root) { protected void fillXml(@NotNull Document document, @NotNull Element root) {
appendChildElement(document, root, "target_action", blok -> { appendChildElement(document, root, "target_action", block -> {
appendTextElement(document, blok, "uuid", input.getTargetActionId()); appendTextElement(document, block, "uuid", input.getTargetActionId());
appendTextElement(document, blok, "description", input.getTargetActionDesc()); appendTextElement(document, block, "description", input.getTargetActionDesc());
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
appendChildElement(document, root, "meta_action_info", element -> { appendChildElement(document, root, "meta_action_info", element -> {

View File

@@ -1,11 +1,19 @@
package work.slhaf.partner.module.action.executor.entity; package work.slhaf.partner.module.action.executor.entity;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.Map; import java.util.List;
@Data @Data
public class ExtractorResult { public class ExtractorResult {
private boolean ok; private boolean ok;
private Map<String, Object> params; private List<ParamEntry> params;
@Data
@AllArgsConstructor
public static class ParamEntry {
private String name;
private String value;
}
} }

View File

@@ -1,7 +1,9 @@
package work.slhaf.partner.module.action.executor; package work.slhaf.partner.module.action.executor;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mockito; import org.mockito.Mockito;
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;
@@ -14,6 +16,7 @@ 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;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -29,6 +32,11 @@ class ActionExecutorTest {
private final List<ExecutorService> executors = new ArrayList<>(); private final List<ExecutorService> executors = new ArrayList<>();
@BeforeAll
static void beforeAll(@TempDir Path tempDir) {
System.setProperty("user.home", tempDir.toAbsolutePath().toString());
}
private static void inject(Object target, String fieldName, Object value) throws Exception { private static void inject(Object target, String fieldName, Object value) throws Exception {
Field field = ActionExecutor.class.getDeclaredField(fieldName); Field field = ActionExecutor.class.getDeclaredField(fieldName);
field.setAccessible(true); field.setAccessible(true);
@@ -137,7 +145,7 @@ class ActionExecutorTest {
ExtractorResult extractorResult = new ExtractorResult(); ExtractorResult extractorResult = new ExtractorResult();
extractorResult.setOk(true); extractorResult.setOk(true);
extractorResult.setParams(Map.of("fresh", "value")); extractorResult.setParams(List.of(new ExtractorResult.ParamEntry("fresh", "value")));
when(paramsExtractor.execute(any())).thenReturn(Result.success(extractorResult)); when(paramsExtractor.execute(any())).thenReturn(Result.success(extractorResult));
doAnswer(invocation -> { doAnswer(invocation -> {
MetaAction metaAction = invocation.getArgument(0); MetaAction metaAction = invocation.getArgument(0);
@@ -242,6 +250,9 @@ class ActionExecutorTest {
assertEquals(MetaAction.Result.Status.FAILED, metaAction.getResult().getStatus()); assertEquals(MetaAction.Result.Status.FAILED, metaAction.getResult().getStatus());
assertTrue(metaAction.getResult().getData().contains("missing")); assertTrue(metaAction.getResult().getData().contains("missing"));
assertEquals(metaAction.getResult().getData(), action.getResult()); assertEquals(metaAction.getResult().getData(), action.getResult());
assertEquals(1, action.getHistory().size());
assertTrue(action.getHistory().containsKey(1));
assertEquals(metaAction.getResult().getData(), action.getHistory().get(1).getFirst().result());
} }
@Test @Test
@@ -282,7 +293,7 @@ class ActionExecutorTest {
ExtractorResult extractorResult = new ExtractorResult(); ExtractorResult extractorResult = new ExtractorResult();
extractorResult.setOk(true); extractorResult.setOk(true);
extractorResult.setParams(Map.of("fresh", "value")); extractorResult.setParams(List.of(new ExtractorResult.ParamEntry("fresh", "value")));
when(paramsExtractor.execute(any())).thenReturn(Result.success(extractorResult)); when(paramsExtractor.execute(any())).thenReturn(Result.success(extractorResult));
doAnswer(invocation -> { doAnswer(invocation -> {
MetaAction metaAction = invocation.getArgument(0); MetaAction metaAction = invocation.getArgument(0);