
การผสานระบบคอมิก 4 ช่องที่เชื่อถือได้ต้องใช้มากกว่าคำขอสร้างเพียงครั้งเดียว ลูปขั้นต่ำสำหรับโปรดักชันคือ:
อัปโหลดภาพอ้างอิงตัวละคร -> สร้าง 4 ช่อง -> ตรวจสอบสถานะจนสิ้นสุด -> ตรวจทุกช่อง -> ซ่อมเฉพาะช่องที่ล้มเหลว
คู่มือเริ่มต้นนี้นำลูปดังกล่าวไปใช้ใน TypeScript โดยเก็บ API key ไว้ที่ฝั่งเซิร์ฟเวอร์ จัดเตรียมข้อมูลตัวละครที่คงที่ ใช้ idempotency key สำหรับคำขอ create บันทึกเวลาที่ใช้และความล้มเหลว และเจาะจงซ่อมเฉพาะช่องเมื่อความต่อเนื่องของตัวละครหลุด
เริ่มต้นด้วย LlamaGen.AI Comic API จากนั้นใช้ตัวอย่างฉบับสมบูรณ์ด้านล่างเป็นพื้นฐานสำหรับการเชื่อมต่อฝั่งเซิร์ฟเวอร์
บทความนี้ได้รับการตรวจสอบตามสัญญาการทำงานจริงกับ เอกสาร Comic API ณ วันที่ 10 ตุลาคม 2026
| Check | Verified behavior |
|---|---|
| Authentication | Bearer token ในทุกคำขอ |
| Production base URL | https://api.llamagen.ai |
| Upload | POST /v1/comics/upload ส่งกลับ fileUrl ที่นำกลับมาใช้ซ้ำได้ |
| Create | POST /v1/comics/generations รองรับ fixPanelNum: 4 และ comicRoles |
| Status | GET /v1/comics/generations/{generationId} ส่งกลับสถานะและผลลัพธ์ของแต่ละช่อง |
| Repair | PATCH /v1/comics/generations/{generationId} สามารถสร้างใหม่เฉพาะช่องเดียวแบบ zero-based |
| Usage | การตอบกลับของ create และ repair เปิดเผยข้อมูลการใช้งานสำหรับบันทึกของคุณเอง |
สรุปฟีเจอร์ใหม่ล่าสุดของ LlamaGen การปรับปรุงผลิตภัณฑ์ อัปเดตดีไซน์ และการแก้ไขบั๊กสำคัญ
เราไม่ได้แต่งตัวเลข benchmark เรื่อง latency หรืออัตราความสำเร็จขึ้นมาเอง เพราะค่าดังกล่าวแตกต่างกันตามบัญชี คิว การตั้งค่าโมเดล และข้อมูลนำเข้า ตัวอย่างนี้จะบันทึกเวลาที่ใช้จริง การใช้งาน API สถานะสุดท้าย และรายละเอียดความล้มเหลว เพื่อให้คุณสร้าง benchmark ที่อ้างอิงได้ในสภาพแวดล้อมของคุณเอง
พรอมป์บอกโมเดลว่าเกิดอะไรขึ้น ส่วนภาพอ้างอิงตัวละครบอกว่าเรื่องราวนั้นกำลังเกิดขึ้นกับใคร
สำหรับบทบาทที่ปรากฏซ้ำ ให้คงข้อมูลเหล่านี้ให้สม่ำเสมอ:
ในตัวอย่างนี้ บทบาทคือลูกส่งของที่มีผมสั้นสีเข้ม เสื้อกันฝนสีเหลือง และกระเป๋าสะพายสีแดง รายละเอียดเหล่านี้กระชับพอที่จะใช้ซ้ำ และชัดเจนพอที่จะตรวจสอบได้

ใช้ Node.js 20 หรือใหม่กว่าเพื่อให้มี fetch, FormData และ Blob พร้อมใช้งาน เก็บคีย์ไว้ในตัวแปรสภาพแวดล้อมที่เข้าถึงได้เฉพาะฝั่งเซิร์ฟเวอร์
export LLAMAGEN_API_KEY="your_server_side_key"
อย่าเปิดเผยคีย์นี้ใน browser bundle, แอปมือถือ, repository สาธารณะ หรือคำขอฝั่งไคลเอนต์ หากผลิตภัณฑ์ของคุณมี frontend ให้ส่งบรีฟของผู้ใช้ไปยัง backend ของคุณเอง แล้วให้ backend นั้นเรียก LlamaGen.AI
เวิร์กโฟลว์นี้มีการทำงานผ่านเครือข่าย 4 ขั้นตอน:

บันทึกไฟล์ต่อไปนี้เป็น comic-quickstart.ts, วางภาพตัวละครไว้ที่ ./courier-reference.png แล้วรันจากสภาพแวดล้อมฝั่งเซิร์ฟเวอร์ของคุณ
import { basename } from "node:path";
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";
const API_BASE = "https://api.llamagen.ai";
const API_KEY = process.env.LLAMAGEN_API_KEY;
if (!API_KEY) {
throw new Error("Set LLAMAGEN_API_KEY before running this script.");
}
type UploadResponse = {
code?: number;
fileUrl: string;
};
type Usage = {
total_comics?: number;
redraw_panels?: number;
amount?: number;
};
type Generation = {
id: string;
status: "LOADING" | "PROCESSED" | "FAILED" | "CANCELLED" | string;
usage?: Usage;
comics?: Array<{
page?: number;
panels?: Array<{
panel?: number;
image?: string;
assetUrl?: string;
caption?: string;
}>;
}>;
error?: unknown;
};
async function api<T>(path: string, init: RequestInit = {}): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
...init,
headers: {
Authorization: `Bearer ${API_KEY}`,
...init.headers,
},
});
const text = await response.text();
const body = text ? JSON.parse(text) : null;
if (!response.ok) {
throw new Error(
`Comic API ${response.status}: ${JSON.stringify(body)}`,
);
}
return body as T;
}
async function uploadReference(filePath: string) {
const bytes = await readFile(filePath);
const form = new FormData();
form.set("file", new Blob([bytes]), basename(filePath));
return api<UploadResponse>("/v1/comics/upload", {
method: "POST",
body: form,
});
}
async function waitForGeneration(
generationId: string,
timeoutMs = 10 * 60_000,
) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const job = await api<Generation>(
`/v1/comics/generations/${generationId}`,
);
console.log(
JSON.stringify({
generationId,
status: job.status,
elapsedMs: Date.now() - startedAt,
usage: job.usage,
}),
);
if (job.status === "PROCESSED") return job;
if (job.status === "FAILED" || job.status === "CANCELLED") {
throw new Error(
`Generation ended as ${job.status}: ${JSON.stringify(job.error)}`,
);
}
await new Promise((resolve) => setTimeout(resolve, 4_000));
}
throw new Error(`Generation ${generationId} timed out.`);
}
async function main() {
const startedAt = Date.now();
try {
const reference = await uploadReference("./courier-reference.png");
const created = await api<Generation>("/v1/comics/generations", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({
prompt: [
"Create one four-panel comic page in a restrained cinematic manga style.",
"Panel 1: Ren enters a rain-soaked station and notices a golden key.",
"Panel 2: Close view as Ren lifts the key and studies it.",
"Panel 3: Ren runs toward an old locked service door.",
"Panel 4: Ren opens the door and warm light spills into the station.",
"Keep the same face, haircut, yellow jacket, and red messenger bag.",
"Leave clean space for later lettering.",
].join("\n"),
preset: "japanese_manga",
size: "1024x1024",
fixPanelNum: 4,
language: "en",
comicRoles: [
{
name: "Ren",
age: 22,
gender: "male",
dress: "yellow rain jacket, black trousers, red messenger bag",
image: reference.fileUrl,
},
],
}),
});
const firstPass = await waitForGeneration(created.id);
console.log("first_pass_complete", {
id: firstPass.id,
elapsedMs: Date.now() - startedAt,
usage: firstPass.usage,
pages: firstPass.comics?.length ?? 0,
});
// Example review result: panel 3 lost the red bag.
const repairQueued = await api<Generation>(
`/v1/comics/generations/${created.id}`,
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
page: 0,
panel: 2,
panelPrompt:
"Ren runs toward the service door. Preserve his exact face, short dark hair, yellow rain jacket, and visible red messenger bag.",
images: [reference.fileUrl],
}),
},
);
console.log("repair_queued", {
id: repairQueued.id,
usage: repairQueued.usage,
});
const repaired = await waitForGeneration(created.id);
console.log("workflow_complete", {
id: repaired.id,
totalElapsedMs: Date.now() - startedAt,
finalStatus: repaired.status,
});
} catch (error) {
console.error("comic_workflow_failed", {
elapsedMs: Date.now() - startedAt,
message: error instanceof Error ? error.message : String(error),
});
process.exitCode = 1;
}
}
await main();
รันด้วยคำสั่ง:
npx tsx comic-quickstart.ts
ตัวอย่างนี้ตั้งใจบันทึก log เป็นอ็อบเจ็กต์ที่มีโครงสร้าง ในโปรดักชัน ให้ส่งฟิลด์เดียวกันนี้ไปยังระบบ observability ของคุณ และใส่ตัวระบุคำขอหรือโปรเจกต์ของคุณเองเพิ่มเติม
อย่าอนุมัติหน้าเพียงเพราะมันดูสวยในขนาด thumbnail ให้ตรวจแต่ละช่องเทียบกับข้อกำหนดตัวละครที่ล็อกไว้:
| Check | Pass condition |
|---|---|
| Face | อายุ ลักษณะใบหน้า และสัดส่วนเหมือนกัน |
| Hair | รูปทรง ความยาว และสีเหมือนกัน |
| Costume | เสื้อแจ็กเก็ตสีเหลืองยังคงเป็นสีเหลืองและมีโครงสร้างเหมือนเดิม |
| Prop | กระเป๋าสะพายสีแดงยังคงอยู่และอยู่ในตำแหน่งที่ถูกต้อง |
| Sequence | กุญแจ ประตู สภาพอากาศ และทิศทางการเคลื่อนไหวยังคงต่อเนื่อง |
| Composition | ทุกช่องมีหน้าที่ในการเล่าเรื่องที่แตกต่างกัน |
| Lettering space | ใบหน้าและการกระทำสำคัญไม่ถูกบัง |
ให้มองสิ่งนี้เป็นการควบคุมคุณภาพแบบ machine-readable หากคุณกำลังสร้าง pipeline ที่ใหญ่ขึ้น จัดเก็บเหตุผลผ่าน/ไม่ผ่านแยกต่อช่อง ไม่ใช่แค่ยกนิ้วให้ทั้งหน้า
พรอมป์สำหรับการซ่อมที่เร็วที่สุดคือการระบุทั้งการกระทำใหม่และองค์ประกอบที่ห้ามเปลี่ยน
การซ่อมช่อง 3:
Ren วิ่งไปยังประตูบริการ
คงใบหน้าของเขา ผมสั้นสีเข้ม เสื้อกันฝนสีเหลือง
และกระเป๋าสะพายสีแดงที่มองเห็นได้ไว้เหมือนเดิม
คงสถานีที่มีฝนตกและการเคลื่อนที่จากซ้ายไปขวา
ดัชนีของช่องเป็นแบบ zero-based ดังนั้นช่องที่สามที่มองเห็นได้คือ panel: 2 endpoint สำหรับอัปเดตรองรับ panelPrompt, images อ้างอิง หรือ caption ทดแทน

เรื่องนี้สำคัญในเชิงปฏิบัติการ การสร้างทั้ง 4 ช่องใหม่อาจทำให้เกิดความเสี่ยงด้านความต่อเนื่องใหม่อีก 3 จุด ขณะกำลังแก้ปัญหาเดิมเพียง 1 จุด การซ่อมแบบเจาะจงช่วยลดพื้นที่ที่ต้องตรวจทาน
ก่อนปล่อยการเชื่อมต่อนี้ใช้งานจริง ให้เพิ่มการควบคุมเหล่านี้:
สำหรับลำดับเรื่องที่ยาวขึ้น ให้เปลี่ยนจาก fixPanelNum ไปใช้ pagination.totalPages และ pagination.panelsPerPage อย่าส่งทั้งสองโหมดในคำขอ create เดียวกัน
ใช้ Comic API เมื่อคอมิกถูกสร้างภายในผลิตภัณฑ์ของคุณเอง งานแบบแบตช์ เครื่องมือการศึกษา pipeline การเผยแพร่ หรือเวิร์กโฟลว์ของลูกค้า
ใช้ visual editor เมื่อนักสร้างสรรค์ต้องการสำรวจเรื่องราวแบบโต้ตอบ เปรียบเทียบทางเลือก หรือกำกับงานศิลป์ของแต่ละหน้าด้วยมือ หลายทีมใช้ทั้งสองแบบร่วมกัน: API ใช้สร้างและบันทึก first pass ที่ทำซ้ำได้ ส่วน editor ใช้จัดการการแก้ไขกรณีพิเศษ
หากโครงสร้างเรื่องราวของคุณยังเปลี่ยนแปลงอยู่ ให้วางแผนก่อนสร้างช่องที่เสร็จสมบูรณ์ คู่มือถัดไป Storyboard First or Comic Panels First? จะแสดงวิธีวัดความแตกต่างของงานแก้ไขซ้ำ
ได้ ส่ง fixPanelNum: 4 สำหรับคำขอหน้าเดียวแบบ 4 ช่อง หากมีหลายหน้า ให้ใช้วัตถุ pagination แทน
อัปโหลดภาพอ้างอิงที่ชัดเจน ส่ง fileUrl ที่ได้กลับมาใน comicRoles[].image คงชื่อบทบาทและคำอธิบายชุดให้เหมือนเดิม และตรวจทุกช่องที่ส่งกลับมาก่อนทำลำดับต่อไป
ได้ ส่งคำขอ PATCH พร้อมดัชนีหน้าและช่องแบบ zero-based รวมถึง panelPrompt ใหม่ ภาพอ้างอิงเพิ่มเติมถ้ามี หรือ caption
ให้ใช้พฤติกรรม idempotency ตามที่ LlamaGen.AI ระบุไว้ในเอกสาร เพื่อไม่ให้การ retry จากปัญหาเครือข่ายสร้างงานซ้ำโดยไม่ตั้งใจ ใช้คีย์ใหม่เฉพาะเมื่อคุณตั้งใจจะสร้าง generation ใหม่จริง ๆ
ไม่ ตัวอย่างนี้เปิดเผยเครื่องมือวัดที่จำเป็นต่อการเก็บค่าจริงของคุณ ให้เผยแพร่ค่า latency การใช้งาน และอัตราความล้มเหลว หลังจากรันข้อมูลนำเข้าเดียวกันซ้ำ ๆ ในบัญชีของคุณเองและบันทึกเงื่อนไขไว้แล้วเท่านั้น
เขียนโดย Jay และตรวจทานด้านเทคนิคกับสัญญาการทำงานของ endpoint จริงโดยทีมคอนเทนต์ของ LlamaGen.AI พฤติกรรมของผลิตภัณฑ์อาจเปลี่ยนแปลงได้ เอกสารที่ลิงก์ไว้คือแหล่งข้อมูลที่ถูกต้องที่สุด
พร้อมสร้างเวิร์กโฟลว์แล้วหรือยัง? เปิด LlamaGen.AI Comic API สร้างคีย์ฝั่งเซิร์ฟเวอร์ แล้วรันคู่มือเริ่มต้นอย่างรวดเร็วด้วยภาพอ้างอิงตัวละครของคุณเอง
Popular creator tools
Go from comics and storyboards to stylized photos with LlamaGen.
Visualize every scene before production starts.
© 2026 LlamaGen.Ai สงวนลิขสิทธิ์
ผู้นำด้านอนาคตของการสร้างคอมิก AI



