用Agent构建每日行业资讯自动整理与推送系统

用Agent构建每日行业资讯自动整理与推送系统 1. 项目背景与需求拆解1.1 为什么我决定用 Agent 做资讯整理做技术这行每天必须看行业动态不然没过多久就跟不上节奏了。我之前每天早上常用的操作是先刷一遍几个技术社区首页再翻公众号、邮件订阅的周报然后打开一堆浏览器标签页。整个过程少说四十分钟看起来很勤奋但真正有效的信息其实没几条。更难受的是很多新闻在多个渠道会重复推送我每天要花不少时间判断这条昨天是不是看过了。后来我意识到这件事天然适合让 Agent 去做。它的输入是几十个信息源输出是一份精简的行业日报中间还牵扯到过滤、去重、摘要、分类这些步骤单靠固定的脚本规则很难写好因为每天的内容千变万化规则永远要在新样本上打补丁。而用大模型驱动的 Agent可以把看内容、做判断这件事交给自然语言指令来完成比一堆正则表达式可靠得多。这个系列写到第四篇了前几篇我分别分享了 Agent 的基础概念、常用框架以及工具调用的落地方式。这篇就沿着一个真实在跑的项目展开每天定时自动整理行业资讯生成日报推送到工作群。整个项目不算复杂但涉及 Agent 开发中的不少典型问题怎么设计架构、怎么处理数据源、要不要引入记忆、怎么保证输出稳定。我会把每一步的选择逻辑和踩过的坑都讲清楚。1.2 需求边界不是做一个通用机器人动手之前我把需求收敛得非常克制。很多人在这个环节容易犯的错误是一上来就想做一个智能资讯助手既要做日报又要做语音播报还要支持对话问答结果项目拖了很久都没上线。我的原则是先把最小闭环跑通每天早上 8 点 30 分Agent 自动抓取行业信息源过滤掉低质量和重复内容用大模型生成每条的摘要和分类标签最后汇总成一篇日报推送到飞书群。具体拆解下来核心需求只有四块采集、清洗、归纳、分发。采集是把分散在 RSS、官网、社区的信息拿回来清洗是去掉广告、无关内容和重复文章归纳是让 Agent 对每篇内容做摘要和打标签分发是把整理好的结果推送到指定渠道。这四块正好对应了一个简单 AI Agent 的完整链路感知输入、处理信息、调用工具、输出结果。我特意把能与 Agent 对话这个需求砍掉了。因为一旦加入对话功能就需要维护上下文窗口、控制回答风格、处理追问复杂度会翻好几倍。而我每天真正需要的只是早晨那份日报而已。这个取舍很重要Agent 项目最怕需求漫无边际先做窄做深再谈扩展。2. Agent 整体架构与框架选型2.1 我为什么没有一开始就套框架最早我想得很简单直接上一个现成的 Agent 框架调度、工具调用、记忆管理全都帮你封装好了我只要写插件就行。但实际用下来我发现对定时抓取资讯→总结推送这种相对固定流程的项目重型框架反而累赘启动慢、配置多、排错时需要理解框架自己的封装逻辑出了问题都不知道该查谁。后来我重新理了一下思路。Agent 项目的核心价值应该放在利用大模型做判断和生成这件事上至于调度、HTTP 请求、消息推送这些用常规的 Python 代码反而更直接。所以最终我采用了一个很轻的架构用 Python 脚本做流程控制把大模型调用封装成一个独立的 Agent 服务再通过函数调用Function Calling让模型在需要时调用工具。这个思路在行业里也常被叫作Agent 编排它的本质就是大脑负责思考和决策工具负责具体执行。这样拆的好处很明显每个环节都可以单独测试。采集逻辑挂了不影响 Agent 服务模型输出抽风了我也能快速定位到是 Prompt 的问题还是上下文太长的问题。如果你的项目也只是固定流程的自动化任务我建议别急着上全家桶框架先理清哪些环节真的需要智能决策哪些环节只是确定性的代码后者完全不需要 Agent 参与。2.2 轻量 Agent 服务的设计思路在这个项目里我定义的 Agent 不是传统意义上的全自主智能体更准确说是一个 ReAct 模式推理行动循环的工作单元。流程是这样的先读取采集模块整理好的候选文章列表然后根据当前批次的任务指令决定是要继续抽摘要还是先调用分类工具等工具结果返回后再继续下一步。我把这套 Agent 服务拆成了三个组件指令层、执行层、工具层。指令层就是我写的 System Prompt 和 User Prompt用来告诉模型当前的任务目标、输出格式、注意禁忌执行层负责与大模型通信管理请求重试和返回结果解析工具层则是具体的函数集合比如内容清洗摘要生成标签分类格式排版。这个结构和很多人理解的Agent 大模型 工具是一样的只是我也挂了一个简易的记忆模块用来记录历史处理过的文章指纹避免重复推送。我特别想聊聊 skill 和 agent 的区别因为这也是我早期容易混淆的点。在这个项目里Agent 更像一个调度大脑它决定把一条资讯分配给哪个 skill 去处理而 skill 是具体的能力包比如摘要生成是一个 skill情感判断是另一个 skill。Agent 负责组合和调度这些 skill而不是自己亲自实现每一个动作。理解这点之后我设计功能时思路就清晰多了先把所有需要的能力拆成独立的 skill再让 Agent 负责编排而不是把所有逻辑都塞进 Prompt 里。2.3 框架与自研代码的边界取舍虽然我倾向轻量实现但也不是说完全不用框架。在这个项目里我用了两个轻量库一个是调度用的 APScheduler负责定时触发一个是 HTTP 客户端 httpx负责抓取和推送。如果你想更省事也可以用云函数自带的定时触发器比如在云服务控制台上配置一条 Cron 表达式函数到点自动执行连常驻进程都省了。真正值得纠结的是要不要引入像 LangChain 或 Dify 这一类的框架。我的建议是如果你需要图形化编排、需要给非技术人员配置流程Dify 这类平台会更合适如果你像我一样需要灵活调试 Prompt、需要把自己的代码和模型逻辑深度融合直接写 Python 会更顺手。这个项目最终选择了自写代码加轻量库的组合因为资讯整理流程很短框架带来的收益不明显反而增加了依赖和学习成本。我还测试过在本地跑一个开源模型来做分类但效果不稳定后来换成 API 调用大模型成本和速度都可控。这一点后面会详细说。3. 核心模块实现采集、清洗、摘要与分类3.1 数据源接入先解决在哪里拿资讯的问题资讯 Agent 的第一步是拿到原始数据数据源的质量直接决定日报质量。我用到的信息源主要有三类RSS 订阅源、官方网站的更新列表、行业社区的话题栏目。RSS 还是最推荐的结构清晰、更新及时而且大部分技术博客和媒体平台都支持。采集时直接用 feedparser 这个库解析几行代码就能拿到标题、链接、发布时间、正文摘要。如果你要抓的网站没有 RSS就需要写 HTML 解析规则了。我的做法是先找到列表页的 DOM 结构提取出文章链接和标题再进入详情页抓正文。这一步要注意遵守网站的服务条款控制抓取频率尽量不要给目标服务器造成压力。我还会给每个数据源配一个抓取间隔比如官方博客最长每 2 小时抓一次行业社区每 30 分钟抓一次避免频繁请求被封。代码层面我封装了一个函数输入是源配置输出是标准化后的文章对象列表。所谓标准化就是不管来源是 RSS 还是 HTML最后都统一成包含 title、url、published、content 四个字段的结构。这样下游处理模块就不用关心数据来自哪个源了。这一步看似简单但非常关键它把采集层和处理层完全解耦后面新增数据源只需要写一个适配器。3.2 清洗去重别让模型把时间浪费在垃圾内容上原始数据拿到后第一步是清洗。很多网站会把导航栏、页脚、推广信息一起抓进来正文里也可能包含大量无关代码块或广告。我写了一套轻量级的清洗规则先去掉标题和正文中的 HTML 标签再按常见广告关键词过滤最后用正则去掉重复的空行和奇怪的字符。这是唯一使用正则比较多的环节因为这些规则非常确定不适合交给模型。去重这块我踩过不少坑。最开始我只用文章 URL 做去重但很快发现同一个事件会被不同媒体转载URL 完全不同内容却高度相似。后来我改用标题相似度 正文摘要指纹结合的方式先把标题做归一化处理去掉标点符号和空格计算两两之间的编辑距离相似度再用 MinHash 粗略估算正文的相似度。两个相似度都超过阈值的文章就认为是重复内容只保留发布时间最早的那一篇。这个去重逻辑最好放在模型调用之前因为大模型按 token 计费处理重复内容等于烧钱。我做过一个粗略统计加入去重层之后每天需要交给模型处理的文章数量大约减少了百分之三十。这意味着成本直接砍掉三成效果非常明显。3.3 摘要与分类让 Agent 输出 JSON 而不是散文清洗完的文章列表接下来要交给 Agent 做摘要和分类。这一步是整个项目的核心也是 Agent 能力体现最明显的地方。我会把文章正文截断到前 2000 字左右和系统指令一起发给模型让它生成三样东西一句话摘要、标签列表、相关度评分。为了不让模型自由发挥我要求它严格返回 JSON 格式字段固定为 summary、tags、score。为了让返回格式稳定我做了两个优化一是在 Prompt 里给出 JSON 示例并明确要求不要输出任何解释性文字二是开启模型的 JSON 输出模式如果 API 支持就尽量开启。这两招下去之后解析出错的概率大幅下降。如果解析失败我会让函数自动重试一次第二次只传摘要指令不传完整的正文成本更低容错也更好。分类标签我预设了一套固定的标签体系比如行业动态产品更新技术方案数据报告招聘然后告诉模型只能从这些标签中选择一到三个。固定标签的好处是后续做统计和归档时非常方便你不用每天面对一套全新技术词。如果你希望标签更开放也可以让模型自由生成但一定要告诉它标签必须简短、名词化否则会出现大量动宾短语很难看。3.4 记忆机制让 Agent 不重复唠叨同一件事记忆是这个项目里比较有意思的部分。我给它设计了一个很轻的短期记忆 长期记忆结构。短期记忆是当天的运行上下文记录本次已经处理过哪些文章、哪些还在队列里长期记忆则是去重指纹库把每天处理过的文章标题和 URL 指纹持久化到本地 SQLite 数据库跨天复用。有人可能觉得资讯去重不是上一节已经做过了吗为什么记忆模块还要再做一次因为清洗层的去重只处理同一天内的重复跨天重复的情况它管不了。很多媒体的热门新闻会连推好几天如果不去查历史记录同一篇文章你会在日报里看见三次。我在记忆模块里设计了一个简单的逻辑每处理完一篇文章就计算标题和 URL 的哈希值写入数据库下一次遇到相同哈希就直接跳过。这个实现其实很朴素但效果很好。后来我在想能不能引入更复杂的向量记忆让模型根据语义判断两篇文章是不是讲同一件事。试验下来准确率确实有提升但对一个日报项目来说成本增加不少而且偶尔出现把所有相似新闻都当成重复的误杀问题。我最后选择了折中方案数据库指纹去重保底向量相似度只用来做推荐排序不参与过滤。4. 定时调度与日报分发4.1 让 Agent 跑得更从容任务编排与优先级每天定时任务最怕什么怕采集还没完成摘要已经开始调模型最后日报里缺了一部分来源。我在最初版本就踩过这个坑调度触发后直接开始处理结果某个数据源响应慢等它返回时后面的文章已经处理到一半了。所以后来我加了简单的任务编排逻辑分两个阶段执行。第一阶段是采集和清洗所有数据源并发抓取统一汇总成候选列表第二阶段才是 Agent 处理按时间倒序排列逐条生成摘要和标签。为什么分开因为采集是 IO 密集型操作并发效率高而且不消耗模型 token处理是计算密集型操作依赖大模型如果混在一起跑一条慢数据源会导致整个流程卡住。分开之后任何一个阶段出问题另一个阶段都能独立重试。为了不让 Agent 处理时把流量全部打到一个模型接口我还做了简单的限流每处理五篇文章线程暂停两秒。这个设计对个人项目来说已经够用接口的限流阈值通常远高于我的需求。4.2 定时触发的三种可行方案定时调度我一开始用的是服务器上的 cron写一条30 8 * * *表达式每天八点半执行。后来项目搬到容器环境又改用 APScheduler 在 Python 进程里管理这样重试逻辑和异常上报都能写在同一个代码文件里排查问题时不用东翻西找。我整理一下三种方案的区别你们可以按自己的环境选择方案优点缺点适用场景系统 cron简单可靠不依赖应用代码异常处理和重试逻辑要自己写单机部署流程简单APScheduler可编程支持复杂调度进程内管理进程崩溃后会丢任务需配合守护进程常驻 Python 进程云函数定时触发免运维按量付费自带重试运行时长受限不适合超长任务轻量任务快速试错我最终用了 APScheduler因为项目里已经有常驻的 Python 进程挂一个调度器成本很低。设了一个misfire_grace_time参数正常运行不存在问题但如果服务器在八点半恰好重启任务错过触发时间后还能补一次避免当天日报直接缺席。4.3 日报格式化与多渠道分发Agent 把每条文章的摘要和标签生成完之后就到了分发环节。我先写了一个格式化函数把结果拼成一个适合在 IM 工具里阅读的 Markdown 文本顶部是日期和统计信息下面按分类分组列出文章标题、摘要和原文链接。格式化函数看起来不起眼但它是日报的门面如果排版混乱读者体验会非常差。推送渠道我接了两个飞书群机器人和邮件。飞书群机器人其实就是一个 Webhook往指定 URL POST 一段 JSON 就能发送消息支持 Markdown 格式日常使用很方便。邮件则用 SMTP 发送适合我这种想把日报归档到邮箱反复查看的习惯。两个渠道互备即使一个挂了还有另一个兜底。推送失败的情况我也做了处理。Webhook 偶尔返回 429 或 5xx我会用指数退避策略重试三次第一次等 5 秒第二次 20 秒第三次 60 秒。三次都失败就发一封告警邮件给自己说明哪一步失败、失败原因是什么。这比静默失败好得多至少你不会等到晚上才发现早上没收到日报。5. 常见问题与排查实录5.1 模型输出不稳定JSON 格式频繁翻车这是我在 Agent 开发里遇到最频繁的问题。模型有时候会在 JSON 前后加解释性文字有时候会把某个字段漏掉还有时候返回的标签不在预设集合里。针对这个问题我总结了一套组合拳一是在 Prompt 里给出只允许输出 JSON的强约束二是开启 JSON 模式三是在代码里做解析容错如果第一次解析失败用正则提取大括号内容再解析一次四是再失败就重启一次请求但这次在 Prompt 里补充一句上次返回格式错误请重新输出规范的 JSON。这套方案看起来很土但实测下来成功率能达到 98% 以上。剩下的 2% 也不会导致日报中断最多是某一条资讯没有摘要我依然会把标题和链接发出来。这里分享一个经验不要追求 100% 的模型输出准确率成本会指数级上升99% 可用配合正常的失败降级策略已经能保证业务稳定。5.2 采集超时与网页结构变化怎么优雅处理网页结构变化是抓取类项目的老大难。某个网站改版CSS 选择器失效采集模块就会静默地返回空列表日报里就会少一个信息源。我现在会在采集模块里加一个心跳检查机制每次抓取都记录返回的文章数量如果连续三天某个数据源返回数量为 0就向自己的告警渠道发一条消息提示我该检查了。这样虽然不能自动修复但至少不会一挂挂一周才发现。抓取超时也是常客。我用 httpx 设置了 10 秒超时超过就放弃该源并记录错误日志。如果某个源连续失败超过五次我会临时将它从本轮抓取中摘除避免整个流程被它拖垮。这个熔断思路是从微服务里借鉴过来的在个人项目里依然管用。5.3 成本控制哪些钱该花哪些钱不该花每次跑日报的成本大头是大模型摘要调用。为了压成本我用了几招。第一招文章正文做截断超过 2000 字的部分不送进模型因为资讯摘要只需要看开头部分就足够。第二招分类和评分用轻量模型完成只有最终摘要生成才使用更强的模型。第三招缓存结果同一篇 URL 在一个月内只处理一次即使它又出现在抓取列表里也不用再花 token。有人会问为什么不直接把摘要任务也交给轻量模型我试过效果差距比较大尤其是技术类资讯轻量模型常常丢关键细节。日报这类内容是给人看的质量比成本更敏感所以核心步骤坚决用强模型边缘步骤用轻量模型这个取舍大家可以根据自己的预算灵活调整。5.4 跑了一百多天后我总结出的 Agent 落地心得这个项目上线到现在稳定运行了一百多天每天的日报基本没断过。如果让我重新做一次我会在一开始就多看几个 Agent 框架但最终还是会更早地回到轻量自研这条路。因为对固定流程的自动化任务来说真正复杂的是对业务细节的理解和异常处理而不是 Agent 本身。日常使用中我发现模型输出的人工感还是差一些偶尔会把重要新闻放在很靠后的位置或者对某条资讯的理解有点偏。不过这并不影响使用日报的意义是帮我节省筛选时间而不是替代我做判断。我每天花两分钟扫一眼日报再挑有兴趣的原文深入阅读这个工作流已经比过去高效太多。最后再分享一个小技巧我把每次运行的结构化中间数据全部存了下来包括原始文章列表、摘要结果、分类统计。这些数据看起来只是日志但实际上是非常宝贵的语料库。比如我后来想做一个这个月行业热点趋势的功能直接把历史数据拿出来统计标签频次不费吹灰之力就实现了。所以做 Agent 项目时中间产物一定要留好便宜又重要。