diff --git a/docs/zh/core/chain.md b/docs/zh/core/chain.md new file mode 100644 index 0000000..5cdce76 --- /dev/null +++ b/docs/zh/core/chain.md @@ -0,0 +1,273 @@ +# 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<>()); +} +```