在 Dify 的二次开发或深度集成过程中,我们经常遇到这样一个需求:
场景描述:
我们需要在调用 Workflow 时,将一些业务上下文参数(如 app_id、tenant_id、外部系统的 trace_id 或用户等级)传递给内部的大模型节点或 Agent 节点使用。
遇到的问题:
-
Start 节点太显眼: 如果在“开始节点”定义变量,前端用户(或 Prompt 编排者)必须手动填写,这既繁琐又不安全,容易被篡改。
-
Context 难以传递: 尝试通过 Flask 的
g对象或全局上下文传递,但在WorkflowRunner的复杂调度和多线程(Parallel Node)环境下,上下文往往容易丢失或导致脏数据。 -
变量池“拒收”: 即使强行塞入
inputs,如果没通过 DSL 的 Schema 校验,变量池(Variable Pool)会自动将其过滤掉。
本文将介绍最优雅的解决方案:扩展系统级变量(System Variables)。
核心原理:Variable Pool 的“后门”
Dify 的 VariablePool(变量池)是所有节点共享数据的内存空间。它在初始化时接受三类数据:
-
User Inputs: 从 Start 节点定义的变量。
-
Environment Variables: 环境变量。
-
System Variables: 系统预置变量(如
sys.query,sys.user_id)。
最佳实践是: 将自定义的隐式参数注入到 System Variables 中。这样既不需要前端显式定义,也能让所有节点通过 {{#sys.xxx#}} 的方式全局访问。
实施步骤
第一步:修改 Runner 入口逻辑
我们需要找到 Workflow 初始化的入口,拦截 system_inputs 的构建过程。
-
代码路径:
api/core/workflow/runner/workflow_runner.py -
目标方法:
run()
在该方法中,找到初始化 VariablePool 之前的代码段,手动注入你的参数。
Python
# api/core/workflow/runner/workflow_runner.py
def run(self, ...):
# ... 省略前序代码 ...
# 1. 找到构建 system_inputs 的位置
# Dify 默认会放入 query, conversation_id, user_id 等
system_inputs = {
'query': query,
'files': files,
'conversation_id': conversation_id,
'user_id': user_id,
# =========== 【修改点 START】 ===========
# 在这里注入你的隐式参数
# 注意:self.workflow 对象通常包含了 app_id
'app_id': self.workflow.app_id if self.workflow else None,
# 如果你需要传外部参数,可以从 kwargs 或者 flask.request 中获取
# 'external_trace_id': kwargs.get('trace_id')
# =========== 【修改点 END】 =============
}
# 2. 初始化 VariablePool
pool = VariablePool(
system_variables=system_inputs, # 这里会将字典加载到 sys. 命名空间
user_inputs=inputs,
environment_variables=environment_variables
)
# ... 后续执行逻辑 ...
第二步:配置白名单(视版本而定)
在 Dify 的部分新版本中,为了安全起见,系统变量可能存在枚举校验。如果你的代码报错提示“Invalid system variable”,请检查以下文件:
-
代码路径:
api/core/workflow/constants.py或api/core/workflow/enums.py
查看是否存在类似 SystemVariable 的枚举类。如果有,请务必添加你的 Key:
Python
class SystemVariable(Enum):
QUERY = 'query'
USER_ID = 'user_id'
CONVERSATION_ID = 'conversation_id'
# 添加你的自定义变量
APP_ID = 'app_id'
如何在前端/节点中使用?
这是最关键的一步。由于我们是通过后端代码“硬注入”的变量,前端的 Prompt 编排器(UI)并不知道这个变量的存在。
操作技巧:
在 LLM 节点或 Agent 节点的 Prompt 编辑框中,直接手动输入 Handlebars 语法,而不要依赖“输入 { 自动补全”的下拉菜单。
输入内容:
Handlebars
{{#sys.app_id#}}
现象说明:
-
UI 表现: 前端可能会提示“变量未定义”或者不高亮显示,这属于正常现象,因为前端 DSL 解析器在静态检查时找不到这个定义。
-
后端执行: 当 Workflow 真正运行时,后端 Parser 解析到
sys.app_id,会去VariablePool的 system 区域查找。由于我们在第一步已经注入了值,这里会被完美替换为真实的 App ID。
为什么不建议修改 _run 方法?
有一种替代方案是修改 api/core/workflow/nodes/llm/node.py 的 _run 方法,在发送请求前强行修改 Prompt。
不推荐理由:
-
侵入性太强: 你需要修改每一个可能用到该参数的节点类型(LLM, Agent, Tool 等)。
-
维护困难: 逻辑分散在各个 Node 类中,难以追踪。
-
破坏性: 可能会覆盖用户在界面上配置的 Prompt 逻辑。
相比之下,变量池注入法利用了 Dify 原生的变量解析机制,是符合架构设计的扩展方式。
总结
通过扩展 system_inputs,我们成功打通了一条从后端代码到 Prompt 上下文的隐形通道。
-
优点: 干净、安全、全局可用。
-
适用场景: 注入 App ID、租户隔离标识、计费 Tag、以及任何不需要用户感知的元数据。

被折叠的 条评论
为什么被折叠?



