☰
Django全屋定制平台智能推荐算法设计与部署实践
2026/10/13 11:10:17 网站建设 项目流程

做全屋定制平台的推荐功能,我踩过的坑比想象中多得多。印象最深的是第一次把协同过滤跑通之后,推荐列表看起来“聪明”却完全不靠谱——用户明明在看布艺沙发,系统推荐的全是真皮沙发,理由仅仅是“同样属于沙发类目”。问题不在算法本身,而在业务理解。

所以当我把整套“Python+Django智能推荐算法的全屋定制平台”真正从零落到线上时,决定把整个设计过程、代码结构和部署经验完整梳理一遍。这个项目除了完整源码,还配了设计文档、部署文档和阶段性讲解资料,适合想走Django全栈方向、想在个人项目或课程设计中加入推荐系统,又不想沦为“demo缝合怪”的朋友参考。它能做的不只是商品展示和下单,而是把用户行为、产品标签、户型场景和推荐引擎串成一条完整的业务链路,解决用户在海量全屋定制方案里不知道从哪下手的痛点,也顺手把网站的转化率拉高了一大截。

1. 先认清全屋定制平台的业务本质

1.1 这不是普通电商,是“决策密集型”平台

全屋定制和普通服装、数码电商最大的区别在于:用户不是在“买一件东西”,而是在“决策一整屋的风格和预算”。一个业主点进平台,脑子里不是“我要买某个SKU”,而是“我家三室一厅,想走轻奢风,预算大概25万,客厅沙发和电视柜要风格统一”。

这种背景下,推荐系统的任务就变了。传统电商只要找到“你大概率会买的商品”,全屋定制平台却需要回答几个更复杂的问题:这个用户的户型适合哪种动线设计?当前选的风格和已有家具怎么匹配?同等价位的材料组合有哪些替代方案?

我一开始把平台设计成标准电商结构,结果业务逻辑根本套不进去。后来把用户决策拆成三个层次:风格决策、空间决策、单品决策,推荐策略也跟着分层。风格层用内容推荐跑,空间层用搭配规则过滤,单品层才轮到协同过滤去猜偏好。这样虽然实现起来麻烦一点,但用户访问路径的顺畅程度立刻上来了。

1.2 平台的核心角色与用户路径

这个平台服务的不只是终端业主,还有设计师、经销商和施工人员。但这几个角色在推荐系统里的优先级完全不同。经过跟调研对象的反复沟通,我把终端用户作为推荐主场景,设计师更多是“方案创建者”和“内容生产者”,经销商关注的是潜客线索和报价效率。

用户的核心路径我梳理为:首页推荐流 → 浏览风格案例 → 打开户型方案 → 调整定制产品组合 → 生成预算报告 → 预约设计师或下单。每一步都埋了行为采集点,尤其关注用户在方案页的停留时长、替换商品的频次、以及最终没有下单就离开时的最后一个操作项。

这些数据最终汇成推荐算法的输入特征。比如一个用户在“现代简约”案例下停留了四十秒,又在“客厅”分类下反复点击了三人位沙发,系统就要有能力把“现代简约客厅沙发组合”的相关方案排到首页靠前的位置。

1.3 推荐模块在整个系统中的位置

技术架构上,推荐模块不是独立的大引擎,而是嵌在Django视图层和缓存层之间的一个服务。我用了最务实的做法:不做实时流计算,不引入重型机器学习框架,而是把用户历史行为定时同步成特征矩阵,推荐结果写入Redis缓存,页面请求时优先读缓存,缓存未命中再实时计算。

这样设计的原因很简单:全屋定制平台的流量大头是“浏览和沉淀决策”,和短视频那种秒级兴趣漂移完全不是一个节奏。用户今天看的北欧风案例,明天大概率还会继续看,不需要每秒钟都调整推荐结果。把推荐计算做成可异步刷新的离线任务,既省机器,逻辑也更可控。

2. 数据库设计:给推荐算法打好地基

2.1 用户行为表怎么建

推荐算法质量的上限,往往在数据库设计阶段就决定了。很多项目算法写得漂漂亮亮,一上线就歇菜,原因是行为数据根本没存全。

我建了三类用户行为表:浏览记录表、操作事件表、订单关联表。

浏览记录表记录用户看了什么内容类型、在哪个界面停留了多少秒;操作事件表记录点击收藏、替换风格、加入方案清单、移除商品这些关键动作;订单关联表则把最终成交结果和当时的推荐上下文串起来。

有个细节很值得注意:行为表一定要存“上下文快照”。比如用户在某个推荐位的第几个位置点击了什么商品,当时页面上下文的风格标签是什么,这些字段一开始我没建,导致后面分析推荐效果时两眼一抹黑。加上context_type和context_value两个字段后,才能说出“首页第二屏推荐现代风沙发的点击率是XX”这种有效结论。

2.2 商品、方案与标签的建模

全屋定制的商品层级比标准SKU复杂。一个“客厅定制方案”由沙发、茶几、电视柜、边柜、灯具、装饰画组成,每个单品又有多组材质和颜色可选。我最终建了五张核心表:产品表、产品变体表、方案表、方案-产品关联表、标签表。

标签表是我个人最满意的设计。每件产品和每个方案都通过一个多态关联表挂标签,标签分“风格”“材质”“空间”“价格带”“人群”五大类。比如一套柜子可以同时有“现代简约”“原木”“客厅”“1.5万-2万”“年轻家庭”五个标签。

这个设计背后的考虑是让推荐算法的特征输入保持稳定。我不需要每次都在SQL里join一堆业务表去拼特征,而是直接查标签关联表,统一转成向量交给算法。数据库规范化的收益在这里体现得很彻底。

2.3 推荐结果要不要落库

推荐结果缓存策略上,我分了两条线。首页推荐流和“相似方案”这类结果,因为更新频率低,我选择落库到推荐结果表,每天凌晨用定时任务全量刷新;而“看了又看”“最近浏览同类推荐”这类强实时结果,直接写Redis,设置两小时过期时间。

选择落库的理由很直白:调试方便,运营可干预。推荐结果表里留了source_type字段,能区分这条结果是来自内容相似、协同过滤、热门补位还是人工置顶。运营人员发现某条推荐有问题,可以直接在后台把人工置顶盖上去,不用逼我临时改算法。这个设计后来在线上救了很多次场。

3. 智能推荐算法:从协同过滤到混合推荐

3.1 第一版:基于内容的推荐

第一版我只做了基于内容的推荐,核心思路是把每个产品变成一组标签向量,再用余弦相似度计算产品之间的接近程度。

标签向量我用最朴素的0/1编码,逻辑是:如果一个产品有两个标签,对应位置就置1,没有就置0。查询时取用户最近收藏或停留过的一个产品,算它和其他所有产品的余弦相似度,取TopN。

import math from collections import Counter def build_tag_vector(tag_list, all_tags): return [1 if tag in tag_list else 0 for tag in all_tags] def cosine_similarity(vec_a, vec_b): dot = sum(a * b for a, b in zip(vec_a, vec_b)) norm_a = math.sqrt(sum(a * a for a in vec_a)) norm_b = math.sqrt(sum(b * b for b in vec_b)) if norm_a == 0 or norm_b == 0: return 0.0 return dot / (norm_a * norm_b)

这个版本的问题马上暴露了:它只会推荐“和用户正在看的商品很像的东西”,但用户有探索新风格的意图时,结果非常单一。一个一直看北欧风的用户,系统永远推荐北欧风,直到他看腻了换别的,算法才开始反应。纯粹基于内容的推荐,在全屋定制这种低频高决策场景里,做不了“惊喜感”。

3.2 第二版:协同过滤

第二版我加了基于物品的协同过滤。考虑到用户量级没那么大,我没有用现成的推荐框架,而是直接用Python字典和列表手写了一个矩阵运算版本,方便看清原理。

核心思路只有一句话:如果两个物品被同一批用户以相似的方式交互过,那么这两个物品就是相似的。这里的“交互方式”我换算成了分值:浏览记1分,收藏记3分,加入方案清单记5分,下单记10分。

def build_item_score_matrix(behavior_data): item_user = {} for behavior in behavior_data: item_user.setdefault(behavior.item_id, {}) item_user[behavior.item_id][behavior.user_id] = behavior.score return item_user def calc_item_similarity(item_user): sim_matrix = {} items = list(item_user.keys()) for i in range(len(items)): for j in range(i + 1, len(items)): item_i = items[i] item_j = items[j] common_users = set(item_user[item_i].keys()) & set(item_user[item_j].keys()) if len(common_users) == 0: continue vec_i = [item_user[item_i][u] for u in common_users] vec_j = [item_user[item_j][u] for u in common_users] sim = cosine_similarity(vec_i, vec_j) sim_matrix[(item_i, item_j)] = sim return sim_matrix

协同过滤跑通后,效果比内容推荐自然很多,因为它是从大量用户的真实行为里找规律。但紧接着就遇到了经典问题——数据稀疏和冷启动。新上架的产品没有行为数据,它在协同过滤里就是一座孤岛,永远不会被推荐出去。新注册用户没有历史行为,系统不知道该推什么,只能推一个全局热门列表。

3.3 混合推荐与冷启动处理

最终方案是加权混合推荐。推荐结果分成三路取并集:基于内容的相似推荐占40%,协同过滤的行为推荐占50%,热门补位和人工置顶占10%。三路结果按各自的权重生成候选集,再用最终得分排序。

对于冷启动用户,我用了一个很取巧的手段:注册流程里加一个“风格偏好选择”页面,用户勾选喜欢的风格、预算区间、空间类型。这些显性偏好直接变成初始标签向量,让新用户第一次进入首页时也能收到看起来像“定制化”的推荐结果,而不是一屏无差别热门款。

对冷启动产品,解决方案是给产品打足标签,先靠内容推荐带流量,等累计到了一定交互量,协同过滤自然接管。这个过程我写成了定时任务,每天扫描一遍“行为数据少于阈值但标签完整”的产品,把它们的展示权重临时上调。

3.4 参数调优与评价指标

推荐算法的参数不是拍脑袋定的。我的做法是每周拉一次离线评测数据,把过去七天的用户行为按照时间切分,前六天做训练,最后一天做验证,统计推荐结果的点击覆盖率和下单转化率。

关键参数有两个:相似度计算时是否做归一化,以及混合推荐各路结果的权重占比。我在实际调参中发现,权重比用固定的0.4、0.5、0.1并非最优,冷启动期需要临时把内容推荐权重提高,成熟期再调回协同过滤主导。所以我把权重设计成可在后台配置,配合定时任务动态调整,比硬编码灵活太多。

4. Django项目结构与核心代码实现

4.1 项目布局与应用划分

推荐系统的代码实现阶段,我坚持不把算法逻辑塞进Django视图层。项目拆成了四个独立应用:users负责用户和偏好标签,products负责产品和方案管理,recommend负责算法计算和结果缓存,trade负责订单流程。

这样的结构让职责非常清楚。recommend应用里只放算法代码和缓存读写逻辑,视图层想用推荐结果,只需要调用一个统一的接口。如果后续要替换算法模型,完全可以不碰其他应用。

一个容易踩的坑是Django项目相对路径。算法模块需要读取用户行为数据时,不要直接在视图里用相对路径拼文件,一定要用项目根目录的BASE_DIR做基准拼接,否则部署到不同环境时文件路径全乱。我一开始没注意,换到服务器上之后,推荐模块一度读不到训练数据。

4.2 推荐服务如何被调用

我封装了一个独立的推荐服务模块,对外只暴露一个函数:

def get_recommendations(user_id, scene_type, top_n=10): cache_key = f"recommend:{user_id}:{scene_type}:{top_n}" cached = cache.get(cache_key) if cached: return cached candidates = [] if user_id: candidates.extend(behavior_based_recommend(user_id, top_n)) candidates.extend(content_based_recommend(user_id, top_n)) else: candidates.extend(hot_recommend(top_n)) candidates = merge_and_rank(candidates, top_n) cache.set(cache_key, candidates, timeout=600) return candidates

视图层只做两件事:取用户ID、调用推荐服务、把结果传给模板。所有缓存逻辑都封装在服务内部,视图不需要感知Redis还是本地缓存。这种分层方式让后续调试省了大力气,因为所有推荐相关的日志都可以统一在服务层打点,而不是散落在各个视图函数里。

4.3 用户行为埋点的实现

行为埋点我用的是前后端配合的方案。前端在关键操作按钮上统一挂载>gunicorn config.wsgi:application -w 4 -b 127.0.0.1:8000

用Supervisor守护Gunicorn进程,保证它挂了能自动拉起来。Supervisor的配置里最容易被忽略的是环境变量和PATH设置,如果进程没有正确加载项目的虚拟环境路径,启动时会直接报“找不到wsgi模块”的错误。

我当时卡在Supervisor启动失败半小时,最后发现只是environment=PATH="/home/myuser/.virtualenvs/proj/bin"这一行没写。

5.3 Nginx静态文件与反向代理

Nginx在这个架构里承担两个职责:托管静态文件和反向代理动态请求到Gunicorn。静态文件要手动执行collectstatic收集到项目的staticfiles目录,然后在Nginx配置里用alias指过去。

反向代理配置没什么花活,但有个重要细节:一定要把X-Forwarded-For和Host头传过去,Django才能正确识别用户真实IP和域名。否则有些依赖客户端IP的功能会全部失效,推荐日志里记录的全是内网地址。

5.4 数据库迁移与数据初始化

数据库迁移直接使用Django自带的迁移系统,上线前务必先备份。数据初始化我写了一系列management command,负责把产品标签表、风格偏好表、演示数据导入进去。

这里最容易被忽略的是迁移顺序。先在本地把迁移文件跑通,其次在服务器上拉取代码后依次执行migrate和collectstatic,最后再做数据初始化。否则表还没建好就灌数据,会报外键约束错误,排查起来非常痛苦。

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

6.1 推荐列表为空或数据稀疏怎么办

最常遇到的问题就是新用户访问首页时推荐列表为空。我排查的第一件事就是确认Redis里有没有缓存,没有缓存再去查用户行为表。发现新用户根本没有行为数据时,直接返回热门推荐列表补位。

对于数据稀疏问题,我建了一个“相似用户”的兜底逻辑:把用户按城市、预算区间、偏好风格三个维度分组,组内其他用户的热门行为就是当前用户的默认推荐。这个逻辑虽然简单,但比纯热门列表的点击率高很多。

6.2 ORM查询慢和N+1问题

推荐服务在组装候选集时,最容易触发N+1查询。比如查出100个候选产品后,再循环查每个产品的标签,就会多出100次数据库查询。我习惯用prefetch_related一次性预取关联数据,效果立竿见影。

有一个排查技巧可以分享:Django有一个调试工具叫silk,可以记录每个请求执行的SQL语句数量和时长。我靠它抓到了一个特别隐蔽的bug——一个标签关联查询在循环里被调用了多次,实际执行了将近500条SQL语句。加上prefetch_related之后,整个推荐页面从3秒降到300毫秒。

6.3 部署后静态文件丢失

静态文件丢失是最常见的部署问题。明明本地页面样式正常,服务器上打开就是纯裸页面。原因通常是STATIC_ROOT和STATIC_URL配置不正确,或者collectstatic没有执行成功。

我的排查顺序是:先确认配置文件里的STATIC_ROOT是绝对路径,再确认执行collectstatic时的虚拟环境正确,最后检查Nginx是否指向正确目录。三步下来,九成问题都能解决。

6.4 线上调试推荐算法的思路

线上调试推荐算法不能只靠肉眼看页面,最关键的是要有完整的日志链路。我在每个推荐结果里都附加了一个隐藏字段——trace_info,记录了这次推荐走了哪些推荐源、每路贡献了多少个候选、最后得分多少。用的时候直接看埋点日志,就能知道某条结果为什么出现在这个位置。

这种方法比传统地“看效果不对就调权重”靠谱一万倍。它能直接告诉你,是协同过滤打分太高把内容推荐挤压掉了,还是缓存过期导致全走热门补位了。

7. 测试与验证:推荐系统不是“能跑”就行

7.1 推荐结果的可解释性测试

推荐系统上线后,我做的第一轮测试不是性能测试,而是可解释性测试。随便抽一个用户,检查他看到的推荐结果能不能从行为日志里找到解释。比如用户因为收藏过一套“日式原木风电视柜”,系统推荐了同风格的茶几,这就合理。如果推了一个“欧式实木书房组合”却说不上来由,那这个推荐模型肯定有问题。

可解释性不光是技术问题,还是产品问题。用户看到一个推荐结果,如果心里有“为什么给我推这个”的疑惑,信任感就没了。所以平台的前端页面上,每条推荐都带了一行小字,标明“因为你看过XX方案”,转化率比不带说明的版本高出不少。

7.2 离线评测与在线A/B测试

完整的推荐系统验证我分了两步。第一步是离线评测,用历史行为数据回放,计算推荐结果的点击率、覆盖率、多样性等指标。第二步是在线A/B测试,把用户分成两组,一组用旧算法,一组用新算法,观察点击率和下单转化率的差异。

在线测试最忌讳的是“测一次就定结论”。我习惯跑满七天,覆盖整周的用户行为周期。全屋定制的决策周期特别长,一个用户从浏览到下单可能要五到十天,测一周是最短统计窗口。

7.3 收集反馈比优化算法更重要

最后想提的是用户反馈渠道。再聪明的算法也会犯错,平台必须给用户表达“我不喜欢”的出口。我建了一个隐式负反馈收集机制:用户连续忽略某个推荐位超过五次,系统自动降低该风格的权重;用户点击了“不喜欢”按钮,立刻把对应标签加入负面标签池。

这个机制看起来简单,对推荐体验的改善极其明显。它让算法不再固执己见,也给了用户一种掌控感。全屋定制用户的耐心本来就有限,一旦觉得平台“听不懂人话”,离开率极高。

项目做完后我最大的体会是:代码本身不是最难的,难的是把业务想明白。推荐算法再精巧,如果连用户的决策路径和产品组合逻辑都没理解透,出来的结果只会“看起来很智能”,但离真正有用还差一大截。最后分享一个深坑里的经验:在推荐结果页永远保留一个“换一批”按钮,再优秀的算法也会有抽风的时候,给用户一条逃生通道,比加十个特征工程都重要。这个项目从源码到设计文档再到部署文档一路整理下来,回头看,最有成就感的不是那些相似度公式,而是看着用户真的顺着推荐流完成了整套柜子下单的那一瞬间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询