当前位置: 首页 > news >正文

Qwen3.6不生图却能生成封面:本地Agent绘图工作流实战

1. 项目概述:当“不会生图”的大模型,亲手帮你把封面画出来

Qwen3.6 不会生图?这句话本身没错——它确实不是 Stable Diffusion 那类原生图像生成模型,没有像素级扩散采样能力,不跑 UNet,不调度 latent space,也不做 CFG 引导。它的参数结构、训练目标、推理范式,全部锚定在语言建模与多模态理解上:读代码、解数学题、分析图表、写技术文档、拆解系统架构图……这才是它真正吃透的“肌肉记忆”。所以当你对它说“画一张图”,它第一反应不是调用 VAE 解码器,而是问:“你想要什么图?谁用?在哪用?要什么尺寸?中文要不要?配色有没有偏好?标题和副标题怎么排版?”——它在做需求工程,不是在渲染像素。

但问题来了:如果它真“不会画”,那我那天下午在 qwen-code 里敲下那行“给我生成一张封面图,风格科技感,主题 Ubuntu + LLM 扩容”之后,屏幕上弹出的那张 1200×630 的 PNG 是从哪来的?不是缓存,不是本地旧图,不是误触剪贴板——是它实时生成、保存、返回给我的。更关键的是,这张图里所有中文字体清晰可读,标签圆角饱满,分隔线粗细得当,连“39.5GB”那个小数点后的“.5”都对齐了 baseline。这不是凑巧,也不是幻觉,而是一整套意图解析—工具编排—环境适配—反馈迭代闭环落地的结果。它没画图,但它指挥了一支由 Pillow、Noto CJK、Python 解释器和你本地终端组成的微型绘图小队,精准完成了任务。

这件事的价值,远不止于“省了一张图”。它揭示了一个正在快速成型的新工作流范式:大模型作为“智能施工队长”,不再亲自搬砖,而是识别图纸、清点工具、分配工种、验收质量、记录工艺——最终把一次性的手工活,沉淀成可复用的标准工序包(Skill)。你不需要成为图形学专家,也不必背熟 PIL.ImageDraw 的所有 API;你只需要说清楚“我要什么”,剩下的,由它来组织资源、绕过坑、试错、收敛、交付。这正是当前本地 Agent 生态最真实、最接地气的进化切口:不是比谁家模型参数更多,而是比谁能把“已有的东西”用得更聪明、更稳、更可持续。

如果你正用 qwen-code 做日常开发、写技术博客、搭个人知识库,或者哪怕只是想搞懂“为什么现在 AI 能干越来越多‘本不该干’的事”,那么这个封面生成过程,就是一份现成的、带血带肉的操作手册。它不讲空泛的 Agent 架构图,只讲你打开终端后,每一行命令背后发生了什么、为什么这么选、换台机器会不会崩、下次改标题要不要重写脚本——全是实打实踩出来的路径。接下来,我们就从头开始,把这张图是怎么“被生出来”的,一帧一帧拆给你看。

2. 核心思路拆解:为什么放弃“直接生图”,选择“指挥绘图”?

2.1 模型能力边界的清醒认知:不硬刚,不幻想

很多人第一次听说“Qwen3.6 生成封面”,下意识反应是:“它是不是偷偷加了多模态生成头?”或者“是不是用了某种隐式图像 token?”——都不是。Qwen3.6-35B-A3B 的开源权重文件里,没有图像 decoder 层,没有 vision-language cross-attention 的额外 FFN,它的 tokenizer 里也没有 image patch token。它的多模态能力,严格限定在理解层面:能看懂你贴进来的截图里的表格结构,能解释 Mermaid 流程图的逻辑走向,能从 PDF 图表中提取坐标轴含义。它“看见”图像,但绝不“生成”图像。

这就决定了一个根本前提:任何指望它像 DALL·E 那样输入 prompt、输出像素的尝试,注定失败。这不是模型“不够强”,而是设计目标完全不同。就像你不能要求一台顶级数控铣床去绣花——它精度再高,刀具和运动轴也不支持针脚轨迹。强行让它“画图”,等于让一个擅长解微分方程的数学家去临摹《蒙娜丽莎》,方向错了,力气白费。

所以整个过程的第一步,不是写 prompt,而是做一次冷静的能力审计:

  • ✅ 它能精准理解自然语言中的视觉需求(“科技感”“Ubuntu 主题”“LLM 扩容”“1200×630”“带中文标签”);
  • ✅ 它能根据上下文推断技术约束(博客封面需适配 Twitter/X 尺寸,PNG 格式利于网页加载,中文字体必须嵌入或指定路径);
  • ✅ 它能调用本地工具链(shell 命令、Python 脚本、YAML 配置);
  • ❌ 它不能直接输出二进制图像数据;
  • ❌ 它不能绕过操作系统字体渲染机制;
  • ❌ 它不能保证远程服务(如 diagrams.net)的可用性。

这个审计结果,直接否决了所有“端到端生成”的幻想路径,把问题锚定在“如何用它调度本地已有资源完成图像产出”这一务实方向上。这不是妥协,而是聚焦——把有限的算力、时间、调试精力,全部押注在可控、可验证、可复用的环节。

2.2 工具链选型的三重权衡:为什么是 Pillow + Noto CJK,而不是别的?

既然不能靠模型自己画,就得找“代笔”。候选工具其实不少:drawio CLI、Inkscape 命令行、LaTeX TikZ、甚至用 HTML+CSS 渲染后截图。但最终锁定 Pillow + Noto CJK,是经过三轮硬碰硬的权衡:

第一轮:执行确定性 vs 环境依赖性
drawio CLI 看似最顺理成章——qwen-code 内置 skill,文档明确支持 PNG 输出。但它依赖drawio-headless或 LibreOffice,而这两者在 Ubuntu Server 环境下安装极其繁琐:drawio-headless需要 Electron 运行时,内存占用大,启动慢;LibreOffice headless 模式对中文字体支持极不稳定,且不同版本导出 SVG 的 CSS 兼容性差异巨大。一次apt install可能触发半小时的依赖地狱。而 Pillow 是纯 Python 库,pip install pillow30 秒搞定,无系统级依赖,执行即启,毫秒级响应。确定性压倒一切。

第二轮:中文渲染可靠性 vs 字体管理复杂度
SVG 路径看似优雅,但跨平台字体问题是致命伤。.drawioXML 中写<text font="Noto Sans CJK">,不代表系统就认得这个字体名。Linux 下字体注册靠 Fontconfig,而 Fontconfig 的缓存、配置文件(/etc/fonts/conf.d/)、用户目录(~/.fonts/)层级混乱,fc-list | grep "Noto"能查到,不代表libreoffice --headless就能加载。Pillow 则完全不同:它不查系统字体库,只认你传进去的.ttf文件路径。只要ls /usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc存在(Ubuntu 默认预装),ImageFont.truetype("/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", size=48)就绝对成功。这是“指定路径”对“名称解析”的降维打击。

第三轮:开发迭代效率 vs 功能完备性
TikZ 功能强大,LaTeX 编译出的 PDF 矢量图质量极高,但它编译周期长(每次改字号都要等xelatex),错误信息晦涩(! Undefined control sequence.这种报错对非 LaTeX 用户就是天书),且无法动态读取 YAML 配置——你得手写\newcommand{\titletext}{...}。而 Pillow 脚本是标准 Python:argparse解析命令行,PyYAML加载 spec,PIL.ImageDraw画图,PIL.ImageFont渲染文字,所有逻辑都在一个.py文件里,改一行代码,python3 gen_cover.py立刻看到效果。这种“改-试-看”的反馈循环,是快速迭代封面设计的生命线。

提示:不要被“专业工具”的名头迷惑。在本地自动化场景中,执行速度、错误可读性、配置灵活性,往往比功能上限重要十倍。Pillow 可能画不出复杂的贝塞尔曲线渐变,但它能让你在 3 分钟内把标题字号从 48 改成 52 并重新生成——而这,恰恰是博客写作中最频繁的需求。

2.3 “Agent 编排”不是玄学,而是可拆解的决策树

很多人听到“Agent 编排”就想到复杂框架、Orchestrator、Tool Calling 协议。但在这个封面案例里,它就是一张朴素的决策树,由 Qwen3.6 在几秒钟内完成遍历:

需求:生成博客封面图 ├── 尝试路径 A:调用 drawioSkill → 检查 which drawio-headless → 未找到 → 失败 ├── 尝试路径 B:生成 .drawio XML → 手动用 libreoffice 导出 SVG → 打开浏览器 → 中文方框 → 失败 ├── 尝试路径 C:上传 .drawio 到 diagrams.net → HTTP 404 → 失败 └── 尝试路径 D:检查本地 Python 环境 → pip list | grep pillow → 存在 检查字体 → fc-list | grep "Noto.*CJK" → 存在 → 启动 gen_cover.py 开发流程 → 成功

这个过程没有魔法,只有三步扎实动作:探测(Probe)、评估(Evaluate)、执行(Execute)。Qwen3.6 的优势在于,它能把这三步压缩在一个对话轮次里完成。当你输入需求,它不是立刻写代码,而是先在后台“摸底”:你的系统里有什么?缺什么?哪些工具能组合?哪些坑已经有人踩过?这种基于上下文的环境感知能力,才是它区别于传统 LLM 的核心——它不是在“回答问题”,而是在“经营一个项目”。

这也解释了为什么 Skill 最终以.spec.yaml+gen_cover.py形式存在:.spec.yaml是它“探测”后确认的稳定输入接口(你只需改文本),gen_cover.py是它“评估”后选定的最可靠执行载体(不依赖外部服务)。二者结合,就把一次性的“人肉试错”,固化成了零配置的“一键生成”。

3. 实操细节解析:从 spec.yaml 到 PNG 的每一步

3.1 spec.yaml 配置文件:用纯文本定义视觉语言

封面生成的第一步,永远不是打开 Photoshop,而是写一个.yaml文件。这不是为了炫技,而是为了把“视觉需求”翻译成机器可读、人可维护、版本可追踪的结构化数据。basic.spec.yaml看似简单,但每个字段都对应着后续绘图脚本的关键分支:

# basic.spec.yaml title: "Qwen3.6 不会生图?" subtitle: "它却给我生成了一张封面——中间发生了什么?" tagline: "从失败到成功的完整复盘" tags: - "Qwen3.6" - "qwen-code" - "Pillow" - "Agent 编排" show_bars: true bar_values: - 8 - 16 - 24 - 32 - 39.5 bar_labels: - "8GB" - "16GB" - "24GB" - "32GB" - "39.5GB" colors: background: "#0F172A" # 深蓝灰,近似 Ubuntu 暗色主题 title_text: "#F1F5F9" # 浅灰白,高对比度 tag_bg: "#334155" # 中灰,标签背景 tag_text: "#CBD5E1" # 浅灰,标签文字 bar_color: "#60A5FA" # 天蓝色,Ubuntu 品牌色

这里的关键设计逻辑是:把设计决策从代码里剥离,放到配置层。比如show_bars: true这个开关,背后对应着脚本里一段条件渲染逻辑:

if spec.get("show_bars", False): # 绘制水平条形图 bar_height = 24 bar_gap = 12 total_bar_width = 800 max_value = max(spec["bar_values"]) for i, (val, label) in enumerate(zip(spec["bar_values"], spec["bar_labels"])): x_start = 200 y_start = 80 + i * (bar_height + bar_gap) bar_width = int((val / max_value) * total_bar_width) # 绘制条形 + 标签 draw.rectangle([x_start, y_start, x_start + bar_width, y_start + bar_height], fill=spec["colors"]["bar_color"]) draw.text((x_start + bar_width + 12, y_start + 4), label, font=font_small, fill=spec["colors"]["title_text"])

这意味着,下次你想关掉对比条,不用碰 Python 代码,只需把show_bars: true改成false,脚本自动跳过这段逻辑。同理,换主题色?改colors下的十六进制值;增删标签?在tags列表里增减字符串。这种“配置驱动”的模式,让非程序员也能安全修改封面样式,极大降低了协作门槛。

注意:bar_valuesbar_labels是分离设计。bar_values用于计算条形长度比例(数值运算),bar_labels用于显示(字符串渲染)。这样你就能让条形按8→16→24→32→39.5等比增长,但标签显示为"基础版"→"Pro版"→"企业版",实现数据逻辑与呈现逻辑的解耦。

3.2 gen_cover.py 脚本核心:Pillow 绘图的七处关键控制点

gen_cover.py全长不到 300 行,但每一处都针对实际渲染痛点做了加固。我们逐段拆解其核心控制逻辑:

① 画布初始化与抗锯齿保障

from PIL import Image, ImageDraw, ImageFont # 创建 1200x630 画布,RGB 模式,深色背景 img = Image.new('RGB', (1200, 630), color=spec["colors"]["background"]) draw = ImageDraw.Draw(img) # 启用抗锯齿(关键!否则文字边缘锯齿严重) draw.fontmode = "L" # 使用抗锯齿字体渲染模式

draw.fontmode = "L"是常被忽略的救命设置。默认fontmode = "1"是二值位图,文字边缘全是马赛克;设为"L"(Luminance)后,Pillow 会用灰度插值,文字瞬间平滑。这个设置不加,再好的字体也白搭。

② 字体加载的绝对路径策略

# 硬编码 Noto CJK 字体路径(Ubuntu 系统) font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc" # 兜底:如果路径不存在,尝试其他常见位置 if not os.path.exists(font_path): font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Bold.ttc" if not os.path.exists(font_path): raise FileNotFoundError(f"未找到 Noto CJK 字体,请手动安装 noto-cjk 或指定 --font-path") font_title = ImageFont.truetype(font_path, size=64) font_subtitle = ImageFont.truetype(font_path, size=36) font_tag = ImageFont.truetype(font_path, size=24) font_small = ImageFont.truetype(font_path, size=20)

不依赖fc-list查找,不信任$FONTCONFIG_PATH,直接写死最可能的位置。这是对 Linux 字体生态混乱的务实妥协。脚本还内置了两级 fallback:先找 Regular,再找 Bold,最后报错并提示安装命令(sudo apt install fonts-noto-cjk),把“字体缺失”这个高频故障,变成一条可执行的修复指令。

③ 标题排版的动态居中算法

# 计算标题文本宽度,实现精确水平居中 title_bbox = draw.textbbox((0, 0), spec["title"], font=font_title) title_width = title_bbox[2] - title_bbox[0] title_x = (1200 - title_width) // 2 title_y = 120 # 距离顶部 120px draw.text((title_x, title_y), spec["title"], font=font_title, fill=spec["colors"]["title_text"]) # 副标题同理,但 Y 坐标基于主标题 bbox 动态计算 subtitle_bbox = draw.textbbox((0, 0), spec["subtitle"], font=font_subtitle) subtitle_width = subtitle_bbox[2] - subtitle_bbox[0] subtitle_x = (1200 - subtitle_width) // 2 subtitle_y = title_y + (title_bbox[3] - title_bbox[1]) + 24 # 主标题高度 + 24px 间距 draw.text((subtitle_x, subtitle_y), spec["subtitle"], font=font_subtitle, fill=spec["colors"]["title_text"])

这里没有用draw.text((600, 120), ...)这种粗暴居中,而是先用textbbox()获取文本真实包围盒,再计算宽度、动态偏移。这样即使标题是超长英文(如Qwen3.6-35B-A3B-Quantized-For-Edge-Deployment),也能完美居中,不会溢出画布。textbbox()是 Pillow 10.0+ 的新 API,比旧版getsize()更准确,尤其对 CJK 字符。

④ 标签(Tag Pills)的圆角矩形绘制

def draw_tag_pill(draw, text, x, y, font, bg_color, text_color, padding=12): """绘制带圆角的标签矩形""" text_bbox = draw.textbbox((0, 0), text, font=font) text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] # 圆角矩形参数:左上角 (x, y),右下角 (x+width+2*padding, y+height+2*padding) pill_width = text_width + 2 * padding pill_height = text_height + 2 * padding radius = pill_height // 4 # 圆角半径设为高度的 1/4 # 绘制圆角矩形(四段弧 + 四条线) draw.rounded_rectangle( [x, y, x + pill_width, y + pill_height], radius=radius, fill=bg_color ) # 绘制居中文字 text_x = x + padding text_y = y + padding draw.text((text_x, text_y), text, font=font, fill=text_color) # 批量绘制 tags tag_start_x = 200 tag_y = 520 # 固定在底部区域 for i, tag in enumerate(spec["tags"]): tag_x = tag_start_x + i * 180 # 每个 tag 间隔 180px draw_tag_pill(draw, tag, tag_x, tag_y, font_tag, spec["colors"]["tag_bg"], spec["colors"]["tag_text"])

rounded_rectangle()是 Pillow 9.2+ 新增的 API,直接支持圆角,无需手动画四段弧。radius=height//4是经验公式:太小(如 2px)显得尖锐,太大(如 height//2)圆角会塌陷。180px 的横向间距,是经过实测的最优值——既能容纳最长的"Agent 编排"(7 个汉字),又不会让短标签(如"Pillow")显得孤立。

⑤ 对比条(Bars)的自适应缩放

if spec.get("show_bars", False): bar_values = spec["bar_values"] bar_labels = spec["bar_labels"] max_val = max(bar_values) bar_height = 24 bar_gap = 12 total_width = 800 start_x = 200 start_y = 80 for i, (val, label) in enumerate(zip(bar_values, bar_labels)): # 条形宽度 = (当前值 / 最大值) * 总宽度 bar_width = int((val / max_val) * total_width) y_pos = start_y + i * (bar_height + bar_gap) # 绘制条形(带阴影增强立体感) draw.rectangle( [start_x, y_pos, start_x + bar_width, y_pos + bar_height], fill=spec["colors"]["bar_color"] ) # 添加 1px 右侧阴影 if bar_width > 0: draw.line( [start_x + bar_width, y_pos, start_x + bar_width, y_pos + bar_height], fill="#4B9CD3", # 比主色稍暗的阴影色 width=1 ) # 绘制标签(右对齐,紧贴条形末端) label_bbox = draw.textbbox((0, 0), label, font=font_small) label_width = label_bbox[2] - label_bbox[0] label_x = start_x + bar_width + 12 label_y = y_pos + 4 draw.text((label_x, label_y), label, font=font_small, fill=spec["colors"]["title_text"])

这里的关键是bar_width = int((val / max_val) * total_width)的归一化计算。它确保无论bar_values[1, 2, 3]还是[8, 16, 24, 32, 39.5],最长的条形都占满 800px,其他按比例缩放。右侧阴影线draw.line()是点睛之笔:1px 宽的深蓝色线,让扁平条形立刻有了向右延伸的视觉动势,比纯色块更符合“扩容”主题。

⑥ 分隔线的视觉权重控制

# 原始方案:一条 4px 高的粗线,分割标题区和标签区 # draw.line([(200, 480), (1000, 480)], fill="#334155", width=4) # 迭代后方案:1px 细线 + 上下留白,降低视觉压迫感 separator_y = 460 draw.line([(200, separator_y), (1000, separator_y)], fill="#334155", width=1) # 添加上下 12px 留白,让标题和标签区呼吸感更强

这个改动源于第一次生成后的真实反馈:“分隔线太重,像一道墙”。1px 线宽是视觉最小单位,#334155是中灰,比纯黑柔和;2001000的 X 范围,避免线条顶到画布边缘,留出 100px 边距。这种“减法设计”,是工程师思维向设计师思维的进化。

⑦ 输出前的 Gamma 校准与元数据清理

# 保存前进行轻微 Gamma 校准,提升暗部细节(针对深色背景) img = ImageEnhance.Brightness(img).enhance(1.05) # 清理 EXIF 元数据,避免泄露本地路径信息 data = list(img.getdata()) img_no_exif = Image.new(img.mode, img.size) img_no_exif.putdata(data) # 保存为 PNG,指定压缩级别(6 是 Pillow 默认,平衡大小与质量) img_no_exif.save(args.output, "PNG", compress_level=6) print(f"✅ 封面已生成:{args.output} ({os.path.getsize(args.output)} bytes)")

ImageEnhance.Brightness(img).enhance(1.05)是个小心机:深色背景(#0F172A)在屏幕显示时容易发灰,提升 5% 亮度能让#334155分隔线和#60A5FA条形更通透。getdata()+putdata()是清除 EXIF 的最简方式,比img.info.clear()更彻底,防止PIL自动写入Software: Pillow等冗余信息。

3.3 本地环境探测脚本:让失败提前暴露

光有gen_cover.py不够,还得有配套的环境检查。我在.qwen/skills/blog-cover/scripts/下写了check_env.py,每次运行前先执行它:

#!/bin/bash # check_env.sh echo "🔍 正在检查本地环境..." python3 -c "import sys; print(f'Python {sys.version[:5]}')" || exit 1 python3 -c "import PIL; print(f'PIL {PIL.__version__}')" || { echo "❌ Pillow 未安装,请运行: pip install pillow"; exit 1; } python3 -c "import yaml; print('✅ PyYAML 已安装')" || { echo "❌ PyYAML 未安装,请运行: pip install pyyaml"; exit 1; } ls /usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc >/dev/null 2>&1 || { echo "❌ Noto CJK 字体缺失,请运行: sudo apt install fonts-noto-cjk"; exit 1; } echo "✅ 环境检查通过,可以生成封面!"

这个脚本的价值,在于把“运行时报错”变成“运行前预警”。比如某次我换了新机器,忘了装fonts-noto-cjkgen_cover.py报错OSError: cannot open resource,堆栈长达 20 行,新手根本找不到问题在哪。而check_env.sh一行就告诉你:“字体缺失,请装这个包”,省下半小时 debug 时间。这就是专业脚本和玩具脚本的区别:前者替用户思考失败场景,后者让用户自己填坑。

4. 实操全流程:从零开始生成你的第一张封面

4.1 准备工作:三分钟搭建可运行环境

别被“Ubuntu”“CLI”吓住,整个环境准备就是三行命令,全程联网即可(假设你用的是主流 Linux 发行版,如 Ubuntu 22.04+、Debian 12+、Fedora 38+):

# 1. 确保系统更新(Ubuntu/Debian) sudo apt update && sudo apt upgrade -y # 2. 安装 Noto CJK 中文字体(关键!) sudo apt install fonts-noto-cjk -y # 3. 安装 Python 依赖(Pillow + PyYAML) pip3 install --user pillow pyyaml # 4. (可选)验证安装 python3 -c "from PIL import ImageFont; f=ImageFont.truetype('/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc', 24); print('✅ 字体加载成功')"

注意:--user参数很重要。它把包装到~/.local/lib/python3.x/site-packages/,无需sudo pip,避免污染系统 Python 环境。pip3而非pip,确保调用 Python 3 解释器。

如果你用的是 macOS,命令略有不同:

# macOS 安装字体(使用 Homebrew Cask) brew tap homebrew/cask-fonts brew install --cask font-noto-sans-cjk # 字体路径变为 FONT_PATH="/opt/homebrew/share/fonts/noto/NotoSansCJK-Regular.ttc" # 修改 gen_cover.py 中的 font_path 即可

Windows 用户也不用慌,虽然本方案主要面向 Linux/macOS,但原理完全通用:下载 Noto CJK 字体包 ,解压后把.ttc文件路径填进gen_cover.pypip install pillow pyyaml,一样能跑。唯一区别是which命令换成where,但这不影响核心绘图逻辑。

4.2 第一次生成:五步走,亲眼见证“不可能”变可能

现在,让我们亲手走一遍生成流程。打开终端,进入你的博客项目根目录(比如~/my-blog/):

第一步:创建技能目录结构

mkdir -p .qwen/skills/blog-cover/{scripts,examples} cd .qwen/skills/blog-cover/

第二步:下载或手写 gen_cover.py
把前面章节里的完整gen_cover.py脚本内容,复制粘贴到scripts/gen_cover.py。确保它有可执行权限:

chmod +x scripts/gen_cover.py

第三步:编写你的第一个 spec.yaml
examples/目录下创建my-first-cover.spec.yaml

title: "我的第一篇技术博客" subtitle: "用 Qwen3.6 自动生成封面" tagline: "告别手动设计,拥抱自动化工作流" tags: - "技术写作" - "AI 工具链" - "Pillow" show_bars: false colors: background: "#1E293B" title_text: "#F1F5F9" tag_bg: "#334155" tag_text: "#CBD5E1"

第四步:运行生成命令
回到项目根目录(~/my-blog/),执行:

python3 .qwen/skills/blog-cover/scripts/gen_cover.py \ --output cover.png \ --spec .qwen/skills/blog-cover/examples/my-first-cover.spec.yaml

第五步:查看成果
几秒钟后,终端输出:

✅ 封面已生成:cover.png (42187 bytes)

用图片查看器打开cover.png——一张 1200×630 的深色科技风封面,标题居中,副标题清晰,四个标签整齐排列在底部。它不是 AI 画的,但它是 AI 指挥生成的。这一刻,你亲手完成了从“需求”到“交付”的全闭环。

实测心得:第一次运行如果报错OSError: cannot open resource,99% 是字体路径不对。用find /usr -name "NotoSansCJK*.ttc" 2>/dev/null命令查找真实路径,然后修改gen_cover.py里的font_path变量。Ubuntu 22.04 的路径是/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc,但某些云服务器可能装在/usr/local/share/fonts/,必须实测确认。

4.3 迭代优化:从“能用”到“专业级”的三次微调

生成第一张图只是起点。真正的价值,在于快速迭代。以下是我在实际使用中,最常做的三次调整,每次只需改 2-3 行代码或配置:

① 调整标题行高与字间距(解决“文字挤在一起”)
问题:长标题如"Qwen3.6-35B-A3B 多模态推理性能深度解析"在 64px 字号下,字母间距过紧,阅读吃力。
解决方案:在gen_cover.py的标题绘制部分,加入spacing参数:

# 原代码 draw.text((title_x, title_y), spec["title"], font=font_title, fill=spec["colors"]["title_text"]) # 修改后(增加 spacing=8) draw.text((title_x, title_y), spec["title"], font=font_title, fill=spec["colors"]["title_text"], spacing=8)

spacing=8表示字符间额外增加 8px 间距,立刻让英文标题呼吸感十足。这个参数对中文无效(CJK 字符本身有固定字宽),所以只影响英文/数字混合场景。

② 为标签添加悬停动画效果(仅限网页发布)
问题:静态 PNG 标签缺乏交互感。如果封面用于网页博客,可以生成带 CSS 动画的 HTML 版本。
解决方案:新增gen_cover_html.py脚本,读取同一份.spec.yaml,输出cover.html

<!-- cover.html --> <div class="cover-container"> <h1 class="cover-title">Qwen3.6 不会生图?</h1> <p class="cover-subtitle">它却给我生成了一张封面——中间发生了什么?</p> <div class="cover-tags"> <span class="tag">Qwen3.6</span> <span class="tag">qwen-code</span> </div> </div> <style> .tag { transition: all 0.3s ease; } .tag:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); } </style>

这样,同一份配置,既可生成静态 PNG 用于社交媒体,又可生成交互 HTML 用于博客正文,复用率拉满。

③ 批量生成多尺寸封面(适配不同平台)
问题:Twitter/X 要求 1200×630,但 LinkedIn 文章封面是 1200×627,Reddit 是 1200×675。手动改尺寸太累。
解决方案:扩展gen_cover.py,支持--size参数:

python3 scripts/gen_cover.py --size 1200x627 --output linkedin-cover.png --spec examples/basic.spec.yaml

脚本内部根据 `--

http://www.jsqmd.com/news/1027854/

相关文章:

  • 有实力的会议用车品牌企业,温州聚游汽车服务的优势 - mypinpai
  • 2026年泰州步阳防盗门厂家推荐指南:官方甄选与本地源头工厂深度评测 - 优质品牌商家
  • 基于7系列FPGA实现万兆网通信的2种方式:10G以太网核和10G PCS PMA核
  • 高级手势:PanGesture滑动、PinchGesture缩放的坐标计算(31)
  • 从HX711到MCP3551:高精度称重传感器电路设计全解析
  • 仲景中医AI:让千年中医智慧在指尖触手可及
  • 选购CCS集成母排,优质定制厂家浙江中燕新能源不可错过 - 工业品牌热点
  • 2026年国内泡沫箱生产厂家推荐甄选:加厚、冷链、生物医用领域优质供应商分析 - 优质品牌商家
  • 注册公司服务推荐哪家,嘉简财税优势在哪 - 工业品牌热点
  • 用Python和AI将YouTube评论聚类生成影评
  • 如果实验失败有“功劳簿”,我的采购平台一定“榜上有名”
  • 微信群内怎么发起投票,云帆投票+西瓜评选+腾讯投票,深度测评 - 投票小程序
  • 多维聚合实战:用Python构建可演化的数据立方体
  • 2026靠谱的礼盒定制厂家排名,翊佳包装上榜 - mypinpai
  • 2026年山特不间断电源TOP5推荐:山特工业UPS电源、山特蓄电池、恒安UPS电源、恒安高频UP电源、施耐德UPS电源选择指南 - 优质品牌商家
  • 【硬核进阶】别再被阻塞拖垮!一文讲透 Tokio + async/await,榨干 Rust 高并发性能
  • 大白话带你速通 Claude Code Skill:如何让你的 AI 编程助手瞬间“社会化”?
  • TopKGraphs:基于Jaccard引导随机游走的节点相似性计算
  • 从Altium到KiCad:实战指南与避坑技巧
  • RACECAR电机控制与电池供电实战指南
  • 西北代理勤策软件服务多少钱?价格一览表 - 工业品牌热点
  • 2026年口碑污水处理药剂厂家官方甄选:高性价比源头供应商电话与地址汇总 - 优质品牌商家
  • Adobe Photoshop 2020 核心功能、优势及详细安装教程
  • 数据科学家必学的轻量级ETL流水线实战
  • 随州漏水检测维修权威推荐:卫生间-厨房-阳台-屋顶天花板漏水维修:靠谱防水补漏公司团队TOP5推荐(2026最新深度调研实测榜单) - 即刻修防水
  • 免费布局写字楼光伏电站哪家强?上喜光伏实力出圈 - mypinpai
  • 2026年企业级AI API集成实践:高可靠聚合调度平台选型指南
  • 2026年柴油发动机选型指南:技术升级与配件服务综合评测 - 优质品牌商家
  • 手推最小二乘法:从散点图到回归公式的完整推导
  • 吾悦广场附近酒店选购指南 - mypinpai