Przejdź do treści głównej

Dostarczanie obrazów i wideo w aplikacji towarzysza AI

Twórz zatwierdzone obrazy i klipy towarzysza: zamroź scenę, pobierz wycenę, odzyskuj zadania asynchroniczne i dostarczaj sprawdzone zasoby bez duplikatów.

Autor
Lydia
Opublikowano
Czas czytania
12 min czytania
Zaktualizowano
Dorosły mężczyzna pochyla się ku okularowi teleskopu, a obok niego uśmiecha się kobieta w czerwonym płaszczu przy ciepło oświetlonych drzwiach.

Użytkownik pisze do Rowana, fikcyjnej dorosłej postaci towarzysza: „Pokaż mi dziś wieczorem obserwatorium”. Model czatu może odpowiedzieć w roli postaci, ale Twoja aplikacja musi stworzyć obraz, zachować spójność sceny i dołączyć prawdziwy zasób, gdy już istnieje. Dbaj o responsywność czatu, podczas gdy zadania obrazu i wideo działają osobno.

Daj użytkownikowi kontrolę nad dwoma zakupami:

  • Zatwierdzenie klatki still: pokaż bezpłatną kartę sceny, pobierz wycenę obrazu i utwórz go dopiero wtedy, gdy użytkownik zaakceptuje cenę.
  • Zatwierdzenie klipu: pokaż gotową klatkę still, pobierz wycenę jej animacji i utwórz wideo dopiero wtedy, gdy użytkownik zaakceptuje ten obraz i cenę.

Zapisuj każde żądanie przed wysłaniem, używaj osobnego klucza dla każdego etapu i dostarczaj z własnej zapisanej kopii. Wywołanie narzędzia uruchamia kartę sceny; nigdy nie autoryzuje zakupu ani nie zapowiada nieistniejącego załącznika.

Dla konkretnego budżetu: edycja Qwen w 1k (0,036 $) plus pięciosekundowy klip Seedance 2.5 w 720p to razem 1,4225 $, nie licząc opłat za dodatkowe obrazy referencyjne, tokenów czatu i ponownych prób. Poproś o wycenę (quote), zanim pokażesz użytkownikowi jakąkolwiek cenę.

Cały przebieg po kolei:

text
1. Chat model calls request_scene_image(scene, details)
2. Server validates owner and scene -> free draft card -> tool result back to the chat
3. Gate 1: user approves the still's price -> Qwen edit task (own idempotency key)
4. Callback or poll -> still ready -> copy it to your own storage
5. Gate 2: user accepts the still -> quote the clip -> user approves its price
6. Seedance task (own key) -> callback or poll -> ready_for_review
7. Review the whole clip -> copy to your storage -> delivered

Zamień prośbę z czatu w obraz, który aplikacja naprawdę może wykonać

Użytkownik może napisać: „Wyślij jeszcze jedno zdjęcie w zielonym płaszczu”, podczas gdy wideo w granatowym płaszczu już się generuje. Odpowiedź czatu i żądanie mediów nie powinny dzielić jednego zmiennego opisu. Aplikacja musi wiedzieć, o którego fikcyjnego dorosłego chodzi, jaki płaszcz, jakie miejsce i czy użytkownik chce klatkę still, czy klip. W przeciwnym razie „jeszcze jedno” może przypadkiem stworzyć nową twarz, użyć nieaktualnej sceny albo kupić wideo, gdy użytkownik prosił o zdjęcie.

Trzymaj stałe dane postaci małe: zatwierdzony portret, pełnoletność, stabilne notatki o wyglądzie i postać wybraną przez użytkownika. Bieżącą scenę trzymaj osobno: płaszcz, pomieszczenie, ujęcie kamery i żądana czynność. Rozmowa może zasugerować scenę, ale zapisaną postać i zatwierdzone zasoby źródłowe ustala Twoja aplikacja. Nie pozwalaj modelowi czatu wymyślać adresu URL referencji ani ID zadania ani twierdzić, że obraz istnieje.

W przykładzie z obserwatorium zamień prośbę w jawny opis sceny przed wyceną: Rowan, zatwierdzony portret, granatowy płaszcz, balkon obserwatorium, plan średni, jedna klatka still. Przy późniejszej prośbie „niech się poruszy” użyj zaakceptowanej klatki still z obserwatorium i niewielkiego obrotu głowy. Nie wysyłaj całej rozmowy do modelu obrazu; niezwiązane opisy innych postaci lub wcześniejszych strojów mogą kolidować z bieżącą prośbą.

Interfejs może pokazywać zwięzłe podsumowanie sceny podczas generowania obrazu. Jeśli użytkownik zmieni zielony na granatowy, potraktuj to jako nową rewizję i odpowiednio oznacz wcześniejszy oczekujący wynik. Skończona stara rewizja może zostać w jego prywatnej historii, ale nie powinna po cichu pojawiać się jako odpowiedź na najnowszą prośbę.

W produkcie towarzysza NSFW sprawdzaj przydatność obrazu i wideo osobno. To, że zadanie obrazu akceptuje treści dla dorosłych, nie oznacza, że następny model wideo zaakceptuje tę samą scenę. Sprawdź stronę wybranego modelu dla każdego etapu i uczciwie pokaż niedostępne zadanie, zamiast obiecywać klip dla dorosłych, którego wybrany model nie obsługuje.

Najpierw wypróbuj w Playground

Otwórz Playground Qwen Image 2.1 edit. W sekcji Images wybierz Add a file i wgraj zatwierdzony portret postaci, opisz nową scenę w polu Prompt (poniższy prompt się nadaje), ustaw Resolution na 1k, a Aspect ratio na 16:9, naciśnij Run model i oceń klatkę still. Zatwierdź tę kompozycję, a potem otwórz Playground Seedance 2.5 image-to-video: dodaj klatkę still jako Input image, opisz ruch w polu Prompt i ponownie naciśnij Run model. Closing frame jest opcjonalne.

Pozwól czatowi żądać mediów wywołaniem narzędzia

Daj modelowi czatu narzędzie request_scene_image, które prosi Twoją aplikację o przygotowanie karty sceny. Użyj zalogowanego użytkownika i zatwierdzonego portretu postaci z Twojej aplikacji, a nie ID wymyślonych w odpowiedzi czatu.

  • Model czatu: opisuje scenę i pyta, czy użytkownik chce ją wykonać.
  • Twoja aplikacja: sprawdza własność postaci, pokazuje cenę i czeka na zatwierdzenie.
  • Zadanie medialne: tworzy obraz dopiero po zatwierdzeniu; animacja to osobny zakup.

Grok 4.6 chat obsługuje wywołania narzędzi w formacie OpenAI. API nie przechowuje pamięci rozmowy, więc przy każdym żądaniu wysyłaj ponownie kartę postaci i kontekst pisania fikcji; przepisy dla czatu pokazują format wiadomości narzędzia.

json
{
  "type": "function",
  "function": {
    "name": "request_scene_image",
    "description": "Ask the app to prepare a picture of the character. Call only when the user asks to see something. The app shows a free scene card first; nothing is made or charged until the user approves a quoted price.",
    "parameters": {
      "type": "object",
      "properties": {
        "scene": {"type": "string", "enum": ["observatory-balcony", "kitchen-morning", "rooftop-dusk"]},
        "details": {"type": "string", "description": "Clothing, props and mood the user asked for, in one sentence."}
      },
      "required": ["scene"]
    }
  }
}

Odpowiedz na wywołanie wiadomością tool, że karta robocza czeka na zatwierdzenie, aby model napisał uczciwą linijkę („Oto karta sceny, mam ją wykonać?”), zamiast twierdzić, że coś dostarczono.

Trzymaj postać i scenę w osobnych rekordach

Trzymaj zatwierdzony portret osobno od żądanej sceny: granatowy płaszcz, balkon obserwatorium i niewielki obrót w stronę teleskopu. Portret zakotwicza postać; zaakceptowany obraz sceny staje się pierwszą klatką wideo. Nie zastępuj portretu postaci przy każdej nowej scenie.

Zapisuj jeden rekord na żądanie przed jego wysłaniem (to pola Twojej aplikacji, nie ładunek API):

json
{
  "mediaRequestId": "scene-104-r1",
  "ownerId": "application-user-id",
  "sceneRevision": 1,
  "referenceAssetId": "approved-rowan-portrait",
  "approvedStillAssetId": null,
  "model": "alibaba/qwen-image-2.1/edit",
  "inputSnapshot": {},
  "idempotencyKey": "<uuid generated for this request>",
  "taskId": null,
  "state": "draft",
  "deliveredAssetId": null
}

Jeśli użytkownik zmieni płaszcz na zielony, utwórz rewizję 2 z nowym kluczem; nigdy nie edytuj żądania z niebieskim płaszczem, gdy trwa jego wykonanie.

Zapisz zatwierdzoną klatkę still u siebie. Link do wyniku zadania działa około 20 minut, a wygenerowany plik około 14 dni, więc skopiuj zaakceptowaną klatkę still do własnego magazynu i podaj Seedance stabilny publiczny adres HTTPS, który zwraca sam plik obrazu (link do strony udostępniania zawiedzie). Przekazanie linku do wyniku Qwen prosto do etapu wideo działa tylko do jego wygaśnięcia. Do rewizji ruchu używaj zapisanej klatki still; zmiana portretu i ruchu naraz utrudnia poprawienie któregokolwiek z nich.

Ustaw osobno sterowanie obrazem i wideo

Trasa Qwen Image 2.1 edit przyjmuje prompt i od jednego do dziesięciu image_urls. Dodatkowe obrazy referencyjne podnoszą cenę dla 1k; 2k kosztuje 0,048 $ przy od jednego do dziesięciu obrazach referencyjnych. Dla pierwszej sceny użyj jednego zatwierdzonego portretu, 1k, 16:9, PNG i wyłączonego rozszerzania promptu.

Use the supplied portrait as the identity reference for the same fictional adult Rowan. Create a medium shot on an observatory balcony, wearing a navy coat. Keep both hands relaxed and visible. A telescope rests on its tripod beside Rowan. Leave open space on the right for a small head turn. Warm doorway light, natural night background, no text or extra people.

Porównaj twarz i strój z zatwierdzonym portretem, zanim zaproponujesz klatkę still użytkownikowi. Na drugiej bramce zatwierdzania użytkownik akceptuje tę klatkę still i cenę klipu przed animacją. Jeśli teleskop unosi się w powietrzu albo po prawej stronie brakuje miejsca na ruch, popraw to na etapie obrazu; wideo nie naprawi złej pierwszej klatki.

Fikcyjny dorosły mężczyzna w granatowym płaszczu stoi na balkonie obserwatorium z rozluźnionymi rękami obok teleskopu na stabilnym statywie.

Ilustracja proponowanej pierwszej klatki: postać ma miejsce na niewielki obrót, a teleskop jest fizycznie podparty. To nie jest wynik Qwen ani test generowania.

Trasa Seedance 2.5 image-to-video przyjmuje image_url, od 4 do 30 sekund, 480p, 720p lub 1080p, przełącznik dźwięku i opcjonalny seed. aspect_ratio akceptuje tylko adaptive, więc klip podąża za kształtem klatki still; najpierw przytnij klatkę still do formatu dostarczenia. Trasa reference-to-video ma inny kontrakt, więc nie mieszaj jej pól.

Pobieraj wycenę w punkcie decyzji

Edycje Qwen kosztują według cennika 0,036 $ dla 1k, 0,048 $ dla 2k i 0,072 $ dla 1.5k, z jednym obrazem referencyjnym w cenie. Dodatkowe obrazy referencyjne podnoszą ceny dla 1k i 1.5k. Seedance kosztuje 0,1234 $ za sekundę w 480p, 0,2773 $ w 720p i 0,6823 $ w 1080p, z dźwiękiem lub bez po tej samej stawce.

Przy ponownej próbie ruchu użyj zaakceptowanej klatki still.

PlanKoszt
Tylko klatka still 1k0,036 $
Klatka still + klip 5 s w 480p0,653 $
Klatka still + klip 5 s w 720p1,4225 $
Klatka still + klip 5 s w 1080p3,4475 $
Klatka still + dwie próby klipu w 720p2,809 $

Kwoty obejmują jeden obraz referencyjny w edycji 1k i nie obejmują opłat za dodatkowe obrazy referencyjne, tokenów czatu ani dodatkowych edycji klatki still.

Pokaż cenę klatki still przed generowaniem, a cenę klipu po tym, jak użytkownik zaakceptuje klatkę still, a nie przy każdej wymianie na czacie. Wycena jest bezpłatna, niczego nie rezerwuje i obowiązuje pięć minut dla danego konta, klucza API, modelu i danych wejściowych; zmiana czasu trwania, rozdzielczości lub referencji wymaga nowej. Wysłane zadanie od razu wstrzymuje szacowany koszt, a to wstrzymanie to maksimum, jakie może zostać naliczone. Ponowne użycie zatwierdzonego obrazu ogranicza rewizję ruchu do opłaty za wideo.

Klient etapu wideo, który przetrwa utraconą odpowiedź

Zainstaluj spicyapi poleceniem python -m pip install spicyapi, ustaw SPICY_API_KEY i APPROVED_STILL_URL, a po zatwierdzeniu klatki still uruchom poniższy kod. Zachowaj zapisany plik przebiegu, aby wznowić tę samą próbę. Dla innego twórczego ujęcia użyj nowej nazwy przebiegu.

python
import json
import os
import time
import uuid
from pathlib import Path
from spicyapi import SpicyClient, output_assets

client = SpicyClient()

def run(model, inputs, name):
    path = Path(name + ".json")
    request = {"model": model, "input": inputs}
    saved = json.loads(path.read_text()) if path.exists() else {"request": request, "key": str(uuid.uuid4())}
    if saved["request"] != request:
        raise ValueError("Changed input needs a new run name; preserve the old attempt.")
    def save():
        temporary = path.with_suffix(".tmp")
        temporary.write_text(json.dumps(saved))
        temporary.chmod(0o600)
        temporary.replace(path)
    if not saved.get("taskId"):
        if time.time() - saved.get("sentAt", time.time()) >= 23 * 3600:
            raise RuntimeError("Reconcile the saved attempt before submitting again.")
        if "quote" not in saved:
            saved["quote"] = client.quote_task(model=model, input_data=inputs)
            if input(f"Maximum ${saved['quote']['maxCharge']}. Create? [y/N] ") != "y":
                raise SystemExit("Stopped without creating this task.")
        saved.setdefault("sentAt", time.time())
        save()
        quote = saved["quote"]
        task = client.create_task(model=model, input_data=inputs,
            idempotency_key=saved["key"], quote_id=quote["quoteId"], expected_cost=quote["estimatedCost"])
        saved["taskId"] = task["taskId"]
        save()
    print("Task:", saved["taskId"])
    for _ in range(360):
        task = client.get_task(saved["taskId"])
        if task["state"] in {"failed", "expired", "canceled"}:
            raise RuntimeError(str(task.get("errorMessage", task["state"])))
        if task["state"] == "succeeded":
            assets = output_assets(task)
            if assets and assets[0].get("unavailable"):
                raise RuntimeError("Output unavailable; keep the task ID for reconciliation.")
            if assets and assets[0].get("url") and not assets[0].get("pending"):
                return assets[0]
        time.sleep(5)
    raise TimeoutError("Rerun the same saved attempt to resume; no new task is needed.")

asset = run("bytedance/seedance-2.5/image-to-video", {
    "image_url": os.environ["APPROVED_STILL_URL"],
    "prompt": "The adult character slowly turns toward the telescope. Hold the camera steady. Keep the navy coat and balcony unchanged.",
    "duration_seconds": 5, "resolution": "720p", "aspect_ratio": "adaptive", "generate_audio": True,
}, "companion-video")
print(asset["url"])

Python SDK i dokumentacja zadań opisują integrację produkcyjną.

Jedna funkcja aktualizacji dla odpytywania i callbacków

Użyj jednego handlera aplikacji zarówno dla wyniku pobranego odpytywaniem, jak i dla zweryfikowanego zdarzenia zakończenia. Powinien dołączyć zasób raz, zachować go jako prywatny i powiadomić czat dopiero wtedy, gdy jest gotowy do sprawdzenia. Zduplikowane zdarzenie nie może tworzyć kolejnego wpisu w galerii.

W działającej aplikacji towarzysza wysyłaj powiadomienie o zakończeniu do zweryfikowanego webhooka i zachowaj okresowe sprawdzanie statusu dla callbacków, które nigdy nie dotrą. Treści callbacków zawierają prompt i podpisane linki do wyniku, więc traktuj je jako prywatne; dokumentacja zadań opisuje konfigurację. Webhooki z przeglądem uzgadniającym (zobacz artykuł „async-task-api-polling-vs-webhook”) nadają się na produkcję; do prototypu wystarczy odpytywanie. Użyj ograniczonego rozdzielania, gdy wielu użytkowników żąda mediów jednocześnie.

Stany dostarczenia, błędy i anulowanie

Używaj draft, submitted, generating, ready_for_review, delivered, failed i rejected. succeeded z API oznacza, że zaczyna się obsługa wyniku; nie znaczy, że użytkownik go widzi. Sprawdź cały klip pod kątem dryfu twarzy, poruszających się rekwizytów i niezamówionych cięć, zanim przejdzie do delivered.

Wynik APICo pokazaćOpłata
succeeded, zasób gotowysprawdzić, potem dostarczyćodczytać settled i cost
succeeded, zasób pending„jeszcze się przygotowuje”, odpytać ponownie za kilka sekundjeszcze nieostateczna
succeeded, zasób unavailableprzywrócić już zapisaną kopię lub zaproponować świadome nowe generowanieodczytać cost
failed lub expiredczytelny komunikat o błędzie i przycisk ponowieniawstrzymanie zwykle zwalniane; niektóre odmowy treści są naliczane, jeśli tak podaje strona modelu

Wygasła wycena wymaga nowej wyceny i zatwierdzenia; już zaakceptowane żądanie odzyskaj jego pierwotnym kluczem. Zaakceptowanego zadania nie można anulować. Jeśli użytkownik mówi „nieważne”, ukryj oczekujący załącznik i pozwól zadaniu się zakończyć; nie obiecuj, że zamknięcie czatu zwolni opłatę. Ponowna próba to nowe żądanie z własną wyceną.

Zachowaj prywatność prywatnych mediów towarzysza

Przed udostępnieniem sprawdź, czy scena należy do zalogowanego użytkownika. Kopiuj zaakceptowane media do magazynu z własnymi regułami dostępu; tymczasowe linki do wyników nie powinny stawać się publicznymi adresami galerii.

Przy usuwaniu traktuj osobno kopię platformy i własną. Skróć czas przechowywania wyniku przez platformę ustawieniem retencji dla zadania, a gdy użytkownik usuwa scenę lub konto, zniszcz zapisaną zawartość każdego zakończonego zadania (rekord rozliczeniowy zostaje). Retencja 0 może usunąć wynik, zanim Twój worker go skopiuje. Usuń własną kopię i osobno cofnij dostęp do jej dostarczania; szczegóły są w dokumentacji retencji. Użyj kontroli retencji i trwałego potoku przechowywania odpowiednich dla produktu.

Przetestuj cztery awarie na fixture'ach

Zasymuluj podwójne kliknięcie, utraconą odpowiedź o przyjęciu, zduplikowane zdarzenie sukcesu i niedostępny zasób. Powinieneś otrzymać jedno żądanie logiczne, odzyskanie pierwotnym kluczem, jeden załącznik w galerii i uczciwy stan „niedostępne”. Żaden z tych testów nie wymaga płatnego generowania. Spójność twarzy między klatkami still i klipami to kwestia wizualna, więc sprawdzaj każdy wynik. Nie pozwól, by wiadomość na czacie „Twoje wideo jest gotowe” przesłoniła prawdziwy stan zadania lub zasobu. Ilustracja z obserwatorium to scena redakcyjna, nie wynik Qwen ani Seedance.

Pozwól Agentowi obsłużyć wywołanie

Użyj Agenta, gdy chcesz, by przygotował dane wejściowe i zwrócił wynik, podczas gdy Ty zatwierdzasz cenę i decyzje twórcze.

  • CLI: z plikiem input.json takim jak {"image_urls": ["<approved portrait URL>"], "prompt": "<the scene prompt above>", "resolution": "1k", "aspect_ratio": "16:9", "output_format": "png", "enable_prompt_expansion": false} uruchom npx @spicyapi/cli tasks create --model alibaba/qwen-image-2.1/edit --input-file input.json --wait. CLI potwierdza cenę; zachowaj zwrócone ID zadania na potrzeby odzyskiwania.
  • MCP: poproś asystenta, by użył spicyapi_task_create dla alibaba/qwen-image-2.1/edit, a następnie spicyapi_task_wait. Serwer MCP przed utworzeniem prosi o potwierdzenie ceny.
  • Skill: zainstaluj go poleceniem npx skills add spicyapi-ai/spicy-skill, a potem poproś $spicyapi, by wykonał ten sam przebieg i zatrzymał się na Twoje zatwierdzenie. Skill korzysta ze skonfigurowanego połączenia CLI lub MCP.

Także w językach English · 日本語 · 한국어 · Deutsch · Français · Español · Português (Brasil) · Русский · Italiano

Wypróbuj samodzielnie

Płacisz tylko za to, co uruchomisz — bez planów i subskrypcji.

Zrobione w MiniMax H3 · ByteDance Seedance 2.0 Fast