Dify 源码实战:如何将 App ID 等隐式参数注入 Workflow 变量池?

在 Dify 的二次开发或深度集成过程中,我们经常遇到这样一个需求:

场景描述:

我们需要在调用 Workflow 时,将一些业务上下文参数(如 app_id、tenant_id、外部系统的 trace_id 或用户等级)传递给内部的大模型节点或 Agent 节点使用。

遇到的问题:

  1. Start 节点太显眼: 如果在“开始节点”定义变量,前端用户(或 Prompt 编排者)必须手动填写,这既繁琐又不安全,容易被篡改。

  2. Context 难以传递: 尝试通过 Flask 的 g 对象或全局上下文传递,但在 WorkflowRunner 的复杂调度和多线程(Parallel Node)环境下,上下文往往容易丢失或导致脏数据。

  3. 变量池“拒收”: 即使强行塞入 inputs,如果没通过 DSL 的 Schema 校验,变量池(Variable Pool)会自动将其过滤掉。

本文将介绍最优雅的解决方案:扩展系统级变量(System Variables)


核心原理:Variable Pool 的“后门”

Dify 的 VariablePool(变量池)是所有节点共享数据的内存空间。它在初始化时接受三类数据:

  1. User Inputs: 从 Start 节点定义的变量。

  2. Environment Variables: 环境变量。

  3. 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.pyapi/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。

不推荐理由:

  1. 侵入性太强: 你需要修改每一个可能用到该参数的节点类型(LLM, Agent, Tool 等)。

  2. 维护困难: 逻辑分散在各个 Node 类中,难以追踪。

  3. 破坏性: 可能会覆盖用户在界面上配置的 Prompt 逻辑。

相比之下,变量池注入法利用了 Dify 原生的变量解析机制,是符合架构设计的扩展方式。


总结

通过扩展 system_inputs,我们成功打通了一条从后端代码到 Prompt 上下文的隐形通道。

  • 优点: 干净、安全、全局可用。

  • 适用场景: 注入 App ID、租户隔离标识、计费 Tag、以及任何不需要用户感知的元数据。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值