☰
用Go构建AI Agent流水线:实现电商详情页自动化生成
2026/11/4 4:36:35 网站建设 项目流程

做电商运营或商品设计这块的朋友应该都有同感:一张商品主图到一整套淘宝详情页,中间隔着的是一个团队几天的劳动。设计师抠图画场景、文案憋卖点、运营对照竞品调参数,流程长且大部分是重复劳动。我一直在想,既然识图、生成、排版都能被大模型搞定,那为什么不把这些能力串成一条自动化的 Agent 流水线?于是我用 Go 写了这样一条流水线:输入一张商品图,经过解析、提炼、渲染几个阶段,直接输出一套可用的淘宝详情页 HTML 模块。这篇文章就围绕这个项目,把架构设计、核心实现、踩坑过程完整拆开讲。

先说清楚这条流水线解决什么问题:传统详情页制作依赖人工完成“看图—找卖点—写文案—排版”整个链路,周期通常 2 到 5 天。我的目标是把这个链路压到分钟级,并且让输出结果有基本质量底线。项目适合这几类人参考:正在做 AI Agent 落地的开发者、电商 SaaS 工具的技术负责人、以及想了解 Go 在 AI 应用场景里能承担什么角色的后端工程师。我会把关键代码、Prompt 设计思路、并发模型都拿出来聊,尽量做到拿走就能用。

1. 项目概述:为什么用 Go 搭电商 Agent 流水线

1.1 详情页制作的真实痛点

先还原一个典型场景。运营拿到一款新品的商品图,要产出详情页,通常需要这些东西:首屏海报文案、核心卖点提炼、规格参数表、场景图描述、售后说明。每一步都依赖人去理解商品、组织语言、套用类目模板。问题在于,大部分 SKU 的详情页结构高度相似,真正有创造力的部分可能只占 20%,剩下 80% 都是信息整理和格式搬运。

这个痛点非常适合用 Agent 流水线解决,因为它的流程是稳定的:图片信息抽取、卖点生成、内容校验、模板渲染。每一阶段都有明确输入输出,是典型的管道式任务。我一开始也想过用 Python 写,毕竟 AI 生态里 Python 的库最多,但后来评估下来,Go 在这类 IO 密集型、并发要求高的场景里反而更顺手,具体原因下一节展开。

1.2 为什么选 Go 而不是 Python

选型阶段我对比了 Python 和 Go 两条路线,最终确定 Go,理由有三点。第一,流水线的瓶颈在外部 API 调用,不在 CPU 计算。无论是调用视觉模型识别图片,还是调用大模型生成文案,大部分时间都花在网络上。Go 的 goroutine 和原生并发模型可以轻松同时处理几十个商品任务,而 Python 要写出同样稳定高效的并发代码,需要额外引入 asyncio 或 Celery,复杂度高不少。

第二,交付形态。我最终希望这条流水线能被打包成一个独立服务,丢到服务器上就能跑。Go 编译出来的单二进制文件完美匹配这个需求,不需要在目标机器上安装 Python 解释器、pip 依赖,更不用处理 virtualenv 这类环境问题。如果你试过在一台干净服务器上部署 Python 项目,应该能体会这种省心。

第三,生态虽然比不上 Python,但关键环节都有可用方案。画像处理有 imaging,模板渲染有标准库的 html/template,HTTP 客户端有 resty,再加上 Go 1.22 之后的泛型支持,写 Agent 状态机之类的抽象会比以前舒服很多。后面我会展示泛型到底用在哪儿。如果你团队已经有 Go 基础,那维护成本比 Python + FastAPI + Celery 这套组合低一个量级。

1.3 流水线的总体形态

这条流水线从输入到输出的完整链路是这样的:用户上传或提交一张商品主图 URL,服务先下载图片并压缩到适合模型输入的尺寸,然后进入三个阶段。第一阶段是图生文,调用视觉语言模型把图片内容解析成结构化 JSON,包括商品类目、颜色、材质、风格、适用人群;第二阶段是文生文,基于第一阶段的 JSON 和预先配置的模板,让大模型生成标题、卖点、详情模块文案;第三阶段是渲染,把生成的文案和规则化的商品规格合并进 HTML 模板,输出淘宝详情页可用的装修片段。

三个阶段的执行我在 Go 里用 channel 串起来,形成真正的流水线。每个阶段是一个独立 goroutine,从自己的输入 channel 取任务,处理完写到下一阶段的 channel。这样做的好处是每个阶段可以独立扩容,比如图片解析是瓶颈,就开 8 个 worker,文案生成相对快,开 3 个 worker 足够。整条流水线通过 context 做生命周期管理,任务被取消时所有 goroutine 会收到信号并尽快退出。

2. 流水线架构设计与关键选型

2.1 Agent 阶段划分:识别、提炼、生成、渲染

我们在设计时没有去搞复杂的“规划器 + 工具调用 + 多轮反思”那种通用 Agent 框架,而是把 Agent 做成四个固定阶段。原因很实际:电商详情页场景的任务边界足够清晰,用固定流水线比通用 Agent 框架更可控、更容易排查问题,token 成本也低很多。

四个阶段的具体分工如下:

阶段输入输出核心组件
图片解析商品图结构化商品特征 JSON视觉模型 + OCR
卖点提炼商品特征 JSON标题、卖点、参数描述对话模型 + Prompt 模板
内容校验生成文案合规、字数、类目匹配的最终文案规则引擎 + 模型自查
模板渲染最终文案 + 规格数据淘宝详情页 HTML/图片Go html/template

四个阶段对应到代码里是四个函数,它们之间没有直接调用关系,而是通过 channel 通信。这样的设计让单测很好写:每个阶段只要保证输入输出符合约定协议就能独立验证。协议就是自定义的ProductMeta、CopyDraft、DetailBlock几个结构体,字段用 JSON tag 标注,方便和模型返回的数据互相转换。

2.2 并发模型:用 channel 把 Agent 串成真正的水线

“流水线”这个词在 Go 里不是抽象概念,而是具体的 channel 模式。我用一个Pipeline结构体管理整条链路,核心代码大概是这样的:

type Pipeline[T any] struct { parseCh chan *ProductImage copyCh chan *ProductMeta renderCh chan *CopyDraft errCh chan error parseOps int copyOps int renderOps int } func (p *Pipeline[T]) Start(ctx context.Context) { for i := 0; i < p.parseOps; i++ { go p.parseWorker(ctx) } for i := 0; i < p.copyOps; i++ { go p.copyWorker(ctx) } for i := 0; i < p.renderOps; i++ { go p.renderWorker(ctx) } }

每个 worker 的逻辑都一样:从输入 channel 读数据,处理,把结果写入输出 channel。区别只在于中间处理函数不同。这里我用了 Go 1.18 引入的泛型,让 Pipeline 不绑定具体数据类型。虽然这个项目里数据处理流程比较固定,但后续如果想把流水线复用到其他场景,比如视频素材生成,泛型的价值就体现出来了。

每个阶段的并发度不能拍脑袋定。我的做法是在配置里维护一个 worker 数量表,根据真实耗时动态调整。图片解析平均耗时 8 秒,文案生成平均 5 秒,渲染 1 秒,那么比例大概就是 8:5:1。整体并发数又要考虑上游模型 API 的 QPS 限制,这块会在第四章展开。

2.3 与 AI 服务的对接方式

这个项目对接模型统一走的是 OpenAI 兼容协议,因为不管是视觉模型还是文本模型,市面上主流的服务商基本都兼容这个协议。好处是代码里只需要封装一个 client,通过切换 base URL 和 model 名就能在不同供应商之间无缝切换,对成本控制和容灾非常有利。

Go 这边我直接用resty来做 HTTP 调用,超时和重试都配好。核心请求结构体是这样封装的:

type ChatRequest struct { Model string `json:"model"` Messages []ChatMessage `json:"messages"` ResponseFormat map[string]any `json:"response_format,omitempty"` Temperature float64 `json:"temperature,omitempty"` } type ChatMessage struct { Role string `json:"role"` Content any `json:"content"` }

一个容易踩坑的点是Content字段。视觉模型的图片输入在 OpenAI 协议里是数组结构,包含type: "image_url"和image_url: {url: "..."},而文本消息的Content又是字符串。如果把Content定义成string,视觉请求就没法复用同一套结构。所以我把Content定义成any类型,序列化时自然可以自动适配两种形态。这个细节看起来小,但解决了多模型统一接入的大问题。

3. 核心实现与代码级拆解

3.1 商品图解析:把图片变成结构化数据

流水线第一站是商品图解析。这一步的目标不是“看懂图”,而是从图里提取出后续文案生成需要的所有事实信息。视觉模型的 Prompt 我压得很结构化,要求它只输出 JSON,字段必须是预设的 key。

type ProductMeta struct { Category string `json:"category"` ProductName string `json:"product_name"` Colors []string `json:"colors"` Material string `json:"material"` Style string `json:"style"` Scenarios []string `json:"scenarios"` TargetUsers []string `json:"target_users"` Features []string `json:"features"` TextInImage []string `json:"text_in_image"` }

这里有一个关键技巧:TextInImage字段专门用来存放图片上出现的文字,比如包装上的“10倍快充”“IPX8 防水”。视觉模型可以直接识别这些文字信息,把它们结构化保存下来,后续生成卖点时这些是最有用的实打实的卖点素材。

提交给模型的图片需要预处理。原图可能是几 MB 的高清图,直接传 base64 会导致请求体过大、耗时变长。我用 imaging 库统一把最长边缩到 2000 像素以内,质量和体积平衡点非常好。

图片下载和压缩还有一个容易忽略的细节:必须设置 HTTP 客户端的超时时间。默认的http.Client没有超时限制,一旦遇到慢速接口,goroutine 会长期占用资源。我最终用的是resty.New().SetTimeout(30 \* time.Second),重试次数限制在 3 次以内。图片下载失败就让上游 URL 重新提交,不做过度的本地重试。

3.2 卖点文案与详情模块生成

拿到结构化的ProductMeta之后,进入文案生成阶段。文案生成不是一次性输出,而是分成两层:先产出方案,再做自检优化。

第一层是生成方案。我把卖点拆成三个独立任务并行执行,分别是 15 字以内的主图标题、五个核心卖点短句、以及针对详情页模块的长文案。并行执行的好处是节省时间,一个商品最多能省两轮串行调用时间。对应 Go 代码用errgroup管理这三个任务,任何一个失败都会取消其他任务并返回错误。

g, ctx := errgroup.WithContext(ctx) var title, bulletPoints, longCopy string g.Go(func() error { title, err = generateTitle(ctx, meta) return err }) g.Go(func() error { bulletPoints, err = generateBullets(ctx, meta) return err }) g.Go(func() error { longCopy, err = generateDetailCopy(ctx, meta) return err }) if err := g.Wait(); err != nil { return nil, err }

第二层是自检优化。我会把第一轮的输出作为输入,要求模型扮演资深电商运营总监,对文案进行打分并提出修改意见,然后让模型重新生成一版。这一步不是走过场,实验下来文案质量能有明显提升,尤其是避免了“纯功能堆砌、没有场景感”的通病。代价是 token 消耗翻倍,所以我只在用户选择“高质量模式”时开启。

生成详情页模块文案时要注意模块化。我们先把详情页拆成五个固定区块:首屏海报、核心卖点、参数规格、细节展示、售后说明。每个区块单独生成,最后再用模板合并。这样后续如果只想重新生成某个区块,就不用整条流水线重跑。

3.3 详情页渲染:模板拼接与出图

最后一个阶段是渲染。这里要区分两个概念:淘宝详情页后台支持两种装修方式,一种是直接上传长图,另一种是粘贴 HTML 源码。我选择输出 HTML 模块,因为它的可编辑性最强,运营拿到之后还能自行调整文字和样式。

Go 标准库的html/template在这里发挥得很好。我准备一套基础模板,然后根据商品类目动态选择配色和结构。比如数码类目用深色科技感模板,服装类目用浅色极简模板。模板里的变量全部是第三阶段生成的结构化数据。

下面是一段简化的模板示例:

<section class="detail-section hero"> <h1>{{.HeroTitle}}</h1> <p>{{.HeroSubtitle}}</p> </section> <section class="detail-section specs"> <table> <tr> <th>参数</th> <th>数值</th> </tr> {{range .Specs}} <tr> <td>{{.Name}}</td> <td>{{.Value}}</td> </tr> {{end}} </table> </section>

渲染完成后,我会同时生成一份 PDF 预览文件和一份 HTML 源码压缩包。PDF 是给运营确认用的,HTML 是最终要发布使用的。HTML 源码里所有图片都会指向一个配置好的 CDN 地址,不会塞 base64 数据,因为详情页图片一多,base64 会导致源码体积膨胀,淘宝后台保存都可能出问题。

4. 上线运行中的常见坑与排查技巧

4.1 并发限流与成本控制

项目刚跑起来时我踩过一个大坑:并发开太高,直接把模型 API 打到限流,大量请求返回 429。原因很直接,本地测试时只有一个商品任务,看不出问题。一旦连上队列批量处理几十个商品,每个商品又有 3 次并行文案请求,瞬间 QPS 就超过了服务商限制。

后面我做了两层限流。第一层是基于golang.org/x/time/rate的全局令牌桶,限制整个应用对某一家模型供应商的每秒请求数。第二层是每阶段 worker 数量上限,通过环境变量配置:

PIPELINE_PARSE_WORKERS=8 PIPELINE_COPY_WORKERS=4 PIPELINE_RENDER_WORKERS=4

成本控制方面,最关键的是缓存。同一个商品图可能被反复提交,比如运营换了个标题重跑一遍流程。我按图片的 SHA-256 哈希值做结果缓存,图片解析阶段的输出默认缓存 7 天,命中缓存直接跳过耗时最久也最贵的视觉模型调用。实测下来,大批量处理相似商品时,缓存命中率能到 30% 以上,成本节省非常可观。

4.2 LLM 返回不稳定的处理

大模型输出天然不稳定,这是项目中最需要兼顾健壮性的部分。第一大问题是 JSON 解析失败。视觉模型或文本模型在输出 JSON 时偶尔会带上 markdown 代码块开头,比如返回\``json和尾部的```,直接json.Unmarshal一定报错。我在代码里写了一个extractJSON函数,先用正则把代码块剥离,再解析。如果还失败,就用jsoniter` 这种容错更强的解析库尝试。

第二个问题是字段类型不稳定。同一字段这次返回字符串,下次可能返回数组。最典型的是features字段,模型有时候输出"features": ["轻便", "耐用"],有时候输出"features": "轻便耐用"。我的解决方案是在结构体字段上做双类型兼容,用一个自定义的FlexStringSlice类型同时实现UnmarshalJSON,统一转换为[]string。

第三个是内容合规问题。生成文案里偶尔会出现极限词,比如“最”“第一”“绝对”,这在电商平台是不能用的。我加了一道规则过滤,用关键词表做硬校验,一旦发现命中就直接打回重新生成,最多重试两次。这一步不能依赖模型自觉,规则引擎是必须的兜底手段。

4.3 图片处理与资源占用的坑

图片处理是最容易被低估的地方。一个常见问题是图片 URL 带签名过期。用户从对象存储拿到一个有有效期的临时链接,提交到流水线后,链接可能在排队阶段已经过期,导致图片下载 403。解决方法是下载动作前移到任务入队那一刻,而不是等到 worker 真正处理时才下载。下载后的图片先缓存到本地临时目录,由任务 ID 做文件名,处理完再统一清理。

另一个问题是内存占用。一批商品图同时进入解析阶段,如果都是 2 万像素边长的大图,内存会迅速飙升。我做了两个优化:一是下载后立即用 imaging 压缩到最长边 2000 像素,原始大图直接删除;二是压缩和解析之间用带缓冲的 channel 衔接,而不是无缓冲 channel,从源头缓解背压问题。

模板渲染阶段也有一个隐蔽的坑:html/template默认会转义 HTML 特殊字符,这是防 XSS 的正确行为。但在详情页文案里,运营希望保留&符号和换行符,默认转义会把&变成&amp;。我最终把文案字段改成用template.HTML类型包裹,并确保所有文案在进入模板前已经过一层安全清洗,避免注入风险。

5. 从 0 到 1 的经验沉淀与后续扩展

项目从设计到跑通首版,前后用了两个周末。最有价值的收获不是“能用 Go 调大模型”,而是对 Agent 流水线落地的几个认知。第一,固定流程比通用框架更可靠。这个项目如果套 LangChain 这类通用 Agent 框架,光是调试 Agent 的“自主决策”就够喝一壶。固定流水线看起来不高级,但稳定性、可观测性、成本控制都是可控的。

第二,中间层数据结构是灵魂。一开始我让各阶段的输出都是自由文本,结果下游 Prompt 时不时被奇怪格式干扰,后来强制每个阶段输出 JSON Schema 结构化数据,问题立刻少了一大半。我建议做类似的 Agent 项目时,先花一天时间把各阶段的输入输出协议定义清楚,不要急着写业务逻辑。协议定好了,后面每一步调试都省力。

第三,可观测性必须前置。流水线涉及多个外部依赖,任何一个环节出问题都需要快速定位。我在每个阶段入口和出口都打了一条结构化日志,字段包括任务 ID、阶段名、耗时、token 用量。排查问题时只要 grep 任务 ID,就能看到这个商品图走完整条链路的每一步耗时和结果,效率非常高。

后续我计划做三个方向上的扩展。一是接入商品知识库,把品牌历史款式的卖点沉淀下来,作为 Prompt 里的参考素材,让生成文案更有品牌调性。二是在详情页之外增加场景扩展,比如根据商品图自动生成小红书种草笔记和抖音短视频脚本,复用现在的 Agent 流水线。三是把渲染模块升级成基于 Chromium 的精确出图,有需要的时候直接用chromedp把 HTML 渲染成长图,满足部分类目只能传图不能贴 HTML 的发布场景。

最后分享一个实操小技巧。这条流水线跑稳定之后,我把它配置成了一个极简 HTTP 接口,基于 Go 的 fiber 框架暴露两个端点:一个接收商品图 URL 创建任务,一个查询任务状态。使用方不需要理解任何 Agent 概念,只需轮询一个状态字段,从pending到done。这种“内部复杂、外部简单”的设计,才让流水线真正变成了团队每天都在用的工具,而不是技术玩具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询