把祝福语塞进向量库,就能“走心”吗?RAG 在祝福场景的真实答案
不知道从什么时候开始,很多团队做 AI 功能的第一反应就是“上 RAG”。我接手过一个内部测试任务:用 RAG 做祝福语生成。想法看起来非常顺理成章——收集几百条高质量祝福语,清洗后灌进向量库,用户输入“给朋友的中秋祝福”“给导师的毕业感谢”,系统从库里检索最相关的片段,拼进大模型提示词,生成一段个性化祝福文案。听起来优雅、有技术含量,还解决了“纯靠大模型容易乱编”的痛点。
但真实跑下来,效果离“走心”差了十万八千里。用户反馈的普遍评价是:“读起来确实顺,但一看就知道是 AI 写的”“感觉每一句都似曾相识,像是在拼积木”。这篇文章就来聊聊这次项目从想法到落地的完整拆解:数据怎么准备、架构怎么搭、检索为什么失效、改进后效果如何,以及最后我得到的那个不太好听的结论——RAG 在这个场景里,能当配角,但当不了主角。
1. 为什么祝福语这个场景,一眼看去特别适合 RAG
1.1 祝福语的三重特性撞上了 RAG 的舒适区
先说直觉上适配的理由。第一,祝福语是典型的“知识密集 + 长尾需求”场景。一个人可能记不住几十种场合下该怎么措辞,但语料库里可以存下几千条不同风格、不同场合、不同关系的祝福语。这天然符合“先检索、再生成”的范式。第二,用户需求高度个性化。同样是中秋节祝福,发给客户、发给闺蜜、发给长辈,内容差异极大,固定模板根本覆盖不过来。第三,祝福语语料获取成本极低,网上公开文案、书籍金句、历史聊天记录里的优秀表达都可以收集,不像医疗、法律那种专业领域需要专家标注。
这三个特征叠加在一起,几乎是 RAG 的标准“样板间”。团队当时的判断是:这不就是个“文档问答”的换皮版吗?用户问“帮我生成一段祝福”,系统回答“一段祝福”,检索的是文案片段而不是事实,生成压力也小。于是项目很快立项,目标也很明确:做一个能根据场合、关系、语气自动生成祝福文案的 Demo,内部代号“某祝福生成实验”。
1.2 但祝福语本质上不是“问答”,而是“情绪传递”
真正开始做之后才发现,这个类比有致命缺陷。文档问答的核心诉求是“信息准确”,比如“合同里违约为啥赔偿 30%”,用户要的是事实。而祝福语的核心诉求是“情绪到位”,用户要的不是信息的正确性,而是读起来像“一个懂我的人在对我说一句心里话”。
这个区别直接影响了检索的目标。在文档问答里,检索的目标是“找到包含答案的那一段文字”;而在祝福场景里,检索的目标是“找到情感姿态最贴近当前关系的那一段表达”。向量检索擅长前者,不擅长后者。因为句向量把“语义相近”映射到距离相近,但“语义相近”在祝福语里往往不等于“情感合适”。举个例子,“祝您福如东海、寿比南山”和“祝您身体健康、万事如意”在向量空间里距离很近,因为它们都指向长辈祝福;但这两句话的风格一个偏传统祝寿,一个偏通用问候,放到具体场景里,选错一个味道就完全不对。向量模型能感知到“主题相近”,却感知不到“语气微妙差异”“关系亲疏”“场合庄重与否”,而这些才是祝福语“走心”的关键。
还有一个更隐蔽的问题:祝福语是强语用的文本。同一句“恭喜恭喜”,放在婚礼上、放在升学宴上、放在拿到 offer 的饭局上,语义看起来都是“祝贺”,但情感负载完全不同。向量库里如果没有显式建模“语用场景”,检索回来的片段就很容易出现“高分低配”——距离最近,但用不上。这一点在后面的实测中反复被验证,也是整个项目最大的坑。
2. 数据准备与向量化,没那么简单
2.1 祝福语的数据不是“收集进来”就完了
很多人以为,RAG 的数据准备工作就是把祝福语扒下来、切成段、灌进向量库。但这个场景里,数据的“结构化程度”直接决定了检索质量。我第一次只做了最基本的清洗(去重、去乱码、去掉明显广告),就急着向量化,结果检索效果惨不忍睹,后来重新设计了一套带多维标签的数据结构,才逐步把效果拉回来。
每条祝福语我最终保留了这些字段:id、原文、场合标签、关系标签、语气标签、长度档位、性别倾向、风格标签、来源批次、备注。其中场合标签包括生日、婚礼、节日、升学、升职、乔迁、道歉、慰问、感谢等;关系标签区分长辈、平辈、晚辈、客户、导师、朋友、恋人等;语气标签区分正式、轻松、深情、俏皮、古风、现代等。这套标签体系非常重要,因为后面做“规则前置”和“结果重排”时,全靠这些字段做硬过滤,而不是完全依赖向量打分。
还要注意一个细节:祝福语短文本不适合粗暴切分。文档 RAG 里常见的做法是把长文档切成 200~500 字的 chunk,但祝福语录里大部分文本本来就短,一条祝福可能就 20~80 字。我第一次按“句号切分”,把一条“愿您岁月常新,福气常伴,身体安康,笑口常开”切成四段,检索时只能召回“笑口常开”这种碎片,完全失去了完整语义。后来改成“整条不切分”,每条祝福语作为一个独立文档单元入库,效果立刻好了不少。在短文本场景里,最少切分单元往往就是完整语义单元。
2.2 嵌入模型选型:通用模型不是万能的
向量化这一步也踩了坑。我对比了三类嵌入方案:通用开源中文句向量模型、商用 API 模型、以及在少量祝福语数据上微调过的垂直模型。结论比较现实:通用模型能用,但别指望它理解“祝福语的好坏”。
我用一个非常简单的测试来检验模型效果:给定“给女朋友的生日祝福,要浪漫一点”,从库里检索 top5。通用模型返回的结果里有“生日快乐,感谢遇见你”这种还行的,也有“祝您生日快乐,健康长寿”这种明显偏长辈风格的,因为向量空间里“生日”主题接近就算“相似”,完全没理会“女朋友”与“浪漫”的约束。商用 API 模型稍好一些,但还是会在“浪漫”和“温馨”之间混淆边界。而微调过的模型在情感风格区分上确实有提升,但微调需要准备大量人工标注的“查询-正确祝福对”,成本比较高,对于一个小实验来说不划算。
最终我的选择是:用通用模型做初召回,用标签体系做硬过滤,再用一个轻量级重排逻辑调整顺序。这个“向量粗排 + 规则重排”的组合拳,比单纯换更强力的嵌入模型要务实得多。嵌入模型本身是“无监督语义相似度”的产物,它理解的是“字面相似”,不是“场景适配”;想让它在祝福场景更聪明,要么花成本微调,要么在检索链路里加规则约束,后者显然更适合快速验证。
2.3 检索参数不是拍脑袋定的
向量检索库我用了常见的 HNSW 索引,几个关键参数需要根据数据规模调:M(每个节点的连接数)、efConstruction(建索引时的搜索宽度)、efSearch(查询时的搜索宽度)。我这里只有几百条数据,所以 M 设小了也没问题,重点是 efSearch。efSearch 太小会导致召回不全,太大又会影响检索速度。实测下来,数据量 1000 条级别时,efSearch 设 200 就够了,再多对效果的提升非常有限,反而白白增加延迟。
真正影响效果的其实是TopK 的取值。我第一次只取 top5 拼进提示词,发现效果很差,因为向量召回的前几名可能都是同一个风格模板的变体。后来把初召回扩大到了 top30,再用标签过滤,最后从过滤后的结果里挑 3~5 条“风格互补”的片段进提示词。这个“先宽召回、后过滤、再精选”的流程,比直接 top5 稳定得多。后面会有具体对比数据。
3. 搭建完整链路:从检索到生成的实操细节
3.1 整体架构与提示词拼装方式
实验的整体流程分四步:用户输入解析、初召回、过滤重排、提示词拼装与大模型生成。
用户输入解析环节,我需要从用户的自然语言描述中抽取出三个关键要素:场合、关系、语气倾向。比如用户说“帮我写一段给导师的毕业感谢,要正式一点”,那么场合是“毕业感谢”,关系是“导师”,语气是“正式”。这个环节可以用大模型做结构化抽取,也可以写规则匹配,我为了让流程可复现,用了“小模型抽取 + 关键词兜底”的方式。抽取质量直接决定后面的检索上限,如果用户只说“来一段祝福”,没有说明场景,系统应该主动追问而不是盲目生成。
提示词拼装是另一个容易翻车的地方。第一次我把检索到的祝福语原文直接塞给大模型,让模型“参考这些风格生成”,结果大模型把原文里的一些词汇原封不动搬了过来,读起来像缝合怪。后来改为提示词里明确区分“风格参考”和“事实信息”:风格参考只负责传递句式结构、措辞风格和情感基调,不要求模型保留原文词语;同时加入一句“以你自己的语言重新组织,不要照搬参考文本”。这一改动对最终文案的“去 AI 味”帮助很大。
3.2 过滤与重排:向量距离说了不算
重排逻辑我分了两层。第一层是硬过滤:根据用户输入解析出的“关系”“场合”“语气”三个标签,过滤掉不匹配的候选。比如用户要“给客户的商务祝福”,凡是标签里带“朋友”“恋人”的候选全部干掉了。第二层是软排序:在硬过滤后的集合里,按照“向量相似度 * 0.7 + 风格匹配度 * 0.3”的加权分排序。这里的风格匹配度是简单的规则打分,比如用户要“深情”语气,候选如果标了“正式”就减分、标了“深情”就加分。
这个流程跑下来之后,我注意到一个有意思的现象:向量相似度最高的结果,往往不是用户最终最满意的结果,反而是第 4、第 5 顺位的结果。因为向量相似度主要反映“主题接近度”,而真实场景里,用户首先排除的是“不合适”,然后才是在合适的候选里挑“最喜欢的”。所以过滤的重要性大于排序的重要性——你可能选不中“最完美的那一条”,但你绝不能把“完全不搭的那一条”推给用户。
3.3 评测方式:别只看生成文本漂不漂亮
项目做到中期,我们需要一个可量化的评测标准。团队内部最终定了两个指标:检索命中相关率和人工走心评分。前者看流程前三步的检索结果是否匹配用户输入场景,纯客观;后者让用户对最终生成的祝福文案打 1~5 分,纯主观。这两个指标缺一不可。只测检索命中率,说明不了生成质量;只测走心评分,又难以定位是检索还是生成出了问题。
评测集是团队同学从真实聊天记录和需求列表里整理的 30 个场景,覆盖生日、婚礼、升职、道歉、感谢、节日慰问等,关系覆盖长辈、朋友、同事、客户、恋人等。每个场景跑一遍完整流程,记录检索结果、生成文案、人工评分。这个评测集后来成了排查问题的主要工具,每次改动链路,都用同一批场景回归,效率很高。
4. 真实踩坑记录:向量库里的“高分低配”
4.1 语义漂移:看起来像,用起来不对
最典型的失败案例是这样的:输入“给同事的离职祝福,表达感谢和不舍”,向量检索 top1 返回的是“感谢您多年来的辛勤付出,祝您在新的岗位一切顺利”。乍一看非常匹配,关键词全中:感谢、付出、新岗位、顺利。但用户反馈“太像年会致辞,而不像对要离职的同事说话”。问题出在哪?向量模型认为“离职”和“岗位变动”是语义近邻,但现实中“离职祝福”和“调岗祝贺”的情感色彩完全不同,前者带着告别和感恩,后者带着晋升的喜庆。向量空间里相距 0.8 的两句话,在情感坐标里可能隔着一条河。
类似的情况反复出现后,我总结出一个规律:向量检索在处理“主题”问题时很可靠,在处理“情感姿态”时非常不可靠。主题是显性的词汇信号,情感姿态是隐性的语用信号,句向量模型没有针对后者做专门训练。所以链路里必须引入显式的情感/关系标签,不能把“语义相似度”当成“情感适配度”。
4.2 同质化召回:Top10 全是同一个“味”
另一个大坑是检索结果的同质化。库里祝福语数量有 800 条左右,但很多出自同一批范文来源,句式结构高度类似。我测试“给老师的教师节祝福”时,返回的 top10 里有 6 条都以“感谢您……”开头,剩余 3 条是“老师辛苦了”的变体,还有 1 条跑偏成了“祝您身体健康”。这样拼进提示词后,模型参考的全是同一个句式,生成结果自然千篇一律、模板味浓重。
这个问题的根源在于向量检索天然倾向于“找相似的”,而不是“找多样的”。解决思路有两个:一是在初召回后做“多样性重排”,让风格标签分布更均匀;二是在提示词层面要求模型把参考内容当作“素材池”而不是“风格模板”。我两个都做了,效果改善明显。后面如果再优化,可以考虑在召回阶段用聚类思想,把“不同风格但主题相近”的候选各选几条,而不是一股脑取全局距离最近的前几名。
4.3 场景时效性:静态向量库的硬伤
祝福语有极强的时效性,这是我一开始忽略的问题。库里收集的素材来自不同年份,有些文案里的“新年快乐,牛年大吉”到虎年再用就显得过时了。而向量库是静态的,关键词检索倒还好,语义检索更麻烦——用户说“今年中秋祝福”,向量模型并不理解“今年中秋”和“去年中秋”的区别,它只看到“中秋祝福”这个主题,于是去年、前年的文案都可能被召回。库龄越老,这种“时空错位”越明显。
后来我意识到,时效性问题本质上不是检索算法问题,而是数据运营问题。RAG 项目必须配套一套“语料入库时间”的字段管理,并且在下游重排时对过期语料做时间衰减。我在每条祝福语上加了“入库时间”和“有效期至”字段,查询时如果用户显式提到年份,就过滤掉非当年的文案;如果没提,则默认优先保留有效期内的素材。这样虽然不能完全解决时效性,但至少不会出现“恭喜发财”配给“七夕表白”的离谱情况。
4.4 长文本祝福库:整段存还是拆句存
库里有少数长文案,比如一整段婚礼致辞两百字。最初为了检索灵活,我把一段致辞按句子拆成若干条短文本,结果发现一个问题:检索“给朋友的婚礼祝福,要幽默一点”时,返回的碎片里有一条“听说你要结婚了,我终于可以光明正大蹭顿饭了”,单看很俏皮,但它在原文里后面跟的是一大段正经的祝福,前语境完全丢失。模型拿这句去生成,反而被带偏,写出来的文案头重脚轻。
教训是:短文本场景下,尽量保持“一条一个完整表达单元”。如果一段话里有多个语义层次(俏皮开头 + 深情正文 + 简洁收尾),应该把整段存成一条,最多打多个风格标签,而不是物理切碎。物理切分解决的是长文档的“精确定位”问题,祝福语本身不到百字,不存在“定位”需求,切分的收益很小、损失很大。后来我把库改成“来源完整文案优先存整条”,检索质量稳定了不少。
5. 改进后效果:混合检索 + 规则前置 + 人机协同
5.1 混合检索:向量召回兜底,关键词召回纠偏
第一轮改进,我给检索链路加了“关键词召回 + 向量召回”的双路混合。关键词召回用简单的 BM25 风格打分,对“场合 / 关系”这类强信号词汇非常敏感,比如用户输入“给客户的生日祝福”,BM25 对“客户”和“生日”的匹配权重很高,能精准锁定一批候选人。向量召回负责“语义扩展”,当用户描述比较抽象(比如“要含蓄一点,不要直白说生日快乐”)时,向量这部分能召回一些表面没提“生日”但语义相关的文案。两路结果做融合,交集部分优先,剩余按加权分排序。
加这一路之后,检索命中相关率从 62% 提升到了 83%。为什么效果这么明显?因为很多祝福语的“情感姿态”信息藏在关键词里(“客户”“导师”“告别”等),这些词是强判别性信号,BM25 能精确匹配;向量模型虽然也能捕捉,但因为训练目标分散,它在这些强信号上往往被“主题相似”的噪声干扰。关键词召回相当于给检索加了一道保险,专治向量召回的“高分低配”。
5.2 规则前置:先定场景,再查库
第二轮改进是“规则前置”。核心思路很简单:不在全库里做“大海捞针”,先根据用户输入的场景标签,把检索范围缩小到对应子集,再在这个子集里做向量召回。
具体做法是:维护一个场景映射表,比如“教师节感谢”“离职告别”“婚礼致辞”各有对应的标签集合。用户输入后,先做标签判定(规则或小模型),然后从库里筛选出至少满足两个标签的候选,再对这部分子集做向量排序。这一步的价值在于,把“全局语义相似”变成了“局部语义相似”。以前全库检索时,“给导师的毕业感谢”可能会被“开学祝福”干扰,因为两者都涉及“学校”相关词汇;经过标签过滤后,“开学祝福”从一开始就出局了,不再产生误导。这轮改进后,检索命中率又升到了 89%。
5.3 提示词优化:让模型“参考”而不是“抄”
第三轮改进集中在生成侧。之前提示词里的参考祝福语经常被模型直接“借用”,导致缝合感很强。我重新设计了提示词模板,核心改动有两点:一是让模型从参考样例中提取“句式骨架”和“情感节奏”,而不是保留原句词汇;二是给模型一个明确的指令“想象你是用户本人,而不是一个文案员”,引导模型用第一人称的口吻输出。
这里有一个细节值得说:大模型非常受参考文本的“风格污染”影响。如果参考的 5 条全是“祝您”“愿您”开头的敬语,模型输出的每句都会带敬语,即便提示词写了“要轻松俏皮”。所以我在精选环节刻意让进入提示词的 3~5 条祝福语覆盖不同风格,比如一条深情、一条直白、一条幽默。这样模型能综合不同风格而非被单一风格锁死。这个“风格混合”的技巧,对生成结果的自然度提升非常明显。
5.4 人机协同:推荐的终极形态不是“一键生成”
做到最后,团队内部争论了一个问题:这个功能的体验应该如何收尾?一键生成走心文案,还是推荐多条供用户选择?我们分别做了测试。一键生成模式下,用户即使对结果不满意,也很少会二次编辑,直接放弃的比例很高;而在“推荐 3~5 条 + 支持小调整”的模式下,用户选择一条后微微改动,满意度大幅提升。
这个结果其实揭示了一个产品层面的真相:祝福语这个场景,用户更需要的不是“被替代”,而是“被启发”。他们心里其实已经隐约知道自己想说什么,只是找不到合适的措辞。这时候“检索出几条接近方向的文案”比“替用户生成一条标准答案”更有价值。所以最终方案改成了“检索为主、生成为辅”:系统先展示检索到的多条候选,用户选中后再用大模型做局部润色,比如把“生日快乐”改成“生日快乐,愿今年所有遗憾都成为明年惊喜的铺垫”,而不是一上来就生成整段。人机协同不是技术妥协,而是这个场景下的产品正解。
5.5 改进前后的评测对比
把四轮改进综合到一起,拿 30 个评测场景做回归,人工走心评分的均值变化如下:
| 版本 | 检索命中相关率 | 走心评分(1~5) | 用户二次编辑率 |
|---|---|---|---|
| 初版(纯向量 top5 直接生成) | 62% | 2.1 | 无统计 |
| 加入标签过滤和风格混合 | 83% | 3.2 | 约 30% |
| 再加入混合检索和规则前置 | 89% | 3.8 | 约 45% |
| 最终版(检索候选 + 人工选择 + 润色) | 89% | 4.2 | 约 18%(低,因为满意度高) |
这个表是内部评估结果,样本量不算大,但趋势很清楚:每一步改进都往上拉分了,但离“走心”的 5 分还有明显距离。真正能逼近 4 分以上的,反而是“检索候选 + 人工选择”这个看起来不太“AI”的方案。这个结论很扎心,但很真实。
6. 关于“RAG 到底能不能让祝福走心”的最终答案
6.1 技术上能解决什么,不能解决什么
先说结论:RAG 能解决“写得出、对得上”,解决不了“打动人”。“走心”是一个由接收者主观体验决定的心理感受,它来源于三样东西:具体细节的针对性、情感姿态的真诚感、语言节奏的自然感。RAG 能从库里检索出包含“对方名字”“共同回忆”“具体事件”的素材(如果库里存了这些信息),从而提高“针对性”;但“真诚感”和“自然感”是模型的生成能力问题,不是一个检索架构能给的。
更残酷的事实是,现实场景里用户往往不会把“对方是谁”“你们之间有什么故事”告诉系统。没有这些信息,任何技术都只能给出泛化的祝福,泛化的祝福必然“礼貌但不动人”。这和技术水平无关,是信息约束决定的。所以我把这个项目的定位从“AI 生成祝福语”改成了“AI 辅助表达”,期望值一旦放对,产品的体验评价反而上去了。
6.2 什么场景适合用 RAG 生成祝福,什么场景坚决不要用
基于这次实践,我梳理了一个比较清晰的适用边界,供以后做同类功能的人参考。
适合用 RAG 做辅助的场景有三个特征:量大、时效紧、表达要求中等。比如电商平台会员生日批量祝福、企业客户节日群发、活动主办方的参会感谢信,这些场景需要大量个性化文案,但接收者对“这是模板还是定制”的敏感度相对低,重点是信息准确(称呼、产品名、时间)和不出错。
坚决不要用 RAG 生成“成品”的场景也有三个特征:关系极其亲密、情感浓度极高、容错率极低。比如给挚友的道歉信、给伴侣的求婚誓词、给逝者的悼念文字。这些场合一旦出现“模板感”,不仅起不到祝福作用,反而会让接收者感到敷衍,甚至伤害关系。这些场景里,AI 最多只能提供一个“开头思路”或“关键词提示”,成稿必须是人自己写的。有些团队为了炫耀技术,硬把这类场景做成功能点,我认为是产品判断失误。
6.3 以后再做类似功能,我会换一套设计思路
如果这个项目重新来一遍,我不会再以“召回-生成”为绝对核心,而是以“表达决策树”为核心:第一步先确认用户和接收者的关系亲密度;第二步确认使用场合的正式程度;第三步确认用户希望表现的情绪姿态。这三个问题决定了祝福的“骨架”,而 RAG 负责的事情只是从库里找出匹配骨架的“血肉”素材。系统可以有一个默认推荐,但永远要给用户留出“修改骨架”的入口——因为没有人比用户更懂那段关系。
产品形态上也会变化:不做“输入一句话自动出整段祝福”的单轮对话框,而是做一个“多步引导 + 素材卡片 + 自由拼接”的编辑器。用户选好场景和关系后,系统推一组经过检索排序的短句卡片,用户可以像搭积木一样选句子、拖顺序、微调用词。这种方式能让用户觉得自己在“写字”,而不是在“收AI的作业”。技术难度其实不高,但产品体验的差异是巨大的。这也算是我这次项目最大的产品教训:技术选型要服务于“表达参与感”,而不是服务于“生成自动化”。
这次实验结束后,我自己最大的体会是:不要因为一个技术叫 RAG、叫向量库,就默认它适合所有场景。技术的边界不是靠热情突破的,是靠真实的用户反馈扎出来的。以后如果再听到“把祝福语塞进向量库就能走心”这种话,我会先问一句:我们做这个功能,到底是想让 AI 替人表达,还是想帮人更好地表达自己?这两个目标的实现路径,差别太大了。