欢迎光临
我们一直在努力

使用蓝耘MaaS,打造一个Markdown博文配图生成器的实现记录:模型接入、并行任务与CORS处理

写技术文章时,我花在配图上的时间有时并不比正文少:封面要概括主题,正文插图要对应段落,生成后还可能需要调整标题、比例或局部元素。过去的做法是逐段整理提示词,再分别生成和修改图片,这次我尝试把这些步骤放进一个工具里。

这个工具叫「博文视觉生成器」。上传 Markdown 文档后,可以运行“AI 智能规划配图”,也可以单独选择全文或某个段落,生成封面或正文插图。

这个项目的文本模型使用 GLM5.3,通过蓝耘元生代接入;视觉生成和图片编辑使用 Sense U1.5 Lite。本文叙述模型名称时统一写作“GLM5.3”,在配置和真实请求中则使用控制台给出的模型 ID:glm-5.3。

一、项目结果与调用链路

《博文视觉生成器》会先让大模型理解全文并生成配图计划,再创建若干生图任务。每个任务先调用语言模型生成视觉指令,再调用 Sense U1.5 Lite 生成封面或插图。对于已生成或已有的图片,还可以继续微调。

这次使用 Codex 开发项目,并通过 CC Switch 管理和切换模型服务。

项目最终效果
图1 项目最终效果

项目使用 Vue 3 + Vite 开发,是一个不含业务后端的纯前端应用。界面分成左右两块:左边是博文文档,支持上传、编辑、预览以及按内容块选择段落;右边是视觉创作区,可选择封面或插图、视觉风格与画面比例。

整个调用链路如下:

#mermaid-svg-CpFPa67HrQmhVu1L{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CpFPa67HrQmhVu1L .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CpFPa67HrQmhVu1L .error-icon{fill:#552222;}#mermaid-svg-CpFPa67HrQmhVu1L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CpFPa67HrQmhVu1L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CpFPa67HrQmhVu1L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CpFPa67HrQmhVu1L .marker.cross{stroke:#333333;}#mermaid-svg-CpFPa67HrQmhVu1L svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CpFPa67HrQmhVu1L p{margin:0;}#mermaid-svg-CpFPa67HrQmhVu1L .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-CpFPa67HrQmhVu1L .cluster-label text{fill:#333;}#mermaid-svg-CpFPa67HrQmhVu1L .cluster-label span{color:#333;}#mermaid-svg-CpFPa67HrQmhVu1L .cluster-label span p{background-color:transparent;}#mermaid-svg-CpFPa67HrQmhVu1L .label text,#mermaid-svg-CpFPa67HrQmhVu1L span{fill:#333;color:#333;}#mermaid-svg-CpFPa67HrQmhVu1L .node rect,#mermaid-svg-CpFPa67HrQmhVu1L .node circle,#mermaid-svg-CpFPa67HrQmhVu1L .node ellipse,#mermaid-svg-CpFPa67HrQmhVu1L .node polygon,#mermaid-svg-CpFPa67HrQmhVu1L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CpFPa67HrQmhVu1L .rough-node .label text,#mermaid-svg-CpFPa67HrQmhVu1L .node .label text,#mermaid-svg-CpFPa67HrQmhVu1L .image-shape .label,#mermaid-svg-CpFPa67HrQmhVu1L .icon-shape .label{text-anchor:middle;}#mermaid-svg-CpFPa67HrQmhVu1L .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CpFPa67HrQmhVu1L .rough-node .label,#mermaid-svg-CpFPa67HrQmhVu1L .node .label,#mermaid-svg-CpFPa67HrQmhVu1L .image-shape .label,#mermaid-svg-CpFPa67HrQmhVu1L .icon-shape .label{text-align:center;}#mermaid-svg-CpFPa67HrQmhVu1L .node.clickable{cursor:pointer;}#mermaid-svg-CpFPa67HrQmhVu1L .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CpFPa67HrQmhVu1L .arrowheadPath{fill:#333333;}#mermaid-svg-CpFPa67HrQmhVu1L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CpFPa67HrQmhVu1L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CpFPa67HrQmhVu1L .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CpFPa67HrQmhVu1L .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CpFPa67HrQmhVu1L .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CpFPa67HrQmhVu1L .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CpFPa67HrQmhVu1L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CpFPa67HrQmhVu1L .cluster text{fill:#333;}#mermaid-svg-CpFPa67HrQmhVu1L .cluster span{color:#333;}#mermaid-svg-CpFPa67HrQmhVu1L div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CpFPa67HrQmhVu1L .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CpFPa67HrQmhVu1L rect.text{fill:none;stroke-width:0;}#mermaid-svg-CpFPa67HrQmhVu1L .icon-shape,#mermaid-svg-CpFPa67HrQmhVu1L .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CpFPa67HrQmhVu1L .icon-shape p,#mermaid-svg-CpFPa67HrQmhVu1L .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CpFPa67HrQmhVu1L .icon-shape .label rect,#mermaid-svg-CpFPa67HrQmhVu1L .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CpFPa67HrQmhVu1L .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CpFPa67HrQmhVu1L .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CpFPa67HrQmhVu1L :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Markdown 全文或选中段落

蓝耘元生代 · GLM5.3

理解内容、抽取主题与信息层级生成视觉指令

商汤 · Sense U1.5 Lite

根据视觉指令生成封面或正文插图

图片编辑器

上传或复用生成结果继续通过自然语言改图

二、开发环境与 CC Switch 配置

2.1 模型服务的选型条件

选择模型服务时,我先核对接入方式、模型字段和用量查询方式,模型列表则用于确认后续是否方便做对照测试。

这个项目既要在 Codex 中开发,也要在前端调用文本模型分析长文章,因此接口是否容易接入、模型参数是否清楚,会直接影响开发和排错效率。

本项目接入蓝耘 MaaS。开始编码前,我检查了以下四项配置:

  • 接口协议
    接口文档提供了 OpenAI 兼容调用示例。现有请求层可以保留,接入时需要替换 base_url、api_key 和模型 ID。
  • 蓝耘接口文档
    图2 蓝耘接口文档

  • 模型字段
    模型列表给出了模型类型、上下文长度、供应商和 API 入口。我先确认上下文长度是否能覆盖长文任务,再复制控制台中的实际调用 ID。
  • 模型切换方式
    Kimi、Qwen 等文本模型可以使用同一类兼容接口。后续做对照测试时,主要调整模型 ID 和提示词,不需要改动业务流程。
  • 密钥与用量查询
    API Key 可以按项目创建,资源包页面记录剩余量和使用量。遇到鉴权或额度问题时,我会先检查这两个入口。
  • 蓝耘用量监控
    图3 蓝耘用量监控

    上述接口和模型字段以 MaaS 控制台中的接口文档与模型列表页面为准。

    本次测试使用 GLM5.3,任务包括编码协作、Markdown 长文理解和结构化视觉策划。发起请求时,模型字段填写 glm-5.3。

    蓝耘模型列表
    图4 蓝耘模型列表

    2.2 Codex 与 CC Switch 的配置关系

    我日常经常使用 Codex 处理代码,但使用额度偶尔会见底,因此会通过 CC Switch 切换其他模型服务。

    CC Switch 可以保存多个供应商配置,并在界面中切换 Provider。选择配置后,它会更新 Codex 当前使用的模型、端点和认证信息,因此切换时不用手工反复编辑配置文件。

    CC Switch 的核心是维护 Codex 配置。Codex 使用 ~/.codex/config.toml 保存 Provider、模型与端点设置,API Key 则由相应的认证配置管理。对于只提供 OpenAI Chat Completions 接口的第三方服务,需要启用本地路由,把 Codex 的 Responses 请求转换为上游支持的格式。

    2.3 创建项目专用 API Key

    本次测试在 MaaS 控制台的 API Key 页面创建密钥。

  • 创建一个只给本项目使用的 API Key。
  • 在模型列表确认模型 ID。日常称为 GLM5.3,控制台中的实际调用 ID 是 glm-5.3。两者含义相同,但配置时必须使用后者。
  • 打开接口说明,确认 OpenAI 兼容的基础地址和路径。不要把控制台网页地址当作 API Base URL。
  • 本次测试为项目单独创建 API Key,测试结束后可以停用。这样处理泄露、401 或额度异常时,不会牵连其他项目的密钥。

    创建API Key
    图5 创建API Key

    2.4 在 CC Switch 中配置 GLM5.3 Provider

    打开 CC Switch,切换到 Codex 页面,点击新增 Provider,选择“自定义”。需要填的字段如下:

    CC Switch 字段应填写的内容说明
    Provider 名称 蓝耘-GLM5.3 仅用于自己识别
    Base URL 控制台提供的 OpenAI 兼容基础地址 不是控制台网页地址
    API Key 刚创建的项目专用 Key 不提交到仓库,不放截图里
    模型 glm-5.3 控制台显示的实际调用 ID
    接口格式 按接口文档选择 如果上游只有 Chat Completions,需要打开本地路由

    这里有一个容易混淆的点:是否需要打开 CC Switch 的本地路由,不取决于模型叫不叫 GLM,而取决于上游接口格式。

    • 如果端点原生支持 Codex 所用的 Responses 兼容格式,可以直接按 Provider 配置使用。
    • 如果端点只提供 OpenAI Chat Completions 兼容接口,则在 CC Switch 的“设置 → 路由”中启动本地路由,并在可路由应用中启用 Codex。此时本地路由负责协议转换。

    我的配置顺序如下:

  • 在 Codex 页点击新增 Provider,选择“自定义”。
  • 填入基础地址、专用 API Key,并将模型填写为 glm-5.3。
  • 根据接口文档选择协议格式;若只有 Chat Completions,打开本地路由并勾选 Codex。
  • 保存后回到 Provider 列表,点击“启用”,再重新打开 Codex。
  • 不要手工把真实 Key 写进文章里的 config.toml 示例。CC Switch 切换后可以检查当前的 ~/.codex/config.toml:model_provider、model 和 base_url 应该已经变成刚启用的 Provider。密钥不要贴到文章、终端录屏或 Git 提交记录里。

    CC Switch配置
    图6 CC Switch配置

    保存配置后,我先进行接口连通性测试。

    接口连通测试
    图7 接口连通测试

    2.5 用 Codex 验证开发模型是否切换成功

    Codex模型验证
    图8 Codex模型验证

    切换 Provider 后重新打开 Codex,给一个足够具体但不消耗太多额度的任务,例如:

    阅读当前 Vue 项目的目录结构,说明将 Markdown 文档拆成“全文”和“段落”两种生成范围时,状态应该放在哪个组件,并给出修改建议。不要直接改文件。

    这一步不评测模型分数,只验证三个结果:Codex 能否响应、模型名是否来自当前 Provider,以及项目上下文能否被读取。

    Codex理解项目
    图9 Codex理解项目

    三、创建项目

    3.1 创建并启动项目

    输入初始Prompt
    图10 输入初始Prompt

    这个项目从零开始开发。我先用一条基础 Prompt 生成项目框架,再通过多轮对话逐步补充功能。

    最初的 Prompt 只描述整体方向,后续再补充页面布局、并行任务、图片编辑和错误重试等需求。这也是我常用的开发方式:先确认大方向,再逐项微调。

    追加开发Prompt
    图11 追加开发Prompt

    项目使用 Vue 3 + Vite 开发。markdown-it 负责把文档转换为 HTML,dompurify 负责清理渲染结果,避免将上传 Markdown 中不可信的 HTML 直接插入页面。图片生成和编辑请求统一放在 src/services,页面组件只处理状态和交互。

    工程目录为 md-visual-studio。

    项目目录结构
    图12 项目目录结构

    3.2 在页面中配置模型

    项目启动后,点击右上角“模型配置”打开设置弹窗。文本模型部分填写服务地址、接口路径、模型 ID glm-5.3 和项目专用 API Key;图像模型部分填写服务地址、生图路径 /images/generations、编辑路径 /images/edits、模型 ID 和访问密钥。

    保存后,配置写入当前浏览器的本地存储,页面顶部会同步显示两组接口的配置状态。整个过程都在页面弹窗中完成,不需要修改项目配置文件。

    四、核心实现

    4.1 让 GLM5.3 先做视觉策划,而不是直接让生图模型读全文

    文章往往很长,直接整篇交给图像模型,得到的画面容易只抓住几个表面关键词。我的处理方式是把“理解文章”和“生成图片”拆开。

    上传 Markdown 后,项目会提取标题和内容块;用户可以选择全文,也可以点击某个段落。随后 createImagePrompt 把选中的文本、图片用途、画面比例和风格发给语言模型。请求中的 model 来自页面配置,本项目实际发送的是 glm-5.3。

    核心调用逻辑如下,省略了错误处理:

    const payload = {
    model,
    messages: [
    { role: 'system', content: visualDirectorPrompt },
    {
    role: 'user',
    content: `请根据以下${scopeName}生成一条${targetName}指令。

    文章标题:${documentTitle}
    画面比例:
    ${ratio}
    视觉风格:
    ${style}

    — 内容开始 —
    ${source}
    — 内容结束 —
    `,
    },
    ],
    temperature: 0.45,
    stream: false,
    }

    visualDirectorPrompt 里我明确约束了七件事:

    • 封面概括全文,插图解释段落;
    • 图片里的标题、数字和技术名词由图像模型直接生成;
    • 需要出现的文字用中文引号逐字标出;
    • 只保留一个主标题和不超过三项核心信息;
    • 描述构图、层级、配色和留白;
    • 不添加原文中没有的事实和结论;
    • 最终只返回一条图像指令,而不是解释性文字或 JSON。

    在这一步中,GLM5.3 把原文整理成可传给图像模型的视觉简报。输出内容包含画面主题、信息层级、构图和文字约束,而不是一条宽泛的“科技风图片”要求。

    4.2 把 U1.5 Lite 的生图和改图收敛成同一个图片工作流

    右侧创作区没有把“智能生成”和“图片编辑”硬切成两个页面。用户生成图片后,可以点击“编辑图片”直接进入近全屏编辑器;也可以先点击“编辑已有图片”上传一张本地图片。

    编辑器处理了三个细节:

  • 保留当前图片版本列表,方便回看原图和每次编辑结果。
  • 编辑指令只要求描述“要改什么”,例如“把背景改成浅色网格,保留中间标题和流程结构”。
  • 编辑时把当前图片、修改要求和输出比例一起发送,未修改部分是否保留则直接写进本次编辑 Prompt。
  • 文生图请求使用顶层 prompt,核心结构如下:

    const payload = {
    model,
    prompt,
    n: 1,
    size: editSizeByRatio[ratio] || 'auto',
    output_format: 'png',
    response_format: 'b64_json',
    watermark: true,
    prompt_extend: true,
    }

    图片编辑走 /images/edits,原图放进 images,自然语言修改要求仍放在顶层 prompt:

    const payload = {
    model,
    images: [{ image_url: imageUrl }],
    prompt: instruction,
    n: 1,
    size: editSizeByRatio[ratio] || 'auto',
    response_format: 'b64_json',
    watermark: true,
    prompt_extend: true,
    }

    前端不负责给图片二次排字,只把视觉指令、原图和比例交给模型。图片中的标题和技术名词与画面一起生成,避免浏览器叠字与原图风格不一致。

    五、实际问题与处理方式

    页面做出来后,真正花时间排查的是三个问题:浏览器跨域、批量任务并发,以及生图接口偶发失败。它们不算复杂,但不处理好,工具就只能偶尔生成一张图,很难连续使用。

    5.1 本地页面直连接口时触发 CORS 限制

    一开始,页面从 http://localhost:5173 直接请求图像接口,浏览器控制台随即报出跨域错误。这并不是 localhost 不能调用 AI,而是浏览器的同源策略在起作用:页面和模型接口的协议、域名或端口不同,接口又没有放行当前来源,请求还没到业务处理阶段就被浏览器拦下了。

    项目用的是 Vite,开发阶段可以加一层同源代理。浏览器请求本站的 /api/sensenova,Vite 开发服务器再把它转发给目标接口:

    const sensenovaProxy = {
    target: 'https://token.sensenova.cn',
    changeOrigin: true,
    rewrite: (path) => path.replace(/^\\/api\\/sensenova/, ''),
    }

    export default defineConfig({
    server: {
    proxy: {
    '/api/sensenova': sensenovaProxy,
    },
    },
    })

    在请求层中,如果识别到目标服务地址,就把浏览器实际请求地址改写为同源代理路径:

    if (target.origin === 'https://token.sensenova.cn') {
    return `/api/sensenova${target.pathname}${target.search}`
    }

    修改后,浏览器发出的请求与本地页面同源,跨域转发由 Vite 完成。重新测试时,生图和改图接口均能返回结果。

    这套代理只在 Vite 的开发和预览服务中生效,打包出来的静态文件不会自带代理能力。要公开部署,服务器仍需配置同名反向代理,或者由后端代调模型接口。页面保存在 localStorage 中的 API Key 也无法真正隐藏,因此这个版本只适合本机测试或受控演示。

    5.2 全篇配图从逐个等待改为并行任务

    第一版一次只能处理一张图,前一项结束后才会开始下一项。“AI 智能策划全篇配图”通常会产生一张封面和多张正文插图,串行执行时,几次模型调用的耗时会叠在一起,等起来很慢。

    我又给 Codex 中使用的 GLM5.3 补了一条 Prompt,大意是:

    将智能配图方案改成独立任务并行执行。应用方案后立即创建全部任务卡片,
    每个任务分别维护提示词生成、生图、完成和失败状态;单个任务失败不能阻塞其他任务。

    实现时没有在循环里逐项 await,而是先为每条建议创建任务,再立即调用 executeTask。简化后的代码如下:

    taskList.forEach((item) => {
    const newTask = {
    id: `task-${Date.now()}${Math.random().toString(36).slice(2, 7)}`,
    title: item.title,
    status: 'pending',
    // 其余字段保存目标段落、图片类型、风格和比例
    }
    tasks.value.unshift(newTask)
    executeTask(newTask)
    })

    每张图单独维护 prompting、generating、completed 和 failed 状态。哪张先完成就先显示;其中一张报错,其他任务照常运行。

    智能策划目前只推荐少量关键配图,直接并行已经够用。如果以后允许一次生成几十张图,还要加并发上限和等待队列,避免短时间内发出太多请求。

    5.3 接口偶发报错时保留任务并支持重试

    生图接口并不总能一次成功。测试时我碰到过网络中断、服务端报错、响应超时,也遇到过请求返回成功但程序没有解析出图片的情况。如果失败后直接移除卡片,错误原因看不到,段落、比例和风格也得重新选择。

    所以我没有删除失败任务,而是把状态改成 failed,将错误信息留在卡片上,并显示“重试”和“重新下发”按钮:

    async function executeTask(task) {
    task.status = 'prompting'
    task.error = ''

    try {
    task.prompt = await createImagePrompt(/* 当前任务参数 */)
    task.status = 'generating'
    task.imageUrl = (await generateImage(/* 当前视觉指令 */)).imageUrl
    task.status = 'completed'
    } catch (error) {
    task.status = 'failed'
    task.error = toErrorMessage(error)
    }
    }

    function retryTask(taskId) {
    const task = tasks.value.find((item) => item.id === taskId)
    if (task) executeTask(task)
    }

    点击重试后,文章内容、图片类型、风格和比例都会沿用原任务,不用再填一遍。现在采用的是整条链路重跑,也就是重新生成视觉指令,再调用生图接口。这样两个阶段的错误都能处理。后面如果要减少重复调用,可以缓存已经生成成功的视觉指令,只重试生图接口。

    六、效果实测

    6.1 上传 Markdown 文章

    测试时,我上传了一篇包含多个章节、操作步骤和技术名词的 Markdown 文章。程序读取一级标题,并统计字符数、文件大小和内容块数量,我用这些信息确认文件是否读取完整。

    上传Markdown

    图13 上传Markdown

    界面分为上、中、下三部分。顶部放项目名称、模型状态和配置入口;中间左边是 Markdown 文档,右边是视觉生成控制台;底部集中显示生图任务。文档区可以在“编辑”和“预览”之间切换,改完原文后能立即检查标题、列表和代码块的渲染结果。

    这篇文章的标题和内容块都识别正确。选择“文章全文”可以生成封面;切换到“选中段落”,再在预览区点选一个或多个内容块,可以生成对应的正文插图。原文、参数和结果都显示在当前页面,不需要在不同工具之间复制段落。

    6.2 AI 智能策划全篇配图

    点击“AI 智能策划全篇配图”后,GLM5.3 会通读文章,挑出适合配图的位置。它不会给每段都放一张图,而是优先处理封面、核心流程、参数对比和操作重点,同时给出标题、理由及对应段落。

    全文配图方案

    图14 全文配图方案

    确认方案后,页面一次创建所有任务。每项任务都记下标题、用途、比例、风格和目标段落,然后调用语言模型生成视觉指令,再交给图像接口处理。几张图会同时生成,不再一张接一张地等待。

    配图并行生成

    图15 配图并行生成

    任务卡片会显示当前阶段和等待时间。图片完成后直接出现在卡片里,还可以展开查看语言模型生成的视觉指令。如果结果偏离原文,可以据此判断问题出在文章理解还是生图环节。

    配图生成完成

    图16 配图生成完成

    这次生成的封面抓住了文章主题,正文插图也能对应到所选段落,没有变成泛化的科技背景图。并行任务中即使有一项失败,其他任务仍会继续,已完成的图片也会保留。

    6.3 图片编辑

    图片生成后,可以点击任务卡片中的“微调”继续编辑,也可以另行上传一张图片。编辑时只要用自然语言说明改哪里,不用重写整条生图提示词。

    图片编辑窗口

    图17 图片编辑窗口

    这次我要求模型把画面右侧的一台平板改成两台,分别表示安卓平板和苹果 iPad,同时保留标题、电脑主体、配色和整体构图。前端没有用 Canvas 覆盖元素,也没有在本地重新排字,只负责把原图和修改要求交给 Sense U1.5 Lite。

    图片编辑结果

    图18 图片编辑结果

    每次编辑都会生成一个新版本,原图仍然保留。左侧的版本列表可以来回切换,用于比较修改前后的差异,再选择下载或继续调整。主体已经符合要求、只需替换设备或局部文字时,我会使用编辑接口,而不是重新生成整张图片。

    6.4 配图回填与文章导出

    图片完成后,可以逐张插入对应段落,也可以点击“一键插入到文章”统一处理。封面放在文章标题附近,正文插图根据任务中保存的原始段落定位;匹配不到时则追加到文末。

    插入并导出文章

    图19 插入并导出文章

    导出文件会在原名称后加上“配图版”,不会覆盖原始 Markdown。整理投稿文件时,我把 Markdown 和下载的原图放在同一目录下,图片集中放进 images 子目录:

    1-单测–开学季平板远程控制电脑实测:ToDesk让iPad安卓平板秒变生产力工具-配图版
    ├── 1-单测–开学季平板远程控制电脑实测:ToDesk让iPad安卓平板秒变生产力工具-配图版.md
    └── images
    ├── iPad远程控制电脑:ToDesk实测,平板秒变生产力工具-1788140637816.png
    ├── 出门前检查:电脑状态与安全设置-1788140635804.png
    ├── 二次微调修图版本-1788140632987.png
    ├── 双指缩放:解决Windows小按钮操作难题-1788140636451.png
    ├── 远程打开WPS:原文件与格式完整保留-1788140635165.png
    └── 远程桌面连接:iPad操作Windows电脑-1788140637078.png

    导出文章预览

    图20 导出文章预览

    重新打开导出的文章,封面和正文插图都在预期位置,原来的标题、段落和代码块也没有被改乱。Markdown 上传、全篇配图、并行生图、自然语言改图和文章导出这几步都跑通了。

    七、测试结论与限制

    项目中的文本模型通过蓝耘 MaaS 接入,对应两个调用环节:

    • 开发阶段:我通过 Codex + CC Switch 完成 Vue 组件拆分、接口边界设计和配置排错。
    • 运行阶段:GLM5.3 把全文或段落转换成结构化、受原文约束的图像指令,再把任务交给 U1.5 Lite。

    U1.5 Lite 负责图像生成与编辑。文本理解和视觉生成使用两组独立接口,出现问题时可以分别查看语言模型输出和生图接口响应。

    目前完成的是一个可运行的 MVP,仍有继续完善的空间。下一步我准备使用后端代理保护长期密钥,并加入持久化生成记录、提示词对比和调用日志。

    调用记录也能用于比较不同文本模型在“长文理解 → 视觉指令”任务中的实际差异。

    赞(0)
    未经允许不得转载:171主机测评 » 使用蓝耘MaaS,打造一个Markdown博文配图生成器的实现记录:模型接入、并行任务与CORS处理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址