WorkBuddy+deepseek-v4-flash:微信AI日报自动推送实战
1. 为什么我要给 WorkBuddy 设一个“十点半闹钟”
每天早上到工位,泡好咖啡,打开电脑,第一件事不是看邮件,而是刷一遍昨天夜里到今早的行业动态、竞品更新、社区热帖。这件事听起来简单,但真正做起来非常碎:公众号、技术社区、社交平台、内部群聊、邮件列表,信息源散落在七八个地方,手动翻一遍至少二十分钟,而且很容易漏掉关键内容。更麻烦的是,有些信息有时效性,上午十点半之前如果没看到,可能就错过了一个值得跟进的线索。
我用的工具是 WorkBuddy,一个可以挂载技能、执行自动化任务的工作台。它本身支持定时触发、脚本执行和消息推送,于是我就想:能不能让它每天上午十点半,自动把过去二十四小时的关键信息汇总成一份 AI 日报,直接推送到微信里?这样我只需要在十点半看一眼微信,就能掌握当天需要关注的重点,剩下的时间全部留给深度工作。
这个项目的核心目标很明确: 用 WorkBuddy 作为调度中枢,用 deepseek-v4-flash 作为内容生成引擎,用微信小程序或微信消息通道作为接收端,实现一份每日自动生成、自动推送的 AI 日报 。它解决的不是“信息太少”的问题,而是“信息太散、筛选太慢、容易遗漏”的问题。适合所有需要跟踪行业动态的从业者、独立开发者、产品经理、运营人员,以及任何想用自动化手段给自己减负的人。
我前后调了三个版本才跑通。第一版死在定时任务不触发,第二版死在微信侧收不到消息,第三版才把链路彻底跑顺。下面我把整个设计思路、关键细节、实操步骤和踩过的坑全部拆开讲,你照着做就能复现。
2. 整体方案设计与核心思路拆解
2.1 为什么选 WorkBuddy 做调度而不是自己写 Cron
很多人第一反应是:不就是定时任务吗,服务器上写个 crontab 不就行了?我一开始也这么想,但实际跑下来发现,纯 Cron 方案有三个硬伤。
第一, 环境依赖太重 。你要自己维护一台常驻服务器,装 Python 环境、配依赖、处理日志轮转,还要保证网络稳定。对于只是想要一份日报的人来说,这是杀鸡用牛刀。
第二, 技能扩展不灵活 。日报的内容源会变,今天抓社区,明天可能加邮件,后天可能加内部知识库。纯脚本方案每加一个源就要改代码、重新部署,维护成本很高。
第三, 消息推送要自己对接 。微信侧的消息通道有各种限制,自己对接不仅麻烦,还容易因为频率问题被限制。
WorkBuddy 的优势在于,它把 定时调度、技能挂载、脚本执行、消息推送 这几件事打包成了一个工作台。你只需要定义“什么时候触发、触发后执行什么、执行完推送到哪里”,中间的运行环境、依赖管理、日志记录它帮你处理了。这就像你本来要自己盖一间厨房才能做饭,现在直接给你一个带灶台的操作间,你只管炒菜就行。
提示:WorkBuddy 的定时任务支持 Cron 表达式,也支持自然语言描述时间。我实测下来,用 Cron 表达式更稳,因为自然语言解析偶尔会有时区歧义。
2.2 deepseek-v4-flash 在链路里扮演什么角色
日报的生成分两步: 信息采集 和 内容生成 。采集靠脚本,生成靠模型。我选 deepseek-v4-flash 的原因很直接: 快、便宜、够用 。
日报这种场景不需要模型做复杂推理,它只需要把采集到的原始信息做三件事:去重、摘要、分类。deepseek-v4-flash 的响应速度在同类模型里属于第一梯队,生成一份八百字左右的日报,通常三到五秒就能返回。而且它的上下文窗口足够放下我采集的几十条原始信息,不需要做复杂的分片处理。
这里有个关键设计: 我不让模型直接生成最终文案,而是让它输出结构化 JSON 。比如:
{
"date": "2025-06-01",
"sections": [
{
"title": "行业动态",
"items": [
{"summary": "某平台发布新版本", "source": "社区", "importance": "high"}
]
}
]
}
拿到 JSON 之后,我再在脚本里做格式化,拼成适合微信阅读的 Markdown 或纯文本。这样做的好处是,模型只负责它擅长的“理解和归纳”,排版和推送逻辑由代码控制,稳定性高很多。如果让模型直接写最终文案,它偶尔会加一些奇怪的修饰词,或者把来源搞混,反而增加排查成本。
2.3 微信侧接收方案的选择逻辑
微信侧接收有三条路可走: 微信小程序订阅消息、企业微信机器人、个人微信文件传输助手 。我三条都试过,最后选了小程序订阅消息作为主通道,企业微信机器人作为备用通道。
小程序订阅消息的优势是 正式、稳定、可追溯 。用户主动订阅之后,每天十点半收到一条服务通知,点进去就能看日报详情。缺点是需要开发一个小程序页面来承载内容,前期投入稍大。
企业微信机器人的优势是 零开发、五分钟接入 。你只需要在群里添加一个机器人,拿到 Webhook 地址,脚本直接 POST 过去就行。缺点是消息长度有限制,超过一定字数会被截断,而且样式比较单一。
个人微信文件传输助手理论上最简单,但实际用下来最不稳定,因为微信对自动化操作有风控,频繁发送容易触发限制。我不推荐这条路。
我的最终方案是: WorkBuddy 定时触发脚本,脚本采集信息后调用 deepseek-v4-flash 生成结构化日报,然后通过小程序订阅消息推送给订阅用户,同时把完整版日报 POST 到企业微信机器人作为备份 。这样即使小程序侧出问题,企业微信里也能看到内容。
3. 核心细节解析与实操要点
3.1 WorkBuddy 定时任务的配置细节
WorkBuddy 的定时任务配置界面里,有几个参数需要特别注意。
Cron 表达式
:我设的是
30 10 * * *
,意思是每天上午十点三十分触发。这里有个坑:WorkBuddy 默认使用 UTC 时间,如果你在中国大陆,需要手动把时区调成 UTC+8,否则任务会在下午六点半才跑。我第一版就是栽在这里,等了三天才发现时间不对。
任务超时时间 :默认是六十秒,但采集加生成加推送的完整链路,实测下来平均需要四十到五十秒,遇到网络波动可能超过六十秒。我建议把超时调到一百二十秒,留足余量。
失败重试策略 :WorkBuddy 支持配置重试次数和重试间隔。我设的是重试两次,间隔三十秒。这样即使某次采集源临时不可用,也有机会在第二次或第三次成功。
日志保留 :默认保留最近七天的执行日志。如果你需要回溯更久,可以在设置里调到三十天。日志里会记录每次执行的开始时间、结束时间、输出内容和错误信息,排查问题时非常有用。
注意:WorkBuddy 的定时任务在触发时,如果上一次任务还没执行完,默认会跳过本次触发。这个行为在日报场景下是合理的,避免重复推送。但如果你希望排队执行,需要在高级设置里手动开启。
3.2 信息采集脚本的编写要点
采集脚本是整个链路里最脏最累的部分,因为每个信息源的抓取方式都不一样。我把它拆成了三个模块: 源配置、抓取器、清洗器 。
源配置 用一个 JSON 文件管理,每个源包含名称、类型、地址、抓取频率、权重。比如:
{
"sources": [
{
"name": "技术社区热帖",
"type": "rss",
"url": "https://example.com/feed",
"weight": 1.0
},
{
"name": "内部知识库更新",
"type": "api",
"url": "https://internal.example.com/api/updates",
"weight": 1.5
}
]
}
抓取器 根据类型分发。RSS 源用 feedparser 解析,API 源用 requests 请求,网页源用 playwright 做无头浏览器渲染。这里重点说 playwright,因为很多社区页面是动态加载的,直接 requests 拿不到内容。playwright 的好处是它能等页面完全渲染后再提取文本,稳定性比 selenium 好很多。
from playwright.sync_api import sync_playwright
def fetch_dynamic_page(url):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(url, wait_until="networkidle")
content = page.inner_text("body")
browser.close()
return content
清洗器 负责去重、去广告、提取正文。去重我用的是 SimHash 算法,把每条内容的文本转成一个六十四位指纹,相似度超过阈值的视为重复。去广告用正则匹配常见广告关键词,比如“点击领取”“限时优惠”这类。提取正文用 readability-lxml,它能自动识别页面里的主体内容区域,把导航栏、侧边栏、评论区过滤掉。
3.3 deepseek-v4-flash 的调用与提示词设计
调用 deepseek-v4-flash 的接口本身不复杂,关键是提示词的设计。我前后改了五版提示词,最终稳定下来的版本包含四个部分: 角色定义、任务说明、输出格式、约束条件 。
角色定义让模型知道自己是一个“信息筛选助手”,任务说明告诉它要把原始信息归纳成几个固定板块,输出格式强制它返回 JSON,约束条件要求它不编造信息、不添加主观评价、每条摘要不超过八十字。
prompt = """
你是一个信息筛选助手。请把下面的原始信息归纳成日报,分为三个板块:
1. 行业动态:与行业趋势、产品更新相关的内容
2. 技术干货:与技术实践、工具使用相关的内容
3. 值得关注:其他有价值但不易归类的内容
输出格式为 JSON,结构如下:
{
"sections": [
{"title": "板块名", "items": [{"summary": "摘要", "source": "来源"}]}
]
}
约束:
- 每条摘要不超过 80 字
- 不编造原始信息中没有的内容
- 不添加主观评价
- 如果某个板块没有内容,items 为空数组
原始信息:
{raw_content}
"""
这里有个经验: 温度参数设成 0.3 。温度太高,模型会自由发挥,摘要里出现原文没有的表述;温度太低,模型会过于保守,把不同来源的相似内容重复输出。0.3 是我实测下来平衡点最好的值。
3.4 微信小程序订阅消息的接入流程
小程序订阅消息的接入分四步: 注册小程序、配置模板、获取订阅授权、发送消息 。
注册小程序需要主体资质,个人开发者可以注册,但部分类目有限制。日报推送属于“服务通知”类目,个人主体可以申请。
配置模板在小程序后台的“订阅消息”页面操作。你需要选一个合适的模板,或者自定义模板。模板里可以定义多个字段,比如“日报日期”“内容摘要”“详情链接”。字段类型支持文本、数字、日期等。
获取订阅授权需要在小程序前端调用
wx.requestSubscribeMessage
,让用户主动点击授权。这里有个关键限制:
一次授权只能发送一条消息
。也就是说,如果用户只授权了一次,你只能给他发一条日报,第二天就发不出去了。解决办法是引导用户勾选“总是保持以上选择”,这样后续可以持续发送。
发送消息在服务端调用微信的接口,需要用到 access_token。access_token 有有效期,需要缓存并定时刷新。我用的方案是存在 Redis 里,设置七千秒过期,每次发送前检查是否过期,过期就重新获取。
def send_subscribe_message(openid, template_id, data):
access_token = get_access_token()
url = f"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}"
payload = {
"touser": openid,
"template_id": template_id,
"data": data
}
response = requests.post(url, json=payload)
return response.json()
提示:小程序订阅消息的发送频率有限制,单个用户每天最多接收一定数量的服务通知。日报场景下每天一条,完全在限制范围内。
4. 实操过程与核心环节实现
4.1 从零搭建 WorkBuddy 任务的完整步骤
第一步,登录 WorkBuddy 工作台,进入“技能”页面,创建一个新的技能。技能类型选“脚本执行”,运行环境选 Python 3.10。为什么选 3.10?因为 playwright 和 feedparser 在这个版本下兼容性最好,3.11 和 3.12 偶尔会有依赖冲突。
第二步,在技能里上传或在线编辑脚本。脚本的入口函数需要按照 WorkBuddy 的规范来写,通常是定义一个
main
函数,接收一个 context 参数,返回执行结果。context 里包含触发时间、任务 ID、环境变量等信息。
def main(context):
raw_content = collect_all_sources()
daily_report = generate_report(raw_content)
push_to_wechat(daily_report)
return {"status": "success", "report_length": len(daily_report)}
第三步,配置定时触发器。在技能详情页找到“触发器”选项卡,添加一个 Cron 触发器,表达式填
30 10 * * *
,时区选 UTC+8。同时把超时时间改成一百二十秒,重试次数设为两次。
第四步,配置环境变量。把 deepseek 的 API Key、微信小程序的 AppID 和 AppSecret、企业微信机器人的 Webhook 地址都存到环境变量里,不要硬编码在脚本中。WorkBuddy 的环境变量是加密存储的,安全性有保障。
第五步,手动触发一次测试。在技能页面点击“立即执行”,观察日志输出。如果一切正常,你会看到采集条数、生成耗时、推送结果等信息。我第一次测试时采集到了四十三条原始信息,生成日报用了四点二秒,推送成功。
4.2 日报内容的格式化与推送实现
模型返回的 JSON 需要经过格式化才能推送。我写了一个
format_report
函数,把 JSON 转成适合微信阅读的纯文本。
def format_report(report_json):
lines = []
lines.append(f"AI 日报 | {report_json['date']}")
lines.append("")
for section in report_json["sections"]:
lines.append(f"【{section['title']}】")
for item in section["items"]:
lines.append(f"- {item['summary']}(来源:{item['source']})")
lines.append("")
return "\n".join(lines)
格式化之后,先推送到企业微信机器人。企业微信机器人的消息体支持 Markdown 格式,但长度限制是四千零九十六字节。如果日报超过这个长度,需要截断或者分多条发送。我的做法是:如果超过三千五百字节,就只推送摘要版,完整版存到数据库,通过小程序详情页查看。
def push_to_wecom(content):
webhook = os.environ["WECOM_WEBHOOK"]
if len(content.encode("utf-8")) > 3500:
content = content[:3000] + "\n\n... 完整内容请在小程序查看"
payload = {
"msgtype": "markdown",
"markdown": {"content": content}
}
requests.post(webhook, json=payload)
小程序侧的推送需要先查询订阅用户列表,然后逐个发送。订阅用户列表存在数据库里,每次用户授权时插入一条记录。发送时遍历列表,调用
send_subscribe_message
。如果某个用户发送失败,记录失败原因,下次重试。
4.3 参数计算与性能优化记录
整个链路的耗时分布大致是:采集三十秒、生成五秒、推送三秒、其他开销五秒,总计约四十三秒。采集是大头,因为要等 playwright 渲染页面。我做了两个优化。
优化一:并发采集 。把独立的源放到线程池里并行抓取,采集时间从三十秒降到了十二秒。注意,playwright 的浏览器实例不要并发太多,我实测下来同时开三个页面比较稳,开五个以上容易内存溢出。
from concurrent.futures import ThreadPoolExecutor
def collect_all_sources():
sources = load_sources()
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(fetch_source, sources))
return merge_results(results)
优化二:缓存静态内容 。有些源的内容变化不频繁,比如内部知识库的文档列表,没必要每次都全量抓取。我加了一个简单的缓存层,用文件的修改时间做判断,如果距离上次抓取不到一小时,直接读缓存。
优化三:模型调用批处理 。如果原始信息超过模型单次处理的上下文限制,需要分批调用。我的做法是按来源分组,每组单独生成摘要,最后合并。这样虽然多调用几次模型,但每次的输入更聚焦,生成质量反而更好。
4.4 完整链路的联调与验证
联调阶段我重点验证了四个场景: 正常执行、源不可用、模型超时、推送失败 。
正常执行场景下,日志显示采集四十三条、生成四点二秒、推送成功,微信侧十点半准时收到消息。
源不可用场景下,我把其中一个源的地址改成无效地址,脚本没有崩溃,而是跳过了该源并在日志里记录了警告。最终日报里少了那个源的内容,但整体流程正常。
模型超时场景下,我把 deepseek 的接口地址改成一个不存在的地址,脚本在等待十秒后抛出超时异常。WorkBuddy 的重试机制在三十秒后重新触发,第二次因为缓存了采集结果,直接跳过采集进入生成,但模型依然不可用,最终任务标记为失败,并发送了告警通知到企业微信。
推送失败场景下,我故意把企业微信的 Webhook 地址改错,脚本捕获了异常并记录日志,但没有影响小程序侧的推送。这说明两个推送通道是解耦的,一个失败不影响另一个。
注意:联调阶段一定要用测试数据,不要直接跑真实数据。我一开始图省事直接跑真实源,结果测试过程中给订阅用户发了好几条重复日报,非常尴尬。
5. 常见问题与排查技巧实录
5.1 定时任务不触发或触发时间不对
这是最常见的问题,我遇到过三次。第一次是时区没设对,WorkBuddy 默认 UTC,我以为是本地时间,结果任务在下午六点半才跑。第二次是 Cron 表达式写错了,把
30 10 * * *
写成了
30 10 * * 1
,变成了每周一才触发。第三次是任务被禁用了,我在调试时手动关掉了触发器,后来忘了重新开启。
排查顺序建议是:先看触发器状态是否启用,再看 Cron 表达式是否正确,最后检查时区设置。WorkBuddy 的触发器页面会显示“下次触发时间”,你可以对照这个时间来判断是否配置正确。
5.2 微信侧收不到推送消息
这个问题分两种情况:小程序订阅消息收不到,和企业微信机器人收不到。
小程序订阅消息收不到,首先检查用户是否真的完成了订阅授权。很多人以为点了“允许”就是订阅了,但实际上微信的订阅消息需要用户主动勾选“总是保持以上选择”才能持续接收。如果用户只授权了一次,你只能发一条。其次检查 access_token 是否有效,过期的 token 会导致接口返回四千零一错误。最后检查模板 ID 是否正确,模板 ID 填错会返回四千零三错误。
企业微信机器人收不到,首先检查 Webhook 地址是否完整,有没有多余的空格。其次检查消息体格式,企业微信对 Markdown 的语法支持有限,某些特殊字符会导致解析失败。最后检查机器人是否被移出群聊,或者群聊是否被解散。
5.3 模型生成内容质量不稳定
模型生成质量不稳定的表现有三种:摘要太长、摘要太短、摘要偏离原文。
摘要太长通常是提示词里没有限制字数,或者温度设得太高。解决办法是在提示词里明确写“每条摘要不超过八十字”,并把温度降到 0.3。
摘要太短通常是原始信息本身就很短,模型没有足够的内容可以归纳。这种情况可以在采集阶段做过滤,把字数少于五十字的原始信息直接丢弃。
摘要偏离原文通常是模型在“自由发挥”。解决办法是在提示词里加一条约束:“摘要必须基于原始信息,不得添加原文没有的内容”。如果还是不行,可以在生成后加一个校验步骤,用另一个模型调用判断摘要是否忠实于原文。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 任务不触发 | 触发器禁用 | 查看触发器状态 | 重新启用触发器 |
| 触发时间不对 | 时区设置错误 | 检查时区配置 | 改为 UTC+8 |
| 采集为空 | 源地址失效 | 手动请求源地址 | 更新源配置 |
| 模型超时 | 接口不可用 | 检查 API Key 和网络 | 重试或更换接口 |
| 小程序收不到 | 未授权或 token 过期 | 检查授权记录和 token | 引导重新授权,刷新 token |
| 企业微信收不到 | Webhook 错误 | 手动 POST 测试 | 更新 Webhook 地址 |
| 日报内容重复 | 去重阈值太低 | 检查 SimHash 阈值 | 提高相似度阈值 |
| 推送内容截断 | 超过长度限制 | 检查内容字节数 | 截断或分条发送 |
5.5 我踩过的三个坑和对应的避坑技巧
坑一:playwright 浏览器实例没有正确关闭
。我在早期版本里每次抓取都新建一个浏览器实例,但忘记关闭,跑了两天之后服务器内存被占满,任务开始随机失败。解决办法是用
with
语句管理浏览器生命周期,确保每次抓取后自动关闭。
坑二:deepseek 接口的并发限制 。我一开始为了提速,把模型调用也放到了线程池里并发执行,结果触发了接口的速率限制,返回四百二十九错误。后来改成串行调用,虽然慢了几秒,但稳定性大幅提升。如果你的场景确实需要并发,建议加一个令牌桶限流器,控制每秒请求数。
坑三:微信 access_token 被其他服务刷新 。如果你有多个服务共用一个小程序,它们可能会同时刷新 access_token,导致先刷新的那个 token 失效。解决办法是统一由一个服务负责刷新,其他服务从缓存读取。我用的方案是把 token 存在 Redis 里,所有服务都从 Redis 读,只有一个定时任务负责刷新。
提示:WorkBuddy 的日志功能非常有用,每次执行都会记录完整的标准输出和标准错误。排查问题时,先看日志里的错误堆栈,大部分问题都能直接定位。
6. 后续可以继续扩展的方向
这套链路跑通之后,我发现它的扩展性比想象中好。最直接的扩展是 增加信息源 ,比如把邮件列表、内部 Wiki 更新、竞品博客都接进来。每加一个源,只需要在源配置里加一条记录,写对应的抓取器就行。
第二个扩展方向是 个性化日报 。不同的人关注的内容不一样,可以在订阅时让用户选择感兴趣的板块,生成日报时根据用户偏好过滤内容。这个功能需要在小程序侧加一个偏好设置页面,后端根据偏好生成不同的日报版本。
第三个扩展方向是 日报归档与检索 。把每天的日报存到数据库里,提供一个搜索页面,支持按关键词、日期、来源检索。这样日报就不只是“当天看完就扔”的东西,而是变成了一个可积累的知识库。
我目前已经把前两个方向跑通了,第三个方向还在做。如果你也在用 WorkBuddy 做类似的事情,建议先从最简单的单源单通道跑通,再逐步加源加通道。一上来就搞大而全,很容易在联调阶段卡住。先把主链路跑顺,后面加东西就是复制粘贴的事。
更多推荐



所有评论(0)