基于AI Agent框架构建智能资讯聚合与推送系统
1. 项目概述:一个能帮你订阅和推送AI资讯的智能助手
如果你和我一样,每天都要花不少时间在微信、微博、推特和各种科技博客上“冲浪”,只为不错过AI领域的最新动态,那你肯定也想过:有没有一个工具,能把这些信息源都聚合起来,然后根据我的兴趣,每天定时把精华内容推给我?最近我在折腾一个叫ai-info-radar的开源项目,它正好就是干这个的。简单说,它是一个运行在OpenClawAI 智能体框架下的“技能”,专门用来发现、聚合和推送AI领域的新闻与媒体内容。
这个项目最吸引我的地方在于它的“接地气”。它不是一个简单的RSS阅读器,而是专门为AI这个垂直领域优化的。它内置了一个超过50个高质量信源的数据库,覆盖了从中文的机器之心、量子位,到英文的OpenAI官方博客、Lex Fridman播客,再到推特上的Karpathy、LeCun等大牛的动态。更关键的是,它针对不同平台(比如需要特殊访问方式的微信公众号、微博)设计了多套抓取策略,确保内容能稳定获取。你可以把它理解为一个24小时待命的AI资讯侦察兵,不仅能帮你“看”,还能根据你的背景(比如是技术小白还是产品经理)给你“推荐”,甚至能把你感兴趣的内容“收藏”起来,最后通过定时任务,把整理好的资讯摘要“推送”给你。整个过程,你只需要和你的AI助手用自然语言对话就能完成,比如“机器之心最近发了什么?”或者“设置每天上午9点给我推AI资讯”。
2. 核心设计思路与架构解析
2.1 为什么选择在AI Agent框架下实现?
ai-info-radar被设计成一个OpenClaw的“技能”(Skill),这背后有很实际的考量。OpenClaw是一个开源的AI智能体框架,它本身提供了对话管理、工具调用、记忆等基础能力。将资讯雷达做成一个技能,意味着它可以直接复用这些能力,我们不需要从零开始构建一个带界面的应用或复杂的后台服务。
核心优势在于交互的自然性。用户不需要学习新的命令或操作界面,直接对AI助手说“搜一下Agent最新进展”或“收藏这篇文章”,AI助手就能理解并调用ai-info-radar技能背后的工具函数来执行。这极大地降低了使用门槛,也让信息获取流程无缝嵌入到日常与AI助手的对话中。从技术实现上看,技能的本质是一组定义清晰的工具函数和对应的自然语言描述,OpenClaw的规划器(Planner)会根据用户意图自动匹配和调用。
2.2 多平台内容获取的“组合拳”策略
聚合资讯最大的技术挑战在于不同内容平台的异构性和反爬机制。ai-info-radar没有采用一刀切的方案,而是为每个平台类型设计了一套主备结合的“组合拳”策略,这是项目设计中最精妙的部分。
1. 分层抓取策略:对于标准化的内容源,如技术博客(OpenAI Blog, Anthropic Blog),优先使用RSS/Atom订阅。这是最规范、对服务器最友好的方式。对于播客(如小宇宙上的Latent Space),则通过解析播客平台的iTunes RSS格式来获取节目列表和元数据,音频内容本身则可能通过faster-whisper这类工具进行转录后摘要。
2. 针对特殊平台的策略:
- 微信公众号:这是中文AI资讯的重镇,但也是抓取难点。项目采用了
Camoufox(一个模拟浏览器环境的工具)结合HTTP代理的方式来访问公众号文章。这是因为公众号内容通常需要微信内打开或登录态,直接请求很难获取。 - Twitter/X:官方API限制严格。项目采用了获取访客令牌(Guest Token)后调用其内部GraphQL接口的方法。这种方式比模拟登录更轻量,但需要处理令牌的定期刷新。
- 微博:同样需要登录态。项目使用
Playwright这类无头浏览器自动化工具,模拟真实用户访问并获取访客Cookie,然后用这个Cookie去请求数据接口。
3. 兜底与降级方案:当上述特定方法失效时,系统会启动降级策略。其核心是一个多工具回退链:首先尝试使用更强大的agent-reach工具(如果配置了),它可能集成了更复杂的反反爬能力;如果不成功,则回退到通用的Jina Search API(一个专注于AI领域的搜索引擎API)进行搜索;最后,作为终极手段,使用最通用的网页抓取(web_fetch)工具,直接下载网页并解析关键内容。这种设计保证了服务的鲁棒性,单一平台接口变化不会导致整个功能瘫痪。
2.3 基于用户画像的智能推荐引擎
单纯的聚合只是信息的堆砌。ai-info-radar的另一个核心是它的推荐系统。它内置的资源数据库(data/resources.json)不仅记录了信源的URL,还为每个信源打上了丰富的标签,例如:
- 受众标签:
beginner(入门),technical(技术),product(产品),research(研究) - 语言标签:
chinese,english - 内容类型标签:
news(新闻),tutorial(教程),paper(论文),podcast(播客),opinion(观点)
当用户请求“适合小白的AI资源”时,技能会调用resource_db.py中的查询逻辑,筛选出标签包含beginner且语言符合用户偏好的信源。更进一步,它还可以结合用户的对话历史(如果OpenClaw框架提供了此类记忆),分析用户常关注的技术关键词(如“LLM”、“Agent”、“Diffusion”),在推荐时进行加权,让推荐结果越来越个性化。这个推荐逻辑相对轻量,但非常实用,它让工具从“广播”变成了“窄播”。
3. 核心功能模块深度拆解
3.1 内容获取器:一个应对复杂网络环境的瑞士军刀
content_fetcher.py是这个项目的中枢神经。它不是一个简单的函数,而是一个精心设计的、面向多平台的内容获取调度器。
其工作流程如下:
- 输入解析:接收一个资源对象(来自
resource_db.py),其中包含了资源类型(type,如wechat,twitter,blog)、源URL以及其他元数据。 - 策略路由:根据
resource.type选择对应的抓取函数。例如,type=blog且feed_url存在,则路由到_fetch_via_rss函数。 - 平台特异性处理:
- RSS抓取:使用
feedparser库解析。这里需要注意处理各种非标准的RSS格式兼容性问题,比如日期格式解析错误、HTML实体编码等。代码中通常会包含try-except块和字段回退逻辑。 - 播客抓取:除了获取RSS中的节目列表,对于音频内容,可能会触发一个异步任务,使用
faster-whisper(一个优化的Whisper语音识别模型实现)对最新一期节目进行本地转录,然后利用大模型对转录文本进行摘要,生成文字版精华内容。这个过程虽然耗时,但对于信息吸收效率提升巨大。 - 动态网页抓取:对于微博、Twitter,抓取函数会管理一个“会话”对象,负责维护和更新
guest token或cookie。这里有一个重要技巧:为了避免频繁触发平台的风控,需要模拟人类行为,如在请求间添加随机延时、使用轮换的User-Agent字符串,并将有效的令牌持久化到本地文件,下次启动时复用。
- RSS抓取:使用
- 统一输出格式化:无论来自哪个平台,抓取到的原始内容(标题、链接、发布时间、摘要/正文片段)都会被清洗并转换成内部统一的数据结构(通常是一个Python字典列表),传递给下一个环节——卡片格式化器。
注意:涉及微信公众号、微博等平台的抓取行为,务必在法律法规和平台服务条款允许的范围内进行,仅限于个人、非商业、低频率的合理使用。切勿用于大规模爬取或商业用途,以免对目标服务器造成压力或引发法律风险。
3.2 书签管理与推送调度:你的个人AI资讯库
bookmark_manager.py和schedule_manager.py这两个模块负责信息的“沉淀”与“投递”,让信息流形成闭环。
书签管理的实现相对直观。它通常在用户目录下(如~/.ai-info-radar/)维护一个favorites.json文件。当用户对某条资讯说“收藏这个”时,AI助手会解析出当前对话上下文中的资讯条目(唯一ID、标题、链接等),然后调用书签工具的“添加”函数。这个函数需要做两件事:一是去重检查,避免同一内容重复收藏;二是将新的收藏项以结构化的格式(如包含收藏时间、标签、用户备注字段)追加到JSON文件中。此外,工具还应该提供“列出”、“删除”和“搜索”书签的功能,这些都可以通过简单的JSON文件读写和列表操作实现。
推送调度管理器则是基于时间的自动化核心。它依赖系统的cron服务(在Linux/macOS上)或计划任务(在Windows上)。schedule_manager.py的核心功能是生成和管理cron任务。
- 配置解析:用户通过自然语言设置,如“每天9点推送”,会被解析成一个配置对象,包括频率(
daily)、时间(09:00)、时区、以及可选的过滤条件(只推某个标签,如llm的内容)。 - Cron表达式生成:将配置转换为标准的cron表达式。例如,每天9点就是
0 9 * * *。 - 任务脚本生成:创建一个独立的Python脚本或Shell脚本,这个脚本的核心是调用
ai_info_radar.py的主逻辑,并传入用户的过滤条件。这个脚本的路径会被注册到cron中。 - 任务管理:提供“查看当前推送设置”和“取消推送”的功能。这通常通过读取和删除cron配置行来实现。一个高级技巧是,在生成的执行脚本开头,可以加入日志记录功能,将每次推送的执行结果和获取到的内容条目数记录到日志文件,便于后期排查问题。
3.3 卡片格式化器:从数据到可读摘要
card_formatter.py负责将原始数据“包装”成适合阅读和推送的格式。它的输入是内容获取器产出的原始条目列表,输出是结构化的文本或Markdown。
其核心任务包括:
- 信息提取与精简:对于抓取到的完整文章摘要或长文本,需要使用文本处理技术(如基于规则或简单模型)提取出核心句段,生成一个80-150字的简要摘要。
- 标签化与分类:根据内容关键词,自动或半自动地打上内容标签(如
#大模型,#AIGC,#行业动态),这有助于用户在推送中快速筛选感兴趣的主题。 - 格式化排版:按照固定的卡片模板进行渲染。一个典型的卡片可能如下所示:
这种格式在即时通讯软件或笔记应用中阅读起来非常清晰。格式化器需要处理好换行、缩进,并确保链接是可点击的(在支持Markdown或HTML的推送渠道中)。【机器之心】 🔖 #模型发布 #开源 | 标题:Meta发布新一代Llama 3模型,性能全面超越GPT-4? | 摘要:Meta今日正式推出Llama 3 70B和405B两个版本。据其公布的基准测试显示,Llama 3 405B在多项任务上超越了GPT-4 Turbo...(此处为生成的简要摘要) | 链接:https://example.com/article/123 | 时间:2024-04-15 10:30
4. 从零开始部署与深度配置指南
4.1 基础环境搭建与依赖安装
假设你已经有一个可用的OpenClaw环境(如果还没有,需要先根据其官方文档进行安装),以下是部署ai-info-radar技能的详细步骤。
步骤一:获取技能代码推荐使用OpenClaw自带的技能包管理工具ClawHub进行安装,这能自动处理路径问题。
# 在OpenClaw的运行环境中执行 openclaw skill install ai-info-radar如果网络问题导致安装失败,或者你想使用开发中的版本,可以手动克隆:
# 切换到OpenClaw的技能目录,通常位于用户目录下 cd ~/.openclaw/skills/ # 克隆仓库 git clone https://github.com/rrrrrredy/ai-info-radar.git步骤二:安装Python依赖项目根目录下通常会有requirements.txt或通过setup.sh脚本安装。
# 进入技能目录 cd ai-info-radar # 运行安装脚本 bash scripts/setup.shsetup.sh脚本内部可能执行如下操作:
#!/bin/bash # scripts/setup.sh 示例内容 pip install feedparser requests playwright # 安装Playwright所需的浏览器内核 playwright install chromium # 如果有其他特定依赖,如音频处理库 # pip install faster-whisper实操心得:在运行
playwright install时,由于需要下载浏览器,国内网络环境可能会非常慢甚至失败。建议先配置好科学的上网环境,或者使用国内镜像源。一个备选方案是,如果暂时不需要微博抓取功能,可以注释掉相关代码,跳过Playwright的安装。
步骤三:验证安装启动你的OpenClawAI助手(具体启动命令取决于你的部署方式,可能是openclaw start或运行一个Python脚本),然后尝试与助手对话,例如输入“AI媒体推荐”。如果助手能正确理解并调用技能,返回一个资源列表,说明基础安装成功。
4.2 关键配置详解与调优
安装成功后,为了让所有功能正常工作,尤其是内容抓取,需要进行一些关键配置。
1. 代理配置(针对微信公众号等)由于部分信源访问受限,你需要在环境变量或代码中配置HTTP代理。
# 在启动OpenClaw的环境前,设置环境变量(Linux/macOS) export HTTP_PROXY="http://your-proxy-address:port" export HTTPS_PROXY="http://your-proxy-address:port"在content_fetcher.py中,requests库会默认读取这些环境变量。对于Playwright,配置方式略有不同,需要在创建浏览器上下文时指定:
# 在 content_fetcher.py 的 Weibo 抓取函数中可能存在的代码片段 browser = await playwright.chromium.launch() context = await browser.new_context( proxy={"server": "http://your-proxy-address:port"} )2. 资源数据库自定义内置的50+信源可能不完全符合你的口味。你可以编辑data/resources.json文件来增删改查。
- 添加一个新博客:在JSON数组中新增一个对象,确保
name,url,feed_url(如果有RSS),type(如blog), 以及tags字段正确。 - 修改标签:你可以调整现有信源的
tags,使其更符合你的分类习惯。例如,给一个偏重理论的博客加上research标签,给一个讲实操的公众号加上tutorial标签。 - 禁用某个源:可以暂时将
enabled字段设为false(如果数据结构支持),或者直接注释掉该条目。
3. 推送定时任务配置当你通过AI助手命令(如“设置每天上午10点推送”)配置推送后,schedule_manager.py会在你的系统cron中创建一条记录。你可以通过crontab -l命令查看。生成的命令可能类似于:
0 10 * * * /usr/bin/python3 /home/username/.openclaw/skills/ai-info-radar/scripts/run_digest.py --user default --filter tag:llm你可以手动编辑这条cron任务来调整时间或参数,但更建议通过AI助手命令来管理,以避免格式错误。
4.3 高级功能:集成自定义信源与消息推送
集成自定义信源:除了修改resources.json,对于没有标准RSS的复杂网站,你可能需要编写自定义的抓取函数。
- 在
content_fetcher.py中,仿照现有的_fetch_via_web函数,编写一个新函数_fetch_my_custom_site(url)。 - 这个函数里,你可能需要使用
requests-html或BeautifulSoup进行更复杂的HTML解析。 - 在
resource_db.py中,为你自定义的资源指定一个独特的type,比如custom。 - 在
content_fetcher.py的策略路由部分,添加一个判断:如果resource.type == 'custom',则调用你刚写的_fetch_my_custom_site函数。 - 最后,在
resources.json中添加你的新资源,并将type设置为custom。
扩展推送渠道:默认的推送可能是在AI助手对话界面中显示。你可以将其扩展至其他平台,如电子邮件、Telegram Bot或飞书Webhook。
- 在
card_formatter.py中,除了生成文本卡片,可以增加一个函数format_for_email(articles)或format_for_telegram(articles),生成对应平台所需的格式(如HTML邮件、Telegram Markdown)。 - 修改
schedule_manager.py中生成的执行脚本(或创建一个新的推送执行器),在获取并格式化内容后,调用相应的发送函数。 - 发送函数需要你配置接收方的密钥或地址(如邮件SMTP信息、Telegram Bot Token),这些敏感信息应存储在环境变量或单独的配置文件中,不要硬编码在代码里。
5. 常见问题排查与实战经验分享
在实际部署和运行ai-info-radar的过程中,你肯定会遇到一些“坑”。下面是我总结的一些典型问题及其解决方案。
5.1 内容抓取失败问题排查
问题一:微信公众号/微博抓取返回空数据或403错误。
- 原因:这是最常见的问题。平台的反爬机制升级,导致原有的访客令牌(Guest Token)或Cookie失效,或IP地址被限制。
- 排查步骤:
- 检查代理:首先确认你的HTTP代理是否有效且速度尚可。可以通过
curl -x http://your-proxy:port https://httpbin.org/ip测试代理是否正常工作并返回了代理IP。 - 更新令牌/Cookie:对于微博/Twitter,尝试清除本地存储的旧令牌文件(位置通常在
~/.ai-info-radar/cache/下),让程序重新获取。对于Playwright,可以尝试增加user_agent的随机性,并使用--no-sandbox等启动参数。 - 降低频率:在代码中增加请求之间的随机延时(例如
time.sleep(random.uniform(5, 15))),模拟人类浏览行为。 - 验证工具:手动在配置了相同代理的浏览器中访问目标公众号或微博,看是否能正常打开。如果不能,说明是网络或账号封锁问题,而非代码问题。
- 检查代理:首先确认你的HTTP代理是否有效且速度尚可。可以通过
问题二:RSS源解析出错,报KeyError或日期解析错误。
- 原因:不同网站的RSS格式千差万别,
feedparser虽然兼容性好,但某些非标准字段可能导致程序逻辑出错。 - 解决方案:在
content_fetcher.py的RSS解析函数中,对关键字段(如entry.title,entry.link,entry.published)进行严格的异常捕获和回退处理。try: title = entry.title except AttributeError: title = "无标题" try: # 尝试多种可能的日期字段 published = entry.get('published_parsed') or entry.get('updated_parsed') if published: pub_date = time.strftime('%Y-%m-%d %H:%M', published) else: pub_date = "日期未知" except Exception: pub_date = "日期未知"
5.2 推送功能不执行或异常
问题:Cron任务设置了但从未执行,或者执行了但没收到推送。
- 排查步骤:
- 检查Cron服务:确保系统cron服务正在运行(
sudo systemctl status cron或crond)。 - 检查Cron日志:查看系统cron日志(
/var/log/cron或grep CRON /var/log/syslog),看任务是否被触发以及是否有错误输出。 - 检查执行权限:确保
run_digest.py脚本有可执行权限(chmod +x /path/to/run_digest.py),并且脚本首行的shebang(#!/usr/bin/env python3)正确。 - 检查环境变量:Cron执行的环境与用户Shell环境不同,可能缺少关键的
PATH或PYTHONPATH。一个可靠的方法是在cron命令中显式设置环境,或者将命令写在一个包装脚本(shell script)里,在脚本中导出所需环境变量。 - 手动测试脚本:直接在终端运行cron命令中的完整命令,观察输出和错误信息。这是最直接的调试方法。
- 检查Cron服务:确保系统cron服务正在运行(
5.3 性能优化与资源管理
问题:抓取大量源时速度慢,或内存占用高。
- 优化建议:
- 异步抓取:将
content_fetcher.py中的抓取函数改造成异步(使用asyncio和aiohttp),可以并发抓取多个源,极大缩短总耗时。 - 缓存策略:对抓取到的内容进行哈希,并与本地缓存对比。如果内容未更新,则直接使用缓存,避免重复处理。可以为每个源设置一个合理的缓存过期时间(如10分钟)。
- 增量抓取:对于支持按时间查询的API或RSS,记录上次抓取的最后时间戳,下次只请求此时间之后的内容。
- 资源限制:对于使用Playwright的抓取,确保在每次抓取完成后正确关闭浏览器上下文和实例(
await context.close(),await browser.close()),防止浏览器进程残留。
- 异步抓取:将
一个实战技巧:处理音频播客的实用方法对于播客源,完整的转录和摘要非常耗时。一个折中的生产级方案是:
- 只对最新一期(或用户标记感兴趣的特定期数)进行转录摘要。
- 使用
faster-whisper的small或medium模型,在保证一定准确率的同时提升速度。可以考虑在拥有GPU的机器上运行。 - 将转录摘要任务放入一个独立的低优先级队列(例如使用
Celery或RQ),避免阻塞主抓取流程。摘要完成后,将结果存储到数据库或文件,下次请求时直接读取。 - 作为备选,可以优先使用播客节目自带的文字描述(Shownotes),这通常已经包含了内容要点,虽然不如全文摘要详细,但获取成本极低。
