mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-04 03:27:54 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
# CyberStrikeAI 图编排使用说明
|
||||
|
||||
[English](workflow-graph_en.md)
|
||||
|
||||
本文档说明 **图编排(Graph Orchestration)** 的完整使用方式:如何在画布上搭建流程、配置各类型节点、在节点之间传递数据,以及如何将流程绑定到角色并自动运行。
|
||||
|
||||
---
|
||||
|
||||
## 一、在哪里使用图编排
|
||||
|
||||
1. 登录 CyberStrikeAI Web 端
|
||||
2. 左侧导航进入 **图编排**
|
||||
3. 在左侧列表选择已有流程,或新建流程
|
||||
4. 在中央画布拖拽、连线、配置节点
|
||||
5. 填写流程 **ID**、**名称**、**描述** 后点击 **保存**
|
||||
|
||||
保存后的流程可在 **角色管理** 中绑定到某个角色。绑定后,用户与该角色对话时会按流程图自动执行(`workflow_policy: auto`)。
|
||||
|
||||
---
|
||||
|
||||
## 二、画布基本操作
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 添加节点 | 点击画布上方节点类型按钮(开始、工具、Agent、条件、审批、输出、结束) |
|
||||
| 连线 | 点击 **连线**,依次点击源节点和目标节点;再次点击 **连线** 退出连线模式 |
|
||||
| 选中元素 | 单击节点或连线,右侧显示 **节点属性** |
|
||||
| 删除选中 | 点击 **删除选中** 删除当前节点或连线 |
|
||||
| 自动布局 | 点击 **自动布局** 整理节点位置 |
|
||||
| 删除流程 | 点击 **删除** 删除整个流程定义 |
|
||||
|
||||
**建议:** 每个流程至少包含 **1 个开始节点** 和 **1 个输出节点**;开始节点不应有入边,输出节点不应有出边。
|
||||
|
||||
---
|
||||
|
||||
## 三、执行模型(先理解再配置)
|
||||
|
||||
图编排按 **有向图** 执行,引擎从 **开始** 节点出发,沿连线依次运行下游节点。
|
||||
|
||||
每次运行会维护一份内部状态,模板变量 `{{...}}` 从这里取值:
|
||||
|
||||
| 内部状态 | 模板前缀 | 含义 |
|
||||
|----------|----------|------|
|
||||
| `inputs` | `{{inputs.xxx}}` | 流程启动时的输入(用户消息、会话 ID 等) |
|
||||
| `lastOutput` | `{{previous.xxx}}` | **上一个刚执行完** 的节点的输出 |
|
||||
| `outputs` | `{{outputs.xxx}}` | 全局 **命名变量池**(由节点的「输出变量名」写入) |
|
||||
| `nodeOutputs` | `{{节点ID.xxx}}` | 指定节点 ID 的完整输出对象 |
|
||||
|
||||
### 3.1 `previous` 是什么?
|
||||
|
||||
`{{previous.output}}` 表示 **紧邻的上一个执行节点** 的 `output` 字段。
|
||||
|
||||
- 每执行完一个节点,引擎都会更新 `lastOutput`
|
||||
- **不是**「画布上画线的上游」,而是 **实际执行顺序上的上一步**
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
开始 → Agent A → Agent B
|
||||
```
|
||||
|
||||
Agent B 的 `{{previous.output}}` = Agent A 的输出。
|
||||
|
||||
但若中间有条件节点:
|
||||
|
||||
```text
|
||||
开始 → Agent A → 条件 → Agent B
|
||||
```
|
||||
|
||||
Agent B 的 `{{previous.output}}` = **条件节点** 的输出(`true` / `false`),**不是** Agent A 的结果。
|
||||
|
||||
### 3.2 `outputs` 是什么?
|
||||
|
||||
`outputs` 是引擎在运行过程中维护的 **命名变量注册表**。
|
||||
|
||||
当 Agent、工具、输出 等节点配置了 **输出变量名**(字段 `output_key`)后,节点执行成功会把结果写入:
|
||||
|
||||
```text
|
||||
outputs["你填的变量名"] = 节点输出内容
|
||||
```
|
||||
|
||||
之后 **任意下游节点** 都可以通过 `{{outputs.变量名}}` 引用,不要求两个节点直接相连。
|
||||
|
||||
示例:
|
||||
|
||||
- Agent A 的 **输出变量名** 填 `agent_result1`
|
||||
- Agent B 的 **输入来源** 填 `{{outputs.agent_result1}}`
|
||||
|
||||
即使 A 和 B 之间隔着条件节点,B 仍能拿到 A 的输出。
|
||||
|
||||
### 3.3 什么时候用 `previous`,什么时候用 `outputs`?
|
||||
|
||||
| 场景 | 推荐写法 |
|
||||
|------|----------|
|
||||
| 两个节点 **直连**,只取上一步结果 | `{{previous.output}}` |
|
||||
| 中间有其他节点(条件、工具、审批等) | `{{outputs.变量名}}` |
|
||||
| 需要引用 **更早** 的某个节点结果 | `{{outputs.变量名}}` 或 `{{节点ID.output}}` |
|
||||
| 条件判断要基于某 Agent 的输出 | `{{outputs.变量名}} != ""` |
|
||||
| 读取用户最初输入 | `{{inputs.message}}` |
|
||||
|
||||
**记忆口诀:**
|
||||
|
||||
- `previous` = 上一步(链式、紧邻)
|
||||
- `outputs` = 按名字取(跨节点、可回溯)
|
||||
|
||||
---
|
||||
|
||||
## 四、模板语法
|
||||
|
||||
### 4.1 基本格式
|
||||
|
||||
```text
|
||||
{{变量路径}}
|
||||
```
|
||||
|
||||
支持字母、数字、下划线、点、连字符,例如:
|
||||
|
||||
```text
|
||||
{{previous.output}}
|
||||
{{outputs.agent_result1}}
|
||||
{{inputs.message}}
|
||||
{{inputs.conversationId}}
|
||||
{{previous.matched}}
|
||||
{{node-abc123.output}}
|
||||
```
|
||||
|
||||
### 4.2 可用路径一览
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `{{inputs.message}}` | 用户消息(开始节点输入) |
|
||||
| `{{inputs.conversationId}}` | 会话 ID |
|
||||
| `{{inputs.projectId}}` | 项目 ID |
|
||||
| `{{previous.output}}` | 上一节点主输出 |
|
||||
| `{{previous.matched}}` | 上一条件节点的匹配结果(`true` / `false`) |
|
||||
| `{{outputs.变量名}}` | 某节点注册过的命名输出 |
|
||||
| `{{节点ID.output}}` | 指定节点 ID 的 `output` 字段 |
|
||||
|
||||
### 4.3 条件表达式
|
||||
|
||||
条件节点和连线条件支持简单比较:
|
||||
|
||||
```text
|
||||
{{outputs.agent_result1}} != ""
|
||||
{{previous.output}} == "ok"
|
||||
{{outputs.count}} == "100"
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 使用 `==` 或 `!=` 做字符串比较(两侧会自动去掉首尾空格和引号)
|
||||
- 无比较符时,非空且不为 `false` / `0` / `null` 视为真
|
||||
|
||||
---
|
||||
|
||||
## 五、节点类型与配置
|
||||
|
||||
### 5.1 开始(start)
|
||||
|
||||
流程入口,将用户输入注入 `inputs`。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 输入变量 | 逗号分隔的输入键名 | `message, conversationId, projectId` |
|
||||
|
||||
开始节点输出包含:`output`、`message`、`conversationId`、`projectId`。
|
||||
|
||||
### 5.2 Agent(agent)
|
||||
|
||||
调用大模型 Agent 处理任务,支持多种运行模式。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| Agent 模式 | `eino_single` / `deep` / `plan_execute` / `supervisor` | `eino_single` |
|
||||
| 输入来源 | 上游数据的模板表达式 | `{{previous.output}}` |
|
||||
| 节点指令 | 本节点要完成的任务描述 | 空 |
|
||||
| 输出变量名 | 写入 `outputs` 的键名 | `agent_result` |
|
||||
|
||||
**消息拼装规则:**
|
||||
|
||||
- 仅填 **节点指令**:直接把指令发给 Agent
|
||||
- 仅填 **输入来源**:生成「请基于上游节点输出继续处理:…」
|
||||
- 两者都填:合并为「上游输入 + 节点指令」
|
||||
|
||||
Agent 节点执行后:
|
||||
|
||||
- `previous.output` 更新为本节点响应文本
|
||||
- 若配置了 **输出变量名**,同时写入 `outputs[输出变量名]`
|
||||
|
||||
### 5.3 工具(tool)
|
||||
|
||||
调用已启用的 MCP 工具。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| MCP 工具 | 工具名称(必填) | — |
|
||||
| 参数模板 | JSON,支持 `{{...}}` 模板 | `{}` |
|
||||
| 超时秒数 | 可选 | 空 |
|
||||
|
||||
示例参数模板:
|
||||
|
||||
```json
|
||||
{"target": "{{inputs.message}}", "port": "443"}
|
||||
```
|
||||
|
||||
若配置了 **输出变量名**,工具返回结果会写入 `outputs`。
|
||||
|
||||
### 5.4 条件(condition)
|
||||
|
||||
根据表达式计算分支,输出 `matched`(`true` / `false`)。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 条件表达式 | 支持 `{{...}}` 与 `==` / `!=` | `{{previous.output}} != ""` |
|
||||
|
||||
**分支规则:**
|
||||
|
||||
- 从条件节点连出的 **第一条线** 默认为 **「是」** 分支(`matched == true`)
|
||||
- **第二条线** 默认为 **「否」** 分支(`matched == false`)
|
||||
- 连线标签可写 `是` / `否`(或 `yes` / `no`、`true` / `false`)辅助识别
|
||||
- 第三条及以后的出边需在 **连线条件** 中自定义表达式
|
||||
|
||||
连线条件示例(选中连线后在右侧配置):
|
||||
|
||||
```text
|
||||
{{previous.matched}} == "true"
|
||||
{{previous.matched}} == "false"
|
||||
```
|
||||
|
||||
### 5.5 审批(hitl)
|
||||
|
||||
人工确认检查点(当前为记录模式,自动标记 `approved: true` 并继续)。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 审批提示 | 支持模板 | `请审批该步骤是否继续执行` |
|
||||
| 审批方 | `human` / `audit_agent` | `human` |
|
||||
|
||||
### 5.6 输出(output)
|
||||
|
||||
将流程最终结果写入 `outputs`,供结束摘要和对话展示使用。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 输出变量名 | 必填,最终结果的键名 | `result` |
|
||||
| 变量来源 | 模板表达式,决定写入的值 | `{{previous.output}}` |
|
||||
|
||||
**注意:** 输出节点是流程的「出口」,不应再有出边。
|
||||
|
||||
### 5.7 结束(end)
|
||||
|
||||
可选节点,用于生成结束摘要模板(角色绑定流程中较少单独使用)。
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 结束摘要模板 | 支持 `{{outputs.xxx}}` | `{{outputs.result}}` |
|
||||
|
||||
---
|
||||
|
||||
## 六、连线配置
|
||||
|
||||
选中 **连线** 后,右侧可配置 **连线条件**。
|
||||
|
||||
| 场景 | 示例 |
|
||||
|------|------|
|
||||
| 普通节点后的过滤 | `{{previous.output}} == "ok"` |
|
||||
| 条件节点「是」分支 | `{{previous.matched}} == "true"` |
|
||||
| 条件节点「否」分支 | `{{previous.matched}} == "false"` |
|
||||
|
||||
若不填连线条件:
|
||||
|
||||
- 非条件节点:连线始终放行
|
||||
- 条件节点:按出边顺序自动分配是/否分支
|
||||
|
||||
---
|
||||
|
||||
## 七、完整示例:跨条件节点传递 Agent 输出
|
||||
|
||||
### 7.1 流程结构
|
||||
|
||||
```text
|
||||
开始 → Agent(生成初始值)→ 条件 → Agent(加工)→ 输出
|
||||
↘ 否 → 输出
|
||||
```
|
||||
|
||||
### 7.2 节点配置
|
||||
|
||||
**Agent 1(第一个 Agent)**
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 节点指令 | 只输出 `123333333` |
|
||||
| 输出变量名 | `agent_result1` |
|
||||
|
||||
**条件**
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 条件表达式 | `{{outputs.agent_result1}} != ""` |
|
||||
|
||||
**Agent 2(第二个 Agent)**
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 输入来源 | `{{outputs.agent_result1}}` |
|
||||
| 节点指令 | 在输入基础上加 100,然后输出 |
|
||||
| 输出变量名 | `agent_result` |
|
||||
|
||||
**输出**
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 输出变量名 | `result` |
|
||||
| 变量来源 | `{{outputs.agent_result}}` |
|
||||
|
||||
### 7.3 常见错误
|
||||
|
||||
| 错误配置 | 原因 |
|
||||
|----------|------|
|
||||
| Agent 2 输入来源写 `{{previous.output}}` | `previous` 指向条件节点,得到的是 `true`/`false`,不是 Agent 1 的文本 |
|
||||
| 未给 Agent 1 填输出变量名 | `outputs.agent_result1` 不存在,下游取到空值 |
|
||||
| 条件表达式写 `{{previous.output}}` | 判断的是开始节点或上一节点的输出,而非 Agent 1 的命名变量 |
|
||||
|
||||
---
|
||||
|
||||
## 八、绑定角色并运行
|
||||
|
||||
### 8.1 在角色管理中绑定
|
||||
|
||||
1. 进入 **角色管理**,编辑或新建角色
|
||||
2. 选择 **工作流 / 图编排** 绑定的流程 ID
|
||||
3. 策略设为 `auto`(默认:有 `workflow_id` 时自动执行)
|
||||
4. 保存角色
|
||||
|
||||
也可在角色 YAML 中直接配置:
|
||||
|
||||
```yaml
|
||||
name: 工作流测试
|
||||
workflow_id: "1233"
|
||||
workflow_version: latest
|
||||
workflow_policy: auto
|
||||
```
|
||||
|
||||
### 8.2 运行效果
|
||||
|
||||
用户选择该角色并发送消息后:
|
||||
|
||||
1. 引擎加载对应 `graph_json` 并按图执行
|
||||
2. 对话页可看到 `workflow_start`、`workflow_node_start`、Agent 推理等进度事件
|
||||
3. 流程结束后返回摘要,列出 `outputs` 中所有命名输出
|
||||
|
||||
若未配置输出节点或条件未命中,`outputs` 可能为空,摘要会提示检查输出节点与分支。
|
||||
|
||||
---
|
||||
|
||||
## 九、保存前校验规则
|
||||
|
||||
保存时系统会自动检查:
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 必须有开始节点 | 至少 1 个 `start` |
|
||||
| 必须有输出节点 | 至少 1 个 `output`,且填写输出变量名 |
|
||||
| 连线合法 | 源/目标节点存在,不能自环 |
|
||||
| 开始节点无入边 | 开始节点不能被指向 |
|
||||
| 输出节点无出边 | 输出节点后不应再连线 |
|
||||
| 工具节点 | 必须选择 MCP 工具 |
|
||||
| 条件节点 | 必须填写表达式;建议 1~2 条出边(是/否) |
|
||||
|
||||
---
|
||||
|
||||
## 十、排错指南
|
||||
|
||||
| 现象 | 可能原因 | 处理建议 |
|
||||
|------|----------|----------|
|
||||
| 下游拿到空值 | 上游未配置输出变量名 | 给上游 Agent/工具填 **输出变量名**,下游用 `{{outputs.xxx}}` |
|
||||
| 下游拿到 `true`/`false` | 误用 `{{previous.output}}`,上一步是条件节点 | 改用 `{{outputs.xxx}}` |
|
||||
| 条件总走「否」 | 表达式与真实输出格式不一致 | 检查 Agent 输出是否带引号、换行;用 `!= ""` 先验证 |
|
||||
| 流程无最终输出 | 未命中输出节点所在分支 | 检查条件分支连线;确保至少一条路径到达 **输出** 节点 |
|
||||
| 角色对话未跑流程 | 角色未绑定或未启用 | 确认 `workflow_id`、`workflow_policy: auto`、流程 `enabled: true` |
|
||||
| 工具节点失败 | 参数 JSON 不合法或工具未启用 | 检查参数模板;在 MCP 中启用对应工具 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、最佳实践
|
||||
|
||||
1. **命名规范**:为每个需要被引用的节点设置有意义的输出变量名,如 `scan_result`、`parsed_targets`,避免都叫 `agent_result`。
|
||||
2. **跨节点传参优先用 `outputs`**:只要中间可能插入条件、工具、审批节点,就应用命名变量。
|
||||
3. **`previous` 仅用于直连**:A → B 且无中间节点时,`{{previous.output}}` 最简洁。
|
||||
4. **条件判断引用源数据**:判断 Agent 输出时用 `{{outputs.xxx}}`,不要用 `{{previous.output}}`(除非条件紧跟在目标 Agent 之后)。
|
||||
5. **每条路径都要有出口**:确保「是」「否」分支最终都能到达 **输出** 节点(或你期望的终点)。
|
||||
6. **保存前跑一遍**:用简单指令(如固定字符串输出)验证数据传递,再替换为真实业务逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 十二、相关代码位置(开发者参考)
|
||||
|
||||
| 模块 | 路径 |
|
||||
|------|------|
|
||||
| 执行引擎 | `internal/workflow/runner.go` |
|
||||
| 画布前端 | `web/static/js/workflows.js` |
|
||||
| 流程 API | `internal/handler/workflow.go` |
|
||||
| 角色绑定 | `internal/config/config.go`(`workflow_id` 字段) |
|
||||
@@ -0,0 +1,403 @@
|
||||
# CyberStrikeAI Graph Orchestration Guide
|
||||
|
||||
[中文](workflow-graph.md)
|
||||
|
||||
This document explains how to use **Graph Orchestration**: building workflows on the canvas, configuring node types, passing data between nodes, and binding a graph to a role for automatic execution.
|
||||
|
||||
---
|
||||
|
||||
## 1. Where to find Graph Orchestration
|
||||
|
||||
1. Log in to the CyberStrikeAI web UI.
|
||||
2. Open **Graph Orchestration** in the left sidebar.
|
||||
3. Select an existing workflow from the list, or create a new one.
|
||||
4. Drag nodes, draw edges, and configure properties on the canvas.
|
||||
5. Fill in **ID**, **Name**, and **Description**, then click **Save**.
|
||||
|
||||
Saved workflows can be bound to a role under **Role Management**. When `workflow_policy` is `auto`, chatting with that role runs the bound graph automatically.
|
||||
|
||||
---
|
||||
|
||||
## 2. Canvas basics
|
||||
|
||||
| Action | Description |
|
||||
|--------|-------------|
|
||||
| Add node | Click a node type button above the canvas (Start, Tool, Agent, Condition, HITL, Output, End) |
|
||||
| Connect | Click **Connect**, then click source and target nodes; click **Connect** again to exit connect mode |
|
||||
| Select | Click a node or edge; properties appear in the right panel |
|
||||
| Delete selected | Remove the current node or edge |
|
||||
| Auto layout | Rearrange node positions |
|
||||
| Delete workflow | Remove the entire workflow definition |
|
||||
|
||||
**Requirements:** Every workflow needs at least **one Start node** and **one Output node**. Start nodes must not have incoming edges; Output nodes must not have outgoing edges.
|
||||
|
||||
---
|
||||
|
||||
## 3. Execution model (read this before configuring)
|
||||
|
||||
The engine executes the workflow as a **directed graph**, starting from the **Start** node and following edges to downstream nodes.
|
||||
|
||||
During a run, the engine keeps internal state. Template expressions `{{...}}` read from that state:
|
||||
|
||||
| Internal state | Template prefix | Meaning |
|
||||
|----------------|-----------------|---------|
|
||||
| `inputs` | `{{inputs.xxx}}` | Workflow inputs at start (user message, conversation ID, etc.) |
|
||||
| `lastOutput` | `{{previous.xxx}}` | Output of the **most recently executed** node |
|
||||
| `outputs` | `{{outputs.xxx}}` | Global **named variable pool** (written by nodes with an output key) |
|
||||
| `nodeOutputs` | `{{nodeId.xxx}}` | Full output object of a specific node ID |
|
||||
|
||||
### 3.1 What is `previous`?
|
||||
|
||||
`{{previous.output}}` is the `output` field of the **immediately preceding executed node**.
|
||||
|
||||
- After every node finishes, the engine updates `lastOutput`.
|
||||
- It is **not** “the node drawn upstream on the canvas”; it is **the previous step in actual execution order**.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
Start → Agent A → Agent B
|
||||
```
|
||||
|
||||
For Agent B, `{{previous.output}}` = Agent A’s output.
|
||||
|
||||
With a condition in between:
|
||||
|
||||
```text
|
||||
Start → Agent A → Condition → Agent B
|
||||
```
|
||||
|
||||
For Agent B, `{{previous.output}}` = the **condition node** output (`true` / `false`), **not** Agent A’s result.
|
||||
|
||||
### 3.2 What is `outputs`?
|
||||
|
||||
`outputs` is a **named variable registry** maintained by the engine during execution.
|
||||
|
||||
When an Agent, Tool, or Output node sets an **Output variable name** (`output_key`), the result is stored as:
|
||||
|
||||
```text
|
||||
outputs["your_variable_name"] = node_output
|
||||
```
|
||||
|
||||
Any downstream node can then reference it via `{{outputs.variable_name}}`, even if other nodes sit in between.
|
||||
|
||||
Example:
|
||||
|
||||
- Agent A **Output variable name**: `agent_result1`
|
||||
- Agent B **Input source**: `{{outputs.agent_result1}}`
|
||||
|
||||
Agent B still receives Agent A’s output even when a condition node lies between them.
|
||||
|
||||
### 3.3 When to use `previous` vs `outputs`
|
||||
|
||||
| Scenario | Recommended |
|
||||
|----------|-------------|
|
||||
| Two nodes are **directly connected**; you only need the last step | `{{previous.output}}` |
|
||||
| Other nodes sit in between (condition, tool, HITL, etc.) | `{{outputs.variable_name}}` |
|
||||
| Reference output from an **earlier** node | `{{outputs.variable_name}}` or `{{nodeId.output}}` |
|
||||
| Condition should test an Agent’s output | `{{outputs.variable_name}} != ""` |
|
||||
| Read the original user input | `{{inputs.message}}` |
|
||||
|
||||
**Rule of thumb:**
|
||||
|
||||
- `previous` = last step (chained, adjacent)
|
||||
- `outputs` = by name (cross-node, look back)
|
||||
|
||||
---
|
||||
|
||||
## 4. Template syntax
|
||||
|
||||
### 4.1 Basic format
|
||||
|
||||
```text
|
||||
{{path.to.value}}
|
||||
```
|
||||
|
||||
Allowed characters in paths: letters, digits, underscore, dot, hyphen. Examples:
|
||||
|
||||
```text
|
||||
{{previous.output}}
|
||||
{{outputs.agent_result1}}
|
||||
{{inputs.message}}
|
||||
{{inputs.conversationId}}
|
||||
{{previous.matched}}
|
||||
{{node-abc123.output}}
|
||||
```
|
||||
|
||||
### 4.2 Available paths
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `{{inputs.message}}` | User message (Start node input) |
|
||||
| `{{inputs.conversationId}}` | Conversation ID |
|
||||
| `{{inputs.projectId}}` | Project ID |
|
||||
| `{{previous.output}}` | Primary output of the previous node |
|
||||
| `{{previous.matched}}` | Match result of the previous condition node (`true` / `false`) |
|
||||
| `{{outputs.variable_name}}` | Named output registered by a node |
|
||||
| `{{nodeId.output}}` | `output` field of the node with that ID |
|
||||
|
||||
### 4.3 Condition expressions
|
||||
|
||||
Condition nodes and edge conditions support simple comparisons:
|
||||
|
||||
```text
|
||||
{{outputs.agent_result1}} != ""
|
||||
{{previous.output}} == "ok"
|
||||
{{outputs.count}} == "100"
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Use `==` or `!=` for string comparison (leading/trailing spaces and quotes are trimmed)
|
||||
- Without a comparator, non-empty values that are not `false`, `0`, or `null` are treated as true
|
||||
|
||||
---
|
||||
|
||||
## 5. Node types and configuration
|
||||
|
||||
### 5.1 Start
|
||||
|
||||
Workflow entry point; injects user input into `inputs`.
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Input keys | Comma-separated input key names | `message, conversationId, projectId` |
|
||||
|
||||
Start node output includes: `output`, `message`, `conversationId`, `projectId`.
|
||||
|
||||
### 5.2 Agent
|
||||
|
||||
Runs an LLM Agent task. Supports multiple modes.
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Agent mode | `eino_single` / `deep` / `plan_execute` / `supervisor` | `eino_single` |
|
||||
| Input source | Template for upstream data | `{{previous.output}}` |
|
||||
| Node instruction | Task description for this node | empty |
|
||||
| Output variable name | Key written into `outputs` | `agent_result` |
|
||||
|
||||
**Message assembly:**
|
||||
|
||||
- Instruction only → send instruction to the Agent
|
||||
- Input source only → “Continue based on upstream output: …”
|
||||
- Both → combined “upstream input + node instruction”
|
||||
|
||||
After execution:
|
||||
|
||||
- `previous.output` becomes this node’s response text
|
||||
- If **Output variable name** is set, the value is also stored in `outputs[variable_name]`
|
||||
|
||||
### 5.3 Tool
|
||||
|
||||
Calls an enabled MCP tool.
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| MCP tool | Tool name (required) | — |
|
||||
| Argument template | JSON with `{{...}}` templates | `{}` |
|
||||
| Timeout (seconds) | Optional | empty |
|
||||
|
||||
Example argument template:
|
||||
|
||||
```json
|
||||
{"target": "{{inputs.message}}", "port": "443"}
|
||||
```
|
||||
|
||||
If an output variable name is configured, the tool result is written to `outputs`.
|
||||
|
||||
### 5.4 Condition
|
||||
|
||||
Evaluates an expression and outputs `matched` (`true` / `false`).
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Expression | Supports `{{...}}` and `==` / `!=` | `{{previous.output}} != ""` |
|
||||
|
||||
**Branching rules:**
|
||||
|
||||
- The **first outgoing edge** defaults to the **“yes”** branch (`matched == true`)
|
||||
- The **second outgoing edge** defaults to the **“no”** branch (`matched == false`)
|
||||
- Edge labels such as `是` / `否` (or `yes` / `no`, `true` / `false`) help identify branches
|
||||
- A third or later edge needs a custom **edge condition**
|
||||
|
||||
Edge condition examples (select an edge, configure in the right panel):
|
||||
|
||||
```text
|
||||
{{previous.matched}} == "true"
|
||||
{{previous.matched}} == "false"
|
||||
```
|
||||
|
||||
### 5.5 HITL (human-in-the-loop)
|
||||
|
||||
Human approval checkpoint (currently record-only; marks `approved: true` and continues).
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Prompt | Supports templates | `Please approve before continuing` |
|
||||
| Reviewer | `human` / `audit_agent` | `human` |
|
||||
|
||||
### 5.6 Output
|
||||
|
||||
Writes the final workflow result into `outputs` for summary and chat display.
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Output variable name | Required key for the final result | `result` |
|
||||
| Variable source | Template deciding what to write | `{{previous.output}}` |
|
||||
|
||||
**Note:** Output nodes are workflow exits and must not have outgoing edges.
|
||||
|
||||
### 5.7 End
|
||||
|
||||
Optional node for an end summary template (less common in role-bound flows).
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Result template | Supports `{{outputs.xxx}}` | `{{outputs.result}}` |
|
||||
|
||||
---
|
||||
|
||||
## 6. Edge configuration
|
||||
|
||||
Select an **edge** to configure its **condition** in the right panel.
|
||||
|
||||
| Scenario | Example |
|
||||
|----------|---------|
|
||||
| Filter after a normal node | `{{previous.output}} == "ok"` |
|
||||
| “Yes” branch from a condition | `{{previous.matched}} == "true"` |
|
||||
| “No” branch from a condition | `{{previous.matched}} == "false"` |
|
||||
|
||||
If no edge condition is set:
|
||||
|
||||
- Non-condition nodes: edge is always allowed
|
||||
- Condition nodes: yes/no branches are assigned by edge order automatically
|
||||
|
||||
---
|
||||
|
||||
## 7. Full example: passing Agent output across a condition
|
||||
|
||||
### 7.1 Graph structure
|
||||
|
||||
```text
|
||||
Start → Agent (initial value) → Condition → Agent (transform) → Output
|
||||
↘ no → Output
|
||||
```
|
||||
|
||||
### 7.2 Node configuration
|
||||
|
||||
**Agent 1**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Node instruction | Output only `123333333` |
|
||||
| Output variable name | `agent_result1` |
|
||||
|
||||
**Condition**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Expression | `{{outputs.agent_result1}} != ""` |
|
||||
|
||||
**Agent 2**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Input source | `{{outputs.agent_result1}}` |
|
||||
| Node instruction | Add 100 to the input, then output |
|
||||
| Output variable name | `agent_result` |
|
||||
|
||||
**Output**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Output variable name | `result` |
|
||||
| Variable source | `{{outputs.agent_result}}` |
|
||||
|
||||
### 7.3 Common mistakes
|
||||
|
||||
| Wrong config | Why it fails |
|
||||
|--------------|--------------|
|
||||
| Agent 2 input source = `{{previous.output}}` | `previous` points to the condition node → `true`/`false`, not Agent 1’s text |
|
||||
| Agent 1 has no output variable name | `outputs.agent_result1` does not exist → empty downstream |
|
||||
| Condition uses `{{previous.output}}` | Tests the wrong upstream value instead of Agent 1’s named output |
|
||||
|
||||
---
|
||||
|
||||
## 8. Bind to a role and run
|
||||
|
||||
### 8.1 Bind in Role Management
|
||||
|
||||
1. Open **Role Management**, edit or create a role.
|
||||
2. Select the workflow / graph ID to bind.
|
||||
3. Set policy to `auto` (default when `workflow_id` is set).
|
||||
4. Save the role.
|
||||
|
||||
You can also configure this in role YAML:
|
||||
|
||||
```yaml
|
||||
name: workflow-test
|
||||
workflow_id: "1233"
|
||||
workflow_version: latest
|
||||
workflow_policy: auto
|
||||
```
|
||||
|
||||
### 8.2 Runtime behavior
|
||||
|
||||
When a user chats with that role:
|
||||
|
||||
1. The engine loads `graph_json` and executes the graph.
|
||||
2. The chat UI shows progress events (`workflow_start`, `workflow_node_start`, Agent reasoning, etc.).
|
||||
3. When finished, a summary lists all named entries in `outputs`.
|
||||
|
||||
If no Output node is reached or no branch matches, `outputs` may be empty and the summary will suggest checking the Output node and branches.
|
||||
|
||||
---
|
||||
|
||||
## 9. Validation before save
|
||||
|
||||
On save, the system checks:
|
||||
|
||||
| Rule | Description |
|
||||
|------|-------------|
|
||||
| Start node required | At least one `start` node |
|
||||
| Output node required | At least one `output` node with an output variable name |
|
||||
| Valid edges | Source and target exist; no self-loops |
|
||||
| Start has no incoming edges | Start must not be targeted |
|
||||
| Output has no outgoing edges | Nothing after Output |
|
||||
| Tool nodes | MCP tool must be selected |
|
||||
| Condition nodes | Expression required; ideally 1–2 outgoing edges (yes/no) |
|
||||
|
||||
---
|
||||
|
||||
## 10. Troubleshooting
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| Downstream gets empty value | Upstream has no output variable name | Set **Output variable name** on upstream; use `{{outputs.xxx}}` downstream |
|
||||
| Downstream gets `true`/`false` | Used `{{previous.output}}` while previous node is a condition | Use `{{outputs.xxx}}` instead |
|
||||
| Condition always takes “no” | Expression does not match actual output format | Check Agent output for quotes/newlines; try `!= ""` first |
|
||||
| No final output | Output node branch not reached | Verify condition wiring; ensure every path reaches an **Output** node |
|
||||
| Role chat does not run workflow | Role not bound or disabled | Check `workflow_id`, `workflow_policy: auto`, workflow `enabled: true` |
|
||||
| Tool node fails | Invalid JSON in arguments or tool disabled | Fix argument template; enable the tool in MCP settings |
|
||||
|
||||
---
|
||||
|
||||
## 11. Best practices
|
||||
|
||||
1. **Meaningful names**: Use descriptive output variable names (`scan_result`, `parsed_targets`) instead of reusing `agent_result` everywhere.
|
||||
2. **Prefer `outputs` for cross-node data**: If a condition, tool, or HITL node might sit in between, use named variables.
|
||||
3. **Use `previous` only for direct links**: `A → B` with nothing in between is the ideal case for `{{previous.output}}`.
|
||||
4. **Conditions should reference source data**: When testing Agent output, use `{{outputs.xxx}}` unless the condition immediately follows that Agent.
|
||||
5. **Every path needs an exit**: Ensure both yes and no branches eventually reach an **Output** node (or your intended end).
|
||||
6. **Validate with a simple run**: Use fixed-string outputs to verify data flow before swapping in real business logic.
|
||||
|
||||
---
|
||||
|
||||
## 12. Code references (for developers)
|
||||
|
||||
| Module | Path |
|
||||
|--------|------|
|
||||
| Execution engine | `internal/workflow/runner.go` |
|
||||
| Canvas UI | `web/static/js/workflows.js` |
|
||||
| Workflow API | `internal/handler/workflow.go` |
|
||||
| Role binding | `internal/config/config.go` (`workflow_id` field) |
|
||||
Reference in New Issue
Block a user