首页
/ Dify Agent 执行上下文层(Execution Context Layer):为插件守护进程调用传递租户与执行身份

Dify Agent 执行上下文层(Execution Context Layer):为插件守护进程调用传递租户与执行身份

2026-09-04 11:56:23作者:韦蓉瑛

Dify Agent 采用分层(Layer)架构组织一次 Agent 运行的全部资源,其中「执行上下文层」(execution-context layer)承担着共享 Dify 运行标识与租户/用户身份的职责,是插件 LLM 层、插件工具层等所有需要访问插件守护进程(plugin daemon)的业务层的共同底座。读完本文,你将理解该层在 Dify Agent 分层组合中的定位、全部配置字段的取值规则与服务端凭证的注入机制,并能结合源码与测试用例掌握其“无状态、不持有 HTTP 客户端”的设计约束,从而正确编写包含执行上下文层的运行组合(RunComposition)。

一、执行上下文层在分层架构中的定位

执行上下文层携带三类信息:

  • 共享的 Dify 运行标识(如 app_idworkflow_run_idnode_id 等);
  • 调用插件守护进程所需的租户 ID 与可选的终端用户 ID;
  • 用于可观测性与链路关联的调用来源分类(invoke_from)等身份信息。

根据官方文档 execution-context-layer 的说明,该层需要与 plugin LLM layer 配合使用;当调用方希望把 Dify 工具暴露给模型时,还需叠加 plugin tool layer。这两个业务层都依赖执行上下文层来触达插件守护进程——例如插件 LLM 层通过 deps={"execution_context": "execution_context"} 显式声明这种绑定关系,因为 API 网关在解析模型凭证时需要调用方身份。

该层的类型 ID 为 dify.execution_context,对应源码常量 DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,定义在 configs.py

DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID: Final[str] = "dify.execution_context"

二、配置字段全解

官方文档给出的字段表如下(原文档骨架):

字段 类型 含义
tenant_id str 调用插件守护进程时使用的 Dify 租户/工作区 ID。
user_id str | None 可选的终端用户 ID,透传给插件守护进程。
invoke_from Literal[...] 记录用于可观测性与关联的 Dify 调用方类别。
app_id / workflow_id / workflow_run_id / node_id / node_execution_id / conversation_id / agent_id / agent_config_version_id / trace_id str | None 随运行转发、由 Dify 管理的可选执行标识。

对照当前仓库的实现 DifyExecutionContextLayerConfig,该配置类在上述字段之外还包含几组必填或可选的身份字段,编写组合时必须注意:

字段 类型 含义
agent_mode Literal["workflow_run", "single_step", "agent_app", "babysit", "fasten"] 必填,Agent 后端运行模式,标识本次运行属于哪类 Dify 产品场景。
invoke_from Literal["service-api", "openapi", "web-app", "trigger", "explore", "debugger", "published", "validation"] 必填,Dify 调用方类别,用于可观测性与关联。
user_from Literal["account", "end-user"] | None 可选,调用者身份模型。知识库层(knowledge layer)会读取该字段,让 Dify 内部 API 区分「平台用户」与「终端用户」的检索,且该身份判定不受模型控制。
agent_config_version_kind Literal["snapshot", "draft", "build_draft"] | None 可选,Agent 配置版本类别。

此外,配置类声明了 ConfigDict(extra="forbid", ...),即拒绝任何未知字段。测试 test_configs.py 明确验证了两点:

  • 传入 daemon_url 这类运行时设置会触发 ValidationError——守护进程传输配置不属于客户端提交的层配置;
  • 传入任意未知字段(如 unknown)同样被拒绝。

另一个容易踩坑的点:测试 test_execution_context_rejects_legacy_agent_mode_in_invoke_from 表明,把旧的 agent_mode 取值(如 workflow_run)塞进 invoke_from 会被视为非法值。invoke_from 只接受上文列出的 8 个调用方类别,运行模式必须写入独立的 agent_mode 字段。

三、基本用法

官方文档给出的最小示例如下(原文档骨架,注意当前版本还需补充必填的 agent_mode 字段):

from dify_agent.layers.execution_context import (
    DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
    DifyExecutionContextLayerConfig,
)
from dify_agent.protocol import RunLayerSpec


execution_context_layer = RunLayerSpec(
    name="execution_context",
    type=DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
    config=DifyExecutionContextLayerConfig(
        tenant_id="replace-with-tenant-id",
        user_id="replace-with-user-id",
        invoke_from="workflow_run",
    ),
)

如果不需要终端用户 ID,省略 user_id 或传 None 即可;其余大部分可选执行标识(app_idworkflow_run_idnode_id 等)在不可获得时同样可以省略——只有 tenant_idagent_modeinvoke_from 是必填项。

结合仓库内 plugin-llm-layer 文档 展示的完整最小模型组合,一份与当前源码取值严格对齐的执行上下文层写法是:

from dify_agent.layers.execution_context import (
    DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
    DifyExecutionContextLayerConfig,
)
from dify_agent.protocol import DIFY_AGENT_MODEL_LAYER_ID, RunComposition, RunLayerSpec

composition = RunComposition(
    layers=[
        RunLayerSpec(
            name="execution_context",
            type=DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
            config=DifyExecutionContextLayerConfig(
                tenant_id="replace-with-tenant-id",
                user_id="replace-with-user-id",
                user_from="account",
                app_id="replace-with-app-id",
                agent_mode="single_step",
                invoke_from="debugger",
            ),
        ),
        # ……以及依赖它的 llm 层(deps={"execution_context": "execution_context"})
    ]
)

四、服务端配置:守护进程凭证不进层配置

官方文档强调:执行上下文层的配置不包含守护进程传输设置,这些凭证配置在 Dify Agent 服务端:

DIFY_AGENT_PLUGIN_DAEMON_URL=http://localhost:5002
DIFY_AGENT_PLUGIN_DAEMON_API_KEY=replace-with-plugin-daemon-server-key

这样做的目的是让服务端凭证既不进入客户端提交的层配置,也不进入会话快照(session snapshot),避免敏感信息随快照流转。

服务端对应的读取位置在 server/settings.py,两个设置项均有默认值:

plugin_daemon_url: str = "http://localhost:5002"
plugin_daemon_api_key: str = ""

即守护进程 URL 默认为本地 http://localhost:5002,API Key 默认为空(生产环境必须显式设置)。这些设置的用途说明可参考 get-started 文档DIFY_AGENT_PLUGIN_DAEMON_API_KEY 是服务端发给插件守护进程的 API Key。

五、源码级实现:凭证如何注入、客户端如何创建

5.1 层实例只能由 Provider 工厂创建

从源码结构看,DifyExecutionContextLayer 是一个刻意保持“纯配置/纯设置”的 PlainLayer

  • 直接调用 from_config(config) 会抛出 TypeError,强制要求经由 Provider 工厂构造;
  • 服务端专用的构造入口是 from_config_with_settings(config, *, daemon_url, daemon_api_key),把服务端注入的 daemon_urldaemon_api_key 作为实例字段保存。

而工厂注入发生在运行时的 compositor_factory.py

layer_type=DifyExecutionContextLayer,
create=lambda config: DifyExecutionContextLayer.from_config_with_settings(
    DifyExecutionContextLayerConfig.model_validate(config),
    daemon_url=plugin_daemon_url,
    ...
)

也就是说,客户端提交的只是 DifyExecutionContextLayerConfig,守护进程 URL 与 API Key 由服务端在组装 Compositor 时补齐——这正是第四节环境变量设计的底层依据。

5.2 create_tool_client:业务层共用同一个共享 HTTP 客户端

执行上下文层为下游业务层提供的核心能力是 create_tool_client

def create_tool_client(self, *, plugin_id: str, http_client: httpx.AsyncClient) -> DifyPluginDaemonToolClient:
    if http_client.is_closed:
        raise RuntimeError("DifyExecutionContextLayer.create_tool_client() requires an open shared HTTP client.")
    return DifyPluginDaemonToolClient(
        tenant_id=self.config.tenant_id,
        plugin_id=plugin_id,
        plugin_daemon_url=self.daemon_url,
        plugin_daemon_api_key=self.daemon_api_key,
        user_id=self.config.user_id,
        http_client=http_client,
    )

要点有三:

  1. 层本身不创建、不缓存、不关闭 HTTP 客户端。运行时把由 FastAPI lifespan 持有的共享 httpx.AsyncClient 在每次工具调用时传入,层只是把它装配进 DifyPluginDaemonToolClient
  2. plugin_id 由调用方(业务层)传入——具体是哪个插件包,属于业务调用细节,执行上下文层只负责身份与传输上下文;
  3. 传入的共享客户端若已关闭,会抛出 RuntimeError 快速失败。

5.3 测试用例验证的三条约束

unit 测试 用三个用例钉死了上述行为:

六、注意事项汇总

结合官方文档 Notes 小节与源码实现,使用时需注意:

  • 不管理 HTTP 客户端生命周期:执行上下文层不会打开、缓存、关闭或快照 HTTP 客户端,其生命周期钩子保持继承的空实现,资源管理交给运行时;
  • plugin_id 归属业务层:模型调用属于插件 LLM 层的 plugin_id,工具调用属于每个插件工具配置的 plugin_id,执行上下文层只携带共享的 Dify 调用上下文;
  • 约定层名为 execution_context:如果使用别的名字,必须同步把 LLM 层与工具层 deps 中的依赖指向改为该名字;
  • extra="forbid" 严格校验:不要在层配置里塞 daemon_url 等传输字段,也不要把运行模式误填进 invoke_from,二者都会导致 ValidationError

至此,执行上下文层的完整链路是:客户端在 RunLayerSpec 中提交租户/用户/执行标识 → 服务端 Provider 工厂注入守护进程 URL 与 API Key → 业务层通过 create_tool_client 复用共享 HTTP 客户端访问插件守护进程。这一设计把敏感凭证与无状态身份配置彻底分离,使会话快照与客户端请求都不携带服务端密钥。

登录后查看全文
热门项目推荐
相关项目推荐