44 KiB
LLM3DGraphics · 跨项目 / AI 调用指南
给其他项目、其他开发者、自动化 Agent用:如何把自然语言需求编排成对 LLM3DGraphics HTTP API 的调用,产出图片、Unity 图片素材、网格、(可选)绑骨角色与骨骼动画。
- 服务默认:
http://192.168.130.16:8008 - OpenAPI:
http://192.168.130.16:8008/docs - DGX 绑骨/动画规范见 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 |
硬约束(调用前必读)
- 先选引擎(§引擎选择):要绑骨/动作 → 只能
dgx;只要官网质量网格 → 可用hunyuan。 - 无参考图时优先走
POST /api/local-images/jobs本地 Turbo(§4.4);用户选择云端生图时走POST /api/image/generate转接(§4.1),勿直连上游、勿带 Key。 dgx可驱动角色 = 两步:/api/jobs→/api/jobs/{id}/animate;多动作多次 animate(注意覆盖)。hunyuan/dgx+static:禁止/animate(会 422)。hunyuan必须当前浏览器客户端已登录混元官网(hunyuan_logged_in);首次登录在客户端页面扫码。- 3D 队列同时
queued+running≤ 5;本地图像最多排队 4 批、每批 1–16 张。轮询间隔 ≥ 2s;单任务可能数分钟~数十分钟。 - Turbo 可与混元共同驻留,图像与 DGX 3D/对比/动作任务仍串行执行。图像队列结束后才能转入 DGX 3D;不要绕过本机调度器直接调用 DGXA 的 8090 worker。
1. 前置:服务与能力探测
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 判断:
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 目录运行只读自检:
.\.venv\Scripts\python.exe .\deploy\check_dgx_connection.py --output .\outputs\dgx_connection_check.json
脚本: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 必须有对应连接设置;使用新机器自己的已授权密钥,例如:
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:服务端用 Playwright(Edge)提交图片生成;微信登录二维码显示在发起登录的客户端页面,由用户用自己的微信扫码。登录态按浏览器客户端隔离。
3.1 登录(客户端页面扫码,会话按客户端复用)
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 侧:
- 读
capabilities.hunyuan_logged_in(或/api/hunyuan/status) - 若为
false:让用户在要使用的浏览器客户端打开工作台并点击「微信登录」,在该页面显示的二维码上用自己的微信扫码;轮询该客户端的 status 至logged_in。不要让用户去服务机窗口扫码。 - 未登录时提交
engine=hunyuan会 401
直接用 HTTP 客户端接入时,必须在 status、login、login/qr 和生成请求之间保留同一个服务端下发的 HttpOnly Cookie;否则请求会被识别为不同客户端。需要把二维码安全地呈现给真人扫码,单纯的无状态 curl/Agent 调用不能代替用户完成登录。
3.2 生成任务
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 示例
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、HelpDoc/gpt-image_doc.md(仅服务器侧) |
探测:GET /api/capabilities → image_generate_ready == true。
4.1 转接接口
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):
{
"id": "a1b2c3...",
"status": "succeeded",
"url": "/api/image/a1b2c3...",
"content_type": "image/png",
"bytes": 123456,
"model": "gpt-image-2.5",
"aspect_ratio": "1:1"
}
下载图片:
GET {BASE}/api/image/{id}
将该文件作为后续 POST /api/jobs 的 image 上传即可。
给 DGX 的构图建议(提高绑骨成功率):
- 主体居中、轮廓清晰;尽量白底或简单底
- 全身可见;角色类避免极端透视 / 大面积遮挡
- 提示词写清物种、体态、材质;少杂乱场景文字
4.2 curl / Python 示例(外部只打本机)
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>"
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)中配置,例如:
"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 页面操作与提示词方法
- 打开「本地图像生成」,确认 Turbo 已安装并完成首次加载。
- 选择素材用途,填写画面描述。纯文字生成不上传参考图;参考编辑上传实际图片文件,按输入顺序写明“图1提供造型、图2提供配色、图3提供风格”。
- 先用 512–1024 分辨率、4–8 张草稿选方向;固定 seed 便于迭代,每张图的 seed 自动递增。
- 点击“作为参考继续编辑”将选中的生成图作为新参考,提高分辨率并描述局部修改;“复用参数”可恢复原批次的提示词、参考图、用途、尺寸和种子。
- 需要 3D 时选择一张轮廓清晰的完整主体图,点击“送到 DGX 3D”,再按 §6 选择
character_type并提交 3D。这个按钮负责填入图片,不会直接开始 3D 生成。 - 需要 2D 素材时勾选图片并导出 Unity ZIP,见 §4.4.4。
提示词按“参考图职责 → 主体 → 画风/摄影 → 姿势与构图 → 背景 → 保持项与修改项”组织。负面限制也写进主提示词,negative_prompt 留空。
| 需求 | 提示词重点与用途 |
|---|---|
| 换角色、保留参考画风 | “保留图1的画风与构图,将角色改为……;重新绘制脸、头发、衣服和年龄特征。”明确哪些保持、哪些改变 |
| 人形 3D 参考图 | “正面全身 T-pose,双臂水平伸直,头、手指、鞋完整入画,四周留白,纯白背景,无道具和遮挡。”严格正面姿势可用 sprite 并明确完整角色;concept 会追加三分之四视角要求 |
| 真人街角照片 | “真实街头摄影,自然儿童/成人比例,日常便服,自然站姿,街角建筑和路面,真实肤色与布料纹理。”选择 background,避免追加白底设定图要求 |
| 透明图标/精灵 | 选择 sprite、transparent=true,说明独立主体、透明背景、轮廓清楚;下载后检查实际 Alpha |
后端会追加所选用途的提示词,因此用途必须与目标一致。白底不是透明背景;页面显示的“含透明像素”才来自实际 Alpha 检查。街角场景照片适合作为图片素材或视觉参考;要生成独立 3D 角色时另生成简单背景、完整主体的参考图。
4.4.2 图片任务接口与参数
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
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 命令。
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/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 文件(注意覆盖问题)
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
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。轮询:
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(每个动作一次)
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 应:
- 每个动作完成后立刻下载并重命名保存(如
{job_id}_run.fbx);或 - 告知用户「当前接口一次只保留最新动画」,由上游自行归档。
剧本 B — 静态物体(道具)
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 / 编排层):
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 — 只要模型、稍后再加动作
- 只跑 Step1(
engine=dgx),把job_id存进你们的数据库。 - 之后任意时刻对同一
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,已有参考图)
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 最小编排)
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:
你通过 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。