项目实训——大数据租房推荐智能体——地图通勤评分(七)
本次博客主要记录动态权重与三模块管线串联工作。
一、两个优化角度
第一:评分引擎的六个维度各有固定权重(价格 0.25、通勤 0.25、配套 0.20……),但不同用户对"好房子"的定义完全不同。一个刚入职的应届生可能把通勤放在第一位,一个有家庭的中年租客更看重户型和周边配套。如果权重写死了,"评分"就成了一个自说自话的东西。
第二:爬虫、地图、评分三个模块虽然各自能跑,但它们之间的关系需要调用方自己来协调——先调爬虫拿原始数据,手动标准化格式,再一条条调地图接口做富化,最后把结果塞进评分引擎。这种"手工管线"在开发调试时没问题,但显然不能作为最终交付的形态。
二、单权重调高
用户通过 API 传入的权重可以是任意子集。比如一个对通勤极度敏感的用户,只想调高通勤权重:
{
"budget": {"min": 3000, "max": 5000},
"weights": {"commute": 0.5}
}
只传了一个 commute,剩下五个维度怎么办?引擎的处理逻辑:
def _resolve_weights(self, user_weights):
merged = {**DEFAULT_WEIGHTS} # 1. 先用默认权重填满
if user_weights:
for k, v in user_weights.items():
if k in merged and v >= 0:
merged[k] = v # 2. 用户指定的覆盖掉
total = sum(merged.values())
return {k: v / total for k, v in merged.items()} # 3. 归一化
关键在第三步的归一化。用户把commute设成了 0.5,但其余维度还是默认值,总和变成了 1.25。归一化之后:commute = 0.5/1.25 = 0.40,其余按比例缩小。这样无论用户传什么、传几个,所有权加起来永远是 1.0,总分不会溢出到 150 分,也不会缩水到 50 分。
三、三模块管线:从四次手动调用到一次请求
手工管线的痛点:
在管线串联之前,要完成一次"搜索+分析+评分"的完整流程,调用方需要:
1. 调 /crawler/search 拿原始房源列表
2. 自己写标准化代码处理三种不同格式
3. 自己拼完整地址
4. 一条条调 /map/enrich(还得控制频率防止触发高德限流)
5. 把富化结果塞进 /scoring/score
这中间每一步都可能出错——格式不一致、地址拼错了地理编码失败、并发太快被高德限流。而且这些步骤和业务逻辑无关,纯粹是数据转换的脏活累活。
管线设计:每一步都有明确的输入输出
pipeline.py 的 RecommendPipeline.run()`把这个流程做成了链式调用,每步之间通过统一的数据模型衔接:
search_params (用户搜索条件)
↓ crawl_housing_data()
raw_listings (原始爬虫输出, List[dict])
↓ normalizer.normalize_batch()
normalized (标准化房源, List[NormalizedHouseListing])
↓ address_builder.build_batch()
normalized (full_address 已填充)
↓ map_service.enrich_batch()
enriched (地图富化后, List[MapEnrichedListing])
↓ engine.score_batch()
results (评分排序, List[ScoreResult])
↓ 取 top_n 返回
每一步的函数签名都很明确——输入类型和输出类型都是确定的 Pydantic 模型。这样做的好处是:如果你想换一个评分算法,只需要替换ScoringEngine,上下游完全不受影响。如果你想用百度地图替代高德,只需要换MapService,管线逻辑不用动。
四、对外暴露的接口
最终这一切被打包成一个端点:
POST /scoring/recommend
{
"search_params": {"city": "济南", "district": "历下", "rent_type": "整租", ...},
"preferences": {"budget": {...}, "rooms": 2, "tags": ["近地铁"], "weights": {...}},
"top_n": 10
}
一个请求完成搜索→标准化→地图分析→评分排序。调用方不需要知道里面有三种爬虫格式、需要构造完整地址、需要控制 API 频率——这些东西是管线内部的事。
五、后续进一步探索优化角度
更多推荐
所有评论(0)