资讯动态

文本生成工程实战:从零搭建稳定生成流水线

发布时间:2026/10/14 16:36:53 来源:尧图企业网站定制
最近在折腾一套内容生成流程的时候有个朋友问我你天天说“生成实战”到底实战什么是不是就是把几句话扔给聊天窗口拿到一段文字就算完事说实话最开始我确实是这样想的但真把一个生成任务做成可复用的项目之后才发现事情远没那么简单。“生成实战1”这个项目就是我从零搭起来的一条文本生成流水线输入原始素材通过提示词组装、模型调用、结构化输出和结果校验四个环节最终稳定地产出一批风格统一、格式干净、基本可用的正文初稿。这个系列的定位很明确——不是讲模型原理也不是做论文复现而是把“生成”这件事当成一个工程任务来拆解适合那些需要批量产出内容的技术同学、内容运营和产品经理参考。如果你也想把生成能力接到自己的业务里却不知道从哪下手这篇文章应该能帮你省掉不少弯路。我会把整个项目的设计思路、模型参数选择、代码实现、迭代优化以及踩坑记录全部摊开来说尽量做到看完就能照着跑。1. 项目定位与思路拆解1.1 这个实战想解决什么问题先交代一下背景。这个项目的原始需求其实很接地气我手头有一批结构零散的产品资料需要把它们整理成统一格式的介绍文案而且量还挺大大概每篇都需要几百字一共有上百篇。如果靠人工逐篇修改不仅耗时间还容易出现前后风格不一致的问题。所以当时的想法很朴素能不能让生成模型先帮我写初稿我再做抽检和修改但真正动手之后我意识到“让模型写初稿”这个说法太模糊了。你需要回答几个问题输入是什么格式输出要什么结构风格如何定义模型答偏了怎么办生成结果里的常识错误怎么拦截这些细节决定了一个生成需求到底能不能落地。所以我把这个项目定义为“生成实战1”不追求做一个大而全的生成平台而是先跑通一条最小可用的文本生成流水线把核心问题解决掉——怎样让模型按照预设的格式稳定输出怎样让生成的初稿质量可控以及怎样把生成过程变成可观测、可修改的工程流程。1.2 整体设计从输入到输出的四段式流水线这个项目的架构很简单但每一段都承担了明确职责整个流程可以拆成四层输入解析层把原始素材做清洗和切分提取出每篇文章需要用到商品名、核心卖点、目标人群等关键字段。提示词组装层根据固定模板和解析出的字段动态生成每次调用的提示词。这里不需要模型参与纯粹是字符串拼接好处是方便排查和审计。模型调用层把提示词发给生成模型并设置好温度、输出长度等参数同时加上重试和超时控制。输出校验层对模型返回的内容做格式检查、长度检查、敏感词过滤和质量评分不合格的自动重新生成或标记为待人工处理。为什么要把“输出校验”单独拆出来这是我在实际项目中踩了坑才养成的习惯。第一次做的时候我以为只要把提示词写清楚模型就会老老实实按格式输出结果发现完全不是这么回事有时候多两个字有时候少一个标题有时候把字段名都改了。如果不在代码里做强制校验下游解析就会全线崩盘。所以输出校验不是可选项而是生成流程的标配。2. 工具选型与参数配置2.1 模型选型本地还是 API这个项目启动时模型选型是我纠结最久的一件事。当时有两个方向一个是本地部署开源模型好处是数据不出内网、没有按调用量计费的压力另一个是调用商业 API好处是上手快、模型能力强且稳定。我后来的选择是“默认 API敏感模块走本地”理由是项目周期紧优先保证效果和迭代速度。如果你也在做类似的评估建议从这四个维度拉一个对比表维度本地开源模型商业 API 模型部署成本需要 GPU 资源运维复杂零部署注册即可调用生成质量中等至优秀取决于模型选择通常上限更高稳定性更好数据隐私数据完全本地存在数据外传风险单次调用成本固定硬件成本按 token 计费每个项目的约束不同没有绝对正确的答案。我的建议是如果你只是验证业务可行性优先用 API如果业务已经跑通、数据敏感度高、调用量很大再考虑迁移到本地模型。不要一上来就投入大量资源部署开源模型因为模型微调、推理优化、并发控制这些坑会消耗你大量精力。2.2 生成参数背后的逻辑很多人调用模型的时候只填一个提示词其余参数全部默认。但实际调参非常能体现功力。这里我把项目里用到的主要参数拿出来说一下temperature控制输出的随机性值的范围一般是 0 到 1有的模型扩大到 2。数值越接近 0输出就越保守、稳定越接近 1就越有创造性。在批量生成内容初稿时我建议从 0.7 起调如果发现内容太“放飞”逐渐降低到 0.5 左右。不要一上来就用 0否则输出会非常机械缺少自然语气。top_p核采样控制模型从累计概率达到 p 的最小 token 集合中采样。和 temperature 有协同作用建议改一个参数时先固定另一个。我的习惯是固定 top_p0.9只调 temperature。max_tokens限制输出长度。这个参数不仅影响成本还会影响模型是否“偷懒”。如果设得太短模型可能列完提纲就没位置写正文了设得太长不必要的内容会增加解析难度。我一般根据目标输出字数加上约 20% 的冗余来计算。frequency_penalty 和 presence_penalty惩罚重复词和话题重复。在长文本生成中如果发现内容开始车轱辘话来回说可以适当调高 presence_penalty。这里有个容易被忽略的点参数不是拍脑袋定的需要结合自己的任务做一组小实验。我在项目里做了一个“五组参数对比”每组生成三篇样本文本人工看一下流畅度和格式稳定性最后选定了 temperature0.7、top_p0.9、max_tokens1200 这组组合。整个过程大概花了一小时但避免了后续反复返工。2.3 提示词模板的结构化设计提示词是这个项目最容易“感觉良好、实际跑偏”的部分。刚开始我写的提示词就是一句话“请根据以下资料生成一篇文章。”结果模型自由发挥输出的内容完全不能用。后来我把提示词模板做了结构化处理固定包含以下五个区块角色设定让模型知道自己要以什么身份来回答比如“你是一位资深产品编辑”。任务描述明确要做什么例如“根据给定素材生成一篇规格统一的产品介绍文案”。输入变量把解析出的字段放进来包括商品名、核心卖点、目标人群。输出要求对格式、长度、风格做出明确约束并说明不要输出什么。示例给出一个完整的输入输出配对作为 few-shot 示例。一个可复用的模板大概是这样的你是一位资深产品编辑。请根据以下【产品资料】生成一篇产品介绍文案。 要求 1. 全文不超过 400 字分为三个自然段。 2. 开头直接点出产品名称和核心定位。 3. 第二段写清楚三个核心卖点每个卖点用一句话概括。 4. 结尾引导用户进一步了解产品。 5. 不要输出标题不要使用“综上所述”之类的话。 【产品资料】 { 产品名称: 便携保温杯, 核心卖点: 轻量、保温、防漏, 目标人群: 通勤族 } 【示例】 便携保温杯是一款专为通勤族设计的随身饮水装备...不要小看“示例”这个区块。我给模型加了一个示例之后输出格式的准确率从大概 70% 提升到了接近 95%。模型本质上是在做模式匹配你给它越明确的参照它就越不容易跑偏。3. 实操过程与关键环节实现3.1 环境准备与依赖安装这步本身不复杂但会卡住不少人。我先创建了一个干净的虚拟环境然后安装必要依赖。项目使用 Python 3.10核心依赖包括用于调 HTTP 接口的 requests以及用于 JSON 结构化解析的 jsonschema。如果你的环境里之前装过一些乱七八糟的包强烈建议用虚拟环境隔离。python3.10 -m venv .genv source .genv/bin/activate pip install requests jsonschema一条条说下为什么这么装。requests 几乎是我每次项目都会用的库它语法简洁、错误处理方便jsonschema 用来校验模型输出的 JSON 结构可以避免拿到一段残缺的 JSON 后在业务代码里反复 try-except。如果公司内部有统一的 API SDK也可以直接引 SDK但原理都一样请求、响应、校验。3.2 第一版生成代码的初步实现我当时的第一个版本写得很直白一个函数完成组装提示词并请求模型的操作。先看代码雏形import os import requests API_URL os.getenv(LLM_API_URL) API_KEY os.getenv(LLM_API_KEY) def generate(prompt: str, temperature: float 0.7) - str: headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: text-gen-v1, prompt: prompt, temperature: temperature, max_tokens: 1200, top_p: 0.9, } resp requests.post(API_URL, headersheaders, jsonpayload, timeout60) resp.raise_for_status() data resp.json() return data[choices][0][text]这个版本有两个隐患。第一没有重试逻辑。模型接口偶尔会超时或者返回 5xx一次失败就直接让整个任务崩溃。第二没有记录每次调用的输入和输出。调试时想复现某个生成结果却不知道当时提示词长什么样。后来我专门做了一个日志装饰器把每次请求的模型参数、时间戳、输入和输出都落到本地 JSON 文件里。这个日志在后续排查问题时帮了大忙。3.3 从“能跑”到“好用”的结构化输出第一版跑通后下一步是把输出定义为 JSON。为什么是 JSON因为生成的内容最终要入库、要往页面模板里填结构化的字段名比一段纯文本要安全得多。于是我把输出要求改成了这样的 JSON 结构{ title: 文章标题, summary: 一句话摘要, paragraphs: [第一段内容, 第二段内容, 第三段内容] }在提示词里明确要求“只输出 JSON不要输出其他任何文字”。接着在代码里加入两层校验第一层用 json.loads 判断是否为合法 JSON第二层用 jsonschema 判断字段类型和必填项是否齐全。import json import jsonschema output_schema { type: object, properties: { title: {type: string}, summary: {type: string}, paragraphs: {type: array, items: {type: string}} }, required: [title, summary, paragraphs] } def parse_generated_text(raw: str) - dict: text raw.strip() if text.startswith(): text text.strip() if text.startswith(json): text text[4:].strip() data json.loads(text) jsonschema.validate(instancedata, schemaoutput_schema) return data这段代码解决了两个真实痛点模型偶尔会额外输出一段“好的这是生成结果如下”之类的前置废话代码里直接把这些干扰剥掉模型还可能把段落写成字符串数组以外的类型jsonschema 会直接报错避免脏数据继续往下游流。3.4 重试策略与并发控制处理一定数量级的数据后单线程逐条调用模型显然太慢了。我的策略是用线程池控制并发同时加上指数退避的重试逻辑。这里要特别提醒一下不要把并发调得太高很多模型服务都有每分钟调用次数限制一旦触发限流反而会拖慢整体进度。import time from concurrent.futures import ThreadPoolExecutor def call_with_retry(prompt, max_retries3): for attempt in range(max_retries): try: return generate(prompt) except Exception as err: if attempt max_retries - 1: raise err wait 2 ** attempt 1 time.sleep(wait) def batch_generate(prompt_list, workers4): with ThreadPoolExecutor(max_workersworkers) as executor: results list(executor.map(call_with_retry, prompt_list)) return results实际测试下来4 个并发对于大多数 API 接口都已经接近临界值。你可以从 2 开始试慢慢调观察服务端是否出现 429 限流。没有限流信息时保守永远比激进好。4. 真实战场迭代优化记录4.1 第一轮结果到底有多翻车项目第一次批量跑完大概生成了 20 篇内容。我随机抽出 5 篇来看问题非常集中内容空泛。模型写出来的东西看起来通顺但信息密度极低比如“该产品体验出色、性能强劲”这类话占了一半篇幅。格式不符合预期。虽然提示词里要求输出三段但有几篇只写了两段还有一篇把示例里的内容原封不动抄了进去。低概率幻觉。有一篇把某个产品规格写错虽然看着毫不起眼但一旦发布出去就会出问题。这说明仅靠提示词约束还不够。模型生成本质上是一个概率过程你再怎么强调要求它也不可能百分之百稳定。我的处理方式是不追求一次生成到完美而是在流程里增加校验和修正环节。具体做法是第一轮生成之后先把输出文本里的关键信息点提取出来和原始资料做一次关键词和实体匹配没有匹配上的内容打上“待人工复核”标签。不要试图用模型去修正另一个模型这不靠谱人工复核在当前阶段仍然不可替代。4.2 针对性优化手段从“加提示”到“加流程”针对内容空泛的问题我的优化方案不是无限堆提示词而是改变生成策略。给模型“打草稿再成文”的指令先让它列出内容提纲确认后再逐段展开。这个办法对长文本尤其有效。短文本生成时提纲会让内容更聚焦。请先生成文章的三段提纲每段用一句话概括。提纲确认无误之后再按以下格式输出最终正文 ...这个改动看起来很小但显著减少了废话比例。因为模型在有提纲约束时更像在做信息整理而不是自由发挥生成结果明显更有条理。另外我引入了“两遍生成”流程第一遍生成完整内容第二遍只用模型做“格式整理者”把第一遍的生成结果规范成目标 JSON 格式。这会多花一点 token但对格式的稳定性很有帮助。4.3 成本和性能的实测对比为了让大家有个直观感知我把优化前后的数据放到一张表里指标优化前第一版优化后稳定版平均生成耗时单篇18 秒22 秒格式合规率约 72%约 96%人工修改量每篇改动较多每篇轻微修改单篇 token 消耗约 700约 980做完这轮对比我最大的体会是多花一点 token 换格式稳定和人工时间是非常划算的。很多团队只盯着生成环节的成本却忽略了人工返工的时间成本后者的昂贵程度远超出想象。在真实业务里一篇稿子的人工审校成本往往是生成成本的数倍甚至数十倍。5. 常见问题排查速查表与安全底线5.1 高频问题原因与解法做这个项目期间我把遇到过的典型问题整理成一份速查表分享给你典型问题可能原因解决方向输出内容重复temperature 过低或存在重复惩罚不足适当提高 temperature调高 presence_penalty输出一段空话提示词缺少约束条件增加“必须包含具体数据和指标”的硬性要求输出格式乱掉提示词示例不足增加 few-shot 示例用结构化输出强制校验响应超时单条提示词过长或并发过高拆短输入降低并发设置合理的超时时间模型编造信息生成任务本身需要事实性回答增加外部知识库校验关键信息强制从资料中提取数据丢失或不完整max_tokens 设置太小为输出长度预留冗余同时压缩输入内容这些问题没有一个是模型“不聪明”导致的更多是使用方式的问题。把这些问题整理成表格后我在后续项目中排查效率提高了很多。建议你在自己的项目里也做一份这样的速查表它会成为团队知识库里的重要资产。5.2 内容安全与合规底线生成内容越接近业务就越要重视内容安全。我在这里说的安全不是网络安全而是内容本身的风险控制。我的做法是在生成流水线里同时加入三层过滤输入侧过滤在用户输入和原始文本进入提示词之前先做敏感词检测和长度截断避免异常内容混入提示词。输出侧过滤模型生成结果出来后先过一道敏感词库和格式校验默认情况下禁止直接把模型输出发送到公开页面。人工审批规则所有生成结果都必须经过人工抽检或全检才能发布。没有任何自动生成系统能完全替代人的判断尤其涉及对外展示的内容。另外还有一个容易被忽视的细节不要把用户输入直接拼进提示词而不做限制。有些恶意用户会试图通过“忽略之前指令”之类的输入引导模型突破边界。这就是常见的提示注入攻击。我的防护策略很简单在提示词里声明输入数据仅仅作为待处理的素材不允许对系统指令做任何修改同时把输入长度限制在一定范围内超出就拒绝处理。这个策略不能说百分之百防止所有攻击但已经能挡住绝大多数风险。6. 一些真正值得保留的实战习惯6.1 永远给生成结果打日志这个习惯是我在这个项目里收获最大的一环。每生成一条内容我都会把时间戳、提示词版本、模型参数、原始返回、最终解析结果记录到本地日志文件。刚开始觉得麻烦但后来一旦线上出问题我都能通过回放日志快速定位是提示词变了还是模型偶发抖动还是下游解析逻辑有问题。没有日志你就是盲人摸象只能一遍遍重新生成碰运气。6.2 提示词也要做版本管理很多人都维护代码的版本却忽略提示词同样需要版本管理。我在项目里用一组 Markdown 文件保存所有提示词模板文件名规范为“日期_版本_用途”。每次调整都留下一个副本而不是直接覆盖原文件。这样做的直接好处是如果某次优化后效果反而变差可以一分钟之内回到上一个稳定版本。6.3 别把模型输出当最终答案写完这个项目我最想强调的一点是生成模型的角色是“初稿生成器”而不是“事实来源”。以当前模型的特性它仍然会流畅地编造内容。所以我在上下游做了很多“校验”而不是轻信。这种思路也可以沿用到你自己的业务里任何关键数据、引用和事实表述都要回到可靠的知识源核对生成内容才能安全地用起来。如果你也想动手做一个类似的生成实战项目我的建议是别一上来就追求复杂架构。先把一条最小路径跑通再逐步加上重试、校验、日志、版本管理这些外挂系统。生成能力只是发动机真正让它在业务里平稳运行的是围绕它搭建的一整套工程保障。后面我打算把这一系列继续往下写下一期可以聊聊如何把图片生成的能力接入同一套流水线以及怎么处理“文生图”场景里更复杂的结果质量评估问题。如果你在实际操作里遇到过别的问题也欢迎回来对照这篇记录聊一聊。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑