推进 ActionExecutor 下的 DynamicActionGenerator 子模块

- 完善了 DynamicActionGenerator 的大致逻辑,序列化逻辑待实现
- 补充了 PhaserRecord 中的阻塞逻辑,使用普通的线程sleep操作
- 调整了 MetaAction 中参数形式,由列表替换为 Map,便于执行时填写参数
- 完善了 DynamicActionGenerator 相关的数据类
This commit is contained in:
2025-12-07 20:10:53 +08:00
parent 6a351413a1
commit 6e3deced77
8 changed files with 116 additions and 22 deletions

View File

@@ -1,12 +1,14 @@
package work.slhaf.partner.core.action.entity;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import static work.slhaf.partner.common.Constant.Path.ACTION_PROGRAM;
import java.io.File;
import java.nio.file.Path;
import java.util.Map;
import static work.slhaf.partner.common.Constant.Path.ACTION_PROGRAM;
import org.jetbrains.annotations.NotNull;
import lombok.Data;
/**
* 行动链中的单一元素,封装了调用外部行动程序的必要信息与结果容器,可被{@link work.slhaf.partner.core.action.ActionCapability}执行
@@ -21,7 +23,7 @@ public class MetaAction implements Comparable<MetaAction> {
/**
* 行动程序可接受的参数,由调用处设置
*/
private String[] params;
private Map<String, String> params;
/**
* 行动结果,包括执行状态和相应内容(执行结果或者错误信息)
*/
@@ -36,18 +38,21 @@ public class MetaAction implements Comparable<MetaAction> {
private boolean io;
/**
* 行动程序类型可分为PLUGIN(jar文件)、SCRIPT(Python程序)、MCP(MCP服务)
* .
*/
private MetaActionType type;
private Path path;
public Path checkAndGetPath() {
Path path = switch (type) {
path = switch (type) {
case PLUGIN -> Path.of(ACTION_PROGRAM, key, "action.jar");
case SCRIPT -> Path.of(ACTION_PROGRAM, key, "action.py");
case MCP -> Path.of(ACTION_PROGRAM, key, "action.json");
};
File action = path.toFile();
if (!action.exists()) {
result.setSuccess(false);
result.setStatus(ResultStatus.FAILED);
result.setData("Action file not found: " + action.getAbsolutePath());
}
return path;
@@ -60,8 +65,14 @@ public class MetaAction implements Comparable<MetaAction> {
@Data
public static class Result {
private boolean success = true;
private ResultStatus status = ResultStatus.WAITING;
private String data = null;
}
public enum ResultStatus {
SUCCESS,
FAILED,
WAITING
}
}

View File

@@ -10,11 +10,24 @@ public record PhaserRecord(Phaser phaser, ActionData actionData) {
actionData.setStatus(ActionStatus.FAILED);
}
/**
* 负责将 ActionData 的状态设置为 INTERRUPTED
* 同时循环检查进行阻塞
*/
public void interrupt() {
actionData.setStatus(ActionStatus.INTERRUPTED);
while (actionData().getStatus() == ActionStatus.INTERRUPTED) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
}
}
/**
* 将状态重新设置为 EXECUTING ,恢复 interrupt 阻塞状态
*/
public void complete() {
actionData().setStatus(ActionStatus.EXECUTING);
}
}

View File

@@ -10,6 +10,7 @@ import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.ActionData.ActionStatus;
import work.slhaf.partner.core.action.entity.MetaAction.ResultStatus;
import work.slhaf.partner.core.cognation.CognationCapability;
import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.*;
@@ -130,7 +131,7 @@ public class ActionExecutor extends AgentRunningSubModule<ActionExecutorInput, V
actionCapability.execute(action);
MetaAction.Result result = action.getResult();
// 该循环对应LLM的调整参数后重试
if (!result.isSuccess()) {
if (!result.getStatus().equals(ResultStatus.SUCCESS)) {
// LLM决策是重构参数、执行自对话反思、还是选择向用户求助(通过cognationCore暴露方法可能需要修改其他模块以进行适应),仅重构参数时无需结束当前循环
// 若使用Phaser作为执行线程与反思、求助等调用流程的同步协调应当需要额外维护Phaser全局字段获取到反思结果或者用户反馈后
// 调用对应的phaser注册任务在ActionExecutor中动态添加任务至actionChain,同时启动异步执行

View File

@@ -127,6 +127,11 @@ public class ActionRepairer extends AgentRunningSubModule<RepairerInput, Repaire
}
});
}
try {
latch.await();
} catch (Exception e) {
log.warn("CountDownLatch 已中断");
}
if (actionKeys.size() - failedCount.get() > 0) {
result.setStatus(RepairerStatus.OK);
} else {
@@ -144,7 +149,7 @@ public class ActionRepairer extends AgentRunningSubModule<RepairerInput, Repaire
@Override
public String modelKey() {
return "";
return "action_repairer";
}
@Override

View File

@@ -1,29 +1,83 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import java.util.List;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
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.api.chat.pojo.ChatResponse;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorResult;
import work.slhaf.partner.common.util.ExtractUtil;
import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaActionType;
/**
* 负责依据输入内容生成可执行的动态行动单元,并选择是否持久化至 SandboxRunner 容器内
*/
@AgentSubModule
public class DynamicActionGenerator extends AgentRunningSubModule<GeneratorInput, GeneratorResult> implements ActivateModel {
public class DynamicActionGenerator extends AgentRunningSubModule<GeneratorInput, GeneratorResult>
implements ActivateModel {
@Override
public GeneratorResult execute(GeneratorInput data) {
public GeneratorResult execute(GeneratorInput input) {
GeneratorResult result = new GeneratorResult();
// 由于 SCRIPT 类型程序都是在 SandboxRunner 内部的磁盘上加载然后执行的,
// 所以此处的输入内容也只需要指定输入参数、临时key、是否持久化即可路径将按照指定规则统一构建不可交给LLM生成
String prompt = buildPrompt(input);
// 响应结果需要包含几个特殊数据: 依赖项、代码内容、是否序列化、响应数据释义
ChatResponse response = this.singleChat(prompt);
GeneratorResponseData generatorData = JSONObject
.parseObject(ExtractUtil.extractJson(response.getMessage()), GeneratorResponseData.class);
MetaAction tempAction = buildAction(input);
waitingSerialize(tempAction, generatorData);
result.setTempAction(tempAction);
return null;
}
/**
* 将临时行动单元序列化至临时文件夹,并设置程序路径、放置在队列中,等待执行状态变化,并根据序列化选项选择是否补充 MetaActionInfo 并持久序列化
*/
private void waitingSerialize(MetaAction tempAction, GeneratorResponseData generatorData) {
}
private MetaAction buildAction(GeneratorInput input) {
MetaAction tempAction = new MetaAction();
tempAction.setKey(input.getKey());
tempAction.setParams(input.getParams());
tempAction.setIo(true);
tempAction.setOrder(-1);
tempAction.setType(MetaActionType.SCRIPT);
return tempAction;
}
private String buildPrompt(GeneratorInput data) {
JSONObject prompt = new JSONObject();
prompt.put("[行动描述]", data.getDescription());
// prompt.putObject("[行动参数]").putAll(data.getParams());
prompt.putObject("[行动参数描述]").putAll(data.getParamsDescription());
return prompt.toString();
}
@Override
public String modelKey() {
return "";
return "dynamic_generator";
}
@Override
public boolean withBasicPrompt() {
return false;
}
@Data
private class GeneratorResponseData {
private List<String> dependencies;
private String code;
private boolean serialize;
private JSONObject responseSchema;
}
}

View File

@@ -1,7 +1,11 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor;
import java.util.HashMap;
import java.util.List;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
@@ -12,8 +16,6 @@ import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.Extra
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.ExtractorResult;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction;
import java.util.List;
/**
* 负责依据输入内容进行行动单元的参数信息提取
*/
@@ -32,7 +34,7 @@ public class ParamsExtractor extends AgentRunningSubModule<ExtractorInput, Extra
log.error("ParamsExtractor解析结果失败返回内容{}", response.getMessage(), e);
result = new ExtractorResult();
result.setOk(false);
result.setParams(new String[0]);
result.setParams(new HashMap<>());
}
return result;
}

View File

@@ -1,9 +1,11 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import java.util.Map;
import lombok.Data;
@Data
public class ExtractorResult {
private boolean ok;
private String[] params;
private Map<String, String> params;
}

View File

@@ -1,7 +1,13 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import java.util.Map;
import lombok.Data;
@Data
public class GeneratorInput {
private String key;
private Map<String, String> params;
private String description;
private Map<String, String> paramsDescription;
}