1. 为什么我要折腾一个自动推送的 AI 日报
每天早上打开微信,工作群、家庭群、订阅号消息混在一起,想找一条真正有价值的信息得翻半天。我做技术内容这行有个习惯,每天必须扫一遍行业动态,不然跟客户聊天的时候容易接不上话。但手动去各个平台翻,少说二十分钟,多则一个小时,时间全耗在“找”上了。
后来我琢磨了一件事:能不能让 AI 帮我把这件事干了?我只需要告诉它“我要什么方向的内容”,它自己去抓、去筛、去总结,最后把成品送到我每天必看的地方——微信。这个想法落地之后,就有了今天要聊的这套方案:用 WorkBuddy 做任务调度,接上 DeepSeek 做内容生成,每天上午十点半自动把一份 AI 日报推到微信里。
这套东西解决的核心问题就一个:把“人找信息”变成“信息找人”。适合谁参考?如果你每天需要固定获取某个领域的动态,又不想被各种 App 的推送轰炸,想自己掌控信息入口,那这套思路可以直接抄。哪怕你不写代码,只要能把几个工具串起来,也能跑通。
我先把整体链路说清楚,免得后面看晕。整条流水线分四段:定时触发 → 数据采集 → AI 加工 → 推送到微信。WorkBuddy 负责第一段和最后一段的调度,DeepSeek 负责中间的内容理解与生成,微信这边我用的是小程序订阅消息加服务号模板消息的组合。下面挨个拆。
2. 整体架构设计与工具选型思路
2.1 为什么是 WorkBuddy 而不是系统自带的定时任务
很多人第一反应是用 Linux 的 crontab 或者 Windows 的任务计划程序。我一开始也是这么干的,但跑了不到一周就放弃了。原因有三个。
第一,crontab 只管“什么时候跑”,不管“跑失败了怎么办”。有次采集源改版,脚本报错退出,我第二天才发现日报没发。WorkBuddy 自带任务重试和失败告警,任务挂了会记录状态,还能配置重试次数,这个在长期运行里太重要了。
第二,WorkBuddy 的任务编排是可视化的。我可以把“采集”“清洗”“生成”“推送”拆成四个独立步骤,每个步骤单独看日志、单独重跑。crontab 里这些全塞在一个脚本里,出问题只能从头 debug。
第三,它支持任务依赖和条件分支。比如采集到的内容少于 5 条,就跳过生成直接发一条“今日无更新”的提示。这种逻辑用 shell 写也能实现,但维护成本高,WorkBuddy 里拖几个节点就搞定了。
提示:WorkBuddy 有国际版和国内版,功能基本一致,选哪个看你常用的服务在哪个区域。我用的国内版,接微信生态更顺。
2.2 DeepSeek 在链路里扮演什么角色
DeepSeek 在这套方案里干的是“编辑”的活。原始采集回来的内容是一堆标题、摘要、链接,杂乱无章。DeepSeek 要做三件事:去重、分类、摘要。
去重好理解,同一个新闻可能好几个源都在发。分类是按我预设的标签(比如“大模型动态”“工具更新”“行业政策”)把内容归堆。摘要是把每条内容压缩到 50 字以内,同时保留关键信息。
为什么选 DeepSeek 而不是别的模型?两个原因。一是它的 API 调用成本低,日报这种每天跑一次、每次处理几十条内容的场景,一个月下来费用几乎可以忽略。二是它对中文内容的摘要质量稳定,我对比过几个模型,DeepSeek 在“把一段技术新闻压缩成一句话”这个任务上,很少出现漏掉关键信息或者胡编的情况。
调用方式就是标准的 HTTP 接口,传 prompt 进去,拿返回结果。后面实操部分我会把具体的请求结构写出来。
2.3 微信侧为什么选小程序订阅消息加服务号模板消息
推送渠道我试过三种:邮件、钉钉机器人、微信。最后留在微信,因为我每天看微信的次数远多于看邮件和钉钉。日报这东西,送到你本来就会打开的地方,才有意义。
微信这边具体用哪种推送形式,取决于你的账号类型。我最终用的是服务号模板消息,因为它支持较长的内容,而且可以在消息里放跳转链接,点进去看完整日报。小程序订阅消息适合更轻量的提醒,但每次推送前需要用户授权,长期自动推送的场景下体验不如服务号。
如果你没有服务号,也可以用企业微信机器人,把日报推到群里,效果类似。我有个朋友就是这么干的,他们团队每天早上在群里看 AI 日报,已经成了固定环节。
2.4 数据采集源怎么定
采集源我分了四类:行业媒体 RSS、技术社区热榜、官方公告页、社交平台关键词。RSS 是最稳定的,格式统一,解析成本低。技术社区热榜需要抓页面,稍微麻烦点,但信息密度高。官方公告页更新频率低,但一旦有更新就是大事,必须覆盖。社交平台关键词用来捕捉突发讨论。
这里有个经验:采集源不要贪多。我一开始接了二十多个源,结果每天采集回来上百条,DeepSeek 处理时间变长,日报也变得又臭又长,根本看不完。后来砍到八个核心源,每天稳定产出 15 到 25 条,摘要后控制在 10 条以内,阅读体验好很多。
3. 核心环节拆解与实操要点
3.1 WorkBuddy 任务配置:从定时触发到流程编排
WorkBuddy 里新建一个任务,触发方式选“定时触发”,cron 表达式写30 10 * * *,意思是每天上午十点半执行。这里注意时区设置,WorkBuddy 默认可能是 UTC,要改成你所在的时区,不然会差几个小时。
任务建好之后,进入流程编排界面。我把它拆成了五个节点:
- 采集节点:并行请求八个数据源,每个源单独设超时时间,避免一个源卡住拖垮整个任务。
- 清洗节点:统一数据格式,去掉 HTML 标签,提取标题、正文、发布时间、来源链接。
- AI 处理节点:调用 DeepSeek API,做去重、分类、摘要。
- 格式化节点:把处理好的内容拼成微信模板消息要求的 JSON 结构。
- 推送节点:调用微信接口发送消息。
每个节点都可以单独配置重试策略。我的设置是:采集节点重试 2 次,间隔 30 秒;AI 处理节点重试 1 次,间隔 10 秒;推送节点重试 3 次,间隔 60 秒。推送节点重试次数最多,因为微信接口偶尔会抽风,多试几次基本能成功。
注意:WorkBuddy 的并行节点有并发上限,具体数值看你的套餐。如果采集源超过十个,建议分批执行,别一次性全放出去。
3.2 DeepSeek API 调用:prompt 设计与参数调优
调用 DeepSeek 的接口,核心是 prompt 怎么写。我试过好几种写法,最后稳定下来的结构是这样的:
prompt = f""" 你是一个技术内容编辑。请对以下内容进行处理: 1. 去除重复内容(标题相似度超过 80% 视为重复) 2. 按以下分类打标签:大模型动态、开发工具、行业政策、其他 3. 每条内容生成不超过 50 字的摘要,保留关键信息 4. 按分类输出,每个分类下最多 3 条 原始内容: {raw_content} 输出格式要求: - 使用 JSON 格式 - 每个分类作为一个数组 - 每条内容包含:标题、摘要、来源、链接 """参数方面,temperature设成 0.3,让输出稳定一些,不要每次跑出来风格差异太大。max_tokens根据内容量调整,我一般设 2000,够用。top_p保持默认的 1.0 就行。
这里有个坑:DeepSeek 返回的 JSON 偶尔会带 markdown 代码块标记,比如 ```json 开头。解析之前要先做一次字符串清洗,把反引号去掉。我一开始没处理,程序直接报错,排查了半天才发现是这个问题。
另一个经验是分批处理。如果采集回来超过 30 条内容,一次性丢给 DeepSeek 容易超 token 限制,而且处理质量会下降。我的做法是每 15 条一批,分多次调用,最后合并结果。
3.3 微信推送接口对接:模板消息与订阅消息的差异
服务号模板消息的接口调用需要三个东西:access_token、template_id、openid。access_token有效期 7200 秒,需要缓存起来,不能每次推送都重新获取,否则会被限流。我的做法是在 WorkBuddy 里加一个前置节点,专门负责刷新和缓存 token。
模板消息的内容结构是固定的,你需要在服务号后台先建好模板,定义好字段。比如我建的模板有这些字段:title(日报标题)、date(日期)、summary(摘要)、link(详情链接)。推送的时候按格式填进去就行。
小程序订阅消息的流程不太一样,需要用户先授权。如果你要做成“用户主动订阅”的模式,可以在小程序里放一个按钮,用户点击后调用requestSubscribeMessage接口。但这种方式每次推送前都要检查授权状态,用户取消授权后就推不了了。长期自动推送的场景,我还是推荐服务号模板消息。
提示:微信接口有频率限制,模板消息每天每个用户最多推 10 条。日报一天一条,完全够用。但如果你测试的时候频繁调用,可能会触发限流,等几分钟再试。
3.4 内容格式化:把 AI 输出变成微信能认的结构
DeepSeek 返回的是 JSON,微信要的是特定格式的 JSON,中间需要一个转换层。这个转换层我写在 WorkBuddy 的格式化节点里,用 JavaScript 处理。
核心逻辑是:遍历 AI 输出的分类数组,把每条内容的标题和摘要拼成一段文本,然后填入微信模板的对应字段。如果内容太长,微信模板消息有长度限制,需要截断。我的做法是摘要控制在 50 字以内,整体内容控制在 500 字以内,超出的部分放到详情页里。
详情页我用的是一个简单的静态页面,部署在对象存储上。日报推送出去的时候带上这个页面的链接,点进去看完整版。页面内容也是 WorkBuddy 在推送前生成的,把完整 JSON 渲染成 HTML 就行。
4. 完整实操流程与关键步骤记录
4.1 环境准备与账号申请
先把需要的东西列出来:
| 项目 | 用途 | 获取方式 |
|---|---|---|
| WorkBuddy 账号 | 任务调度与流程编排 | 官网注册 |
| DeepSeek API Key | 内容处理 | 开发者平台申请 |
| 微信服务号 | 消息推送 | 微信公众平台注册 |
| 对象存储 | 存放详情页 | 各云厂商均有 |
| 域名(可选) | 详情页访问 | 域名注册商 |
服务号注册需要企业资质,个人只能注册订阅号。订阅号没有模板消息接口,这是个硬限制。如果你是个人的话,替代方案是用企业微信机器人,或者用邮件推送。我帮朋友搭过企业微信机器人的版本,效果也不错,就是阅读体验不如服务号。
DeepSeek 的 API Key 申请很快,注册开发者账号后就能拿到。注意保管好 Key,不要直接写在代码里,WorkBuddy 支持环境变量,把 Key 存在环境变量里更安全。
4.2 WorkBuddy 任务创建与节点配置
登录 WorkBuddy 后,点“新建任务”,填写任务名称和描述。触发方式选“定时触发”,cron 表达式填30 10 * * *。时区选Asia/Shanghai。
然后进入流程编排。第一个节点选“HTTP 请求”,配置采集源的 URL。每个采集源单独一个节点,并行执行。请求方法一般是 GET,超时设 15 秒。返回结果存到变量里,供后续节点使用。
第二个节点选“脚本处理”,语言选 JavaScript。在这里做数据清洗:去掉 HTML 标签、统一时间格式、提取关键字段。代码大概长这样:
const items = input.items.map(item => ({ title: item.title.replace(/<[^>]+>/g, ''), content: item.content.replace(/<[^>]+>/g, '').slice(0, 500), pubDate: new Date(item.pubDate).toISOString(), source: item.source, link: item.link })); return { items };第三个节点选“HTTP 请求”,调用 DeepSeek API。请求方法 POST,URL 填https://api.deepseek.com/v1/chat/completions,Headers 里加Authorization: Bearer ${DEEPSEEK_API_KEY},Body 里放前面说的 prompt 结构。
第四个节点选“脚本处理”,把 DeepSeek 返回的内容转成微信模板消息格式。第五个节点选“HTTP 请求”,调用微信接口推送。
每个节点配置完后,点“测试运行”验证一下。WorkBuddy 会显示每个节点的输入输出,方便排查问题。
4.3 DeepSeek 调用参数与 prompt 模板
完整的请求体结构:
{ "model": "deepseek-chat", "messages": [ { "role": "system", "content": "你是一个技术内容编辑,擅长对信息进行去重、分类和摘要。" }, { "role": "user", "content": "请处理以下内容:\n\n{raw_content}\n\n要求:\n1. 去重\n2. 分类\n3. 摘要不超过50字\n4. 输出JSON格式" } ], "temperature": 0.3, "max_tokens": 2000, "top_p": 1.0 }model字段填deepseek-chat就行,这是通用对话模型。如果你需要更强的推理能力,可以用deepseek-reasoner,但处理摘要这种任务,deepseek-chat足够了,而且速度更快、成本更低。
prompt 里的{raw_content}是变量,WorkBuddy 会自动替换成上一个节点的输出。注意转义问题,如果内容里有双引号,需要先转义,不然 JSON 会解析失败。
4.4 微信模板消息发送与 access_token 管理
获取access_token的接口:
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET返回结果里access_token有效期 7200 秒。我的做法是在 WorkBuddy 里加一个“缓存节点”,把 token 和过期时间存起来。每次推送前先检查缓存,没过期就直接用,过期了再重新获取。
发送模板消息的接口:
POST https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN请求体:
{ "touser": "OPENID", "template_id": "TEMPLATE_ID", "url": "https://your-domain.com/daily/2024-01-01", "data": { "title": { "value": "AI日报 2024-01-01" }, "summary": { "value": "今日要点:..." }, "date": { "value": "2024-01-01 10:30" } } }touser填你的 openid。获取 openid 的方式是用户在服务号里发一条消息,你从回调里拿。或者用网页授权的方式获取。我图省事,直接在自己的服务号里发条消息,从后台日志里找到 openid,填进去就行。
4.5 详情页生成与链接跳转
详情页我用的是静态 HTML,WorkBuddy 在推送前生成好,上传到对象存储。页面内容就是把完整 JSON 渲染成列表,每条内容带标题、摘要、来源链接。
生成 HTML 的代码在格式化节点里:
const html = ` <!DOCTYPE html> <html> <head><meta charset="utf-8"><title>AI日报</title></head> <body> <h1>AI日报 ${date}</h1> ${categories.map(cat => ` <h2>${cat.name}</h2> <ul> ${cat.items.map(item => ` <li> <a href="${item.link}">${item.title}</a> <p>${item.summary}</p> </li> `).join('')} </ul> `).join('')} </body> </html> `;生成后调用对象存储的上传接口,把文件传上去,拿到访问链接。这个链接填到微信模板消息的url字段里。
5. 常见问题与排查技巧实录
5.1 任务执行失败排查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 任务没触发 | cron 表达式错误 | 检查时区和表达式 | 改成30 10 * * *,时区设上海 |
| 采集节点超时 | 源站响应慢 | 看节点日志 | 增加超时时间或换源 |
| DeepSeek 返回空 | token 超限 | 检查输入长度 | 分批处理,每批 15 条 |
| JSON 解析失败 | 返回带 markdown 标记 | 打印原始返回 | 清洗反引号后再解析 |
| 微信推送失败 | token 过期 | 检查 token 缓存 | 加缓存节点,自动刷新 |
| 推送成功但没收到 | openid 错误 | 核对 openid | 重新获取并更新 |
5.2 内容质量不稳定的调整方法
DeepSeek 偶尔会把不相关的内容归到某个分类里,或者摘要写得过于笼统。我的调整方法是在 prompt 里加示例。比如在分类要求后面加一句“例如:’某模型发布新版本‘归入’大模型动态‘,’某工具更新功能‘归入’开发工具‘”。加了示例之后,分类准确率明显提升。
另一个方法是后处理校验。在格式化节点里加一段逻辑,检查每个分类下的内容数量,如果某个分类超过 5 条,只取前 3 条。这样即使 AI 分类有偏差,最终输出也不会太离谱。
5.3 推送时间与频率的优化经验
十点半这个时间是我试出来的。太早,采集源还没更新完;太晚,上午的工作节奏已经开始了,没时间看。十点半刚好是上午工作告一段落的时候,花五分钟扫一遍日报,然后继续干活。
频率上,一天一次足够了。我试过一天两次,结果第二次的内容和第一次大量重复,反而增加了阅读负担。如果你确实需要更高频率,建议改成“有重大更新时才推”,在 WorkBuddy 里加个条件判断,内容少于 3 条就不推。
5.4 成本控制与资源占用
整套方案的成本主要在三块:WorkBuddy 订阅费、DeepSeek API 调用费、对象存储费用。WorkBuddy 我用的基础版,够跑这个任务。DeepSeek 每天处理 20 条左右内容,一个月费用不到一杯咖啡的钱。对象存储几乎可以忽略,详情页文件很小。
资源占用方面,WorkBuddy 的任务是云端执行的,不占本地资源。唯一需要注意的是 DeepSeek 的并发限制,免费版可能有 QPS 限制,如果同时跑多个任务可能会排队。我的做法是把采集和 AI 处理错开,采集完成后等 5 秒再调 AI 接口。
6. 几个我踩过的坑和后来想明白的事
第一个坑是采集源没有做去重。同一个新闻,A 源和 B 源都发了,标题略有不同,DeepSeek 有时候能识别出来,有时候识别不出来。后来我在清洗节点加了一步:先按标题的编辑距离做一次粗筛,相似度超过 80% 的直接丢掉,再交给 DeepSeek 处理。这样既减轻了 AI 的负担,也提高了去重准确率。
第二个坑是微信模板消息的内容长度限制。我一开始把完整摘要都塞进去,结果推送失败,报错说内容超长。后来改成只推标题和一句话摘要,详细内容放详情页,问题解决。微信模板消息的字段值有长度限制,具体数值看模板定义,一般控制在 200 字以内比较安全。
第三个坑是access_token 缓存没有加锁。有次任务重试的时候,两个节点同时去刷新 token,导致其中一个失效。后来在缓存节点加了简单的锁机制,同一时间只允许一个请求去刷新,问题就没再出现过。
这套东西跑了大半年,中间调整过几次,现在基本稳定了。每天早上十点半,微信准时弹出日报,我花几分钟扫一遍,该记的记,该转的转。省下来的时间,够我多写半篇文章。如果你也在做类似的信息聚合,希望这些经验能帮你少走点弯路。