1. 为什么说“文本扩展”是LLM应用里最被低估的生产力场景开发者在拿到大模型API之后,第一反应通常是让它做总结、改写、翻译、抽取关键词。我自己带过好几轮内部培训,前两个课时大家兴致都很高,一到文本生成和聊天机器人,问题就开始扎堆出现——要么生成内容偏离主题,要么聊不到三轮就开始胡言乱语,要么输出格式一团糟。但真正让我改变教学思路的,是课时10里把“文本扩展”单拎出来讲透之后,大家突然发现,原来很多看似复杂的应用场景,底层都是同一套能力。先说清楚什么是文本扩展。简单讲,就是给模型一段相对简短的输入,让它基于这段输入生成更丰富、更完整、更具体的内容。输入可以是一个标题、几个要点、一段简短的备注,输出可以是一封完整的邮件、一篇社交媒体文案、一份详细的任务说明,甚至是一段带有明确语气和风格的长文本。这个过程不是简单的“扩写”两个字能概括的,它背后依赖的是模型对语义的理解、对上下文的保持,以及对输出格式的控制能力。为什么我说它被低估?因为很多人觉得“让模型写长一点”有什么难的,真到了生产环境才发现:不加约束的扩展就是失控。你要让模型帮你把一条客服回复扩展成一个礼貌、完整、不推卸责任的版本,如果只丢一句话给API,它可能会自作主张加入大量跟原意无关的内容,甚至编出根本不存在的承诺。文本扩展类任务的核心,不是“让它写”,而是“让它在一个明确边界内写”,这恰恰是开发者在设计和写Prompt时最需要下功夫的地方。这篇笔记我会围绕课时10的两个重点来展开:前半部分把文本扩展的原理、参数和实战场景讲透,后半部分用课题11的聊天机器人项目,把文本扩展从单轮能力推进到多轮对话的完整实现。适合正在学习LLM应用开发、想系统理解提示词工程、以及准备做客服机器人或对话式工具的开发者参考。下面的内容不是我对着文档念,是实际跑过API、踩过坑之后的整理。2. Temperature与max_tokens:两个参数背后的生成逻辑与失控风险2.1 温度系数决定的是“选择的确定性”,不是“字数多少”很多初学者有个误解,以为temperature调高,模型输出的字数就会变多。实际上这两个完全没有关系。Temperature控制的是模型在每一步生成时,从候选词概率分布里采样的随机程度。温度越低,模型越倾向于选择概率最高的词,输出越保守、越可预测;温度越高,低概率的词被选中的机会越大,输出越多样、越有“创造力”,但也越容易跑偏。比如在文本扩展场景里,你要把一条销售线索记录“客户对价格敏感,希望得到折扣方案”扩展成一封跟进邮件。温度设为0.2左右,模型写出来的邮件基本围绕价格、折扣、方案这组关键词展开,语气平稳;温度调到0.9,它可能把邮件写成一篇激情澎湃的营销文案,甚至会加入“我们正在推出限时特惠”这种原文里根本没有的信息。对于企业级文本生成,编造信息是致命问题,所以大部分正式场景里,温度都会控制在0到0.4之间。从开发者角度,我的习惯是把temperature当作一个“风险旋钮”而非“创意旋钮”来用。需要模板化、专业、稳定的输出,就压到0.2甚至0;需要生成多个候选再人工挑选,可以用0.5到0.7提多样;低于0.7的场景,我基本不用。这个判断不仅能提升输出质量,还能大幅减少后续的清洗、过滤成本。2.2 max_tokens是硬边界,但它隐藏着“截断即垃圾”的陷阱max_tokens这个参数相对直观,就是限制模型最多生成多少个token。但实际使用中有一个特别容易被忽视的问题:一旦对话总token数达到上限,模型会直接截断输出,而截断位置大概率落在句子中间。这时候你拿到的是一段语义不完整的文本,直接展示给用户也好,写入数据库也好,都会留下一个很难看的尾巴。解决办法有两个层面。第一个层面是用足够大的max_tokens,让它尽量接近完整输出。第二个层面是改变产品逻辑——不要让模型一次生成超大文本,而是引导它分段落、分批次输出。比如做长报告,可以让模型先输出大纲,再按大纲逐段生成;做邮件扩展,可以让它先写正文,再单独补充主题行。这样既能把单次生成控制在合理的token范围内,又能保证每一段都是完整的。我见过一个很典型的翻车案例:某团队给客服机器人设置了固定的max_tokens,用户投诉内容稍长,回复就被截断,后半段是一堆毫无意义的半句话,用户直接截图发到公开展示渠道,体验非常糟糕。这类问题如果提前意识到“截断不是自然结束,而是强制打断”,在设计阶段就能规避。2.3 实测一组温度对比:同一个提示词在不同温度下的表现差异我跑过一组非常直观的对比实验,输入是同一段话:“客户王先生反馈产品安装后无法启动,要求今天内给出解决方案。”分别用temperature=0、0.4、0.8请求同一个模型,得到的三版回复有明显差异。温度0的版本:陈述事实、给出排查步骤、承诺时限,全篇没有多余修饰,像标准工单回复。温度0.4的版本:多了“非常理解您的心情”“我们会优先处理”这类缓冲语气,但仍然保持事实边界。温度0.8的版本:加上了“给您带来不便我们深表歉意,同时我们的技术团队已经第一时间介入”,甚至出现“请放心,我们一定会妥善解决”这样原文没有指向的承诺。这类差异在日常聊天里感知不强,但在客服、法务、财务这类需要字斟句酌的场景里,说错一句话的代价完全不同。我建议每个做文本扩展的开发者,都做一次类似的温度巡检,把高风险场景的边界温度测出来,写进配置清单。这不是学术要求,是上线前的必要检查。3. 从提示词模板到有温度的自动回复:三个可复现的经典场景3.1 由要点扩展成完整邮件:明确读者身份与语气边界文本扩展最经典的落地场景,就是把零散的要点变成结构完整的邮件。我做过的项目里有一类很典型:销售人员在CRM里记录了一堆简短备注,比如“客户提到预算有限,对手报价更低,希望功能简化,月底前要答复”,这些备注直接发出去肯定不行,但它们包含了回复所需的所有关键信息。正确的做法是设计一个包含三个部分的提示词模板。第一部分是约束条件,写明“你是一位专业的客户经理”;第二部分是任务描述,写明“请根据以下要点的信息,撰写一封回复客户的邮件,要求结构完整、语气礼貌、不添加原文未提及的内容”;第三部分是输入数据,也就是那些零散备注。把这三部分用分隔符和标签明确区分,模型输出的稳定性能提升一大截。不要小看“不添加原文未提及的内容”这句话。模型默认的目标是“生成看起来合理的文本”,而不是“忠于原文的文本”。如果不加这个约束,它经常会替你“脑补”方案和承诺。加了之后虽然不能100%杜绝,但能极大降低信息编造率。我在实际项目中测试过,加与不加这个约束,虚构信息出现的比例能差两到三倍。3.2 快捷指令扩展成任务清单:利用元指令制造结构化输出另一个我很常用的场景,是把一句简短指令扩展成可执行的步骤清单。比如成员在群里说“安排一下周五的客户拜访”,这个信息太模糊,但通过文本扩展,可以生成一份包含时间确认、材料准备、路线规划、突发预案、会后跟进五大部分的任务清单。这里的关键不是让模型自由发挥,而是给出一个“输出框架”。我在提示词里直接写出要求:“请将上述指令扩展为一份包含5个模块的任务清单,每个模块用简短标题,模块下用不超过3条要点说明具体动作,不要输出与任务无关的寒暄。”这样模型输出的结构就是可控的,可以直接接入后续的任务流程,不需要再让开发人员写解析逻辑去猜。顺带说一个经验:文本扩展的输出结构越明确,后续接代码就越省事。如果你让模型输出一个JSON格式的任务清单,就用“请输出JSON,包含字段title、steps、deadline”这种极其明确的说明,比写一整段自然语言要求的效果好得多。模型对清晰格式的响应能力,远超大多数人的预期。3.3 差评转化为安抚话术:注入行业常识与合规底线这个场景我做客服系统时验证过很多次。用户在公开评论区写了一条差评,你不可能直接复制用户的原话去回复,但你又需要让客服在短时间内写出一封得体的回信。文本扩展可以把“用户抱怨物流慢”这几个字,扩成一段说明物流状态、表达歉意、给出查询链接、并说明后续保障的完整话术。这个场景的特殊之处在于,它不只是语言层面的扩展,还牵扯到业务红线。比如“赔偿”“退款”“承诺时效”这些敏感词,如果没有在设计提示词时约束,模型可能会替你做出超出权限的承诺。我的做法是把“可承诺事项”和“不可承诺事项”直接写进提示词,比如“可以说明订单正在配送中,不得承诺具体的赔偿金额”。这一条约束,胜过后续加一堆审核逻辑。4. 聊天机器人并非Chatbot:Multi-Turn上下文管理的三个基础动作4.1 模型本身不“记事儿”,是Prompt在替它背记忆从课时10进入课题11,最大的转变是单轮的文本扩展变成了多轮对话。很多初学者第一次写聊天机器人,会以为把多轮历史消息直接拼在一起塞给模型就完事了。跑一轮发现效果还行,跑几轮之后,模型开始答非所问,甚至把用户早前说的一句话当成最新的需求。问题出在哪?出在对“对话记忆”的理解上。大模型本身没有传统意义上的记忆,它看到的每一轮请求都是全新的一次推理。所谓的“上下文”,完全取决于你这次请求里把多少历史对话拼进了Prompt。这意味着,聊天机器人的核心工程问题不是“模型会不会聊天”,而是“你的Prompt构造逻辑能不能准确还原当前对话状态”。我在项目里用的是一个三段式构造方法。第一段是系统指令,固定不变,定义角色和边界;第二段是对话历史,把之前多轮的“用户消息”和“助手消息”按顺序拼进去;第三段是当前用户输入。每一段用明确的标签分隔。这个看起来简单的设计,直接决定了机器人在第10轮、第20轮对话时是否还能保持稳定。4.2 对话历史的取舍:全部拼进去的方案为什么不可持续初学者最常见的做法是把所有历史消息都拼进Prompt,理由是“模型知道的越多回答越准”。实际上,这个方案有两个致命问题。第一个问题是token消耗随对话轮次线性增长,长对话很快会被token上限打爆。第二个问题更隐蔽:历史消息越长,模型对“当前用户意图”的注意力就越容易被稀释,结果就是回答越来越泛、越来越没有针对性。我的做法是给对话历史加上一个策略——保留最近N轮完整对话,更早的历史做摘要或者直接裁剪。具体保留多少轮,取决于业务场景。客服场景里,最近3到5轮的上下文通常就能覆盖大部分问题的连续性;知识问答场景可以适当放宽;多步骤任务型对话则需要更精确的状态管理,不能简单按轮次截断。这个取舍没有绝对正确的答案,需要根据实际测试来调。但方向要明确:对话历史不是越多越好,而是越“精炼且相关”越好。4.3 角色定位与固定指令:为什么每一轮都要重申边界还有一个很多教程里不会强调的细节:每一轮请求都带着同样的系统指令,这件事在代码里看起来有点重复,但对输出质量至关重要。因为模型没有跨请求的记忆,你上一轮对它说的“你是客服助手,只回答产品相关问题”,和当前这一轮没有半毛钱关系。如果当前请求里忘了把系统指令拼进去,它可能会以“通用AI”的身份回答问题,态度、边界、风格都会变。所以我的Prompt模板里,系统指令永远放在最前面,每轮都拼。这样做的好处也不仅仅是维持人设,它还是一个“安全圈”。比如“不回答与技术无关的问题”“不编造产品承诺”这些约束,必须在每一轮都重新陈述,才能持续压制模型生成不可控内容的概率。这不是代码层面的优雅问题,而是稳定性的底层保障。5. 让机器人记住上下文的关键:Prompt中系统性信息与对话历史的拼接策略5.1 一个可落地的Prompt组装模板:System、History、User三层结构先给出一个我实际在用的、可以直接抄的Prompt组装结构。整套逻辑分三块:[系统指令块] 你是“某某产品”的官方客服助手。你的职责范围是:回答与产品使用、订单查询、售后流程相关的问题。对于不属于以上范围的问题,你应当说明无法回答并引导用户联系人工客服。你不得编造订单状态、价格、库存等信息。当前日期是:{当前日期} [对话历史块] 以下是之前的对话记录: 用户:{第1轮用户消息} 助手:{第1轮助手消息} 用户:{第2轮用户消息} 助手:{第2轮助手消息} [当前消息块] 用户:{当前问题}把这三块拼接成一个完整的字符串,作为messages数组里的content发送给模型。如果你用的是支持多消息角色的API,也可以用结构化的system、assistant、user列表来替代纯文本拼接,但核心思路一致:系统信息、历史信息、当前信息三者缺一不可。这个模板看起来简单,但稳定性的关键在于细节。比如在系统指令块里给出当前日期,是因为模型本身就不知道“今天”是什么时候,如果你问它“物流要几天”,它只能靠训练数据里的泛化经验回答,而带上当前日期后,回答会贴近真实时效。这个细节我在项目里反复验证过,加了日期之后,涉及“今天”“明天”“几日内”的回答准确度明显提升。5.2 对话历史的拼接顺序为什么会影响准确性拼接顺序这件事,我要单独拿出来强调。LLM在长文本处理时,对文本不同位置的注意力分布并不均衡,通常开头和结尾的内容更容易被模型重视,中间部分容易出现信息减弱。把系统指令放最前面、把当前用户消息放最后面,是利用了这一注意力特点——模型在生成回复时,最靠后的提示内容往往对输出方向影响最大。如果你把用户当前消息放在中间,系统指令放在最后,模型可能会把“你是客服助手”当成最新指令,或者把对用户问题的聚焦转移到对身份声明的复述上。我在调试一个机器人时出现过这种情况:历史消息里用户问过“你们价格多少”,助手答过“请参考官网”,之后用户再问新问题时,模型还在反复解释“我无法提供价格信息”,原因是历史里的“价格”话题干扰了当前意图识别。正确的做法是:始终保证“最新用户输入”处于Prompt末尾且紧接生成位置。这样即使历史的注意力衰减,当前用户意图也能获得最强权重。如果你发现模型总是答非所问,优先检查是不是Prompt拼接顺序出了问题,而不是急着换更大参数的模型。5.3 什么情况下历史对话需要做重度压缩连续对话超过一定轮数后,即使只保留最近几轮,Prompt长度还是会变得可观。尤其是模型回复本身很长的情况下,几轮对话就能吃掉上千token。这时候如果只是简单地丢掉更早的消息,可能会造成信息断层:用户在第2轮提过一个订单号,到第15轮问“那个订单现在什么状态”,而此时第2轮的订单号已经被裁掉了。针对这类场景,我在历史裁剪的基础上增加了一个“摘要记忆”机制。具体做法是:当对话历史达到设定的水位线时,先调用一次文本摘要能力,把被裁剪掉的历史段落压缩成几个关键信息点,比如“用户订单号:A123;上次承诺回复时间:周五前”。在下一次Prompt组装时,把摘要内容当作历史块的一部分放进去。这样既控制了token增长,又保住了对话的连续性。这个方案我在长会话场景里用了相当长时间,稳定性比纯轮次裁剪高不少。代价是需要额外消耗一次摘要请求,但换来的是对话质量的显著提升,这个取舍在很多业务场景里是划算的。6. 从玩具到工具:Multi-Turn状态保存的方案与取舍6.1 为什么必须在服务端维护会话状态,而不是依赖模型端聊到Multi-Turn,就绕不开“状态保存”这个问题。所谓状态,是指整个对话过程中双方已经交换过的信息总和。对开发者来说,最简单的实现方式是每次请求都把所有历史消息发给模型,让模型自己理解当前状态。这样做代码最少,但工程上不可控。第一个问题是重复性。每次请求都带上全部历史,意味着每一轮对话的输入长度都在增长,费用和延迟都在上升。第二个问题是隔离性。如果你的机器人服务对接了多个用户,而你没有在服务端划分每个用户的独立会话,那所有用户的历史消息可能会在某个环节被错误地拼接到一起,造成跨用户的信息泄漏。第三个问题是恢复性。用户隔了一天回来继续聊,你要是没有持久化存储,就只能让他从头开始。所以从“演示Demo”走向“可用工具”的第一步,就是把对话历史从“每次现场拼”改成“服务端存、按需取”。我在项目里用了一个非常轻量的方案:每次对话完成后,把这一轮的“用户消息+助手消息”写入会话存储,同时维护一个会话ID。下一次用户发消息时,根据会话ID取出最近的N轮历史,和系统指令、当前用户消息一起组装成Prompt。这个改动不大,但它是从玩具到工具的关键一步。6.2 内存级存储、Redis缓存、数据库持久化:三档方案的适用边界状态存到哪里,取决于项目的规模和对话频率。我按实际场景划分成了三档。第一档是内存级存储,直接放在服务进程的Map或Dict里。优点是实现最快、零额外组件,缺点是服务重启就丢,多实例部署无法共享。适合单机Demo、内部测试工具,绝对不适合生产。第二档是Redis缓存,把会话历史和会话ID以Key-Value形式存起来,设置适当的过期时间。优点是读写快、多实例共享、天然支持TTL,适合客服机器人、知识问答这类短时高频会话。第三档是数据库持久化,用关系库或文档库把历史对话落盘。优点是永久保存、支持回溯和分析,缺点是读写开销比Redis大,通常结合缓存分层使用。我自己的默认方案是Redis存热数据、数据库做冷备份。会话进行中,从Redis取历史拼Prompt;会话结束后,把完整记录同步到数据库。这样既保证了对话交互的低延迟,又保留了后续做数据分析、人工审计的可能性。如果你刚开始做,从内存级起步完全可以,但要提前把换到Redis的方案结构留好,避免后期重构。6.3 会话ID的设计:别小看这个字段最后说一个容易被忽略的设计点——会话ID。它看起来只是一个唯一标识,实际上它同时承担了三个职责:路由到正确的历史记录、隔离不同用户的数据、支撑后续的排查和监控。我在项目里用的是“用户标识+会话序号+时间戳”的组合方式,格式大致是uid_20250215_seq。这样做的好处是:后端拿到会话ID就能知道是哪个用户、哪一天、第几段会话,查问题的时候不需要翻数据库才能定位。每个会话的过期策略也围绕会话ID来设计——超过一定天数无活跃,就标记为可归档。有一点要特别注意:会话ID绝对不能从用户输入里直接信任并读取。我见过有人把前端传的字符串直接当Redis Key用,结果遇到注入式的畸形字符串,轻则查不到数据,重则泄露其他会话记录。正确做法是后端根据登录态或临时令牌生成会话ID,用户端只持有这个ID的使用权,不参与生成逻辑。7. 踩坑实录:聊着聊着就跑题、重复、超限,我是怎么一步步排查的7.1 现象一:第五轮开始答非所问,问题出在历史拼接顺序我在调试某跨平台系统的聊天机器人时,遇到过非常典型的现象:前四轮对话都很正常,到第五轮用户问“那这个功能支持团队版吗”,机器人回答的却是第三轮里讨论过的价格问题。当时第一反应是模型能力不够,换了更强的模型,结果依然跑题。后来我在日志里把每一轮的Prompt拼出来看,发现问题出在历史消息的排序。我用的方法是从数据库按时间倒序拉取最近几轮历史,拼进Prompt时直接按返回顺序排列,结果最新的历史变成了最早的内容,最旧的历史反而排在后面。模型看到“最新”的其实是几轮前的消息,自然就把当前输入和错误上下文绑在一起了。排查链路供参考:把每一轮的Prompt和输出同时记录到日志 → 对比前几轮与当前轮的拼接顺序 → 确认“当前用户消息”是否真正处在最后位置 → 修正排序逻辑。这类问题不是模型玄学,就是工程细节。7.2 现象二:机器人重复说“抱歉,我无法回答”,是约束写得太重另一个高频问题是机器人变得异常保守,用户问什么都回“该问题不在我的回答范围内”。原因往往在于系统指令里的限制条件被过度强调,或者模型在历史里看到过多次“无法回答”的先例,产生了自我强化。我排查时发现,历史对话块如果存在多轮“用户提问-助手拒绝”的记录,模型会倾向于学习这个模式,后续遇到相似句式就直接套用拒绝。解决方法是两类:一是调整系统指令的表达,不要在每一轮重复过于强硬的限制词,而是给出明确的引导逻辑,比如“当你不确定时,可以请用户补充信息”;二是定期清理历史中过于消极的问答对,避免模型从历史里学到错误行为。技术人员遇到这类问题,最容易直接调大temperature或换模型,但真正的源头往往在Prompt的历史内容里。多翻日志,少拍脑袋。7.3 现象三:长输出被截断,同时触发token费用飙升这个坑前面提过一点,但这里说具体现象。某次测试中,用户问了一个开放性很强的问题,机器人开始长篇大论地输出,到接近max_tokens边界时被截断,用户追问“然后呢”,机器人又从头生成一遍类似内容,造成重复和费用飙升。根源是我的max_tokens设置得不够大,加上没有在系统消息里约束“回答尽量简洁、分点陈述”。修法分两层:提示词层面,约束输出长度和格式,减少超长输出概率;工程层面,把单次最大token设置到合理阈值,并增加对输出截断的检测——如果检测到截断,就自动追加一条“回复未完成,请补充说明”的后续请求。这个体验问题非常常见,不管是用收费大模型还是本地部署模型都会遇到。处理的关键是提前想清楚“输出被截断之后,产品层要怎么表现”,而不是等用户来投诉。8. 从文本扩展到聊天机器人:我总结的几条实用工程经验8.1 温度不是调一次就完事的,要分场景建配置我在项目里建了一张配置表,把不同场景的temperature、max_tokens、是否带历史摘要的默认值都列出来。比如邮件扩展场景温度固定0.2,客服对话场景0.3,创意文案0.6,仅供内部头脑风暴的生成0.8,整体呈梯度。这样团队成员在调用API时不是凭感觉传参,而是按场景查表取值。这张表本身也会随着测试数据更新。每次发现某个场景输出质量波动,先查温度,再查Prompt,最后才怀疑模型。排查次序固定之后,定位问题会快很多。8.2 历史压缩的触发条件与摘要提示词模板关于历史摘要,我给一个可以直接套用的提示词模板:请对以下对话进行压缩,保留与用户当前需求相关的关键信息,包括:用户提到的具体编号、时间、金额、地址、承诺事项等。忽略寒暄和无关讨论。输出格式为简洁的中文要点列表。在长会话场景里,我设定水位线是“历史对话超过8轮且预估token超过2500”时触发一次摘要压缩。压缩后的摘要替换掉被裁剪的历史,再参与后续组装。这个触发条件不是固定的,需要根据模型窗口大小和响应时延来调整。8.3 给机器人“主动结束话题”的能力:避免无边界对话最后一条经验是给机器人设置“对话出口”。很多开发者在设计聊天机器人时只关注“如何让对话持续”,却忽略了“如何让对话自然结束”。如果机器人永远在接话,用户会陷入无休止的对话流里,既消耗token也消耗耐心。我的做法是在系统指令里加入一条:“当用户的问题已经得到解答,或用户表达了结束意图时,主动总结本次对话要点并告知可以通过‘提新问题’继续求助。”实测下来,这条指令能显著减少无意义的多轮来回,用户满意度反而更高。这算是文本扩展与聊天机器人结合时,最容易被低估但也最体现产品成熟度的一个细节。这些经验不是教科书里的方法论,都是我在真实项目里反复调试、复盘、修正之后沉淀下来的。对刚接触LLM开发的同行来说,照着做可以少走不少弯路;对已经有经验的人来说,也可以当作一个对照清单,看看自己有没有遗漏这些细节。
1. 为什么说“文本扩展”是LLM应用里最被低估的生产力场景开发者在拿到大模型API之后,第一反应通常是让它做总结、改写、翻译、抽取关键词。我自己带过好几轮内部培训,前两个课时大家兴致都很高,一到文本生成和聊天机器人,问题就开始扎堆出现——要么生成内容偏离主题,要么聊不到三轮就开始胡言乱语,要么输出格式一团糟。但真正让我改变教学思路的,是课时10里把“文本扩展”单拎出来讲透之后,大家突然发现,原来很多看似复杂的应用场景,底层都是同一套能力。先说清楚什么是文本扩展。简单讲,就是给模型一段相对简短的输入,让它基于这段输入生成更丰富、更完整、更具体的内容。输入可以是一个标题、几个要点、一段简短的备注,输出可以是一封完整的邮件、一篇社交媒体文案、一份详细的任务说明,甚至是一段带有明确语气和风格的长文本。这个过程不是简单的“扩写”两个字能概括的,它背后依赖的是模型对语义的理解、对上下文的保持,以及对输出格式的控制能力。为什么我说它被低估?因为很多人觉得“让模型写长一点”有什么难的,真到了生产环境才发现:不加约束的扩展就是失控。你要让模型帮你把一条客服回复扩展成一个礼貌、完整、不推卸责任的版本,如果只丢一句话给API,它可能会自作主张加入大量跟原意无关的内容,甚至编出根本不存在的承诺。文本扩展类任务的核心,不是“让它写”,而是“让它在一个明确边界内写”,这恰恰是开发者在设计和写Prompt时最需要下功夫的地方。这篇笔记我会围绕课时10的两个重点来展开:前半部分把文本扩展的原理、参数和实战场景讲透,后半部分用课题11的聊天机器人项目,把文本扩展从单轮能力推进到多轮对话的完整实现。适合正在学习LLM应用开发、想系统理解提示词工程、以及准备做客服机器人或对话式工具的开发者参考。下面的内容不是我对着文档念,是实际跑过API、踩过坑之后的整理。2. Temperature与max_tokens:两个参数背后的生成逻辑与失控风险2.1 温度系数决定的是“选择的确定性”,不是“字数多少”很多初学者有个误解,以为temperature调高,模型输出的字数就会变多。实际上这两个完全没有关系。Temperature控制的是模型在每一步生成时,从候选词概率分布里采样的随机程度。温度越低,模型越倾向于选择概率最高的词,输出越保守、越可预测;温度越高,低概率的词被选中的机会越大,输出越多样、越有“创造力”,但也越容易跑偏。比如在文本扩展场景里,你要把一条销售线索记录“客户对价格敏感,希望得到折扣方案”扩展成一封跟进邮件。温度设为0.2左右,模型写出来的邮件基本围绕价格、折扣、方案这组关键词展开,语气平稳;温度调到0.9,它可能把邮件写成一篇激情澎湃的营销文案,甚至会加入“我们正在推出限时特惠”这种原文里根本没有的信息。对于企业级文本生成,编造信息是致命问题,所以大部分正式场景里,温度都会控制在0到0.4之间。从开发者角度,我的习惯是把temperature当作一个“风险旋钮”而非“创意旋钮”来用。需要模板化、专业、稳定的输出,就压到0.2甚至0;需要生成多个候选再人工挑选,可以用0.5到0.7提多样;低于0.7的场景,我基本不用。这个判断不仅能提升输出质量,还能大幅减少后续的清洗、过滤成本。2.2 max_tokens是硬边界,但它隐藏着“截断即垃圾”的陷阱max_tokens这个参数相对直观,就是限制模型最多生成多少个token。但实际使用中有一个特别容易被忽视的问题:一旦对话总token数达到上限,模型会直接截断输出,而截断位置大概率落在句子中间。这时候你拿到的是一段语义不完整的文本,直接展示给用户也好,写入数据库也好,都会留下一个很难看的尾巴。解决办法有两个层面。第一个层面是用足够大的max_tokens,让它尽量接近完整输出。第二个层面是改变产品逻辑——不要让模型一次生成超大文本,而是引导它分段落、分批次输出。比如做长报告,可以让模型先输出大纲,再按大纲逐段生成;做邮件扩展,可以让它先写正文,再单独补充主题行。这样既能把单次生成控制在合理的token范围内,又能保证每一段都是完整的。我见过一个很典型的翻车案例:某团队给客服机器人设置了固定的max_tokens,用户投诉内容稍长,回复就被截断,后半段是一堆毫无意义的半句话,用户直接截图发到公开展示渠道,体验非常糟糕。这类问题如果提前意识到“截断不是自然结束,而是强制打断”,在设计阶段就能规避。2.3 实测一组温度对比:同一个提示词在不同温度下的表现差异我跑过一组非常直观的对比实验,输入是同一段话:“客户王先生反馈产品安装后无法启动,要求今天内给出解决方案。”分别用temperature=0、0.4、0.8请求同一个模型,得到的三版回复有明显差异。温度0的版本:陈述事实、给出排查步骤、承诺时限,全篇没有多余修饰,像标准工单回复。温度0.4的版本:多了“非常理解您的心情”“我们会优先处理”这类缓冲语气,但仍然保持事实边界。温度0.8的版本:加上了“给您带来不便我们深表歉意,同时我们的技术团队已经第一时间介入”,甚至出现“请放心,我们一定会妥善解决”这样原文没有指向的承诺。这类差异在日常聊天里感知不强,但在客服、法务、财务这类需要字斟句酌的场景里,说错一句话的代价完全不同。我建议每个做文本扩展的开发者,都做一次类似的温度巡检,把高风险场景的边界温度测出来,写进配置清单。这不是学术要求,是上线前的必要检查。3. 从提示词模板到有温度的自动回复:三个可复现的经典场景3.1 由要点扩展成完整邮件:明确读者身份与语气边界文本扩展最经典的落地场景,就是把零散的要点变成结构完整的邮件。我做过的项目里有一类很典型:销售人员在CRM里记录了一堆简短备注,比如“客户提到预算有限,对手报价更低,希望功能简化,月底前要答复”,这些备注直接发出去肯定不行,但它们包含了回复所需的所有关键信息。正确的做法是设计一个包含三个部分的提示词模板。第一部分是约束条件,写明“你是一位专业的客户经理”;第二部分是任务描述,写明“请根据以下要点的信息,撰写一封回复客户的邮件,要求结构完整、语气礼貌、不添加原文未提及的内容”;第三部分是输入数据,也就是那些零散备注。把这三部分用分隔符和标签明确区分,模型输出的稳定性能提升一大截。不要小看“不添加原文未提及的内容”这句话。模型默认的目标是“生成看起来合理的文本”,而不是“忠于原文的文本”。如果不加这个约束,它经常会替你“脑补”方案和承诺。加了之后虽然不能100%杜绝,但能极大降低信息编造率。我在实际项目中测试过,加与不加这个约束,虚构信息出现的比例能差两到三倍。3.2 快捷指令扩展成任务清单:利用元指令制造结构化输出另一个我很常用的场景,是把一句简短指令扩展成可执行的步骤清单。比如成员在群里说“安排一下周五的客户拜访”,这个信息太模糊,但通过文本扩展,可以生成一份包含时间确认、材料准备、路线规划、突发预案、会后跟进五大部分的任务清单。这里的关键不是让模型自由发挥,而是给出一个“输出框架”。我在提示词里直接写出要求:“请将上述指令扩展为一份包含5个模块的任务清单,每个模块用简短标题,模块下用不超过3条要点说明具体动作,不要输出与任务无关的寒暄。”这样模型输出的结构就是可控的,可以直接接入后续的任务流程,不需要再让开发人员写解析逻辑去猜。顺带说一个经验:文本扩展的输出结构越明确,后续接代码就越省事。如果你让模型输出一个JSON格式的任务清单,就用“请输出JSON,包含字段title、steps、deadline”这种极其明确的说明,比写一整段自然语言要求的效果好得多。模型对清晰格式的响应能力,远超大多数人的预期。3.3 差评转化为安抚话术:注入行业常识与合规底线这个场景我做客服系统时验证过很多次。用户在公开评论区写了一条差评,你不可能直接复制用户的原话去回复,但你又需要让客服在短时间内写出一封得体的回信。文本扩展可以把“用户抱怨物流慢”这几个字,扩成一段说明物流状态、表达歉意、给出查询链接、并说明后续保障的完整话术。这个场景的特殊之处在于,它不只是语言层面的扩展,还牵扯到业务红线。比如“赔偿”“退款”“承诺时效”这些敏感词,如果没有在设计提示词时约束,模型可能会替你做出超出权限的承诺。我的做法是把“可承诺事项”和“不可承诺事项”直接写进提示词,比如“可以说明订单正在配送中,不得承诺具体的赔偿金额”。这一条约束,胜过后续加一堆审核逻辑。4. 聊天机器人并非Chatbot:Multi-Turn上下文管理的三个基础动作4.1 模型本身不“记事儿”,是Prompt在替它背记忆从课时10进入课题11,最大的转变是单轮的文本扩展变成了多轮对话。很多初学者第一次写聊天机器人,会以为把多轮历史消息直接拼在一起塞给模型就完事了。跑一轮发现效果还行,跑几轮之后,模型开始答非所问,甚至把用户早前说的一句话当成最新的需求。问题出在哪?出在对“对话记忆”的理解上。大模型本身没有传统意义上的记忆,它看到的每一轮请求都是全新的一次推理。所谓的“上下文”,完全取决于你这次请求里把多少历史对话拼进了Prompt。这意味着,聊天机器人的核心工程问题不是“模型会不会聊天”,而是“你的Prompt构造逻辑能不能准确还原当前对话状态”。我在项目里用的是一个三段式构造方法。第一段是系统指令,固定不变,定义角色和边界;第二段是对话历史,把之前多轮的“用户消息”和“助手消息”按顺序拼进去;第三段是当前用户输入。每一段用明确的标签分隔。这个看起来简单的设计,直接决定了机器人在第10轮、第20轮对话时是否还能保持稳定。4.2 对话历史的取舍:全部拼进去的方案为什么不可持续初学者最常见的做法是把所有历史消息都拼进Prompt,理由是“模型知道的越多回答越准”。实际上,这个方案有两个致命问题。第一个问题是token消耗随对话轮次线性增长,长对话很快会被token上限打爆。第二个问题更隐蔽:历史消息越长,模型对“当前用户意图”的注意力就越容易被稀释,结果就是回答越来越泛、越来越没有针对性。我的做法是给对话历史加上一个策略——保留最近N轮完整对话,更早的历史做摘要或者直接裁剪。具体保留多少轮,取决于业务场景。客服场景里,最近3到5轮的上下文通常就能覆盖大部分问题的连续性;知识问答场景可以适当放宽;多步骤任务型对话则需要更精确的状态管理,不能简单按轮次截断。这个取舍没有绝对正确的答案,需要根据实际测试来调。但方向要明确:对话历史不是越多越好,而是越“精炼且相关”越好。4.3 角色定位与固定指令:为什么每一轮都要重申边界还有一个很多教程里不会强调的细节:每一轮请求都带着同样的系统指令,这件事在代码里看起来有点重复,但对输出质量至关重要。因为模型没有跨请求的记忆,你上一轮对它说的“你是客服助手,只回答产品相关问题”,和当前这一轮没有半毛钱关系。如果当前请求里忘了把系统指令拼进去,它可能会以“通用AI”的身份回答问题,态度、边界、风格都会变。所以我的Prompt模板里,系统指令永远放在最前面,每轮都拼。这样做的好处也不仅仅是维持人设,它还是一个“安全圈”。比如“不回答与技术无关的问题”“不编造产品承诺”这些约束,必须在每一轮都重新陈述,才能持续压制模型生成不可控内容的概率。这不是代码层面的优雅问题,而是稳定性的底层保障。5. 让机器人记住上下文的关键:Prompt中系统性信息与对话历史的拼接策略5.1 一个可落地的Prompt组装模板:System、History、User三层结构先给出一个我实际在用的、可以直接抄的Prompt组装结构。整套逻辑分三块:[系统指令块] 你是“某某产品”的官方客服助手。你的职责范围是:回答与产品使用、订单查询、售后流程相关的问题。对于不属于以上范围的问题,你应当说明无法回答并引导用户联系人工客服。你不得编造订单状态、价格、库存等信息。当前日期是:{当前日期} [对话历史块] 以下是之前的对话记录: 用户:{第1轮用户消息} 助手:{第1轮助手消息} 用户:{第2轮用户消息} 助手:{第2轮助手消息} [当前消息块] 用户:{当前问题}把这三块拼接成一个完整的字符串,作为messages数组里的content发送给模型。如果你用的是支持多消息角色的API,也可以用结构化的system、assistant、user列表来替代纯文本拼接,但核心思路一致:系统信息、历史信息、当前信息三者缺一不可。这个模板看起来简单,但稳定性的关键在于细节。比如在系统指令块里给出当前日期,是因为模型本身就不知道“今天”是什么时候,如果你问它“物流要几天”,它只能靠训练数据里的泛化经验回答,而带上当前日期后,回答会贴近真实时效。这个细节我在项目里反复验证过,加了日期之后,涉及“今天”“明天”“几日内”的回答准确度明显提升。5.2 对话历史的拼接顺序为什么会影响准确性拼接顺序这件事,我要单独拿出来强调。LLM在长文本处理时,对文本不同位置的注意力分布并不均衡,通常开头和结尾的内容更容易被模型重视,中间部分容易出现信息减弱。把系统指令放最前面、把当前用户消息放最后面,是利用了这一注意力特点——模型在生成回复时,最靠后的提示内容往往对输出方向影响最大。如果你把用户当前消息放在中间,系统指令放在最后,模型可能会把“你是客服助手”当成最新指令,或者把对用户问题的聚焦转移到对身份声明的复述上。我在调试一个机器人时出现过这种情况:历史消息里用户问过“你们价格多少”,助手答过“请参考官网”,之后用户再问新问题时,模型还在反复解释“我无法提供价格信息”,原因是历史里的“价格”话题干扰了当前意图识别。正确的做法是:始终保证“最新用户输入”处于Prompt末尾且紧接生成位置。这样即使历史的注意力衰减,当前用户意图也能获得最强权重。如果你发现模型总是答非所问,优先检查是不是Prompt拼接顺序出了问题,而不是急着换更大参数的模型。5.3 什么情况下历史对话需要做重度压缩连续对话超过一定轮数后,即使只保留最近几轮,Prompt长度还是会变得可观。尤其是模型回复本身很长的情况下,几轮对话就能吃掉上千token。这时候如果只是简单地丢掉更早的消息,可能会造成信息断层:用户在第2轮提过一个订单号,到第15轮问“那个订单现在什么状态”,而此时第2轮的订单号已经被裁掉了。针对这类场景,我在历史裁剪的基础上增加了一个“摘要记忆”机制。具体做法是:当对话历史达到设定的水位线时,先调用一次文本摘要能力,把被裁剪掉的历史段落压缩成几个关键信息点,比如“用户订单号:A123;上次承诺回复时间:周五前”。在下一次Prompt组装时,把摘要内容当作历史块的一部分放进去。这样既控制了token增长,又保住了对话的连续性。这个方案我在长会话场景里用了相当长时间,稳定性比纯轮次裁剪高不少。代价是需要额外消耗一次摘要请求,但换来的是对话质量的显著提升,这个取舍在很多业务场景里是划算的。6. 从玩具到工具:Multi-Turn状态保存的方案与取舍6.1 为什么必须在服务端维护会话状态,而不是依赖模型端聊到Multi-Turn,就绕不开“状态保存”这个问题。所谓状态,是指整个对话过程中双方已经交换过的信息总和。对开发者来说,最简单的实现方式是每次请求都把所有历史消息发给模型,让模型自己理解当前状态。这样做代码最少,但工程上不可控。第一个问题是重复性。每次请求都带上全部历史,意味着每一轮对话的输入长度都在增长,费用和延迟都在上升。第二个问题是隔离性。如果你的机器人服务对接了多个用户,而你没有在服务端划分每个用户的独立会话,那所有用户的历史消息可能会在某个环节被错误地拼接到一起,造成跨用户的信息泄漏。第三个问题是恢复性。用户隔了一天回来继续聊,你要是没有持久化存储,就只能让他从头开始。所以从“演示Demo”走向“可用工具”的第一步,就是把对话历史从“每次现场拼”改成“服务端存、按需取”。我在项目里用了一个非常轻量的方案:每次对话完成后,把这一轮的“用户消息+助手消息”写入会话存储,同时维护一个会话ID。下一次用户发消息时,根据会话ID取出最近的N轮历史,和系统指令、当前用户消息一起组装成Prompt。这个改动不大,但它是从玩具到工具的关键一步。6.2 内存级存储、Redis缓存、数据库持久化:三档方案的适用边界状态存到哪里,取决于项目的规模和对话频率。我按实际场景划分成了三档。第一档是内存级存储,直接放在服务进程的Map或Dict里。优点是实现最快、零额外组件,缺点是服务重启就丢,多实例部署无法共享。适合单机Demo、内部测试工具,绝对不适合生产。第二档是Redis缓存,把会话历史和会话ID以Key-Value形式存起来,设置适当的过期时间。优点是读写快、多实例共享、天然支持TTL,适合客服机器人、知识问答这类短时高频会话。第三档是数据库持久化,用关系库或文档库把历史对话落盘。优点是永久保存、支持回溯和分析,缺点是读写开销比Redis大,通常结合缓存分层使用。我自己的默认方案是Redis存热数据、数据库做冷备份。会话进行中,从Redis取历史拼Prompt;会话结束后,把完整记录同步到数据库。这样既保证了对话交互的低延迟,又保留了后续做数据分析、人工审计的可能性。如果你刚开始做,从内存级起步完全可以,但要提前把换到Redis的方案结构留好,避免后期重构。6.3 会话ID的设计:别小看这个字段最后说一个容易被忽略的设计点——会话ID。它看起来只是一个唯一标识,实际上它同时承担了三个职责:路由到正确的历史记录、隔离不同用户的数据、支撑后续的排查和监控。我在项目里用的是“用户标识+会话序号+时间戳”的组合方式,格式大致是uid_20250215_seq。这样做的好处是:后端拿到会话ID就能知道是哪个用户、哪一天、第几段会话,查问题的时候不需要翻数据库才能定位。每个会话的过期策略也围绕会话ID来设计——超过一定天数无活跃,就标记为可归档。有一点要特别注意:会话ID绝对不能从用户输入里直接信任并读取。我见过有人把前端传的字符串直接当Redis Key用,结果遇到注入式的畸形字符串,轻则查不到数据,重则泄露其他会话记录。正确做法是后端根据登录态或临时令牌生成会话ID,用户端只持有这个ID的使用权,不参与生成逻辑。7. 踩坑实录:聊着聊着就跑题、重复、超限,我是怎么一步步排查的7.1 现象一:第五轮开始答非所问,问题出在历史拼接顺序我在调试某跨平台系统的聊天机器人时,遇到过非常典型的现象:前四轮对话都很正常,到第五轮用户问“那这个功能支持团队版吗”,机器人回答的却是第三轮里讨论过的价格问题。当时第一反应是模型能力不够,换了更强的模型,结果依然跑题。后来我在日志里把每一轮的Prompt拼出来看,发现问题出在历史消息的排序。我用的方法是从数据库按时间倒序拉取最近几轮历史,拼进Prompt时直接按返回顺序排列,结果最新的历史变成了最早的内容,最旧的历史反而排在后面。模型看到“最新”的其实是几轮前的消息,自然就把当前输入和错误上下文绑在一起了。排查链路供参考:把每一轮的Prompt和输出同时记录到日志 → 对比前几轮与当前轮的拼接顺序 → 确认“当前用户消息”是否真正处在最后位置 → 修正排序逻辑。这类问题不是模型玄学,就是工程细节。7.2 现象二:机器人重复说“抱歉,我无法回答”,是约束写得太重另一个高频问题是机器人变得异常保守,用户问什么都回“该问题不在我的回答范围内”。原因往往在于系统指令里的限制条件被过度强调,或者模型在历史里看到过多次“无法回答”的先例,产生了自我强化。我排查时发现,历史对话块如果存在多轮“用户提问-助手拒绝”的记录,模型会倾向于学习这个模式,后续遇到相似句式就直接套用拒绝。解决方法是两类:一是调整系统指令的表达,不要在每一轮重复过于强硬的限制词,而是给出明确的引导逻辑,比如“当你不确定时,可以请用户补充信息”;二是定期清理历史中过于消极的问答对,避免模型从历史里学到错误行为。技术人员遇到这类问题,最容易直接调大temperature或换模型,但真正的源头往往在Prompt的历史内容里。多翻日志,少拍脑袋。7.3 现象三:长输出被截断,同时触发token费用飙升这个坑前面提过一点,但这里说具体现象。某次测试中,用户问了一个开放性很强的问题,机器人开始长篇大论地输出,到接近max_tokens边界时被截断,用户追问“然后呢”,机器人又从头生成一遍类似内容,造成重复和费用飙升。根源是我的max_tokens设置得不够大,加上没有在系统消息里约束“回答尽量简洁、分点陈述”。修法分两层:提示词层面,约束输出长度和格式,减少超长输出概率;工程层面,把单次最大token设置到合理阈值,并增加对输出截断的检测——如果检测到截断,就自动追加一条“回复未完成,请补充说明”的后续请求。这个体验问题非常常见,不管是用收费大模型还是本地部署模型都会遇到。处理的关键是提前想清楚“输出被截断之后,产品层要怎么表现”,而不是等用户来投诉。8. 从文本扩展到聊天机器人:我总结的几条实用工程经验8.1 温度不是调一次就完事的,要分场景建配置我在项目里建了一张配置表,把不同场景的temperature、max_tokens、是否带历史摘要的默认值都列出来。比如邮件扩展场景温度固定0.2,客服对话场景0.3,创意文案0.6,仅供内部头脑风暴的生成0.8,整体呈梯度。这样团队成员在调用API时不是凭感觉传参,而是按场景查表取值。这张表本身也会随着测试数据更新。每次发现某个场景输出质量波动,先查温度,再查Prompt,最后才怀疑模型。排查次序固定之后,定位问题会快很多。8.2 历史压缩的触发条件与摘要提示词模板关于历史摘要,我给一个可以直接套用的提示词模板:请对以下对话进行压缩,保留与用户当前需求相关的关键信息,包括:用户提到的具体编号、时间、金额、地址、承诺事项等。忽略寒暄和无关讨论。输出格式为简洁的中文要点列表。在长会话场景里,我设定水位线是“历史对话超过8轮且预估token超过2500”时触发一次摘要压缩。压缩后的摘要替换掉被裁剪的历史,再参与后续组装。这个触发条件不是固定的,需要根据模型窗口大小和响应时延来调整。8.3 给机器人“主动结束话题”的能力:避免无边界对话最后一条经验是给机器人设置“对话出口”。很多开发者在设计聊天机器人时只关注“如何让对话持续”,却忽略了“如何让对话自然结束”。如果机器人永远在接话,用户会陷入无休止的对话流里,既消耗token也消耗耐心。我的做法是在系统指令里加入一条:“当用户的问题已经得到解答,或用户表达了结束意图时,主动总结本次对话要点并告知可以通过‘提新问题’继续求助。”实测下来,这条指令能显著减少无意义的多轮来回,用户满意度反而更高。这算是文本扩展与聊天机器人结合时,最容易被低估但也最体现产品成熟度的一个细节。这些经验不是教科书里的方法论,都是我在真实项目里反复调试、复盘、修正之后沉淀下来的。对刚接触LLM开发的同行来说,照着做可以少走不少弯路;对已经有经验的人来说,也可以当作一个对照清单,看看自己有没有遗漏这些细节。