写技术文章时,最容易拖慢进度的往往不是正文,而是配图:封面需要概括主题,正文插图需要解释段落,生成后还经常要调整标题、比例或局部元素。现在用 AI 生图已经方便很多,但还缺少一款能根据整篇文章自动规划配图的工具。
所以我做了一个「博文视觉生成器」:上传 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 开发,是一个不含业务后端的纯前端应用。界面分成左右两块:左边是博文文档,支持上传、编辑、预览以及按内容块选择段落;右边是视觉创作区,可选择封面或插图、视觉风格与画面比例。
整个调用链路如下:
二、开发环境与 CC Switch 配置
2.1 为什么选择蓝耘元生代
选择模型服务时,我先看接入方式、模型信息和用量管理,模型数量也是参考因素之一。
这个项目既要在 Codex 中开发,也要在前端调用文本模型分析长文章,因此接口是否容易接入、模型参数是否清楚,会直接影响开发和排错效率。
我最终选择蓝耘 MaaS,主要基于以下几点:
- 提供 OpenAI 兼容接口
查阅蓝耘官方文档后可以看到,只需替换base_url、api_key和模型 ID,即可继续使用 OpenAI SDK 或兼容客户端,现有代码不必为此重写请求层。
图2 蓝耘接口文档
- 模型信息展示得比较完整
平台模型列表给出了模型类型、上下文长度、供应商和 API 入口。选型时可以先确认模型能否处理长文,再复制真实调用 ID,不必根据展示名称猜配置值。 - 多种模型可以放在同一个 MaaS 平台中管理
如果以后要尝试 Kimi、Qwen 等其他文本模型,可以沿用相同的兼容接口,主要调整模型 ID 和提示词,并对比不同模型的实际效果。 - API Key 和资源用量有独立入口
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 接口的第三方服务,需要启用本地路由,让 CC Switch 把 Codex 的 Responses 请求转换为上游支持的格式。CC Switch 接入文档和Provider 配置说明都介绍了相关设置。
2.3 在蓝耘元生代创建专用 API Key
打开蓝耘平台 https://maas.lanyun.net/#/system/apiKey。
- 创建一个只给本项目使用的 API Key。
- 在模型列表确认模型 ID。日常称为 GLM5.3,蓝耘控制台中的实际调用 ID 是
glm-5.3。两者含义相同,但配置时必须使用后者。 - 打开蓝耘提供的接口说明,确认 OpenAI 兼容的基础地址和路径。不要把控制台网页地址当作 API Base URL。
建议为每个项目单独创建 API Key,并在项目结束后停用。这样即使发生泄露、401 或额度异常,处理时也不会影响其他项目。
图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。这样 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 提交记录里。
图6 CC Switch配置
先测试一下是否连通。
图7 接口连通测试
2.5 用 Codex 验证开发模型是否切换成功
图8 Codex模型验证
切换 Provider 后重新打开 Codex,给一个足够具体但不消耗太多额度的任务,例如:
阅读当前 Vue 项目的目录结构,说明将 Markdown 文档拆成“全文”和“段落”两种生成范围时,状态应该放在哪个组件,并给出修改建议。不要直接改文件。这一步的目的不是评测模型分数,而是验证三件事:Codex 能正常响应、模型名确实来自蓝耘 Provider、以及项目上下文能被正确理解。
图9 Codex理解项目
三、创建项目
3.1 创建并启动项目
图10 输入初始Prompt
这个项目从零开始开发。我先用一条基础 Prompt 生成项目框架,再通过多轮对话逐步补充功能。
最初的 Prompt 只描述整体方向,后续再补充页面布局、并行任务、图片编辑和错误重试等需求。这也是我常用的开发方式:先确认大方向,再逐项微调。
图11 追加开发Prompt
项目使用 Vue 3 + Vite 开发。markdown-it负责把文档转换为 HTML,dompurify负责清理渲染结果,避免将上传 Markdown 中不可信的 HTML 直接插入页面。图片生成和编辑请求统一放在src/services,页面组件只处理状态和交互。
工程目录为md-visual-studio。
图12 项目目录结构
3.2 在页面中配置模型
项目启动后,点击右上角“模型配置”打开设置弹窗。蓝耘部分填写服务地址、接口路径、模型 IDglm-5.3和项目专用 API Key;商汤部分填写服务地址、生图路径/images/generations、编辑路径/images/edits、模型 ID 和访问密钥。
保存后,配置写入当前浏览器的本地存储,页面顶部会同步显示蓝耘和商汤的配置状态。整个过程都在页面弹窗中完成,不需要修改项目配置文件。
四、核心实现
4.1 让 GLM5.3 先做视觉策划,而不是直接让生图模型读全文
文章往往很长,直接整篇交给图像模型,得到的画面容易只抓住几个表面关键词。我的处理方式是把“理解文章”和“生成图片”拆开。
上传 Markdown 后,项目会提取标题和内容块;用户可以选择全文,也可以点击某个段落。随后createImagePrompt把选中的文本、图片用途、画面比例和风格发给蓝耘模型。请求中的model来自页面配置,本项目实际发送的是glm-5.3。
核心调用逻辑如下,省略了错误处理:
constpayload={model,messages:[{role:'system',content:visualDirectorPrompt},{role:'user',content:`请根据以下${scopeName}生成一条${targetName}指令。 文章标题:${documentTitle}画面比例:${ratio}视觉风格:${style}--- 内容开始 ---${source}--- 内容结束 ---`,},],temperature:0.45,stream:false,}visualDirectorPrompt里我明确约束了七件事:
- 封面概括全文,插图解释段落;
- 图片里的标题、数字和技术名词由图像模型直接生成;
- 需要出现的文字用中文引号逐字标出;
- 只保留一个主标题和不超过三项核心信息;
- 描述构图、层级、配色和留白;
- 不添加原文中没有的事实和结论;
- 最终只返回一条图像指令,而不是解释性文字或 JSON。
这一步是蓝耘在项目中承担的核心任务。GLM5.3 输出的不是通用的“帮我画一张科技图”,而是一份以原文为边界、可直接传给 U1.5 Lite 的视觉简报。
4.2 把 U1.5 Lite 的生图和改图收敛成同一个图片工作流
右侧创作区没有把“智能生成”和“图片编辑”硬切成两个页面。用户生成图片后,可以点击“编辑图片”直接进入近全屏编辑器;也可以先点击“编辑已有图片”上传一张本地图片。
编辑器处理了三个细节:
- 保留当前图片版本列表,方便回看原图和每次编辑结果。
- 编辑指令只要求描述“要改什么”,例如“把背景改成浅色网格,保留中间标题和流程结构”。
- 编辑时把当前图片、修改要求和输出比例一起发送,未修改部分是否保留则直接写进本次编辑 Prompt。
文生图请求使用顶层prompt,核心结构如下:
constpayload={model,prompt,n:1,size:editSizeByRatio[ratio]||'auto',output_format:'png',response_format:'b64_json',watermark:true,prompt_extend:true,}图片编辑走/images/edits,原图放进images,自然语言修改要求仍放在顶层prompt:
constpayload={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 开发服务器再把它转发给商汤:
constsensenovaProxy={target:'https://token.sensenova.cn',changeOrigin:true,rewrite:(path)=>path.replace(/^\/api\/sensenova/,''),}exportdefaultdefineConfig({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)=>{constnewTask={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,将错误信息留在卡片上,并显示“重试”和“重新下发”按钮:
asyncfunctionexecuteTask(task){task.status='prompting'task.error=''try{task.prompt=awaitcreateImagePrompt(/* 当前任务参数 */)task.status='generating'task.imageUrl=(awaitgenerateImage(/* 当前视觉指令 */)).imageUrl task.status='completed'}catch(error){task.status='failed'task.error=toErrorMessage(error)}}functionretryTask(taskId){consttask=tasks.value.find((item)=>item.id===taskId)if(task)executeTask(task)}点击重试后,文章内容、图片类型、风格和比例都会沿用原任务,不用再填一遍。现在采用的是整条链路重跑,也就是重新生成视觉指令,再调用商汤生图。这样写比较稳,两个阶段的错误都能处理。后面如果要节省调用量,可以缓存已经生成成功的视觉指令,只重试生图接口。
六、效果实测
6.1 上传 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 组件拆分、接口边界设计和配置排错。
- 运行阶段:作为视觉策划模型,把全文或段落转换成结构化、受原文约束的图像指令,再把任务交给商汤 U1.5 Lite。
而商汤 U1.5 Lite 则专注图像生成与编辑。把文本理解和视觉生成拆成两个清晰的职责后,项目既保留了多模型协作的灵活性,也避免让一个模型同时承担所有工作。
目前完成的是一个可运行的 MVP,仍有继续完善的空间。下一步我准备使用后端代理保护长期密钥,并加入持久化生成记录、提示词对比和调用日志。
调用记录也能帮助我比较不同蓝耘模型在“长文理解 → 视觉指令”任务中的实际差异。