Compare commits
1 Commits
main
...
competitor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46da1fa495 |
24
README.md
24
README.md
@@ -1,3 +1,5 @@
|
||||
[Nano Banana🍌 公益站](https://image-generation.zenfeed.xyz/):集成 Twitter 热门 Prompt,轻松玩转各种姿势
|
||||
---
|
||||
[English](README-en.md)
|
||||
|
||||
---
|
||||
@@ -53,6 +55,14 @@ zenfeed 是你的 <strong>AI 信息中枢</strong>。它既是<strong>智能 RSS
|
||||
|
||||
---
|
||||
|
||||
**赞助项目可以领取 Gemini Key**
|
||||
|
||||
<a href="https://afdian.com/a/glidea"><img src="docs/images/sponsor.png" width="500"></a>
|
||||
<br/>
|
||||
<a href="https://afdian.com/a/glidea">赞助项目,支持发展</a>
|
||||
|
||||
---
|
||||
|
||||
## 💡 前言
|
||||
|
||||
RSS(简易信息聚合)诞生于 Web 1.0 时代,旨在解决信息分散的问题,让用户能在一个地方聚合、追踪多个网站的更新,无需频繁访问。它将网站更新以摘要形式推送给订阅者,便于快速获取信息。
|
||||
@@ -205,26 +215,18 @@ $env:API_KEY = "sk-..."; docker-compose -p zenfeed up -d
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img src="https://github.com/glidea/zenfeed/blob/main/docs/images/wechat.png?raw=true" alt="Wechat QR Code" width="300">
|
||||
<img src="docs/images/wechat.png" alt="Wechat QR Code" width="300">
|
||||
<br>
|
||||
<strong>AI 学习交流社群</strong>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="https://github.com/glidea/banana-prompt-quicker/blob/main/images/glidea.png?raw=true" width="250">
|
||||
<img src="docs/images/sponsor.png" width="500">
|
||||
<br>
|
||||
<strong><a href="https://glidea.zenfeed.xyz/">我的其它项目</a></strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" colspan="2">
|
||||
<img src="https://github.com/glidea/banana-prompt-quicker/blob/main/images/readnote.png?raw=true" width="400">
|
||||
<br>
|
||||
<strong><a href="https://www.xiaohongshu.com/user/profile/5f7dc54d0000000001004afb">📕 小红书账号 - 持续分享 AI 原创</a></strong>
|
||||
<strong><a href="https://afdian.com/a/glidea">请杯奶茶 🧋</a></strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
都看到这里了,顺手点个 **Star ⭐️** 呗,这是我持续维护的最大动力!
|
||||
|
||||
有好玩的 AI 工作也请联系我!
|
||||
|
||||
152
docs/competitor-research-zh.md
Normal file
152
docs/competitor-research-zh.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 竞品调研(Zenfeed)
|
||||
|
||||
更新时间:2025-10(基于公开已知信息与产品体验的综合判断,可能随时间变化)
|
||||
|
||||
目的:梳理 Zenfeed 所在赛道的主要竞品与替代方案,按产品类型与能力维度对比,识别差异化与机会点,指导路线和定位。
|
||||
|
||||
|
||||
一、产品类型与代表选手
|
||||
|
||||
1) AI 加持的 RSS/SaaS 阅读器
|
||||
- Feedly(含 Feedly AI/Leo)
|
||||
- Readwise Reader
|
||||
- Inoreader(规则、搜索较强,近年也引入部分 AI 功能)
|
||||
- NewsBlur(基于训练的过滤偏好)
|
||||
|
||||
2) 自托管 RSS 聚合器(开源)
|
||||
- FreshRSS
|
||||
- Tiny Tiny RSS(TT-RSS)
|
||||
- Miniflux
|
||||
- + 插件生态(例如部分 OpenAI/总结插件)
|
||||
|
||||
3) 信息监控/自动化编排
|
||||
- Huginn(自托管 IFTTT/IF-This-Then-That)
|
||||
- n8n(低代码自动化编排,SaaS/自托管)
|
||||
- changedetection.io(网页变更监控,侧重 DOM 变化)
|
||||
- Distill.io / Visualping(SaaS 页面变更监控)
|
||||
|
||||
4) 情报监测/关键词追踪(论坛/社媒/开发者社区)
|
||||
- Syften(跨社区关键词监控)
|
||||
- 万物追踪(中文市场关注度较高)
|
||||
- TeamWiseFlow/WiseFlow(“AI 首席情报官”思路)
|
||||
|
||||
5) 爬取/采集与任务平台(更多是基础设施/补充)
|
||||
- Scrapy、Apify、Crawlee、Playwright/Chromium 爬取方案
|
||||
- RSSHub(补齐数据源的通用 RSS 生成器,侧重“供给侧”,非直接竞品)
|
||||
|
||||
|
||||
二、核心对比维度
|
||||
|
||||
- 数据源覆盖与可扩展性:
|
||||
- 是否仅限 RSS,是否支持 RSSHub/网页爬取、API、邮件等;是否易于接入新源。
|
||||
- 信息筛选与查询:
|
||||
- 规则引擎(关键词/布尔条件/正则/评分)、保存的搜索、优先级/标签。
|
||||
- AI 能力:
|
||||
- 摘要、打标、主题聚类、意图识别、相似度搜索/去重、长文重写、对话式问答。
|
||||
- 工作流/编排:
|
||||
- 是否可声明式编排(pipeline)、分步处理、多路分发、可插拔 Prompt/算子。
|
||||
- 通知与路由:
|
||||
- 邮件/Slack/Telegram/企业微信/飞书/Webhook/RSS 二次生成,是否支持相似分组/摘要汇总。
|
||||
- 存储与检索:
|
||||
- 结构化索引、倒排索引、向量库、对象存储(音频/附件)、缓存与过期策略。
|
||||
- 部署与成本:
|
||||
- SaaS 订阅 vs 自托管;API Key 成本、并发限速、隐私数据掌控。
|
||||
- 协作与团队能力:
|
||||
- Board/共享、标注/高亮、评论、权限与多用户。
|
||||
- 开放接口:
|
||||
- JSON-RPC/HTTP API、MCP Server、二次开发友好度。
|
||||
- 体验与生态:
|
||||
- Web/移动端、浏览器插件、自动化生态与第三方集成。
|
||||
|
||||
|
||||
三、典型竞品速览
|
||||
|
||||
1) Feedly + Feedly AI(Leo)
|
||||
- 定位:面向专业/团队的信息监测与阅读;AI“Leo”用于优先级、主题、去重与摘要。
|
||||
- 优势:成熟度高,协作/Board 丰富,企业方案完善,数据源与集成较多。
|
||||
- 局限:闭源、订阅价格较高;自定义度受限;自托管不可行;对中文开发者生态与私有化场景不友好。
|
||||
|
||||
2) Readwise Reader
|
||||
- 定位:稍偏“稍后读/知识管理”;支持网页/邮件/订阅,AI 摘要和高亮回顾。
|
||||
- 优势:体验优秀、阅读流程顺滑、与 Readwise 知识库联动。
|
||||
- 局限:闭源、偏个人阅读;自动化编排/告警监控能力有限;难以做灵活的规则路由。
|
||||
|
||||
3) Inoreader / NewsBlur
|
||||
- 定位:传统 RSS 强者;Inoreader 的规则、过滤、搜索很成熟,NewsBlur 有“训练”机制。
|
||||
- 优势:稳定可靠、生态与客户端广、学习成本低。
|
||||
- 局限:AI 能力与向量检索通常非核心;深度个性化编排和二次开发空间有限。
|
||||
|
||||
4) FreshRSS / TT-RSS / Miniflux(自托管)
|
||||
- 定位:轻量的开源 RSS 聚合;可通过插件接 AI 摘要。
|
||||
- 优势:自托管、可控、成本低;社区活跃。
|
||||
- 局限:缺少原生的“语义处理流水线”和“查询/路由/通知”的深度耦合;要实现“AI + 监控 + 通知”的端到端闭环需要较多自定义。
|
||||
|
||||
5) Huginn / n8n(自动化)
|
||||
- 定位:通用自动化与数据流编排。
|
||||
- 优势:灵活、组件多、可拉通任意 API 与存储。
|
||||
- 局限:非“RSS/情报”领域专用;需要自己拼装爬取、解析、去重、总结、相似分组、通知等环节,心智与运维成本较高。
|
||||
|
||||
6) changedetection.io / Distill.io / Visualping(页面变更监控)
|
||||
- 定位:检测网页 DOM/文本变化,并告警。
|
||||
- 优势:上手快、对不提供 RSS 的页面友好。
|
||||
- 局限:语义层理解弱;AI 总结/聚类/多源关联较少;难以形成“知识库 + 查询”。
|
||||
|
||||
7) Syften / 万物追踪 / WiseFlow 等(情报监控)
|
||||
- 定位:跨社区关键词/实体/主题跟踪,生成简报。
|
||||
- 优势:场景契合“情报/监控/简报”;SaaS 即开即用。
|
||||
- 局限:闭源或私有;可编排性有限;数据可控性与隐私诉求难满足;对中文/特定垂直源支持取决于厂商。
|
||||
|
||||
8) RSSHub(重要补充而非直接竞品)
|
||||
- 定位:把各种站点“喂成 RSS”。
|
||||
- 价值:极大丰富数据源;与 Zenfeed 组合可形成“源→处理→查询→路由”的闭环。
|
||||
|
||||
|
||||
四、Zenfeed 的差异化与定位
|
||||
|
||||
- 面向“AI + RSS/情报/监控”的自托管开源引擎:
|
||||
- 声明式 YAML 配置 + 热更新;组件化框架,订阅 Watcher 实时生效。
|
||||
- 处理管道以“标签集”为核心抽象,支持 LLM 驱动的摘要、打标、评分、过滤、聚类、脚本生成、抓取增强等。
|
||||
- 存储侧内置主索引、倒排索引与向量索引;NutsDB 缓存嵌入;可选 MinIO/S3 存储富媒体。
|
||||
- 调度器支持基于语义/规则的查询生成“事件”;通知器做相似合并与 LLM 汇总,路由到 Email/Webhook 等。
|
||||
- 对外提供 JSON-RPC/HTTP、MCP Server、导出 RSS,易于二次集成。
|
||||
|
||||
- 与 SaaS 阅读器相比:更可定制、数据私有、可与私有模型/网络融合;缺点是需要自行部署与维护。
|
||||
- 与通用自动化平台相比:Zenfeed 在“RSS/情报管道”有更强的领域内建语义模型与数据结构;通用性略弱但落地更快。
|
||||
- 与纯 RSS 聚合器相比:原生引入 LLM 语义处理、向量搜索、调度/路由/通知,强调“读写闭环”和“监控/简报”。
|
||||
|
||||
|
||||
五、潜在短板与改进机会(相对竞品)
|
||||
|
||||
- 认证与多用户/权限:当前暴露到公网存在安全顾虑;完善 Auth、分权与审计将有助企业团队采用。
|
||||
- GUI 配置与可视化编排:YAML 心智强,建议提供可视化 Pipeline/路由设计器、规则调试器、Prompt 管理。
|
||||
- 移动端/客户端生态:继续完善官方 Web/移动端体验;浏览器插件“保存到 Zenfeed/一键追踪”。
|
||||
- 第三方通知与协作:拓展 Slack/Telegram/飞书/企业微信等官方集成;通知中的交互(反馈、标注)回写管道。
|
||||
- 数据源扩展:内置“RSSHub 模板化连接”、网页自动提取(Readability/Boilerplate)、邮件源、GitHub/Reddit 等常见垂直源。
|
||||
- 模型与成本:
|
||||
- 多模型策略(廉价 Embedding + 中等 Summarize + 高质少量 Deep Reasoning);
|
||||
- 限速/重试/缓存/去重更精细以优化成本;
|
||||
- 插件式模型提供商抽象,方便企业接私有 LLM。
|
||||
- 团队协作:Board/共享视图、事件指派、评论、标签规范与知识沉淀。
|
||||
|
||||
|
||||
六、目标用户与场景建议
|
||||
|
||||
- 高阶 RSS 用户、研究员/分析师、开发者、投资/BD、开源维护者、增长/品牌观察;
|
||||
- 典型场景:
|
||||
- 行业/竞品/技术情报监控与周报;
|
||||
- 个性化“高质量源 + 语义过滤 + 摘要 + 去重 + 分发”;
|
||||
- 会议/活动/政策/漏洞/版本发布等事件跟踪;
|
||||
- 私有化部署的“团队情报中枢”。
|
||||
|
||||
|
||||
七、结论(定位与策略)
|
||||
|
||||
- 定位:自托管、可编排的 AI 信息中枢(“RSS × 语义处理 × 调度路由 × 通知”)。
|
||||
- 策略:
|
||||
1) 做强“声明式管道 + 语义检索 + 调度路由”的一体化体验;
|
||||
2) 打通 RSSHub 与常见垂直源,降低“连接成本”;
|
||||
3) 完善认证/多用户与 GUI 编排,提升团队与企业可用性;
|
||||
4) 提供一组开箱即用的“行业模板/蓝图”(安全情报、开源监控、投研资讯、竞品监测、政策合规等);
|
||||
5) 控制模型成本,提供可观测性(速率、花费、召回/精确度、摘要质量回馈)。
|
||||
|
||||
备注:以上信息仅用于产品规划参考,不构成对第三方产品的评价承诺。实际功能与定价以各产品官方为准。
|
||||
@@ -59,7 +59,6 @@
|
||||
| `scrape.past` | `time.Duration` | 抓取 Feed 的回溯时间窗口。例如 `1h` 表示只抓取过去 1 小时的 Feed。 | `24h` | 否 |
|
||||
| `scrape.interval` | `time.Duration` | 抓取每个源的频率 (全局默认值)。例如 `1h`。 | `1h` | 否 |
|
||||
| `scrape.rsshub_endpoint` | `string` | RSSHub 的端点。你可以部署自己的 RSSHub 服务器或使用公共实例 (参见 [RSSHub 文档](https://docs.rsshub.app/guide/instances))。例如 `https://rsshub.app`。 | | 是 (如果使用了 `rsshub_route_path`) |
|
||||
| `scrape.rsshub_access_key` | `string` | RSSHub 的访问密钥。用于访问控制。(详情见 [RSSHub文档访问控制](https://docs.rsshub.app/deploy/config#access-control-configurations)) | | 否 |
|
||||
| `scrape.sources` | `对象列表` | 用于抓取 Feed 的源列表。详见下方的 **抓取源配置**。 | `[]` | 是 (至少一个) |
|
||||
|
||||
### 抓取源配置 (`scrape.sources[]`)
|
||||
|
||||
@@ -59,7 +59,6 @@ This section configures parameters related to the Jina AI Reader API, primarily
|
||||
| `scrape.past` | `time.Duration` | Time window to look back when scraping feeds. E.g., `1h` means only scrape feeds from the past 1 hour. | `24h` | No |
|
||||
| `scrape.interval` | `time.Duration` | Frequency to scrape each source (global default). E.g., `1h`. | `1h` | No |
|
||||
| `scrape.rsshub_endpoint` | `string` | Endpoint for RSSHub. You can deploy your own RSSHub server or use a public instance (see [RSSHub Documentation](https://docs.rsshub.app/guide/instances)). E.g., `https://rsshub.app`. | | Yes (if `rsshub_route_path` is used) |
|
||||
| `scrape.rsshub_access_key` | `string` | The access key for RSSHub. Used for access control. (see [RSSHub config](https://docs.rsshub.app/deploy/config#access-control-configurations))| | No |
|
||||
| `scrape.sources` | `list of objects` | List of sources to scrape feeds from. See **Scrape Source Configuration** below. | `[]` | Yes (at least one) |
|
||||
|
||||
### Scrape Source Configuration (`scrape.sources[]`)
|
||||
|
||||
@@ -95,11 +95,10 @@ type LLM struct {
|
||||
}
|
||||
|
||||
type Scrape struct {
|
||||
Past timeutil.Duration `yaml:"past,omitempty" json:"past,omitempty" desc:"The lookback time window for scraping feeds. e.g. 1h means only scrape feeds in the past 1 hour. Default: 3d"`
|
||||
Interval timeutil.Duration `yaml:"interval,omitempty" json:"interval,omitempty" desc:"How often to scrape each source, it is a global interval. e.g. 1h. Default: 1h"`
|
||||
RSSHubEndpoint string `yaml:"rsshub_endpoint,omitempty" json:"rsshub_endpoint,omitempty" desc:"The endpoint of the RSSHub. You can deploy your own RSSHub server or use the public one (https://docs.rsshub.app/guide/instances). e.g. https://rsshub.app. It is required when sources[].rss.rsshub_route_path is set."`
|
||||
RSSHubAccessKey string `yaml:"rsshub_access_key,omitempty" json:"rsshub_access_key,omitempty" desc:"The access key for RSSHub. Used for access control. (see [RSSHub config](https://docs.rsshub.app/deploy/config#access-control-configurations))"`
|
||||
Sources []ScrapeSource `yaml:"sources,omitempty" json:"sources,omitempty" desc:"The sources for scraping feeds."`
|
||||
Past timeutil.Duration `yaml:"past,omitempty" json:"past,omitempty" desc:"The lookback time window for scraping feeds. e.g. 1h means only scrape feeds in the past 1 hour. Default: 3d"`
|
||||
Interval timeutil.Duration `yaml:"interval,omitempty" json:"interval,omitempty" desc:"How often to scrape each source, it is a global interval. e.g. 1h. Default: 1h"`
|
||||
RSSHubEndpoint string `yaml:"rsshub_endpoint,omitempty" json:"rsshub_endpoint,omitempty" desc:"The endpoint of the RSSHub. You can deploy your own RSSHub server or use the public one (https://docs.rsshub.app/guide/instances). e.g. https://rsshub.app. It is required when sources[].rss.rsshub_route_path is set."`
|
||||
Sources []ScrapeSource `yaml:"sources,omitempty" json:"sources,omitempty" desc:"The sources for scraping feeds."`
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
|
||||
@@ -80,7 +80,6 @@ func (c *Config) From(app *config.App) {
|
||||
URL: app.Scrape.Sources[i].RSS.URL,
|
||||
RSSHubEndpoint: app.Scrape.RSSHubEndpoint,
|
||||
RSSHubRoutePath: app.Scrape.Sources[i].RSS.RSSHubRoutePath,
|
||||
RSSHubAccessKey: app.Scrape.RSSHubAccessKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ type ScrapeSourceRSS struct {
|
||||
URL string
|
||||
RSSHubEndpoint string
|
||||
RSSHubRoutePath string
|
||||
RSSHubAccessKey string
|
||||
}
|
||||
|
||||
func (c *ScrapeSourceRSS) Validate() error {
|
||||
@@ -47,22 +46,9 @@ func (c *ScrapeSourceRSS) Validate() error {
|
||||
return errors.New("URL must be a valid HTTP/HTTPS URL")
|
||||
}
|
||||
|
||||
// Append access key as query parameter if provided
|
||||
c.appendAccessKey()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ScrapeSourceRSS) appendAccessKey() {
|
||||
if c.RSSHubEndpoint != "" && c.RSSHubAccessKey != "" && !strings.Contains(c.URL, "key=") {
|
||||
if strings.Contains(c.URL, "?") {
|
||||
c.URL += "&key=" + c.RSSHubAccessKey
|
||||
} else {
|
||||
c.URL += "?key=" + c.RSSHubAccessKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Factory code block ---
|
||||
func newRSSReader(config *ScrapeSourceRSS) (reader, error) {
|
||||
if err := config.Validate(); err != nil {
|
||||
|
||||
@@ -122,55 +122,6 @@ func TestNewRSS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "Valid Configuration - RSSHub with Access Key",
|
||||
Given: "a valid configuration with RSSHub details and access key",
|
||||
When: "creating a new RSS reader",
|
||||
Then: "should succeed, construct the URL with access key, and return a valid reader",
|
||||
GivenDetail: givenDetail{
|
||||
config: &ScrapeSourceRSS{
|
||||
RSSHubEndpoint: "http://rsshub.app/",
|
||||
RSSHubRoutePath: "/_/test",
|
||||
RSSHubAccessKey: "testkey",
|
||||
},
|
||||
},
|
||||
WhenDetail: whenDetail{},
|
||||
ThenExpected: thenExpected{
|
||||
wantErr: false,
|
||||
validateFunc: func(t *testing.T, r reader) {
|
||||
Expect(r).NotTo(BeNil())
|
||||
rssReader, ok := r.(*rssReader)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(rssReader.config.URL).To(Equal("http://rsshub.app/_/test?key=testkey"))
|
||||
Expect(rssReader.config.RSSHubEndpoint).To(Equal("http://rsshub.app/"))
|
||||
Expect(rssReader.config.RSSHubRoutePath).To(Equal("/_/test"))
|
||||
Expect(rssReader.config.RSSHubAccessKey).To(Equal("testkey"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "Valid Configuration - URL with Access Key",
|
||||
Given: "a valid configuration with URL and access key",
|
||||
When: "creating a new RSS reader",
|
||||
Then: "should succeed, append access key to URL, and return a valid reader",
|
||||
GivenDetail: givenDetail{
|
||||
config: &ScrapeSourceRSS{
|
||||
URL: "http://example.com/feed",
|
||||
RSSHubAccessKey: "testkey",
|
||||
},
|
||||
},
|
||||
WhenDetail: whenDetail{},
|
||||
ThenExpected: thenExpected{
|
||||
wantErr: false,
|
||||
validateFunc: func(t *testing.T, r reader) {
|
||||
Expect(r).NotTo(BeNil())
|
||||
rssReader, ok := r.(*rssReader)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(rssReader.config.URL).To(Equal("http://example.com/feed"))
|
||||
Expect(rssReader.config.RSSHubAccessKey).To(Equal("testkey"))
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// --- Run tests ---
|
||||
|
||||
Reference in New Issue
Block a user