适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:
crates/cheng-engine/src/executor/workflow_executor/node_execution.rs、crates/cheng-engine/src/executor/context.rs、crates/cheng-nodes/src/nodes/builtin/utils/smart_variable_node.rs、chengflow-ui/src/features/editor/components/VariableSelectorDrawer.tsx
数据在节点之间有三种流动方式:连线(主要机制)、节点自身配置里的 ${...} 替换,以及引擎的环境注入。它们之间有严格的优先级顺序,搞清楚这个顺序,几乎能解释所有「我的节点为什么拿到了那个值」的疑问。
连线
一条连线指明四件事:源节点、源输出端口、目标节点、目标输入端口。
节点 A ──[ text ]────────►[ user_message ] 节点 B
source_output target_input
执行时,引擎遍历某个节点的每条入边,读取源节点在该端口上的输出,并以目标端口名写进目标节点的输入。端口按名称匹配,因此在节点 schema 里改掉一个输出端口名,会让所有引用旧名字的连线失效。
动态数组端口
当不存在同名端口时,形如 prefix_N 的端口会去数组输出里解析。引擎先试 prefix_outputs[N],再试 prefixs[N]:
branch_0 → branch_outputs[0] (先试这个)
→ branches[0] (回退)
正因如此,一个路由节点只需声明一个数组输出,却仍能在画布上暴露带编号的端口。
null 意味着「不走这条路」
若某节点的入边全部产出 null,它会被自动跳过,而不是带着空输入去运行。这正是条件路由的机制:条件节点在选中的分支上发出值、在其余分支上发出 null,未被选中的下游子树便自行跳过。跳过会级联——跳过一个节点可能解锁更下游节点的跳过判定。
输入优先级
节点运行时,输入按以下顺序装配。除非特别注明,后面的步骤不会覆盖前面的。
| # | 来源 | 是否覆盖? |
|---|---|---|
| 1 | 入边 | ——(第一写入者) |
| 2 | 扁平执行输入(仅限无入边的起始节点) | 只补空缺 |
| 3 | 循环虚拟输入(item、index、上一轮值) |
只补空缺 |
| 4 | 节点自身配置(经 ${...} 替换后) |
只补空缺 |
| 5 | 按节点 id 指定的执行期输入 | 覆盖 |
| 6 | AgentContext 注入到 context |
仅当 context 为空 |
| 7 | 环境字段(workspace_id、conversation_id、document_id) |
只补空缺 |
有两个后果最容易绊人:
- 连线永远压过你在属性面板里填的值。 配置是第 4 步,连线是第 1 步。已连接的输入在面板里不可编辑,原因正在于此。
- 按节点的执行期输入压过一切。 这正是单节点测试能工作的原因:测试运行器按节点 id 提供输入,它们会覆盖已连好的图。
第 6 步是一道真实的防错:如果某条连线已经提供了 context——比如 RAG 检索器的结果——引擎不会用智能体上下文把它冲掉。
${...} 替换
在节点配置内部,${name} 会被替换成名为 name 的执行输入:
{ "url": "https://api.example.com/v1/${resource}",
"header": "Bearer ${api_token}" }
规则被刻意收得很窄:
- 只替换字符串,并递归穿过对象与数组。
- 只有简单值会内插——字符串、数字、布尔。对象或数组值会让占位符原样保留。
- 未知名字保留
${name}原样,而不是替换成空串。因此,缺失的值会在你发出的请求里显示为一个可见的占位符,而不是一段悄无声息的空白。
以 __env__ 为前缀的键携带由技能解析出的密钥。它们可用于 ${__env__KEY} 替换,但绝不作为直接的节点输入暴露——因为节点输入会与输出一起被持久化,密钥就会落进执行记录里。
环境字段
workspace_id、conversation_id 和 document_id 由引擎从智能体上下文或执行 metadata 注入。这些输入在画布上是隐藏的,通常留空。
对工具调用而言,这种注入是强制覆盖而非补空:模型填进该字段的东西会被替换掉。模型无法靠编造 workspace_id 来扩大自己的作用域。见知识库与工作区上下文。
值得知道的兼容处理
ai/llm和agent/llm在user_message为空时会回落到raw_input。网关与对话入口把用户文本作为raw_input传入,因此模型节点直接接在对话输入之后就能工作,中间不需要映射节点。- 按节点 id 指定的执行输入若不是对象,则对
io/input_text写入text,对其他节点写入content。
智能变量节点
utils/smart_var 是为模板替换处理不好的场景准备的:你手上有一团 JSON、一条 curl 命令或一段 YAML,而你想把它的字段变成可连线的输入。
把文本粘进 template,节点就会提取变量名并为每个变量生成一个输入端口。连上你关心的端口,节点便输出完成替换后的文本。
提取模式:
| 模式 | 行为 |
|---|---|
auto(默认) |
识别键名——先把文本按 JSON 解析并递归遍历;不是合法 JSON 时回退到正则扫描 |
manual |
只识别 {{var}} 占位符 |
重名会加上索引后缀(token、token_2……),于是两个同名字段变成两个不同端口,而不是在 map 里悄悄互相覆盖。无意义的词会从候选列表中被过滤掉。
输出:
full_text——全部变量替换完成的模板文本;下游节点通常消费的就是它。selected_vars——只包含你在output_vars里勾选的变量,以键值对形式给出。all_variables——找到的全部名字,选择器的选项就来自它。preview_content——供画布显示的格式化预览。statistics——变量的总数/已连接数/取默认值数/缺失数,以及模板与输出的字符数。
statistics 这一块就是调试工具:missing_variables 大于零,说明有端口没连上——你还没看输出文本就知道了。
变量选择器
output_vars 通过一个抽屉编辑,而不是一个文本框:可按名称和描述搜索、逐个勾选,或对当前筛选结果一键全选/全不选。它还显示「已选/总数」,于是面对一个 60 字段的载荷,你一眼就能看出选了多少。
该用哪种机制
| 场景 | 用 |
|---|---|
| 一个节点的输出喂给另一个的输入 | 连线 |
| 固定的配置字符串里需要一个运行时值 | 配置里的 ${...} |
| 一段文本里有大量字段要连 | utils/smart_var |
| 节点需要当前工作区或会话 | 什么都不用做——它是环境注入的 |
下一步
- 节点配置与 Schema 驱动表单——配置值从哪来。
- 子流程与批量子流程——跨嵌套执行的映射。
- 执行模型——输入装配相对于调度发生在什么时候。

暂无评论内容