蓝耘MaaS本地搭建:Markdown博文配图生成

文章配图

写技术文章时,配图常常很费时间——封面要概括主题,正文插图要对准段落,生成后还可能要改标题、比例或局部元素。

工具概览

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

  • 文本模型 :通过蓝耘 MaaS 接入 GLM5.3(控制台模型 ID 为 glm-5.3),负责理解文章、抽取主题与信息层级并生成视觉指令;
  • 视觉模型 :图像生成和编辑使用 Sense U1.5 Lite。

文章配图

项目用 Vue 3 + Vite 开发,是纯前端、无业务后端。界面左右两栏:左边是博文文档区(上传、编辑、预览、选段),右边是视觉创作区(选封面/插图、风格与比例)。

整体调用链路如下:

Markdown 全文或选中段落

蓝耘MaaS · GLM5.3

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

文章配图

商汤 · Sense U1.5 Lite

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

图片编辑器

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

一、模型服务选型与接入配置

文章配图

1.1 选型考虑

项目在 Codex 里开发,前端又要调文本模型啃长文,所以接入方式、模型参数清晰度与排错便利性成了主要考量。蓝耘 MaaS 提供 OpenAI 兼容接口,能复用现有请求层,只换 base_url、api_key 与模型 ID 即可。

接入前我核对了这几个配置项:

  1. 接口协议 :文档给了 OpenAI 兼容调用示例。
  2. 模型字段 :确认上下文长度能否覆盖长文,并记下实际调用 ID glm-5.3。
  3. 模型切换方式 :同类兼容接口方便后续做对照测试。
  4. 密钥与用量查询 :API Key 按项目创建,资源包页可看剩余量。

图1 蓝耘接口文档

文章配图

图2 蓝耘用量监控

图3 蓝耘模型列表

1.2 Codex 与 CC Switch 配置

日常用 Codex 偶尔额度见底,要靠 CC Switch 切到别的模型服务。CC Switch 能存多个供应商配置,在界面里切 Provider,顺手更新 Codex 的模型、端点与认证,不用手改配置文件。

Codex 用 ~/.codex/config.toml 存 Provider、模型与端点。对只给 OpenAI Chat Completions 的第三方服务,要开本地路由,把 Codex 的 Responses 请求转成上游支持的格式。

文章配图

1.3 创建项目专用API Key

在 MaaS 控制台建一个仅限本项目的 API Key,确认模型 ID 是 glm-5.3,并核对 OpenAI 兼容基础地址。独立密钥便于隔离问题,测试完可停用。

图4 创建API Key

1.4 在CC Switch中配置Provider

CC Switch 里新增自定义 Provider,关键字段如下:

文章配图

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

注意:开不开本地路由看上游接口格式,不看模型名。配完保存启用,检查 ~/.codex/config.toml 确认 model_provider、model、base_url 已更新。

图5 CC Switch配置

配置后进行连通性测试,确保接口可用。

图6 接口连通测试

文章配图

1.5 验证Codex模型切换

切完 Provider 重开 Codex,给个不怎么费额度的具体任务,比如读 Vue 项目目录结构并给状态管理建议,验证 Codex 能否响应、模型名是否来自当前 Provider、项目上下文能否读取。

图7 Codex模型验证

图8 Codex理解项目

二、项目创建与初始化

文章配图

2.1 创建项目

先用初始 Prompt 生成 Vue 3 + Vite 框架,后续多轮对话补页面布局、并行任务、图片编辑与错误重试。markdown-it 把 Markdown 转 HTML,dompurify 清理结果;生图与编辑请求统一放 src/services,页面组件只管状态与交互。

图9 输入初始Prompt

图10 追加开发Prompt

图11 项目目录结构

文章配图

2.2 页面模型配置

右上角“模型配置”弹窗填写文本模型与图像模型的服务地址、接口路径、模型 ID 和 API Key;保存后写进 localStorage,页面顶部显示配置状态。

三、核心实现

3.1 文本模型做视觉策划,而非直接生图

直接让图像模型读全文,容易只抓到表面关键词。所以把“理解文章”和“生成图片”两步拆开:上传 Markdown 后提取标题与内容块,用户选全文或段落,再由 createImagePrompt 把选中内容、用途、比例和风格发给 GLM5.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。

文章配图

3.2 统一生图和改图工作流

右侧创作区没有把“智能生成”和“图片编辑”硬拆成两个页面。生成后可点“编辑图片”进入近全屏编辑器,也能先“编辑已有图片”上传本地图。

编辑器保留图片版本列表,编辑指令只说“要改什么”,比如“背景换成浅色网格,保留中间标题和流程结构”。编辑时把当前图片、修改要求与输出比例一起发过去。

文生图请求:

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

文章配图

图片编辑请求(/images/edits):

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

前端不负责二次排字,视觉指令、原图和比例交给模型,标题与技术名词与画面一同生成。

四、实际遇到的问题与处理

4.1 本地开发CORS限制

文章配图

页面直接请求图像接口会触发跨域错误,用 Vite 代理解决:

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

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

请求层将目标地址改写为同源代理路径:

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

文章配图

修改后生图和改图接口正常。注意此代理仅开发预览有效,公开部署需服务端配置反向代理或后端代调。

4.2 并行任务提升效率

最初一次只能处理一张图,整篇配图很慢。后来改成独立任务并行:先为每条建议建任务,立刻调用 executeTask,每个任务各自维护 prompting、generating、completed、failed 状态。

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)
})

文章配图

单个任务失败不阻塞别的。当前配图量小,直接并行够用;要大规模生成,后续可加并发上限与等待队列。

4.3 失败任务保留与重试

生图接口偶尔会网络中断或服务端报错。失败后不删任务,而是置为 failed 状态并给出错误信息,同时提供“重试”和“重新下发”按钮。

async function executeTask(task) {
  task.status = 'prompting'
  task.error = ''
  try {
    task.prompt = await createImagePrompt()
    task.status = 'generating'
    task.imageUrl = (await generateImage(task.prompt)).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)
}

文章配图

重试沿用原文、类型、风格与比例,不用重填。目前整条链路重跑,后续可缓存已成功的视觉指令,只重试生图阶段。

五、效果实测

5.1 上传Markdown

上传含多章节、操作步骤与技术名词的 Markdown,程序能正确读出一级标题、字符数、文件大小与内容块数量。

图12 上传Markdown

文章配图

界面分区清晰:顶部项目名和配置入口;中间左文档右视觉控制台;底部生图任务列表。文档区可在编辑和预览间切换。

5.2 AI智能策划全篇配图

点“AI 智能策划全篇配图”后,GLM5.3 通读全文,优先处理封面、核心流程、参数对比与操作重点,给出标题、理由及对应段落。

图13 全文配图方案

确认方案后,并行创建所有任务,每项任务独立生成视觉指令并调用图像接口。

文章配图

图14 配图并行生成

任务卡片显示当前阶段和等待时间,图片完成后直接展示,并可展开查看视觉指令。

图15 配图生成完成

这次生成的封面与正文插图都能对应主题与具体段落,没退化成泛化背景图;并行任务里个别失败也不影响其余。

5.3 图片编辑

点击任务卡片中的“微调”或另行上传图片,用自然语言说明修改要求,无需重写整条生图提示词。

图16 图片编辑窗口

例如要求将画面右侧的平板改成两台(安卓平板和iPad),同时保留标题、电脑主体、配色和整体构图。

图17 图片编辑结果

每次编辑生成新版本,原图保留,版本列表可来回切换比较。

5.4 配图回填与导出

图片可逐张插到对应段落,或点“一键插入到文章”统一处理。封面放标题附近,正文插图按任务保存的段落定位,匹配不到就追加到文末。

图18 插入并导出文章

导出文件在原名后加“配图版”,不覆盖原始 Markdown;图片集中放进 images 子目录。

图19 导出文章预览

重新打开导出的文章,封面和正文插图位置正确,原标题、段落和代码块未被改动。

六、总结与后续方向

本项目用蓝耘 MaaS 接入 GLM5.3 作文本模型,既在开发阶段(Codex 协作)也在运行阶段(把全文/段落转成结构化图像指令)使用;图像生成与编辑交给 Sense U1.5 Lite。两组接口独立,便于分别排查。

当前版本是一个可运行的 MVP,后续计划包括:用后端代理保护长期密钥、持久化生成记录与提示词对比、以及用调用记录比较不同文本模型在“长文理解→视觉指令”任务上的表现差异。

  • 使用后端代理保护长期密钥;
  • 持久化生成记录、提示词对比和调用日志;
  • 利用调用记录比较不同文本模型在“长文理解→视觉指令”任务中的表现差异。
0

评论0

请先
显示验证码
没有账号?注册  忘记密码?