# Chain 执行链 执行链(Chain)是由多个节点(Node)按照一定顺序编排、组合而成的链条,用于实现复杂任务的自动化执行。 在执行链中,通常包含开始节点、中间节点和结束节点。通过合理组织这些节点,可以实现各种复杂的业务逻辑。 在 agents-flex 中,内置了多种节点类型,可以满足不同场景的需求。此外,还可以与 [Tinyflow](https://www.tinyflow.cn/zh/core/node.html) 结合使用,进一步扩展节点功能,获取更多强大的节点类型。关于两者结合的具体案例,可以参考 [AIFlowy](https://aiflowy.tech/zh/product/workflow/what_is_workflow.html) 工作流部分的文档。 ## 简单示例 以下是一个简单的执行链示例,展示了如何创建包含开始节点、中间节点和结束节点的执行链,并设置节点之间的连接关系,最后执行该执行链: ```java public static void main(String[] args) { // 创建执行链 Chain chain = new Chain(); // 创建开始节点 StartNode startNode = new StartNode(); startNode.setId("1"); chain.addNode(startNode); // 创建中间节点(动态代码节点) JsExecNode jsExecNode = new JsExecNode(); jsExecNode.setId("2"); jsExecNode.setCode("console.log('hello world')"); chain.addNode(jsExecNode); // 创建结束节点 EndNode endNode = new EndNode(); endNode.setId("3"); endNode.setMessage("success"); chain.addNode(endNode); // 创建 1-2 的边 ChainEdge edge12 = new ChainEdge(); edge12.setSource("1"); edge12.setTarget("2"); chain.addEdge(edge12); // 创建 2-3 的边 ChainEdge edge23 = new ChainEdge(); edge23.setSource("2"); edge23.setTarget("3"); chain.addEdge(edge23); // 执行执行链 Map result = chain.executeForResult(new HashMap<>()); System.out.println(result); } ``` ## 节点 在 agents-flex 中,所有节点都继承自 `com.agentsflex.core.chain.node.BaseNode` 类。除了公共参数外,每个节点类型还具有各自的特定参数。以下是两种常见节点类型的介绍: ### 大模型节点(LlmNode) 大模型节点用于调用大型语言模型(LLM),实现如文本生成、问答等复杂功能。其主要参数包括: - `llm`:大模型实例。 - `chatOptions`:聊天参数,默认为 `ChatOptions.DEFAULT`。 - `userPrompt`:用户提示词。 - `userPromptTemplate`:用户提示词模板。 - `systemPrompt`:系统提示词。 - `systemPromptTemplate`:系统提示词模板。 - `outType`:输出类型,可选值包括 `text`、`markdown`、`json` 等。 代码如下: ```java public class LlmNode extends BaseNode { protected Llm llm; protected ChatOptions chatOptions = ChatOptions.DEFAULT; protected String userPrompt; protected TextPromptTemplate userPromptTemplate; protected String systemPrompt; protected TextPromptTemplate systemPromptTemplate; protected String outType = "text"; //text markdown json // 其他相关代码... } ``` ### 动态代码节点(CodeNode) 动态代码节点允许用户编写自定义代码逻辑,并在执行链中动态执行,从而增加了执行链的灵活性。 其核心参数为 `code`,表示要执行的代码。代码如下: ```java public abstract class CodeNode extends BaseNode { protected String code; public String getCode() { return code; } public void setCode(String code) { this.code = code; } @Override protected Map execute(Chain chain) { if (StringUtil.noText(code)) { throw new IllegalStateException("Code is null or blank."); } return executeCode(this.code, chain); } // 不同语言有不同的实现 protected abstract Map executeCode(String code, Chain chain); } ``` 目前支持以下几种语言: - JavaScript(`com.agentsflex.chain.node.JsExecNode`) - Groovy(`com.agentsflex.chain.node.GroovyExecNode`) - QLExpress(`com.agentsflex.chain.node.QLExpressExecNode`) ## 边(ChainEdge) 边(`com.agentsflex.core.chain.ChainEdge`)用于连接节点,确保节点按照指定的顺序执行。可以通过调用 `setCondition` 方法为边设置执行条件,只有当满足条件时,才会执行下一个节点。 ## 异步执行 `ChainNode` 中有一个属性 `async`,用于设置节点是否为异步执行,默认值为 `false`。 设置为 `true` 后,节点将运行在单独的线程中。 ## 循环执行 在 agents-flex 中,节点可以设置成循环执行,即重复执行该节点直到满足条件为止。 节点配置属性如下: ```java // 是否启用循环执行 protected boolean loopEnable = false; // 循环间隔时间(毫秒) protected long loopIntervalMs = 1000; // 跳出循环的条件表达式(如:Groovy/SpEL 表达式) protected NodeCondition loopBreakCondition; // 0 表示不限制循环次数 protected int maxLoopCount = 0; ``` 示例代码: ```java public static void main(String[] args) { Chain chain = new Chain(); ChainNode a = new ChainNode() { @Override protected Map execute(Chain chain) { System.out.println("a 节点 >>> execute!"); return Maps.of(); } }; a.setId("a"); chain.addNode(a); ChainNode b = new ChainNode() { @Override protected Map execute(Chain chain) { System.out.println("b 节点 >>> execute!"); // 模拟请求执行结果,并存入内存。 getMemory().put("result", getRes()); return Maps.of(); } }; b.setId("b"); // 节点 b 开启循环执行 b.setLoopEnable(true); // 循环间隔 500ms b.setLoopIntervalMs(500); // 设置循环结束条件 b.setLoopBreakCondition(new NodeCondition() { @Override public boolean check(Chain chain, NodeContext context) { ContextMemory memory = context.getCurrentNode().getMemory(); // 当结果为5时,结束循环。 return (int) memory.get("result") == 5; } }); chain.addNode(b); ChainEdge ab = new ChainEdge(); ab.setSource("a"); ab.setTarget("b"); chain.addEdge(ab); chain.executeForResult(new HashMap<>()); } public static int getRes() { return new Random().nextInt(10); } ``` ## 执行条件 在 agents-flex 中,节点和边都可以设置执行条件。执行条件通过 `com.agentsflex.core.chain.JsCodeCondition` 类实现,这意味着可以直接使用 JavaScript 代码来定义条件逻辑。 示例: ```java public static void main(String[] args) { Chain chain = new Chain(); TestNode a = new TestNode(); a.setId("a"); chain.addNode(a); TestNode b = new TestNode(){ @Override protected Map execute(Chain chain) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("b"); return Maps.of(); } }; b.setId("b"); // 设置 b 节点为异步执行 b.setAsync(true); chain.addNode(b); TestNode c = new TestNode(); c.setId("c"); chain.addNode(c); TestNode d = new TestNode() { @Override protected Map execute(Chain chain) { System.out.println("d: "+ Thread.currentThread().getId()); return Maps.of(); } }; d.setId("d"); // 此处设置 d 节点的条件为上游节点执行完毕后才执行 d.setCondition(new JavascriptStringCondition("_context.isUpstreamFullyExecuted()")); chain.addNode(d); ChainEdge ab = new ChainEdge(); ab.setSource("a"); ab.setTarget("b"); chain.addEdge(ab); ChainEdge ac = new ChainEdge(); ac.setSource("a"); ac.setTarget("c"); chain.addEdge(ac); ChainEdge bd = new ChainEdge(); bd.setSource("b"); bd.setTarget("d"); chain.addEdge(bd); ChainEdge cd = new ChainEdge(); cd.setSource("c"); cd.setTarget("d"); chain.addEdge(cd); /** * 最终执行顺序: * B * ↗ ↘ * A D * ↘ ↗ * C */ chain.executeForResult(new HashMap<>()); } ```