# LLM3DGraphics · 跨项目 / AI 调用指南 给**其他项目、其他开发者、自动化 Agent**用:如何把自然语言需求编排成对 `LLM3DGraphics` HTTP API 的调用,产出图片、Unity 图片素材、网格、(可选)绑骨角色与骨骼动画。 - 服务默认:`http://192.168.130.16:8008` - OpenAPI:`http://192.168.130.16:8008/docs` - **DGX 绑骨/动画规范**见 **[DGX3D生成说明.md](./DGX3D生成说明.md)**;本文偏「怎么编排调用」 - 引擎可选:`dgx`(Spark 绑骨+动作)、`hunyuan`(混元官网网页) - 本地图像入口:`http://192.168.130.16:8008/#local-images`;当前仅保留 **Qwen-Image-2.1-Turbo BF16**,文字生成、多参考图编辑、批量草稿见 **§4.4**。 --- ## 0. 一分钟结论 | 用户说法(示例) | AI 应做的事 | |------------------|------------| | 「用这张图做一个会动的狼,再给 idle / 跑」 | **`engine=dgx`**:图 → 绑骨 → 多次 `/animate` | | 「官网混元出个模型就行 / 走混元网页」 | **`engine=hunyuan`**:先确认微信已登录 → 出 `model` / `model_ex`(**无绑骨、无 animate**) | | 「做一个石灯道具」 | 优先 `dgx` + `character_type=static`;或 `hunyuan` 只要网格 | | 「做一个士兵,再套走路」 | **`engine=dgx`** + `humanoid` → Step2 Kimodo | | 「写段文字直接出 3D」 | 先通过 §4.4 本地 Turbo 生成参考图 → 再选 3D 引擎 | | 「文字出图 / 多图生成新图 / 批量草稿」 | **§4.4 本地 Turbo**:提交图片批次 → 轮询 → 下载 PNG / Unity ZIP | | 「照这个画风换成运动装男孩,再做 3D」 | §4.4 上传风格参考图并写清修改要求 → 选定生成图片 → 上传 `/api/jobs` | **硬约束(调用前必读)** 1. 先选引擎(§引擎选择):要绑骨/动作 → **只能 `dgx`**;只要官网质量网格 → 可用 **`hunyuan`**。 2. 无参考图时优先走 **`POST /api/local-images/jobs` 本地 Turbo**(§4.4);用户选择云端生图时走 **`POST /api/image/generate` 转接**(§4.1),勿直连上游、勿带 Key。 3. `dgx` 可驱动角色 = **两步**:`/api/jobs` → `/api/jobs/{id}/animate`;多动作多次 animate(注意覆盖)。 4. `hunyuan` / `dgx`+`static`:**禁止** `/animate`(会 422)。 5. `hunyuan` 必须当前浏览器客户端已登录混元官网(`hunyuan_logged_in`);首次登录在客户端页面扫码。 6. 3D 队列同时 `queued`+`running` ≤ 5;本地图像最多排队 4 批、每批 1–16 张。轮询间隔 ≥ 2s;单任务可能数分钟~数十分钟。 7. Turbo 可与混元共同驻留,**图像与 DGX 3D/对比/动作任务仍串行执行**。图像队列结束后才能转入 DGX 3D;不要绕过本机调度器直接调用 DGXA 的 8090 worker。 --- ## 1. 前置:服务与能力探测 ```http GET /api/capabilities ``` 关注字段(名称以实际 JSON 为准): | 字段 | 含义 | |------|------| | `image_generate_ready` | 本机生图转接是否可用 | | `hunyuan_logged_in` / `hunyuan_status` | 混元官网微信登录态 | | `image_ready` | 混元网页是否已登录 | | `dgx_ready` / `dgx.hy3d_ready` | DGX Spark 图生高模 | | `dgx.tokenrig_ready`(或 `unirig_ready`) | 能否绑骨(`static` / 混元路径不需要) | | `dgx.unimate_ready` | 非人形文生动画 | | Mixamo 数量相关字段 | 人形动作库是否非空 | 未就绪时:**不要硬调**,向用户报告缺哪一段。 本地 Turbo 的能力单独探测,不用 `image_generate_ready` 判断: ```http GET /api/local-images/status ``` 检查 `connected`、`ready` 和 `models.turbo.installed`;当前模型 ID 应为 `Qwen/Qwen-Image-2.1-Turbo`。`loaded_model=turbo` 表示已加载,`busy=true` 且 `loaded_model=null` 时通常正在首次加载,应先等待。`co_resident`、`hy3d_paused`、`available_gb` 用于观察驻留和内存状态。混元因图像模式暂时暂停时,图像队列空闲后先调用 `POST /api/local-images/prepare-3d`,再重新探测 DGX 能力。 ### 1.1 Windows 跨机器部署:Hunyuan 绿灯、TokenRig/Kimodo 红灯 状态探测在 **运行 LLM3D 后端的 Windows 机器与账号**中执行,不在打开网页的浏览器机器执行。复制工程和 `config.json` 不会自动配置该账号的 SSH 别名、密钥授权、SSH Agent 和 `known_hosts`。 | 状态灯 | 当前探测内容 | |--------|--------------| | Hunyuan3D | HTTP GET `dgx.hy3d_url` 首页,5秒内状态码小于500算通过;没有执行造型推理 | | TokenRig | SSH 到 `dgx.ssh_host`,检查 SkinTokens 的入口脚本、可执行Python和两个固定权重文件;远程失败时另检查本机环境 | | Kimodo | SSH 到同一个主机,检查 `kimodo_gen`、Llama文本编码器目录和LLM2Vec配置;没有执行动作推理 | 因此模型可以仍在 DGXA 正常安装,却因新 Windows 后端的 SSH 环境不完整而显示红灯。绿灯也不表示权重已加载到 GPU。当前每个远程探测约12秒超时,SSH错误统一折叠为 `false`,状态缓存20秒;多个SSH失败串行发生时,能力接口可能等待约48秒。 在**部署机上,用启动 LLM3D 的同一个账号**,从 `LLM3DGraphics` 目录运行只读自检: ```powershell .\.venv\Scripts\python.exe .\deploy\check_dgx_connection.py --output .\outputs\dgx_connection_check.json ``` 脚本:[check_dgx_connection.py](./LLM3DGraphics/deploy/check_dgx_connection.py)。它检查账号、SSH解析配置、认证、远端各个文件及混元HTTP连接,不加载模型、不修改密钥或服务,不读取/打印私钥和API Key内容。 | 报告项 / 错误 | 排查方向 | |---------------|----------| | `account` | 是否确实为运行后端的账号;管理员终端和计划任务/服务账号可能不同 | | `resolved_ssh` | `hostname`、`user`、`port`、`identityfile` 是否指向该机可用的连接配置 | | `Could not resolve hostname` | 当前账号未配置 `DGXA` 别名,或DNS无法解析 | | `Permission denied (publickey)` | SSH用户、密钥文件、Agent或DGXA上的公钥授权不匹配 | | `Connection timed out/refused` | 地址、端口、路由、防火墙、代理或SSH服务问题 | | `Host key verification failed` | 用该账号交互登录并核对服务器指纹,完成主机身份确认 | | `remote.checks.*.ok=false` | SSH已连通;按报告中的具体路径检查文件或执行权限 | 若采用 `dgx.ssh_host=DGXA`,该Windows账号的 `%USERPROFILE%\.ssh\config` 必须有对应连接设置;使用新机器自己的已授权密钥,例如: ```text Host DGXA HostName User spark Port 22 IdentityFile ~/.ssh/id_ed25519 ``` `IdentityFile` 以部署机实际密钥为准,DGXA需已授权对应公钥。先确认该账号执行 `ssh -o BatchMode=yes DGXA "echo READY"` 成功,再重启/刷新后端状态。私钥不放进工程或说明文件。部署机访问混元HTTP成功,不代表它能用SSH访问DGXA。 --- ## 2. 引擎选择 `POST /api/jobs` 的 `engine` 二选一: | `engine` | 高模从哪来 | 绑骨 / 动作 | 典型用途 | |----------|------------|-------------|----------| | **`dgx`** | Spark Hunyuan3D 2.1 | TokenRig + `/animate`(或 `static` 跳过) | 角色可驱动、要动作、DGX 静物 | | **`hunyuan`** | 腾讯混元3D**官方网页**(Playwright 自动化) | **无** | 只要官网网格质量;不依赖 Spark | 决策提示: - 用户明确说「混元官网 / 混元网页 / 微信混元」→ `hunyuan` - 用户要「会动 / 绑骨 / Mixamo / UniMate」→ **必须 `dgx`** - 只要静物网格且 DGX 忙/未就绪 → 可降级 `hunyuan` - `character_type` **仅 `engine=dgx` 生效**;混元任务里该字段会被忽略 --- ## 3. 混元3D 官方网页流程(`engine=hunyuan`) 走 [3d.hunyuan.tencent.com](https://3d.hunyuan.tencent.com/):服务端用 Playwright(Edge)提交图片生成;微信登录二维码显示在发起登录的客户端页面,由用户用自己的微信扫码。登录态按浏览器客户端隔离。 ### 3.1 登录(客户端页面扫码,会话按客户端复用) ```http GET /api/hunyuan/status # logged_in? POST /api/hunyuan/login # 服务端启动登录;客户端页面显示二维码(约 3 分钟内) GET /api/hunyuan/login/qr # 读取当前客户端的二维码图片 POST /api/hunyuan/logout # 清除当前客户端会话 ``` 后端仍在服务机运行 Playwright,但不会要求用户去服务机窗口扫码。扫码二维码由客户端页面请求并显示。每个浏览器客户端通过独立的 HttpOnly Cookie 标识登录会话,服务端分别保存到 `data/hunyuan/clients/<客户端标识>/storage_state.json`。同一浏览器刷新后会复用登录态;不同设备、浏览器或无痕窗口需要分别扫码。生成任务使用提交请求的客户端登录态,退出登录只清除该客户端的会话。旧版共享文件 `data/hunyuan/storage_state.json` 不再使用。 Agent 侧: 1. 读 `capabilities.hunyuan_logged_in`(或 `/api/hunyuan/status`) 2. 若为 `false`:让用户在要使用的浏览器客户端打开工作台并点击「微信登录」,在该页面显示的二维码上用自己的微信扫码;轮询该客户端的 status 至 `logged_in`。不要让用户去服务机窗口扫码。 3. 未登录时提交 `engine=hunyuan` 会 **401** 直接用 HTTP 客户端接入时,必须在 `status`、`login`、`login/qr` 和生成请求之间保留同一个服务端下发的 HttpOnly Cookie;否则请求会被识别为不同客户端。需要把二维码安全地呈现给真人扫码,单纯的无状态 curl/Agent 调用不能代替用户完成登录。 ### 3.2 生成任务 ```http POST /api/jobs Content-Type: multipart/form-data engine=hunyuan mode=image image=@ref.png source_faces=50000 target_faces=5000 texture_size=1024 output_format=glb ``` | 字段 | 说明 | |------|------| | `mode` | **仅 `image`**(混元网页引擎不接受 text) | | `source_faces` | 官网高模档:`5000`(5k)/ `20000`(20k)/ `50000`(50k)/ `500000` / `1000000`;常用 `50000`。5k 与 20k 页面无按钮,提交时改写 `faceCount` | | `target_faces` | 本机低模目标;`<500000` 且低于高模面数时触发 Blender 烘培 → `model_ex` | | `texture_size` | 低模烘培贴图:`512` / `1024` / `2048` | | `output_format` | `glb` 或 `fbx`(fbx 时额外打 ZIP) | | `character_type` | **无效**(不要指望混元出绑骨) | 轮询 `GET /api/jobs/{id}` → `status=completed`。 ### 3.3 产物(网格 only) | 下载键 | 含义 | |--------|------| | `glb` / `fbx` | 官网高模 | | `glb_ex` / `fbx_ex` | 本机低模+烘培(若触发) | | `preview_url` | 网页预览 | **不要**调用 `/animate`。若后续仍要动作:把混元网格当作参考图/资产另开 `engine=dgx` 流程(当前无「混元网格直接喂 TokenRig」的一键接口;需要绑骨请直接用 DGX 图生)。 ### 3.4 耗时与依赖 - 官网生成常见数分钟~约 20 分钟;另加本机低模烘培时间 - 依赖:服务端 Playwright + Edge 通道、当前客户端有效的微信登录态、可选 Blender(低模/FBX) - 与 DGX 共用同一任务队列(≤5) ### 3.5 curl 示例 ```bash BASE=http://192.168.130.16:8008 # 确认登录 curl -s "$BASE/api/hunyuan/status" # 未登录时:先在工作台当前客户端点击「微信登录」,再由真人扫描页面上的二维码。 # 纯 curl 示例需要持久化 Cookie,并自行将二维码呈现给真人;不要只靠无状态 curl 完成扫码登录。 # curl -s -X POST "$BASE/api/hunyuan/login" curl -s -X POST "$BASE/api/jobs" -F engine=hunyuan -F mode=image -F source_faces=50000 -F target_faces=5000 -F texture_size=1024 -F image=@ref.png ``` --- ## 4. 生成参考图:本地 Turbo 与云端转接 DGX **不能直接文生 3D**。用户只给文字外貌时,先通过 **§4.4 本地 Turbo** 生成图片,再交给 §6 的 DGX 编排剧本。用户选择云端生图时使用下述 §4.1–4.3 转接接口;两条生图路径都通过 LLM3DGraphics 调度。 | 角色 | 说明 | |------|------| | **外部项目 / Agent** | 云端路径只调 `POST /api/image/generate`,**不要**拿上游 Key、**不要**直连 grsai;本地路径见 §4.4 | | **本机服务** | 用 `config.json` → `dgx.grsai`(或环境变量 `GRSAI_API_KEY`)调上游,并把结果缓存到本机 | | 运维配置参考 | [生成路径.md](./生成路径.md)、[HelpDoc/gpt-image_doc.md](./HelpDoc/gpt-image_doc.md)(仅服务器侧) | 探测:`GET /api/capabilities` → `image_generate_ready == true`。 ### 4.1 转接接口 ```http POST {BASE}/api/image/generate Content-Type: application/json { "prompt": "一只棕色四足狼,全身正面三视图感,纯白背景,游戏概念设定图,无文字水印", "aspect_ratio": "1:1", "quality": "auto", "images": [] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `prompt` | 是 | 外貌描述,≤4000 字 | | `aspect_ratio` | 否 | 默认用服务端 `dgx.grsai.aspect_ratio`(常见 `1:1`) | | `quality` | 否 | 默认 `auto` | | `images` | 否 | 图生图参考:URL 或 data URL / base64,最多 4 张 | 成功响应(**不含**上游 URL / API Key): ```json { "id": "a1b2c3...", "status": "succeeded", "url": "/api/image/a1b2c3...", "content_type": "image/png", "bytes": 123456, "model": "gpt-image-2.5", "aspect_ratio": "1:1" } ``` 下载图片: ```http GET {BASE}/api/image/{id} ``` 将该文件作为后续 `POST /api/jobs` 的 `image` 上传即可。 **给 DGX 的构图建议**(提高绑骨成功率): - 主体居中、轮廓清晰;尽量白底或简单底 - 全身可见;角色类避免极端透视 / 大面积遮挡 - 提示词写清物种、体态、材质;少杂乱场景文字 ### 4.2 curl / Python 示例(外部只打本机) ```bash BASE=http://192.168.130.16:8008 curl -s -X POST "$BASE/api/image/generate" \ -H 'Content-Type: application/json' \ -d '{ "prompt": "石质庭院灯笼,白色背景,产品设定图", "aspect_ratio": "1:1" }' # 记下返回的 id / url,再下载: curl -L -o prop.png "$BASE/api/image/" ``` ```python import requests from pathlib import Path BASE = "http://192.168.130.16:8008" def text_to_image(prompt: str, out_path: Path, aspect_ratio: str = "1:1") -> Path: r = requests.post( f"{BASE}/api/image/generate", json={"prompt": prompt, "aspect_ratio": aspect_ratio}, timeout=300, ) r.raise_for_status() data = r.json() if data.get("status") != "succeeded" or not data.get("url"): raise RuntimeError(data) url = data["url"] if url.startswith("/"): url = BASE + url img = requests.get(url, timeout=120) img.raise_for_status() out_path.write_bytes(img.content) return out_path ``` ### 4.3 服务器侧配置(仅运维,不对外部暴露) 在本机 `LLM3DGraphics/config.json`(已 gitignore)中配置,例如: ```json "dgx": { "grsai": { "api_key": "<密钥,见生成路径.md>", "model": "gpt-image-2.5", "bases": ["https://grsai.dakka.com.cn", "https://grsaiapi.com"], "aspect_ratio": "1:1", "quality": "auto" } } ``` 也可用环境变量 `GRSAI_API_KEY` / `GPT_IMAGE_API_KEY`。外部调用方**无需**知道这些。 ### 4.4 本地图像生成:Qwen-Image-2.1-Turbo BF16(当前推荐流程) 更新于 **2026-10-13**。当前只安装 `Qwen/Qwen-Image-2.1-Turbo`,原生 8 步采样、CFG=1,支持文生图、最多 10 张参考图编辑和 RGBA 输出。Qwen-Image-2512、Qwen-Image-2.1 标准版及专用缓存已移除;不要提交 `model=quality` 或重新安装退役模型。 网页入口:`{BASE}/#local-images`。图片和任务记录保存在 Windows `LLM3DGraphics/data/_local_images/<批次ID>/`,推理在 DGXA 的 `/home/spark/llm3d-local-images/` 中运行。调用方只访问 **Windows LLM3DGraphics API**,本地路径不需要云端 Key。 #### 4.4.1 页面操作与提示词方法 1. 打开「本地图像生成」,确认 Turbo 已安装并完成首次加载。 2. 选择素材用途,填写画面描述。纯文字生成不上传参考图;参考编辑上传实际图片文件,按输入顺序写明“图1提供造型、图2提供配色、图3提供风格”。 3. 先用 512–1024 分辨率、4–8 张草稿选方向;固定 seed 便于迭代,每张图的 seed 自动递增。 4. 点击“作为参考继续编辑”将选中的生成图作为新参考,提高分辨率并描述局部修改;“复用参数”可恢复原批次的提示词、参考图、用途、尺寸和种子。 5. 需要 3D 时选择一张轮廓清晰的完整主体图,点击“送到 DGX 3D”,再按 §6 选择 `character_type` 并提交 3D。这个按钮负责填入图片,不会直接开始 3D 生成。 6. 需要 2D 素材时勾选图片并导出 Unity ZIP,见 §4.4.4。 提示词按“参考图职责 → 主体 → 画风/摄影 → 姿势与构图 → 背景 → 保持项与修改项”组织。负面限制也写进主提示词,`negative_prompt` 留空。 | 需求 | 提示词重点与用途 | |------|----------------| | 换角色、保留参考画风 | “保留图1的画风与构图,将角色改为……;重新绘制脸、头发、衣服和年龄特征。”明确哪些保持、哪些改变 | | 人形 3D 参考图 | “正面全身 T-pose,双臂水平伸直,头、手指、鞋完整入画,四周留白,纯白背景,无道具和遮挡。”严格正面姿势可用 `sprite` 并明确完整角色;`concept` 会追加三分之四视角要求 | | 真人街角照片 | “真实街头摄影,自然儿童/成人比例,日常便服,自然站姿,街角建筑和路面,真实肤色与布料纹理。”选择 `background`,避免追加白底设定图要求 | | 透明图标/精灵 | 选择 `sprite`、`transparent=true`,说明独立主体、透明背景、轮廓清楚;下载后检查实际 Alpha | 后端会追加所选用途的提示词,因此用途必须与目标一致。白底不是透明背景;页面显示的“含透明像素”才来自实际 Alpha 检查。街角场景照片适合作为图片素材或视觉参考;要生成独立 3D 角色时另生成简单背景、完整主体的参考图。 #### 4.4.2 图片任务接口与参数 ```http POST {BASE}/api/local-images/jobs Content-Type: multipart/form-data model=turbo profile=sprite prompt=保留图1的二维手绘风格,改为10岁运动装小男孩,正面全身T-pose,双手和鞋完整入画,纯白背景,无文字 width=1024 height=1504 count=4 seed=314159 transparent=false negative_prompt= images=@style_reference.png ``` | 字段 | 默认 / 范围 | 说明 | |------|-------------|------| | `model` | `turbo`,唯一支持值 | 对应 `Qwen/Qwen-Image-2.1-Turbo` BF16;步数和采样由 worker 固定,不传 `quality`、CFG 或步数 | | `prompt` | 必填,1–4000 字 | 画面与编辑要求;限制条件也写在这里 | | `profile` | 默认 `concept` | `concept`:完整 3D 主体、三分之四视角、纯色背景;`sprite`:独立精灵/UI、居中留白;`albedo`:平铺 base color;`background`:场景背景 | | `width` / `height` | 默认 1024;256–2048 | 都必须为 32 的倍数;人物竖图可用 1024×1504 | | `count` | 默认 4;1–16 | 一个批次逐张生成;最多同时排队 4 批 | | `seed` | 默认 -1;-1 或 0–4294967280 | -1 随机起点;第 i 张使用起点+i,i 从 0 开始 | | `transparent` | 默认 `false` | `true` 追加透明 RGBA 要求,仍需检查实际 Alpha | | `negative_prompt` | 空字符串 | CFG=1 不使用负面提示词;非空返回 422 | | `images` | 可省略,最多 10 个同名文件字段 | 传 PNG/JPEG/WebP 实际文件,不传 URL/base64 字符串;每张 ≤12MB、≤1600万像素,归一化最长边1536并保留 Alpha | 纯文生图省略 `images` 即可;无文件时也支持表单编码,**不是 JSON 请求体**。提交返回 **202** 和图片批次 `id`,轮询 `GET /api/local-images/jobs/{id}` 至 `completed` / `failed`。该 `id` 与之后创建的 3D job ID 不同。 结果从 `images` 数组取,不从 3D 的 `downloads` 取:每项含 `index`、实际 `seed`、`has_alpha`、`url`。PNG 路径为 `/api/local-images/jobs/{id}/images/{index}`;参考图路径为 `/api/local-images/jobs/{id}/references/{index}`。`failed` 批次也可能已有可下载图片,应保存结果和错误记录。 #### 4.4.3 curl 与 Python:生成、下载、转入 3D ```bash BASE=http://192.168.130.16:8008 curl -s "$BASE/api/local-images/status" # 多参考图编辑;纯文生图时省略两个 images 字段 curl -s -X POST "$BASE/api/local-images/jobs" \ -F model=turbo -F profile=sprite \ -F 'prompt=图1提供角色造型,图2提供配色。生成完整角色的正面全身T-pose,纯白背景,无文字。' \ -F width=1024 -F height=1504 -F count=4 -F seed=314159 \ -F transparent=false \ -F 'images=@shape.png' -F 'images=@palette.png' # IMG_JOB 替换为图片批次 id;轮询间隔至少2秒 curl -s "$BASE/api/local-images/jobs/$IMG_JOB" curl -fL -o selected.png "$BASE/api/local-images/jobs/$IMG_JOB/images/0" # 所有图像批次结束后准备DGX 3D;返回409时等待,不绕过保护 curl -s -X POST "$BASE/api/local-images/prepare-3d" curl -s -X POST "$BASE/api/jobs" \ -F engine=dgx -F mode=image -F character_type=humanoid \ -F target_faces=5000 -F texture_size=1024 -F output_format=glb \ -F 'image=@selected.png' ``` Windows PowerShell 使用 `curl.exe` 或下述 Python 示例,避免将 PowerShell 的 `curl` 别名当作上述 Bash 命令。 ```python import json import mimetypes import time from contextlib import ExitStack from pathlib import Path import requests BASE = "http://192.168.130.16:8008" def local_images(prompt, out_dir, references=(), *, profile="sprite", width=1024, height=1504, count=4, seed=314159, transparent=False, timeout=1800): with requests.Session() as session: response = session.get(f"{BASE}/api/local-images/status", timeout=20) response.raise_for_status() state = response.json() if not (state.get("connected") and state.get("ready") and state.get("models", {}).get("turbo", {}).get("installed")): raise RuntimeError(state) load_deadline = time.monotonic() + timeout while state.get("busy") and not state.get("loaded_model"): if time.monotonic() > load_deadline: raise TimeoutError("Turbo 尚未完成首次加载") time.sleep(2) response = session.get(f"{BASE}/api/local-images/status", timeout=20) response.raise_for_status() state = response.json() data = dict(model="turbo", prompt=prompt, profile=profile, width=width, height=height, count=count, seed=seed, transparent=str(transparent).lower(), negative_prompt="") with ExitStack() as stack: files = [] for item in references: path = Path(item) mime = mimetypes.guess_type(path.name)[0] or "application/octet-stream" files.append(("images", (path.name, stack.enter_context(path.open("rb")), mime))) response = session.post(f"{BASE}/api/local-images/jobs", data=data, files=files or None, timeout=30) response.raise_for_status() job = response.json() folder = Path(out_dir) / job["id"] folder.mkdir(parents=True, exist_ok=True) record = folder / "job.json" deadline = time.monotonic() + timeout while True: record.write_text(json.dumps(job, ensure_ascii=False, indent=2), encoding="utf-8") if job["status"] in ("completed", "failed"): break if time.monotonic() > deadline: raise TimeoutError(f"图片任务仍在运行,记录见 {record}") time.sleep(2) response = session.get(f"{BASE}/api/local-images/jobs/{job['id']}", timeout=30) response.raise_for_status() job = response.json() paths = [] for image in job["images"]: response = session.get(BASE + image["url"], timeout=120) response.raise_for_status() path = folder / f"image_{image['index']}_seed_{image['seed']}.png" path.write_bytes(response.content) paths.append(path) if job["status"] != "completed": raise RuntimeError(f"{job.get('error')};已保存 {len(paths)} 张图片,记录见 {record}") return job, paths # 多参考编辑:references=["style.png", "colors.png"];文生图不传 references。 # image_job, paths = local_images( # "蓝色运动装小男孩,二维手绘,正面全身T-pose,纯白背景,无文字", # "generated", references=["style.png"], count=4) # 人工或上游选定 paths 中一张,再交给 §9.2 create_rigged。 # 转入DGX前:requests.post(f"{BASE}/api/local-images/prepare-3d", timeout=360).raise_for_status() # rig_job = create_rigged(paths[0], "humanoid") ``` 图片下载后逐张检查主体、年龄/物种、姿势、手脚完整度、背景和伪影。批量固定种子是便于复现,不保证每张满足全部要求。需要精确 `#FFFFFF` 背景时另做背景清理,并保留原始输出。真人测试使用 `profile=background`,例如“约10岁小女孩,日常便服,自然站姿,真实街角建筑与人行道,午后自然光,真实肤色和服装纹理”。 #### 4.4.4 Unity 导出与 3D 转接 下载选中图片和参数:`GET /api/local-images/jobs/{id}/unity?selected=0,2`。`selected` 是以逗号分隔的图片索引;省略时导出该批次全部已生成图片。 ZIP 内含 `Assets/LLM3DGenerated/<批次ID>/` 下的 PNG、参考图和 `manifest.json`,以及 `Assets/Editor/LLM3DImageImporter.cs`。将 Assets 合并到 Unity 工程,保留目录结构: | 用途 | Unity 导入设置 | |------|----------------| | `sprite` | Sprite / Single,sRGB,Clamp,不生成 Mipmap,PPU=100,保留 Alpha | | `albedo` | Default,sRGB,Repeat,生成 Mipmap;检查平铺接缝 | | `concept` / `background` | Default,sRGB,Clamp,不生成 Mipmap,Max Size 2048 | 若导入顺序导致设置尚未应用,选中生成图片后执行 `Assets > LLM3D > Apply image import settings`。这是 ZIP 素材包,不是 `.unitypackage`;生成色彩图不是完整 PBR,也不是自动切片的 Sprite Sheet。3D 贴图继续走现有 UV、烘焙和 PBR 流程。 转入 3D 必须下载 PNG 后作为 `/api/jobs` 的 `image` 文件上传;图片批次 id 不能替代 3D job id。`prepare-3d` 只检查/恢复资源,不创建 3D 任务;§6 的建模、绑骨和动作步骤照常执行。 #### 4.4.5 共同驻留、内存兜底与实测耗时 当前配置为 `auto_switch=true`、`co_resident=true`。用户级服务 `llm3d-qwen.service` 启动时在后台预加载 Turbo;已有工作忙碌时跳过预加载。Turbo GPU 常驻约 32GiB。**共同驻留不等于并行生成**:混元、ComfyUI 和本机图像队列均通过预约与队列保护串行使用;图像批次结束后释放预约。 | 场景 | 当前策略 | |------|----------| | 图像生成,Turbo 已加载 | 可用内存至少 24GiB 时保留混元;不足时释放 ComfyUI 缓存并暂停本流程拥有的混元服务 | | 首次加载 Turbo | 共享模式要求可用内存至少为 Turbo 权重大小 + 12GiB | | 准备 DGX 3D,混元已运行 | 可用内存至少 32GiB 时保留 Turbo;不足则先释放 Turbo | | 需要启动被暂停的混元 | 至少 72GiB 可用内存才同时保留 Turbo,否则先释放 Turbo | | 3D/动作加载期间出现内存压力 | 每秒检查;可用内存低于16GiB且 Turbo 空闲、无图像预约时自动释放 Turbo | 调用方通过 `POST /api/local-images/prepare-3d` 让调度器决定,响应 `images_retained=true` 表示本次准备保留了 Turbo。需要手动释放时,在图像队列空闲后调用 `POST /api/local-images/unload`。不要停止其他用户任务、手动操作 Docker 或绕过 8090 的资源保护。 2026-10-13 的实测(不是固定 SLA): | 测试 | 耗时口径 | |------|----------| | 512×512,多参考透明图标 | 8步去噪约6–7秒/张,不含加载和编码/解码 | | 1024×1504,全身角色参考图编辑 | 8步去噪约21秒/张 | | 1024×1504,真人街角文生图 | 8步去噪约18秒;该次含换模加载总耗时约226秒 | | Turbo 已加载、与混元共同驻留,连续两张512×512文生图 | 提交至保存合计约8.2秒,无参考图 | | 同时保留 Turbo,混元造型+贴图 | 128抽取分辨率、静态分支约158秒,40,000面带贴图GLB;采样最低可用内存约31.9GiB | 首次加载可能需要数分钟;参考图数量、提示词长度、分辨率和场景都会改变耗时。上述3D测试没有调用 TokenRig/UniMate,不能据此保证所有动作模型或最高分辨率都能同时驻留,它们仍沿用按需加载。 详细部署、服务维护和验证说明见 [本地图像生成与 Unity 素材流程](./LLM3DGraphics/docs/local-image-generation.md);本次证据见 [常驻验证说明](./LLM3DGraphics/outputs/turbo_residency_20261013/常驻验证说明.txt)。服务机维护脚本在 `LLM3DGraphics/deploy/local_images/`,运行记录为 DGXA 的 `worker.log`;维护需等待相关队列空闲。Turbo 使用研究/评估许可,许可链接及商业使用约束见上述模型说明。 --- ## 5. 意图 → 参数映射(给 Agent 的决策表) 从用户描述里抽出:`asset_kind`、`character_type`、`reference_image`、`motions[]`。 ### 5.1 资产类型 | 判断 | `character_type` | 后续 | |------|------------------|------| | 道具 / 建筑 / 静物 / 「不要骨骼」 | `static` | 只 Step1,下载 `glb` / `glb_ex` | | 标准双足人、士兵、NPC 人型 | `humanoid` | Step1 + Step2(Kimodo 文生) | | 四足、怪、非标拓扑生物 | `non_humanoid` | Step1 + Step2(UniMate 文生) | 不确定时:有四肢且明显非人 → `non_humanoid`;明确是人 → `humanoid`;无生命 / 场景件 → `static`。 ### 5.2 参考图从哪来 | 情况 | 做法 | |------|------| | 用户已给图 | 直接作 `POST /api/jobs` 的 `image` | | 用户只有文字外貌 | **§4.4 本地 Turbo** → 选图 → 再选 `dgx` / `hunyuan`;选择云端时用 §4.1 | | 用户给多张风格/造型参考 | §4.4 按顺序上传 `images`,描述各图职责 → 选图 → 上传到 3D | | 只要图片、图标或场景背景 | §4.4 → PNG / Unity ZIP,流程在图片阶段结束 | | 既要外貌又要动作 | §4.4 文生图 → **必须 `engine=dgx`** → Step1 → Step2 | | 明确要混元官网质量、不要骨骼 | §4(如需)→ **`engine=hunyuan`**(§3) | ### 5.3 动作列表 - 用户说「idle、跑、跳」→ 拆成 `motions = ["idle 待机", "四足原地跑步", "原地跳跃"]`(示例),**每个一条** `motion_prompt`。 - 人形:交给 Kimodo,写清动作(走、跑、挥手等)。 - 非人形:写清主体与动作,可加「原地」「可循环」;中文即可(服务端会规范成英文)。 --- ## 6. 标准编排剧本(主要为 engine=dgx) ### 剧本 A — 角色模型 + 若干动作(最常见) ``` 输入: reference_image, character_type, motions[1..N], (可选) target_faces 输出: rigged 角色 + 每个动作对应的 animated 文件(注意覆盖问题) ``` ```mermaid flowchart LR A[参考图] --> B["POST /api/jobs
engine=dgx"] B --> C{轮询 completed
ready_for_motion} C --> D["POST .../animate
motion_prompt_1"] D --> E[下载 animated.*] E --> F["animate motion_2 ..."] ``` **Step 1** ```http POST /api/jobs Content-Type: multipart/form-data engine=dgx mode=image character_type=non_humanoid # 或 humanoid output_format=glb # Unity 需要网格 FBX 包时用 fbx target_faces=5000 # 游戏向低模;只要高模可调极大面数跳过减面 texture_size=1024 image=@character.png ``` 响应 `202` → 记下 `id`。轮询: ```http GET /api/jobs/{id} ``` 直到 `status == "completed"` 且 `ready_for_motion == true`。 推荐下载(角色): | 用途 | `downloads` 键 / 路径 | |------|------------------------| | 非人形 Unity 角色 | `rigged_fbx`(ZIP,内含 `model.fbx`)或 `rigged_glb` | | 人形 | `mixamo_glb` / 对应 mixamo FBX | | 网页预览 | `preview_url` | **Step 2(每个动作一次)** ```http POST /api/jobs/{id}/animate Content-Type: multipart/form-data motion_prompt=四足动物原地跑步,可循环 ``` 再轮询至 `completed`,下载: | 用途 | 键 | |------|-----| | Unity 动画(骨架+动画,无角色网格) | `animated_fbx` / `animated_glb` | | 带皮预览(可选) | `anim_preview_url` | **多动作注意**:服务端同一 job 目录里 `animated.glb` / `animated.fbx` 会被**覆盖**。Agent 应: 1. 每个动作完成后立刻下载并重命名保存(如 `{job_id}_run.fbx`);或 2. 告知用户「当前接口一次只保留最新动画」,由上游自行归档。 ### 剧本 B — 静态物体(道具) ```http POST /api/jobs engine=dgx mode=image character_type=static target_faces=20000 image=@prop.png ``` 完成标志:`status=completed`,`ready_for_motion=false`,有 `downloads.glb`(及可选 `glb_ex`)。 **禁止**调用 `/animate`。 ### 剧本 C — 「一句话」端到端(文生图 + 角色 + 动作) 伪代码(给 Agent / 编排层): ```text parse(user_text) -> { appearance_prompt, # 外貌,给文生图 character_type, # static | humanoid | non_humanoid motions[] # 可为空 } image_job, image_paths = local_images(appearance_prompt, "generated") # §4.4函数已轮询并下载 image = select_image(image_paths) # 从保存的PNG中选一张;底层下载地址为images[].url wait_until_all_image_batches_idle() POST /api/local-images/prepare-3d # 内存允许时保留Turbo,否则自动切换 # 用户选择云端时,上述生图步骤改用 §4.1 POST /api/image/generate job = POST /api/jobs (engine=dgx, image=image, character_type=...) wait_until(job, completed) if character_type == static: return download(job, ["glb", "glb_ex"]) assert job.ready_for_motion assets = { "rig": download(job, ["rigged_fbx", "rigged_glb"]) } for m in motions: POST /api/jobs/{id}/animate (motion_prompt=m) wait_until(job, completed) assets[m] = download(job, ["animated_fbx", "animated_glb"]) # 立刻另存 return assets ``` ### 剧本 D — 只要模型、稍后再加动作 1. 只跑 Step1(`engine=dgx`),把 `job_id` 存进你们的数据库。 2. 之后任意时刻对**同一** `job_id` 调 `/animate`(任务须已 `completed` 且仍有绑骨文件)。 ### 剧本 E — 混元官网只要网格 ``` 确认 hunyuan_logged_in →(可选)§4 文生图 → POST /api/jobs engine=hunyuan → 下载 glb / glb_ex → 禁止 animate ``` 细节见 §3。 --- ## 7. 轮询与状态机 | `status` | 含义 | Agent 行为 | |----------|------|------------| | `queued` / `running` | 进行中 | 读 `stage`、`progress` 展示;继续轮询 | | `completed` | 成功 | 读 `downloads` / `ready_for_motion` | | `failed` | 失败 | 拉 `GET /api/jobs/{id}/log`;部分文件可能仍可下 | 建议:`sleep(2~5s)`;Step1(含 HY3D+TokenRig)常 5–20+ 分钟;UniMate 动画常 10–40+ 分钟。设超时并向上游报 `stage`。 本地图像批次也使用 `queued/running/completed/failed`,但轮询路径是 `/api/local-images/jobs/{id}`,输出读取 `images[].url`。即使 `failed` 也保存已完成图片和 `error`;超时只停止调用方等待,不会取消服务端任务,不要立即重复提交。转入 DGX 前,等待所有图像批次空闲;`prepare-3d` 返回409时继续等待。 --- ## 8. 下载键速查 相对路径:`GET /api/jobs/{id}/files/{format}`(或直接用响应里的 `downloads.*` URL)。 | format | 内容 | 典型消费者 | |--------|------|------------| | `glb` | 高模 | 预览 / 静态 | | `glb_ex` | 低模+烘培 | 游戏 / 静态低模 | | `rigged_glb` | 绑骨+蒙皮 | 非人形预览 / 再动画 | | `rigged_fbx` | ZIP:`model.fbx`+贴图 | Unity 角色 | | `mixamo_glb` | 人形可驱动 | 人形再动画 | | `animated_glb` / `animated_fbx` | 骨架+动画(精简) | Unity Animator | | (`anim_preview_url`) | 蒙皮动画预览 | 仅预览,勿当生产动画源 | Unity 非人形:**角色**用 `rigged_fbx` 内 `model.fbx`,**动画**用精简 `animated.fbx`,骨架路径对齐 `Armature/bone_*`。细节见 `DGX3D生成说明.md` §5。 --- ## 9. 调用示例 ### 9.1 curl(DGX,已有参考图) ```bash BASE=http://192.168.130.16:8008 # Step 1 curl -s -X POST "$BASE/api/jobs" \ -F engine=dgx -F mode=image \ -F character_type=non_humanoid \ -F target_faces=5000 -F texture_size=1024 \ -F image=@./wolf.png # 轮询(把 JOB 换成返回的 id) curl -s "$BASE/api/jobs/$JOB" # Step 2 curl -s -X POST "$BASE/api/jobs/$JOB/animate" \ -F "motion_prompt=四足动物原地跑步,可循环" # 下载 curl -L -o wolf_rig.zip "$BASE/api/jobs/$JOB/files/rigged_fbx" curl -L -o wolf_run.fbx "$BASE/api/jobs/$JOB/files/animated_fbx" ``` ### 9.2 Python(文生图 + DGX 最小编排) ```python import time, requests from pathlib import Path # 本地生图函数 local_images 见 §4.4.3;云端函数 text_to_image 见 §4.2。 BASE = "http://192.168.130.16:8008" def wait_job(job_id, timeout=7200): t0 = time.time() while time.time() - t0 < timeout: j = requests.get(f"{BASE}/api/jobs/{job_id}").json() if j["status"] in ("completed", "failed"): return j time.sleep(3) raise TimeoutError(job_id) def create_rigged(image_path, character_type="non_humanoid"): with open(image_path, "rb") as f: r = requests.post( f"{BASE}/api/jobs", files={"image": f}, data={ "engine": "dgx", "mode": "image", "character_type": character_type, "target_faces": "5000", "texture_size": "1024", "output_format": "glb", }, ) r.raise_for_status() job_id = r.json()["id"] job = wait_job(job_id) if job["status"] != "completed" or not job.get("ready_for_motion"): raise RuntimeError(job.get("error") or job) return job def animate_and_save(job_id, motion_prompt, out_fbx): r = requests.post( f"{BASE}/api/jobs/{job_id}/animate", data={"motion_prompt": motion_prompt}, ) r.raise_for_status() job = wait_job(job_id) if job["status"] != "completed": raise RuntimeError(job.get("error") or job) url = job["downloads"]["animated_fbx"] if url.startswith("/"): url = BASE + url out_fbx.write_bytes(requests.get(url).content) return job # 示例:文字外貌 → 图 → 角色 → 两个动作 # image_job, images = local_images("棕色四足狼,完整主体,白底设定图", "generated", profile="concept", count=4) # 选定 images 中一张;全部图像批次空闲后: # requests.post(f"{BASE}/api/local-images/prepare-3d", timeout=360).raise_for_status() # job = create_rigged(images[0], "non_humanoid") # animate_and_save(job["id"], "四足待机,轻微呼吸", Path("idle.fbx")) # animate_and_save(job["id"], "四足动物原地跑步,可循环", Path("run.fbx")) ``` --- ## 10. 给 AI Agent 的系统提示词片段(可复制) 可把下面段落贴进你们项目的 Agent system prompt / skill: ```text 你通过 HTTP 调用本机 LLM3DGraphics(BASE 见调用指南顶部)生成 3D。 规则: - 先选 engine:要绑骨/动作 → dgx;用户要混元官网网页 → hunyuan。 - 图片生成优先使用本地Turbo:GET {BASE}/api/local-images/status,确认connected/ready/models.turbo.installed。 POST {BASE}/api/local-images/jobs,model=turbo,表单请求;多参考图用按顺序重复的images文件字段(最多10张)。 宽高256–2048且为32倍数;count=1–16;negative_prompt留空,把限制写进prompt;不使用已退役的quality/2512模型。 轮询图片批次至completed,读取images[].url并保存PNG;只要2D素材时可导出unity ZIP。 按目标选择profile:3D主体concept,严格正面角色可用sprite并说明完整角色,真实街景background,平铺颜色albedo。 无参考图时省略images;用户选择云端生图时改用POST {BASE}/api/image/generate,再GET返回url。 所有生图请求通过本机服务;禁止直连上游、传播API Key或直接调用DGXA的8090 worker绕过调度。 - hunyuan:先确认当前客户端的 hunyuan_logged_in;否则让用户在工作台当前客户端点击「微信登录」并扫描页面二维码。直接 HTTP 接入须保留同一 HttpOnly Cookie,并把二维码呈现给真人。 提交 engine=hunyuan, mode=image;只要 model/model_ex;禁止 /animate。 - dgx 角色:POST /api/jobs → ready_for_motion → POST .../animate;多动作多次 animate 并立刻下载。 - dgx 静物:character_type=static,禁止 animate。 - character_type 仅对 dgx 有效:人→humanoid;四足/怪→non_humanoid;静物→static。 - 先 GET /api/capabilities,缺能力则说明,不要硬调。 - 图片与DGX 3D/对比/动作串行:等待全部图片批次结束,POST /api/local-images/prepare-3d后再提交DGX。 共同驻留由服务端决定,内存不足会释放Turbo;不要假设所有模型或所有分辨率都能一起驻留。 - 不要假设文生 3D;不要走已废弃 TopoCap/GIF 路径。 - Unity(dgx):角色 rigged_fbx,动画 animated_fbx。 详细:DGX3D_AI调用指南.md;DGX 深度规范:DGX3D生成说明.md。 ``` --- ## 11. 错误与排错(调用侧) | 现象 | 处理 | |------|------| | 422「静态物体…不生成动作」 | 用错类型;静态只下网格 | | 422「请先完成角色绑骨」 | Step1 未成功或类型不对 | | 503 UniMate / Kimodo | 能力未就绪;人形要 Kimodo,其他类型要 UniMate | | 409 任务仍在处理 | 等 `completed` 再 animate | | 429 队列已满 | 退避重试 | | `failed` | `GET /api/jobs/{id}/log`;规范文档 Checklist 日志顺序 | | 503 生图转接未配置 | 运维在本机 `config.json` 配 `dgx.grsai.api_key` | | 502 生图转接失败 | 改写 prompt;运维查上游 / Key;外部仍勿直连 grsai | | 422 本地图像模型/尺寸/数量/参考图无效 | 仅 `model=turbo`;宽高为32倍数;count≤16;images≤10;negative_prompt为空 | | 409 本地图像与 DGX 任务冲突 / 正在恢复3D | 等待已有任务结束或恢复完成,再提交;勿停掉其他任务 | | 429 本地图像队列已满 | 图像队列最多4批,退避后重试,不反复创建同一批次 | | 本地图像状态未连接或模型未安装 | 检查 `/api/local-images/status`;服务机运维检查 `llm3d-qwen.service` 与 `worker.log` | | 本地图像批次 `failed`,仍有 `images` | 保存已生成PNG与job.json;修正错误后按需要提交新批次 | | 本地首次加载较慢 / 内存不足 | 查看busy/loaded_model/available_gb,等待预加载或让调度器执行内存兜底;勿自行改BF16精度 | | 401 请先微信登录混元3D | 在发起请求的客户端工作台点击「微信登录」并扫码;确认 status 与任务请求使用同一客户端 Cookie;或改用 `dgx` | | 422 混元仅支持图片 | `engine=hunyuan` 时 `mode` 必须为 `image` | | 422 仅 DGX 支持动作 | 对 hunyuan 任务误调了 `/animate` | 跨域:浏览器页面直连时服务有 Origin 限制;**服务端 / Agent / curl** 调 API 不受网页同源限制影响。 --- ## 12. 与规范文档的分工 | 文档 | 读者 | 内容 | |------|------|------| | **本文** `DGX3D_AI调用指南.md` | 其他项目、编排 AI | 本地Turbo生图、多参考编辑、批量与Unity导出、引擎选择、云端转接、DGX剧本 | | `LLM3DGraphics/docs/local-image-generation.md` | 图像流程实现 / 运维 | Turbo部署、共同驻留、资源保护、Unity导入与实测 | | `DGX3D生成说明.md` | 本仓库实现 / 深度排错 | DGX 阶段、产物、UniMate、Unity、禁忌 | | `生成路径.md` | **仅运维** | 上游模型与 Key(勿发给外部 Agent) | | `HelpDoc/gpt-image_doc.md` | **仅运维** | 上游画图字段细节 | 服务变更时:外部调用顺序以本文为准;产物与内部阶段以 `DGX3D生成说明.md` 为准;上游 Key 只放本机 `config.json`。