1 引言
半个月前,我发布了一个基于贝叶斯算法的Telegram广告拦截机器人 @BayesSpamSniperBot
(https://t.me/BayesSpamSniperBot)
项目地址:https://github.com/ramsayleung/bayes_spam_sniper
系列文章:
尽管项目代码开源,但我始终以产品思维运营它。上线半个月以来,经历了故障、用户反馈与持续优化,现将这段经历分享出来。
2 上线即故障
没想到我的产品的第一个线上故障来得这么快,发布的时候直接不可用,把正常消息都给删了,用户在各种途径都向我反馈:
故障的原因是我当时一直在收集垃圾广告的数据,太专注于垃圾广告数据,而忽略了收集的正常数据, 导致垃圾广告数据过多,消息都被认为是垃圾广告,被误删了。
通过补充大量正常消息数据,重新平衡训练集,模型逐渐恢复正常识别能力。
3 挑战
3.1 邮件与即时消息的差异
我在《基于贝叶斯算法的Telegram广告拦截机器人(一):从问题到产品》里面提到过:
常见的 Telegram 广告机器人是大多是基于关键字的,通过匹配关键字进行文本拦截,非常容易被发垃圾广告的人绕过。
这不禁让我想起了保罗.格雷厄姆在《黑客与画家》一书在2002年介绍的情况:
当时电子邮件兴起,也有非常多的垃圾邮件,常见的垃圾广告拦截方式是关键字匹配+邮件地址黑名单,但是既低效也容易被绕过。
保罗.格雷厄姆就创造性地使用贝叶斯算法(Bayes Theorem)实现了一个广告拦截器, 效果竟然出奇地好。
但产品上线之后,我发现聊天软件消息和Email虽然都是文字,还是有很大差别的:
Email 大多时候都是长文的,内容较长,并且大多情况,一封邮件上下文本身也很完整,就有较多的内容,较高的准确度来判断是否是广告。
而 Telegram, 微信这类的即时聊天软件,聊天消息大多都不长,可能把内容分成多条消息来发,就没有完整的上下文,比如:
换U
找我
单条消息很较难准确判断是否是广告,所以对即时消息做广告拦截本身就更难, 「短文本+无上下文」是NLP中的经典难题,也是本项目最大的技术挑战。
3.2 漏删与误删
漏删与误删是广告拦截中不可避免的矛盾权衡。
若想提高拦截率(召回率),就需降低置信度阈值,将更多疑似广告的消息拦截,但这也会增加误删正常消息的风险。
反之,若想避免误删(提高精确率),则必须提高置信度阈值,但这又会导致更多广告被漏掉。
在即时消息短小、上下文缺失的特性下,想同时实现零误删和零漏删几乎是不可能的。
权衡之下,我选择优先保证用户体验: 宁可漏删,不可误删
因为漏掉的广告,群友可以举报或由管理员手动删除;但误删的正常消息却无法恢复,对用户的伤害更大。
因此,我将拦截阈值设置为95%,即仅当模型有极高把握(>95%概率)判定为广告时才会删除。
这虽然会放过一些疑似广告,但最大程度地保障了正常聊天不被误删。
4 优化之路
4.1 自动删除消息
产品上线之后,很快就有用户来试用了,然后其中一个用户就提了一个非常好的优化建议。
这个警告的消息不会自动删除,如果有很多人在群里发广告,那么群里就会有一堆这样的消息,也算是对群消息的污染。
所以用户建议:
可以发这个提醒,但在几分钟后也把这个提醒消息删除掉
我觉得这是个非常好的优化体验,因为就把这个功能给加上了,提醒消息本身会在5分钟后自动删除。
倾听用户的声音是非常重要的,他们可能就会从他们的角度提出非常好的建议。
但是不要盲目听从用户的建议,比如也有用户建议:
我觉得还应该有以下功能.
- 恢复消息, 恢复用户. (让管理员恢复误删的消息和用户)
- 主动投喂正常消息. (让管理员主动投喂一些消息. 比如, 群里面昨天 的消息, 随便选一些正常的, 投喂给机器人)
恢复消息这个功能没有太大必要,并且也不实用,因为恢复消息这个功能本身就很微妙,是直接恢复被删除的消息呢,还是重新发一条新消息?
如:
- 2025-09-09 10:01:00 张三: 我今天吃了鸡翅
- 2025-09-09 10:02:00 李四:鸡翅有啥好的(被误删消息)
- 2025-09-09 10:03:00 王五:人家就喜欢吃,你管得着嘛
如果是直接恢复被删除的消息,当前时间是 2025-09-09 11:00:00
,把消息恢复之后,还有人会手动刷历史消息,查找旧消息么?
Telegram客户端不一定支持会跳转被恢复的旧消息,这意味着,你恢复误删的消息,也没人看得到。
假如是重新发一条新消息 鸡翅有啥好的
, 因为缺失了上下文,群里的人反而会疑惑,你在说什么。
解决误删问题本质是提高拦截的准确率,而非考虑如何恢复被误删消息,准确率提高了,误删就会减少, 自然就不需要考虑如何恢复消息,用户体验还会更好.
而主动投喂消息这个想法有点理所当然了。
没有任何群管理员有意愿帮忙训练这个机器人,对用户而言,他们只想要一个好用的广告拦截机器人,至于怎么开发,训练出来的,用户并不在乎。
所以用户不会有意愿和动力来优化这个机器人,不好用就再换一个好了,更何况,逐条消息收集的效率实在太慢太慢了, 所以我后面想出了一个比手工收集数据提效至少100倍的主意。
4.2 过滤重复消息
发现人难免会有误区,总会以为别人会和自己一样,之前看到发垃圾广告的人的时候,总会觉得他们是正常的用户手工发。
但是最近几天发现了一些规律,有用户把同一条消息反复发,不同的群还是发同样的内容 即使是复制粘贴也难免会多个或者少个空格,然后消息被删了还一直发同样的内容。
此外,还有一些群,内容的聊天内容都是广告,我还很奇怪,大家都在发广告,正常用户不都跑了嘛?
此时,我才意识到,发消息的都是机器人。
所以我加了个优化,计算消息内容的 hash 值,保存到数据库,并为这个字段建立索引。
后面检测消息的时候,先根据 hash 值查询,检查是否存在已有的消息,如果消息已经存在且已经被标记成广告或者正常消息,那么就无需再使用模型检测,可以直接返回之前的检测结果。
这样既提高了准确度,也优化了性能,也减少了人工干预的成本。
同一个用户如果在同一个群发了三条广告,那么就会自动被封禁掉,也就是相同的广告只要发三条,就会马上被自动封禁掉。
4.3 自动收集数据
使用机器学习算法来实现一个类似的垃圾广告过滤器并不难,困难的持续收集高质量的训练数据,训练数据是非常宝贵的,毕竟数据才是核心资产。
而对于我这个产品来说,最难的是冷启动时的训练数据问题:
因为没有训练数据,模型就不准确,模型不好用就不会有人使用,自然也无法通过用户来收集垃圾广告数据,就无法良性循环, 存在一个鸡生蛋,还是蛋生鸡的问题。
所以冷启动时,我是手动加了非常多的 Telegram大群,然后人工在里面收集垃圾广告.
但是这个效率实在是太低了,我收集了快一周才只有几百条数据, 一个是我无法一直盯着各个群,另外是这种20w的大群,一般都会有几个管理员,会手工删除广告,一会没有看垃圾广告数据就会被删掉了。
这样手工收集数据实在在太痛苦了,我就在想有没有什么办法自动收集数据呢?
我本来想的是直接把我的机器人拉到这些大群里面,即使没有管理员权限无法删除消息,也可以收集数据嘛,后面才意识到 Telegram 有个规定,只有群管理员才有权限加机器人,因为我不是管理员,所以自动没有权限添加机器人。
但是 telegram 的客户端是开源的,他们提供了 tdlib 1这个跨平台的 C++ 库便于社区构建第三方的 Telegram 客户端,那么我自然可以使用这个库来登录我自己的账号,然后使用我的模型来过滤消息,然后把疑似广告的数据都收集起来,我再人工确认下。
(顺便说一下,tdlib 和 telegram-bot-api 2这两个库竟然都是同一个作者 Aliaksei Levin 3在维护,实在是太强了。)
我现在需要做的就是添加各种大群,然后程序就会自动监听并收集数据,我再人工批量确认下。
实现起来也不复杂, 200行代码就实现了这个监听消息,分析,并且收集的功能。
得益于这个自动化的数据收集程序,我1周不到就收集了近上万条的高质量训练数据了,效率实在高太多太多了。
懒惰真的是程序员的美德, 这个经历再次证明:自动化工具往往能成倍提升效率,这正是工程师价值的体现.
5 推广
所谓酒香也怕巷子深,没有用户使用,代码写得再好也没有意义。从产品角度,运营推广至关重要。
作为个人开发者,我没有大量粉丝关注,也没有营销预算,因此采用了传统的推广方式:撰写博客并在相关社区分享。
我撰写了两篇双语博客文章,中文版本分享至:
- V2EX: https://www.v2ex.com/t/1156542]]
- Emacs China: https://emacs-china.org/t/emacs-telegram/30043
- 微信公众号「宫孙说」:https://mp.weixin.qq.com/s/Sgq9vDqpHykwge11bwZJrw
- 项目被收录到阮一峰的科技爱好者周刊(第 364 期)
英文版本发布至:
- Reddit: Built my first Rails project: A Telegram spam blocker bot 4- 获得一些讨论
- HackerNews:https://news.ycombinator.com/item?id=45105908
- Twitter: https://x.com/foobar_ramsay/status/1967277792267247916
虽然推广效果有限,但这些努力为项目带来了最初的用户关注。
6 成果与数据
上线半个月,截止到目前为止, 已经有超过80个群使用过这个机器人,用户数已经比我预期要多了:
指标 | 数值 |
---|---|
GitHub Stars | 106 |
使用群组数 | 83 |
训练数据量 | 10543 |
最开心的是看到我自己的程序在这些群成功拦截垃圾广告,就很有成就感,证明我做的东西真的能用户解决问题。
7 结语
这半个月的运营让我深刻体会到:产品不是代码写完就结束,而是从用户反馈中不断迭代的开始。
产品是需要持续运营的,而写代码只是产品生命周期的其中一个环节,甚至不是最耗费时间的环节。
下一步,我计划进一步优化模型准确率,并探索多语言支持,也欢迎关注我的频道或提交Issue一起讨论。
- 开源地址:https://github.com/ramsayleung/bayes_spam_sniper
- 立即体验:https://t.me/BayesSpamSniperBot
- 我的频道(菠萝油与天光墟): https://t.me/pipeapplebun
