
Dify Agent 运行服务端实战指南Redis 事件流、运行时后端配置与 Run 调度语义全解析【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify本篇技术指南围绕 Dify Agent 子项目的 Agent Run ServerMVP API 服务端展开基于官方运维文档 dify-agent/docs/dify-agent/guide/index.md 并结合 FastAPI 应用工厂 与 配置模型源码 进行纵深解读。读完本文你将掌握该服务端的本地启动方式、全部DIFY_AGENT_*配置项的取值与校验规则、Redis 中 Run 记录与事件流的保留机制、Local/E2B 运行时后端的部署验证方法以及 Run 的调度、超时、取消与 SSE 观测协议能够独立完成一次从配置、部署到观测的完整联调。一、服务端定位与默认本地启动Dify Agent 的运行服务端是一个独立的 FastAPI/uvicorn 进程源码位于 dify-agent/src/dify_agent/server/app.py它只把 Redis 用作两处状态存储Run 记录与每个 Run 独立的事件流Redis Stream并不把 Redis 当作任务队列使用。默认本地启动方式为先启动 Redis再运行单个 uvicorn 进程uv run --project dify-agent uvicorn dify_agent.server.app:app --reload从 app.py 中的 lifespan 实现 可以确认进程启动时 FastAPI lifespan 会创建一个 Redis 后端的RedisRunStore供 HTTP 路由读取/写入 Run 记录与事件流两个共享的httpx.AsyncClient实例分别用于调用 Dify plugin daemon 与 Dify API 的 inner 接口二者共用同一套出站超时与连接池配置见_create_shared_http_client一个按部署选项构建的、无状态的运行时后端 profileLocal / Enterprise / E2B 三选一一个进程本地调度器RunScheduler负责以后台asyncio任务方式执行 Run。正因为 Run 的执行发生在请求处理器之外asyncio后台任务客户端断连不会取消正在执行的 Agent Run——这是理解整个服务端语义的关键前提。本地开发的环境依赖为一个 uvicorn 进程 Redis若使用插件模型层还需要一个可达的 Dify plugin daemon。二、配置体系ServerSettings与环境变量配置模型为 dify-agent/src/dify_agent/server/settings.py 中的ServerSettings基于 pydantic-settings。其model_config声明了环境变量前缀DIFY_AGENT_自动加载.env与dify-agent/.env两个文件若存在extraignore未声明的DIFY_AGENT_*变量不会导致启动失败。2.1 完整配置项参考以下为运维文档给出的全部环境变量及其默认值均可在 settings.py 的字段定义中一一对应验证环境变量默认值说明DIFY_AGENT_REDIS_URLredis://localhost:6379/0Redis 连接 URL。DIFY_AGENT_REDIS_PREFIXdify-agentRedis 记录与事件 key 的前缀。DIFY_AGENT_SHUTDOWN_GRACE_SECONDS30优雅关闭时等待活动本地 Run 的秒数超时后取消。DIFY_AGENT_RUN_RETENTION_SECONDS7200Run 记录与事件流自最后一次写入后在 Redis 中保留的秒数默认 2 小时。DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH5000每个 per-run Redis Stream 中可重放事件的近似上限MAXLEN ~语义。DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLEDtrue设为false时每个 text delta 不做合并直接发布。DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS100兼容 text delta 的软防抖间隔。已就绪的源事件在此间隔后仍可能继续合并等待中的源事件会触发定时 flush。必须大于 0禁用合并请使用DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLEDfalse。DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS4096缓冲的 text-delta 事件被立即 flush 的字符阈值。DIFY_AGENT_RUN_TIMEOUT_SECONDS3600Pydantic AIagent.run(...)模型/工具循环的墙钟时限秒。超时以agent_run_limit_exceeded失败。其默认值刻意与DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS一致但二者可独立配置。DIFY_AGENT_BINDING_FILE_DOWNLOAD_COMMAND_TIMEOUT_SECONDS210在沙箱内执行dify-agent file upload --no-download-link转换命令的 shell 时限。需大于 CLI 自身的 180 秒上传时限。DIFY_AGENT_API_TOKEN空私有 Run、Execution Binding、Home Snapshot 与 Binding 文件控制面路由所需的可选 Bearer token必须与 Dify API 侧AGENT_BACKEND_API_TOKEN一致。DIFY_AGENT_PLUGIN_DAEMON_URLhttp://localhost:5002Dify plugin daemon 的基础 URL。DIFY_AGENT_PLUGIN_DAEMON_API_KEY空发送往 Dify plugin daemon 的 API key。DIFY_AGENT_INNER_API_URLhttp://localhost:5001dify-agent 调用/inner/api/...时使用的 Dify API 服务根地址。DIFY_AGENT_INNER_API_KEY空发送往 Dify API inner 插件端点的 API key。应设为 Dify API 的INNER_API_KEY_FOR_PLUGINDocker 环境即PLUGIN_DIFY_INNER_API_KEY。DIFY_AGENT_RUNTIME_BACKENDlocal选择一套自洽的local、enterprise或e2bHome Snapshot Execution Binding 后端 profile。DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT空Local shellctl 数据面 URL。在默认 Local 选择下留空会禁用dify.runtime与资源端点。DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN空发送往 Local shellctl 的可选 Bearer token。DIFY_AGENT_LOCAL_SANDBOX_MATERIALIZED_HOME_ROOT/home/difyLocal shellctl 文件系统上per-Binding 物化 Home 的根目录。DIFY_AGENT_LOCAL_SANDBOX_WORKSPACE_ROOT/workspaceLocal shellctl 文件系统上可变 Workspace 的根目录。DIFY_AGENT_LOCAL_SANDBOX_HOME_SNAPSHOT_ROOT/home/dify/.snapshotsLocal shellctl 文件系统上不可变 Home Snapshot 的根目录。DIFY_AGENT_ENTERPRISE_SANDBOX_GATEWAY_ENDPOINT空Enterprise Gateway 端点该后端下为配置必填项。支持 Default-Home Binding不可变 Home Snapshot 操作仍不支持。DIFY_AGENT_ENTERPRISE_SANDBOX_GATEWAY_AUTH_TOKEN空发送往 Enterprise Gateway 的可选X-Inner-Api-Key。DIFY_AGENT_ENTERPRISE_SANDBOX_GATEWAY_TIMEOUT30Enterprise 控制面超时秒。DIFY_AGENT_ENTERPRISE_SANDBOX_PROXY_TIMEOUT60Enterprise shellctl-proxy 超时秒。DIFY_AGENT_E2B_API_KEY空E2B API key选择 E2B 时必填。DIFY_AGENT_E2B_TEMPLATEdifys-default-team/dify-agent-local-sandbox预置好 shellctl 与部署默认 Home 环境的 E2B 模板。DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS3600覆盖一次完整 Agent Run 的 RuntimeLease 最大连续活跃时间。默认值刻意与DIFY_AGENT_RUN_TIMEOUT_SECONDS一致但两者独立配置。Binding 资源在超时时暂停该设置不拥有 Run 终态也不是保留 TTL。DIFY_AGENT_E2B_SHELLCTL_PORT5004E2B 模板暴露的 shellctl 端口。DIFY_AGENT_SHELL_REDACT_PATTERNS空JSON 数组形式的附加正则用于对 Shell 输出做脱敏。DIFY_AGENT_STUB_API_BASE_URL空沙箱可达的 HTTP(S) Agent Stub API 基础 URL可以是服务根或/agent-stub用于为用户shell.run任务启用DIFY_AGENT_STUB_*环境变量注入。DIFY_AGENT_SANDBOX_FILES_BASE_URL空沙箱可达的 Dify API 基础 URL用于签名/files/*的字节上传/下载含 Config 文件与 skill 拉取启用 Agent Stub 文件操作时必填。可以包含 ingress 路径前缀但不得含 query 或 fragment。DIFY_AGENT_STUB_UPLOAD_FILE_SIZE_LIMIT50Agent 服务端拥有的 Agent Stub 上传大小上限MiB。文件请求处理工厂会将其换算为字节并作为必需的max_size发送给 Dify API用于签发大小受限的上传 URL。DIFY_AGENT_SERVER_SECRET_KEY空安全敏感的服务器级根密钥用于派生 Agent Stub Bearer token 的 JWE 加密密钥设置DIFY_AGENT_STUB_API_BASE_URL时必填。文档附带示例为开发值生产环境应设置唯一的 unpadded base64url 32 字节密钥。DIFY_AGENT_OUTBOUND_HTTP_CONNECT_TIMEOUT10共享出站 HTTP 连接超时秒。DIFY_AGENT_OUTBOUND_HTTP_READ_TIMEOUT600共享出站 HTTP 读超时秒。DIFY_AGENT_OUTBOUND_HTTP_WRITE_TIMEOUT30共享出站 HTTP 写超时秒。DIFY_AGENT_OUTBOUND_HTTP_POOL_TIMEOUT10共享出站连接池等待超时秒。DIFY_AGENT_OUTBOUND_HTTP_MAX_CONNECTIONS100共享出站 HTTP 最大总连接数。DIFY_AGENT_OUTBOUND_HTTP_MAX_KEEPALIVE_CONNECTIONS20共享出站 HTTP 最大空闲保活连接数。DIFY_AGENT_OUTBOUND_HTTP_KEEPALIVE_EXPIRY30空闲保活过期时间秒。2.2 配置校验与默认值出处源码中可以看到若干校验细节配置时值得注意出站 HTTP 参数在 settings.py 中有取值约束如连接数ge1它们被create_outbound_http_timeout组装成一个共享httpx.Timeout对象plugin daemon 与 Dify API inner 两个客户端共用——这是“运维调优集中一处端点 URL 与 API key 各管各的”设计。DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS的上限不是任意的settings.py 中leE2B_MAX_ACTIVE_TIMEOUT_SECONDS而 e2b.py 定义E2B_MAX_ACTIVE_TIMEOUT_SECONDS 60 * 60即不能超过 1 小时。DIFY_AGENT_RUN_TIMEOUT_SECONDS默认值来自 runner.py 的DEFAULT_AGENT_RUN_TIMEOUT_SECONDS 60 * 60约束为gt0。URL 类字段带规范化校验DIFY_AGENT_INNER_API_URL与DIFY_AGENT_SANDBOX_FILES_BASE_URL都会解析并拒绝含 query/fragment 的值见 settings.py 校验器。DIFY_AGENT_SHELL_REDACT_PATTERNS通过get_shell_redact_patterns解析空值返回空列表非 JSON 字符串数组会抛错。Agent Stub 依赖闭环由模型级校验器强制见 validate_agent_stub_requirements设置了DIFY_AGENT_STUB_API_BASE_URL就必须提供DIFY_AGENT_SERVER_SECRET_KEY若同时配置了 inner API key 却没配DIFY_AGENT_SANDBOX_FILES_BASE_URL启动即失败。2.3 完整.env示例DIFY_AGENT_REDIS_URLredis://localhost:6379/0 DIFY_AGENT_REDIS_PREFIXdify-agent-dev DIFY_AGENT_SHUTDOWN_GRACE_SECONDS30 DIFY_AGENT_RUN_RETENTION_SECONDS7200 DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH5000 DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS100 DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS4096 DIFY_AGENT_RUN_TIMEOUT_SECONDS3600 DIFY_AGENT_API_TOKENreplace-with-agent-backend-token DIFY_AGENT_PLUGIN_DAEMON_URLhttp://localhost:5002 DIFY_AGENT_PLUGIN_DAEMON_API_KEYreplace-with-daemon-key DIFY_AGENT_INNER_API_URLhttp://localhost:5001 DIFY_AGENT_INNER_API_KEYreplace-with-dify-inner-api-key-for-plugin DIFY_AGENT_RUNTIME_BACKENDlocal DIFY_AGENT_LOCAL_SANDBOX_ENDPOINThttp://127.0.0.1:5004 DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKENreplace-with-shellctl-token # Set these when shellctl runs directly on a host that does not have /home/dify. DIFY_AGENT_LOCAL_SANDBOX_MATERIALIZED_HOME_ROOT/tmp/dify-agent/materialized-homes DIFY_AGENT_LOCAL_SANDBOX_WORKSPACE_ROOT/tmp/dify-agent/workspaces DIFY_AGENT_LOCAL_SANDBOX_HOME_SNAPSHOT_ROOT/tmp/dify-agent/home-snapshots DIFY_AGENT_STUB_API_BASE_URLhttps://agent.example.com/agent-stub DIFY_AGENT_SANDBOX_FILES_BASE_URLhttps://dify.example.com DIFY_AGENT_STUB_UPLOAD_FILE_SIZE_LIMIT50 # This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens. # Replace this development default in production. # Generate one with: python -c import secrets; print(secrets.token_urlsafe(32)) DIFY_AGENT_SERVER_SECRET_KEYMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY2.4 三个 URL 的归属边界重点文档特别强调了两组容易混淆的 URL从 settings.py 的注释与字段结构 也可以印证Agent Stub 控制请求使用DIFY_AGENT_STUB_API_BASE_URL签名文件字节传输使用DIFY_AGENT_SANDBOX_FILES_BASE_URL。二者分属不同 owner不可混用。DIFY_AGENT_INNER_API_URL始终是可信的服务间 URL永远不会返回给沙箱。Config 文件与 skill 的拉取遵循同样的拆分Agent Stub 负责授权 Config 目标并返回短期 URL随后沙箱直接从 Dify API 的/files/*数据面取回字节对应源码中的DifyApiAgentStubConfigRequestHandler与DifyApiAgentStubFileRequestHandler见 settings.py。2.5 迁移与遗留别名移除 Agent Stub gRPC 是破坏性传输层迁移需把所有grpc://Agent Stub URL 替换为 HTTP(S)删除DIFY_AGENT_STUB_GRPC_BIND_ADDRESS且不要保留 gRPC 回退。沙箱 ingress 暴露面远程沙箱部署时只从 Agent Backend 暴露/agent-stub/*与既有的 Dify API/files/*数据面。/files/*ingress 必须完整保留签名 query string且其请求体限制应高于配置的文件大小上限为 multipart 帧与请求头留出余量两个限制无需数值相等大下载建议使用响应流式传输与合适的超时。不要把 Agent Backend 的/runs、Workspace 或 Binding 管理路由暴露给沙箱 ingress。浏览器展示 URL 独立于上述体系把 Dify API 的FILES_URL配置为浏览器可达的公开源或留空让响应使用同源相对/files/...URI切勿设为http://api:5001这类仅 Docker 内部可达的服务名。遗留别名DIFY_AGENT_SHELLCTL_ENTRYPOINT与DIFY_AGENT_SHELLCTL_AUTH_TOKEN仍被接受但它们只是DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT/DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN的遗留别名settings.py 中通过AliasChoices实现。新部署必须使用 Local 系列设置被移除的 shell-provider 选择器没有兼容配置。运行时后端选择是部署私有的启用 Shell 的 Run 请求使用 Execution Context、dify.runtime与dify.shell图运行配置中只携带由 Dify API 解析的不透明backend_binding_ref。其所有权与生命周期契约参见 运行时资源概念文档。2.6 保留策略与文本增量合并Run 记录与事件流共用同一保留窗口状态写入会刷新记录 TTL事件写入会同时刷新流 TTL 与对应记录 TTL从而让持续产出事件的活动 Run 始终可观测。这一点在 redis_run_store.py 的 Lua 终结脚本 中可以直接看到终态事件通过XADD ... MAXLEN ~追加事件、EXPIRE刷新流、SET ... EX刷新记录全部在同一 Lua 脚本内原子完成。文本增量合并由 event_coalescer.py 实现其行为与文档描述一一对应同一响应部件的文本 delta 与 provider 元数据在“软防抖 大小窗口”内合并后才发布防抖计时器从第一个缓冲 delta 开始而非每个 token 滑动当“读取下一个源事件会阻塞”时触发 flush已就绪的事件可以继续合并直到达到字符阈值或出现不兼容事件每个 Redis Stream 写入都会应用配置的近似最大长度MAXLEN ~语义重连游标早于保留窗口时会从剩余最旧事件恢复——因此调用方必须把终态快照与应用自有历史视为权威数据而不是依赖 Agent 事件流做长期历史存储。三、验证 E2B Compose 部署E2B overlay 要求一个预置模板默认difys-default-team/dify-agent-local-sandbox启动 shellctl 于 5004 端口以及DIFY_AGENT_E2B_API_KEYCompose 插值只把E2B_API_KEY与E2B_API_TOKEN作为部署级回退。从仓库根目录出发保持docker/.env不变在当前 shell 导出密钥后启动叠加部署叠加文件为 docker/docker-compose.e2b.yamlexport DIFY_AGENT_E2B_API_KEY${DIFY_AGENT_E2B_API_KEY:-${E2B_API_KEY:-${E2B_API_TOKEN:-}}} test -n $DIFY_AGENT_E2B_API_KEY export DIFY_AGENT_E2B_TEMPLATEdifys-default-team/dify-agent-local-sandbox docker compose \ --env-file docker/.env \ -f docker/docker-compose.yaml \ -f docker/docker-compose.e2b.yaml \ up -d --build该 overlay 会从当前检出构建dify-api:e2b-local与dify-agent-backend:e2b-local两个镜像禁用常规 Local sandbox 服务、把 Dify Agent 切到 E2B并把 PostgreSQL 挂载到独立的dify_e2b_postgres_dataCompose 卷上——该库在卷首次创建时为空、与常规栈隔离且会在后续启动中持续复用直到运维显式删除该卷。验证合并后的部署与分支构建的 Dify Agent APIdocker compose \ --env-file docker/.env \ -f docker/docker-compose.yaml \ -f docker/docker-compose.e2b.yaml \ ps agent_backend_port$( docker compose \ --env-file docker/.env \ -f docker/docker-compose.yaml \ -f docker/docker-compose.e2b.yaml \ port agent_backend 5050 | awk -F: NR 1 { print $NF } ) test -n $agent_backend_port curl --fail --silent --show-error \ --connect-timeout 2 --max-time 5 \ --retry 12 --retry-delay 1 --retry-connrefused --retry-max-time 60 \ http://127.0.0.1:${agent_backend_port}/openapi.json \ /dev/null docker compose \ --env-file docker/.env \ -f docker/docker-compose.yaml \ -f docker/docker-compose.e2b.yaml \ logs --tail100 agent_backend api worker停止验证栈但不删除其隔离数据库卷docker compose \ --env-file docker/.env \ -f docker/docker-compose.yaml \ -f docker/docker-compose.e2b.yaml \ down3.1 E2B 活跃超时与 Run 截止期的竞态DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS控制的是连续活跃 E2B 时间该超时触发时Binding 背后的物理资源会暂停并保留当前 Workspace。它不是资源年龄 TTL不会删除已暂停资源或不可变快照也不会权威地终结 Agent Run。若暂停的沙箱先被 Shell 工具观察到该 provider 失败会作为工具错误观察返回给 Pydantic AI。Run 与 E2B 的默认值都是 3600 秒但由于两者是独立时钟且 E2B 暂停传播是异步的先后顺序不可确定若 Shell provider 的RuntimeError先被观察到 → 成为一次工具观察tool observation若 Run 截止期取消先发生 → 取消会穿过 Shell 边界传播只有 Dify Agent 的 Run 截止期拥有终态agent_run_limit_exceeded失败。四、运行运行时后端集成契约测试4.1 一次性 Local 契约在dify-agent目录下运行脚本位于 tests/integration/dify_agent/runtime_backend/run_local_integration.sh。该脚本会启动一个监听空闲端口的 local-sandbox 容器并在退出时精确清理该容器。它刻意不提供镜像回退必须提供与待测代码同一 commit 构建的 Local Sandbox 镜像否则旧版 shellctl 实现可能让生命周期契约错误地通过cd dify-agent DIFY_AGENT_TEST_LOCAL_SANDBOX_IMAGEsame-commit-local-sandbox-image \ tests/integration/dify_agent/runtime_backend/run_local_integration.sh若要改用已受管的 Local shellctl 端点cd dify-agent DIFY_AGENT_TEST_LOCAL_SHELLCTL_ENDPOINThttp://127.0.0.1:5004 \ DIFY_AGENT_TEST_LOCAL_SHELLCTL_AUTH_TOKENreplace-with-shellctl-token \ pdm run pytest --import-modeimportlib \ tests/integration/dify_agent/runtime_backend/test_working_environment.py \ -k local -q -rs4.2 真实 E2B 契约cd dify-agent DIFY_AGENT_TEST_E2B_API_KEY$E2B_API_TOKEN \ DIFY_AGENT_TEST_E2B_TEMPLATEdifys-default-team/dify-agent-local-sandbox \ pdm run pytest --import-modeimportlib \ tests/integration/dify_agent/runtime_backend/test_working_environment.py \ -k e2b -q -rs测试文件为 tests/integration/dify_agent/runtime_backend/test_working_environment.py。要点Local auth token 在 shellctl 关闭认证时可选E2B 契约使用 1 小时E2B_MAX_ACTIVE_TIMEOUT_SECONDS的 RuntimeLease 上限这是连续活跃测试时间而非测试后保留 TTL两个契约都会创建独占资源并在finally块中显式清理。五、调度与关闭语义结合 runs 路由 与文档说明该服务端的调度模型可以概括为POST /runs持久化一条runningRun 记录并在同一进程内启动一个asyncio任务。没有 Redis job stream、消费组、pending 回收或自动重试层一旦请求 DTO 通过校验请求形态的运行时失败坏的 composition、prompt、output、snapshot 输入会在稍后以 failed run 上报而不是在创建时同步拒绝每个 Run 显式把 Pydantic AI 限制在500 个模型请求步内。工具调用没有单独计数限制但每次用于延续工具循环的模型请求都会消耗一个步数DIFY_AGENT_RUN_TIMEOUT_SECONDS的墙钟时限只套在 Pydantic AI 的agent.run(...)上含模型/工具循环与事件处理器不包含 compositor 入口、RuntimeLease 获取、工具准备、会话快照生成与资源退出。超时会取消活动 Run 任务、允许 compositor 释放资源并把 Run 终结为error_type: agent_run_limit_exceeded的失败。5.1 优雅关闭FastAPI 关闭期间调度器拒绝新 Run最多等待DIFY_AGENT_SHUTDOWN_GRACE_SECONDS秒让活动任务结束随后取消剩余任务并尝试将其终结为失败。成功与失败使用原子 Redis 状态迁移完成即上文 Lua 脚本中的running → 终态检查。取消流程则分两步先原子记录一个私有意图private intentowner 进程退出 runner 后第二次原子迁移追加run_cancelled、更新 Run 记录并删除意图。首个被接受的成功、失败或取消意图获胜。硬性进程崩溃仍可能把活动 Run包括已接受取消意图的卡在running服务内没有恢复或 worker 交接机制。5.2 水平扩展的边界多个 API 进程可以指向同一 Redis 前缀实现水平扩展但每个进程只执行自己接受的 RunRedis 提供共享的状态/事件可见性而非负载均衡或排队作业恢复。取消端点可以在任意进程上原子接受一个 running Run拥有 runner 的进程会观察到私有取消意图流取消并清理本地任务后才发出run_cancelled。HTTP 响应只确认“取消意图已持久化”GET /runs/{run_id}在清理完成前仍可能返回running。对已接受或已完成的取消重试是幂等的。原子终态终结目前假设配置的 Redis URL 指向一个可以执行全部 run 协调 key 的 Lua 脚本的单一 Redis 部署。记录与事件 key 名称不变取消仅新增一个私有 cancel-intent key这些 key 没有共享 Redis Cluster hash tag因此不支持 Redis Cluster。滚动升级期间旧进程仍可能走旧的拆分式事件/状态写入应把“单一终态不变量”视为仅在旧进程退出后生效。正确顺序是先全量部署原子终态终结再确保所有可能拥有 runner 的进程具备取消观察能力然后才可依赖跨路由取消运维应对“每个 Run 出现超过一个终态事件”与“Run 记录状态和终态事件类型不一致”配置告警。六、Run 输入与会话快照API不接受顶层user_prompt。应提交一个RunComposition由 Agenton 层提供用户输入在 MVP provider 集合下使用plain.prompt及其config.user字段{ composition: { schema_version: 1, layers: [ { name: prompt, type: plain.prompt, config: { prefix: You are concise., user: Summarize the current state. } } ] } }config.user可以是字符串或字符串列表。空或纯空白字符的有效 prompt 会在创建 Run 的校验阶段被拒绝——发生在持久化或调度之前。可选的 Pydantic AI 历史层使用保留名history其捕获的消息会保存在会话快照中以供后续恢复。恢复时应使用终态事件的session_snapshot并保持相同的层组合、名称与顺序。快照包含规则成功总是包含快照失败与取消仅在 compositor 入口成功且层退出完成时包含快照否则调用方应保留上一次快照。七、观测 Run状态轮询、事件流与 SSE用 HTTP 状态端点做粗粒度状态观测用事件端点做细粒度进度观测端点定义见 routes/runs.pyPOST /runs创建 running run 并在本地调度返回 202调度器关闭时返回 503。GET /runs/{run_id}返回running、succeeded、failed或cancelled失败记录可以额外暴露稳定的机器可读error_type与诊断性error文本并存404 表示 run 不存在。POST /runs/{run_id}/cancel在任意 API 进程上原子接受取消并立即返回。CancelRunResponse.status cancelled确认的是持久化接受的取消意图而非 runner 清理已完成需要清理完成状态或其会话快照的调用方必须等待公开的run_cancelled事件或使用cancel_run_and_wait。只有当成功/失败终态已经获胜时才返回409。GET /runs/{run_id}/events以after与next_cursor游标轮询 Redis Stream 事件日志源码中after默认0-0limit默认 100、上限 500。GET /runs/{run_id}/events/sse通过 SSE 重放并流式传输事件。SSEid即事件的 Redis Stream IDafterquery 游标优先于Last-Event-ID头。服务端在投递终态事件后正常关闭 SSE 响应客户端消费该事件后必须停止重连。两种游标形式都是排他式恢复游标服务端不会重发游标已排除的终态事件。事件生命周期与载荷规则成功 Run 依次发出run_started、零个或多个pydantic_ai_event、run_succeeded失败 Run 以run_failed结束已接受的取消以run_cancelled结束。每个 Run 至多追加一个终态事件事件信封保留id、run_id、type、data、created_atdata按事件类型定型包括pydantic_ai_event的 Pydantic AIAgentStreamEvent载荷以及终态事件可能携带的用于恢复的CompositorSessionSnapshotrun_succeeded总是包含快照run_failed与run_cancelled仅在 compositor 入口成功、层退出完成且实际产出了退出后快照时包含成功 Run 恰好有一个活动结果分支JSON 安全的output最终答案或当某层如dify.ask_human以外部延迟工具调用结束当前 Agent Run 时的deferred_tool_call失败事件载荷包含诊断性error、可选的来源特定reason、可选稳定error_type与可选session_snapshot取消载荷同样可能包含session_snapshotPydantic AI 请求/步数预算耗尽由 Dify Agent 强制与 Dify Agent 拥有的墙钟 Run 截止期都报告为error_type: agent_run_limit_exceeded——消费方应分支判断该值而不是解析错误文本provider 与连接超时不使用该 error type对应的失败 Run 记录与终态事件以相同 error type 原子提交。若 Agent backend 与 API 服务独立部署应先部署能接受该可选字段的消费方再让生产方开始发出它——公开协议模型会拒绝未知字段。八、仓库内的消费方示例仓库提供了两个打印观察输出/事件的简单消费方可直接作为调用模板dify-agent/examples/dify_agent/dify_agent_examples/run_server_consumer.py创建 Run 并轮询事件dify-agent/examples/dify_agent/dify_agent_examples/run_server_sse_consumer.py针对已有 run id 消费原始 SSE 帧。注意创建 Run 的示例提交的是 Dify 插件模型层因此运行它们需要 Redis、Agent 服务端、Dify API gateway 配置以及 Dify 中已配置的模型 provider。小结Dify Agent Run Server 的设计哲学在源码与文档中高度一致Redis 只做“状态与事件可见性”不做队列执行归属明确的进程所有终态迁移经由 Lua 脚本原子化文本事件在高频率下先合并再落流以控制 Redis 写入压力。运维时的关键决策点集中在三处运行时后端三选一Local/Enterprise/E2B及其目录/网关参数、Agent Stub 双 URL 的归属拆分与安全密钥闭环、以及保留/超时/步数预算这组相互独立又可协同的限流参数。按本文的配置表与 E2B 验证流程执行即可在单 commit 语义下完成从本地开发到容器化 E2B 部署的完整联调。【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考