ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用LLM把新闻推荐做成“会说话的推荐器”:可解释推荐架构实践

用LLM把新闻推荐做成“会说话的推荐器”:可解释推荐架构实践

1. 为什么新闻推荐非得“开口说话”

做推荐系统的人,可能都有过这种体验:你明明给用户推了一篇“看起来符合兴趣”的文章,用户却压根不买账,后台数据显示点击率很低,反馈也很冷淡。问题出在哪儿?黑盒推荐系统的通病——模型算出了“用户可能喜欢”,但说不清“为什么喜欢”。

新闻推荐尤其特殊。新闻讲究时效、讲究冲突、讲究贴近读者当下的关注点,用户点开一篇文章,往往有一个很具体的心理动机:可能因为标题提到他关心的公司,可能因为文章里那段话戳中了他的某个立场,也可能单纯因为他刚刚看完同主题的短视频。传统的协同过滤、CTR预估模型,把用户历史行为压成一个向量,再做匹配,给出一个分数排序。这个分数的确能排序,但没法回答“为什么”。

于是行业里这些年一直有人在尝试做“可解释推荐”。早年的做法很朴素:统计你浏览过的文章里“体育”类最多,那我推体育文章,解释词就是“因为你常看体育资讯”;或者用item间的相似度,解释成“因为你喜欢过类似内容”。这类解释能用,但粗糙,而且经常闹笑话——因为你看过的某篇爆款里有“美食”标签,系统就把它当成你爱吃辣,推了一堆辣椒专栏,解释还理直气壮。

那段时间我自己的体感是:可解释性在推荐场景里,一直是“锦上添花”的东西,属于“有了更好、没有也不致命”。但这两年情况变了。一方面,新闻产品的用户粘性越来越依赖信任感——用户需要知道平台推给他的理由,才会愿意持续留下来,而不是把新闻App当成一个不知道背后逻辑的“黑箱分发机器”;另一方面,大语言模型的出现,第一次让“生成自然语言解释”变成了成本极低、效果极好的事情。

这篇文章,我想用一个实际项目的思路,聊聊怎么用LLM把新闻推荐做成“会说话的推荐器”——不光是给出排好序的候选列表,而是给每一条推荐都配上用户能看懂、愿意信、甚至能引发点击欲的自然语言解释。

2. 思路拆解:可解释新闻推荐的本质问题

在动工之前,得先把“可解释”这事儿拆开。业内对推荐解释的研究,长期以来围绕三个层面的问题转:解释给谁看、解释到什么粒度、解释由谁生成。

第一个问题“解释给谁看”看起来废话——当然是给用户看。但在真实的新闻产品里,解释的用户可能是三类人:最终读者、内容运营编辑、以及推荐算法团队自己。读者要的解释是“人情味”的,能说服他点进来;运营编辑要的解释是“复盘式”的,能看出来推荐逻辑有没有跑偏;算法团队要的解释是“诊断式”的,能帮助定位模型行为异常。我见过不少团队一开始想做一个解释模块,结果做出来之后只有算法组自己看,用户端完全不展示——这是目标没定清楚就开工的典型症状。

第二个问题“解释到什么粒度”其实更关键。粗粒度解释是“因为你关注了科技频道”,细粒度解释是“因为你最近阅读的3篇文章都与芯片行业相关,而这篇是同一个话题的深度追踪”。细粒度的解释用户感知更好,但技术难度也更高——需要从用户近期行为里抽取出真正触发推荐的那个“兴趣点”。

第三个问题“解释由谁生成”,在LLM出现前,主流是模板式生成,比如填空:“推荐理由:你可能喜欢{兴趣标签}相关的{内容类型}”。用LLM之后,变成了开放式的自然语言生成,但引入了一个新的麻烦:生成的解释可能胡编乱造,即“幻觉”。

回到这个项目,我把核心矛盾定义成一句话:想要可解释,但不能因为解释伤了用户信任。解释如果和真实的推荐原因对不上,用户反而觉得你在骗他。

基于这个定义,方案选型上我遵循几个原则:

  • 解释必须能被溯源。哪怕语言是自然生成的,背后的推荐依据必须来自可以查证的用户行为信号。
  • 解释不是独立生成的任务。解释要拿推荐结果和用户行为配合着生成,不能把推荐和解释拆成两条线。
  • 解释要在成本和效果之间取平衡。并非所有推荐都需要解释,解释的展示位置、展示时机、生成成本都要规划。

这三点决定了后面整个架构的走向。

3. 整体架构设计:召回、排序、解释三层联动

很多LLM推荐项目的第一反应,是把全文内容一股脑丢给大模型,让模型又做召回又做排序还顺带写解释。这样做的技术验证很快,但落地很痛苦:一方面是成本太高,用户每一次刷新页面都要喂给模型上百万token的新闻内容;另一方面是延迟完全不可控,用户等不起十几秒才看到结果。

这个项目采用了一个更稳的三层架构——召回、排序、解释各司其职,LLM只负责做它最擅长的事:理解和生成语言。

召回层照旧用轻量级方案。这一层基于用户画像、行为序列、热门榜单、话题聚类,从海量新闻里粗筛出几百篇候选。这个阶段完全不碰大模型,用向量检索加规则加权就能搞定。召回的目的不是精准,而是别漏掉可能感兴趣的内容,把规模从一个资讯库收敛到一次排序能处理的数量级。

排序层承接候选集,用行为模型打个分。这里延续原来的CTR预估模型,把用户历史行为、新闻特征、场景特征作为输入,输出一个排序分数。我见过有些团队想在排序层直接引入LLM做语义排序,但实践下来,成本和延迟的代价太高,而且排序效果未必比专精的行为模型好。LLM的强项是语言理解和生成,不是大规模数值排序,没必要拿导弹打蚊子。

解释层才是LLM的主场。排序层输出带分数的候选列表之后,解释模块取候选新闻的正文摘要、标题、关键实体,再取用户近期的行为记录,一起丢给LLM,让它生成自然语言解释。这里有个关键细节:传给LLM的用户行为记录,不是原始日志,而是经过一层“兴趣抽取”加工后的结构化信号,例如“用户近7天点击了18篇科技类内容,其中9篇与芯片话题相关”。这样做既能让LLM基于事实生成解释,又能避免把原始行为序列全量塞给模型的成本和隐私风险。

这三层联动的结果是:用户看到的解释,建立在可验证的兴趣信号之上,而且新闻本身是由传统推荐模型精挑细选出来的——LLM只是给结果“配解说词”。这种分工方式,落地的时候改动最小,出问题的面也最小。

4. 核心实现一:兴趣信号抽取与压缩

LLM生成解释要靠谱,首先得喂给它靠谱的“料”。我在这块踩过不少坑,最初直接拿原始行为序列试过,效果很差——模型看着一大串URL链接和数据ID,完全不知道用户到底喜欢什么。后来改成把行为数据加工成人话,才稳定下来。

这一层我叫它“兴趣信号抽取”。流程是:从推荐系统里拿用户近N天的点击行为,做几件事——统计点击量的频道分布;做一次关键实体的聚类,把点击文章里的高频人名、机构名、主题词挑出来;再算一下点击时间的集中度,识别用户是“持续关注型”还是“一次性刷到型”。

加工出来的信号,是一个结构化的兴趣摘要,例如:

用户近7天点击34篇新闻,分布为: - 科技类 16篇(芯片话题 9篇,AI应用话题 5篇,其他 2篇) - 财经类 9篇(股市相关 6篇,产业公司动态 3篇) - 社会类 5篇 - 体育类 4篇 高频关注点:芯片、智能汽车、人工智能行业动态 近期活跃时段:工作日早晚通勤时间

这段话包含的信息量不大,但它是幻觉的克星——LLM生成解释时所有素材都从这里来。你给它一个具体可考的信号,它生成的解释至少不会离用户真实兴趣太远。

兴趣抽取的任务不重,我用现成的实体识别工具加一个轻量聚类就能完成。比较关键的设计决策是:这段摘要的结构设计必须服务于解释生成的“可引用性”。所谓可引用性,就是LLM在解释里提到的每个理由,都能对应到摘要里的某一行信息。这个约束在后面的prompt里有明确要求。

5. 核心实现二:面向推荐的解释生成Prompt设计

Prompt是解释质量的分水岭。同一套兴趣信号,不同写法的prompt,生成结果一个天上一个地下。这个项目我迭代了好几版prompt,最终稳定下来的结构包含四部分:任务定义、输入材料、生成约束、输出格式。

任务定义部分要写得足够窄。不能只说“请你为用户生成推荐理由”,而是明确到“你是一个新闻推荐的解释器,你的任务是基于用户的兴趣信号和新闻内容,解释系统为什么将这篇新闻推荐给该用户”。我看过太多生成解释太泛泛的案例,根子就是把一个要命的“为什么”让模型自己揣摩。

输入材料部分,同时给兴趣信号和新闻内容。新闻内容也不是全文,取前200字的摘要加上核心实体列表就够了。正文全文没必要喂,一来浪费token,二来新闻的导语段落已经涵盖了最核心的信息点。

生成约束部分是最重要的。我固定了三条硬性约束:

  • 解释中出现的观点必须有出处,要么出自兴趣信号中的统计,要么出自新闻摘要中的事实。不允许凭空创造。
  • 解释要简短,控制在三句话以内。新闻信息流的场景,用户扫一眼就过,解释写五句话反而是干扰。
  • 语气要自然,不要使用“根据您的喜好推荐”这种AI味十足的模板用语。要像编辑在推荐给朋友文章时的语气。

输出格式用JSON封装,让后续的工程链路好接:

{ "explanation": "你对芯片行业动态的关注比较持续,这篇报道提到的芯片厂商正是你最近阅读中多次出现的当事人。", "reason_tags": ["芯片", "科技"], "evidence_detail": ["近7天点击芯片类文章9篇", "新闻中出现实体:某芯片厂商"] }

这里的evidence_detail字段,是为了后续做“解释可点开”的交互——用户点一下解释,能看到支持这个解释的行为依据是什么,进一步增加可信度。这部分最初没有设计,是给用户做可用性测试后加上的,效果很好。

这个prompt结构后来我复用到了其他项目里,发现只要任务定义清晰、来源约束到位、输出结构可解析,LLM生成的质量整体是稳的。

6. 核心实现三:LLM生成效果的质量控制

解释生成的第一个可用版本其实很差,差在“不接地气”。模型生成的是“根据您的阅读偏好,这篇包含您感兴趣的芯片话题的新闻可能对您有价值”这种四平八稳的句子。语法没有错,信息也没错,但用户看了毫无感觉。这也让我意识到:可解释推荐的解释,不仅要“对”,还要“动人”。

要动人,得在质量控制上做文章。我的处理方式是把生成解释分成两路选优。

一路走“事实优先”的生成器,约束严格,按前面说过的prompt生成,保证不出错、可溯源。另一路走“情绪优先”的生成器,约束放松,让它更有发挥空间,例如把新闻里最具冲突感的句子提炼出来,跟用户兴趣产生勾连。两路都生成之后,用一个简单规则把它们拼起来或者择优。

举个例子。事实优先的解释是:你近期较多阅读芯片产业报道,这篇新闻的核心人物正是你关注过的某半导体公司。情绪优先的解释是:你关注的某半导体公司最近有了大动作,这篇报道把来龙去脉讲清楚了。拼起来效果就很好:你关注的那家半导体公司最近有了新动作,这篇报道正好跟你平时看的芯片产业动态连上了,帮你把来龙去脉讲清楚。

质量控制另一个重要的点是自动评测。我当时建了一个小型的解释质量标注集,请团队里几名编辑同学给生成的解释打分,维度是准确度(解释依据是否与兴趣信号一致)、相关性(解释与新闻内容是否匹配)、感染力(用户看了会不会想点)。跑了几轮下来发现,prompt约束里的“禁止幻觉”直接影响准确度,而感染力最有效的变量是“是否使用具体实体”——解释里提到某公司的具体名字,比只说“相关企业”点击意愿强很多。

这种自动评测流程,要在项目早期就建立起来。没有评测集,后面每一次调整prompt都只能凭感觉,很难持续优化。

7. 实操过程:一个新闻App的改造实录

这套方案是在一个模拟的新闻资讯App上做的改造,目标场景是“频道信息流 + 相关推荐”两个位置。改造过程大致分四步走。

第一步是在现有推荐系统上接兴趣信号抽取模块。这一步比较关键,因为它要读日志、写特征,但又不能影响线上的主流程。我把抽取模块做成了离线任务,每五到十分钟跑一批,把抽取结果写入缓存服务。线上推荐请求时直接从缓存读兴趣信号,响应不会感知到这一层处理的存在。

第二步是接解释生成的服务。这一步踩了延迟的坑——最初直接同步调用LLM接口,单次生成稳定消耗一到两秒,用户请求整体变慢。后来改成异步预生成:当推荐结果确定后,后台先把解释生成好缓存在Redis里,前端展示时直接读缓存,基本没有增量延迟。只有当缓存未命中时,才触发同步生成,做一个兜底。

第三步是AB实验测试。对照组用原版推荐,不展示解释;实验组展示解释。只改一个变量,别同时动推荐策略和解释逻辑,否则出了问题根本没法归因。跑了大约两周数据之后,实验组的点击率显著提升,但细看发现,提升主要集中在“用户画像丰富”的群体——也就是那些有较多历史点击行为的人。冷启动或行为稀疏的用户,解释的提升不明显,甚至有时候会下降。

第四步是处理冷启动和低质解释的兜底。行为稀疏用户缺乏兴趣信号,LLM生成解释时可用素材太少,效果自然差。后来我加了一条策略:对兴趣信号置信度低的用户,不展示解释,或者退化为频道级粗粒度解释——“因为你关注了科技频道”。绝不硬生成,这是我在这个坑里反复摔出来的教训。

8. 常见问题与排查技巧实录

8.1 解释与推荐结果逻辑矛盾

最常遇到的问题,是解释看起来合理,但跟推荐的新闻对不上。例如解释里写了“因为你最近关注某公司的财报”,推的新闻却压根不是财经类。排查后发现,问题出在兴趣信号抽取的滞后性——用户最近点击的新闻已经换了话题,但兴趣信号缓存还是旧数据。

解决办法是给兴趣信号加时间衰减权重,近一天的点击行为权重高,三天前的行为权重衰减掉一半。另外,生成解释时加入一个校验环节:比较新闻的实体/话题标签和兴趣信号里的高频实体/话题,如果交集为空,就直接降级为不展示解释或用默认解释,别硬解释强推。

8.2 LLM生成的解释语气过于官方

LLM默认输出风格是比较正式的,指示词换成“像个懂行的编辑在推荐给朋友”效果会好很多。我在生成约束里加了一条:明确禁止使用“根据您的阅读偏好”“为了让您更好地了解”这类套话。

如果想要更稳定的风格,可以在prompt里给一个样例解释作为few-shot示例。一次给两到三个正反样例,效果比在约束里反复强调有用得多。

8.3 成本爆炸

LLM解释生成的成本,一开始容易被低估。如果每天有几百万次信息流刷新,每次都同步生成一次解释,token开销是相当可观的。

实际应对策略有三个:缓存优先,同一篇新闻面向同一类兴趣信号生成的解释,可以缓存起来复用,不必每个用户单独生成;异步批量生成,集中在低峰期把热门新闻的解释池提前填满;控制输入输出token,新闻摘要截断到200字、兴趣信号控制在几行内、输出控制在三句话内,这比任何模型优化都省。

8.4 解释影响推荐的自然感

有个很有意思的问题:解释生成得太好,用户把注意力放在解释上,反而忽略了新闻内容。AB实验数据发现,某些解释里如果提到了具体的数字(“你最近看了9篇”“近一周点击了16次”),用户会觉得被“监视”了,点击意愿不仅没提升反而下降。

这个教训让我调整了解释的生成策略:数字细节只作为模型生成依据,不允许明晃晃写进解释里。用户能接受“你最近比较关注芯片话题”,但接受不了“你这周点了16篇芯片新闻”。这里面有个微妙的隐私感知边界,要靠实际测试摸出来。

8.5 语言风格与新闻调性不匹配

严肃新闻、娱乐新闻和体育新闻,它们的信息语境差异很大。用同一套解释prompt跑出来的东西,放在娱乐新闻下可能显得太理性,放在深度报道下又不够有分量。

我最终的解法是准备两到三套风格模板,按新闻频道做切换。严肃报道的解释重背景关联,娱乐新闻的解释重情绪共鸣,体育新闻的解释侧重赛程和观众体验。这个区分需要花一点成本维护,但对用户体验的提升是实实在在的。

9. 落地效果复盘:价值在哪里、边界在哪里

改造之后,我整理过一轮数据。点击率提升可以归功于解释的出现,但它不是唯一的价值点。更大的变化在用户侧——信息流里的“推荐理由”展位一出现,用户对推荐内容的信任感明显更强。从测试用户的回访来看,不少人提到“至少我知道为什么给我推这个了”,这种信任感用旧系统是给不了的。

解释对产品侧的另一个隐性价值是运营可解释。内容运营编辑可以查看解释生成日志,判断推荐策略是否跑偏。曾经有一次模型改版后运动类的推荐量异常上升,运营就是从解释日志里发现“运动”标签的权重被意外拉高,及时止损。这算是意外收获。

边界也很明显。解释不是解决推荐效果问题的银弹,它只是让推荐结果更容易被理解和接受的润滑剂。如果你的推荐本身质量差,解释写得再好也救不回来,甚至会放大用户的负面感知。我曾经测试过在低质推荐上强行加解释,结果用户反馈的是“平台在找借口骗我点击”。所以解释模块一定要放在推荐质量已经过关的前提下再做。

冷启动用户是另一个边界。行为数据不足时,解释效果有限,不宜强求。宁可让这部分用户看到没有解释的干净信息流,也不要为了追求功能覆盖率而牺牲体验。

10. 如果你要复刻这个方案,我的一些建议

动手之前,先确认你的推荐系统质量是否稳定。可解释是放大器,不是修复器,推荐本身太差就别急着上解释。

上线顺序从离线开始。先做兴趣信号抽取的离线任务,再做解释生成的离线批量生成,观察解释质量的评估结果,最后再接入线上异步缓存链路。每一阶段都单独验证过再进下一层,排查问题会方便很多。

评测至关重要。整理一个小的标注集,至少包含300条样本,覆盖不同兴趣类型、不同新闻类别的解释质量标注。每次改prompt或改抽取逻辑,都拿这个标注集跑一遍打分。算上人力成本,前期投入不大,但能帮你建立可复现的优化基准线。

写prompt时,把“可溯源”放在第一优先级。每一句解释都要能对应到证据,这个约束要写死在任务定义里。

最后,多看解释的失败样本。我复盘过大量生成效果差的case,发现大部分失败不是什么技术难题,而是用户兴趣信号表达得太粗糙。一个具体的、结构化的、可读性强的兴趣摘要,胜过再多花哨的prompt技巧。

新闻推荐的可解释化,本质是让推荐系统从“算得准”进化的“说得清”。LLM把这个进化的成本降到了可以接受的程度,剩下的,就是各个团队从自己的产品场景出发,找到那条真正适合的路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进