三源 POI 合并规则文档

数据版本 v4 · 最后更新 2026-07-01 · 覆盖城市 12 个

目录
  1. 数据源概览
  2. 处理流水线
  3. 匹配规则(三级漏斗)
  4. 字段合并策略
  5. 评分算法
  6. 噪声过滤
  7. 输出格式
  8. 运行时动态评分
  9. Places 差异化打分 NEW
    1. source_category 生成
    2. 五种类型分布
    3. 差异化打分矩阵
    4. build_breakdown() 六维明细
    5. Places 独有 POI 追加
    6. 输出排序

1. 数据源概览

数据源角色获取方式更新频率覆盖
高德地图 主表骨架 高德 POI 2.0 API 每日 3:00 12 城市全量
百度地图 补充字段 + 独有 POI 百度 Place API(39 分类) 每日 3:30(轮转,2 城/天) 12 城市轮转
Google Place 评分增强 + 小众挖掘 Google Place Details API 每日 4:00 高德主表按名称查询 + 独立 POI 发现
设计原则:高德 POI 提供核心骨架(坐标 + 分类),百度 POI 补全字段(标签 / 价格 / 营业时间)并补充高德未收录的 POI,Google 用于评分增强、人气判断及小众景点挖掘。Google Places 返回的 POI 额外标记 is_placessource_category,在评分阶段享受差异化加分。

2. 处理流水线

步骤时间脚本说明
13:00daily_poi_cron.py高德拉取 12 城市全量 POI + 评分,输出 v2.json
23:30baidu_rotator.py百度轮转拉取(2 城/天),写入 raw/baidu_{city}.json
34:00google_ratings_cron.py按 v3 名称查询 Google 评分,写入 google_ratings.json
44:30merge_enrich.py三源合并输出 v3.json,本文档即描述此步骤
5手动places_merge.pyPlaces 景点 API 富化,输出 places_cache.json;详见下方流水线

Places 数据流水线

Places 数据由独立脚本 places_merge.py 处理,与高德/百度主流水线分离:

2.1 数据源

文件说明
/data/git/places/popular.json人工维护的热门景点列表
/data/git/places/unpopular.json人工维护的小众冷门景点
/data/git/places/value.json人工维护的口碑景点(最高优先级)

三文件合并时优先级 value > unpopular > popular,同景点 tags 取并集,source_category 累积。

2.2 富化流程(places_merge.py)

步骤规则
本地匹配优先使用高德 v3 文件、百度 raw 文件做名称匹配(不消耗 API 配额)
API 查询(本地未命中)依次查询 高德 Place API → 百度 Place API → Google Places API
坐标兜底所有 API 均未命中时,使用城市中心坐标作为兜底

2.3 缓存机制(places_cache.json)

参数
TTL30 天CACHE_TTL_DAYS=30
缓存键城市;景点名称(如 深圳;大梅沙海滨公园
过期策略加载时自动删除过期条目;被删条目在本次运行中走完整富化流程重新查询 API
写回机制仅未过期缓存复用 + 本次 API 富化结果写回,过期条目不保留

2.4 merge_enrich.py 集成

操作说明
匹配已有 POI城市;名称 键匹配高德主表 POI,注入 desc / tags / highlights / places_type / source_category
追加独有 POIPlaces 中未匹配到高德 POI 的条目,若有坐标则追加,并使用 classify() 重新分类

Google 评分匹配时机

Google 评分缓存为 名称 → rating 的键值对,与城市无关。每当出现新 POI 名称(高德新增或百度独有追加),该名称会在次日 4:00 自动添加到 Google 批量查询队列中,4:30 合并时即可命中评分。

3. 匹配规则(三级漏斗)

L1 — 精确名称匹配 L1

条件阈值可信度
名称去括号后完全一致 距离 ≤ 1000m 100%
示例: "海上世界" ⇔ "海上世界" (括号不影响)

L2 — 归一化名称匹配 L2

规则说明
去括号去掉 (…) 及(…)
去符号移除 · - — - 空格
数字归一化一→1 二→2 … 九→9 零→0
异体字统一覔→觅 峯→峰 羣→群 啓→启 衞→卫 裏→里 佈→布
距离约束≤ 100m
示例: "2号沙滩" ⇔ "二号沙滩"
"溪涌度假村海滨浴场" ⇔ "溪涌度假村-海滨浴场"
"深圳湾公园日出剧场" ⇔ "深圳湾公园-日出剧场"
"覔书店" ⇔ "觅书店"

L3 — 词片共识匹配 L3

需同时满足以下全部条件:

  1. 同类型:高德一级分类映射后与百度一级 tag 一致
  2. 距离 ≤ 30m
  3. 语义关联(二选一):
正确匹配:
  "深圳书城(宝安城店)" ⇔ "深圳书城·宝安城"  (词片"深圳书城""宝安城"重叠)
  "广东梧桐山国家森林公园" ⇔ "梧桐山"       (包含关系)

过滤案例(L3 拒绝):
  "大梅沙湾游艇会" ⇔ "深圳海钓游乐园"       (同类型 + 20m,但无词重叠)

类型映射表

高德一级分类百度一级 tag
风景名胜旅游景点
公园广场旅游景点
科教文化服务文化场馆
体育休闲服务休闲娱乐
购物服务购物
餐饮服务美食
住宿服务酒店

4. 字段合并策略

字段优先级默认值说明
rating Google > 高德 > 百度 3.0 先按 Google 缓存名称匹配注入 google_rating,评分计算时按优先级取值
price 百度 > 高德 无默认值 百度有价则覆盖,否则保留高德原值。无为 null
shop_hours 百度 百度有则写入,否则保留高德原值
baidu_tag 百度 匹配成功后写入,用于分类映射和噪声判断
source_count 1 每命中一个额外数据源 +1(max 3)

匹配后处理

  1. 已匹配的百度 POI 标记为"已消费",不再作为独有 POI 追加
  2. 未被匹配的百度 POI 过噪声后追加为独立记录(source_count=1
  3. 百度独有 POI 的 typetypecode 留空

5. 评分算法(family_score)

满分 10 分,四维加权 + Places 背书 + 年龄适配(静态预计算):

维度满分规则
评分 (rating) 3.0 Google > 高德 > 百度 > 默认 3.0,min(rating, 5.0) / 5.0 × 3.0
Places value 类型:直接设为满分 3.0
内容丰富度 (content) 1.5 有营业时间 +0.4 / 有价格 +0.4 / 有电话 +0.35 / 有地址 +0.35
知名度 (popularity) 1.60 百度热度:≥500→10, ≥200→7, ≥100→5, ≥50→3, ≥10→1,无→0
百度评论数:≥200→5, ≥50→3, >0→1,无→0
百度排行榜:命中 +3
Google 评分:有评分 +0.6
各子项相加后 / 10 × 1.6
Places popular 类型:直接设为满分 1.60
亲子适配 (family_fit) 3.0 按分类加权(见下表)
cat_weight / 3.0 × 3.0
Places 背书 (places_bonus) 3.0 仅 is_places POI 有效,按 source_category 差异化:
popular 类型 → 0(已通过 popularity 满分体现)
value 类型 → 1.5
unpopular 类型 → 3.0
详见 第 9 章
年龄适配 (age_adapt) ±1.6 静态预计算写入 score_breakdown.age_adapt,在 server.py Phase 4 查表取用
底层逻辑:AGE_BONUS[category] / 5.0

汇总公式family_score = rating_score + content_score + pop_score + family_fit_score + places_bonus,最终 min(s, 10.0)

亲子适配权重

分类权重分类权重
儿童乐园3.0运动场馆1.8
主题乐园2.8展览馆1.8
水上乐园2.8文化场馆1.8
动物园2.8休闲娱乐1.8
水族馆2.5美术馆1.5
科技馆2.5图书馆1.5
天文馆2.5其他1.5
博物馆2.5
户外公园2.5
户外活动2.2
风景名胜2.0
植物园2.0

6. 噪声过滤

百度噪声规则

命中以下任一一级 tag 的 POI 直接过滤:

购物 | 出入口 | 交通设施 | 房地产 | 政府机构 | 酒店
生活服务 | 美食 | 公司企业 | 医疗 | 道路 | 组织机构 | 金融 | 丽人

命中以下组合需同时满足子标签才过滤:

一级 tag子标签
教育培训幼儿园 / 小学 / 培训机构 / 校内设施 / 职能机构
休闲娱乐茶馆 / 电玩电竞
注意:噪声过滤仅在百度独有 POI 追加阶段执行。在匹配阶段,即使百度 POI 属于噪声类别,只要与高德 POI 匹配成功,仍会参与字段补全(因为高德侧已确认该 POI 有价值)。

分类映射

合并后每条 POI 按 name + type + baidu_tag + tag 文本关键词匹配,归属于以下 19 个分类之一:

儿童乐园 | 主题乐园 | 水上乐园 | 水族馆 | 动物园 | 植物园 | 科技馆
天文馆 | 博物馆 | 展览馆 | 美术馆 | 图书馆 | 运动场馆 | 户外公园
户外活动 | 风景名胜 | 文化场馆 | 休闲娱乐 | 其他

7. 输出格式

输出文件 /var/www/kids-trip/family_scored_v3.json

{
  "scored": [
    {
      "name": "深圳世界之窗",
      "city": "深圳",
      "lat": 22.537,
      "lng": 113.973,
      "type": "风景名胜;国家级景点;世界之窗",
      "typecode": "110200",
      "rating": 4.6,
      "price": "220",
      "cost": null,
      "address": "深圳市南山区深南大道9037号",
      "tel": "0755-26608000",
      "shop_hours": "09:00-22:30",
      "poi_id": "B0FFGXXXXX",
      "first_seen": "2025-01-01",
      "fee": "",
      "baidu_tag": "旅游景点;风景名胜;主题公园",
      "d7_hot_value": 90,
      "baidu_comment_num": 200,
      "baidu_ranking": "全民爱去榜NO.3",
      "google_rating": 4.5,
      "source_count": 3,
      "is_places": false,
      "category": "主题乐园",
      "family_score": 7.84,
      "places_type": null,
      "desc": "",
      "tags": [],
      "highlights": "",
      "source_category": [],
      "score_breakdown": {
        "rating": 2.76,
        "content": 1.5,
        "popularity": 0.96,
        "family_fit": 2.8,
        "places_bonus": 0.0,
        "age_adapt": {"under12": 1.6, "12+": 1.6}
      }
    }
  ]
}
字段来源说明
name高德 / 百度POI 名称
city高德 / 百度所属城市
lat / lng高德 / 百度WGS84 坐标
type / typecode高德高德多级分类,百度独有 POI 留空
rating高德高德原始评分
price百度 > 高德合并后的价格(数字字符串)
cost高德高德原始费用信息(保留但不使用)
address / tel高德 / 百度地址和电话
shop_hours高德 / 百度营业时间(百度优先)
poi_id高德 / 百度唯一标识
baidu_tag百度百度原始分类标签(分号分隔)
google_ratingGoogleGoogle 评分(命中时存在)
source_count合并命中数据源数量(1-3)
is_places NEWGoogle是否来自 Google Places 查询
source_category NEWAI 分类(每日更新)AI 从公网多源数据聚合分类,详见第 9 章
places_type NEWGooglePlaces 返回的类型标签(中文)
desc NEWGooglePlaces 描述文案
tags NEWGooglePlaces 标签列表
highlights NEWGooglePlaces 亮点文案
category分类映射统一亲子分类
family_score评分算法家庭友好度评分(0-10)
score_breakdown评分算法六维评分明细(含 places_bonus 和 age_adapt)

8. 运行时动态评分

用户请求到达 server.py 后,依次执行四阶段流水线,对当前页 POI 逐条叠加实时修正。最终评分 adj_score = family_score + Σ修正项增量

请求处理流程

阶段操作说明
Phase 1关键词过滤kw 匹配 name / address / category,不匹配的直接跳过
Phase 2静态排序 + 分页(免费优先, -family_score) 排序,再按 offset/limit 分页
Phase 3OSRM 驾车数据调用 OSRM Table API 一次请求算起点到当前页所有 POI 的真实驾车耗时和距离,非全量
Phase 4逐 POI 动态修正依次叠加年龄段、天气、距离、Google、交通等修正项

Phase 4 修正项明细

修正项触发条件计算逻辑分值范围
年龄段适配 用户传入 age 参数 score_breakdown.age_adapt.under12.12+ 直接取用(已由 merge_enrich 静态预计算),不再每次请求动态查表 ±2.0
实时天气 用户传了经纬度 + 天气缓存命中 基于 precip_prob 分五档:
博物馆/科技馆:降雨 +10×ratio
儿童乐园/水族馆:+7×ratio
主题乐园/动物园/水上乐园:-5×ratio
海滩/公园/植物园/景区:-10×ratio
晴天额外:户外类 +5
AQI 修正:≥150∩户外 -8 / ≥100 -4 / 优 +5
-10 ~ +10
距离重算 用户传了经纬度 OSRM 驾车公里数替代 Haversine 直线距离,按阶梯分档替换原 distance_score
≤3km→10 / ≤5km→8 / ≤10km→6 / ≤20km→4 / ≤30km→2 / ≤50km→0 / >50km→-5
替换原值
Google 评分融合 Google 缓存命中 quality = max(amap, google) / 5 × 32
trending 按 Google review 总数分档:≥5000→10 / ≥2000→8 / ≥500→6 / ≥200→4 / ≥50→2 / 0→0
quality: 0~32
trending: 0~10
Google 实时拥挤 popular_times 有 current_hour + avg_hourly ratio = now / avg。若 >1.5 则 crowd_penalty = -8
无 popular_times 时按 review 数回退推算:>10000→-3 / >5000→-1 / ≥200→0 / ≥50→+2 / >0→+3
-8 ~ +3
交通综合 始终执行 max(公交分, 停车分 + 驾车分)
公交分:按城市默认 transit 时间分档(≤15→7 / ≤30→5 / ≤45→3 / ≤60→1 / ≤90→-2 / >90→-5)
停车分:Google 停车场数据命中 → +3 有停车场 / -3 明确无
驾车分:按 OSRM 驾车分钟分档(同公交分档)
-5 ~ 10

修正后的排序

所有修正项叠加完成后,当前页 POI 按 (免费优先, -adj_score) 二次排序返回。免费 + 实时修正高分 POI 靠前。

性能优化

机制实现
OSRM 懒加载仅请求当前分页的 POI(最多 ~50 个),非全量 POI
天气缓存按 0.01° 网格粒度内存缓存,同一网格 POI 共享,随 weather_cache 文件 mtime 失效
逆地理缓存按 0.01° 粒度高德逆地理编码缓存,10 分钟有效
城市公交默认值深圳 45min / 广州 114min / 东莞 140min / 惠州 212min / 珠海 191min / 中山 211min / 香港 65min / 澳门 65min / 其他 78min

9. Places 差异化打分 NEW

背景:Google Places 返回的 POI 天然带有 popularity(人气)信号和 review 数据,但小众景点往往因 Google 数据稀疏而在传统评分体系中吃亏。因此在 merge_enrich.py 中引入 source_category 三层标记和差异化打分,让小众高质量景点不被埋没。

9.1 source_category 生成

source_category 由 AI 从公网数据抓取自动分类生成,每日更新。分类规则由多个数据源聚合确定(评分、评论数、热度趋势等),主要标记:

标记AI 分类依据(多源聚合)含义
popular多源口碑数据达到规模化阈值(如评论数充足、榜单命中)热门景点
unpopular公网数据稀疏但仍被收录小众冷门
value多源评分聚合后达到高评价阈值口碑优良

三个标记互不排斥,一个 POI 可同时携带多个标记(如 ["popular", "value"])。标记优先级:popular 和 value 分别覆盖对应维度,unpopular 仅在无 popular 且无 value 时触发 places_bonus。

9.2 五种类型分布

source_category数量说明
["popular"]70纯热门
["unpopular"]64纯小众(无评分信号)
["value"]40纯口碑(高评分低人气)
["popular", "value"]16热门 + 口碑
["popular", "unpopular"]1人气临界(review 数接近 50)

9.3 差异化打分矩阵

score_poi()build_breakdown() 中,根据 source_category 决定三个维度是否设为满分或额外加分:

source_categoryrating_scorepop_scoreplaces_bonus典型 family_score
popular 原始计算 (max 3.0) 1.60(满分) 0.0 ~7.5
value 3.0(满分) 原始计算 1.5 ~8.0
unpopular 原始计算 原始计算 3.0 ~8.7–9.2
popular + value 3.0 1.60 1.5 ~9.3
popular + unpopular 原始计算 1.60 0.0 ~7.5

核心逻辑(伪代码):

places_bonus = 0.0
if is_places:
    if 'value' in source_category:
        rating_score = 3.0           # 高评分 → 评分满分
        places_bonus = 1.5           # 高评分 → 额外 1.5 分背书
    if 'popular' in source_category:
        pop_score = 1.60             # 有人气数据 → 知名度满分
    if 'unpopular' in source_category 
       and 'value' not in source_category 
       and 'popular' not in source_category:
        places_bonus = 3.0           # 纯小众 → 3.0 分探索背书

family_score = min(rating_score + content_score + pop_score 
                   + family_fit_score + places_bonus, 10.0)
设计意图:popular 通过 popularity 满分确保排名不输给大景点;value 通过 rating 满分 + 1.5 背书奖励高质量但人气不高的景点;unpopular 通过 3.0 背书让真正的隐藏宝藏有机会上前排。纯 unpopular 虽无评分信号,但 places_bonus=3.0 足以将其 family_score 推至 8.7–9.2,使其在冷门城市或长尾列表中曝光。

9.4 build_breakdown() 六维明细

每条 POI 的 score_breakdown 包含六维:

字段类型说明
ratingfloat评分维得分 (max 3.0)
contentfloat内容丰富度得分 (max 1.5)
popularityfloat知名度得分 (max 1.60)
family_fitfloat亲子适配得分 (max 3.0)
places_bonusfloatPlaces 背书加分 (0 / 1.5 / 3.0)
age_adaptobject年龄适配值 {"under12": float, "12+": float}

9.5 Places 独有 POI 追加

places_cache.json 中未匹配到高德主表 POI 的条目(即 Places 独有),经过滤后作为独立 POI 追加:

Places 数据在 merge_enrich.py 中重新走 classify() 分类(含 CATEGORY_MAP 关键词兜底),解决高德 typecode 映射错误(如渔村误分类为餐饮)。详见 第 2 章 Places 数据流水线

条件说明
does_not_exist = True未在现有 POI 池中找到同名匹配
Google type 不在非亲子黑名单排除 lodging / clothing_store / electronics_store / hardware_store / home_goods_store / liquor_store / furniture_store / shoe_store / pharmacy / car_repair / gas_station / real_estate_agency 等
有 lat/lng坐标不为空

追加的 POI 继承 is_places=Truesource_categoryplaces_typedesctagshighlights 字段,参与后续评分。

9.6 输出排序

最终输出按 -family_score 降序排列。places_bonus 生效后,纯 unpopular 景点(如塱头古村 family_score 9.16)的排序位置大幅提升,popular 热门景点(如大梅沙 7.48)因无 bonus 而相对偏低——差异反映了"大众热门 vs 隐藏宝藏"的排序理念。


kids-trip 三源 POI 合并规则文档 · v4 · 2026-07-01