先交代一下背景:上周我接了一个很典型的生成需求,某电商团队要做新品活动,需要在一天内产出上百条商品详情页文案。他们之前手动写,三个人写了一天只出来了十几条,还写得参差不齐。需求摆到我面前时,对方觉得“用AI批量生成嘛,很简单”,但实际上手之后才发现,批量内容生成这件事的坑比想象中多得多。
这个“生成实战1”系列对我来说,就是把这类实战经验记下来。本文聊的就是这次批量文案生成项目的完整过程:从需求拆解、模型选型、提示词设计,到批量脚本实现、并发控制、质量验收,再到踩过的五个坑和对应的排查思路。如果你是刚接触AI生成落地、或者已经在用但总被结果不稳定折磨的人,这篇应该能帮你省不少弯路。
1. 为什么批量生成看着简单,做起来却全是细节
先把这个需求拆开看。对方要的文案长什么样?输入是商品名、核心卖点、目标人群;输出要三段:一个标题、一段短描述、一段长描述。输出规整、关键词覆盖到位、风格贴合品牌调性。听着确实不复杂,本质就是“给模型一个模板,让它按模板填空”。
但“批量”这个词一掺进来,事情就变味了。一百万条和一百条是两种完全不同的工程问题。这次规模虽然只有一百多条,但已经暴露出几个容易翻车的点:单条生成的质量波动、调用接口的耗时和限流、输出格式的不稳定、以及最后验收时标准怎么定。
我把需求拆成了四个问题:
- 生成什么:标题、短描述、长描述,各自长度和风格要求。
- 拿什么生成:输入字段有哪些,哪些是必填,哪些是可选。
- 规模多大:总条数、允许失败的比例、总耗时预算。
- 怎么验收:怎么定义“好”,是关键词覆盖率,还是人工抽检通过率。
很多人一上来就急着写提示词、调接口,反而忽略了第3、4问。实际上这两个问题直接决定了技术方案怎么做。比如有严格的耗时预算,就得考虑并发;允许一定失败率,就可以在脚本里加大重试逻辑,而不必追求单次调用的极致成功率。
这次我一开始就明确了验收标准:每条文案必须包含至少两个卖点词,标题不超过30字,短描述50字左右,长描述150字左右,整体风格统一。这个标准后面对接了很多决策,比如temperature调多少、提示词里要不要放示例、要不要加后置校验脚本。可以说,需求定义得越清晰,后面的活儿越省力。
2. 生成链路搭建:模型选型、提示词模板和参数调优
2.1 模型选型不要一步到位,先跑通再升级
很多人选模型时纠结半天,我的建议是:第一版用你最顺手、最便宜的通用对话模型跑通全流程,再根据效果决定要不要换更强的模型。原因很简单,生成类任务的瓶颈往往不是模型能力,而是你对提示词和参数的控制。用一个能力强的模型但提示词写得稀烂,结果未必比弱模型加好提示词强。
这次项目我先用了一个普通的通用对话模型,跑通脚本和验收流程,做了50条测试数据。发现的主要问题集中在格式和风格上,而不是内容理解,这说明模型能力基本够用。后来在个别风格要求很高的品类上,才单独换用更强一点的模型参数或补充示例。
2.2 提示词模板怎么设计才可复用
批量生成和单次对话最大的区别是:提示词必须模板化,而且模板要稳定。我把提示词分成了固定部分和变量部分。固定部分包括角色设定、输出格式要求、风格约束;变量部分用占位符填充,比如商品名、卖点、目标人群。
这里贴一个我最后稳定下来的简化模板:
你是一名电商文案专家。请根据以下信息生成商品详情页文案,要求: 1. 先输出一个不超过30字的标题; 2. 再输出一段50字左右的短描述; 3. 最后输出一段150字左右的长描述。 风格要求:口语化、亲切、不浮夸,避免使用空泛形容词。 务必覆盖以下卖点词(至少出现两个):卖点A、卖点B、卖点C。 目标人群:年轻女性,注重性价比。 商品名称:{商品名} 核心卖点:{卖点} 目标人群:{人群}为什么这么写?关键在于约束的可检查性。一个提示词里如果写了“输出要专业”“要吸引人”,这是不可检查的约束,模型做不到你也无法验收。而“包含至少两个卖点词”“标题不超过30字”这种是可检查的,后面写校验脚本时直接用得上。
除此之外,我还加了一条很重要的规则:文案中不得编造具体奖项、具体技术参数、具体用户评价等事实性内容。这条后面在排查幻觉时派上了大用场。
2.3 温度参数和长度参数别照搬默认值
temperature这个参数,很多人要么不闻不问,要么直接抄别人经验。实际上它的选择应该取决于你的任务类型。这次生成文案是要“有创意但不过头”,我最后用了0.7左右。之前试过默认的1.0,结果文案瑰丽是瑰丽,但卖点词经常一个都不出现,风格也飘得厉害。降到0.3之后倒是稳定了,但文字干巴巴的,像说明书。
长度参数max_tokens也要算一下。中文字符和token不是一对一,通常一个汉字大约对应1.5到2个token。如果你要求150字的长描述,想留点余量,基本要设到350到400 token。我们这次把max_tokens设为500,算是给足裕量,避免输出到一半被截断。截断是批量生成一个特别隐蔽的问题,后面专门讲。
2.4 上下文管理:每次请求都是独立的,别带历史
批量生成场景里,我强烈建议每个请求都走一个新的对话,不要复用长上下文。带上历史对话看似能让模型“记住”风格,但实际上会让输出受到无关信息干扰,还会增加token消耗和延迟。
我的做法是:固定一段系统提示词,每次请求都带上,然后只拼接本次的变量内容。简单、干净、可复现。
3. 批量脚本的落地实现:从单条调用到流水线
3.1 输入数据的准备和校验
原始数据是一份Excel表格,字段有商品名、卖点、目标人群,部分行缺失卖点。用pandas读进来之后,第一件事不是急着生成,而是做数据清洗。缺失卖点的行,我补了一个默认值“品质好,性价比高”,同时在结果表里标记出来,表示这条是补全数据,验收时要重点看。这个细节不起眼,但很重要——因为生成内容里如果有问题,你可以追溯到是哪条原始数据可疑,而不是怀疑模型发神经。
读取和校验的代码大致长这样:
import pandas as pd df = pd.read_excel("products.xlsx") print(df.shape, df.columns.tolist()) df["卖点"] = df["卖点"].fillna("品质好,性价比高") df["来源标记"] = df["卖点"].eq("品质好,性价比高") # 标记补全行3.2 请求封装:重试、超时和异常捕获
调用接口时最容易犯的错是没有重试逻辑。网络抖动、服务端限流,都会导致单条请求失败。如果脚本里不做重试,批量跑到一半断了,你想续跑都很麻烦。我封装了一个函数,加上指数退避的重试机制:
import time, requests, json def generate_text(payload, max_retries=3): for attempt in range(max_retries): try: resp = requests.post( "https://api.example.com/v1/chat/completions", json=payload, timeout=30 ) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"] except Exception as e: wait = 2 ** attempt print(f"第{attempt+1}次失败: {e}, 等待{wait}s") time.sleep(wait) return None这里有个小经验:timeout必须设。如果不设,默认情况下请求可能挂很久,批量脚本卡住,你根本不知道是接口慢了还是服务端没响应。30秒是我试下来比较合适的值,太短容易误杀慢请求,太长浪费时间。
3.3 并发控制:快但别把接口打爆
一百多条数据,串行调用太慢。我当时先试了并发20,结果接口开始返回限流错误。后来把并发降到5,单条耗时控制在2秒左右,整体跑完大概一分多钟,稳定多了。这里的关键是:并发数不是一个固定值,而是取决于你的接口配额。应该先做一个小批量测试,比如拿5条数据并发调,观察成功率和耗时,再逐步加并发。
from concurrent.futures import ThreadPoolExecutor def build_payload(row): user_prompt = TEMPLATE.format( name=row["商品名"], selling_point=row["卖点"], audience=row["目标人群"] ) return { "model": "your-model-name", "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_prompt} ], "temperature": 0.7, "max_tokens": 500 } with ThreadPoolExecutor(max_workers=5) as executor: futures = {executor.submit(generate_text, build_payload(row)): idx for idx, row in df.iterrows()} for future in futures: idx = futures[future] result = future.result() df.at[idx, "生成结果"] = result time.sleep(0.1) # 轻微限速,进一步降低限流概率那个0.1秒的sleep很多人忽略,但实测对稳定性帮助很大。它让每批请求自然错开,不容易触发并发瞬时的集中突刺。
3.4 结果落地:同时存原文和结果,方便回溯
生成结果不能只存一段文案,要把整行上下文都存下来。我用的是JSONL格式,每一行对应一条记录,包含原始输入、生成结果、耗时、重试次数、是否补全数据。这个格式做回溯分析特别好用,出问题的时候一查就知道是哪条、什么输入、跑了多久、失败过几次。
{"商品名":"保温杯","卖点":"304不锈钢, 12小时保温","源数据":"正常","生成结果":"..."}4. 实测遇到最多的五个坑和完整排查思路
4.1 同一份提示词,生成结果风格为什么还是漂
这个问题最气人。明明提示词一字没改,前10条输出风格还很统一,第11条突然变成另一副口吻。一开始我以为是自己代码拼错了参数,排查了半天发现参数没问题,问题出在模型采样的随机性上。
排查思路是:先固定temperature为0,跑10次同样的调用,看结果是否一致。如果一致,说明随机性主要来自temperature;如果不一致,那还要查是不是提示词里有什么不明确的部分。
这次我排查后发现,风格漂移的主要原因是提示词里的“口语化、亲切”这种抽象要求,不同采样下模型会作出不同诠释。解决办法是加一个参考示例。在提示词末尾放一个“参考风格”的例子,模型模仿具体例子的稳定性远高于理解抽象形容词。
4.2 并发一高就超时,是代码问题还是接口限制
有一次我把并发调到20,立刻开始大面积超时和失败。当时第一反应是改代码,把timeout调大。但调大之后更糟,请求全部堆积,整体耗时几乎等于永续等待。
这里要特别提醒:并发升高导致的超时,大概率是服务端限流,而不是你的网络问题。排查方法是在日志里记录HTTP状态码。如果看到429、503这类返回,说明被限流了,这时候翻倍重试间隔比加大timeout有用得多。我最后把并发降回5,加上指数退避,问题彻底消失。
4.3 生成内容里出现编造的奖项和参数
有一批文案里出现了“荣获某国际金奖”“采用第X代技术”这类表述,但原始数据里根本没有这些信息。这就是典型的幻觉。排查思路是看这类内容是不是集中在特定商品或特定提示词变体下,然后针对性加约束。
我的处理方式是在系统提示词里强制加一条规则:“所有事实性陈述必须以输入信息为准,如果输入中不存在该信息,使用‘等亮点’替代具体描述,严禁自行创作数据、奖项或认证。”然后跑了几条验证,幻觉基本消失。这是一个提示词工程层面低成本的解法,如果再不生效,可能就要考虑降低temperature甚至更换模型。
4.4 输出格式不稳定,JSON里混进多余内容
我一度想让模型以JSON格式返回结果,方便直接解析。结果发现成功率不高,经常在JSON前后出现解释性文字,或者JSON被截断少了一半。排查思路是先不追求一次成功,而是想好怎么容错。
最终方案我改成了纯文本分段生成,用固定分隔符:
【标题】... 【短描述】... 【长描述】...然后按分隔符切分。如果切分后缺段,就标记这条为失败并重新生成一次。虽然笨,但稳定性远高于JSON输出。这个经验后来在不少生成任务里复用,效果都很好。
4.5 批量生成的结果里,部分文案重复度过高
有一批文案虽然长度达标、卖点词也覆盖了,但两两之间结构高度相似,读起来全是模板味。排查后发现是提示词里示例只有一个,所有输出都在模仿同一个句式。
解决方法是多给了三个参考风格不同的示例,并明确说“避免与示例句式雷同”。同时把temperature从0.7提到0.8,增加多样性。实际跑下来,重复率明显下降。
5. 从“能出结果”到“能验收”:质量评估和调优闭环
5.1 验收标准要可量化
前面提到这次项目的验收标准是:每条文案包含至少两个卖点词、标题不超过30字、短描述50字左右、长描述150字左右。这些标准都可以写成自动校验脚本,而不是靠肉眼一条条看。
def validate_item(text, selling_points, max_title_len=30): title, desc_short, desc_long = split_by_marker(text) checks = { "标题长度": len(title) <= max_title_len, "短描述长度": 30 <= len(desc_short) <= 80, "长描述长度": 120 <= len(desc_long) <= 200, "卖点覆盖": sum(1 for sp in selling_points if sp in text) >= 2 } return checks这种自动校验脚本的价值在于:它把“感觉不行”变成了“哪里不行”。之后每一次调提示词、调参数,都有了对照的量化结果,而不是凭感觉。
5.2 用失败样本反推提示词问题
第一次全量跑完,人工抽检感觉还不错,但自动校验报告显示“卖点覆盖”这一项失败率高达三成。我翻了几条失败样本,发现两个规律:一是卖点词里的“304不锈钢”这种表达,模型经常写成“不锈钢材质”;二是目标人群是“年轻女性”时,模型会自动写“小姐姐”,导致用词不一致。
于是我把提示词改成“严格沿用卖点词原词,不得改写或替换”,并在提示词中直接举例:“如果卖点词为‘304不锈钢’,文案中必须出现‘304不锈钢’,不能写成‘不锈钢’”。第二次跑,这项失败率降到5%以下。
5.3 调优不是一步到位,要形成循环
整个调优过程不是一次性的,而是循环:全量生成 → 自动校验 → 人工抽检 → 定位失败模式 → 修改提示词或参数 → 重新生成。每轮都保存提示词版本号,记录当时的校验通过率。
比如版本v1.1把temperature从0.7调到0.8,版本v1.2加入参考示例,版本v1.3加了幻觉约束。通过这种小步迭代,而不是一次性把所有改动堆上去,你才能知道哪一项改动是真正起了作用。
这里也顺带说一句:很多项目上来就考虑微调模型,我建议优先把提示词和调度调好。这次项目逻辑上完全没有到需要微调的地步。对大多数生成任务而言,提示词工程的杠杆率高得多,成本也低得多。
6. 这个方法的适用范围和我的几点体会
这套“需求拆解 → 模板化提示词 → 批量脚本 → 自动校验 → 小步调优”的流程,不止能用来写商品文案。我做过的产品摘要生成、文档风格改写、结构化信息抽取,本质上都是同一套逻辑。核心就三点:把输入和输出格式定义到可检查的程度,给足模型约束和示例,再把质量验收自动化。
最后说几句掏心窝的话。做了这么多次生成任务,我最大的感受是:真正花时间的不是“调接口跑脚本”,而是定义清楚“到底要什么”和“怎么算好”。模型本身不挑活,挑活的是你给它的条件清不清楚。提示词改一版可能只要十分钟,但想明白怎么改,往往需要先跑完一轮数据,拿到失败样本,才能对症下药。
还有一个特别实用的小习惯:每次跑完批量生成,把当时的提示词、参数、校验脚本、成功率和失败样本打包存成一个有版本号的文件。下次再遇到类似任务,直接翻开上次的档案,站在上次的结论上继续往下走,而不是每次都从头开始试错。这大概是我在做生成类项目里,性价比最高的一个习惯了。