1. 从手动刷榜到自动推送这个定时任务到底解决了什么每天早上到工位第一件事就是打开十几个信息源挨个翻一遍有没有新上线的 AI 产品这件事我坚持了大概两个月就扛不住了。不是信息量太大而是重复动作太消耗注意力——同一个动作每天做一遍做的时候还不敢走马观花生怕漏掉某个刚冒头的小工具。后来我干脆把这件事交给一个定时任务每天固定时间自动去搜索、抓取、整理然后把结果推送到我常用的接收端。这就是我给 Hermes 安排每日自动任务的核心动机。Hermes 在这里扮演的角色是一个可以承载定时调度和任务编排的执行框架。它本身不负责搜索这个动作而是负责什么时候执行、执行什么、执行完把结果送到哪里这条链路。你可以把它理解成一个闹钟加一个流水线调度员闹钟负责准点叫醒调度员负责把几个工序按顺序串起来。真正干活的是挂在它下面的搜索模块、解析模块和推送模块。这个任务适合谁如果你也在做产品调研、竞品跟踪、行业信息收集或者单纯想每天花两分钟了解 AI 圈又冒出了什么新东西这套思路都能直接复用。它不要求你会写复杂的爬虫也不要求你维护服务器核心是把定时触发 数据获取 结果分发这三件事拆清楚然后逐个落地。下面我会把整个搭建过程、每个环节的取舍理由、以及我踩过的坑完整讲一遍。需要先说明一点本文涉及的搜索来源、推送渠道、调度配置都是基于常见实践的合理方案你可以根据自己的实际环境替换成对应的工具。重点不在具体用了哪个服务而在于整条链路的组织方式和排错思路。2. 拆解任务链路定时、搜索、解析、推送四段式设计2.1 为什么是四段而不是一个大脚本很多人第一反应是写一个脚本从头到尾把搜索、解析、推送全塞进去然后挂个定时器。我一开始也是这么干的结果跑了三天就出问题了某天搜索接口返回慢了整个脚本卡住推送也没发出来又某天解析逻辑遇到一个格式异常的条目直接抛异常后面所有条目都没处理。一个环节出问题整条链路全废。后来我把它拆成四段触发段负责定时唤醒获取段负责拿到原始数据解析段负责把原始数据整理成结构化结果推送段负责把结果送到接收端。每段之间通过一个中间产物比如一个临时文件或一段内存数据衔接。这样做的好处是任何一段失败其他段的状态是清晰的排查的时候能立刻定位到是哪一段的问题而不是面对一个几百行的脚本从头看到尾。四段式还有一个隐性好处每一段都可以单独测试。获取段可以手动跑一次看返回什么解析段可以拿一份样例数据喂进去看输出对不对推送段可以单独发一条测试消息。这种可测试性在后期维护时价值极高因为搜索来源的页面结构会变推送渠道的接口也可能调整能单独验证某一段改动成本会低很多。2.2 触发段定时策略比你想的更讲究触发段看起来最简单设个时间就行但实际有几个细节值得说。第一是执行频率。我最初设的是每小时一次结果发现大部分时段根本没有新内容白白消耗请求次数还容易触发来源方的频率限制。后来改成每天两次早上一次覆盖前一天的更新傍晚一次覆盖当天的更新信息密度刚好也不会给来源方造成压力。第二是执行时刻的选择。如果你推送的目的是让人早上看到那触发时间要留出足够的处理时间。我实测下来从触发到推送完成整个链路大概需要一到三分钟取决于搜索返回速度和条目数量。所以如果希望八点半收到推送触发时间设在八点二十左右比较稳妥留出缓冲。第三是失败重试。定时任务最怕的是到点了但没执行成功而且没人知道。我的做法是给触发段加一个简单的重试逻辑如果本次执行在获取段就失败了隔十分钟再试一次最多重试两次。同时无论成功失败都在日志里留一条记录方便事后核对。这里要注意重试不能无限制否则遇到来源方持续不可用的情况会陷入反复请求的死循环。2.3 获取段与解析段原始数据到结构化结果的转换获取段的核心是从哪里拿数据。常见的选择有几类公开的搜索接口、页面的结构化数据、以及各类信息聚合源。我倾向于优先选择返回结构相对稳定的来源因为解析成本低。如果只能用页面抓取那就要接受页面结构可能变化的事实解析逻辑要写得足够宽容——比如某个字段缺失时不要直接报错而是跳过或填默认值。解析段是整个链路里最需要花心思的地方。原始数据往往是杂乱的标题里带各种前缀后缀、描述字段长度不一、发布时间格式五花八门。我的处理原则是先归一化再筛选。归一化包括统一时间格式、去掉标题里的冗余符号、把描述截断到合理长度。筛选则是根据关键词或来源白名单把明显不相关的内容过滤掉。这一步做得好推送出来的结果质量会高很多否则每天收到一堆噪音用不了几天你就不想看了。这里有个经验解析逻辑里一定要留一个原始数据快照。也就是说在解析之前把获取到的原始内容原样存一份。这样当解析结果看起来不对时你可以回头对比原始数据判断是获取阶段拿错了还是解析阶段处理错了。我因为这个快照至少省下了两次从头排查的时间。2.4 推送段结果送到哪里最顺手推送段的选择取决于你平时在哪里看信息。常见的有几类即时通讯工具的消息、邮件、以及各类笔记或待办应用。我个人的偏好是推送到一个我每天必看的地方这样不需要额外养成查看习惯。如果你推到一个很少打开的应用里那这个任务的价值会大打折扣。推送内容的格式也值得设计。我试过纯文本、Markdown 列表、以及带链接的卡片式消息。实测下来带标题和一句话摘要的列表最实用扫一眼就知道有哪些新东西感兴趣的再点链接深入看。纯文本太挤卡片式又太重列表是平衡点。另外推送里最好带上本次的条目数量和执行时间这样你一眼就能判断这次任务是否正常完成——如果某天数量突然变成零可能是来源出了问题而不是真的没有新内容。3. 环境准备与调度配置让任务稳定跑起来的前提3.1 运行环境的选择与依赖管理这套任务对运行环境的要求其实不高一台能持续运行的机器加上基础的运行时就够了。我用的是最朴素的方式一个轻量运行环境加上几个必要的依赖库。依赖管理上我强烈建议用虚拟环境或容器隔离不要直接装在系统全局环境里。原因很简单搜索和推送相关的库版本更新频繁全局安装容易和其他项目冲突出了问题也很难回退。依赖清单里通常包括负责发起网络请求的库、负责解析结构化数据的库、以及负责发送推送消息的库。如果你用的是现成的调度框架还要加上框架本身的依赖。安装的时候建议锁定版本号不要用最新版因为最新版可能引入不兼容的改动。我吃过一次亏某个解析库升级后改了默认行为导致原本正常的解析逻辑突然返回空结果排查了半天才发现是版本问题。3.2 调度配置的关键参数调度配置里有几个参数必须想清楚否则任务要么跑得太频繁要么跑得太稀疏。参数建议值说明执行频率每天 1 到 2 次频率过高会触发来源方限制也增加噪音单次超时60 到 120 秒超过这个时间说明来源异常应中断并记录失败重试次数最多 2 次避免持续失败时反复请求重试间隔10 分钟给来源方恢复留出时间日志保留至少 7 天方便回溯一周内的执行情况这些数值不是固定的你可以根据自己的来源稳定性和信息更新速度调整。但核心原则是宁可少跑几次也不要因为跑得太频繁而被来源方限制。一旦被限制恢复起来很麻烦而且会影响后续所有执行。3.3 配置与代码分离这一点是我踩坑之后才重视的。最初我把搜索关键词、推送地址、执行时间都硬编码在脚本里结果每次调整都要改代码、重新部署。后来我把这些可变的部分抽到一个配置文件里代码只负责读取配置并执行。这样调整关键词或推送地址时只需要改配置文件不用碰代码逻辑。配置文件里通常包含搜索关键词列表、来源地址、推送目标、执行时间、以及各类超时和重试参数。建议给配置文件加一个简单的校验比如必填项缺失时启动就报错而不是跑到一半才发现某个参数是空的。这种启动即校验的习惯能帮你省下很多运行时的困惑。4. 搜索与筛选逻辑怎么让结果既全又不吵4.1 关键词的设计宽窄结合搜索关键词直接决定了你能拿到什么。我的经验是宽窄结合用几个宽泛的词保证覆盖面再用几个精确的词保证相关性。比如宽泛的词可以是AI 工具智能应用这类精确的词可以是具体的技术方向或产品类型。如果只用宽泛词结果里会混入大量不相关的内容如果只用精确词又可能漏掉一些新出现的、还没被归类的产品。关键词的数量也要控制。我一开始列了二十多个词结果每次搜索返回的条目太多解析和筛选压力大推送出来也看不过来。后来精简到六到八个核心词覆盖面反而更聚焦。关键词不是越多越好而是要覆盖你真正关心的几个方向。4.2 去重与排序避免重复推送去重是必须做的。同一个产品可能在多个来源出现或者连续几天都在搜索结果里。如果不做去重你会反复收到同一条信息很快就会对这个任务失去信任。我的做法是维护一个已推送记录的列表每次解析完成后把新条目和记录比对只推送没出现过的。记录保留最近一段时间的即可太老的可以清理掉。排序则决定了推送的阅读顺序。我一般按发布时间倒序最新的排前面。如果来源没有可靠的发布时间就按获取顺序排。排序看起来是小事但直接影响阅读体验——把最新的放前面你扫一眼就能知道今天有什么新东西。4.3 筛选规则的边界处理筛选规则要写得有边界感。什么叫边界感就是明确知道哪些情况该过滤哪些情况该保留。比如一个条目标题里包含关键词但描述明显是广告或无关内容这种应该过滤。但如果一个条目信息不完整只有标题没有描述我倾向于保留因为信息不完整不代表不相关可能只是来源本身就没提供描述。这里有个容易犯的错误筛选规则写得太严格导致漏掉真正有价值的内容。我建议初期把规则放宽一点观察几天推送结果看看有没有误杀再逐步收紧。反过来如果一开始就收得很紧你可能根本不知道漏了什么。5. 推送结果的设计让人愿意每天点开看5.1 消息结构标题、摘要、链接三件套推送消息的结构我反复调整过好几版最后稳定在标题 一句话摘要 链接这个组合。标题让人快速判断是否感兴趣摘要提供一点上下文链接供深入查看。摘要不要写太长一到两句话足够写长了反而没人看。链接要确保可点击如果推送渠道不支持富文本就把链接单独放一行。消息开头我会加一个简短的头部说明这是哪一天的结果、共多少条。这样即使某天只有一条你也能确认任务正常执行了。头部信息不需要花哨一行字就够。5.2 推送时机与阅读习惯的匹配推送时机要和你的阅读习惯匹配。如果你习惯早上到工位先看一遍信息那就把推送时间设在到工位之前。如果你习惯午休时看那就设在午休前。这个没有标准答案关键是让推送出现在你本来就会看信息的时间点而不是需要你额外想起来去看。我试过设在凌晨结果早上打开时消息已经被其他通知淹没了找起来费劲。后来改到早上八点多正好是我坐下来准备开始工作的时候打开就能看到体验好很多。5.3 异常情况的提示推送段还要考虑异常提示。如果某次执行失败了你是希望收到一条执行失败的通知还是希望什么都不收到我的选择是失败时也推送一条简短提示。因为如果失败时静默你可能以为今天没有新内容实际上是任务挂了。一条本次执行失败原因获取超时的提示能让你立刻知道需要去看一眼日志。但要注意失败提示不要刷屏。如果连续失败不要每次都推可以合并成一条或者设置一个失败次数阈值超过才提醒。否则任务出问题时你会被自己的通知轰炸。6. 实测中的坑与排查链路6.1 搜索返回为空从来源到解析逐段排查有一次连续两天推送都是零条我第一反应是这两天真的没有新内容吗但还是去查了日志。排查链路是这样的先看获取段的日志确认请求是否发出、返回状态是什么再看解析段的日志确认原始数据有多少条、解析后剩多少条最后看筛选段的日志确认过滤掉了多少、过滤原因是什么。那次的问题是来源页面结构变了原本的解析规则匹配不到内容导致解析后为零。原始数据其实是有的只是解析逻辑失效了。如果我没有保留原始数据快照可能就要花更多时间才能定位到这一点。所以再强调一次原始数据快照是排查的命根子。6.2 推送失败接口限制与内容长度推送失败遇到过两次。一次是推送内容太长超过了渠道的单条消息长度限制导致发送被拒。解决办法是把长内容拆成多条或者只推送摘要加链接详细内容放在一个可访问的页面里。另一次是推送频率触发了渠道的限制短时间内发太多条被临时限制。解决办法是控制单次推送的条目数量比如超过二十条就只推前二十条其余的留到下次或提供一个完整列表的入口。这两次经历让我养成了一个习惯推送段也要有日志记录每次发送的结果。不要以为推送是最后一步就不重要它失败了你前面的工作全白费。6.3 定时任务没触发时区与调度器状态还有一次是任务根本没触发。查下来是两个原因叠加一是调度器所在的运行环境时区和我预期的不一致我设的早上八点实际是另一个时间二是调度器进程因为某次异常退出了没有自动重启。时区问题通过显式指定时区解决调度器状态则通过加一个守护机制解决——定期检查调度器是否在运行不在就拉起来。这个坑的教训是定时任务本身也需要被监控。你不能假设它永远正常运行要有机制发现它没运行。最简单的做法是让任务每次执行后写一条心跳记录如果超过预期时间没有心跳就说明有问题。7. 让这套任务长期跑下去的维护心得7.1 定期检查解析规则的有效性搜索来源的页面结构会变解析规则不可能一劳永逸。我的做法是每周花几分钟看一眼最近的推送结果如果发现某几天条目明显偏少或格式异常就去检查解析规则。这种定期检查不需要很频繁但要有。完全不管的话可能某天开始任务就一直在推空结果而你要过很久才发现。检查的时候重点看两件事一是条目数量是否稳定二是条目内容是否相关。数量骤降通常意味着解析或获取出了问题内容跑偏通常意味着筛选规则需要调整。7.2 关键词和来源的迭代关键词和来源不是设一次就不动的。随着你关注的方向变化关键词也要跟着调整。我大概每个月会回顾一次关键词列表把不再关心的去掉把新关注的方向加进去。来源也是如果某个来源长期质量不高就把它从列表里移除换一个更稳定的。迭代的时候建议一次只改一个变量改完观察几天再改下一个。如果一次改太多出了问题很难判断是哪个改动导致的。7.3 日志的阅读姿势日志不是写完就扔在那里的要会看。我的习惯是每周扫一遍日志重点看三类信息执行失败的记录、解析后条目数为零的记录、以及推送失败的记录。这三类信息能覆盖大部分异常情况。日志格式上我建议每条记录都带上时间戳和阶段标识这样筛选和定位都快。如果日志量不大直接看就行如果量大可以写个简单的统计脚本每天汇总一下各阶段的成功失败次数。这个统计脚本本身也可以挂在这个定时任务里作为任务的一部分执行。7.4 关于自动化的一个提醒最后说一点体会。自动化任务的价值在于把你从重复动作里解放出来但它不是设完就不管了。它更像是一个需要偶尔照看的工具你要给它做定期维护、根据反馈调整参数、在它出问题时能快速定位。我见过不少人设了自动化任务之后就再也不看结果任务早就失效了自己还在等推送。所以把它当成一个需要维护的小系统而不是一个一次性的脚本这样它才能长期稳定地为你服务。这套任务我跑了几个月中间修修补补好几次现在基本稳定在每天早上推送一次、偶尔傍晚补一次的状态。它没有多复杂但确实帮我省下了每天手动翻信息的时间也让我对 AI 产品的动态保持了一个稳定的感知。如果你也在做类似的信息收集不妨按这个思路搭一套先从最简单的两段式开始跑通了再逐步加上解析和筛选。