Files
AIGame/DGX3D_AI调用指南.md
T

906 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <DGXA实际可达地址>
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/<id>"
```
```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<br/>engine=dgx"]
B --> C{轮询 completed<br/>ready_for_motion}
C --> D["POST .../animate<br/>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`。