40 条候选里 38 条来自同一个源,主卡整屏重样

我自己手搓的一个副业点子搜索有关的日报系统里有三个地方要展示候选:主卡、六维雷达、开源方案匹配。原来三处各自写了一句 items[:40]。

跑了一阵子发现一个尴尬的 bug:同一天的日报,主卡讲的是 Reddit 上的 SaaS 吐槽,雷达却全是 HN 的条目。三个模块各切各的,拿到根本不是同一批东西。

于是把取窗收敛成一个模块。但真正麻烦的是下一个问题:切出来的 40 条里,38 条来自同一个源。

分数不能直接比

最直觉的做法是按 signal_score 排序取前 40。问题是这个分数在不同源上不是一把尺子:

  • 降级后的 RSS 源,分数是位次估计(1~30),282 条里绝大多数并列在 30
  • Lobsters 是真实票数(0~54)

跨源比大小,等于拿两把尺子量。有人会想到"除以源内峰值"做归一------我试过,更糟:250 条并列 1.0 的条目会直接把窗口淹掉,把本来没有梯度的数据硬编出一条假梯度。

最后用的是序数归一:不看分数绝对值,只看源内名次。每个源先出第 1 名、再出第 2 名,按权重决定每轮各源取几条。

python 复制代码
def rank_norm_weights(cfg):
    rn = ((cfg or {}).get("signals") or {}).get("pool_rank_norm") or {}
    if not isinstance(rn, dict) or not rn.get("enable"):
        return None          # 默认关:不配这个键时与直接切片逐字节等价
    out = {}
    for name, w in (rn.get("weights") or {}).items():
        try:
            out[name] = max(1, int(w))
        except (TypeError, ValueError):
            continue
    return out

一个我刻意留着的默认值

注意 return None。这套轮转默认是关闭的。

原因不是偷懒:这个模块在数据采集主链上,任何改动都可能让已经在跑的日报变形。所以红线是------不配 pool_rank_norm 这个键时,取窗结果必须与原来的 items[:40] 逐字节一致。开了才生效,关了等于没改过。

同样的护栏还有一处:影子源(新接入、只写池不进窗的源)用 window_enabled: false 标出来,否则新源一接进来就改主卡,"影子"就影不住了。

python 复制代码
def shadow_sources(cfg):
    out = set()
    for name, sc in ((cfg or {}).get("pool_sources") or {}).items():
        if isinstance(sc, dict) and sc.get("window_enabled") is False:
            out.add(name)
    return out

这里有个容易踩的坑:pool_sources 和 sources 是两套不相干的东西。后者是热点源(arxiv / techmeme 之类),写混了会把风口数据当池源过滤掉。我在 docstring 里专门写了这一句。

最花时间的是"哪条算真需求"

单源霸榜解决之后,还有个更隐蔽的问题:按分数取窗时,需求向的帖子几乎进不来。展示帖("我做了个 X")的赞数天然比提问帖高,10 张卡里只剩 2 张是真需求。

第一版用宽口径正则找需求,10-27 实测 32 条"需求"里 23 条是 Looking for beta testers / honest feedback------那是展示帖,不是需求。

拿 LLM 判过的结果当真值对了一遍,原正则 precision 3/4,但 recall 只有 3/11。漏掉的全是这类真痛点:

How do people fix this without hiring a designer? Why is it so expensive to send an email? So tired of these AI crawlers

同时它还会把 I built X. Is there a market? 这种"展示帖 + 附一句提问"误判成需求------因为负例只杀了 testers / feedback,没杀展示帖标记。

改法是收窄成意图词 + 硬负例两条正则,并且每条新增模式都要有实测支撑 (命中数、展示帖数、LLM 真值),没证据的一律不加。how can i / looking for a / best way 这些都量过并否决了。

最后给需求子队列留名额时还有个细节:留了名额也要走源内轮转,否则名额会被 Reddit 一家吃光------刚止住的霸屏,换个入口又放回来了。

最后

这套东西做完,我最大的感受不是"归一算法怎么选",而是:多源数据混在一起,最先坏掉的从来不是排序,是"谁有资格出现在候选里"。

排序再漂亮,只要一个源占满窗口,剩下的数据等于不存在。而这类问题不会报错,日志里一切正常,只是你每天看到的日报越来越像同一个网站。

相关推荐
宅小年6 分钟前
为什么你的 Grok Bot 总走偏?教你写一个真正好用的 Bot 模板
人工智能
明月_清风7 分钟前
Meta 开源了一个 AI Native 组件库:Astryx,150+ React 组件,专为 AI Agent 而设计
前端·人工智能·ai编程
神鹰手哥_起飞版10 分钟前
一句「你确定吗」能让 AI 把白的说成黑的?我用直接用豆包来做实验
人工智能
afumu17 分钟前
把五个网页版 AI 拉进同一个群聊:不用 API key 的多 Agent 编排
人工智能·chrome·typescript
寂寞的船37 分钟前
【Unity知识速递】第4期 MCP for Unity:用 AI 自然语言控制编辑器
人工智能·unity·编辑器
彭祥.1 小时前
Jetson边缘计算实现 红外测温+可见光AI识别系统
人工智能·边缘计算
東隅已逝,桑榆非晚1 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom1 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
明王明王1 小时前
从零搭建一个 AI Infra 实验室㉑:llm-d——从单个 vLLM 走向分布式推理
人工智能·ai infra
武子康2 小时前
图片删了,为什么旧链接还能打开、后台还能生成回来?
人工智能·agent