diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/ExecuteOption.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/ExecuteOption.java new file mode 100644 index 000000000..2deacb8e7 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/ExecuteOption.java @@ -0,0 +1,111 @@ +package com.yomahub.liteflow.core; + +/** + * {@link FlowExecutor} 的执行选项参数对象。 + * + *
用于在不增加方法 overload 数量的前提下,灵活组合各种执行维度——例如同时 + * 指定 {@code requestId}、{@code conversationId} 与自定义上下文。所有字段都是 + * 可选的:未设置即沿用框架默认行为。 + * + *
典型用法: + *
{@code
+ * // 仅指定 conversationId(agent 连续对话场景)
+ * flowExecutor.execute2Resp("chain1", param,
+ * ExecuteOption.of().conversationId("user-1024-task-abc"));
+ *
+ * // 让框架自动生成 conversationId
+ * LiteflowResponse r = flowExecutor.execute2Resp("chain1", param,
+ * ExecuteOption.of().autoConversationId());
+ * String cid = r.getConversationId(); // 取回后续调用可复用
+ *
+ * // 同时指定 rid 和 cid,并附带上下文 Class
+ * flowExecutor.execute2Resp("chain1", param,
+ * ExecuteOption.of()
+ * .requestId(rid)
+ * .conversationId(cid)
+ * .contextClass(MyCtx.class));
+ * }
+ */
+public class ExecuteOption {
+
+ private String requestId;
+ private String conversationId;
+ private boolean autoConversationId;
+ private Class>[] contextBeanClasses;
+ private Object[] contextBeans;
+
+ private ExecuteOption() {}
+
+ /** 创建一个空的执行选项,所有字段均未设置。 */
+ public static ExecuteOption of() {
+ return new ExecuteOption();
+ }
+
+ /**
+ * 指定本次执行的 requestId。{@code null} 或空字符串等价于未设置——
+ * 框架会按既有逻辑自动生成。
+ */
+ public ExecuteOption requestId(String requestId) {
+ this.requestId = requestId;
+ return this;
+ }
+
+ /**
+ * 指定本次执行的 conversationId(业务会话标识)。
+ *
+ * 主要用于 ReAct Agent 连续对话场景:同一段对话中的所有 agent + * 共享 workspace 目录,跨次调用传入相同 conversationId 即可恢复会话。 + * + *
显式调用本方法会取消 {@link #autoConversationId()} 的语义。 + */ + public ExecuteOption conversationId(String conversationId) { + this.conversationId = conversationId; + this.autoConversationId = false; + return this; + } + + /** + * 声明本次执行需要 conversationId 但具体值由框架生成(NanoId 格式)。 + * 生成的值可通过 {@link com.yomahub.liteflow.flow.LiteflowResponse#getConversationId()} + * 取回,调用方据此在下一次调用中传回 {@link #conversationId(String)} 以延续会话。 + * + *
调用本方法会清掉之前 {@link #conversationId(String)} 设置的具体值。 + */ + public ExecuteOption autoConversationId() { + this.autoConversationId = true; + this.conversationId = null; + return this; + } + + /** 指定上下文 bean 的 Class 数组。框架会根据 Class 创建实例。 */ + public ExecuteOption contextClass(Class>... contextBeanClasses) { + this.contextBeanClasses = contextBeanClasses; + return this; + } + + /** 指定上下文 bean 实例数组。 */ + public ExecuteOption contextBean(Object... contextBeans) { + this.contextBeans = contextBeans; + return this; + } + + public String getRequestId() { + return requestId; + } + + public String getConversationId() { + return conversationId; + } + + public boolean isAutoConversationId() { + return autoConversationId; + } + + public Class>[] getContextBeanClasses() { + return contextBeanClasses; + } + + public Object[] getContextBeans() { + return contextBeans; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java index a6e482855..853b7ad77 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java @@ -43,6 +43,7 @@ import com.yomahub.liteflow.slot.Slot; import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder; import com.yomahub.liteflow.spi.holder.PathContentParserHolder; import com.yomahub.liteflow.thread.ExecutorHelper; +import com.yomahub.liteflow.util.ConversationIdGenerator; import com.yomahub.liteflow.util.ElRegexUtil; import java.util.*; @@ -386,6 +387,69 @@ public class FlowExecutor { return this.execute2Resp(chainId, param, requestId, null, contextBeanArray); } + /** + * 使用 {@link ExecuteOption} 执行 chain,自由组合 requestId、conversationId、上下文等执行维度。 + * + *
这是新代码的推荐入口。当需要 conversationId(典型场景:ReAct Agent 连续对话)、 + * 同时传入 requestId 与多上下文时,相比多个 {@code WithXxx} 方法 overload, + * 单一 ExecuteOption 入口能避免命名爆炸: + *
{@code
+ * flowExecutor.execute2Resp("chain1", param,
+ * ExecuteOption.of()
+ * .requestId(rid)
+ * .conversationId(cid)
+ * .contextClass(MyCtx.class));
+ * }
+ *
+ * 已有 {@code execute2RespWithRid(...)} 等方法继续可用,行为不变。
+ *
+ * @param chainId chain id
+ * @param param 入参,将作为 chainReqData 注入 slot
+ * @param option 执行选项;{@code null} 等价于 {@link ExecuteOption#of()}
+ * @return LiteflowResponse
+ */
+ public LiteflowResponse execute2Resp(String chainId, Object param, ExecuteOption option) {
+ ExecuteOption opt = option == null ? ExecuteOption.of() : option;
+ return this.execute2Resp(chainId, param, opt.getRequestId(), resolveConversationId(opt),
+ opt.getContextBeanClasses(), opt.getContextBeans());
+ }
+
+ /**
+ * 异步版本:{@link #execute2Resp(String, Object, ExecuteOption)}。
+ */
+ public Future {@code conversationId} 用于在 chain 执行期间标识一段"业务会话",由
+ * {@link com.yomahub.liteflow.slot.Slot#setConversationId(String)} 写入 slot,
+ * 同 chain 内所有需要会话上下文的组件(典型如 ReAct Agent)共享。
+ *
+ * 不传 conversationId 时由 {@link com.yomahub.liteflow.core.FlowExecutor} 在 slot
+ * 创建后调用本工具生成一次性标识;传入则原样使用,便于跨调用恢复会话。
+ */
+public final class ConversationIdGenerator {
+
+ private static final char[] CODE_ALPHABET =
+ "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
+
+ private ConversationIdGenerator() {}
+
+ public static String generate() {
+ String date = DateUtil.format(new Date(), "yyyyMMdd");
+ String code = NanoId.randomNanoId(null, CODE_ALPHABET, 12);
+ return StrUtil.format("{}_{}", date, code);
+ }
+}
+ *
+ */
+ private String resolveConversationId(ExecuteOption opt) {
+ if (StrUtil.isNotBlank(opt.getConversationId())) {
+ return opt.getConversationId();
+ }
+ if (opt.isAutoConversationId()) {
+ return ConversationIdGenerator.generate();
+ }
+ return null;
+ }
+
public List