
1. 这套自动日报系统到底在解决什么问题每天早上到工位第一件事是打开各种信息源刷一遍行业动态、看看竞品更新、翻翻社群里的讨论等把有价值的东西筛出来半小时已经过去了。更麻烦的是这件事每天都在重复而且一旦忙起来就容易断档断个两三天再想捡起来信息差就出来了。我给自己搭的这套东西核心目标就一个每天上午十点半一份整理好的 AI 领域日报自动推送到微信里我只需要点开看不需要任何手动操作。整个链路涉及三个环节——定时触发、内容抓取与整理、消息推送。听起来简单但每个环节都有坑尤其是推送这一环微信生态的限制比想象中多。这套方案适合什么人参考如果你有基础的程序运行环境懂一点 Python 或者愿意照着步骤操作那基本可以复现。如果你完全不懂代码也可以理解整体思路把关键环节交给现成的工具去完成。我尽量把每一步的选择理由和踩过的坑都写清楚你照着抄作业就行。先说整体架构我用的是WorkBuddy 作为任务调度和执行的载体配合DeepSeek 系列模型做内容摘要和整理最后通过微信小程序的消息能力把日报推送到手机上。为什么选这个组合下面会逐个拆解。提示整套系统的核心不是某个单点技术而是“定时触发 内容处理 消息触达”这条链路的稳定性。任何一个环节断了日报就送不到所以每个环节都要有兜底方案。2. 为什么选 WorkBuddy 做调度载体2.1 WorkBuddy 在自动化链路里的角色定位WorkBuddy 本质上是一个任务编排和执行平台你可以把它理解成一个“能跑代码的闹钟”。它支持定时触发任务也支持手动触发任务里可以执行脚本、调用接口、处理数据。我把它放在整个链路的最前端负责每天上午十点半准时启动整个流程。为什么不用系统的 crontab 或者 Jenkins 这类传统调度工具几个原因。第一WorkBuddy 的任务管理界面更直观我能随时看到每个任务的执行状态、历史记录、失败原因排查问题不用去翻日志文件。第二它内置了一些常用的连接器和工具函数比如 HTTP 请求、文件读写、环境变量管理省去了不少胶水代码。第三它支持任务之间的依赖编排比如“抓取内容”完成后才触发“生成日报”这个在 crontab 里要自己写脚本判断在 WorkBuddy 里配置一下就行。当然如果你已经有成熟的 Jenkins 或者 Airflow 环境用它们也完全可以核心逻辑是一样的。我选 WorkBuddy 主要是因为它轻量不需要额外维护一套调度集群对于个人项目来说够用了。2.2 定时触发的配置细节与避坑点定时触发看起来是最简单的一环但实际配置时有几个细节要注意。时区问题。WorkBuddy 的任务调度默认可能用的是 UTC 时间如果你直接填“10:30”实际触发可能是北京时间下午六点半。我第一次配置的时候就踩了这个坑等了半天没收到日报后来发现是时区没设对。解决办法是在任务配置里明确指定时区为Asia/Shanghai或者把触发时间换算成 UTC 再填。触发频率与执行时长的关系。我设的是每天一次但你要考虑任务执行需要多长时间。如果抓取内容加处理加推送总共需要五分钟那触发时间设在 10:25 比较稳妥留出缓冲。如果设成 10:30 触发实际推送可能到 10:35 才到虽然差别不大但如果你对时间点有严格要求就要把这个执行时长算进去。失败重试机制。网络请求可能超时接口可能临时不可用所以一定要配置重试。我的做法是任务失败后自动重试两次间隔分别是 30 秒和 60 秒。如果两次都失败就发一条告警消息到我的备用通知渠道这样我知道今天日报没发出去可以手动补。# WorkBuddy 任务配置示例伪代码具体字段以实际平台为准 task: name: daily-ai-report schedule: cron: 30 10 * * * timezone: Asia/Shanghai retry: max_attempts: 3 interval: 30 steps: - name: fetch-content type: http url: https://api.example.com/ai-news - name: process-content type: script runtime: python3 - name: push-to-wechat type: http url: https://api.example.com/push注意定时任务的时区一定要确认清楚这是最容易出错的地方。建议配置完成后先手动触发一次确认整个链路能跑通再等定时触发验证。2.3 任务编排中的依赖管理WorkBuddy 支持把多个步骤串成一个任务流。我的日报生成流程分三步第一步抓取原始内容第二步调用模型做摘要和分类第三步推送到微信。这三步有严格的先后顺序第二步依赖第一步的输出第三步依赖第二步的输出。在 WorkBuddy 里我通过“步骤依赖”来配置这个顺序。每个步骤的输出会作为下一个步骤的输入如果某一步失败后续步骤不会执行整个任务标记为失败。这样做的好处是避免脏数据往下传比如抓取失败了就不要再去调模型浪费 token。另外我还加了一个“数据校验”步骤在抓取完成后检查内容是否为空、格式是否正确。如果校验不通过直接终止任务并告警不往下走。这个校验步骤帮我省了不少事有一次接口返回了空数组如果没有校验模型会基于空内容生成一份毫无意义的日报推送到微信里反而干扰我。3. 内容抓取与 AI 摘要的完整实现3.1 内容源的选取与抓取策略日报的内容质量取决于源的质量。我主要抓三类内容一是几个固定的 AI 资讯站点二是几个技术社区的讨论帖三是我关注的几个开源项目的更新动态。抓取方式上能用 RSS 的就用 RSS因为格式稳定、解析简单。没有 RSS 的站点我用 Python 的requests加BeautifulSoup做页面解析。这里要注意的是有些站点有反爬机制请求频率太高会被封 IP。我的做法是每个源之间间隔 2 到 3 秒并且设置一个合理的 User-Agent模拟正常浏览器访问。import requests from bs4 import BeautifulSoup import time def fetch_rss(url): 抓取 RSS 源返回条目列表 resp requests.get(url, timeout10) soup BeautifulSoup(resp.content, featuresxml) items soup.find_all(item) results [] for item in items[:10]: # 每个源最多取 10 条 results.append({ title: item.title.text, link: item.link.text, pub_date: item.pubDate.text if item.pubDate else }) return results def fetch_page(url, selector): 抓取普通网页按 CSS 选择器提取内容 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } resp requests.get(url, headersheaders, timeout10) soup BeautifulSoup(resp.text, html.parser) elements soup.select(selector) return [el.get_text(stripTrue) for el in elements] # 抓取多个源每个之间间隔 2 秒 sources [ {type: rss, url: https://example.com/feed}, {type: page, url: https://example.com/news, selector: .news-item} ] all_content [] for src in sources: if src[type] rss: all_content.extend(fetch_rss(src[url])) else: all_content.extend(fetch_page(src[url], src[selector])) time.sleep(2)抓取到的原始内容需要做一轮清洗去掉 HTML 标签、去掉多余空白、过滤掉广告和无关内容。这一步我用正则表达式加简单的规则过滤比如标题里包含“广告”“推广”的直接丢掉。3.2 用 DeepSeek 做摘要与分类的提示词设计内容抓回来之后下一步是让模型帮我做摘要和分类。我用的是 DeepSeek 系列的模型具体版本根据当时可用的来选。这里的关键是提示词的设计提示词写得好输出质量差很多。我的提示词结构是这样的先给模型一个角色设定然后说明任务目标再给出输出格式要求最后附上待处理的内容。具体来说PROMPT_TEMPLATE 你是一个 AI 领域的内容编辑负责把原始资讯整理成一份简洁的日报。 任务要求 1. 从以下原始内容中筛选出与 AI 技术、产品、开源项目相关的条目 2. 每条内容用一句话概括核心信息不超过 50 字 3. 按重要性排序最重要的放前面 4. 如果某条内容信息量不足或重复直接跳过 5. 输出格式为 JSON 数组每个元素包含 title 和 summary 两个字段 原始内容 {raw_content} 请直接输出 JSON不要加任何额外说明。 def summarize_with_ai(raw_content): prompt PROMPT_TEMPLATE.format(raw_contentraw_content) # 调用模型接口具体调用方式以实际 API 为准 response call_model(prompt) return parse_json(response)这个提示词我迭代了好几次。最初的版本没有要求 JSON 输出模型返回的是自然语言段落我还得再写代码去解析很麻烦。后来改成强制 JSON 输出解析就简单多了。另外“不超过 50 字”这个限制也很重要不限制的话模型会写得很啰嗦日报就失去了“快速浏览”的意义。还有一个细节我在提示词里加了“如果信息量不足或重复直接跳过”这样模型会自动做一轮去重和过滤减少我的后处理工作量。实测下来这个提示词能把 50 条原始内容压缩到 10 到 15 条精华效果比较满意。3.3 内容去重与质量过滤的实操技巧模型摘要之后我还会做一轮程序化的去重。因为不同源可能报道同一件事模型有时候会分别摘要导致日报里出现重复内容。我的去重逻辑比较简单计算每条摘要的标题相似度如果两条内容的标题相似度超过 80%就只保留一条。from difflib import SequenceMatcher def deduplicate(items, threshold0.8): 基于标题相似度去重 result [] for item in items: is_duplicate False for existing in result: ratio SequenceMatcher(None, item[title], existing[title]).ratio() if ratio threshold: is_duplicate True break if not is_duplicate: result.append(item) return result质量过滤方面我设了几条硬规则摘要字数少于 10 个字的丢掉标题里包含“点击查看”“阅读全文”这类无意义文字的丢掉链接无法访问的丢掉。这些规则看起来简单但能过滤掉不少噪音。实操心得去重阈值不要设得太低否则会把相关但不重复的内容误删。我试过 0.6 的阈值结果把“某模型发布新版本”和“某模型版本更新解读”当成重复内容删掉了其实这两条都有价值。后来调到 0.8 就比较合适。4. 微信推送环节的三种方案对比4.1 方案一微信小程序订阅消息微信小程序提供订阅消息能力用户可以主动订阅然后服务端在合适的时候推送。这个方案的优点是触达率高消息直接出现在微信聊天列表里和普通微信消息一样。缺点是用户需要手动订阅而且每次订阅只能推送一条长期使用需要用户反复订阅。如果你要做的是一个给多人使用的日报服务这个方案比较合适。但如果是个人自用每次都要手动订阅就有点麻烦。我最初试过这个方案后来因为订阅次数限制放弃了。4.2 方案二企业微信机器人 Webhook企业微信的群机器人支持通过 Webhook 推送消息配置非常简单复制一个 Webhook 地址往上面发 POST 请求就行。这个方案的优点是配置简单、没有订阅限制、支持 Markdown 格式。缺点是需要有一个企业微信账号而且消息是推送到企业微信里不是个人微信。如果你本身就在用企业微信这个方案最省事。我现在的日报就是推送到企业微信的一个只有我自己的群里每天十点半准时收到体验很稳定。import requests import json def push_to_wecom(webhook_url, content): 通过企业微信机器人推送 Markdown 消息 payload { msgtype: markdown, markdown: { content: content } } resp requests.post( webhook_url, datajson.dumps(payload), headers{Content-Type: application/json}, timeout10 ) return resp.json() # 构造日报内容 report_lines [# AI 日报, f日期2025-01-01, ] for item in daily_items: report_lines.append(f**{item[title]}**) report_lines.append(f{item[summary]}) report_lines.append() report_content \n.join(report_lines) push_to_wecom(https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY, report_content)4.3 方案三微信服务号模板消息微信服务号支持模板消息可以推送到关注了服务号的用户微信里。这个方案的优点是直接触达个人微信缺点是服务号需要认证而且模板消息的格式限制比较多不能随意排版。综合对比下来我的建议是个人自用选企业微信机器人给团队用选企业微信机器人或者小程序订阅消息给大量外部用户用才考虑服务号。下面这张表可以帮你快速决策方案配置难度触达位置格式支持适合场景小程序订阅消息中微信聊天列表受限多人服务企业微信机器人低企业微信Markdown个人/团队服务号模板消息高微信聊天列表受限大量外部用户注意企业微信机器人的 Webhook 地址包含密钥不要泄露到公开代码仓库里。我一般把它放在环境变量里代码里通过os.environ读取。5. 常见问题排查与稳定性优化5.1 日报没收到按这个顺序排查日报没收到是最常见的问题排查顺序建议从后往前查。第一步检查推送环节。手动调用一次推送接口看能不能收到消息。如果收不到说明 Webhook 地址或者消息格式有问题。常见原因是 Webhook 被禁用、消息内容超长、Markdown 格式不合法。第二步检查内容生成环节。如果推送正常但内容是空的那问题出在模型调用或者抓取环节。先看抓取有没有拿到数据再看模型有没有正常返回。模型调用失败常见原因是 API Key 过期、余额不足、请求超时。第三步检查定时触发环节。如果手动触发能跑通但定时不触发那就是调度配置的问题。重点检查时区、Cron 表达式、任务是否被禁用。我整理了一个速查表遇到问题可以直接对照现象可能原因解决办法完全没收到消息Webhook 失效重新生成 Webhook 地址收到空消息抓取失败或模型返回空检查抓取日志和模型调用日志消息内容乱码编码问题统一使用 UTF-8 编码定时不触发时区错误确认时区设置为 Asia/Shanghai消息被截断内容超长分段发送或精简内容重复收到消息重试机制导致增加幂等判断避免重复推送5.2 让系统更稳定的几个关键设置超时设置。所有网络请求都要设超时不设的话一个请求卡住整个任务就挂在那里。我的经验是抓取请求设 10 秒模型调用设 30 秒推送设 10 秒。超过就报错重试。日志记录。每个步骤都要打日志记录开始时间、结束时间、处理条数、是否成功。出问题的时候日志是唯一的线索。我把日志写到文件里保留最近 7 天方便回溯。幂等设计。如果任务重试要避免重复推送。我的做法是在推送前检查今天是否已经推送过用一个简单的标记文件或者数据库记录来实现。import os from datetime import date def should_push_today(marker_dir/tmp/daily_report): 检查今天是否已经推送过 today date.today().isoformat() marker_file os.path.join(marker_dir, f{today}.done) if os.path.exists(marker_file): return False return True def mark_pushed_today(marker_dir/tmp/daily_report): 标记今天已推送 os.makedirs(marker_dir, exist_okTrue) today date.today().isoformat() marker_file os.path.join(marker_dir, f{today}.done) with open(marker_file, w) as f: f.write(done)降级方案。如果主推送渠道失败要有备用渠道。我的配置是企业微信推送失败后自动尝试发送邮件。邮件虽然不如微信及时但至少能保证信息不丢。5.3 内容质量的持续调优经验系统跑起来之后内容质量是需要持续调的。我每周会花十分钟看一下这周的日报标记哪些内容有价值、哪些是噪音然后根据反馈调整抓取源和提示词。比如我发现某个源经常产出低质量内容就直接把它从抓取列表里去掉。又比如我发现模型有时候会把旧闻当新闻就在提示词里加了“优先选择 24 小时内的内容”这个条件。这些调整都是小步快跑每次改一点观察几天效果。还有一个技巧我会在日报末尾加一个“反馈”入口点进去可以快速标记“有用”或“没用”。虽然目前只有我自己用但这个反馈数据积累下来对后续优化很有帮助。实操心得不要追求一次做到完美。先让系统跑起来哪怕内容质量只有 60 分也比没有强。然后在使用的过程中逐步优化这样压力小效果反而更好。6. 从个人自用到团队共享的扩展思路6.1 多用户订阅的改造方案如果想把日报分享给团队成员需要做几个改造。第一推送目标从单个 Webhook 变成多个每个成员有自己的推送渠道。第二内容可能需要按角色定制比如开发关注技术更新产品关注行业动态。第三需要有一个简单的订阅管理界面让成员自己选择关注哪些板块。技术上可以把用户配置存到一个简单的数据库里每次推送时遍历用户列表根据每个人的订阅偏好生成定制内容。这个改造工作量不大但能让日报的覆盖面广很多。6.2 内容板块的横向扩展目前的日报主要是 AI 领域资讯但这个框架可以扩展到任何领域。比如你可以加一个“竞品动态”板块专门监控几个竞品的官网更新加一个“社群精选”板块抓取几个技术社群的热门讨论加一个“数据看板”板块把关键指标的变化也放进去。扩展的方式很简单在抓取环节增加新的源在提示词里增加新的分类要求在推送模板里增加新的板块。整个架构不用动只是内容变多了。6.3 后续可以尝试的优化方向有几个方向我还在尝试。一是加入语音播报把日报转成音频通勤路上可以听。二是加入交互能力比如在消息里直接回复“详情 3”就能看到第三条的完整内容。三是做历史归档把每天的日报存下来支持按关键词搜索。这些优化不是必须的但能让日报从一个“信息推送”变成一个“信息助手”。我个人的原则是先保证核心链路稳定再考虑锦上添花的功能。毕竟日报最重要的是准时、准确其他都是加分项。最后分享一个小技巧如果你也想搭这套系统建议先从最简单的版本开始——一个抓取源、一个模型调用、一个推送渠道跑通之后再逐步加东西。我见过不少人一上来就想做得很完善结果卡在某个环节就放弃了。先跑起来比什么都重要。