推进 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; package work.slhaf.partner.core.action.entity;
import lombok.Data; import static work.slhaf.partner.common.Constant.Path.ACTION_PROGRAM;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.nio.file.Path; 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}执行 * 行动链中的单一元素,封装了调用外部行动程序的必要信息与结果容器,可被{@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; private boolean io;
/** /**
* 行动程序类型可分为PLUGIN(jar文件)、SCRIPT(Python程序)、MCP(MCP服务) * 行动程序类型可分为PLUGIN(jar文件)、SCRIPT(Python程序)、MCP(MCP服务)
* .
*/ */
private MetaActionType type; private MetaActionType type;
private Path path;
public Path checkAndGetPath() { public Path checkAndGetPath() {
Path path = switch (type) { path = switch (type) {
case PLUGIN -> Path.of(ACTION_PROGRAM, key, "action.jar"); case PLUGIN -> Path.of(ACTION_PROGRAM, key, "action.jar");
case SCRIPT -> Path.of(ACTION_PROGRAM, key, "action.py"); case SCRIPT -> Path.of(ACTION_PROGRAM, key, "action.py");
case MCP -> Path.of(ACTION_PROGRAM, key, "action.json"); case MCP -> Path.of(ACTION_PROGRAM, key, "action.json");
}; };
File action = path.toFile(); File action = path.toFile();
if (!action.exists()) { if (!action.exists()) {
result.setSuccess(false); result.setStatus(ResultStatus.FAILED);
result.setData("Action file not found: " + action.getAbsolutePath()); result.setData("Action file not found: " + action.getAbsolutePath());
} }
return path; return path;
@@ -60,8 +65,14 @@ public class MetaAction implements Comparable<MetaAction> {
@Data @Data
public static class Result { public static class Result {
private boolean success = true; private ResultStatus status = ResultStatus.WAITING;
private String data = null; 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.setStatus(ActionStatus.FAILED);
} }
/**
* 负责将 ActionData 的状态设置为 INTERRUPTED
* 同时循环检查进行阻塞
*/
public void interrupt() { public void interrupt() {
actionData.setStatus(ActionStatus.INTERRUPTED);
while (actionData().getStatus() == ActionStatus.INTERRUPTED) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
}
} }
/**
* 将状态重新设置为 EXECUTING ,恢复 interrupt 阻塞状态
*/
public void complete() { 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.ActionCore;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.ActionData.ActionStatus; 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.cognation.CognationCapability;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.*; import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.*;
@@ -130,7 +131,7 @@ public class ActionExecutor extends AgentRunningSubModule<ActionExecutorInput, V
actionCapability.execute(action); actionCapability.execute(action);
MetaAction.Result result = action.getResult(); MetaAction.Result result = action.getResult();
// 该循环对应LLM的调整参数后重试 // 该循环对应LLM的调整参数后重试
if (!result.isSuccess()) { if (!result.getStatus().equals(ResultStatus.SUCCESS)) {
// LLM决策是重构参数、执行自对话反思、还是选择向用户求助(通过cognationCore暴露方法可能需要修改其他模块以进行适应),仅重构参数时无需结束当前循环 // LLM决策是重构参数、执行自对话反思、还是选择向用户求助(通过cognationCore暴露方法可能需要修改其他模块以进行适应),仅重构参数时无需结束当前循环
// 若使用Phaser作为执行线程与反思、求助等调用流程的同步协调应当需要额外维护Phaser全局字段获取到反思结果或者用户反馈后 // 若使用Phaser作为执行线程与反思、求助等调用流程的同步协调应当需要额外维护Phaser全局字段获取到反思结果或者用户反馈后
// 调用对应的phaser注册任务在ActionExecutor中动态添加任务至actionChain,同时启动异步执行 // 调用对应的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) { if (actionKeys.size() - failedCount.get() > 0) {
result.setStatus(RepairerStatus.OK); result.setStatus(RepairerStatus.OK);
} else { } else {
@@ -144,7 +149,7 @@ public class ActionRepairer extends AgentRunningSubModule<RepairerInput, Repaire
@Override @Override
public String modelKey() { public String modelKey() {
return ""; return "action_repairer";
} }
@Override @Override

View File

@@ -1,29 +1,83 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor; 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.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.ActivateModel;
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule; 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.GeneratorInput;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.GeneratorResult; 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 容器内 * 负责依据输入内容生成可执行的动态行动单元,并选择是否持久化至 SandboxRunner 容器内
*/ */
@AgentSubModule @AgentSubModule
public class DynamicActionGenerator extends AgentRunningSubModule<GeneratorInput, GeneratorResult> implements ActivateModel { public class DynamicActionGenerator extends AgentRunningSubModule<GeneratorInput, GeneratorResult>
implements ActivateModel {
@Override @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; 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 @Override
public String modelKey() { public String modelKey() {
return ""; return "dynamic_generator";
} }
@Override @Override
public boolean withBasicPrompt() { public boolean withBasicPrompt() {
return false; 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; 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.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule; 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.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.ExtractorResult;
import work.slhaf.partner.module.modules.action.dispatcher.executor.entity.HistoryAction; 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); log.error("ParamsExtractor解析结果失败返回内容{}", response.getMessage(), e);
result = new ExtractorResult(); result = new ExtractorResult();
result.setOk(false); result.setOk(false);
result.setParams(new String[0]); result.setParams(new HashMap<>());
} }
return result; return result;
} }

View File

@@ -1,9 +1,11 @@
package work.slhaf.partner.module.modules.action.dispatcher.executor.entity; package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import java.util.Map;
import lombok.Data; import lombok.Data;
@Data @Data
public class ExtractorResult { public class ExtractorResult {
private boolean ok; 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; package work.slhaf.partner.module.modules.action.dispatcher.executor.entity;
import java.util.Map;
import lombok.Data; import lombok.Data;
@Data @Data
public class GeneratorInput { public class GeneratorInput {
private String key;
private Map<String, String> params;
private String description;
private Map<String, String> paramsDescription;
} }