feat(action): add stage descriptions to action chains

This commit is contained in:
2026-04-27 17:54:38 +08:00
parent f957d7caa4
commit 822ea82593
7 changed files with 53 additions and 8 deletions

View File

@@ -76,6 +76,10 @@ final class ActionPoolStateCodec {
.<StateValue>map(entry -> { .<StateValue>map(entry -> {
Map<String, StateValue> stageMap = new LinkedHashMap<>(); Map<String, StateValue> stageMap = new LinkedHashMap<>();
stageMap.put("stage", StateValue.num(entry.getKey())); stageMap.put("stage", StateValue.num(entry.getKey()));
String stageDescription = action.getStageDescriptions().get(entry.getKey());
if (stageDescription != null && !stageDescription.isBlank()) {
stageMap.put("description", StateValue.str(stageDescription));
}
stageMap.put("actions", StateValue.arr(entry.getValue().stream() stageMap.put("actions", StateValue.arr(entry.getValue().stream()
.map(metaAction -> (StateValue) encodeMetaAction(metaAction)) .map(metaAction -> (StateValue) encodeMetaAction(metaAction))
.toList())); .toList()));
@@ -126,7 +130,11 @@ final class ActionPoolStateCodec {
return null; return null;
} }
Map<Integer, List<MetaAction>> restoredChain = decodeActionChain(actionObject.getJSONArray("action_chain")); Map<Integer, String> restoredStageDescriptions = new LinkedHashMap<>();
Map<Integer, List<MetaAction>> restoredChain = decodeActionChain(
actionObject.getJSONArray("action_chain"),
restoredStageDescriptions
);
ExecutableAction executableAction; ExecutableAction executableAction;
if ("schedulable".equals(kind)) { if ("schedulable".equals(kind)) {
String scheduleType = actionObject.getString("schedule_type"); String scheduleType = actionObject.getString("schedule_type");
@@ -173,11 +181,15 @@ final class ActionPoolStateCodec {
if (result != null) { if (result != null) {
executableAction.setResult(result); executableAction.setResult(result);
} }
executableAction.getStageDescriptions().putAll(restoredStageDescriptions);
executableAction.getHistory().putAll(decodeHistory(actionObject.getJSONArray("history"))); executableAction.getHistory().putAll(decodeHistory(actionObject.getJSONArray("history")));
return executableAction; return executableAction;
} }
private static Map<Integer, List<MetaAction>> decodeActionChain(@Nullable JSONArray actionChainArray) { private static Map<Integer, List<MetaAction>> decodeActionChain(
@Nullable JSONArray actionChainArray,
Map<Integer, String> stageDescriptions
) {
Map<Integer, List<MetaAction>> restored = new LinkedHashMap<>(); Map<Integer, List<MetaAction>> restored = new LinkedHashMap<>();
if (actionChainArray == null) { if (actionChainArray == null) {
return toMutableActionChain(restored); return toMutableActionChain(restored);
@@ -188,10 +200,14 @@ final class ActionPoolStateCodec {
continue; continue;
} }
Integer stage = stageObject.getInteger("stage"); Integer stage = stageObject.getInteger("stage");
String description = stageObject.getString("description");
JSONArray actions = stageObject.getJSONArray("actions"); JSONArray actions = stageObject.getJSONArray("actions");
if (stage == null || actions == null) { if (stage == null || actions == null) {
continue; continue;
} }
if (description != null && !description.isBlank()) {
stageDescriptions.put(stage, description);
}
List<MetaAction> metaActions = new ArrayList<>(); List<MetaAction> metaActions = new ArrayList<>();
for (int j = 0; j < actions.size(); j++) { for (int j = 0; j < actions.size(); j++) {
JSONObject actionObject = actions.getJSONObject(j); JSONObject actionObject = actions.getJSONObject(j);

View File

@@ -96,6 +96,11 @@ sealed class ExecutableAction(
*/ */
abstract val actionChain: MutableMap<Int, MutableList<MetaAction>> abstract val actionChain: MutableMap<Int, MutableList<MetaAction>>
/**
* 行动阶段描述,用于为每个 stage 提供可落地的执行目标说明
*/
val stageDescriptions: MutableMap<Int, String> = mutableMapOf()
/** /**
* 行动阶段(当前阶段) * 行动阶段(当前阶段)
*/ */
@@ -143,6 +148,7 @@ sealed class ExecutableAction(
status = status, status = status,
tendency = tendency, tendency = tendency,
actionChainSize = actionChain.size, actionChainSize = actionChain.size,
stageDescriptions = stageDescriptions.toMap(),
executingStage = executingStage, executingStage = executingStage,
result = result, result = result,
history = history.mapValues { (_, value) -> value.toList() }, history = history.mapValues { (_, value) -> value.toList() },
@@ -268,6 +274,7 @@ data class ExecutableActionSnapshot(
val tendency: String, val tendency: String,
val actionChainSize: Int, val actionChainSize: Int,
val stageDescriptions: Map<Int, String>,
val executingStage: Int, val executingStage: Int,
val result: String?, val result: String?,
val history: Map<Int, List<HistoryAction>>, val history: Map<Int, List<HistoryAction>>,

View File

@@ -406,7 +406,12 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
result.reset(); result.reset();
metaAction.getParams().clear(); metaAction.getParams().clear();
Result<ExtractorInput> extractorInputResult = assemblyHelper.buildExtractorInput(metaAction.getKey(), actionData.getUuid(), actionData.getDescription()); Result<ExtractorInput> extractorInputResult = assemblyHelper.buildExtractorInput(
metaAction.getKey(),
actionData.getUuid(),
actionData.getDescription(),
resolveCurrentStageDescription(actionData, executingStage)
);
AgentRuntimeException exception = extractorInputResult.exceptionOrNull(); AgentRuntimeException exception = extractorInputResult.exceptionOrNull();
if (exception != null) { if (exception != null) {
failureReason.set(exception.getMessage()); failureReason.set(exception.getMessage());
@@ -512,6 +517,10 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
} }
} }
private String resolveCurrentStageDescription(ExecutableAction executableAction, int executingStage) {
return executableAction.getStageDescriptions().get(executingStage);
}
private String buildAttemptFailureReason(String prefix, String detail) { private String buildAttemptFailureReason(String prefix, String detail) {
if (detail == null || detail.isBlank()) { if (detail == null || detail.isBlank()) {
return prefix; return prefix;
@@ -745,13 +754,14 @@ public class ActionExecutor extends AbstractAgentModule.Standalone {
private AssemblyHelper() { private AssemblyHelper() {
} }
private Result<ExtractorInput> buildExtractorInput(String actionKey, @NotNull String uuid, @NotNull String description) { private Result<ExtractorInput> buildExtractorInput(String actionKey, @NotNull String uuid, @NotNull String description, String currentStageDescription) {
return actionCapability.loadMetaActionInfo(actionKey).fold( return actionCapability.loadMetaActionInfo(actionKey).fold(
metaActionInfo -> { metaActionInfo -> {
ExtractorInput input = new ExtractorInput(); ExtractorInput input = new ExtractorInput();
input.setMetaActionInfo(metaActionInfo); input.setMetaActionInfo(metaActionInfo);
input.setTargetActionId(uuid); input.setTargetActionId(uuid);
input.setTargetActionDesc(description); input.setTargetActionDesc(description);
input.setCurrentStageDesc(currentStageDescription);
return Result.success(input); return Result.success(input);
}, },
Result::failure Result::failure

View File

@@ -29,7 +29,7 @@ public class ParamsExtractor extends AbstractAgentModule.Sub<ExtractorInput, Res
你会收到: 你会收到:
- 一条结构化上下文消息,其中可能包含当前行动相关状态、近期交流轨迹、以及活跃记忆切片; - 一条结构化上下文消息,其中可能包含当前行动相关状态、近期交流轨迹、以及活跃记忆切片;
- 一条任务消息,其中包含: - 一条任务消息,其中包含:
- target_action本次参数提取所面向的目标行动用于帮助你判断上下文中哪些内容与当前提取直接相关 - target_action本次参数提取所面向的目标行动用于帮助你判断上下文中哪些内容与当前提取直接相关其中 current_stage_description 表示本次 MetaAction 所属阶段的具体执行目标,应优先用于约束参数提取;
- meta_action_info该行动对应的说明以及允许提取的参数列表。每个 <param name="..."> 节点的文本内容表示该参数的含义或期望内容。 - meta_action_info该行动对应的说明以及允许提取的参数列表。每个 <param name="..."> 节点的文本内容表示该参数的含义或期望内容。
你的任务: 你的任务:
@@ -78,6 +78,9 @@ public class ParamsExtractor extends AbstractAgentModule.Sub<ExtractorInput, Res
appendChildElement(document, root, "target_action", block -> { appendChildElement(document, root, "target_action", block -> {
appendTextElement(document, block, "uuid", input.getTargetActionId()); appendTextElement(document, block, "uuid", input.getTargetActionId());
appendTextElement(document, block, "description", input.getTargetActionDesc()); appendTextElement(document, block, "description", input.getTargetActionDesc());
if (input.getCurrentStageDesc() != null && !input.getCurrentStageDesc().isBlank()) {
appendTextElement(document, block, "current_stage_description", input.getCurrentStageDesc());
}
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
appendChildElement(document, root, "meta_action_info", element -> { appendChildElement(document, root, "meta_action_info", element -> {

View File

@@ -16,6 +16,11 @@ public class ExtractorInput {
*/ */
private String targetActionDesc; private String targetActionDesc;
/**
* 当前执行阶段的 description
*/
private String currentStageDesc;
/** /**
* 目标 MetaActionInfo * 目标 MetaActionInfo
*/ */

View File

@@ -150,10 +150,13 @@ public class ActionEvaluator extends AbstractAgentModule.Sub<EvaluatorInput, Lis
primaryActionChain primaryActionChain
- 只在 ok=true 时填写。 - 只在 ok=true 时填写。
- 每个元素包含 order 和 actionKeys。 - 每个元素包含 order、description 和 actionKeys。
- 不要写自然语言步骤 - description 是该 stage 的具体执行目标,用一句短句说明本阶段要获得、检查、修改或触发什么;它不是用户回复文案,也不是完整推理过程
- 不要写伪代码。 - 不要写伪代码。
- 若一个能力即可承接,使用单步链 - 若 tendency 包含多个依赖步骤、需要先获取 A 再根据 A 决定 B、需要多次调用同一能力处理不同对象或需要为后续汇报获取多类事实应输出多 order 的 primaryActionChain
- 不要为了减少步骤而把复杂诊断、查找、读取、分析全部压缩成单个 command::execute。
- 同一个 available_meta_action 可以在不同 order 中重复使用,只要每次对应的阶段目标、参数或对象不同。
- 对需要最终汇报的任务,行动链应负责获取支撑汇报所需的事实;最终自然语言汇报由 Communication 基于 action-finished state 完成,不需要把“汇报”本身作为一个 MetaAction。
scheduleData scheduleData
- 仅当 tendency 明确要求未来、周期、延迟、提醒、定时或计划安排时填写。 - 仅当 tendency 明确要求未来、周期、延迟、提醒、定时或计划安排时填写。

View File

@@ -55,6 +55,7 @@ public class EvaluatorResult {
@Data @Data
public static class ChainElement { public static class ChainElement {
private Integer order; private Integer order;
private String description;
private List<String> actionKeys; private List<String> actionKeys;
} }
} }