返回博客

搭一个 AI 漫画制作网站:Agent 自动分镜排版,中途接 4sapi 跑通文生图

人工智能8050
搭一个 AI 漫画制作网站:Agent 自动分镜排版,中途接 4sapi 跑通文生图

搭一个 AI 漫画制作网站:Agent 自动分镜排版,中途接 4sapi 跑通文生图

输入一个故事,输出一整页排好版的漫画。这篇讲我怎么用 Agent 把"编剧—分镜—画图—排版"这条链路自动化,以及中途怎么接入 4sapi 中转 API 把文生图跑通。

写在前面

上一篇我写了 AI 漫剧(动态漫短剧)网站,这篇是它的姊妹篇——静态漫画制作网站

两者链路像,但难点不同:漫剧难在"让画面动起来 + 配音剪辑",漫画难在**"分镜排版" + "角色一致性"**——同一个角色在几十个格子里得长得像同一个人,而且每一页的格子布局还得讲究阅读节奏。

这篇我同样用"Agent 编排 + 中转 API 统一接入"的思路来搭,重点讲漫画特有的那几个坎怎么过,以及中途接入 4sapi.com 这个中转网关把文生图环节跑通的具体步骤。


一、先理清楚:一页漫画是怎么"生产"出来的

和漫剧类似,漫画生产也是一条流水线,但环节不太一样:

① 剧情脚本    故事梗概 → 分章节/分页的剧情文本

② 分镜脚本    剧情 → 每页几个格子,每格画什么、谁说什么

③ 角色设定    生成统一的角色参考图(保证一致性的基准)

④ 文生图      每个格子的画面描述 → 漫画分格图

⑤ 排版合成    把分格图 + 对话气泡,按布局拼成成品页

其中①②靠大语言模型,③④靠文生图模型,⑤靠图像合成(代码排版 + 加气泡)。

和漫剧一样的老问题:这些模型分散在不同厂商。挨个对接官方 API 太累,所以我们中途引入一个中转网关,用一个 Key、一套格式把它们全接进来


二、架构:四个 Agent + 一个排版引擎 + 统一网关

                 ┌────────────────────────┐
  用户输入故事 ─→ │   Orchestrator 总调度    │
                 └──────────┬─────────────┘
        ┌──────────┬────────┼────────┬──────────┐
        ↓          ↓        ↓        ↓          ↓
   [脚本Agent] [分镜Agent] [角色Agent] [画图Agent]  │
        └──────────┴────────┼────────┴──────────┘

                   ┌────────────────┐    ┌──────────────┐
                   │ 统一网关(4sapi) │    │  排版引擎     │
                   └────────────────┘    │ (分格+气泡)   │
                                         └──────────────┘

设计要点和漫剧那套一脉相承:所有模型调用统一走 4sapi 中转网关,业务层只跟一个 OpenAI 兼容接口打交道。区别是漫画多了一个纯代码实现的排版引擎(不调模型,用 Pillow/Canvas 拼图加气泡)。

我选 4sapi.com 当网关的理由不变:兼容 OpenAI 格式、文本和图像模型都能调、一个 Key 管到底、计费集中好对账。


三、中途接入 4sapi:把文生图网关接通

漫画这套系统最吃接口的是文生图环节,所以网关接通是第一优先级。还是三步走。

第一步:拿 Key、记 BaseURL

在 4sapi.com 后台创建令牌,记下 Key 和接口地址,放进环境变量,别硬编码:

bash
# .env
FOURSAPI_BASE_URL=https://4sapi.com/v1
FOURSAPI_KEY=sk-你的令牌

第二步:封装统一客户端

4sapi 兼容 OpenAI 格式,直接用 openai SDK 把 base_url 指过去:

python
# gateway.py
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["FOURSAPI_KEY"],
    base_url=os.environ["FOURSAPI_BASE_URL"],   # 指向中转网关
)

def gen_image(model, prompt, size="1024x1024", **extra):
    """文生图统一入口,所有画图都走这里"""
    resp = client.images.generate(
        model=model, prompt=prompt, size=size, extra_body=extra
    )
    return resp.data[0].url

第三步:发一张测试图验证连通

python
if __name__ == "__main__":
    url = gen_image("flux-dev", "a cute cat, comic style")
    print("生成成功:", url)   # 能打印出图片 URL 即网关接通

能拿到图片 URL,网关就通了。接入的全部工作量,就是「换个 base_url + 配个 Key」——一次配好,文本和图像都能调。


四、逐环节实现:漫画特有的难点怎么破

4.1 脚本 Agent —— 故事变分页剧情

python
def script_agent(story):
    prompt = f"""你是漫画编剧。把下面的故事改写成漫画剧情,按页划分,
每页标注关键情节和情绪节奏。故事:{story}"""
    return chat(model="gpt-4o",
                messages=[{"role": "user", "content": prompt}],
                temperature=0.8)

4.2 分镜 Agent —— 决定每页"几个格子、怎么排"

这是漫画区别于漫剧的核心。漫画的分镜不光要描述画面,还要规划格子的布局(panel layout)和阅读顺序——大格子给高潮,小格子推进节奏。我让模型直接输出结构化布局:

python
def panel_agent(page_script):
    prompt = f"""把这一页剧情拆成漫画分镜,输出 JSON。
字段:page_layout(布局,如 "2x2"/"1大2小"),
panels[]: 每格含 visual_prompt(画面英文描述),
dialogue(对白), bubble_pos(气泡位置:top/bottom/left/right)。
剧情:{page_script}"""
    raw = chat(model="gpt-4o",
               messages=[{"role": "user", "content": prompt}],
               response_format={"type": "json_object"})
    return json.loads(raw)

4.3 角色 Agent —— 先定一张"角色基准图"

漫画的角色一致性比漫剧更难,因为格子更多。我的解法是先生成一张角色参考图,后面所有格子都基于它做图生图,而不是每次重新文生图:

python
def role_agent(role_desc):
    # 先生成一张高质量的角色立绘当"基准"
    ref_url = gen_image(
        model="flux-dev",
        prompt=f"{role_desc}, full body, character reference sheet, comic style, white background",
        size="768x1024",
    )
    return ref_url   # 这张图后面反复复用

4.4 画图 Agent —— 基于基准图画每一格

关键在于用**图生图(image-to-image)**而非纯文生图,把角色基准图作为参考传进去,角色长相就能稳住:

python
def panel_image_agent(panel, role_ref_url, style="japanese manga"):
    full_prompt = f"{panel['visual_prompt']}, {style}, consistent character"
    # 走网关的图生图接口,传入角色参考图
    resp = client.images.edit(
        model="flux-dev",
        image=role_ref_url,            # 角色基准图,锁住一致性
        prompt=full_prompt,
        extra_body={"strength": 0.6},  # 控制对参考图的保留程度
    )
    return resp.data[0].url

一致性三板斧:① 统一角色基准图;② 图生图而非文生图;③ 固定画风描述词。三个一起上,角色基本不会跑偏。


五、排版引擎:把分格图拼成一页漫画

这一步不调任何模型,纯靠代码。用 Pillow 按分镜给的布局把格子拼起来,再贴上对话气泡:

python
from PIL import Image, ImageDraw, ImageFont

def compose_page(panels_with_img, layout):
    page = Image.new("RGB", (1240, 1754), "white")  # A4 比例画布
    boxes = layout_to_boxes(layout, page.size)      # 布局→每格坐标

    for panel, box in zip(panels_with_img, boxes):
        img = load(panel["img_url"]).resize(box.size)
        page.paste(img, box.pos)                     # 贴分格图
        draw_bubble(page, panel["dialogue"],         # 画对话气泡
                    pos=panel["bubble_pos"], box=box)
    return page

def draw_bubble(page, text, pos, box):
    """在指定格子的指定位置画一个对话气泡 + 文字"""
    draw = ImageDraw.Draw(page)
    # 简化:画个圆角白底气泡,写上对白
    bubble_box = calc_bubble_box(box, pos, text)
    draw.rounded_rectangle(bubble_box, radius=20, fill="white", outline="black")
    draw.text(bubble_box.text_pos, text, fill="black", font=load_font())

排版引擎是漫画系统里最"工程"的一块——没有 AI 的魔法,但决定了成品看起来专不专业。布局映射、气泡避让、文字换行,这些细节抠到位,出来的页面才像正经漫画而不是图片拼贴。


六、串起来:从一个故事到一整本漫画

python
def make_comic(story):
    pages_script = script_agent(story)            # ① 分页剧情
    role_ref = role_agent("少年, 黑发, 校服")      # ③ 先定角色基准图

    comic_pages = []
    for page_script in pages_script:
        layout = panel_agent(page_script)         # ② 本页分镜布局
        panels = []
        for panel in layout["panels"]:
            # ④ 基于角色基准图画每一格
            img = panel_image_agent(panel, role_ref)
            panels.append({**panel, "img_url": img})
        # ⑤ 排版合成本页
        page_img = compose_page(panels, layout["page_layout"])
        comic_pages.append(page_img)

    return export_pdf(comic_pages)                # 导出整本 PDF

同样地,所有模型调用——脚本、分镜、角色、画图——全部走 4sapi 一个网关、一个 Key。排版那步是纯代码,不烧接口。


七、前端:上传故事,预览成品

jsx
function ComicMaker() {
  const [story, setStory] = useState("");
  const [pages, setPages] = useState([]);

  async function generate() {
    const res = await fetch("/api/comic", {
      method: "POST",
      body: JSON.stringify({ story }),
    });
    const data = await res.json();
    setPages(data.pages);   // 渲染预览
  }

  return (
    <div>
      <textarea value={story} onChange={e => setStory(e.target.value)}
                placeholder="贴入你的故事,AI 帮你画成漫画" />
      <button onClick={generate}>生成漫画</button>
      <div className="preview">
        {pages.map((p, i) => <img key={i} src={p} alt={`第${i+1}页`} />)}
      </div>
    </div>
  );
}

同样的安全提醒:/api/comic 背后每次都在烧文生图算力,上线前务必加鉴权 + 频率限制,并给单用户设每日生成上限,防止被恶意刷爆。


八、几条实战经验

  1. 角色基准图是一致性的地基,一定要先把它生成好、让用户确认满意了,再批量画格子。基准图歪了,后面全废。
  2. 分镜的布局字段越结构化越好"2x2""1大2小" 这种枚举值,比让模型自由描述布局好解析得多。
  3. 气泡排版别交给 AI,用代码精确控制。我试过让模型连气泡一起画进图里,结果文字经常糊、位置乱。分离开——AI 画画面,代码加气泡。
  4. 画图批量并发,但要限流。一页好几格、一本几十页,串行太慢;并发又容易触发网关限流。我的做法是控制在并发 3-5,配合重试。
  5. 画风描述词做成预设。给用户几个预设画风(日漫/美漫/水彩/Q版),背后对应固定的 style prompt,比让用户自己描述靠谱得多。

写在最后

漫画系统和漫剧系统是一对姊妹——骨架都是"Agent 编排 + 中转网关统一接入",区别只在各自的难点:漫剧难在动效配音,漫画难在分镜排版和角色一致性。

把模型能力收敛到一个网关后面,把工程难点(排版、一致性)留在自己代码里——这套分工,是我做这类 AI 内容生产工具最核心的心得。

中途接入 4sapi 这一步,依然是整套方案里最"四两拨千斤"的地方:一个 base_url、一个 Key,就把文本和图像两类模型一次性接齐了,剩下的精力全可以花在真正的难点上。

两篇姊妹篇到这就齐了。如果你打算动手,建议先跑通漫画(静态、链路短、好调试),再上漫剧(多了视频和配音环节)。


本文代码为说明思路的简化示意,模型名称与接口字段请以 4sapi.com 官方文档为准。涉及付费接口,务必做好用量控制与接口鉴权。

标签:人工智能漫画代理人4sapi文生图分镜排版

推荐阅读

探索更多前沿洞察与行业干货。