用 AI 自动生成壁纸标题、描述和 SEO Slug

用 AI 自动生成壁纸标题、描述和 SEO Slug

200+ 张壁纸手写标题描述不现实,我用 Qwen-VL 视觉模型 + 文本模型搭了一条三步流水线。


壁纸平台最头疼的不是代码,是内容。

每张壁纸需要:中文标题、中文描述、英文标题、英文描述、SEO Slug。一个 Pack 里 20 张壁纸,算下来 100+ 条文案。手写?一天写不完一个 Pack。

WallpaperSense 目前有 200+ 张壁纸、20+ 个 Pack,全部内容由 AI 自动生成。下面分享具体的实现方案。

三步流水线:Extract - Translate - Slug

AI 内容生成被设计为异步任务,分三个阶段严格顺序执行:

rust 复制代码
Extract(提取)    -> 视觉模型看图,生成中文标题和描述
     |
Translate(翻译)  -> 文本模型将中文翻译为英文
     |
Slug(URL 优化)   -> 从英文标题提取关键词,生成 SEO 友好的 URL slug

为什么不直接生成英文?因为我的壁纸面向中文和英文两个市场。中文到英文的翻译质量比直接让 AI 生成英文要好------中文输出更稳定,翻译时可以加更多约束。

阶段一:Extract------让 AI 看图说话

壁纸的标题和描述不能靠文本推理,得让 AI 看到图片。我用的是通义千问的视觉模型 qwen-vl-max,通过 Vercel AI SDK 的 OpenAI 兼容协议调用:

typescript 复制代码
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';

export async function describeWallpaper(input: WallpaperDescribeInput) {
  const config = await getVisionConfig();
  const provider = createOpenAI({ baseURL: config.baseUrl, apiKey: config.apiKey });

  const { text } = await generateText({
    model: provider.chat(config.model),
    messages: [{
      role: 'user',
      content: [
        { type: 'text', text: prompt },
        { type: 'image', image: new URL(input.imageUrl) },
      ],
    }],
    temperature: 0.7,
  });

  return parseJsonResponse<WallpaperDescribeResult>(text);
}

关键设计:

  • 传缩略图 URL 而不是原图:原图可能 40MB,视觉模型只需要看到画面内容,400x800 的缩略图完全够用
  • 分辨率作为 Prompt 上下文:告诉模型这是 4K 还是 8K 壁纸,让生成的描述包含分辨率信息

Prompt 工程经验

写 Prompt 踩了不少坑。最初生成的内容模板化严重,每张图都是"这是一幅xx风格的壁纸,展现了xx场景"。优化后的要点:

不要:

  • "这张壁纸展示了..." -> AI 几乎每次都会用这个开头
  • 要求"详细描述画面中的每个元素" -> 会变成流水账

要:

  • 直接用场景句:"晨雾笼罩的山间溪流"
  • 限制字数:标题 15-25 字,描述 50-80 字
  • 给具体示例:提供 3-5 个优质标题让 AI 参考风格
  • 包含分辨率和设备信息:既是 SEO 需要,也是用户关心的

阶段二:Translate------精准翻译而非重新生成

翻译不是简单的 API 调用。壁纸标题翻译有几个特殊要求:

typescript 复制代码
const prompt = `You are a professional translator...

Rules:
- Keep brand names unchanged
- Do NOT transliterate Chinese titles into pinyin
  For example "枫韵东方" should NOT become "Feng Yun Dong Fang"
- Title fields: keep concise but include subject, style, resolution
- Description fields: describe scene, mood, resolution in natural language
- Also generate a "seo_slug" field - URL-friendly, 3-5 keywords
`;

不拼音化是最重要的规则。很多 AI 模型遇到中文专有名词会直接用拼音,比如"枫韵东方"变成"Feng Yun Dong Fang"------对英文用户毫无意义。正确做法是意译:"Oriental Maple Charm"。

翻译时同时生成 SEO Slug 是一个效率优化------反正 AI 已经在处理英文内容了,顺便提取关键词生成 slug 几乎零成本。

阶段三:Slug------语义化的 URL

SEO Slug 的生成有两个来源:

  1. AI 生成的 seo_slug(优先):翻译阶段同步产出
  2. 代码生成的 fallback:如果 AI 结果无效,用 generateSeoSlug() 从英文标题提取

最终 slug 格式:{关键词}-{entity_id前8位}

复制代码
seaside-stairs-summer-healing-5f8ee3d4
japanese-mountain-wildflower-valley-8k-a3b2c1d4

加 ID 后缀的原因是避免重复------两张"日落海滩"壁纸不能共用同一个 URL。

波次执行(Wave Execution)

三个步骤有严格依赖:没有中文描述就没法翻译,没有英文翻译就没法生成 slug。

任务处理器每次被调用时,按优先级检查:

typescript 复制代码
async processPendingTasks(limit: number) {
  const WAVES = [AiTaskStep.EXTRACT, AiTaskStep.TRANSLATE, AiTaskStep.SLUG];

  let currentStep = null;
  for (const step of WAVES) {
    const { data: peek } = await aiContentTaskDAO.findPending(1, step);
    if (peek?.length) {
      currentStep = step;
      break;
    }
  }

  // 本轮只处理 currentStep 类型的任务
  const { data: tasks } = await aiContentTaskDAO.findPending(limit, currentStep);
  // ...
}

效果:如果还有 Extract 任务没完成,不会开始 Translate。全部 Extract 完成后,才轮到 Translate。这保证了数据一致性。

Pack 描述:不看图,看全局

Pack(主题包)的描述生成和单张壁纸不同------不是看图,而是综合信息:

typescript 复制代码
export async function describePack(ctx: PackDescribeContext) {
  // 输入:内部名称、壁纸数量、设备分布、子壁纸标题列表
  const prompt = buildPackDescribePrompt({
    internalName: 'japanese-mountain-wildflower',
    wallpaperCount: 20,
    phoneCount: 8, desktopCount: 8, tabletCount: 4,
    wallpaperTitles: ['雾中山间野花', '晨曦下的山谷', ...],
  });
}

用文本模型(qwen-plus)而不是视觉模型,因为 Pack 描述需要的是概括能力而非识图能力。把所有子壁纸的标题作为上下文传入,让 AI 归纳出 Pack 的整体主题。

翻译存储设计

翻译内容存在独立的 entity_translations 表中,而不是在 wallpapers 表上加 title_zhtitle_en 字段:

arduino 复制代码
entity_translations:
  entity_type: 'wallpaper' | 'pack'
  entity_id: uuid
  field_name: 'title' | 'description' | 'name' | 'seo_slug'
  locale: 'zh' | 'en' | 'ja' | ...
  value: text

优点:

  • 动态扩展语言:加日语只需要新建 Translate 任务,不用改表结构
  • 字段级粒度:可以只翻译标题不翻译描述
  • AI 结果回写主表:英文翻译完成后同步写回 wallpapers.title,保证向后兼容

容错和重试

AI 调用不稳定是常态。整个系统的容错机制:

  • JSON 解析容错:AI 有时返回 markdown 代码块包裹的 JSON,解析器会自动剥离
  • Skip 逻辑:如果目标语言已有翻译且 overwrite=false,跳过不重复生成
  • 重试机制:失败任务自动标记,最多重试 3 次
  • Stuck 检测:处理中超过 5 分钟的任务自动重置为 pending

效果

一个 20 张壁纸的 Pack,AI 完成全部内容生成大约需要 2-3 分钟:

步骤 耗时 内容
Extract ~60s 20 个中文标题 + 描述
Translate ~30s 20 个英文标题 + 描述 + SEO slug
Slug ~10s 20 个语义化 URL

之前手动写一个 Pack 的内容需要 2-3 小时。

技术选型小结

决策 选择 原因
AI SDK Vercel AI SDK (@ai-sdk/openai) 统一的 OpenAI 兼容协议,切换模型零成本
视觉模型 qwen-vl-max 中文识图效果好,价格合理
文本模型 qwen-plus 翻译质量稳定,上下文能力强
配置存储 数据库 system_configs 表 运行时切换模型/key,无需重新部署
任务架构 异步任务队列 + 前端轮询 适配 Vercel Hobby 10 秒超时

如果你对 AI 生成的壁纸内容效果感兴趣,可以访问 WallpaperSense 看实际效果------每一条标题、描述和 URL 都是 AI 生成的。


标签: #AI #prompt工程 #qwen #内容生成 #独立开发 #SEO #nextjs

相关推荐
大雨还洅下1 小时前
前端 JS: async, await; Generator
javascript
juejin_cn1 小时前
[转][译] 从零开始构建 OpenClaw — 第三部分(元技能)
javascript
颜酱4 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
炫饭第一名4 小时前
速通Canvas指北🦮——路径与形状篇
前端·javascript·程序员
无责任此方_修行中5 小时前
如何利用 pnpm 的安全控制功能防御 npm 供应链攻击
javascript·npm·node.js
进击的尘埃5 小时前
前端状态管理的本质:从 Vuex 到 Pinia,我们到底在管理什么?
javascript
码路飞5 小时前
GPT-5.3 Instant 终于学会好好说话了,顺手对比了下同天发布的 Gemini 3.1 Flash-Lite
java·javascript
Lee川5 小时前
从回调地狱到同步之美:JavaScript异步编程的演进之路
javascript·面试
进击的尘埃5 小时前
WebSocket 长连接方案设计:从心跳保活到断线重连的生产级实践
javascript