← 完整项目实验室 · 第 5 篇

项目:阅读推荐与解释服务

先建立合成书目与规则推荐,再加入有出处的解释,处理冷启动、偏好权限、无结果和推荐评估。

学完这一篇,你能做到

实现可解释的规则推荐基线,并检验空偏好、排序、证据和权限。

从明确的阅读偏好开始

这个项目提供一份能解释推荐理由的阅读清单。练习书目使用合成 ID、标签和简介,不采集真实读者行为,也不把一次点击当作某人的长期兴趣。先让用户选择“AI”“系统”“设计”等类别,返回匹配条目;没有匹配时解释筛选条件,让用户调整偏好。

第一版只需要目录、筛选、排序和反馈。模型可以帮助组织推荐理由,但书籍是否存在、属于哪些类别、有没有合适版本,都由目录记录决定。不要让模型凭记忆补作者、出版时间或用户没看过的书。规则排序能给后续模型方案提供对照,否则很难判断增加调用成本是否换来了实际改善。

把需求写成能验收的行为:同样的书目版本和偏好得到同样的顺序;用户能看到匹配标签;空偏好有明确入口;停用模型后仍能浏览推荐;用户只能读取和修改自己的偏好。推荐解释与推荐排序分开,解释服务失败不应清空已经算好的列表。

数据模型决定理由能否追溯

book 保存条目 ID、名称、简介、状态与书目版本,tag 定义受控类别,关联表记录书籍与类别。偏好记录属于某个用户,并保存修改版本。recommendation_run 记录使用的书目版本、偏好快照、排序规则版本、候选 ID 和分数,解释则关联这次运行。

删除或禁用一个条目后,新推荐应排除它;旧推荐记录仍要说明当时使用了什么数据。可以保存必要的条目快照,也可以保留版本化目录,选择取决于存储和留存政策。不能把当前简介直接当作旧推荐当时的证据。

偏好是用户明确提交的数据。服务端从认证上下文确定归属,不接收一个任意 user_id 就替别人写入。修改偏好与记录版本放进同一事务;读取推荐结果也检查拥有者。演示中使用虚构数据,实际产品还要提供清除历史、撤回偏好和相应留存说明。

做一个可以检查的排序基线

标签集合可以用 Jaccard 相似度:交集大小除以并集大小。书籍与偏好共享的标签越多,分数可能越高;书籍额外包含的标签也会影响分母。它衡量集合重叠,不能证明书籍质量或读者满意度。

下面的三个书目 ID 都是合成数据。两个 AI 条目中,B-1 与偏好完全重合,B-3 只共享一个标签。排序先看分数,再用 ID 固定并列顺序。

books = {"B-1": {"ai", "systems"}, "B-2": {"design"}, "B-3": {"ai", "data"}}
preferences = {"ai", "systems"}
def score(tags):
    return len(tags & preferences)/len(tags | preferences)
ranked = sorted(books, key=lambda book: (-score(books[book]), book))
assert ranked[0] == "B-1"
assert score(books["B-2"]) == 0
print([(book, round(score(books[book]), 3)) for book in ranked])

输出为 [('B-1', 1.0), ('B-3', 0.333), ('B-2', 0.0)]。代码把零分条目也排进列表,便于观察;产品接口可以先过滤零分项,并在过滤后为空时返回无结果。不要把零分条目的最后一名解释成“也很适合你”。

示例没有处理两个集合同时为空的除零情况。实现时先定义空偏好策略:让用户选择类别,或提供明确标注的编辑推荐。若允许空标签,应在相似度函数中规定并集为空时的返回值。数据量较大时再考虑预计算或索引,不能凭这几个条目声称系统具有某种吞吐能力。

用 Spring 接口连接完整路径

采用 Spring Boot 时,可以从 Spring 官方 REST 指南建立本地服务,再设计这个项目自己的资源。控制器处理输入和响应,推荐服务负责筛选与排序,数据访问层负责目录与偏好。HTTP 映射和 JSON 序列化只是基础,认证、资源授权和业务校验仍需实现。

例如读取目录使用 GET /books,更新自己的偏好使用 PUT /me/preferences,提交一次推荐请求使用 POST /recommendations,查询已保存结果使用带 ID 的读取接口。分页数量、标签数和文本长度在服务端设置上限;未知标签不能悄悄转成任意查询条件。响应中给出运行 ID 和版本,前端才有办法识别旧结果。

按一条纵向路径开发:导入固定合成目录,完成标签选择,返回确定性排序,然后保存运行记录,最后补解释适配器。前端在请求等待、空结果、解释失败和偏好已更新时显示不同状态。用户修改偏好后,旧请求即使晚返回,也不能覆盖新请求的列表。

解释只能使用已选条目的证据

给模型的材料限制为已选条目的 ID、简介和匹配标签,输出绑定相应条目。服务端校验返回 ID 是否在候选集合中、引用字段是否存在、理由是否与该次快照一致。模型写出格式正确的理由,仍可能误述字段;必要时直接使用“匹配了 AI 与系统标签”这样的规则解释。

解释请求设置超时和调用预算,拒绝或中断时保留列表并提供规则理由。不要把任意目录文本当作工具指令,也不要把解释模型连接到修改偏好、删除历史或购买入口。若以后加入外部书目检索,先处理资料权限、来源与版本,再讨论更长的解释。

向量检索可以作为候选生成方法,学习排序则需要相应数据和评估。先记录它们解决的是标签表达不足、候选遗漏还是排序问题,避免用一个模型同时掩盖几个不同缺陷。现有示例是规则排序,并没有训练个性化模型。

缓存键包含书目、偏好和规则版本,并限制在正确用户范围内。共享的目录缓存可以复用,带个人偏好的结果不能按一个通用路径混给其他用户。目录更新后失效哪些缓存要能检查;否则接口虽然记录了新版本,实际仍可能返回旧候选。

验收相关性、稳定性和权限

建立独立于调参过程的测试偏好,包括单标签、多标签、空集合、未知标签、无匹配和并列分数。逐项检查顺序、空结果与理由归属;再模拟目录停用、偏好更新、重复请求、模型超时和越权读取。保存版本信息,失败时才能重放当时的条件。

相关性、多样性和解释可信度应分别评估。可请审阅者按明确标准判断候选是否符合偏好、是否重复相近条目、理由是否由目录支持。合成偏好测试通过只说明这些规则被正确执行,不能推断真实点击率提高,也不能用调参时看过的样本证明泛化能力。

第一版验收完成后,再比较规则解释与模型解释。在相同候选上记录错误类型、等待时间和调用成本,确认收益能解决读者的实际困难,再决定是否增加模型范围。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。