我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页

0 阅读13分钟

我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页

系列说明:这是「AI 详情页生成工具」系列的第 1 篇(架构解析)。第 2 篇讲如何用 Wails v2 从零搭建这个 AI 桌面应用(实战教程),第 3 篇复盘一个人做 AI 产品踩过的坑。发出去后会在这里补上链接。

一、为什么做这件事

淘宝/天猫的中小卖家上架一个新品,详情页通常要找外包美工:一套 300–1500 元,等 3–7 天。市面上的 AI 工具要么只能"一键美化",要么生成的图里商品被"重新设计"得面目全非,还有的直接让大模型在图里"写字",出来一堆变形假汉字。

我做的这个项目(ecom-detail-ai)目标是:上传 1 张商品图,全程不选品类、不选风格,约 10 分钟得到一整套带真实感光影的详情页长图 + 主图包 + 文案。它是一个 Wails v2 桌面客户端(Go 后端 + Vue3 前端),核心是一条全自动的 Agent 流水线。

这篇文章把这条流水线完整拆开,讲讲每一步的设计取舍。文中代码节选自项目源码,有删减。

先看一眼最终产出效果(示例商品,均为项目真实生成结果):

商品SOP长图

示例商品图

示例商品图

二、流水线全景

一条任务从创建到完成,实际走过的步骤是: 在这里插入图片描述

抠图(matte) → 读图感知(perceive) → 自主规划(plan) → [规划确认门]
    → 渲染×N(render,含自检重渲染) → 画面描述(caption)
    → 文案(copy) → 本机排版合成(layout) → [SOP 卡片] → 完成(成本落库)

编排核心在 internal/service/pipeline.go。有两个设计决策值得先说:

第一,LLM 步骤和确定性步骤严格分家。 感知、规划、文案、自检这四个"需要智商"的环节收敛在 internal/agent/ 包里,这个包只做"调模型 + 解析结构化结果",不碰数据库;抠图、渲染、排版、导出这些"不需要智商但要紧"的环节全是确定性代码。LLM 负责决策,代码负责执行,两边接口就是 JSON。

第二,每一步都持久化、都可观测。 每个子步骤写 TaskStep 记录并通过 Wails 事件 task:progress 推给前端,用户在界面上能看到"正在读图 → 识别为『便携榨汁机』→ 规划了 5 张图 → 第 2 张渲染触发限流,20 秒后重试……"这样的实时时间线。做长耗时 Agent 产品,进度可见不是锦上添花,是信任的底座。

在这里插入图片描述

三、第一步:感知——先把商品"看懂"

流水线的质量上限由第一环决定。感知步骤用视觉模型(GLM-4.6V)读商品图,输出一个结构化 Analysis

type Analysis struct {
    Name          string   `json:"name"`           // 商品名建议
    Category      string   `json:"category"`       // 一级类目
    Material      string   `json:"material"`       // 材质
    SellingPoints []string `json:"selling_points"` // 卖点线索
    Details       []string `json:"details"`        // 结构特征:顶部提手/侧面散热孔/logo位置
    VisibleText   []string `json:"visible_text"`   // 图中真实印有的文字,逐字转写
    Params        []Param  `json:"params"`         // 图中可见参数(键值对)
    Palette       string   `json:"palette"`        // 商品主色 hex,驱动排版配色
}

系统提示词里有一条铁律,是整个项目对抗幻觉的第一道闸:

"visible_text 与 params 只允许逐字转写图中真实可见的内容,看不清或没有就返回空数组,禁止凭常识推断补全——编造参数属于违规。"

这条规则后面还会反复出现:下游任何环节(规划、文案、参数表)能引用的事实,最多到感知输出 ∪ 用户输入为止。模型看不到的,宁可空着,不许编。

另外两个细节:

  • 对"抽 JSON"这类任务,视觉模型的思考模式要显式关掉(WithThinkingDisabled())。长推理链只会拖慢请求甚至拖挂,对抽取质量没有帮助。
  • 解析模型输出时不能天真地 json.Unmarshal。模型爱包 markdown 代码围栏、爱加前后缀解释语,所以有一个 unmarshalJSON:剥掉围栏,取首个 { 到末个 } 再解析。这是所有要求纯 JSON 输出的 LLM 应用的标配防御。

四、第二步:规划——让模型当导演,但给它剧本框架

规划步骤(DeepSeek)根据识别结果决定:出几张图、每张什么 kind(whitebg / hero / scene / detail / selling)、什么场景和光影、详情页模块序列、文案基调。同样是纯 JSON 输出。

这里踩过一个很典型的坑:当你想"让模型自己决定数量"时,提示词里绝不能出现数字 0。早期版本用户不指定张数时传 image_count: 0,模型非常听话地规划了 0 张图,任务直接中断。现在的写法:

countHint := fmt.Sprintf("计划 %d 张图", imageCount)
if imageCount <= 0 {
    // 0 不能出现在提示词里,否则模型会"听话地"返回 0 个条目
    countHint = "渲染张数由你决定(3-8 张)"
}

规划还是整条流水线的第一环,输出空 items 会直接中断任务,所以规划调用内置了一次"自动纠正重试":解析失败或 items 为空时,追加一条用户消息骂它"必须输出至少 3 个条目",再给它一次机会。

规划完成后有一个规划确认门:默认模式下停留 30 秒"机会窗口"(用户可以顺手调整计划)后自动继续;确认模式下任务置为 planned 状态最长挂 10 分钟等用户点继续。全自动和人在回路之间,我选择两个都要,用配置切换。

五、第三步:渲染——五段式提示词与商品保真

渲染是整条流水线最贵的环节(按张计费),也是最容易翻车的环节:图生图模型会"顺手优化"你的商品——换个 logo、改个孔位,一套图下来已经是另一个产品了。

对策是五段式渲染提示词internal/agent/prompt.go),把一条 prompt 拆成模型自由度截然不同的五段:

保留段(零自由)+ 场景段(模型规划)+ 光影段(词库映射)+ 构图段(按kind固定)+ 品质段(全kind共享)

保留段不是一句"保持一致",而是引用感知结果逐条点名

b.WriteString("严格保持参考图中的商品原样不变")
if len(a.Details) > 0 {
    b.WriteString(",以下特征必须原样保留、不得改动:")
    b.WriteString(strings.Join(a.Details, "、"))  // 顶部提手、侧面散热孔……
}
if len(a.VisibleText) > 0 {
    b.WriteString("商品上的文字「…」必须逐字保持原样、清晰无变形。")
}

(显式列出保留项而不是笼统说"保持一致",这是 Google 编辑模型官方指南的要求,实测也确实有效。)

光影段用中文预设映射到摄影术语词库——"柔光"映射为"柔光箱布光,光线均匀柔和,阴影过渡自然",比扔形容词给模型稳定得多。构图段按 kind 写死(hero 固定"三分之二侧视角,低机位平视,商品占画面 60%~70%"),品质段全 kind 共享,保证整套详情页视觉统一。品质段里有一句"无霓虹描边、光影符合真实商业摄影物理规律",来自实测教训:规划给出"轮廓光勾边"时,Seedream 会字面化执行成赛博朋克霓虹描边。

渲染的工程骨架是 renderOne:多参考图 data URL → 图生图 → 下载落盘 → 自检。两个细节:

  • 限流指数退避:生图新账户 RPM 极低,429 是常态。识别"速率限制/429/Throttling"等错误特征,按 10s/20s/40s/60s 退避重试,等待期间用 select 同时监听 ctx.Done(),用户点取消立刻退出等待。
  • 重试必须带诊断:自检不过触发重渲染时,同参数重跑毫无意义。第二次的 prompt 会追加"上一轮生成存在问题:商品与原图不一致、光影违和。重新生成时务必修正,其余要求不变。"——把评审意见回灌给生成模型。

首屏(hero)是转化关键位,我给它加了双候选选优:规划构图和"正面平视"构图各渲染一次,由自检打分择优保留。best-of-N 是图像生成里性价比最高的质量杠杆,一次 +0.1 元的成本。但注意代码注释里的实测教训:双候选本身已是有效重试,单候选就不再叠加自检重试了,否则最坏情况一个槽位烧掉 4 张图的钱。

六、"文字零幻觉":这个项目我最满意的设计

电商详情页有海量的文字:标题、卖点、参数表。让生图模型画文字,出来必然是变形假汉字;那能不能让模型"尽量别画字"?也不行,模型管不住自己的手。

我的方案是从源头把文字这件事从 AI 手里收走

  1. 规划提示词里写入铁律(planNoTextRule):场景描述不得携带"画文字"的意图,prompt 只描述商品、场景、氛围与构图;
  2. 渲染品质段再显式禁止:"画面中不得出现任何文字、数字、标注、标签贴纸(商品本身印刷的文字除外)"——注意例外条款要与保留段的"商品上印刷的文字逐字保留"自洽,否则模型会困惑;
  3. 真实文字由本机排版引擎后期叠加internal/render/ 是一个手写的 750px 宽位图合成引擎,用 golang.org/x/image 加系统 CJK 字体,把文案以真实字体排进对应模块槽位。

这样 AI 只负责"画面",排版引擎负责"文字",各干各的擅长的事。字体、字号、行距、配色全部可控,改一个字不需要重新生成图片。顺带的好处是:文案编辑后实时预览重排版只要几百毫秒,而重生成一张图要几十秒加几毛钱。

自检环节还会专门检查"画面文字错误"(text_error 是硬伤 tag,直接判不通过)——禁令 + 质检双保险。

七、第四步:自检——模型当评委,代码设门禁

自检用视觉模型做双图对比:参考图 + 生成图 + 规划描述一起给"质检员",输出:

type CheckResult struct {
    Pass       bool     `json:"pass"`
    Fidelity   float64  `json:"fidelity"`     // 商品保持度 0-1
    Tags       []string `json:"tags"`         // 诊断标签(五选一枚举)
    Issue      string   `json:"issue"`        // 不通过的人话原因
    ...
}

关键认知:不能只信模型的 pass 判断,服务端要做一道确定性门限。模型评委有放水倾向,所以拿到评分后代码再硬校验一遍——fidelity < 0.95 直接判不过;出现 product_drift / structure_deform / text_error 任一硬伤 tag 直接判不过;light_off、scene_mismatch 这类氛围问题单独出现时可放行但保留提示。这就是 enforceCheckGate

诊断 tags 同时服务两个消费方:回灌重渲染的 prompt(机器读),和前端时间线上"待人工确认"的 warn 状态(人读)。一张"自检存疑"的图不会被扔掉,而是标黄交给用户决定——自动化系统的最后一格永远留给人。

场景图/卖点图自检仍不过时,还有合成保真兜底:文生图只生成纯背景(提示词明确"不要出现任何商品"),再把真实抠图的商品像素贴回去。商品像素 100% 真实,不存在漂移,直接免自检。这也是"省流模式"的底层路线,更便宜且成本可预期。

八、多模型分工:一个网关、五个模型位

没有任何一家平台的每个模态都最强,所以我按"每个环节用该环节性价比最优的模型"设计,同时用架构把供应商锁定风险压到零。

配置抽象成五个模型位internal/config/config.go):

模型位默认配置职责
perception智谱 GLM-4.6V读图感知 + 成品自检
brainDeepSeek flash(fallback: pro)规划(Agent 大脑)
copyDeepSeek flash详情文案
renderGLM-Image(可切方舟 Seedream)图像生成
matting阿里云商品分割抠图

而网关层(internal/llm/gateway.go)只实现 OpenAI 兼容协议,换供应商 = 换 base_url + api_key + model 三个参数

var presetBaseURL = map[string]string{
    "zhipu":    "https://open.bigmodel.cn/api/paas/v4",
    "deepseek": "https://api.deepseek.com",
    "ark":      "https://ark.cn-beijing.volces.com/api/v3",
}

智谱、DeepSeek、火山方舟全部兼容 chat/completions,一个 Chat 方法通吃。唯一的协议例外是图生图——各家参考图参数形状不一致,我把它隔离在 images.go 一个文件里,注释写明"联调时只允许改本文件,调用方不受影响"。脏活收敛在最小面积里。

BYOK(用户自带密钥)则顺便解决了合规和成本透明:费用直接结算在用户自己的模型账号,每个任务结束时按刊例价折算消耗(taskCost 记录 tokens 与出图张数)在界面明示。产品方不经手资金、不存储商品图,排版合成全在本机。

九、文案与排版的防呆契约

文案(DeepSeek)按模块撰写,有两条铁律写进 system prompt:参数只能用"图中可见参数 ∪ 卖家输入",都没有就写"详细参数以实物为准";不同模块之间不得复述同一卖点,"重复即浪费"。

模型输出与排版引擎之间还有一层容量契约(CleanCopy):标题 ≤12 字、正文 ≤80 字,与排版字号档位配套,超限截断防溢出;空块丢弃;清洗后一块不剩就回退到用感知字段拼装的基础文案——流水线可以降级,但绝不产出空结果。文案步骤整体失败也不阻塞任务,兜底文案照常出,前端提示可手动修改。

顺带一提"文案对图"的小设计:渲染完成后每张成图会过一遍视觉模型生成一句 ≤40 字的画面描述(caption),文案的 user 消息里会带上"第 2 张 scene/卧室床头:暖光下商品立在床头柜上……",让文字模块写出来的话和画面真正呼应,消灭图文两张皮。

十、小结

回头看,这条流水线的核心方法论就三条:

  1. LLM 输出全部收敛为纯 JSON + 结构化解析 + 确定性门限。模型负责创意和判断,代码负责校验和执行,评委放水就硬拦。
  2. 每个易幻觉的环节都有"事实源白名单":参数只能来自图中可见 ∪ 卖家输入;文字只能由排版引擎写入画面。
  3. 失败路径层层兜底且成本如实:限流退避 → 诊断回灌重渲染 → 合成保真兜底 → 本地裁切兜底 → 基础文案兜底;同时每一张付费生成的图都计数进成本明细,重试也计费,绝不自欺。

这个工具是怎么在桌面端把这套东西跑起来的(Wails 绑定、SQLite 调度器、密钥加密、三平台打包),以及一路上踩过的具体的坑,分别是系列第 2、3 篇的主题,欢迎关注。

(项目代码节选自个人开源/私有项目 ecom-detail-ai,均已做删减;文中模型价格与配置以写稿时为准。)