☰
Go实战:构建AI Agent流水线自动生成电商详情页
2026/10/1 23:28:42 网站建设 项目流程

一个做电商的朋友凌晨给我发了张保温杯的商品图,问我明天能不能给她一整套详情页:主标题、五张卖点图、规格参数、场景文案、排版骨架。搁一年前我肯定劝她找专业美工和文案,但这次我把图拖进了自己用 Go 搭的 AI Agent 流水线里,十几分钟后吐出来一个可以直接上架的 HTML 详情页。这篇文章就是把这条流水线从零到一的完整复盘:为什么用 Go 而不是更主流的 Python、五个环节怎么串、关键代码怎么落、并发怎么扛,以及我在模型幻觉和 Token 成本上踩出来的两个大坑。

这套东西适合谁参考?如果你不光想让 Agent 在 Notebook 里“跑通一个 demo”,而是想把它变成能接真实流量、能批量排队、能压测、能上线的后端服务,那 Go 这条路线会很有价值。如果你是纯业务同学,想快速体验 Agent 效果,那直接找现成的工作流平台更快;但如果你恰好是后端开发,又眼馋 AI 这块,这篇文章就是写给你看的。

1. 选型复盘:Go 凭什么进 AI Agent 流水线这个局

1.1 流水线的本质是编排,不是模型调参

这两年 AI Agent 的热度高得离谱,随便一搜都是“从0到1搭建AI Agent”的教程,但绝大多数教程是用 Python 在 Jupyter 里串 prompt。等到要做“一张图进、整套详情页出”这种正式需求时,你会发现真正的难点根本不是让模型输出一段漂亮的文案,而是如何把多个模型调用、多个处理步骤、多次失败重试像工厂流水线一样稳定地串起来。

这条流水线本质上是一个编排问题:上游视觉模型分析图片,中间文本模型生成文案,下游规则引擎做校验,最后模板引擎渲染。每一步的耗时都是秒级,中间数据是 JSON,边界条件一大堆。这种“多阶段、长耗时、可重试、可观测”的业务形态,恰好是 Go 最舒服的领域。goroutine 便宜到可以开几万个,channel 能天然表达任务队列,defer + context 能把超时和取消管理得很干净,编译出来一个二进制就能扔到服务器上跑。对一个单人维护的小团队来说,这些特性比“AI 生态丰富”更值钱。

我见过不少团队把 Agent 服务放在 Python 里,跑起来是没问题,但一上并发就头疼。写爬虫和调模型都是 IO 密集,GIL 在纯 IO 场景下其实没那么痛,可一旦夹了图片缩放、水印合成、PDF 解析这类 CPU 密集的后处理,Python 的并发短板就藏不住了。Go 的调度器会自动把 goroutine 分布在多核上,你不需要操心线程池和进程池,代码结构天然就是并发的。

1.2 一次调用等两秒,瓶颈必然在并发而不是单任务

算一笔最简单的账:视觉理解一次调用平均 8~15 秒,文案生成 4~8 秒,审核再跑一轮模型又是 2~4 秒。我最初用最朴素的顺序流程跑一张图,全程要 42 秒。如果只有几张图,那无所谓;但电商场景经常是几百上千张图批量进,按这个速度,一千张图要跑将近十二个小时,完全不可接受。

所以这条流水线从设计第一天就必须考虑并发。Go 在这里有一个别家没法比的天然优势:goroutine 的内存开销只有几 KB,你可以为每一张商品图、每一个 Agent 调用开一个 goroutine,再用有界 channel 或者信号量去限制整体并发数。这种模型在 Java 里要上线程池,在 Python 里要上 asyncio 或者 multiprocessing,在 Go 里就是go func()加一个chan struct{}的事。

1.3 生态不是理由:Go 调各家大模型 API 早就不费劲了

很多后端同事一聊到 AI 就下意识觉得“这不是 Go 的主场”,其实这是刻板印象。你要做的不是训练模型,而是调用模型。大模型厂商基本都提供 OpenAI 兼容的 HTTP 接口,Go 这边成熟的 SDK 早就有了。就算没有官方 SDK,裸写 REST 调用也就是几十行代码的事,因为本质上就是POST一个 JSON,再GET一个流式响应。

我在项目里封装了一个统一的ChatClient接口,底层可以是通义、智谱、DeepSeek 或者 GPT 系的任意一家。多模态模型、JSON 结构化输出、工具调用这些能力,各家通过 OpenAI 兼容层都能覆盖。真到了需要切模型的时候,改一个环境变量就行,不需要动业务代码。所谓的“Go 没有 AI 生态”,指的是没有 LangChain 那种全家桶式的开发框架,但对于一条定义清晰的流水线来说,你需要的只是 HTTP、JSON、context 和 goroutine,这些 Go 全都内置了。

1.4 和 Python/LangChain 路线的真实对比

我也用过 LangChain 和 LangGraph 做过原型,优点是 Chain、Memory、Tool 这些抽象开箱即用,写 demo 的速度确实快。但生产环境里我很快碰到了几个问题:依赖升级频繁导致接口说变就变、报错堆栈又深又长、部署体积和内存占用都不小。做集成测试的时候,光 mock 掉它的内部组件就要写一堆样板代码。相比之下,我自己在 Go 里维护一个几十行的 stage runner,反而所有逻辑都透明可控。

至于 Dify 这类工作流平台,我实际部署过,它的知识库和可视化编排做得确实好,十分钟就能搭一个 Agent demo。但问题在于它是个“灰盒”:生成结果要回写到自己的订单系统、素材库、对象存储时,你得接一堆 Webhook 和自定义节点,调试链路反而更长。所以我的最终选型是:主体编排自己用 Go 写,Dify 只作为知识库检索服务挂在旁边,各取所长。

2. 流水线全貌:五个 Agent 环节和它们之间的交接件

2.1 每个 Agent 负责什么,谁来决定下一步

整条流水线被我拆成五个 Agent,每个 Agent 只负责一个阶段,输入输出都是明确的 JSON:

环节职责典型耗时
入口解析 Agent下载图片、校验格式、生成 job_id、落盘0.5s
视觉理解 Agent多模态模型抽取商品名称、类目、材质、卖点8~15s
创作 Agent生成标题、卖点文案、详情模块顺序6~12s
审核 Agent规则校验 + 二轮模型复核广告法风险3~5s
渲染交付 Agent套模板输出 HTML + manifest + 资源包2s

很多教程会强调“让 Agent 自己决定下一步”,听起来很酷,但落到生产环境里,我强烈不建议让大模型来决定整条链路的走向。Agentic 的价值应该体现在单个节点内部:比如创作节点可以根据类目选择不同的话术模板,视觉理解节点可以根据图片质量决定要不要调用更贵的模型。节点之间的流转用确定性的代码控制,这样任何一次失败都能精准定位,而不是模型一拍脑袋把流程带到沟里去。

2.2 中间数据模型:贯穿全流程的 ProductData

五个环节之间传什么?我用一个统一的结构体把整个流程串起来:

type ProductData struct { JobID string `json:"jobId"` ImageRef string `json:"imageRef"` Probe *ProductProbe `json:"probe,omitempty"` Copy *CopyDraft `json:"copy,omitempty"` Review *ReviewReport `json:"review,omitempty"` RenderURL string `json:"renderUrl,omitempty"` }

每一阶段只负责填充自己对应的字段,比如视觉理解 Agent 只写Probe,创作 Agent 只写Copy。这个设计的最大好处是:任何一步失败重试时,已经完成的阶段可以直接复用,不必从头再跑一遍。而且ProductData本身就是一条流动的记录,把它序列化存起来,就相当于给每个商品建了一整套完整的处理档案。

2.3 状态机还是链式调用:我最后选了带 Checkpoint 的有向图

最朴素的实现是链式调用:download -> understand -> copy -> review -> render,一个函数里顺序写完。缺点是中间任何一步挂了,整单要重来。比如审核不通过,你想只重跑创作环节,就得为此给整条链路加各种 if else,代码很快变成屎山。

我最后用一个简单的有向图结构来管理。每个节点实现统一的接口:

type Stage interface { Run(ctx context.Context, in *ProductData) (*ProductData, error) }

主流程只有一条主干线,但审核节点有一条回边:审核不通过时,把ProductData打回创作节点重新生成,最多重试两次。每次节点跑完,就把结果 JSON 写进 SQLite 的checkpoint表,以job_id + stage_name做唯一键。这样即使服务重启,任务也能从断点继续,不用从头再来。千万别一上来就上 Temporal 那类重型工作流引擎,先用一个结构体加一个 switch 撑住,等业务复杂度真的上来再演进也不迟。

2.4 知识库在这条流水线里的位置

创作 Agent 生成文案时,最怕的就是对品类的理解不够细。同样是“保温杯”,卖点是“6小时保温”还是“316不锈钢内胆”,取决于目标人群和价格带。这些信息不在图片里,也不该靠模型脑补,而是需要外部知识支撑。

我自托管了 Dify,把商品类目说明书、常见话术范式、违禁词表放进了知识库数据集。创作 Agent 在生成前,会先去调用 Dify 的检索 API,取回 top-k 条相关规范,拼接进 Prompt 上下文。这样改话术风格不用改代码,直接在知识库里维护就行。

但这里也要泼一盆冷水:知识库不是流水线的必需品。小规模场景下,把十几条话术模板写死在配置里,比每次做向量检索更快、更省、更可控。我上线跑了两周后,把默认链路里的知识库检索关掉了,只在新品类、新行业第一次跑的时候手动开启。知识库的价值在于大规模共享和热更新,如果你的用户量没到那个程度,别为了“有知识库”而硬加一层网络调用和一次计费。

3. 关键代码拆解:从图片到 JSON,再把 JSON 变成详情页

3.1 图片理解 Agent:用结构化输出逼模型说“人话”

视觉模型返回的自然语言是没法直接进下游处理的,必须让它在给定的 JSON Schema 里吐结果。我定义了一个ProductProbe结构体:

type ProductProbe struct { ProductName string `json:"productName"` Category string `json:"category"` Material string `json:"material"` Capacity string `json:"capacity"` Scenes []string `json:"scenes"` SellingPts []string `json:"sellingPts"` Risk string `json:"risk"` Confidence float64 `json:"confidence"` }

调用时把 Schema 传给模型的response_format参数,同时用 Prompt 做双保险:

你是一名有 10 年经验的电商运营专家。请根据商品图片提取信息,严格输出 JSON。图片中无法确认的字段必须填空字符串,禁止根据常识脑补。如果整体置信度低于 0.7,请在 risk 字段里说明不确定的部分。

请求的核心代码大致长这样:

resp, err := s.client.CreateChatCompletion(ctx, ChatRequest{ Model: "qwen-vl-max", Messages: []ChatMessage{ {Role: "system", Content: visionPrompt}, {Role: "user", Content: fmt.Sprintf("图片地址:%s", img.URL)}, }, ResponseFormat: jsonSchemaOf(ProductProbe{}), Temperature: 0.1, })

这里有两个非常实用的细节。第一,图片不要传原图,先在本地用图像库缩放到最长边 1024 像素、转成 JPEG,再传 Base64 或者对象存储 URL。视觉模型的计费跟图片分辨率强相关,压缩后速度更快,成本直接降一半都不止。第二,Base64 大字符串在内存里非常占地方,高并发下会造成 GC 压力。我压测时用 pprof 一查,发现堆上最大的对象全是图片字节流,后来改成先上传到 OSS 再传 URL,内存占用肉眼可见地掉下来。

3.2 创作 Agent:先分类再写文案,提示词分两步走

一开始我把“分析类目、确定人群、生成标题、写五点描述”塞进同一个 Prompt,结果输出经常失控:类目判断错了,后面全错。后来学乖了,把创作拆成两步。

第一步,用一个便宜快速的文本模型完成类目和人群判断,输出结果里包含一个templateKey。第二步,根据templateKey从配置文件里加载对应的话术骨架,再让大模型往骨架里填内容。这样做的好处有两个:一是便宜模型承担大部分分类工作,贵模型只负责精品文案;二是话术骨架固定后,输出风格不会跑偏,同一个保温杯不会这次写出“职场轻奢风”、下次写成“户外硬核风”。

创作节点输出的CopyDraft结构体:

type CopyDraft struct { Title string `json:"title"` Keywords []string `json:"keywords"` SellPoints []SellPoint `json:"sellPoints"` Sections []string `json:"sections"` } type SellPoint struct { Headline string `json:"headline"` Body []string `json:"body"` Score int `json:"score"` }

温度参数我固定设成 0.6。太低了文案会显得干巴,太高了容易跑偏。另外,每种类目的 few-shot 示例在 Prompt 里固定不变,并且我会把 Prompt 的版本号打到一个字段里,跟着请求一起发出去,方便后面排查风格问题到底是谁变了。

3.3 审核 Agent:规则引擎 + 二轮模型校验

模型生成的内容绝对不能直接上架,这是我做这条流水线最坚持的原则。审核环节分两层。

第一层是代码里的规则引擎,纯确定性检查,速度毫秒级:

  • 标题长度不得超过 30 个字,必须包含主关键词;
  • 命中违禁词表(极限词、绝对化用语)直接打回;
  • 卖点数量少于 3 个打回;
  • 规格参数里的单位、格式必须合法。

第二层才是模型复核。我让一个独立的审核模型这样读文案:“你是电商平台的合规审核员,请找出文案中没有依据的表述,以 JSON 输出违规项和修改建议。”这一步能抓出不少规则引擎漏掉的东西,比如“保温 12 小时”这种从图片里根本没法验证的夸大表述。复核之后,凡是置信度低于阈值的任务会进入一个人工复核队列,哪怕我这边只有一张简单的后台列表页,也至少要把这些高风险单子单独捞出来给运营过目。

3.4 渲染交付:html/template 拼装一整套详情页

审核通过后的ProductData就交到渲染节点。这一步我用 Go 标准库的html/template做拼接,绝不用字符串直接拼 HTML,否则转义和 XSS 问题迟早教做人。模板里把卖点循环渲染出来:

{{range .SellPoints}} <div class="sellpoint"> <h3>{{.Headline}}</h3> <ul> {{range .Body}}<li>{{.}}</li>{{end}} </ul> </div> {{end}}

输出物是一个目录:detail.html、manifest.json、assets/资源文件夹。manifest.json里记录了商品标题、类目、关键词、审核结果和生成时间,这是给后端系统对接用的;detail.html是最终的消费视图。图片引用走对象存储签名 URL,避免 HTML 体积无限膨胀。整套页面样式我直接内置在模板的<style>标签里,不依赖外部 CDN,因为电商后台很多时候不允许加载外部资源。

4. 并发实测:单张 42 秒压到 8 秒,我做了什么

4.1 最初的顺序版到底慢在哪

我先跑了一版完全顺序的代码,拿一批保温杯商品图做基准测试,单张平均 42.3 秒。用pprof一看,CPU 占用不到 30%,时间全部花在等待网络响应上。这个结论非常直白:性能瓶颈在大模型 API 的往返时延,不在本机算力。所以优化方向不是堆机器,而是把等待时间重叠起来,让一张图的视觉理解阶段和另一张图的文案生成阶段并发进行。

4.2 Worker Pool 和限流器的配合

并发不是简单地“把 100 个 goroutine 全打开”。模型厂商的 API 都有 QPS 和并发限制,盲目并发只会吃满 429 错误。我的做法是两把锁叠加。

第一把锁是全局信号量,控制同时处理的图片数量:

sem := make(chan struct{}, 6) g, ctx := errgroup.WithContext(ctx) for _, img := range images { img := img g.Go(func() error { select { case sem <- struct{}{}: defer func() { <-sem }() case <-ctx.Done(): return ctx.Err() } return processOne(ctx, img) }) } if err := g.Wait(); err != nil { // 记录失败批次,进入重试队列 }

第二把锁是每家的 API 限流器,用golang.org/x/time/rate实现。不同供应商的限流阈值不一样,我做成配置项,按照各自的 QPS 上限设置速率。遇到 429 或者 5xx,用指数退避加随机抖动重试,最大重试三次。每个阶段的 context 单独设 30 秒超时,整张图的总时长上限 90 秒,杜绝 goroutine 因为模型一直不返回而泄漏。

4.3 压测数据:吞吐量、内存和错误率

用 2000 张真实商品图压了一轮,数据如下:

版本单张墙钟时间吞吐量平均内存失败率
顺序版42.3s约 85 张/h120MB0.8%
6 路并发 + 限流 + 模板复用8.7s约 410 张/h310MB0.2%

单张墙钟时间从 42 秒压到 8.7 秒,靠的是多张图并行处理;整体吞吐量没有再线性往上飙,是因为视觉模型的 API 并发已经到了上限。我后来申请了第二个模型账号做负载分摊,理论上吞吐还能再翻一倍。内存从 120MB 涨到 310MB,这个代价换 5 倍吞吐,完全值得。实际操作中如果你只给自己用,6 路并发已经非常够用,没必要一上来就追求极致压榨。

4.4 顺带探索:在流水线里嵌一个 Wasm 沙箱

这条流水线做到中后期,有朋友问能不能让运营自己提交一些“自定义排序逻辑”或者“详情页特效片段”,而不是每次改代码重新发布。这属于开放平台的需求,核心问题是:不能信任运营提交的任意代码直接在宿主进程里跑。

我评估过 Lua 脚本、Go plugin、进程隔离和 Wasm 沙箱,最后选了 wazero。wazero 是纯 Go 实现的 WebAssembly 运行时,不需要 CGO,嵌入成本低。我用 TinyGo 写了一个“卖点排序插件”的示例,编译成.wasm,在创作节点里用 wazero 实例化并按固定接口调用,同时限制内存上限和执行时间。这样既保留了扩展能力,又不会让一段几百 KB 的插件把整个流水线拖垮。

实测下来,一次 Wasm 实例化的开销在毫秒级,跟一次大模型调用动辄几秒相比完全可以忽略。如果你的流水线只给自己内部用,这个功能确实有点过度设计;但如果你想把 Agent 能力开放给第三方,Wasm 沙箱是一条非常值得提前布局的技术路线。

5. 现场排过的雷:模型幻觉、风格漂移和成本失控

5.1 模型乱编商品参数:三层防线

视觉模型从一张图里提取信息时,特别喜欢“脑补”。我印象很深的一个案例:一张黑色保温杯的照片,原图根本没有任何材质标识,模型却一本正经地输出“316 不锈钢内胆”。这种文案要是直接上架,分分钟被职业打假人盯上。

我的解决方案是三层防线。第一层,Prompt 里写死“图片中无法确认的字段必须填空字符串,禁止根据常识脑补”,同时要求模型输出置信度。第二层,规则引擎做格式和枚举校验,比如容量格式必须匹配数字 + mL/L的正则,材质必须在白名单内,不匹配就标记为不可信。第三层,审核 Agent 再跑一轮复核,凡涉及具体参数但置信度不达标的,直接把整条任务打进人工复核队列。

加了这三层之后,因参数捏造导致的售后风险基本归零。最重要的心得体会是:永远别指望模型凭空变出事实,哪怕它语气再笃定。

5.2 风格漂移:Prompt 版本化,和模型参数无关

我一度以为把temperature调到 0.1,输出就能稳定。后来发现模型厂商端更新版本、调整默认行为,都会让你的输出风格悄悄变化。同一个 Prompt,上周还是简洁风,这周突然变成华丽风,而你什么都没改。

应对办法是把 Prompt 当成代码一样做版本管理。我给每个核心 Prompt 加了版本号字段,跟请求一起发送并写入日志;few-shot 示例固定在同一份配置里,不允许临时手改;每一类目都锁定话术骨架模板,模型只负责填空。上新的 Prompt 版本前,我会拿固定 20 张商品图跑一遍黄金测试,人工对比前后输出的差异。这套流程跑下来,风格漂移基本被控制住了。

5.3 Token 成本失控:重复图检测和降级模型

最早跑完第一批 2000 张图之后,账单数字让我心里一紧。细看才找到三个出血点。第一个是重复图片:同款商品不同角度的照片,视觉模型全部重新理解了一遍,其实卖点高度重合。我给图片加了感知哈希去重,近似度超过阈值的图直接复用已有文案,只换场景图部分。第二个是原图直传:长边 4000 像素的图片和 1024 像素的图片,视觉计费差距很大,压缩到 1024 之后视觉 Token 开销直接降了四成。第三个是无差别调用贵模型:我现在先让一个便宜模型做类目粗筛,只有低置信度的精品任务才走大模型精修,整体文案成本又砍了一截。

最后我给流水线加了一个 Token 记账中间件,每次模型调用都记录输入输出 Token 数,并设置单任务预算。超过预算自动降级到便宜模型或者暂停任务,防止某次异常 prompt 把一天的预算烧光。

5.4 大量报错时的定位手段:trace + slog

最开始的日志惨不忍睹:五个环节的日志混在一起,没有唯一的请求标识,出问题时根本不知道是视觉模型超时还是审核环节误判。后来我给每个任务生成全局唯一的reqID,从入口解析开始一路透传,所有日志都带上这个 ID:

logger := slog.With("req_id", reqID, "agent", "copywriting") logger.Info("start copywriting", "image_ref", imgRef, "prompt_version", "copy_v7")

同时用 OpenTelemetry 把每个 Agent 设成一个 span,每次模型调用都记录模型名、Token 数、耗时和错误码。排查问题时,先按reqID拉出整条链路,再看是哪一段 span 的耗时异常,效率比看一堆互相没有关联的日志高了十倍不止。

最后说一点个人体会:这条流水线最让我满意的不是生成效果有多惊艳,而是我终于把 Agent 当成一种普通后端组件在管理——它有输入输出、有超时、有重试、有可观测性。模型在变,Prompt 在变,但编排骨架、并发模型和工程质量这些东西不会过时。如果你也想从 0 到 1 搭一个真正能扛事的 AI Agent,我的建议是别急着追新框架,先把一条最基本的主链路跑通,加上并发控制和审计日志,再慢慢加知识库、Wasm、人工审核这些花活。这个思路,哪怕换一个业务场景,依然适用。

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

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

立即咨询