原计划建进共享 muse-dify 遇两阻塞:①muse 锁单工作区+create-tenant 抛错 ②HTTP 节点经 ssrf_proxy 打内网 worker 被 deny to_private_networks 拦(muse→:9501 实测403)。 创始人拍板给 game 单起隔离实例:独立 compose(game-dify)+错开端口(28080/28443/25003) +独立卷+新 SECRET_KEY,自有 ssrf 模板放行 gen worker,muse 零受扰(仍403)。 导入 DSL(0.1.5→0.6.0迁移完好)+发布+真跑:HTTP节点succeeded/worker返202/ worker日志'受理(202) userToken=<none>全局key回落'。凭据档回写 game-dify 端点+登录+ headless token 签发法(RSA/CSRF 绕过)。spike README/DSL 更新为已验证态。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.2 KiB
dify 游戏开发节点(形态 A / a-min)—— 已建成并跑通
内测 WU3「dify workflow 驱动 agentscope 生成」形态 A 的落地留痕。设计与边界见 docs/agent-specs/2026-07-07-内测-WU3-dify游戏开发节点-设计.md;实例端点与登录见 canonical docs/内网凭据与端点.md「game 专属 dify 实例」段。这里放怎么搭起来的、HTTP 节点怎么配、以及搭建时踩到的真配置阻塞与解法。
一句话与现状
在 game 专属的 dify 里搭一条最小 workflow:开始节点收一句话创意 brief → 一个 HTTP Request「游戏开发节点」把它组成后端早已定义的 §6.1 job、POST 给便宜档 cheap-worker :9501/generate → 结束节点回显那次投递的响应。worker 入队即返 202,agentscope 在后台真跑一局。后端一行不改。
2026-07-08 已建成并端到端跑通(LIVE):workflow 应用「游戏开发节点-形态A」建在 game 专属 dify(http://100.64.0.8:28080,app_id 5ba490f2-e0be-4c84-98a4-8ab5873a17c4),发布后真跑一次——HTTP 节点 succeeded、worker 返 202、workflow succeeded,出参 accepted:true / job_id dify-… / queued:1;worker 侧日志同刻 收到 job … → 受理(202)、凭据来源=全局 env key(回落) userToken=<none>。本目录的 workflow-游戏开发节点-formA.yml 就是这次真导入进去的定义,已验证可导入可跑(dify 导入时从 DSL 版 0.1.5 迁到现版 0.6.0,三节点与 HTTP 节点的 URL/body/超时全部完好)。
为什么是「专属实例」——搭建时揭出的两个真阻塞
原计划想把 game 的 workflow 建进 mini-infra 上那套现成 dify,搭的过程揭出两条只有真配置核查才暴露的阻塞,最终导致改走「给 game 单起一套」:
其一,那套 dify 是 muse 与 game 两个项目共用的,且被 muse 锁成单工作区(is_allow_register:false、is_allow_create_workspace:false),flask create-tenant 在这套部署里还会在注册流程里抛错。要给 game 干净地开一个隔离工作区,绕不开动 muse 共用的实例。
其二,dify 的 HTTP Request 节点经它自带的 SSRF 代理出网,而那个代理默认把内网 worker 拦死。dify 部署自带一个 squid 反 SSRF 代理(*-ssrf_proxy 容器),所有 HTTP 节点出站都从它走,规则里 http_access deny to_private_networks 排在放行之前、且含 100.64.0.0/10(Tailscale CGN 段)。实测:经 muse 的 ssrf_proxy 打 100.64.0.7:9501 返 403。所以直接在共享实例的 UI 里配好节点点运行会得到 403,不是节点配错,是 SSRF 防护按设计拦掉了所有私网目标。
创始人据此拍板:给 game 单起一套专属 dify 实例,天生隔离、零 muse 风险、我们全控。这套 game 实例自己的 ssrf_proxy 模板在开机前就放行了生成 worker(acl gen_worker dst 100.64.0.7 插在 deny to_private_networks 前),实测 game→:9501 已 404 通、muse→:9501 仍 403(muse 一字未动)。
HTTP「游戏开发节点」配置(手搭/核对照填)
-
方法 / URL:
POST http://100.64.0.7:9501/generate(dev worker 在 mini-desktop;worker 迁机改这个 IP,并同步 game ssrf 模板的gen_worker放行 IP)。 -
超时:握手级,约 10 秒。这是异步投递面,worker 入队立即返 202,真生成在后台跑几十秒到几分钟。绝不能设成等生成完成的长超时——结果经落盘/回调回流,不从这个 HTTP 响应回来。
-
Body(application/json):一份 §6.1 job,a-min 省略
callback。便宜档生成核心真正只读brief与gameId,其余给合理默认即可:{ "job_id": "dify-{{#sys.workflow_run_id#}}", "traceId": "dify-{{#sys.workflow_run_id#}}", "tier": "cheap", "kind": "generate", "templateId": "generic", "brief": "{{#start.brief#}}", "model": "MiniMax-M2.7", "gameId": "dify-{{#sys.workflow_run_id#}}", "budget": { "maxYuan": 0.15, "maxLlmCalls": 8 }, "idempotency_key":"dify-{{#sys.workflow_run_id#}}", "deadline_ms": 600000 }几条写死的红线(读码坐实,别踩):
traceId必带dify-前缀做命名空间隔离,且令job_id=traceId=idempotency_key=同一个dify-<workflow_run_id>。worker 按job_id(缺则traceId)在进程级_seen去重:同键重投直接返 202、不重入、不重复烧 token——所以 dify 侧对同一次运行配 HTTP 重试是安全的。反过来,一次真正的重新生成必须是一次新的 dify 运行(新workflow_run_id= 新 job_id);别在节点里每次重试新造 uuid,也别以为同 job_id 再 POST 能拿到新产物(会被静默去重照返 202)。_seen进程级、永不淘汰、worker 重启即清空。budget.maxYuan是死字段,不承载封顶。单局真实成本上界由服务端cheap_budget固定的 ¥10 软停 / ¥15 硬地板决定(调用方 override 被 pop 掉)。dify 路真正的额度兜底 = 给 dify 专用 new-api token 在 new-api 侧配用量配额上限。- 不要试图从 dify 节点透传 token 做 per-user 计费——driver 的 key 只从进程级 env
NEWAPI_KEY取,job 里没有 token 位。隔离靠「这台 dev 栈就是 dify 专用栈」,不靠 job 自带凭据(本次实测正是userToken=<none>→ 全局 key 回落)。
复现:导入 + 发布 + 跑
前提 = game 专属 dify 已起、生成 worker 在 :9501 且 game ssrf 已放行(见凭据档「game 专属 dify 实例」段)。这套 dify 登录要 RSA、console 走 CSRF double-submit,headless 取 token 与导入的完整办法见凭据档同段。要点:AccountService.login 在 flask shell 里签发 access_token+csrf_token → POST /console/api/apps/imports {"mode":"yaml-content","yaml_content":<本目录 yml>} 导入 → POST /apps/<id>/workflows/publish 发布 → POST /apps/<id>/workflows/draft/run {"inputs":{"brief":"…"}} 真跑,成功判据 = HTTP 节点出参 status_code=202、worker 日志 受理(202)。
文件
workflow-游戏开发节点-formA.yml—— dify workflow 定义,已验证可导入可跑(2026-07-08 真导入进 game 实例并跑出 202)。