eiler2005/reddit-compass
GitHub: eiler2005/reddit-compass
一个 AI 驱动的多源趋势监测雷达,自动收集 21 个信息源的内容并通过 Qwen LLM 提取趋势情报,以 REST API 和仪表板提供服务。
Stars: 0 | Forks: 0
# 🧭 reddit-compass
[](LICENSE)
[](https://www.python.org/)
[](https://github.com/eiler2005/reddit-compass/actions)
[](https://github.com/eiler2005/reddit-compass/actions)
**你的广域趋势探测雷达。12 个稳定领域,多源证据,一个指南针。**
你正在写一本关于 AI 如何改变工作的书。或者在运营一个专栏。又或者正在开发一款产品,需要知道市场*真正*的想法——而不是新闻稿里说的那些。
所以你查看 Reddit。接着是 Hacker News。然后是 NYT。接着是 Wired。然后是 FT。接着是 TechCrunch。然后是 Medium。再是 ProductHunt。每。一。天。早。晨。
这是喝第一杯咖啡前长达 45 分钟的切换标签页时间。而且你*仍然*会错过凌晨 2 点爆火的帖子——那个有 400 名被裁工程师准确描述了你的产品正在解决的痛点的帖子。
**reddit-compass 为你完成这些。每天晚上。广域雷达(Broad Radar)监控全球动态;AI 原生透镜(AI-native Lens)**
让书籍/RBC 的研究专注于 AI、工作、机构和市场。**
## reddit-compass 能为你做什么
### 每天晚上,自动运行
在 Qwen 折扣窗口运行(**17:00–03:00 莫斯科时间 = 14:00–00:00 UTC**),因此 LLM
分析使用更便宜的非高峰费率。
| 时间 (UTC / MSK) | 发生了什么 | 你为何需要关注 |
|---|---|---|
| 14:00 / 17:00 | 来自 BBC、Guardian、Reuters、TechCrunch、Verge、Ars 的 227 篇文章 (RSS) | 主流叙事——大众明天会听到什么 |
| 14:10 / 17:10 | 来自 Hacker News 的 197 个故事 (Algolia API,过去 7 天) | 开发者正在构建什么以及在争论什么 |
| 14:20 / 17:20 | 来自 NYT、WaPo、FT、Wired、Medium 及其他 7 家的 183 篇文章 (Ladder 付费墙代理) | 付费墙背后的*真正*分析 |
| 14:30 / 17:30 | 来自 ProductHunt 的 30 款产品 (GraphQL) | 现在正在发布什么 |
| 独立运行 | Collector 在 `compass.db` 中敲定原始事实 | 收集状态不依赖于 LLM |
| 手动/影子模式 | 带版本控制的 Engine 运行 facets → stories → trends | 无需重新收集即可重复分析 |
你醒来时看到的报告会这样显示:
### 当你向它提问时
```
reddit-compass collect --profile broad --sources reddit,hn,rss,ladder,ph
reddit-compass engine release create --run RUN_ID
reddit-compass engine facets --release RELEASE_ID --profile broad
reddit-compass engine stories propose --facet-release FACET_ID --limit 50
reddit-compass engine trends propose --story-release STORY_ID --window 30d
reddit-compass fetch --stealth # Reddit: 40 subreddits, stealth mode
reddit-compass hn # Hacker News: AI stories
reddit-compass rss # RSS: 6 free sources
reddit-compass ladder # Paywall: 12 sources via Ladder
reddit-compass ph # ProductHunt: top products
reddit-compass signals # LLM analysis (Qwen API, all sources)
reddit-compass serve # REST API + UI on :8900
reddit-compass db rebuild # Только legacy recovery, не Engine workflow
reddit-compass db stats # SQLite history
reddit-compass fetch --dry-run # Preview without network
```
### 设计上它不会做的事
已写入 [`AGENTS.md`](AGENTS.md)。该服务在架构上拒绝:
- 在 Reddit 上**发帖、投票或评论**。(只读。永远如此。)
- **绕过账号封禁**或冒充用户。(代理仅用于速率限制。)
- 使用收集的内容**训练 ML 模型**。(明确禁止。)
- 在 git、日志或 API 响应中**泄露机密**。(detect-secrets 提交前门禁。)
- **触碰 VPS 上的其他服务**。(隔离的网络 + 数据卷。)
## 架构
```
🌐 Reddit packs 💬 Hacker News 📰 RSS sections 🪜 Ladder optional 🚀 ProductHunt
Playwright + JSON Algolia front/search aiohttp + XML fallback GraphQL API
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│ two independent runtimes in one repository │
│ │
│ Collector → compass.db ──read-only snapshot──► Trend Engine → publication → Radar/Today │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
JSONL snapshots compass.db trend_engine.db REST API :8900
(exchange format) (raw facts) (immutable versions) (FastAPI + OAuth2)
```
**数据源 → 5 个数据源集群 → 12 个宽泛领域 → stories/trends → Today + Radar**
收集和分析是独立的运行时。`collect` 将原始事实写入 `compass.db`;
带版本控制的 Engine 将它们冻结到 `trend_engine.db` 中。Radar 仅读取手动发布的
Story/Trend 组合,因此收集失败或实验性尝试无法抹除上一次
经过验证的 dashboard。
包含部署图的完整架构:[`ARCHITECTURE.md`](ARCHITECTURE.md)
## 数据源与领域
默认收集配置为 [`config/profiles/broad.json`](config/profiles/broad.json):
AI/技术、劳动力/职业、商业/市场、社会/政治、世界/地缘政治、
文化/媒体、体育、科学/健康/教育、金融/消费、
气候/能源/基础设施、安全/隐私,以及 `other`。
| 集群 | 数据源 | 访问方式 |
|---|---|---|
| **Mainstream** | BBC, Guardian, NYT/WaPo/USA Today via RSS/Google News;可选 Ladder | RSS + Ladder 降级方案 |
| **Business** | Reuters, FT, Fox Business, American Banker | RSS + Ladder 降级方案 |
| **Tech/Culture** | TechCrunch, Verge, Ars Technica, Wired, New Yorker, Vanity Fair | RSS + Ladder 降级方案 |
| **Voices** | Reddit 宽泛包,Medium | 公开的 JSON/RSS + Ladder 降级方案 |
| **Developers / Pulse** | Hacker News, ProductHunt | Algolia + GraphQL/feed |
实现说明:[`docs/RADAR_TRENDWATCHING_IMPLEMENTATION.md`](docs/RADAR_TRENDWATCHING_IMPLEMENTATION.md)。
Prompt 契约:[`docs/RADAR_PROMPTS.md`](docs/RADAR_PROMPTS.md)。
完整数据源映射:[`docs/MULTI_SOURCE_PLAN.md`](docs/MULTI_SOURCE_PLAN.md)
## 快速开始
### 前置条件
- Python 3.12+,[`uv`](https://docs.astral.sh/uv/)
- Playwright:`uv run playwright install chromium`
- (可选)用于 VPS 部署的 Docker
### 安装
```
git clone https://github.com/eiler2005/reddit-compass.git
cd reddit-compass
uv sync
```
### 首次运行
```
# 预览将要收集的内容(无网络):
uv run reddit-compass fetch --dry-run
# 收集 Reddit 帖子:
uv run reddit-compass fetch
# 收集所有内容(Reddit + HN + RSS):
uv run reddit-compass all
# 启动 API:
uv run reddit-compass serve
# → http://localhost:8900/docs (Swagger UI)
```
### 可选的 Reddit 住宅代理
在 `.env.secrets` 中将 `REDDIT_COMPASS_PROXIES` 设置为提供商分配的 HTTP 代理
URL(占位符格式请参见 `.env.example`)。Reddit 的引导请求
和公开的 `.json` 列表/评论请求随后将使用该代理;如果 HTTP 客户端
收到 HTML 或 HTTP 403,Playwright 降级方案将通过
同样配置的代理启动 Chromium。请求间隔保持在 4 秒,最多进行两次 429
重试,间隔为 10 秒。代理仅用于缓解速率限制 —
绝不用于账号封禁、登录、发帖、投票或评论。
对于轮换住宅 IP 池(例如 IPRoyal),请设置 `REDDIT_COMPASS_ENGINE=playwright`:
Reddit 会向完整的浏览器流量提供 `.json` 数据,但经常以 403 拒绝来自
IP 池的纯 HTTP 请求(已于 2026-07-27 验证)。如果提供商提供
粘性会话 endpoint(约 20 分钟内使用相同的出口 IP),请优先使用它 — 每次连接都
进行 IP 轮换会导致短暂的 `Failed to fetch` 错误(浏览器引擎会
自动重试)。
凭据只能保存在 `.env.secrets`(已 gitignored)中;切勿提交或记录它们。
用于其他已批准的公开只读数据源的可选本地代理,仅记录在
gitignored 的运维笔记和机密文件中;切勿将其移动到受版本控制的
配置或 `REDDIT_COMPASS_PROXIES` 中。
### 夜间自动化
**VPS**(RSS, HN, Ladder, PH, LLM, radar)— 在 Qwen 折扣窗口内使用主机 cron
(14:00–15:30 UTC = 17:00–18:30 MSK)。详见 [`deploy/hostkey/README.md`](deploy/hostkey/README.md)。
**Reddit**(住宅 IP)— 从 Mac 上收集(Reddit 会封锁数据中心 IP),
然后同步到 VPS。夜间的 `scripts/fetch-and-sync.sh`(由 launchd 运行,03:17)会交替
路由以避免耗尽家庭 IP:偶数天 — 直接使用家庭 IP,奇数天 — 使用
带有 `REDDIT_COMPASS_ENGINE=playwright` 的 IPRoyal 住宅代理(代理来自 gitignored 的
`deploy/hostkey/.env.secrets`)。使用 `RC_PROXY_MODE=on|off` 强制指定路由:
```
./scripts/fetch-and-sync.sh # fetch + sync (route chosen automatically)
RC_PROXY_MODE=on ./scripts/fetch-and-sync.sh --fetch # force proxy route
# 手动替代方案:
uv run reddit-compass fetch --stealth # collect on Mac (~11 min)
scp data/snapshots/$(date +%F)/posts.jsonl deploy@VPS:/tmp/
ssh deploy@VPS "docker cp /tmp/posts.jsonl rc-api:/data/snapshots/$(date +%F)/"
```
### VPS 部署
```
./deploy/hostkey/deploy.sh
# 部署:API (FastAPI) + Caddy (reverse proxy) + 批量收集器
# → http://VPS_IP:8900/health
```
## API
OAuth2 客户端凭据 → JWT。针对外部使用者配置了 CORS。
```
# 获取 token:
curl -X POST http://localhost:8900/oauth/token \
-H "Content-Type: application/json" \
-d '{"client_id":"practicum","client_secret":""}'
# 查询帖子:
curl -H "Authorization: Bearer " \
"http://localhost:8900/api/v1/posts?date=2026-07-22&subreddit=artificial&limit=10"
```
| Endpoint | 认证 | 描述 |
|---|---|---|
| `GET /health` | — | 存活探针 |
| `GET /dashboard` | — | HTML 仪表板(编辑风格) |
| `GET /docs` | — | Swagger UI |
| `POST /oauth/token` | — | 客户端凭据 → JWT |
| `GET /api/v1/snapshots` | Bearer | 列出快照日期 |
| `GET /api/v1/posts` | Bearer | 带有过滤和分页的帖子 |
| `GET /api/v1/signals` | Bearer | LLM 提取的信号 |
| `GET /api/v1/stats` | Bearer | 聚合统计信息 |
## LLM 分析 (Qwen API)
不仅仅是收集——而是**情报**。分析**所有可用数据源**
(Reddit, HN, RSS, Ladder, ProductHunt)——即使没有 Reddit 数据也能工作。
```
export QWEN_TOKEN_PLAN_KEY=sk-... # QwenCloud: https://home.qwencloud.com/api-keys
uv run reddit-compass signals
```
对于每个帖子,LLM 会提取:
- **痛点** — 人们描述了什么问题
- **购买意向** — 是否有人正在寻找购买 AI 产品的机会?
- **业务相关性** (1–10) — 与企业级 AI 的相关程度
- **书籍相关性** (1–10) — 与叙事内容的相关程度
- **主题** — 关键话题(每个帖子 1–3 个)
然后进行综合:**前 5 大深度主题**(含解释)、**3 个专栏想法**、
**叙事转变**、**按书籍相关性排名前 10**、**所有痛点**。
### 模型金字塔(价格 / 质量)
| 任务 | 模型 | 原因 |
|---|---|---|
| **综合**(主题、专栏想法、叙事转变) | `qwen3.8-max-preview` | 复杂任务,调用次数少,可享 17:00–03:00 MSK 的非高峰折扣 |
| **分类**(每个帖子的痛点、相关性) | `qwen3.7-plus` | 量大,价格/质量均衡 |
| **简单任务**(过滤、总结) | `qwen3.6-flash` | 最便宜 |
## Dashboard 与趋势雷达
三种模式,三种场景:
| 视图 | URL | 目的 |
|---|---|---|
| **Today** | `/today` | 晨间简报:3–5 项变动,阅读推荐,工作进展 |
| **News** | `/news` | 已发布 Data Release 的原始收件箱:材料、数据源、板块、相关 story |
| **Stories** | `/stories` | 带有 evidence items 的具体事件;非原始新闻也非趋势 |
| **Trends** | `/trends` | 基于多个 stories 的重复性模式 |
| **Pulse** | `/pulse` | Reddit 原生社区信号:sub 内的百分位、速度、讨论深度、缺口 |
| **Radar** | `/radar` → `/runs/{date}/radar` | 完整的分析工作区:landscape、shelves、coverage、项目面板 |
| **Project Lens** | `/projects/rbc`, `/projects/book` | 基于已发布 stories/trends 的书籍/RBC/商业视角 |
| **Explore** | `/explore` | 兼容旧版 projection 的遗留搜索 |
| **Story 详情** | `/stories/{id}` | 已发布的 Engine story evidence;如有需要会降级到遗留详情页 |
| **Trend 详情** | `/trends/{id}` | 已发布的 trend pattern、成员 stories 和 evidence |
| **Runs** | `/runs` | 包含真实数量的运行历史 |
遗留版本(仅一个过渡版本):`/legacy/dashboard`, `/legacy/runs/{date}/radar`
### 带版本控制的 Story/Trend Engine
为了实现无需 full rebuild 的迭代,使用了 `trend_engine.db`:完整的 frozen Data Releases,
独立的 Facet/Story/Trend 尝试,Golden Set,仅针对灰度地带的 Qwen-review 以及原子化的
发布指针。
已发布的分析被明确拆分为 `News → Stories → Trends → Project Lens`。
参见 [`docs/NEWS_STORIES_TRENDS.md`](docs/NEWS_STORIES_TRENDS.md)。端到端的收集和
分析血缘记录在
[`docs/COLLECTOR_TO_TRENDS_FLOW.md`](docs/COLLECTOR_TO_TRENDS_FLOW.md)。
已发布的 Radar 包含一个链接这些层的驾驶舱(cockpit)部分。
```
reddit-compass engine release create --run 2026-07-29:broad
reddit-compass engine facets --release RELEASE_ID --profile broad
reddit-compass engine embeddings --release RELEASE_ID --model lexical-hash-v1
reddit-compass engine stories propose \
--facet-release FACET_ID \
--limit 50 \
--embedding-model lexical-hash-v1 \
--dense-top-k 24 \
--dense-threshold 0.55
reddit-compass engine stories inspect --story-release STORY_ID
reddit-compass engine reddit-pulse propose \
--release RELEASE_ID \
--date 2026-07-29 \
--profile broad \
--story-release STORY_ID
reddit-compass engine experiments compare --facet-release FACET_ID --limit 300
reddit-compass engine trends propose --story-release STORY_ID
reddit-compass engine publish --story-release STORY_ID --trend-release TREND_ID --channel shadow
```
`broad` 的发布受到质量门禁的限制;实验性版本仅在
`shadow` 中发布。`compass.db` 以只读模式打开;Radar 仅已发布的版本。旧的
`lab` 作为一个兼容别名保留一个版本。完整契约:
[`docs/TREND_ENGINE.md`](docs/TREND_ENGINE.md).
### 设计
动感优先,暗黑科技美学(根据 Awwwards 的描述:icreon-digital-velocity)。
两种主题:暗色(默认)+ 亮色切换。排版:Space Grotesk / Inter / JetBrains Mono。
Scroll-reveal,hover lift + glow,count-up KPI,环境背景。
### 故事卡片
Title → 直接跳转至最重要的数据源(主要证据)。
排名:content_scope(full > excerpt > abstract > headline)× 集群权重。
旁边的 📋 → 带有时间线和 evidence matrix 的完整故事。
集群:🗣 Voices,💻 Developers,📰 Mainstream,💰 Business,Tech/Culture,🚀 Products。
```
http://127.0.0.1:8900/today # утренний бриф
http://127.0.0.1:8900/radar # полный Radar
http://127.0.0.1:8900/explore # поиск + фильтры
http://127.0.0.1:8900/explore?date=2026-07-28&profile=broad&pain=security+breach
http://127.0.0.1:8900/legacy/dashboard # legacy
```
认证:Basic Auth(凭据位于 `.env.secrets`),Let's Encrypt TLS。
## 安全模型
| 层级 | 机制 |
|---|---|
| **机密** | `.env.secrets` (gitignored) + `detect-secrets` 提交前检查 |
| **API 认证** | OAuth2 客户端凭据 → JWT (1小时过期) |
| **网络** | 仅限 Loopback,Caddy 反向代理 |
| **容器** | `read_only`, `no-new-privileges`, `cap_drop: ALL` |
| **Reddit** | 只读,速率受限,仅限公开数据 |
| **Git** | 每次提交进行机密扫描,禁止 `--no-verify` |
完整规则:[`AGENTS.md`](AGENTS.md)
## 文档
| 文档 | 主题 |
|---|---|
| [`ARCHITECTURE.md`](ARCHITECTURE.md) | 包含图表的完整系统架构 |
| [`ROADMAP.md`](ROADMAP.md) | 阶段 2–6,状态 |
| [`docs/MULTI_SOURCE_PLAN.md`](docs/MULTI_SOURCE_PLAN.md) | 21 个数据源,5 个集群 |
| [`docs/COLLECTOR_TO_TRENDS_FLOW.md`](docs/COLLECTOR_TO_TRENDS_FLOW.md) | 从数据源收集到 News、Stories、Trends 和 Radar 的文本图表 |
| [`docs/DATA_FLOW_DIAGRAMS.md`](docs/DATA_FLOW_DIAGRAMS.md) | Mermaid 图表:Reddit → stories → trends → Reddit Pulse → 发布 |
| [`docs/ENGINE_REVIEW_V3.md`](docs/ENGINE_REVIEW_V3.md) | Engine v3 审查 + 阶段 1–8 计划及实施状态 |
| [`docs/TREND_ENGINE.md`](docs/TREND_ENGINE.md) | 规范的不可变 Engine 工作流、门禁和回滚 |
| [`docs/CLUSTER_LAB.md`](docs/CLUSTER_LAB.md) | 已弃用的 Cluster Lab 兼容指南 |
| [`docs/STORY_TREND_CLUSTERING_RESEARCH.md`](docs/STORY_TREND_CLUSTERING_RESEARCH.md) | 基于研究的 story/trend 聚类路线图 |
| [`docs/COMPETITIVE_ANALYSIS.md`](docs/COMPETITIVE_ANALYSIS.md) | GitHub 全景,Ladder |
| [`docs/IMPROVEMENTS.md`](docs/IMPROVEMENTS.md) | 排名后的改进计划 |
| [`CHANGELOG.md`](CHANGELOG.md) | 保持更新日志 |
| [`AGENTS.md`](AGENTS.md) | LLM 代理契约 |
## 技术栈
| 层级 | 技术 |
|---|---|
| 语言 | Python 3.12,严格模式的 mypy |
| 收集 | Playwright, aiohttp, Ladder 代理 |
| 存储 | JSONL + SQLite |
| LLM | Qwen API — 金字塔结构:qwen3.8-max-preview / qwen3.7-plus / qwen3.6-flash |
| API | FastAPI + uvicorn + JWT |
| 部署 | Docker + Caddy + host-cron |
| 质量 | ruff, 严格模式的 mypy, pytest (84%), detect-secrets |
| CI/CD | GitHub Actions → GHCR |
## 许可证
[MIT](LICENSE) © 2026 Denis Ermilov
标签:AI, API, LLM, Python, Unmanaged PE, 无后门, 特征检测, 自动化代码审查, 请求拦截, 资讯聚合, 趋势分析