首页
/ Dify Agent 的 Plugin LLM 层:通过 dify.plugin.llm 选择插件模型、绑定执行上下文并启用上下文压缩

Dify Agent 的 Plugin LLM 层:通过 dify.plugin.llm 选择插件模型、绑定执行上下文并启用上下文压缩

2026-09-06 14:32:44作者:幸俭卉

在 Dify Agent 的分层组合(RunComposition)中,Plugin LLM 层负责为当前一次运行选定"哪个插件包、哪个模型提供方、哪个模型"以及可选的模型参数。读完本文,你将掌握 DifyPluginLLMLayerConfig 各配置字段的准确含义、deps 依赖绑定的语义、context_window_tokens 驱动的两级上下文压缩(compaction)机制及其预算计算公式,并能直接复制一份包含 prompt 层、执行上下文层和 LLM 层的最小可运行模型组合。

1. Plugin LLM 层的定位与依赖要求

Plugin LLM 层从保留层名 llm 读取模型配置,而模型凭据的解析由 Dify API 完成,Agent 端不持有 Provider 密钥。

  • 该层的 type id 为 dify.plugin.llm,在源码中由 DIFY_PLUGIN_LLM_LAYER_TYPE_ID 常量定义,见 dify_plugin/configs.py 第 29 行;
  • 运行时的保留层名 llmprotocol/schemas.py 中的 DIFY_AGENT_MODEL_LAYER_ID = "llm" 定义。Runner 会校验 composition 中必须存在该层,缺失时抛出 AgentRunValidationError("Missing required 'llm' layer."),见 runtime/runner.py 与第 315 行;
  • 必须依赖 execution context 层。执行上下文层提供 API 网关所需的调用方身份(租户/用户),源码中该依赖是硬编码的 dataclass 字段:DifyPluginLLMDeps 声明了唯一的 execution_context: DifyExecutionContextLayer 依赖,见 dify_plugin/llm_layer.py。因此在 RunLayerSpec 中必须通过 deps 将其绑定到一个已声明的执行上下文层。

2. 配置字段详解

公开配置类 DifyPluginLLMLayerConfig 定义在 dify_plugin/configs.py。字段语义与源码实现一致如下:

字段 类型 含义
plugin_id str 插件包 id,例如 langgenius/openai。模型调用属于插件专属业务调用,因此该字段放在 LLM 层而非共享层中。
model_provider str plugin_id 内部的 Provider 名称。应使用 dify-agent/.envDIFY_AGENT_PROVIDER 的取值。
model str 模型名称。应使用 dify-agent/.envDIFY_AGENT_MODEL_NAME 的取值。
model_settings ModelSettings | None 可选的 pydantic-ai 模型设置(如 max_tokens 等),默认 None
context_window_tokens int | None 模型有效上下文窗口能力元数据,约束为大于 0 的整数(源码中为 Field(default=None, gt=0))。提供该值时启用基于窗口的压缩;省略则禁用。

几点源码级补充:

  • model_config 设置为 extra="forbid",即不允许传入未声明的字段,组合体在协议层同样是 extra="forbid"(见 protocol/schemas.pyRunLayerSpec),拼写字段名会在反序列化阶段直接报错而不是被静默忽略;
  • 该配置类带有一个 discard_legacy_credentials 模型校验器:如果旧客户端的请求体中仍携带 credentials 键,会被直接丢弃而不保留、不转发(见 dify_plugin/configs.py)。这从实现上保证了"模型凭据绝不接受来自 Agent 请求"这一安全约束。

3. 基本用法与 deps 绑定语义

最小 LLM 层声明如下:

from dify_agent.layers.dify_plugin import DIFY_PLUGIN_LLM_LAYER_TYPE_ID, DifyPluginLLMLayerConfig
from dify_agent.protocol import DIFY_AGENT_MODEL_LAYER_ID, RunLayerSpec


MODEL_PROVIDER = "replace-with-provider-from-dify-agent-env"
MODEL_NAME = "replace-with-model-from-dify-agent-env"
PLUGIN_ID = "langgenius/openai"

llm_layer = RunLayerSpec(
    name=DIFY_AGENT_MODEL_LAYER_ID,
    type=DIFY_PLUGIN_LLM_LAYER_TYPE_ID,
    deps={"execution_context": "execution_context"},
    config=DifyPluginLLMLayerConfig(
        plugin_id=PLUGIN_ID,
        model_provider=MODEL_PROVIDER,
        model=MODEL_NAME,
    ),
)

deps={"execution_context": "execution_context"} 的含义是:把 LLM 层的依赖字段 execution_context 绑定到组合中名为 execution_context 的层。RunLayerSpec 的四个字段 name/type/deps/metadata 会被规范化为 Agenton 的 provider 图配置,而 config 在 Agenton 边界单独保留、按层名传入 Compositor.enter(configs=...)(见 protocol/schemas.pyRunLayerSpec docstring)。

MODEL_PROVIDERMODEL_NAME 应与 dify-agent/.envDIFY_AGENT_PROVIDERDIFY_AGENT_MODEL_NAME 保持一致;官方入门文档 get-started/index.md 在示例注释中也明确要求二者对齐。可参考仓库中的完整示例 examples/dify_agent/dify_agent_examples/run_pydantic_ai_agent.py 观察真实取值如何传入组合。

3.1 运行时的模型构建链路

从源码结构看,get_model() 是模型构建的入口:它基于 inner_api_url(默认 http://localhost:5001)与内部 API key 构造 DifyApiLLMProvider,再从 deps.execution_context.config 取共享请求上下文,最终包装为 Pydantic AI 模型适配器 DifyLLMAdapterModel,见 dify_plugin/llm_layer.py。也就是说,model_provider 在这里作为请求级的模型身份传给适配器,而插件传输身份由 API Provider 承担。适配器把 Pydantic AI 消息映射为 Dify API 可信 LLM 网关的 Graphon 兼容请求/流式响应,Agent 端因此不直接托管任何 Provider SDK,见 adapters/llm/model.py

4. 上下文压缩(Context Compaction)

4.1 谁负责提供 context_window_tokens

context_window_tokens 是模型能力元数据:

  • Dify 产品侧的请求构建器会从所选模型的插件 schema 中解析该值,并使用当前租户与用户凭据;
  • 直接构造 DifyPluginLLMLayerConfig 的客户端则必须自行提供准确的正整数值;
  • Dify Agent 不会把它作为 Provider 参数转发,也不会合并进 model_settings

省略该字段仅禁用 Agent 侧基于窗口的压缩,不会限制或改变 Provider 自身对上下文窗口的执行。

4.2 压缩目标预算公式

当窗口已知时,Harness 的压缩目标按如下公式计算:

min(floor(context_window_tokens * 0.8), context_window_tokens - max_tokens)

第二项仅在 model_settings.max_tokens 为正时参与。目标预算若非正数(例如 max_tokens 逼近整个窗口),运行会在调用模型之前被拒绝。源码实现与文档公式一一对应,见 runtime/compaction.py

input_budget = context_window_tokens * 4 // 5
max_tokens = model_settings.get("max_tokens") if model_settings is not None else None
if max_tokens is not None and max_tokens > 0:
    input_budget = min(input_budget, context_window_tokens - max_tokens)
if input_budget <= 0:
    raise ValueError("Model max_tokens must leave a positive input context budget.")

4.3 两级压缩策略

build_compaction_capability 构建了一个 TieredCompaction,包含两个按序执行的子压缩器:

  1. ClearToolResults:先清空旧的 tool 结果,保留最近 3 个 tool-call/result 对及其输入(keep_pairs=3, clear_tool_inputs=False);
  2. SummarizingCompaction:若仍超预算,由当前模型增量摘要更老的历史,保留最近 20 条消息及首条用户消息(keep_messages=20, preserve_first_user_message=True, incremental=True)。

源码注释特别说明:两个子压缩器中的 max_tokens=1 只是用来满足子构造器"至少配置一个触发器"的校验要求,并非 Dify 的一 token 策略阈值(见 runtime/compaction.py 的模块 docstring)。

4.4 压缩对历史层的影响

压缩只有在组合中包含 history 层 时才影响后续运行。一旦 pydantic-ai 在 run capture 中完成消息绑定与构建,成功、失败、超时与被取消的运行都会把捕获到的重写后历史写入各自的终态会话快照。若失败或取消发生在快照尚不包含任何消息之前,则保留先前恢复的历史。被打断的半条消息可能被包含进来,并在后续独立运行复用该检查点时被修复;被打断运行的终态状态本身保持不变。

5. 完整的最小模型组合

多数运行包含 prompt 层、执行上下文层与 LLM 层,三者组合如下:

from agenton_collections.layers.plain import PLAIN_PROMPT_LAYER_TYPE_ID, PromptLayerConfig
from dify_agent.layers.execution_context import DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID, DifyExecutionContextLayerConfig
from dify_agent.layers.dify_plugin import (
    DIFY_PLUGIN_LLM_LAYER_TYPE_ID,
    DifyPluginLLMLayerConfig,
)
from dify_agent.protocol import DIFY_AGENT_MODEL_LAYER_ID, RunComposition, RunLayerSpec


MODEL_PROVIDER = "replace-with-provider-from-dify-agent-env"
MODEL_NAME = "replace-with-model-from-dify-agent-env"
PLUGIN_ID = "langgenius/openai"

composition = RunComposition(
    layers=[
        RunLayerSpec(
            name="prompt",
            type=PLAIN_PROMPT_LAYER_TYPE_ID,
            config=PromptLayerConfig(prefix="You are concise.", user="Say hello."),
        ),
        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",
            ),
        ),
        RunLayerSpec(
            name=DIFY_AGENT_MODEL_LAYER_ID,
            type=DIFY_PLUGIN_LLM_LAYER_TYPE_ID,
            deps={"execution_context": "execution_context"},
            config=DifyPluginLLMLayerConfig(
                plugin_id=PLUGIN_ID,
                model_provider=MODEL_PROVIDER,
                model=MODEL_NAME,
            ),
        ),
    ]
)

相关层文档:execution-context-layerprompt-layerhistory-layer

6. 关键约束速查

  • 模型层必须使用保留名 llm(即 DIFY_AGENT_MODEL_LAYER_ID),否则 Runner 校验直接失败。
  • plugin_id 之所以放在 LLM 层,是因为模型调用是插件专属的业务调用;共享的 Dify 调用方上下文(租户/用户等)由执行上下文层携带,两者职责分离。
  • 模型凭据绝不接受来自 Agent 请求:Dify API 解析租户当前的 Provider 配置并负责配额核算。旧请求中携带的 credentials 字段会在配置校验阶段被静默丢弃。
  • 省略 context_window_tokens 只是禁用窗口级压缩,不影响 Provider 自身对上下文窗口的执行。
  • model_settings.max_tokens 若导致输入预算非正,运行在模型调用前即被拒绝(ValueError),不会浪费一次模型调用。
登录后查看全文
热门项目推荐
相关项目推荐