一个可靠的四格漫画集成不仅仅需要一次生成请求。最小可用的生产循环是:
上传角色参考图 -> 创建四个面板 -> 轮询终态状态 -> 检查每个面板 -> 仅修复失败的面板
本快速入门用 TypeScript 实现了这一循环。它会将 API 密钥保留在服务端,提供稳定的角色记录,为创建请求使用幂等键,记录耗时和失败情况,并在连续性出错时只针对单个面板进行处理。
先从 LlamaGen.AI Comic API 开始,然后将下面的完整示例作为服务端集成的基线。
本文已于 2026 年 10 月 10 日根据在线 Comic API 文档 进行了合同级核对。
| 检查项 | 已验证行为 |
|---|---|
| 身份验证 | 每个请求都使用 Bearer token |
| 生产环境基础 URL | https://api.llamagen.ai |
| 上传 | POST /v1/comics/upload 返回可复用的 fileUrl |
| 创建 | POST /v1/comics/generations 接受 fixPanelNum: 4 和 comicRoles |
| 状态 | GET /v1/comics/generations/{generationId} 返回状态和面板输出 |
| 修复 | PATCH /v1/comics/generations/{generationId} 可重新生成单个从零开始编号的面板 |
| 用量 | 创建和修复响应都会暴露用量数据,供你写入自己的日志 |
我们没有虚构延迟或成功率基准。那些数值会因账户、队列、模型配置和输入而变化。示例会记录你的真实耗时、API 用量、终态状态和失败细节,以便你在自己的环境中产出可信的基准数据。
提示词告诉模型发生了什么。角色参考图告诉模型故事发生在谁身上。
对于重复出现的角色,请保持以下字段稳定:
在这个示例中,角色是一名快递员,短深色头发,黄色雨衣,背着红色邮差包。这些细节足够简洁,便于重复,也足够鲜明,便于审查。

使用 Node.js 20 或更高版本,以便可以使用 fetch、FormData 和 Blob。将密钥保存在仅服务端可见的环境变量中。
export LLAMAGEN_API_KEY="your_server_side_key"
不要在浏览器打包产物、移动应用、公共仓库或客户端请求中暴露这个密钥。如果你的产品有前端,请将用户的需求描述发送到你自己的后端,再由该后端调用 LlamaGen.AI。
这个工作流包含四个网络操作:

将以下内容保存为 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
该示例有意记录结构化对象。在生产环境中,请将相同字段发送到你的可观测性系统,并附上你自己的请求或项目标识符。
不要因为页面缩略图看起来不错就直接通过。请根据角色锁定条件审查每个面板:
| 检查项 | 通过条件 |
|---|---|
| 面部 | 年龄、特征和比例一致 |
| 头发 | 轮廓、长度和颜色一致 |
| 服装 | 黄色夹克始终保持黄色,并保持相同结构 |
| 道具 | 红色邮差包始终存在且位置正确 |
| 连续性 | 钥匙、门、天气和移动方向保持连续 |
| 构图 | 每个面板都承担不同的叙事任务 |
| 留字空间 | 面部和关键动作没有被遮挡 |
如果你在构建更大的流水线,请将其视为机器可读的质量控制。为每个面板存储通过/失败原因,而不仅仅是页面级别的一个“通过”。
最快的修复提示词会同时说明新的动作和不能改变的元素。
面板 3 修复:
Ren 跑向服务门。
保留他完全一致的面部、短深色头发、黄色雨衣,
以及清晰可见的红色邮差包。
保持雨中的车站环境和从左到右的移动方向。
面板索引从零开始,所以可见的第三个面板是 panel: 2。更新端点可以接受 panelPrompt、参考 images,或替换 caption。

这在运营层面很重要。重建全部四个面板,可能会在修复一个旧问题的同时引入三个新的连续性风险。定向修复可以缩小审查范围。
在发布集成之前,加入以下控制项:
对于更长的序列,请从 fixPanelNum 切换到 pagination.totalPages 和 pagination.panelsPerPage。不要在一次创建请求中同时发送这两种模式。
当漫画是在你自己的产品、批处理任务、教育工具、出版流水线或客户工作流中生成时,请使用 Comic API。
当创作者希望以交互方式探索故事、比较不同方案,或逐页手动进行艺术指导时,请使用可视化编辑器。很多团队会两者并用:API 负责创建并记录可重复的首轮结果,而编辑器负责处理例外修订。
如果你的故事结构仍在变化,请先规划好再制作最终面板。下一篇指南 Storyboard First or Comic Panels First? 会展示如何衡量返工差异。
可以。对于单页四格请求,发送 fixPanelNum: 4。如果是多页,请改用 pagination 对象。
上传清晰的参考图,将返回的 fileUrl 传入 comicRoles[].image,保持角色名称和服装描述稳定,并在继续后续序列之前审查每一个返回的面板。
可以。发送一个 PATCH 请求,包含从零开始的页码和面板索引,再加上新的 panelPrompt、可选参考图或说明文字。
请使用 LlamaGen.AI 文档中说明的幂等行为,这样网络重试就不会意外创建重复工作。只有在你明确想要一次新生成时,才使用新的键。
不会。它暴露的是捕获你真实数字所需的监测手段。只有在你自己的账户中对相同输入反复运行并记录条件之后,才应发布延迟、用量和失败率。
本文由 Jay 撰写,并由 LlamaGen.AI 内容团队根据在线端点契约进行技术审阅。产品行为可能会变化;以上链接文档才是权威来源。
准备好构建这个工作流了吗?打开 LlamaGen.AI Comic API,创建一个服务端密钥,然后用你自己的角色参考图运行本快速入门。
热门创作工具
从漫画、分镜到照片风格化,用 LlamaGen 更快完成视觉创作。
了解 LlamaGen 最新功能发布、产品增强、设计更新和重要错误修复。
© 2026 LlamaGen.Ai 保留所有权利
引领 AI 漫画生成的未来



