Add File
This commit is contained in:
273
docs/zh/core/chain.md
Normal file
273
docs/zh/core/chain.md
Normal file
@@ -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<String, Object> 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<String, Object> execute(Chain chain) {
|
||||
if (StringUtil.noText(code)) {
|
||||
throw new IllegalStateException("Code is null or blank.");
|
||||
}
|
||||
return executeCode(this.code, chain);
|
||||
}
|
||||
|
||||
// 不同语言有不同的实现
|
||||
protected abstract Map<String, Object> 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<String, Object> execute(Chain chain) {
|
||||
System.out.println("a 节点 >>> execute!");
|
||||
return Maps.of();
|
||||
}
|
||||
};
|
||||
a.setId("a");
|
||||
chain.addNode(a);
|
||||
|
||||
ChainNode b = new ChainNode() {
|
||||
@Override
|
||||
protected Map<String, Object> 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<String, Object> 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<String, Object> 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<>());
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user