1. 为什么我要给 WorkBuddy 装一个“十点半闹钟”每天早上到工位第一件事不是泡茶而是打开各种信息源翻一遍昨天项目群里有没有新需求、关注的几个技术社区有没有值得看的帖子、手头几个自动化任务跑完没有、有没有新的 AI 工具更新值得试。这套动作熟练之后大概要花二十分钟但问题是它每天都在重复而且一旦忙起来就容易漏掉关键信息。我当时的想法很朴素能不能让一个 AI 助手在固定时间把这些东西整理好直接推到我微信里我睁眼就能看这个需求听起来像“做个定时任务”那么简单但真正落地的时候牵扯到的东西比想象中多。WorkBuddy 本身是一个偏 AI Agent 方向的工作助手类工具它的核心能力是理解任务、调用工具、组织输出。而微信作为接收端最大的好处是不用再装一个新 App消息触达率也高。把这两者串起来中间需要解决三个问题定时怎么触发、内容怎么生成、消息怎么送达。这三个问题分别对应调度层、AI 层和通道层任何一层没打通整个链路就是断的。我最终跑通的方案是每天上午 10:30由调度器触发 WorkBuddy 执行一份预设的“日报生成任务”任务内部调用大模型对采集到的信息做摘要和归类生成一份结构化的日报文本再通过微信的接收通道把这份文本推送给我自己。整个过程不需要我手动干预也不需要一直开着电脑。下面我把这套东西从设计到落地的完整过程拆开讲包括我踩过的坑和最后稳定运行的配置。提示这篇文章面向的是已经对 AI Agent、自动化调度、微信消息接收有基本概念但还没把它们串成一条完整链路的读者。如果你完全没接触过 WorkBuddy建议先把它跑起来再回来看集成部分。2. 拆解这条自动化链路调度、生成、推送三段式2.1 调度层为什么选定时任务而不是常驻进程最开始我想的是写一个常驻脚本用while True加sleep的方式每小时跑一次。实测下来问题很明显脚本一旦因为网络抖动或者依赖更新挂掉就再也不会自己起来而且常驻进程在笔记本上跑合盖休眠就断了。后来换成系统级的定时任务由操作系统来负责触发脚本执行完就退出下一次到点再拉起。这样即使中间某次执行失败下一次调度依然会正常触发稳定性完全不是一个量级。在 Linux 环境下我用的是cron在 Windows 环境下用的是“任务计划程序”。两者的核心逻辑一样指定一个时间表达式到点执行一条命令。以 cron 为例30 10 * * *就代表每天上午 10:30 触发一次。这里有个细节很多人会忽略cron 执行时的环境变量和你登录 shell 的环境变量不一样如果你在脚本里依赖了某个 PATH 里的命令最好在脚本开头显式声明 PATH否则会出现“手动跑没问题、定时跑就报 command not found”的经典问题。2.2 AI 层WorkBuddy 在链路里到底干什么WorkBuddy 在这条链路里的角色不是“定时器”而是“内容生产者”。调度器只负责在 10:30 把它叫醒真正决定日报质量的是 WorkBuddy 内部的任务编排。我给它设定的任务大致分四步第一步从几个固定的信息源拉取原始内容比如项目协作工具的更新记录、我订阅的几个技术站点的 RSS、以及本地一个记录待办的 Markdown 文件第二步把原始内容交给大模型做摘要要求它按“今日重点 / 待办提醒 / 值得一看”三个板块输出第三步对输出做一次格式校验确保没有空板块、没有超长段落第四步把最终文本交给推送模块。这里的关键在于提示词的设计。我试过直接让模型“总结一下今天的信息”结果它经常把不重要的内容也塞进来日报变得又长又水。后来我把提示词改成带明确结构和字数约束的版本比如“每个板块不超过三条每条不超过 50 字如果某个板块没有内容就写‘今日无’”输出质量立刻稳定了很多。这也是我建议所有做 AI 日报的人注意的一点约束比自由更能出好结果。2.3 通道层微信接收消息的几种可行路径微信本身不提供个人号的消息推送 API所以“把消息送进微信”这件事需要绕一下。我实际验证过并且认为可用的路径有这么几种各有取舍路径实现方式优点缺点文件传输助手通过桌面端自动化操作发送不需要额外账号依赖桌面端在线稳定性一般企业微信应用消息注册企业微信用应用消息接口接口稳定支持 Markdown需要企业微信账号公众号模板消息自己注册测试号或服务号触达规范有资质和审核门槛第三方聚合推送通过支持微信通道的推送服务接入快依赖第三方可用性我最后选的是企业微信应用消息这条路原因是它的接口足够稳定支持 Markdown 格式而且消息会同步到微信里企业微信可以关联个人微信接收消息。配置过程不复杂在企业微信后台建一个自建应用拿到corpid、corpsecret和agentid然后调用获取 token 的接口再用 token 调发送消息的接口。整个流程用 Python 的requests库二十行代码就能搞定。注意不管你选哪条路径都要注意消息内容的长度限制。企业微信应用消息的 Markdown 内容有长度上限日报太长会被截断。我的做法是在推送前做一次长度检查超过阈值就自动拆成两条发送。3. 把 WorkBuddy 的任务编排写成可复用的配置3.1 任务定义文件的结构设计WorkBuddy 的任务如果每次都靠手动点那自动化就无从谈起。我的做法是把整个日报任务写成一个配置文件让调度器直接调用这个配置。配置文件我用的 YAML 格式结构大致分三块sources定义信息源prompt定义提示词模板output定义输出和推送方式。这样设计的好处是以后我想加一个新的信息源只需要在sources里加一行不用动任何代码逻辑。task: name: daily_report schedule: 30 10 * * * sources: - type: rss url: https://example.com/feed.xml limit: 10 - type: file path: ./todo.md prompt: | 你是一个工作日报助手。请根据以下原始信息生成一份日报。 要求 1. 分为“今日重点”“待办提醒”“值得一看”三个板块 2. 每个板块不超过三条每条不超过 50 字 3. 没有内容的板块写“今日无” 原始信息 {{content}} output: channel: wecom format: markdown这个结构里最值得说的是{{content}}这个占位符。它代表前面所有信息源拉取到的内容拼接后的结果。我在实现的时候做了一个小优化不同来源的内容之间加一个分隔标记比如--- 来源RSS ---这样模型在摘要的时候能区分不同来源归类会更准确。3.2 信息源采集的容错处理信息源采集看起来简单实际上是最容易出问题的一环。RSS 源可能因为对方服务器抽风而超时本地文件可能因为路径写错而读不到接口可能因为 token 过期而返回 401。如果采集环节直接抛异常整个日报任务就挂了。我的处理方式是每个信息源独立 try-catch失败就记录一条警告并跳过保证只要有任意一个源成功日报就能生成。具体实现上我给每个采集函数加了超时参数RSS 请求超时设 10 秒接口请求超时设 15 秒。超时之后不重试直接跳过因为日报讲究的是“按时到达”而不是“内容绝对完整”。如果某个源连续三天都失败我会在日报末尾加一行提示提醒我去检查那个源。这个设计思路来自监控系统里的“降级”概念核心链路优先保证可用非核心部分可以牺牲。3.3 提示词模板的迭代过程提示词不是一次写好的我前后改了大概五版。第一版太笼统输出像流水账第二版加了板块划分但模型经常把内容放错板块第三版我在提示词里加了每个板块的定义说明比如“今日重点指需要我今天处理的事项”准确率明显提升第四版加了字数约束解决了输出过长的问题第五版加了“如果信息不足以判断就写‘信息不足’而不是编造”这一条对抑制模型幻觉非常关键。我把最终版的提示词固化在配置文件里同时保留了一个prompt_version字段方便以后回溯是哪一版提示词产出的日报。这个习惯是我做自动化测试时养成的任何会影响输出的配置都要有版本标记否则出了问题根本不知道是哪次改动导致的。4. 微信推送通道的落地细节与踩坑记录4.1 企业微信应用消息的完整调用流程企业微信应用消息的调用分两步先拿 access_token再用 token 发消息。access_token 有有效期默认 7200 秒所以不能每次发消息都重新获取那样太浪费。我的做法是把 token 缓存在本地文件里每次发消息前先检查缓存是否过期过期了再重新获取。这里有个坑企业微信对获取 token 的接口有频率限制如果你在短时间内反复获取会被限流。缓存机制正好也解决了这个问题。import requests, json, time, os CACHE_FILE ./wecom_token.json def get_token(corpid, corpsecret): if os.path.exists(CACHE_FILE): with open(CACHE_FILE) as f: cache json.load(f) if cache[expire_at] time.time(): return cache[token] url https://qyapi.weixin.qq.com/cgi-bin/gettoken resp requests.get(url, params{corpid: corpid, corpsecret: corpsecret}, timeout15) data resp.json() token data[access_token] with open(CACHE_FILE, w) as f: json.dump({token: token, expire_at: time.time() 7000}, f) return token def send_message(token, agentid, content): url https://qyapi.weixin.qq.com/cgi-bin/message/send payload { touser: all, msgtype: markdown, agentid: agentid, markdown: {content: content} } resp requests.post(url, params{access_token: token}, jsonpayload, timeout15) return resp.json()这段代码里我把过期时间设成 7000 秒而不是 7200 秒留了 200 秒的缓冲避免边界情况下拿到一个即将过期的 token。这个细节看起来小但在实际运行中能避免不少偶发的 401 错误。4.2 消息内容格式化的几个实用技巧企业微信的 Markdown 支持有限不是所有 Markdown 语法都能渲染。我实测下来标题、加粗、列表、分割线是支持的但表格和代码块支持得不好。所以我在生成日报的时候会刻意避免用表格改用列表加缩进的方式表达层级。另外消息开头最好加一个时间戳比如“2025-01-15 日报”这样在聊天记录里往回翻的时候能快速定位。还有一个技巧是给不同板块加不同的前缀符号。我用的是【重点】、【待办】、【推荐】这样的方括号标记比单纯的 Markdown 标题更醒目而且在手机窄屏上也不会因为标题层级而显得混乱。这个是我看了十几份日报之后总结出来的移动端阅读视觉锚点比层级结构更重要。4.3 推送失败时的重试与告警推送失败的原因通常有三种网络超时、token 失效、内容超长。我的处理策略是分类对待网络超时重试两次间隔 5 秒token 失效就强制刷新 token 再试一次内容超长就自动截断并加省略号。如果三次都失败就把日报内容写到本地文件并在下一次调度时优先补发。这个“本地落盘 补发”的机制是我从消息队列的持久化思路里借鉴的能保证日报不会因为一次推送失败就彻底丢失。提示补发的时候要注意去重。我的做法是在本地文件里记录已发送的日报日期补发前先检查当天是否已经发过避免重复推送。5. 让日报真正有用的内容筛选逻辑5.1 信息源的取舍少即是多一开始我往信息源里塞了十几个 RSS结果日报变得又长又杂很多内容我根本不看。后来我做了一次精简只保留三类源和我当前项目直接相关的、我长期关注且更新频率适中的、以及我自己的待办文件。数量从十几个降到五个日报长度从一千多字降到四百字左右阅读体验反而好了很多。这个取舍背后的逻辑是日报的价值不在于“信息全”而在于“信息准”。一份四百字但每条都和我相关的日报比一份两千字但一半是噪音的日报有用得多。如果你也在做类似的东西建议先问自己一个问题这条信息如果今天没看到会对我造成什么影响如果答案是“没什么影响”那它就不该进日报。5.2 用模型做二次筛选而不是一次摘要我最初的流程是“原始内容 → 模型摘要 → 输出”后来改成“原始内容 → 模型初筛 → 模型精摘 → 输出”。多出来的这一步初筛是让模型先判断每条信息是否值得进入日报把明显无关的过滤掉再对剩下的做精摘。这样做的成本是调用两次模型但输出质量提升很明显尤其是当信息源里有大量重复内容的时候。初筛的提示词我写得很简单“以下内容中哪些与软件开发、AI 工具、项目管理相关只返回相关的条目编号。”模型返回编号列表后我再根据编号取对应的原文进入精摘环节。这个思路其实和推荐系统里的“召回 排序”两阶段模型是一样的先用低成本的方式缩小候选集再用高成本的方式做精细处理。5.3 日报的“可执行性”设计一份日报如果只是信息罗列那和刷信息流没区别。我在日报里加了一个“今日建议”板块让模型基于当天信息给出一条具体的行动建议比如“建议今天优先处理 XX 项目的接口联调”或者“建议抽 30 分钟看一下 XX 工具的更新说明”。这条建议不一定每次都对但它能起到一个“提醒我思考”的作用。为了让这条建议更有依据我在提示词里要求模型结合待办文件里的内容来生成。比如待办文件里有一条“周三前完成数据迁移脚本”如果当天是周二模型就会在建议里提醒我这件事。这种把静态待办和动态信息结合的做法是让日报从“信息汇总”变成“行动助手”的关键一步。6. 稳定运行一个月后的复盘与调优6.1 实际运行数据与预期差距这套东西跑了一个月我统计了一下30 天里成功推送 28 次失败 2 次。失败的原因一次是 RSS 源超时导致采集为空日报内容只有“今日无”我手动补发了另一次是 token 缓存文件被误删导致获取 token 时走了全量流程恰好碰上接口限流。两次失败都在可接受范围内而且都有明确的日志可查。和预期差距最大的是日报的阅读率。我原本以为每天都会认真看实际上大概只有一半的日子会仔细读另一半是扫一眼就过。这让我意识到日报的价值不在于“每天都有”而在于“有内容的时候足够醒目”。后来我加了一个机制如果当天日报内容少于三条就在标题里加一个“轻量”标记提醒我这天没什么重要信息可以快速跳过。6.2 几个值得固化的经验第一个经验是日志要记全。我在每个环节都加了日志采集了多少条、初筛剩下多少条、精摘用了多少 token、推送返回什么状态码。这些日志平时不看但出问题的时候能快速定位是哪一环出了岔子。第二个经验是配置和代码分离。信息源、提示词、推送通道这些都放在配置文件里代码只负责读配置和执行逻辑。这样我想调整日报风格的时候改配置文件就行不用动代码也不会引入新的 bug。第三个经验是不要追求一次做到完美。我最开始的版本很粗糙信息源只有两个提示词也很简单但它在第一天就成功推送了。后面所有的优化都是在这个能跑的版本上迭代出来的。如果一开始就想着把所有细节都设计好再动手很可能到现在还没跑起来。先跑通再跑好这句话在自动化项目里尤其适用。6.3 后续可以扩展的方向这套链路跑通之后能扩展的地方其实很多。比如把日报从“每天一次”改成“早晚各一次”早上推待办和计划晚上推完成情况和明日提醒。再比如把单向推送改成双向交互我在微信里回复“完成 XX”WorkBuddy 自动更新待办文件。还可以把日报内容存进数据库月底生成一份月度回顾看看这个月主要在处理哪些类型的事情。不过这些扩展我暂时不打算做因为当前版本已经满足了我的核心需求每天十点半一份整理好的日报自动送到微信里。在自动化这件事上我的原则是“够用就好”过度设计反而会增加维护成本。等哪天现有版本真的不够用了再动手扩展也不迟。最后分享一个我在调试阶段用的小技巧如果你不确定定时任务有没有触发可以在脚本开头加一行写时间戳到日志文件的代码。这样即使脚本后面报错退出了你也能从日志里看到它确实被触发过。这个技巧帮我排除了好几次“以为是调度问题、其实是脚本问题”的误判。