发布时间:2026/9/2 18:46:08
Agentic RSS阅读器:基于本地Embedding与小模型的个性化信息筛选系统 先聊一个很实际的场景你每天打开微信、微博、知乎刷到大量信息流但真正想看的深度技术内容反而被淹没在碎片里。RSS 阅读器其实就是对抗这种信息过载的老牌方案但传统 RSS 阅读器只能“按时间顺序展示全文”它不理解你关注什么、不判断内容价值、也不知道哪些文章其实和昨天读过的重复。这两年 Agentic、Agentic RAG、本地小模型这些概念越来越热很多人开始把 AI 决策能力引入个人知识管理工具。RSSMonster 正是这样一个方向一个基于本地 embeddings 和小模型的 agentic RSS 阅读器它不只帮你聚合信息还会自己判断“这条内容值不值得读、和你的兴趣匹配度有多高、和已读文章是否重复”最后输出一份真正个性化的阅读简报。这篇文章会从核心概念讲起拆解 RSSMonster 的整体架构然后给出一个可以在本地跑起来的完整实现包含 RSS 抓取、文本向量化、小模型摘要、Agent 决策、SQLite 持久化以及最后的排错清单和工程建议。无论你是想给个人博客做信息聚合还是想为团队做一个内部信息监控工具这套方案都能直接借鉴。读完之后你会理解什么是 agentic 的 RSS 阅读器也能自己动手改造它。1. 背景与核心概念1.1 RSS 阅读器为什么还需要被“重新发明”RSS 本身并不复杂它是一套 XML 格式的订阅协议站点发布结构化内容阅读器定时拉取并解析。很多开发者对 RSS 的印象停留在“上古互联网工具”但实际上直到今天RSS 依然是跟踪开源项目、博客、论文预印本、安全公告最高效的方式因为它不会被推荐算法干扰也是站方主动推送的完整数据源。传统 RSS 阅读器的问题在于“管道化”抓取 → 展示它把所有压力都转嫁给了读者。当你的 feed 数量超过 30 个每天未读可能就有几百条手动筛选要消耗大量时间。大部分阅读器提供的“文件夹/标签”又是静态的需要用户手动归类。还有一个容易被忽略的痛点重复内容。同一篇技术新闻会被多个公众号、科技博客转载传统阅读器无法识别这种语义级重复只能靠用户肉眼判断。这就引申出一个新的需求阅读器能不能像一位懂行的编辑那样先读一遍所有内容再决定推送哪些给你这正好是 AI Agent 的用武之地。1.2 Agentic RSS 是什么Agentic 这个词在 2025 年已经成为 AI 领域的高频热词围绕它延伸出了 Agentic RAG、Agentic RL基于强化学习的智能体训练等方向。Agentic 系统与传统自动化脚本的关键区别在于是否具备“自主决策能力”。在一个 Agentic 系统里程序不再只是执行预先写好的 if-else 或定时任务而是按照“感知 → 规划 → 行动 → 反思”的循环来工作。它可以基于当前任务状态动态选择工具、调整执行策略、评估结果质量。放到 RSS 阅读器这个场景“Agentic”意味着系统要完成以下四层决策决策层作用传统 RSS 阅读器去重层判断新条目是否与历史条目语义重复无兴趣层判断条目与用户兴趣主题的匹配程度无只能按文件夹手动分类内容层自动生成摘要、提取关键词无只展示原文排序层动态决定阅读优先级按发布时间排序这是 RSSMonster 这类工具的核心竞争力它把“读者阅读前要做的工作”前置到了系统内部完成你只需要定义自己的兴趣系统会替你完成筛选、去重、总结和排序。1.3 为什么选择本地 Embeddings 小模型很多人会问既然要做语义筛选和摘要为什么不用大模型的在线 API原因很实际隐私RSS 内容里可能包含公司内部博客、未公开的产品动态。将内容发送到外部 API 会带来数据泄露风险。本地推理可以让整个管道完全离线运行。成本如果每天抓取几百条 RSS 内容每条都调用在线大模型做摘要成本会持续累积。而本地小模型跑在 CPU 或入门级 GPU 上边际成本几乎为零。速度Local Embeddings 模型通常只有几百 MB小模型0.5B~3B 参数在普通笔记本上也能达到可接受的推理速度。相比在线 API 动辄数秒的响应本地小模型在简单任务上更快、更稳定。RSSMonster 的思路是“大小模型分工”轻量级 embedding 模型负责把文本映射成向量用于相似度计算和兴趣匹配小参数 LLM 负责生成摘要、分类标签等需要语义理解的任务。embedding 模型速度快、占用低适合处理批量内容LLM 虽然更重但只在最终筛选出的少量条目上运行整体开销可控。2. 环境准备与版本说明2.1 运行环境RSSMonster 是一个 Python 项目建议在以下环境中运行项目建议配置操作系统Windows 10/11、Ubuntu 20.04、macOS 12Python3.10 及以上版本内存至少 8GB如果加载 embedding 模型 小模型16GB 更稳妥硬盘预留 3GB 以上空间存放模型网络首次需要下载模型权重之后可离线运行示例代码以 CPU 运行为主如果你的机器有 CUDA 显卡可以在加载模型时把device改成cuda推理速度会明显提升。2.2 依赖清单项目主要依赖以下库feedparser6.0.11 beautifulsoup44.12.3 lxml5.3.0 PyYAML6.0.2 sentence-transformers3.0.1 numpy1.26.4 requests2.32.3版本号可根据你本地环境适当调整重点是sentence-transformers与numpy之间不要出现兼容性冲突。如果安装较慢可以给 pip 配置国内镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt2.3 推荐目录结构为了方便后续扩展建议把代码按职责拆分rssmonster/ ├── config.yaml ├── requirements.txt ├── main.py └── rssmonster/ ├── __init__.py ├── config.py ├── fetch.py ├── clean.py ├── embed.py ├── summarize.py ├── agent.py └── store.py每个模块只负责一件事后面单独替换模型、增加数据源时不会牵一发动全身。3. 核心原理拆解3.1 RSS 抓取与文本归一化RSS 抓取的第一步是解析 feed。Python 生态里 feedparser 是事实标准它能处理 RSS 2.0、Atom 等主流格式。但有两点容易被忽略第一是请求头。很多站点会拦截没有User-Agent的爬虫请求所以需要伪装成正常浏览器。第二是 HTML 清洗。RSS 的summary字段通常直接包含 HTML 标签比如p本文介绍 a hrefhttps://example.comRAG/a 的本地部署方案。/p如果直接把这个字段拿去做向量化和摘要文本中会混入大量无意义标签噪音语义向量质量会明显下降。因此必须在入口处做一次统一的文本清洗# 文件路径rssmonster/clean.py from bs4 import BeautifulSoup def clean_html(raw_html: str) - str: 把 HTML 片段转换为纯文本并压缩多余空白。 if not raw_html: return soup BeautifulSoup(raw_html, lxml) text soup.get_text(separator ) return .join(text.split())这里用BeautifulSoup解析 HTMLget_text提取可见文本最后把连续的空白字符压缩为单个空格。清洗后的文本会作为后续所有 AI 模块的输入。3.2 本地 Embeddings 向量化Embedding 模型的作用是“把文本变成向量”。在 RSSMonster 中向量化主要有两个用途语义去重把新条目与历史条目分别编码为向量通过余弦相似度判断是否重复。即使两篇文章标题完全不一样只要正文描述的是同一事件向量依然会很接近。兴趣匹配把你的兴趣主题比如“本地部署 AI”也编码成向量然后计算每条内容与兴趣向量的相似度。选择 Embedding 模型时all-MiniLM-L6-v2这类小模型是非常合适的第一个选择。它只有约 80MB384 维向量在 CPU 上编码一条文本只需要几十毫秒且支持中文场景。如果你的内容以中文为主也可以换成BAAI/bge-small-zh-v1.5量化后体积同样很小。加载模型和计算向量的代码非常简洁# 文件路径rssmonster/embed.py import numpy as np from sentence_transformers import SentenceTransformer class LocalEmbedder: 本地 Embedding 模型封装负责文本向量化和相似度计算。 def __init__(self, model_name: str sentence-transformers/all-MiniLM-L6-v2, device: str cpu): self.model SentenceTransformer(model_name, devicedevice) def embed_texts(self, texts: list[str]) - np.ndarray: 将文本列表转为归一化向量矩阵。 return self.model.encode( texts, normalize_embeddingsTrue, show_progress_barFalse ) staticmethod def cosine_similarity(vec_a: np.ndarray, vec_b: np.ndarray) - float: 计算两个向量的余弦相似度。 因为向量已经归一化这里的点积就是余弦相似度。 return float(np.dot(vec_a, vec_b))这里有一个非常重要的细节normalize_embeddingsTrue。归一化之后每个向量的模长为 1此时两个向量的点积就是它们之间的余弦相似度省去了一次除法运算批量计算时性能更好。3.3 小模型做摘要与分类传统的文本摘要可以直接抽取文章前几句话但质量不稳定。使用本地小模型可以生成更精炼、更符合阅读习惯的摘要。以 Ollama 作为推理后端为例这类工具可以在本机拉起一个兼容 OpenAI 风格的 API 服务加载 1B~3B 参数的指令微调模型。轻量级模型在 CPU 上生成的摘要质量虽然不如大模型但对“帮我判断这篇文章讲什么”这种任务完全够用。调用 Ollama 的核心代码如下# 文件路径rssmonster/summarize.py import requests def summarize_with_ollama(text: str, base_url: str http://localhost:11434, model: str qwen2.5:1.5b, max_tokens: int 200) - str: 调用本地 Ollama 服务生成摘要。 模型名称以你本地拉取的模型为准例如 qwen2.5:1.5b、phi3:mini。 # 控制输入长度避免超出小模型上下文窗口 prompt f请用中文为下面这篇内容生成 3-5 句话摘要\n\n{text[:3000]} resp requests.post( f{base_url}/api/generate, json{ model: model, prompt: prompt, stream: False, options: {num_predict: max_tokens} }, timeout120, ) resp.raise_for_status() return resp.json().get(response, ).strip()注意小模型的上下文窗口通常不会太大。抓取到的正文如果过长直接塞给模型会超出限制或者导致注意力涣散所以代码中对输入做了截断。实际项目中更稳妥的做法是先用摘要算法比如 TextRank抽取关键句再把关键句交给小模型生成摘要。这个问题我们在最佳实践一节还会讨论。3.4 Agent 决策层感知、决策、行动RSSMonster 的 Agent 决策层是整篇文章的核心。它不像传统管道那样“来什么展示什么”而是对每条内容做一个完整的决策链路。决策流程可以拆成四步感知对每一条 RSS 条目做向量化得到它的语义表示。去重与历史条目计算相似度如果和已读内容相似度超过阈值直接丢弃。兴趣匹配计算条目与兴趣向量的相似度。低于阈值的代表“大概率不是你想看的”可以过滤掉。编排执行对通过筛选的条目调用小模型生成摘要、判断优先级最终写入数据库并输出日报。为了让 Agent 决策更清晰这个模块会把“判断逻辑”集中封装在一个类里# 文件路径rssmonster/agent.py import numpy as np from rssmonster.embed import LocalEmbedder class RSSAgent: 负责对 RSS 条目做去重、兴趣过滤和优先级排序。 def __init__(self, interests: list[str], embedder: LocalEmbedder, dedup_threshold: float 0.92, interest_threshold: float 0.25): self.embedder embedder self.dedup_threshold dedup_threshold self.interest_threshold interest_threshold # 把兴趣主题预先向量化 self.interest_vectors embedder.embed_texts(interests) def is_duplicate(self, item_vector: np.ndarray, history_vectors: list[np.ndarray]) - bool: 判断当前条目与历史条目是否语义重复。 if not history_vectors: return False sims [self.embedder.cosine_similarity(item_vector, vec) for vec in history_vectors] return max(sims) self.dedup_threshold def interest_score(self, item_vector: np.ndarray) - float: 计算条目与所有兴趣主题的最高相似度。 if len(self.interest_vectors) 0: return 1.0 scores [self.embedder.cosine_similarity(item_vector, vec) for vec in self.interest_vectors] return max(scores) def decide(self, item_vector: np.ndarray, history_vectors: list[np.ndarray]) - str: 输出决策结果 - duplicate: 语义重复 - filtered: 兴趣匹配度太低 - accepted: 通过筛选 if self.is_duplicate(item_vector, history_vectors): return duplicate if self.interest_score(item_vector) self.interest_threshold: return filtered return accepted这段代码最需要注意的思维转变是Agent 并不总是返回“通过”它还可以返回明确的拒绝理由。有了duplicate和filtered这两种中间状态我们就可以在日志里统计每次运行过滤了多少重复内容方便后期调优阈值。4. 完整实战搭建 RSSMonster下面我们把前面的模块组装成一个可以运行的完整项目。假设场景是我关注“本地部署 AI”“RAG 应用”“开源工具”三个主题希望通过 RSS 每天自动筛选最相关的技术文章并生成一份 Markdown 日报。4.1 创建项目结构和配置文件先在终端创建项目目录mkdir rssmonster cd rssmonster mkdir rssmonster touch rssmonster/__init__.py然后创建config.yaml。这个文件集中管理所有可调参数避免把配置散落在代码里# 文件路径config.yaml feeds: - https://www.ruanyifeng.com/blog/atom.xml - https://feeds.feedburner.com/TechCrunch - https://news.ycombinator.com/rss embedding: model_name: sentence-transformers/all-MiniLM-L6-v2 device: cpu summarizer: enabled: true engine: ollama base_url: http://localhost:11434 model: qwen2.5:1.5b max_tokens: 200 agent: interests: - 本地部署 AI 模型与 Agent 应用 - RAG 检索增强生成与向量数据库 - 开源 RSS 阅读器与信息管理工具 dedup_threshold: 0.92 interest_threshold: 0.25 max_items_per_run: 10 storage: db_path: data/rssmonster.db output_dir: outputfeeds列表我写的是几个常见的示例源实际使用时请替换成你自己订阅的 RSS 地址。本项目演示用的 RSS 源可以通过https://news.ycombinator.com/rss这类公共源获取无需任何 API Key。4.2 编写配置加载模块配置文件写好后需要一个模块负责把 YAML 转成 Python 字典# 文件路径rssmonster/config.py import yaml def load_config(path: str config.yaml) - dict: 加载 YAML 配置文件。 with open(path, r, encodingutf-8) as f: return yaml.safe_load(f)这段代码很简单但把配置集中管理的价值在后面会体现出来——调整兴趣主题、修改去重阈值都不需要改动业务代码。4.3 RSS 抓取模块在fetch.py里封装一个函数负责抓取并清洗所有源# 文件路径rssmonster/fetch.py import feedparser import requests from rssmonster.clean import clean_html def fetch_feed(feed_url: str) - list[dict]: 抓取单个 RSS 源返回标准化条目列表。 headers {User-Agent: RSSMonster/0.1 (Local Agentic Reader)} resp requests.get(feed_url, headersheaders, timeout15) resp.raise_for_status() parsed feedparser.parse(resp.content) items [] for entry in parsed.entries: title entry.get(title, ).strip() link entry.get(link, ) summary clean_html(entry.get(summary, )) if not title or not link: continue items.append({ title: title, link: link, summary: summary, published: entry.get(published, ), source: feed_url, }) return items def fetch_all_feeds(feed_urls: list[str]) - list[dict]: 抓取所有源并把结果合并为一个列表。 all_items [] for url in feed_urls: try: items fetch_feed(url) print(f[fetch] {url} 获取到 {len(items)} 条内容) all_items.extend(items) except Exception as exc: print(f[fetch] {url} 抓取失败: {exc}) return all_items这个模块做了两层保护一是单条源抓取失败不会影响其他源二是对返回的条目做了字段清洗避免脏数据进入 AI 管道。4.4 向量化模块实际使用时直接使用之前写好的LocalEmbedder类即可。为了后续便于调试可以再加一个批量向量化的函数# 文件路径rssmonster/embed.py import numpy as np from sentence_transformers import SentenceTransformer class LocalEmbedder: 本地 Embedding 模型封装负责文本向量化和相似度计算。 def __init__(self, model_name: str sentence-transformers/all-MiniLM-L6-v2, device: str cpu): self.model SentenceTransformer(model_name, devicedevice) def embed_texts(self, texts: list[str]) - np.ndarray: 将文本列表转为归一化向量矩阵。 return self.model.encode( texts, normalize_embeddingsTrue, show_progress_barFalse ) staticmethod def cosine_similarity(vec_a: np.ndarray, vec_b: np.ndarray) - float: 计算两个向量的余弦相似度向量需为归一化向量。 return float(np.dot(vec_a, vec_b))向量化是整个系统的地基这部分代码虽然看起来短但质量直接决定去重和兴趣匹配的效果。4.5 存储模块为了支持语义去重我们必须把历史条目的向量持久化。选用 SQLite 是因为零部署、单文件、适合个人工具。向量可以以 BLOB 形式存储读取时再反序列化回numpy.ndarray。# 文件路径rssmonster/store.py import sqlite3 import numpy as np from pathlib import Path class SQLiteStore: 基于 SQLite 的条目与向量存储。 def __init__(self, db_path: str): self.db_path db_path Path(db_path).parent.mkdir(parentsTrue, exist_okTrue) self.conn sqlite3.connect(db_path) self._init_table() def _init_table(self): self.conn.execute( CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, link TEXT NOT NULL UNIQUE, summary TEXT, source TEXT, published TEXT, vector BLOB NOT NULL, interest_score REAL, summary_text TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) ) self.conn.commit() def insert_item(self, item: dict, vector: np.ndarray, interest_score: float, summary_text: str): self.conn.execute( INSERT OR IGNORE INTO items (title, link, summary, source, published, vector, interest_score, summary_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?) , (item[title], item[link], item[summary], item[source], item[published], vector.astype(np.float32).tobytes(), interest_score, summary_text) ) self.conn.commit() def get_all_vectors(self) - list[np.ndarray]: 读取全部历史向量用于去重判断。 rows self.conn.execute(SELECT vector FROM items).fetchall() return [np.frombuffer(row[0], dtypenp.float32) for row in rows] def still_unread(self, item: dict) - bool: 判断某条链接是否已入库。 row self.conn.execute( SELECT 1 FROM items WHERE link ?, (item[link],) ).fetchone() return row is None这里有个设计细节INSERT OR IGNORE配合link的唯一索引可以防止同一篇文章被再次插入语义去重则通过get_all_vectors来判断两者是互补关系——前者防止 URL 级重复后者防止内容级重复。4.6 Agent 主流程现在把 Agent 决策和主流程串起来。先补全agent.py的RSSAgent类然后在main.py里统一调度。agent.py里除了判断逻辑还需要一个“筛选并排序”的方法# 文件路径rssmonster/agent.py import numpy as np from rssmonster.embed import LocalEmbedder class RSSAgent: 负责对 RSS 条目做去重、兴趣过滤和优先级排序。 def __init__(self, interests: list[str], embedder: LocalEmbedder, dedup_threshold: float 0.92, interest_threshold: float 0.25): self.embedder embedder self.dedup_threshold dedup_threshold self.interest_threshold interest_threshold self.interest_vectors embedder.embed_texts(interests) def is_duplicate(self, item_vector: np.ndarray, history_vectors: list[np.ndarray]) - bool: 判断当前条目与历史条目是否语义重复。 if not history_vectors: return False sims [self.embedder.cosine_similarity(item_vector, vec) for vec in history_vectors] return max(sims) self.dedup_threshold def interest_score(self, item_vector: np.ndarray) - float: 计算条目与所有兴趣主题的最高相似度。 if len(self.interest_vectors) 0: return 1.0 scores [self.embedder.cosine_similarity(item_vector, vec) for vec in self.interest_vectors] return max(scores) def rank_items(self, items: list[dict], vectors: list[np.ndarray]) - list[dict]: 为每条内容计算兴趣分并按分数倒序排列。 实际是否接受由主流程根据分数阈值决定。 scored [] for item, vec in zip(items, vectors): score self.interest_score(vec) item[interest_score] round(score, 4) scored.append(item) scored.sort(keylambda x: x[interest_score], reverseTrue) return scoredmain.py是整个项目的入口它负责把抓取、嵌入、决策、摘要、存储串成一个完整闭环# 文件路径main.py from rssmonster.config import load_config from rssmonster.fetch import fetch_all_feeds from rssmonster.embed import LocalEmbedder from rssmonster.agent import RSSAgent from rssmonster.summarize import summarize_with_ollama from rssmonster.store import SQLiteStore def main(): cfg load_config(config.yaml) # 1. 初始化所有组件 embedder LocalEmbedder( model_namecfg[embedding][model_name], devicecfg[embedding][device] ) agent RSSAgent( interestscfg[agent][interests], embedderembedder, dedup_thresholdcfg[agent][dedup_threshold], interest_thresholdcfg[agent][interest_threshold] ) store SQLiteStore(cfg[storage][db_path]) # 2. 抓取 RSS print(开始抓取 RSS 源...) items fetch_all_feeds(cfg[feeds]) print(f共抓取到 {len(items)} 条原始内容) # 3. 构建待处理文本标题 摘要 texts [f{item[title]}\n{item[summary]} for item in items] vectors embedder.embed_texts(texts) history_vectors store.get_all_vectors() # 4. Agent 决策去重 兴趣过滤 accepted [] for item, vec in zip(items, vectors): decision agent.decide(vec, history_vectors) if decision accepted: item[vector] vec accepted.append(item) print(fAgent 过滤后剩余 {len(accepted)} 条) # 5. 对通过筛选的条目做摘要 for item in accepted[:cfg[agent][max_items_per_run]]: if cfg[summarizer][enabled]: try: summary summarize_with_ollama( textitem[title] \n item[summary], base_urlcfg[summarizer][base_url], modelcfg[summarizer][model], max_tokenscfg[summarizer][max_tokens] ) item[summary_text] summary except Exception as exc: print(f[summarize] 摘要生成失败: {exc}) item[summary_text] item[summary][:200] else: item[summary_text] item[summary][:200] score agent.interest_score(item[vector]) store.insert_item(item, item[vector], score, item[summary_text]) print(f已入库: {item[title]} (兴趣分: {score:.3f})) # 6. 输出 Markdown 日报 accepted_sorted agent.rank_items(accepted, [i[vector] for i in accepted]) write_markdown(accepted_sorted, cfg[storage][output_dir]) def write_markdown(items: list[dict], output_dir: str): 把筛选结果输出为 Markdown 文件。 from pathlib import Path import datetime Path(output_dir).mkdir(parentsTrue, exist_okTrue) today datetime.date.today().isoformat() out_path Path(output_dir) / frss-report-{today}.md lines [f# RSS 日报 - {today}, ] for i, item in enumerate(items, 1): lines.append(f## {i}. {item[title]}) lines.append() lines.append(f- 来源{item[source]}) lines.append(f- 链接{item[link]}) lines.append(f- 兴趣分{item.get(interest_score, 0)}) lines.append() lines.append(item.get(summary_text, )) lines.append() Path(out_path).write_text(\n.join(lines), encodingutf-8) print(f日报已输出到: {out_path}) if __name__ __main__: main()注意主流程的排序逻辑。虽然 Agent 在决策时已经过滤了一遍但rank_items会对最终入库存的内容按照兴趣分重新排序这样输出日报时最符合兴趣的内容会排在最前面而不是按照抓取顺序机械排列。4.7 运行与验证在运行之前确认本地已经安装并启动了 Ollama同时拉取了配置里指定的模型ollama pull qwen2.5:1.5b ollama serve然后执行主程序python main.py预期输出类似开始抓取 RSS 源... [fetch] https://news.ycombinator.com/rss 获取到 30 条内容 [fetch] https://www.ruanyifeng.com/blog/atom.xml 获取到 10 条内容 共抓取到 40 条原始内容 Agent 过滤后剩余 6 条 已入库: 本地向量数据库对比与选型 (兴趣分: 0.873) 已入库: OpenAI 发布新 Agent SDK (兴趣分: 0.812) ... 日报已输出到: output/rss-report-2025-07-22.md第一次运行会比后续慢很多原因是需要下载 Embedding 模型。模型下载完成后会自动缓存到本地目录之后启动速度会快很多。4.8 结果说明打开生成的日报文件你会看到类似这样的结构# RSS 日报 - 2025-07-22 ## 1. 本地向量数据库对比与选型 - 来源https://news.ycombinator.com/rss - 链接https://example.com/post/vector-db - 兴趣分0.873 本文对比了 Chroma、Qdrant、Milvus 三款向量数据库在本地部署场景下的 性能与资源占用并给出了选型建议... ## 2. OpenAI 发布新 Agent SDK - 来源https://www.ruanyifeng.com/blog/atom.xml - 链接https://example.com/post/openai-agent-sdk - 兴趣分0.812 ...日报的内容已经被 Agent 筛选过并且自动生成了摘要不需要再手动翻几十个 feed 就能快速掌握今天值得读的内容。5. 常见问题与排查思路第一次运行这类项目时最容易踩的坑基本集中在依赖、模型下载、性能和文本质量几个方面。下面用表格总结高频问题问题现象常见原因解决思路feedparser 解析不出条目目标源返回了 JSON 或反爬页面先用浏览器访问该 RSS 地址确认是否属于合法 XML feed增加 User-Agent 和超时设置sentence-transformers 下载模型失败网络问题或未配置镜像源使用HF_ENDPOINThttps://hf-mirror.com环境变量或手动下载模型后放入缓存目录内存占用过高Embedding 模型和小模型同时加载把摘要任务改成逐条调用跑完释放或使用量化版 GGUF 小模型所有条目都被过滤interest_threshold设置过高首次运行建议先设为 0.1观察兴趣分分布之后再逐步上调重复内容仍然出现dedup_threshold太高0.92 已经偏高可下调到 0.85 测试同时确认历史向量确实写入数据库摘要生成慢小模型在 CPU 上推理效率低只用摘要处理兴趣分最高的前 5 条或换成 0.5B 参数模型中文内容向量匹配效果差使用了非中文优化的 embedding 模型改用BAAI/bge-small-zh-v1.5重新生成向量并重建数据库遇到问题时建议按下面的顺序排查先确认网络出问题的是哪一层RSS 抓取、模型下载还是 Ollama 调用。打印或日志输出每一步的输入长度排除空文本导致的向量化异常。单独抽取一条已知内容打印它的兴趣分判断阈值是否合理。清空数据库重新跑一次排除历史脏数据影响。6. 最佳实践与工程建议6.1 配置与参数调优interest_threshold和dedup_threshold这两个参数是整个系统里最敏感的部分。它们不是一次调好就永远不变的建议先跑两周数据记录每天的“总抓取数 / 过滤数 / 入库数”比例。如果入库率超过 30%说明兴趣过滤太宽松可以适当上调如果低于 5%说明阈值过严可能会漏掉潜在有价值的内容。参数不要直接改代码要放进config.yaml。这样切换兴趣主题、调整模型时不需要动一行业务逻辑。6.2 模型选择与性能优化Embedding 模型的选择要看你的内容语言。如果以中文技术博客为主all-MiniLM-L6-v2虽然能用但效果不如专门优化过中文的模型。同时要记住当更换 embedding 模型时向量维度可能发生变化旧数据库里的向量必须清空重建否则np.frombuffer读出来的数组形状会不匹配导致相似度计算报错。小模型的摘要能力取决于模型本身的指令遵循能力。1.5B 以下模型在长文本摘要上容易出现“复述开头”的问题可以配合一个简单的抽取式逻辑先选出文章里信息密度最高的一句话再交给模型。6.3 数据与隐私安全虽然 RSSMonster 的设计强调本地运行但依然要防范风险RSS 源内容可能包含恶意链接日报里的超链接不要用脚本自动打开点击前先确认域名。SQLite 数据库文件包含标题和摘要如果团队内部共享这台机器建议对数据库文件设置访问权限避免敏感内容泄露。如果后续想接入在线大模型做增强分析必须明确告知使用者“外部请求会把标题发送给第三方”并让用户自行决定是否开启。6.4 可维护性与扩展方向RSSMonster 的模块化设计天然方便扩展。后期可以在不破坏主流程的前提下做这些增强增加 Web 界面把 SQLite 数据渲染成本地网页。引入定时任务crontab 或系统服务每天早上自动运行生成日报。把代码封装为一个 CLI 工具支持--source参数手动指定某几个源优先抓取。增加向量检索能力从历史库中搜索“与某条内容最相关的旧文章”实现更完整的知识管理闭环。7. 总结与下一步学习路线到这里RSSMonster 的核心实现已经搭建完成。我们实现了从 RSS 抓取、HTML 清洗、本地向量化、小模型摘要到 Agent 去重与兴趣筛选的完整闭环。你通过这个项目应该掌握三个关键点语义去重与兴趣过滤可以完全依赖本地轻量 embedding 模型完成不需要在线 API成本低且隐私安全。Agent 决策层不是复杂算法核心在于“为每条内容输出一个明确的决策状态”这让系统具备可观测性也方便调优。小模型负责少量高价值任务对性能的影响完全可控。下一步建议你去尝试把自己的实际订阅源接入项目运行两周观察筛选效果。动手调整 embedding 模型对比中文内容下不同模型对兴趣匹配的影响。尝试把日报输出改成 HTML 邮件或者接入夜间定时任务体验“无人值守的信息助手”。如果在第二步换了模型我建议先记录旧模型产生的向量再决定是否清空数据库。这个项目最大的特点就是“可折腾”每换一个模型、每调一次阈值都是一次对信息筛选方案的实验。动手跑起来你才能真正理解 agentic 系统如何改变信息获取的方式。

相关新闻

2026/9/2 18:46:08

FastReport 4.10.1中文修正版在Delphi老项目中的报表实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/2 18:41:08

用MLP做全球海温预测:从数据处理到模型调参实践

简介:一份面向神经网络初学者的完整实践资源,用于全球海温预测类课程作业与入门学习。压缩包共5个文件,内含Python训练与预测脚本、netCDF格式的全球海温数据集、依赖包版本清单以及Python 3.6.5国内镜像快速下载地址,整体约164.7…

2026/9/2 18:41:08

MLP做全球海温预测:基于PyTorch的完整实战与踩坑指南

简介:面向神经网络入门与大作业选题的全球海温预测MLP项目包,内置完整可复现的训练与预测流程。资源共5个文件,以Python代码、NetCDF数据文件和说明文档为主,其中HadISST海温数据集用于实验,两个py脚本分别完成模型训练…

2026/9/2 18:56:09

2026年AI论文写作软件推荐:9款高效AI工具全攻略

一、AI 全面赋能学术写作 人工智能技术正以前所未有的速度渗透到学术研究的各个环节,AI工具在提升论文写作效率与质量方面展现出强大潜力。从选题构思到内容撰写,再到语言润色与查重,AI已实现全流程支持。 本文将为您推荐9款当前市场上最具实…

2026/9/2 18:56:09

C# TCP北斗定位服务器:协议解析与高并发稳定部署实战

简介:这是一个基于C#开发的北斗转发服务器网络版程序,面向需要接入北斗指挥机、统一管理多客户端数据收发的物联网或通信开发者。程序采用多线程、异步处理与线程池、select等技术,用于监听北斗客户端上报数据并转发至服务器端,实…

2026/9/2 18:56:09

Minecraft 1.12.2原版生存服务器开荒实战指南

最近一批老玩家回流,第一句话基本都是:“有没有 1.12.2 的原版生存服?不要模组,不要 RPG,就想要最开始那种开荒的感觉。”这让我意识到,老版本原版生存服的需求一直没消失过。CST 服务器这一轮 1.12.2 原版…

2026/9/2 18:56:09

吃豆人AI作业全解析:从搜索算法到强化学习实战

简介:来自伯克利大学人工智能课程的吃豆人(Pacman)Python源代码,是一份将搜索算法、评估函数与强化学习落地到经典游戏的实战作业,适合高校学生、人工智能初学者及算法爱好者作为课后练习或项目参考。资源以Python源码…

2026/9/2 18:56:09

ThinkPad T480黑苹果OpenCore引导配置与踩坑指南

简介:联想 ThinkPad T480 专用的黑苹果引导文件,基于 OpenCore 0.6.6 构建,面向希望在这台笔记本上安装并使用 macOS 的用户。作者针对 i5-8250U 处理器、UHD 620 核显等硬件组合做了适配,将 ACPI/ASL 补丁、关键驱动、配置文件等…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/2 9:00:32

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/2 8:41:06

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/2 0:03:41

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/9/2 0:03:41

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

这次我们来看一个很实用的 DeepSeek 落地场景:用 DeepSeek 把英文视频字幕自动翻译成中文。具体案例是《恶魔君》1989 年第 28 集的英转中字幕任务,标题写得很直白,但背后其实是一整套可以复用的技术流程:字幕解析、模型调用、批量…

2026/9/2 0:03:41

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

当你家里摆着一台天猫精灵,却总希望语音助手偶尔“不正经”一点,不用官方腔回答问题,而是张口就接几句搞笑段子,会是什么体验?我最近动手验证了一下这个想法——没有去改装任何市面上现有的智能音箱,而是直…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…