Dify Agent 的 Plugin LLM 层:通过 dify.plugin.llm 选择插件模型、绑定执行上下文并启用上下文压缩
在 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 行; - 运行时的保留层名
llm由 protocol/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/.env 中 DIFY_AGENT_PROVIDER 的取值。 |
model |
str |
模型名称。应使用 dify-agent/.env 中 DIFY_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.py 的RunLayerSpec),拼写字段名会在反序列化阶段直接报错而不是被静默忽略;- 该配置类带有一个
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.py 的 RunLayerSpec docstring)。
MODEL_PROVIDER 与 MODEL_NAME 应与 dify-agent/.env 中 DIFY_AGENT_PROVIDER、DIFY_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,包含两个按序执行的子压缩器:
- ClearToolResults:先清空旧的 tool 结果,保留最近 3 个 tool-call/result 对及其输入(
keep_pairs=3, clear_tool_inputs=False); - 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-layer、prompt-layer、history-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),不会浪费一次模型调用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00