产品上线半月的故障、挑战与优化之路 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
samray
V2EX    分享创造

产品上线半月的故障、挑战与优化之路

  •  
  •   samray
    ramsayleung 26 天前 2149 次点击

    博客原文: 基于贝叶斯算法的 Telegram 广告拦截机器人(二):上线半月的故障、挑战与优化之路


    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 分钟后自动删除。

    倾听用户的声音是非常重要的,他们可能就会从他们的角度提出非常好的建议。

    但是不要盲目听从用户的建议,比如也有用户建议:

    我觉得还应该有以下功能.

    1. 恢复消息, 恢复用户. (让管理员恢复误删的消息和用户)
    2. 主动投喂正常消息. (让管理员主动投喂一些消息. 比如, 群里面昨天 的消息, 随便选一些正常的, 投喂给机器人)

    恢复消息这个功能没有太大必要,并且也不实用,因为恢复消息这个功能本身就很微妙,是直接恢复被删除的消息呢,还是重新发一条新消息?

    如:

    • 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 推广

    所谓酒香也怕巷子深,没有用户使用,代码写得再好也没有意义。从产品角度,运营推广至关重要。

    作为个人开发者,我没有大量粉丝关注,也没有营销预算,因此采用了传统的推广方式:撰写博客并在相关社区分享。

    我撰写了两篇双语博客文章,中文版本分享至:

    英文版本发布至:

    虽然推广效果有限,但这些努力为项目带来了最初的用户关注。

    6 成果与数据

    上线半个月,截止到目前为止, 已经有超过 80 个群使用过这个机器人,用户数已经比我预期要多了:

    指标 数值
    GitHub Stars 106
    使用群组数 83
    训练数据量 10543

    最开心的是看到我自己的程序在这些群成功拦截垃圾广告,就很有成就感,证明我做的东西真的能用户解决问题。

    7 结语

    这半个月的运营让我深刻体会到:产品不是代码写完就结束,而是从用户反馈中不断迭代的开始。

    产品是需要持续运营的,而写代码只是产品生命周期的其中一个环节,甚至不是最耗费时间的环节。

    下一步,我计划进一步优化模型准确率,并探索多语言支持,也欢迎关注我的频道或提交 Issue 一起讨论。

    [^1]: https://github.com/tdlib/td
    [^2]: https://github.com/tdlib/telegram-bot-api
    [^3]: https://github.com/levlam
    [^4]: https://old.reddit.com/r/rails/comments/1n6p791/built_my_first_rails_project_a_telegram_spam/

    2 条回复    2025-09-15 08:03:37 +08:00
    BeCool
        1
    BeCool  
    PRO
       26 天前
    感谢分享
    billzhuang
        2
    billzhuang  
       26 天前 via iPhone
    感谢分享

    站在上帝视角,“宁可漏删,不可误删”,应该是此类产品的第一原则
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2671 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 13:55 PVG 21:55 LAX 06:55 JFK 09:55
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86