Files
CyberStrikeAI/docs/workflow-graph.md
T
2026-07-06 11:47:09 +08:00

19 KiB
Raw Blame History

CyberStrikeAI 图编排使用说明

English

本文档说明 图编排(Graph Orchestration 的完整使用方式:如何在画布上搭建流程、配置各类型节点、在节点之间传递数据,以及如何将流程绑定到角色并自动运行。


一、在哪里使用图编排

  1. 登录 CyberStrikeAI Web 端
  2. 左侧导航进入 图编排
  3. 在左侧列表选择已有流程,或新建流程
  4. 在中央画布拖拽、连线、配置节点
  5. 填写流程 ID名称描述 后点击 保存

保存后的流程可在 角色管理 中绑定到某个角色。绑定后,用户与该角色对话时会按流程图自动执行(workflow_policy: auto)。


二、画布基本操作

操作 说明
添加节点 点击画布上方节点类型按钮(开始、工具、Agent、条件、审批、输出、结束)
连线 点击 连线,依次点击源节点和目标节点;再次点击 连线 退出连线模式
选中元素 单击节点或连线,右侧显示 节点属性
删除选中 点击 删除选中 删除当前节点或连线
自动布局 点击 自动布局 整理节点位置
试运行 点击 试运行 使用安全 dry-run 验证数据流;工具、Agent、审批不会真实执行
删除流程 点击 删除 删除整个流程定义

硬性规则: 每个流程至少包含 1 个开始节点1 个输出节点;开始节点不能有入边,输出 / 结束节点不能有出边。保存时前端和后端都会执行严格校验。


三、执行模型(先理解再配置)

图编排按 有向图 执行,引擎从 开始 节点出发,沿连线依次运行下游节点。

每次运行会维护一份内部状态,模板变量 {{...}} 从这里取值:

内部状态 模板前缀 含义
inputs {{inputs.xxx}} 流程启动时的输入(用户消息、会话 ID 等)
lastOutput {{previous.xxx}} 上一个刚执行完 的节点的输出
outputs {{outputs.xxx}} 全局 命名变量池(由节点的「输出变量名」写入)
nodeOutputs {{节点ID.xxx}} 指定节点 ID 的完整输出对象
metrics 运行详情中查看 节点耗时、工具调用数、可收集到的 token / cost 等指标

3.1 previous 是什么?

{{previous.output}} 表示 紧邻的上一个执行节点output 字段。

  • 每执行完一个节点,引擎都会更新 lastOutput
  • 不是「画布上画线的上游」,而是 实际执行顺序上的上一步

示例:

开始 → Agent A → Agent B

Agent B 的 {{previous.output}} = Agent A 的输出。

但若中间有条件节点:

开始 → Agent A → 条件 → Agent B

Agent B 的 {{previous.output}} = 条件节点 的输出(true / false),不是 Agent A 的结果。

如果一个节点有 多个上游节点 同时连入,previous 会先按该节点的 汇聚策略 生成:

汇聚策略 含义 适合场景
all_merge 合并所有上游输出,previous.output 为数组 默认推荐,综合多路结果
last_by_canvas 按画布顺序取最后一个上游输出 明确只采用一路结果
first_non_empty 取第一个非空输出 多路兜底
fail_fast 任一上游失败则中止当前节点 关键链路、审批前置、安全检查

3.2 outputs 是什么?

outputs 是引擎在运行过程中维护的 命名变量注册表

当 Agent、工具、输出 等节点配置了 输出变量名(字段 output_key)后,节点执行成功会把结果写入:

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 基本格式

{{变量路径}}

支持字母、数字、下划线、点、连字符,例如:

{{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 字段
{{previous.kind}} 上一节点输出类型,如 agent / tool / condition
{{previous.status}} 上一节点状态,如 completed / failed / simulated

节点输出会保留兼容字段(如 outputmatched),同时带有结构化字段:

{
  "kind": "agent",
  "node_id": "node-2",
  "node_type": "agent",
  "status": "completed",
  "output": "..."
}

4.3 条件表达式

条件节点和连线条件支持比较、文本匹配、正则、逻辑组合与安全 JSONPath/JQ 路径读取:

{{outputs.agent_result1}} != ""
{{previous.output}} == "ok"
{{outputs.count}} >= 100
{{previous.output}} contains "success"
{{previous.output}} matches "^ok"
{{outputs.risk_score}} >= 8 && {{previous.output}} != ""
jsonpath({{previous.output}}, "$.status") == "ok"
jq({{outputs.scan}}, ".severity") == "high"

规则:

  • 支持 ==!=>>=<<=
  • 支持 contains 子串匹配与 matches 正则匹配
  • 支持简单 && / ||
  • 支持 jsonpath(value, "$.path")jq(value, ".path")安全路径子集,仅做字段读取,不执行任意脚本
  • 比较两侧会自动去掉首尾空格和引号
  • 无比较符时,非空且不为 false / 0 / null 视为真
  • 保存时会静态校验表达式格式、JSONPath/JQ 路径和正则语法

4.4 嵌套字段绑定

节点的字段绑定除 outputmessage 等普通字段外,也支持 JSONPath/JQ 风格路径:

绑定配置 含义
from=previous, field=$.status 从上一节点输出对象读取 status
from=outputs, field=$.scan.severity 从命名输出中读取嵌套字段
from=node-1, field=.output.items[0] 从指定节点输出读取数组元素

五、节点类型与配置

5.1 开始(start

流程入口,将用户输入注入 inputs

字段 说明 默认值
输入变量 逗号分隔的输入键名 message, conversationId, projectId

开始节点输出包含:outputmessageconversationIdprojectId

5.2 Agentagent

调用大模型 Agent 处理任务,支持多种运行模式。

字段 说明 默认值
Agent 模式 eino_single / deep / plan_execute / supervisor eino_single
输入来源 上游数据的模板表达式 {{previous.output}}
节点指令 本节点要完成的任务描述
输出变量名 写入 outputs 的键名 agent_result
汇聚策略 多上游进入本节点时如何生成 previous all_merge

消息拼装规则:

  • 仅填 节点指令:直接把指令发给 Agent
  • 仅填 输入来源:生成「请基于上游节点输出继续处理:…」
  • 两者都填:合并为「上游输入 + 节点指令」

Agent 节点执行后:

  • previous.output 更新为本节点响应文本
  • 若配置了 输出变量名,同时写入 outputs[输出变量名]
  • Agent 子图在 Eino 中拆为 prepare → execute → finalize,便于 trace 与后续局部 checkpoint

5.3 工具(tool

调用已启用的 MCP 工具。

字段 说明 默认值
MCP 工具 工具名称(必填)
参数模板 JSON,支持 {{...}} 模板 {}
超时秒数 可选
汇聚策略 多上游进入本节点时如何生成 previous all_merge

示例参数模板:

{"target": "{{inputs.message}}", "port": "443"}

若配置了 输出变量名,工具返回结果会写入 outputs

5.4 条件(condition

根据表达式计算分支,输出 matchedtrue / false)。

字段 说明 默认值
条件表达式 支持 {{...}}== / != {{previous.output}} != ""
汇聚策略 多上游进入本节点时如何生成 previous all_merge

分支规则:

  • 从条件节点连出的 第一条线 默认为 「是」 分支(matched == true
  • 第二条线 默认为 「否」 分支(matched == false
  • 连线标签可写 / (或 yes / notrue / false)辅助识别
  • 第三条及以后的出边需在 连线条件 中自定义表达式

连线条件示例(选中连线后在右侧配置):

{{previous.matched}} == "true"
{{previous.matched}} == "false"

5.5 审批(hitl

人工确认检查点。流程运行到该节点前会通过 Eino interrupt/checkpoint 暂停,等待 API 或监控面板审批后恢复。

字段 说明 默认值
审批提示 支持模板 请审批该步骤是否继续执行
提示字段绑定 留空审批提示时,从绑定字段读取说明 previous.output
审批方 human / audit_agent human
汇聚策略 多上游进入本节点时如何生成 previous all_merge

HITL 等待信息会记录:

  • checkpointId
  • interrupt beforeNodes
  • resume target / address / path
  • resume payload schemaapprovedcomment

5.6 输出(output

将流程最终结果写入 outputs,供结束摘要和对话展示使用。

字段 说明 默认值
输出变量名 必填,最终结果的键名 result
变量来源 模板表达式,决定写入的值 {{previous.output}}
固定输出值 可选,填写后覆盖变量来源
汇聚策略 多上游进入本节点时如何生成 previous all_merge

注意: 输出节点是流程的「出口」,不应再有出边。

5.7 结束(end

可选节点,用于生成结束摘要模板(角色绑定流程中较少单独使用)。

字段 说明 默认值
结束摘要模板 支持 {{outputs.xxx}} {{outputs.result}}
汇聚策略 多上游进入本节点时如何生成 previous all_merge

六、连线配置

选中 连线 后,右侧可配置 连线条件

场景 示例
普通节点后的过滤 {{previous.output}} == "ok"
条件节点「是」分支 {{previous.matched}} == "true"
条件节点「否」分支 {{previous.matched}} == "false"

若不填连线条件:

  • 非条件节点:连线始终放行
  • 条件节点:按出边顺序自动分配是/否分支

七、完整示例:跨条件节点传递 Agent 输出

7.1 流程结构

开始 → 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 中直接配置:

name: 工作流测试
workflow_id: "1233"
workflow_version: latest
workflow_policy: auto

8.2 运行效果

用户选择该角色并发送消息后:

  1. 引擎加载对应 graph_json 并按图执行
  2. 对话页可看到 workflow_startworkflow_node_start、Agent 推理等进度事件
  3. 流程结束后返回摘要,列出 outputs 中所有命名输出

若未配置输出节点或条件未命中,outputs 可能为空,摘要会提示检查输出节点与分支。


九、调试、试运行与复盘

9.1 安全试运行(dry-run

画布工具栏点击 试运行,输入一条测试消息即可模拟执行流程。

dry-run 的安全边界:

  • start / condition / output / end 会按真实逻辑计算
  • tool 不会真实调用 MCP,只返回 [dry-run] tool call skipped
  • agent 不会真实调用模型,只返回 [dry-run] agent execution skipped
  • hitl 不会暂停,只模拟通过

相关 API

POST /api/workflows/dry-run

请求体:

{
  "graph": { "nodes": [], "edges": [], "config": {} },
  "inputs": { "message": "ping" }
}

响应包含:

  • outputs
  • nodeOutputs
  • trace
  • metrics
  • replayScript

9.2 运行详情与 replay

运行后可查询完整节点执行轨迹:

GET /api/workflows/runs/{runId}

返回 runnodeRuns,每个节点记录包含:

  • input 快照
  • output 快照
  • status / error
  • started_at / finished_at
  • duration_ms

复盘接口:

GET /api/workflows/runs/{runId}/replay

该接口只根据已保存的 nodeRuns 生成步骤,不会重新执行工具或 Agent。

9.3 指标(metrics

工作流会尽量累计:

  • node_count
  • duration_ms
  • tool_call_count
  • Agent progress 中可收集到的 prompt_tokens / completion_tokens / total_tokens / cost

token 与成本是否存在取决于底层模型/Agent 事件是否上报 usage。


十、保存前校验规则

保存时系统会自动检查:

规则 说明
必须有开始节点 至少 1 个 start
必须有输出节点 至少 1 个 output,且填写输出变量名
连线合法 源/目标节点存在,不能自环
开始节点无入边 开始节点不能被指向
输出 / 结束节点无出边 输出 / 结束节点后不应再连线
非开始节点必须有入边 避免孤岛节点
非输出 / 结束节点必须有出边 避免执行到死路
无环路 Workflow 编排必须是 DAG
可达性 所有节点必须能从开始节点到达,并能最终到达 output/end
工具节点 必须选择 MCP 工具;参数 JSON 必须合法;超时必须为正整数
Agent 节点 必须填写节点指令或输入绑定;必须填写输出变量名
条件节点 必须填写表达式;需要 1~2 条出边;分支必须标记是/否且不能重复
连线条件 表达式、正则、JSONPath/JQ 路径必须通过静态校验
汇聚策略 必须是 all_merge / last_by_canvas / first_non_empty / fail_fast

十一、排错指南

现象 可能原因 处理建议
下游拿到空值 上游未配置输出变量名 给上游 Agent/工具填 输出变量名,下游用 {{outputs.xxx}}
下游拿到 true/false 误用 {{previous.output}},上一步是条件节点 改用 {{outputs.xxx}}
条件总走「否」 表达式与真实输出格式不一致 检查 Agent 输出是否带引号、换行;用 != "" 先验证
流程无最终输出 未命中输出节点所在分支 检查条件分支连线;确保至少一条路径到达 输出 节点
角色对话未跑流程 角色未绑定或未启用 确认 workflow_idworkflow_policy: auto、流程 enabled: true
工具节点失败 参数 JSON 不合法或工具未启用 检查参数模板;在 MCP 中启用对应工具
保存失败提示分支非法 条件节点出边未标记是/否或重复 选中连线,设置条件分支为 truefalse
多上游结果不符合预期 汇聚策略不合适 根据场景改为 all_merge / first_non_empty / last_by_canvas / fail_fast
嵌套字段取不到 JSONPath/JQ 路径不符合安全子集 使用 $.a.b[0].a.b[0],不要用通配符/递归/表达式

十二、最佳实践

  1. 命名规范:为每个需要被引用的节点设置有意义的输出变量名,如 scan_resultparsed_targets,避免都叫 agent_result
  2. 跨节点传参优先用 outputs:只要中间可能插入条件、工具、审批节点,就应用命名变量。
  3. previous 仅用于直连A → B 且无中间节点时,{{previous.output}} 最简洁。
  4. 条件判断引用源数据:判断 Agent 输出时用 {{outputs.xxx}},不要用 {{previous.output}}(除非条件紧跟在目标 Agent 之后)。
  5. 每条路径都要有出口:确保「是」「否」分支最终都能到达 输出 节点(或你期望的终点)。
  6. 多上游节点显式选择汇聚策略:综合结果用 all_merge,兜底用 first_non_empty,关键链路用 fail_fast
  7. 嵌套 JSON 用 JSONPath/JQ 安全路径:例如 jsonpath({{previous.output}}, "$.status") == "ok"
  8. 保存前先 dry-run:用简单消息验证数据传递和分支,再绑定角色真实执行。

十三、相关代码位置(开发者参考)

模块 路径
执行引擎 internal/workflow/runner.go
Eino 编译 / checkpoint / HITL internal/workflow/eino_compile.go
图校验 internal/workflow/validation.go
表达式 / JSONPath / 汇聚 internal/workflow/expression.gojsonpath.gojoin.go
dry-run / replay 数据 internal/workflow/dry_run.gointernal/handler/workflow_run.go
画布前端 web/static/js/workflows.js
流程 API internal/handler/workflow.go
角色绑定 internal/config/config.goworkflow_id 字段)