mcpField
R
runway-image

runway-image

github.com/calesthio创意与媒体219102 次安装15 周前更新
代码仓库

关于 runway-image

runway-image 是通过 Runway 官方 API 制作静态图像的技能,面向创意团队和开发者,涵盖模型选择、参考图编辑、费用审批与输出检查。

主要功能

  1. 01文本生成图像
  2. 02参考图生成与编辑
  3. 03选择 Runway 原生 Gen-4 模型
  4. 04异步任务轮询与取消
  5. 05费用、权利与输出质检指导

适用场景

  1. 01根据文字需求制作图像
  2. 02使用产品或风格参考图迭代
  3. 03编辑图像并保留指定细节

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/calesthio/generative-media-skills --skill runway-image

安装后在 Claude Code 中输入/runway-image确认已加载。

附带文件EVAL.md

详细介绍

Runway 图像

使用官方 Runway API 创建静态图像。优先使用 Runway 原生的 gen4_image 和 gen4_image_turbo;合作伙伴的图像模型应明确标注。此技能不涵盖 Runway 视频端点、网页应用 UI、非官方封装或第三方网关。

首次响应

编写代码或开始会产生费用的任务前,先确认:

  • 所需输出:主体、环境、构图、风格、宽高比/分辨率、数量和验收标准。
  • 模式:纯文本生成、参考图引导生成,或以源图为参考进行自然语言编辑。
  • 参考图:路径/URL、权利和同意情况、每张图的用途,以及是否必须保持身份一致。
  • 交付:目标位置、命名、元数据/清单,以及最高预算。
  • 运行方式:直接调用 REST、使用 Python SDK 或 Node SDK。常规应用代码优先使用官方 SDK。

如果缺少必要的创作细节,请提出一个具体的默认方案,并且只询问会实质影响费用、权利或输出的问题。未经明确批准,绝不进行付费调用。

当前契约快照

已于 2026-07-09 根据第一方文档和官方 Python SDK 核实。生产部署前,请重新检查 API 参考、模型目录、定价、限制、变更日志、条款和使用政策;这些内容可能随时变动。

路由和身份验证
用途方法和路由结果
创建图像任务POST https://api.dev.runwayml.com/v1/text_to_image{ "id": "..." }
读取任务GET https://api.dev.runwayml.com/v1/tasks/{id}带判别字段的任务对象
取消或删除任务DELETE https://api.dev.runwayml.com/v1/tasks/{id}取消正在运行的任务,或删除已结束的任务
开始临时上传POST https://api.dev.runwayml.com/v1/uploads多部分上传说明及 runwayUri

在 API 调用中发送以下标头:

Authorization: Bearer $RUNWAYML_API_SECRET
Content-Type: application/json
X-Runway-Version: 2024-11-06

/v1 是 URL 命名空间,不是 API 版本。Runway 使用日期值标头控制行为版本,并表示旧版本在推出替代版本后可继续支持四个月。请固定并测试该标头;不要省略。

将组织范围的密钥保存在服务器端的密钥管理器中。Runway 只会显示一次密钥。移除组织成员不会撤销其密钥,因此请单独禁用/轮换密钥。绝不要将 bearer token 发送给参考图所在主机或输出 CDN URL。

Runway 原生模型选择
模型输入适用场景费用快照
gen4_image文本;可选 1–3 张参考图纯文本创作、最高质量的原生生成、编辑、风格/身份/构图控制每张 720p 图像 5 个点数;每张 1080p 图像 8 个点数
gen4_image_turbo文本和必需的 1–3 张参考图更快、费用更低的参考图驱动迭代支持的任意分辨率下,每张图像 2 个点数

每个点数为 $0.01,不含适用税费。经内容审核的生成与成功生成费用相同。不要仅凭尺寸推断特定比例适用的 720p/1080p 计费档位;请在当前定价页面或开发者门户中确认。

提供商发布的 Turbo 宣传说法(速度、相对质量或基准测试分数)属于营销声明,并非保证。请使用有代表性的提示词和参考图进行基准测试。

精确的原生请求架构

两种原生模型都接受:

  • model:必须恰为 gen4_image 或 gen4_image_turbo。
  • promptText:必填,不得为空,最多 1,000 个 UTF-16 代码单元。
  • ratio:必填,且必须是以下值之一:
1024:1024  1080:1080  1168:880   1360:768
1440:1080  1080:1440  1808:768   1920:1080
1080:1920  2112:912   1280:720   720:1280
720:720    960:720    720:960    1680:720
  • referenceImages:对象,必填字段为 uri,可选字段为 tag。gen4_image 接受 0–3 个;Turbo 要求 1–3 个。
  • seed:可选整数,范围为 0..4294967295。相同的请求和种子应生成相似结果,但不一定逐位相同。
  • contentModeration.publicFigureThreshold:可选值为 auto 或 low。low 对可识别的公众人物限制较宽松;它不是安全限制豁免、同意授权或权利许可。

当前 API 架构规定,参考标签必须为 3–16 个小写字符,并匹配 ^[a-z][a-z0-9_]+$;在 promptText 中使用 @product_ref 提及标签。截至核验日期,官方入门示例仍展示大写标签,与此规定不一致;请将标签规范化为这种保守格式。

目前没有文档说明单独的 Gen-4 静态图像蒙版/局部重绘/编辑路由。要编辑图像,请将源图作为参考提交,并描述修改后的结果,同时重申需要保持不变的内容。不要臆造蒙版、去噪强度、负面提示词、ControlNet、权重或图像强度字段。

Runway 提供的合作伙伴模型

公开模型目录目前在同一路由上列出了 gemini_image3_pro、gpt_image_2 和 gemini_2.5_flash。这些是合作伙伴模型,并非 Runway 原生 Gen-4 模型。它们的架构和提供方行为各不相同:

模型主要约定当前价格
gemini_image3_pro最多 5,500 个 UTF-16 代码单元;最多 14 张参考图(最多 5 个 human、9 个 object);outputCount 为 1 或 4;1K/2K/4K 比例1K/2K 为 20 个积分;4K 为 40 个积分
gpt_image_2最多 32,000 个字符;最多 16 张参考图;outputCount 为 1–10;quality 为 low/medium/high/auto;背景为不透明/自动,不支持透明背景每张输出 1–41 个积分,具体取决于质量和分辨率;auto 分辨率按 4K 计费
gemini_2.5_flash当前 API 架构最多支持 5,500 个 UTF-16 代码单元;最多 3 张参考图;文档列出十种比例每张图像 5 个积分

2026-07-09,官方 Python SDK 架构列出了 gemini_image3.1_flash,但公开模型目录和定价页面未列出。当前 API 架构和模型目录列出了 seedream5_pro,但定价页面和当前 Python SDK 未列出。在请求架构、模型目录、定价、SDK/账户访问权限达成一致之前,或者 Runway 支持团队确认缺失的约定之前,请将其中任一模型视为不可用于受成本控制的生产调用。切勿将一个模型的字段照搬到另一个模型。

输入媒体

参考 uri 支持:

  1. HTTPS URL。
  2. data:image/...;base64,... 数据 URI。
  3. 临时上传返回的 runway://... URI。

支持的静态图像格式为 JPEG(image/jpeg 或 image/jpg)、PNG 和 WebP;不支持 GIF。当前限制:

传输方式图像限制操作说明
HTTPS URL16 MBURL ≤2,048 个字符;主机必须是域名,不能是 IP;不允许重定向;必须支持 HEAD 并提供 Content-Length;媒体 Content-Type 必须准确;响应状态为 200
数据 URI编码后 5 MB考虑 Base64 扩展后,二进制数据应约为 3.3 MB 或更小
临时上传200 MB至少 512 字节;需要购买 API 额度;受速率限制;runway:// URI 有效期为 24 小时

Runway 的输入指南建议参考图像尺寸约为 640px 至 4K;它没有公布 Gen-4 参考图像的 8,000 像素尺寸规则。优先使用清晰、压缩程度低、主体明确、光线均匀柔和且没有水印或叠加文字的素材。Runway 可能会调整输入图像的尺寸。不要将独立超分辨率端点的 8,000 像素限制套用到此路由。

对于由客户托管的参考 URL,请实现专用的签名资源端点,使其无需重定向即可响应 HEAD 和 GET,返回准确的 Content-Type 和 Content-Length,并允许 User-Agent 前缀为 RunwayML API/ 的 Runway 请求。不要暴露私有网络资源。

提示词与参考设计

纯文本生成

用正向、视觉化的方式描述画面内容:

[medium/style] of [subject and defining details] in [scene].
[composition and angle]. [lighting]. [palette]. [focus/lens/material/mood].

示例:

Editorial product photograph of a matte cobalt travel mug on pale limestone.
Three-quarter view, centered with generous negative space on the right. Soft north-window
light, crisp rim highlight, restrained blue and warm gray palette, 85mm lens, shallow depth.

使用完整句子和具体的视觉语言。Gen-4 不支持单独的负面提示词字段;应将否定描述改写为正向描述(例如“秃头的人”,而不是“没有头发的人”)。避免使用“请生成”等对话式填充语。将要呈现的准确文字放在引号中,然后手动检查拼写。

参考图像与编辑

为每张参考图像指定一种用途和一个稳定的小写蛇形标签:

{
  "referenceImages": [
    {"uri": "https://assets.example.com/mug.jpg", "tag": "product_ref"},
    {"uri": "https://assets.example.com/light.jpg", "tag": "style_ref"}
  ],
  "promptText": "@product_ref unchanged in shape, logo placement, and cobalt color, photographed with the soft window lighting and pale stone setting of @style_ref. Three-quarter product view with clear negative space on the right."
}

未标记的参考图像可能会影响整体风格。带标签的参考图像能更清晰地表明用途。若要保持角色一致性,请使用光线均匀、面部特征无遮挡的简洁中性视图。草图或带注释的构图也可以作为另一张参考图像,但 API 会将其作为普通图像接收;没有单独的草图控制字段。

编辑时,请描述目标图像、要做的更改以及必须保持不变的内容:

@source_ref in the same camera position and crop. Replace the background with a warm,
minimal studio wall and add soft late-afternoon side light. Preserve the person's identity,
facial expression, hairstyle, clothing silhouette, and hand position.

每次只做一项复杂更改。将批准的结果作为下一步的参考图像。这是一种启发式工作流,无法保证锁定身份。

迭代与质量检查

使用分支台账记录提示词、模型、宽高比、种子、参考图像哈希值/URI、任务 ID、成本估算、输出哈希值和审核决定。每个分支只更改一个因素:

  1. 锁定构图和主体。
  2. 调整光线和配色。
  3. 调整风格/材质/镜头。
  4. 通过基于新参考图像的编辑,修正一项局部语义问题。
  5. 固定已批准源文件的哈希值,并按交付宽高比渲染。

以完整尺寸检查每个输出,确认其是否符合提示词要求、解剖结构/几何形状是否正确、身份/产品是否忠实、文字/徽标是否准确、边缘是否存在瑕疵、是否出现意外的人物/标记,以及是否存在隐私/权利方面的问题,并确认交付尺寸。相近的种子可用于辅助探索,但不能证明结果可复现。

异步生命周期

创建操作仅返回任务 ID。轮询 GET /v1/tasks/{id};不要指望每隔不到 5 秒就能获得有意义的更新。

状态操作
PENDING至少等待 5 秒,然后轮询
THROTTLED有效的排队任务;等待后轮询,不要重新提交
RUNNING记录 progress;等待后轮询
SUCCEEDED立即下载所有输出并持久化清单
FAILED检查 failureCode 和人类可读的 failure;应用失败策略
CANCELLED停止;不要视为成功

对临时性非 200 响应使用带抖动的轮询和指数退避。官方 SDK 等待助手默认超时为 10 分钟;超时或中止会停止本地轮询,但不会取消远程任务。仅当确实要取消任务时才调用 DELETE /v1/tasks/{id}。删除终态任务后,无法再通过 API 获取其任务数据和输出。

输出 URL 会在 24–48 小时内过期。重新获取保留的成功任务可以生成新的 URL,但这并非持久存储。请尽快下载并存储到自己控制的存储桶中,切勿直接向最终用户公开由提供商签名的 URL。

完整的直接 REST 示例

此 Python 示例创建一个仅含文本的 gen4_image 任务,在轮询前记录其 ID,处理所有任务状态,并在不转发 API 凭据的情况下下载输出。请先安装 Pillow。默认情况下,示例会运行脱敏演练;只有设置 SEND_RUNWAY_REQUEST=1 后才会产生费用。请先检查模型、比例、提示词、允许列表和预算。

#!/usr/bin/env python3
import hashlib, ipaddress, json, os, random, socket, tempfile, time
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import HTTPRedirectHandler, Request, build_opener

from PIL import Image, UnidentifiedImageError

BASE = "https://api.dev.runwayml.com"
VERSION = "2024-11-06"
KEY = os.environ.get("RUNWAYML_API_SECRET", "")
OUT = Path(os.environ.get("RUNWAY_OUTPUT_DIR", "runway-output")).resolve()
MAX_BYTES = 25 * 1024 * 1024
MAX_API_BYTES = 4 * 1024 * 1024
MAX_IMAGE_PIXELS = 20_000_000
Image.MAX_IMAGE_PIXELS = MAX_IMAGE_PIXELS
ALLOWED_HOSTS = frozenset(
    x.strip().lower().rstrip(".")
    for x in os.environ.get("RUNWAY_OUTPUT_HOSTS", "").split(",") if x.strip()
)

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

opener = build_opener(NoRedirect)

def api(method, path, body=None, timeout=60):
    data = None if body is None else json.dumps(body).encode()
    req = Request(BASE + path, data=data, method=method, headers={
        "Authorization": f"Bearer {KEY}",
        "X-Runway-Version": VERSION,
        "Content-Type": "application/json",
        "Accept": "application/json",
    })
    with opener.open(req, timeout=timeout) as response:
        raw = response.read(MAX_API_BYTES + 1)
    if len(raw) > MAX_API_BYTES:
        raise ValueError("Runway API response exceeded configured JSON cap")
    return json.loads(raw)

def atomic_json(path, value):
    fd, tmp = tempfile.mkstemp(dir=path.parent, prefix=path.name, suffix=".tmp")
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            fd = None
            json.dump(value, f, indent=2, sort_keys=True)
            f.flush(); os.fsync(f.fileno())
        os.replace(tmp, path)
    finally:
        if fd is not None: os.close(fd)
        if os.path.exists(tmp): os.unlink(tmp)

def task_snapshot(task):
    # Signed output URLs are bearer credentials. Never log or persist them.
    return {
        key: task[key] for key in (
            "id", "status", "createdAt", "progress", "failureCode", "failure"
        ) if key in task
    } | {"outputCount": len(task.get("output") or [])}

def wait_for_task(task_id, timeout_s=900):
    deadline, delay = time.monotonic() + timeout_s, 5.0
    while time.monotonic() < deadline:
        try:
            task = api("GET", f"/v1/tasks/{task_id}")
            delay = 5.0
        except HTTPError as e:
            if e.code not in (429, 502, 503, 504): raise
            delay = min(30.0, max(5.0, delay * 2))
            time.sleep(delay + random.uniform(0, delay * 0.5)); continue
        except URLError:
            delay = min(30.0, max(5.0, delay * 2))
            time.sleep(delay + random.uniform(0, delay * 0.5)); continue
        atomic_json(OUT / "task.json", task_snapshot(task))
        status = task["status"]
        if status == "SUCCEEDED": return task
        if status == "FAILED":
            raise RuntimeError(f"task failed: {task.get('failureCode')}: {task.get('failure')}")
        if status == "CANCELLED": raise RuntimeError("task cancelled")
        if status not in ("PENDING", "THROTTLED", "RUNNING"):
            raise RuntimeError(f"unknown task status: {status}")
        time.sleep(5.0 + random.uniform(0, 1.0))
    raise TimeoutError("local wait expired; remote task was NOT cancelled")

def checked_output_url(url):
    p = urlparse(url)
    host = (p.hostname or "").lower()
    if (p.scheme != "https" or p.username or p.password or p.fragment or not host
            or p.port not in (None, 443)):
        raise ValueError("unsafe output URL")
    if host.rstrip(".") not in ALLOWED_HOSTS:
        raise ValueError(f"output host not allowlisted: {host}")
    for info in socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM):
        ip = ipaddress.ip_address(info[4][0])
        if not ip.is_global: raise ValueError(f"non-public output address: {ip}")
    return url

def download(url, index):
    # Deliberately no Authorization header and no redirects.
    req = Request(checked_output_url(url), headers={"Accept": "image/*"})
    with opener.open(req, timeout=60) as response:
        ctype = response.headers.get_content_type()
        ext = {"image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp"}.get(ctype)
        if not ext: raise ValueError(f"unexpected media type: {ctype}")
        declared = int(response.headers.get("Content-Length", "0") or 0)
        if declared and declared > MAX_BYTES: raise ValueError("output too large")
        fd, tmp = tempfile.mkstemp(dir=OUT, suffix=".part")
        digest, total = hashlib.sha256(), 0
        try:
            with os.fdopen(fd, "wb") as f:
                fd = None
                while chunk := response.read(1024 * 1024):
                    total += len(chunk)
                    if total > MAX_BYTES: raise ValueError("output exceeded byte cap")
                    digest.update(chunk); f.write(chunk)
                f.flush(); os.fsync(f.fileno())
            head = Path(tmp).read_bytes()[:12]
            valid = (head.startswith(b"\xff\xd8\xff") if ext == ".jpg" else
                     head.startswith(b"\x89PNG\r\n\x1a\n") if ext == ".png" else
                     head[:4] == b"RIFF" and head[8:12] == b"WEBP")
            if not valid: raise ValueError("media signature mismatch")
            try:
                with Image.open(tmp) as probe:
                    dimensions = probe.size
                    if dimensions[0] * dimensions[1] > MAX_IMAGE_PIXELS:
                        raise ValueError(f"output exceeds pixel cap: {dimensions}")
                    decoded_format = probe.format
                    probe.verify()
                with Image.open(tmp) as probe:
                    probe.load()
            except (UnidentifiedImageError, OSError, Image.DecompressionBombError) as exc:
                raise ValueError("output failed bounded pixel decode") from exc
            expected_format = {".jpg": "JPEG", ".png": "PNG", ".webp": "WEBP"}[ext]
            if decoded_format != expected_format:
                raise ValueError("decoder and MIME/signature disagree")
            final = OUT / f"image-{index:02d}{ext}"
            os.replace(tmp, final)
            tmp = None
            return {"path": str(final), "bytes": total, "sha256": digest.hexdigest(),
                    "mediaType": ctype, "dimensions": list(dimensions)}
        finally:
            if fd is not None: os.close(fd)
            if tmp is not None and os.path.exists(tmp): os.unlink(tmp)

request_body = {
    "model": "gen4_image",
    "ratio": "1920:1080",
    "promptText": "Editorial product photograph of a matte cobalt travel mug on pale limestone. Three-quarter view, soft north-window light, crisp rim highlight, restrained blue and warm gray palette, 85mm lens.",
}

request_bytes = json.dumps(request_body, separators=(",", ":")).encode()
print({"model": request_body["model"], "ratio": request_body["ratio"],
       "request_sha256": hashlib.sha256(request_bytes).hexdigest(),
       "estimated_credits_snapshot_2026_07_10": 8})
if os.environ.get("SEND_RUNWAY_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_RUNWAY_REQUEST=1 after approving the paid call")
if not KEY or not ALLOWED_HOSTS:
    raise SystemExit("Set RUNWAYML_API_SECRET and exact RUNWAY_OUTPUT_HOSTS")
OUT.mkdir(parents=True, exist_ok=True)

# Do not blindly retry this POST after a timeout: its outcome may be ambiguous and a retry may duplicate cost.
try:
    created = api("POST", "/v1/text_to_image", request_body)
except HTTPError as e:
    raise SystemExit(f"create rejected with definite HTTP {e.code}; fix or classify before retry")
except (URLError, TimeoutError) as e:
    raise SystemExit(f"create outcome unknown; reconcile before retrying: {e}")
task_id = created["id"]
atomic_json(OUT / "job.json", {"taskId": task_id, "request": request_body, "createdAt": time.time()})
task = wait_for_task(task_id)
artifacts = [download(url, i) for i, url in enumerate(task["output"], 1)]
atomic_json(OUT / "manifest.json", {"taskId": task_id, "artifacts": artifacts})
print(json.dumps({"taskId": task_id, "artifacts": artifacts}, indent=2))

将 RUNWAY_OUTPUT_HOSTS 设置为针对你的账户观察到并经过安全审核的确切输出主机名。不要允许 .cloudfront.net 这类共享租户后缀。此标准库示例会在连接前检查公共 DNS,但 DNS 可能在解析和连接之间发生变化;对于来自恶意多租户输入的情况,请通过出口代理路由下载请求,由代理固定已验证的地址,同时保留 TLS SNI/证书验证。

完整的官方 SDK 参考示例

安装 runwayml,设置 API 密钥和两个获准的 HTTPS/runway:// 引用,然后使用此 Turbo 示例。默认情况下,示例会运行脱敏演练;只有设置 SEND_RUNWAY_REQUEST=1 后才会产生费用。对于任务创建,max_retries=0 是有意设置的,因为 Runway 未公开说明创建操作的幂等性契约。

import os
import hashlib
import json
import random
import time
from pathlib import Path
from runwayml import RunwayML

product_uri = os.environ.get("RUNWAY_PRODUCT_URI", "<required-product-uri>")
style_uri = os.environ.get("RUNWAY_STYLE_URI", "<required-style-uri>")
request_summary = {
    "model": "gen4_image_turbo", "ratio": "1920:1080",
    "reference_hashes": [hashlib.sha256(value.encode()).hexdigest()
                         for value in (product_uri, style_uri)],
    "estimated_credits_snapshot_2026_07_10": 2,
}
print(json.dumps(request_summary, indent=2))
if os.environ.get("SEND_RUNWAY_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_RUNWAY_REQUEST=1 after approving the paid call")
if not os.environ.get("RUNWAYML_API_SECRET") or product_uri.startswith("<required-") or style_uri.startswith("<required-"):
    raise SystemExit("Set RUNWAYML_API_SECRET and both approved reference URIs")

create_client = RunwayML(api_key=os.environ["RUNWAYML_API_SECRET"], max_retries=0)
created = create_client.text_to_image.create(
    model="gen4_image_turbo",
    ratio="1920:1080",
    prompt_text=(
        "@product_ref unchanged in shape, cobalt color, and logo placement, photographed "
        "with the soft window light and pale stone setting of @style_ref. Three-quarter "
        "product view with clear negative space on the right."
    ),
    reference_images=[
        {"uri": product_uri, "tag": "product_ref"},
        {"uri": style_uri, "tag": "style_ref"},
    ],
)
Path("runway-task-id.txt").write_text(created.id + "\n", encoding="utf-8")
print("persisted task id:", created.id)

# Use a separate retrying client only for idempotent reads. Poll manually because
# the synchronous SDK wait helper in the reviewed version did not terminate on CANCELLED.
read_client = RunwayML(api_key=os.environ["RUNWAYML_API_SECRET"], max_retries=2)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
    task = read_client.tasks.retrieve(created.id)
    status = task.status
    print(created.id, status)
    if status == "SUCCEEDED":
        print("outputs ready; pass in-memory URLs to the secure downloader above")
        break
    if status == "FAILED":
        raise RuntimeError(f"generation failed: {getattr(task, 'failure_code', None)}")
    if status == "CANCELLED":
        raise RuntimeError("generation cancelled")
    if status not in ("PENDING", "THROTTLED", "RUNNING"):
        raise RuntimeError(f"unknown task status: {status}")
    time.sleep(5 + random.uniform(0, 1))
else:
    raise TimeoutError("local polling timed out; the provider task was NOT cancelled")

投入生产使用前,请在等待之前持久化保存 created.id,并使用上述安全下载器/清单模式,而不是打印或返回签名 URL。

错误、重试与重复费用控制

HTTP 层
响应是否重试?操作
400、401、404、405否修正请求、凭据、ID 或方法
429是,有次数限制遵守配额/预算;使用带最高 50% 抖动的指数退避
502、503、504是,有次数限制使用带抖动的退避

官方 SDK 默认会对连接错误、408、409、429 和 5xx 重试两次。这对读取操作很有用,但如果创建请求已到达服务器而响应丢失,重试可能会导致重复创建。截至 2026-07-09,在第一方公开文档中未发现针对特定端点的幂等键或对账 API。因此:

  • 重复计费会造成影响时,创建操作优先使用 max_retries=0。
  • 成功收到响应后,立即持久化保存任务 ID。
  • 使用应用级作业键和台账,防止本地重复提交。
  • 可以安全地重试 GET;不要重新提交 THROTTLED 任务。
  • 如果创建结果不明确,请停止操作并通过额度/支持渠道进行对账,不要猜测。
任务失败层
失败类别处理策略
SAFETY.INPUT.*、INPUT_PREPROCESSING.SAFETY.TEXT不要在输入不变的情况下重试;修改或清除输入。输入安全检查失败不会退款。
SAFETY.OUTPUT.*不要反复重试;检查提示词、参考内容和策略。经过审核的生成内容会计费。
ASSET.INVALID修正 URL、MIME 类型、尺寸、大小或编码。
INPUT_PREPROCESSING.INTERNAL稍后使用有限次数的退避重试。
THIRD_PARTY.UNAVAILABLE等待后再重试;适用于依赖合作方模型的情况。
INTERNAL.BAD_OUTPUT.*移除水印或叠加文字,避免要求模型编写提示词,简化明确的文本请求,然后更改请求后重试。
INTERNAL 或缺少代码延迟后进行有限次数的重试;保留证据和任务 ID。

不要在缺少上下文的情况下直接向最终用户展示原始 failure 文本;它是诊断字符串,可能会发生变化。

限制和调度

限制按组织和套餐层级设定。所有图像生成模型共用图像并发额度。超出限制的已接受任务会变为 THROTTLED,并大致按创建顺序排队。每日生成限制采用滚动 24 小时窗口;超出限制会返回 HTTP 429。Runway 目前说明,在每日限制范围内不会限制每分钟请求数,但这并不意味着可以不受限制地突发发送请求。

已公布的套餐表可能变动,也可能因账户而异。请以开发者门户为准,在低于服务提供商限制的范围内设置内部并发上限以及每日/每月额度预算,并联系 Runway 申请生产环境 SLA 所需的定制或保证容量。

权利、隐私、安全与来源

这是操作指南,不构成法律建议。

  • 输入和输出由你负责。请取得版权、商标、隐私、公开权以及生物特征/肖像相关许可;对于可识别的人,请取得知情同意。公众人物的认定门槛并不会赋予相关权利。
  • 标准条款(最后更新于 2026-05-11)称,Runway 不主张拥有输入/输出的所有权,并且在合规的情况下不限制输出的商业用途,但输出可能并非独一无二,也不保证相关权利。条款还要求 API 应用展示链接至 runwayml.com 的 Powered by Runway,并要求采用保护终端用户的条款。
  • 当前 API 营销页面称“不会使用你的数据进行训练”,并称“默认零数据留存”;而标准条款称,输入和输出可能会依据广泛许可用于训练/改进系统。这一营销页面没有说明 API 特定的数据删除边界或 SLA。这些公开材料中的说法相互矛盾。应以已签署的订单表、企业条款/DPA 以及 Runway 的书面确认为准;不要仅凭营销文案承诺不会训练或零留存。
  • 公开材料没有针对该端点提供普遍适用的 API 数据留存期限、删除 SLA、区域处理清单或客户可选的数据驻留选项。输出链接的 24–48 小时有效期和 runway:// URI 的 24 小时有效期指的是访问期限,并非完整的后端数据留存说明。对于受监管/敏感工作负载,应在合同中明确这些未知事项。
  • 合作伙伴模型会引入子处理方。Runway 的企业版 FAQ 称,企业条款/DPA 涵盖所选第三方模型,且合同禁止服务提供商使用客户内容进行训练。请核实你账户对应的具体 API 模型、提供商、处理区域、子处理方清单和合同。
  • Runway 使用自动化系统审核文本、媒体输入和输出,并可能进行人工审核。请预先审核终端用户内容。反复提交被审核拦截的请求可能导致服务暂停。
  • 请遵守现行《使用政策》,其中包括禁止涉及儿童性虐待/性化、未经同意的私密内容、严重暴力、欺诈/欺骗、非法活动、侵犯隐私、仇恨/骚扰性侵害,以及试图绕过安全措施的行为。请查阅完整政策;摘要并不涵盖所有内容。
  • Runway 表示,生成的输出包含 C2PA 来源元数据。请保留原始字节和元数据,在摄取时计算哈希,并记录任务/模型/提示词来源信息。请核验实际交付的文件,因为转换、重新编码、屏幕截图或下游工具可能会剥除元数据。不要声称 C2PA 可以证明内容真实或已取得同意。
  • 除非适用合同和安全审查允许,否则请勿在提示词/参考素材中放入机密、机密个人数据或受监管数据。请使用遵循最小权限原则的素材 URL、较短的有效期、加密、审计日志和删除工作流。

提交前与交接清单

提交前:

  • 用户已批准计费生成、模型、比例、输出数量和最高支出。
  • 提示词符合原生 Gen-4 的 1,000 个 UTF-16 单位限制;使用正向视觉描述。
  • Turbo 有 1–3 个参考项;标准 Gen-4 不超过 3 个。
  • 标签各不相同、均为小写、长度为 3–16 个字符,且提及方式正确。
  • 参考素材的传输方式、MIME 类型、大小、尺寸、权利、同意和保留期限均已确认。
  • 已固定版本标头;密钥保存在服务端;任务 ID 和作业密钥将被持久化。
  • 已明确并发数、超时时间、取消行为和重试预算。
  • 输出主机允许列表、字节数上限、MIME 类型/文件魔数验证、哈希计算、原子存储和清单均已准备就绪。
  • 已针对此账户审查使用政策、署名要求、最终用户条款、训练/保留、区域和分处理商。

交接时,提供模型和确切请求参数、任务 ID、成本估算/实际点数、产物路径和 SHA-256 哈希、QA 结果、被拒分支、已知的权利/同意依据,以及任何尚未解决的服务提供商政策假设。不要泄露 API 密钥或带签名的输出 URL。

第一方来源

访问日期:2026-07-09:

评论

支持 Markdown