160 lines
8.3 KiB
Markdown
160 lines
8.3 KiB
Markdown
---
|
||
name: multi-shot
|
||
summary-en: Re-render image from new camera angle
|
||
summary-cn: 拖动 3D 球体,给一张图换新视角
|
||
display-name-zh: 多角度
|
||
trigger-words: [多角度, 换视角, 换镜头, multi shot, change angle, new camera angle]
|
||
description: |
|
||
把已有的一张图片用全新的相机视角重新生成。用户拖动 3D 球体控件设定旋转/倾斜/缩放后,nano banana 模型按该视角重渲染原图。GUI 仅负责参数收集,结果由主站对话流呈现。
|
||
allowed-tools: hub_open_remote_tool_gui, hub_submit_dag
|
||
tags: [Image, 3D, Multi-View]
|
||
tags-cn: [图片, 3D, 多视角]
|
||
version: 1.0.0
|
||
exported-by: MiniMax-hub
|
||
---
|
||
|
||
# Multi Shot
|
||
|
||
拖动 3D 球体调整摄像机角度,给图片换一个新视角
|
||
|
||
## 参数
|
||
|
||
| ID | 类型 | 必填 | 取值约束 | 说明 |
|
||
|----|------|------|---------|------|
|
||
| image_url | file(image/*) | 是 | — | 原图引用,由主站从对话流注入;GUI 不上传也不做格式校验。可以是 CDN URL(http/https)、blob:、data: 或本地路径,统一原样透传给后端。 |
|
||
| view_prompt | string | 是 | — | 视角描述 prompt(英文自然语言)。由 GUI 在用户点击「立即使用」时根据 rotation/tilt/zoom 三参数自动拼装,外部 inject 会被覆盖。 |
|
||
|
||
## 全局禁令
|
||
|
||
- ❌ **禁止**调用 `save_file_to_session` 工具 — 本工具产生的所有资产 gateway 已自动保存到 workspace 并通过 `recordAsset` 注册到画布,任何手动保存(`save_file_to_session` / `curl` / `wget` / `download_videos` 等)都会让同一资产入两次库。完成后只需用 markdown 媒体语法引用 `local_path` / 脚本 `outputs` 返回的路径即可。
|
||
|
||
## 执行流程
|
||
|
||
### STEP 1: 打开 GUI 收集参数
|
||
|
||
调用 `hub_open_remote_tool_gui`,入参:
|
||
|
||
```ts
|
||
{
|
||
tool_name: "multi-shot",
|
||
entry: "scripts/index.js",
|
||
initial_params?: { /* 可选,用户在对话里已经给出的具体参数,key 对齐"参数"表里的 id */ },
|
||
}
|
||
```
|
||
|
||
**预填规则**:如果用户在对话中已经明确给出某个参数的具体值(例如"用 kissing 这个 pose"、"prompt 写'夕阳下奔跑'"),把它们打包进 `initial_params` 一并传入,GUI 挂载后会自动预填,用户无需再次输入。没明确给值的参数不要瞎猜,留给 GUI 表单收集即可。
|
||
|
||
**附件预填(本工具的 file 参数:`image_url`)**:
|
||
|
||
用户消息可能以系统自动注入的 `[User attached files: <path1>, <path2>, ...]` 开头 —— 这是文件路径,不是用户手打的文字。出现这种情况时**必须**按下表把附件路径塞进 `initial_params`,即使用户文字里没说"用这张图":
|
||
|
||
| 参数 id | accept | 取值规则 |
|
||
|---------|--------|----------|
|
||
| `image_url` | `image/*` | 附件列表中第一个匹配该 MIME 的路径 |
|
||
|
||
补充约束:
|
||
|
||
- 把匹配到的本地路径**直接**塞进 `initial_params[<id>]`(GUI 会识别本地路径并自动上传到 CDN,agent 不要自己调 `hub_upload_to_cdn` / `upload_to_cdn`)
|
||
- 没匹配到的 file 字段:不传(留给 GUI 表单让用户上传)
|
||
- 同一字段有多张匹配:只取第一个,其余交给 GUI
|
||
|
||
示例:
|
||
|
||
- 用户消息: `[User attached files: /Users/me/face.webp]\n\n/multi-shot`
|
||
- 应调用: `open_remote_tool_gui({ tool_name: "multi-shot", entry: "scripts/index.js", initial_params: { image_url: "/Users/me/face.webp" } })`
|
||
|
||
`initial_params` 仅用于 `open_remote_tool_gui` 调用时的预填。GUI 打开后由用户操作,不要尝试再用其他方式去操控 GUI。
|
||
|
||
### STEP 2: 接收用户提交
|
||
|
||
GUI 提交后,系统会注入一条 user message,形如:
|
||
|
||
```
|
||
User submitted GUI form for tool "multi-shot". Form data:
|
||
{ "params": { ... }, "files": [{ "param_id": "...", "url": "...", "name": "...", "type": "..." }] }
|
||
```
|
||
|
||
把 `files[].url` 按 `param_id` 匹配到对应字段(`params[<param_id>] = files[i].url`),组装出完整的 DAG inputs。**无需再次上传文件**(GUI 已通过 `sdk.uploadFile` 上传到 CDN)。
|
||
|
||
#### 收到 form data 后直接进入 STEP 3,严格禁令
|
||
|
||
- ❌ **禁止**重新调用 `hub_open_remote_tool_gui`(无论是否改 `initial_params`)—— 用户已经在 GUI 里确认过参数,你不是产品经理,不要替用户改主意。同一 turn 内重开 GUI 会让新 link 覆盖旧 pending、UI 永远停在 "Thinking..."、用户被迫重填,直接死循环
|
||
- ❌ **禁止**在对话流输出"本次方案 / 这版思路 / 我再帮你调整一版 / 我重新设计一下"之类的二次提案 —— 参数已经 lock,不要"反悔"
|
||
|
||
如果你判断 GUI 提交的参数确实不合理,只有两个合法选项:(a) STEP 3 照常 submit,让 DAG 后端报错,你再用错误信息向用户解释;(b) 完全终止本次任务并向用户说明原因。**绝对不是悄悄重开 GUI**。
|
||
|
||
### STEP 3: 调用 hub_submit_dag
|
||
|
||
用上一步组装好的 inputs 调用工具:
|
||
|
||
| 字段 | 值 |
|
||
|------|----|
|
||
| dag_id | "508694798475620358" |
|
||
| inputs | `{ "image_url": "<cdn_url>", "view_prompt": "<视角描述 prompt(英文自然语言)。由 GUI 在用户点击「立即使用」时根据 rotation/tilt/zoom 三参数自动拼装,外部 inject 会被覆盖。>" }` |
|
||
| asset_keys | `[]`(始终传空数组) |
|
||
|
||
**inputs 格式约束**:
|
||
- 所有 value **必须是字符串字面量**(包括数字、布尔)
|
||
- 上方"参数"表中所有参数都必须传递,可选参数无值时传空字符串 `""` 占位
|
||
- file 类型参数填 CDN URL(`https://...` 开头)
|
||
|
||
**出参**:`{ run_id: string, estimated_seconds: number }`
|
||
|
||
### STEP 4: 提交后立即结束当前 turn
|
||
|
||
`submit_dag` 返回后,**当前 turn 必须立即结束**。Gateway 在后台代理轮询(15s 间隔,30 分钟超时),完成时会自动注入新 user message 唤醒。
|
||
|
||
#### 严格禁令
|
||
|
||
- ❌ **禁止**重新调用 `hub_open_remote_tool_gui` —— submit_dag 已发出,任务在跑;再开 GUI = 同一任务排队两次 + 旧 link 被新 link 覆盖 + UI 永远卡在 "Thinking..." + 用户被迫重填,死循环。**这是同一 turn 内最常见、危害最大的违规**,任何"我再帮你调整一版"的冲动都意味着你已经踩进死循环
|
||
- ❌ **禁止**在对话流输出"本次方案 / 这版思路 / 我再设计一版"之类二次提案文本 —— 参数已交给 DAG,你不是产品经理,不要替用户改主意
|
||
- ❌ **禁止**用 `question` / `ask` 工具"通知用户等待"或"询问是否继续" — UI 已自带 loading 反馈,question 会卡住对话流,DAG 完成消息排队进不来
|
||
- ❌ **禁止**主动调用 `hub_query_dag_result` — gateway 会回调,主动查会浪费 turn 还可能 race condition
|
||
- ❌ **禁止**重复 `submit_dag` — 会在后端创建多个任务,消耗用户配额
|
||
- ❌ **禁止**输出"任务进行中,请稍候"之类的中间态文本 — 会让 UI 误以为在等用户回复
|
||
- ❌ **禁止** sleep / 自旋 / 任何形式的等待 — 事件驱动,等待靠系统注入
|
||
|
||
### STEP 5: 处理完成消息
|
||
|
||
注入的 user message 形如:
|
||
|
||
```
|
||
Async task completed:
|
||
{
|
||
"task_id": "<run_id>",
|
||
"status": "succeeded" | "failed" | "timeout",
|
||
"outputs": { ... },
|
||
"asset_outputs": [
|
||
{ "key": "image", "status": "finished", "url": "https://cdn...", "local_path": "..." }
|
||
],
|
||
"error_message": null
|
||
}
|
||
```
|
||
|
||
#### `status: "succeeded"`
|
||
|
||
用 markdown 媒体语法插入资产,从 `asset_outputs` 中遍历 `status == "finished"` 的条目:
|
||
|
||
- 图片:``
|
||
- 视频:`` 或链接 `[查看视频](<local_path>)`
|
||
- 其他文件:`[<描述>](<local_path>)`
|
||
|
||
**关键约束**:
|
||
- ✅ **必须用 `local_path`**(gateway 已自动下载到 workspace 并通过 `recordAsset` 注册到画布)
|
||
- ❌ **不要用 `url`**(CDN URL 仅作 fallback,直接用会让画布丢失资产引用)
|
||
- ❌ **禁止**调用 `save_file_to_session` / `curl` / `wget` / `download_videos` 等下载工具 — gateway 已下载,重复会让同一资产入两次库
|
||
- ⚠️ 纯文本(如「已生成」「已保存」)不会渲染媒体,**必须用 markdown 语法**
|
||
|
||
#### `status: "failed"`
|
||
|
||
向用户报告 `error_message`,建议调整输入后重试。**不要**自动 retry。
|
||
|
||
#### `status: "timeout"`
|
||
|
||
任务已被 gateway abort(30 分钟未完成),向用户报告超时并建议稍后重试。
|
||
|
||
## 排查参考
|
||
|
||
- `submit_dag` 调用本身报错(返回非 ok):检查 inputs 是否完整,所有 key 都必须传(可选项传 `""`)
|
||
- 完成消息一直没来:用户应能看到 UI 端 loading;若超过 30 分钟仍无完成消息,gateway 可能已 abort,等下一次用户主动操作
|