我用 AI 构建了实时 HackerNews 趋势雷达(并且它会自行运行)

发布: (2026年1月16日 GMT+8 11:11)
12 min read
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the post (the paragraphs, headings, lists, etc.) in order to do so. Could you please paste the content you’d like translated (excluding the source link you’ve already provided)? Once I have the article text, I’ll translate it into Simplified Chinese while preserving the original formatting, markdown syntax, and technical terms.

Source:

每天,HackerNews 静静地决定开发者世界接下来会关注什么。

但如果你不是整天 doom‑scrolling(刷屏),就会错过真正的信号:哪些话题正在真正起飞,遍布各个帖子和深层评论链。

于是,我在其之上构建了一个 实时“趋势雷达”

  • 持续获取最新的 HN 文章和评论

  • 使用 LLM 提取结构化话题(公司、工具、模型、技术术语)

  • 将所有数据流入 Postgres,便于即时查询,例如:

    • “现在 HN 上有什么趋势?”
    • “今天哪些帖子在为 Claude / LangChain / Rust 推波助澜?”

所有这些都以 声明式 CocoIndex 流程 运行,具备增量同步、LLM 驱动的抽取以及简易查询处理器。

在本文中,你将看到它的端到端实现,以及如何 fork 它来追踪任何社区(Reddit、X、Discord、内部 Slack 等)。

为什么 HN 是金矿(如果你能结构化它)

HackerNews 是以下早期信号中最强的之一:

  • 开发者实际尝试的新工具和框架
  • 哪些 AI 模型/产品正在获得关注度
  • 评论中的真实情绪和反馈
  • 新兴创业公司和可能在 6‑12 个月内爆发的冷门库

但原始的 HN 存在三个问题:

  1. 线程嘈杂,评论层级混乱
  2. 除了自由文本外,没有“主题”概念
  3. 没有内置方式来询问:“整个信息流中有什么趋势?”

CocoIndex 中的 HackerNews Trending Topics 示例本质上是:“将 HN 转变为结构化、持续更新的主题索引,供 AI 代理和仪表盘在毫秒级查询。”

架构:从 HN 火焰流到可查询的话题

在高层次上,管道如下所示:

HackerNews API

HackerNewsConnector (Custom Source)
    ├─ list() → thread IDs + updated_at
    ├─ get_value() → full threads + comments
    └─ provides_ordinal() → enables incremental sync

CocoIndex Flow
    ├─ LLM topic extraction on threads + comments
    ├─ message_index collector (content)
    └─ topic_index collector (topics)

Postgres
    ├─ hn_messages
    └─ hn_topics

Query Handlers
    ├─ search_by_topic("Claude")
    ├─ get_trending_topics(limit=20)
    └─ get_threads_for_topic("Rust")

核心思想: 将发现(discovery)与获取(fetching)分离

  • list() 调用 HN Algolia 搜索 API,获取轻量级元数据:线程 ID + updated_at 时间戳。
  • get_value() 只对 updated_at 发生变化的线程运行,从 items API 获取完整内容 + 评论。
  • 序号(时间戳)让 CocoIndex 跳过所有未改变的内容,使后续同步的 API 调用减少超过 90 %。

这使得在 30 秒轮询间隔 的“实时模式”成为可能,而不会耗尽 API 配额或钱包。

步骤 1:将 HackerNews 打造成一等增量数据源

首先,定义 线程和评论的数据模型

class _HackerNewsThreadKey(NamedTuple):
    thread_id: str

@dataclasses.dataclass
class _HackerNewsComment:
    id: str
    author: str | None
    text: str | None
    created_at: datetime | None

@dataclasses.dataclass
class _HackerNewsThread:
    author: str | None
    text: str
    url: str | None
    created_at: datetime | None
    comments: list[_HackerNewsComment]

然后声明一个 SourceSpec,用于配置如何查询 HN:

class HackerNewsSource(SourceSpec):
    """Source spec for HackerNews API."""
    tag: str | None = None      # e.g. "story"
    max_results: int = 100      # hits per poll

自定义源连接器将该规范映射为实际的 HTTP 调用:

  • list() → 调用 https://hn.algolia.com/api/v1/search_by_date,参数 hitsPerPage=max_results,产生以线程 ID 为键、以 updated_at 为序号的 PartialSourceRow 对象。
  • get_value() → 调用 https://hn.algolia.com/api/v1/items/{thread_id},并将完整的线程及其嵌套评论解析为 _HackerNewsThread_HackerNewsComment
  • provides_ordinal() → 返回 True,让 CocoIndex 能进行增量同步。

CocoIndex 负责处理最难的部分:跟踪序号并在每次同步时仅重新拉取已更改的行。

步骤 2:使用 LLM 从每个线程和评论中抽取话题

源进入流后,真正有趣的部分开始了:语义增强

定义一个最小的 Topic 类型,由 LLM 填充:

@dataclasses.dataclass
class Topic:
    """
    A single topic extracted from text:
    - products, tools, frameworks
    - people, companies
    - domains (e.g. "vector search", "fintech")
    """
    topic: str

在流中,每个线程的主题通过单一声明式转换提取:

with data_scope["threads"].row() as thread:
    thread["topics"] = thread["text"].transform(
        cocoindex.functions.ExtractByLlm(
            llm_spec=cocoindex.LlmSpec(
                api_type=cocoindex.LlmApiType.OPENAI,
                model="gpt-4o-mini",
            ),
            output_type=list[Topic],
        )
    )

对评论执行相同操作:

with thread["comments"].row() as comment:
    comment["topics"] = comment["text"].transform(
        cocoindex.functions.ExtractByLlm(
            llm_spec=cocoindex.LlmSpec(
                api_type=cocoindex.LlmApiType.OPENAI,
                model="gpt-4o-mini",
            ),
            output_type=list[Topic],
        )
    )

在幕后,CocoIndex:

  • 使用结构化提示调用 LLM,并强制 output_type=list[Topic]
  • 将杂乱的自由文本规范化为一致的话题字符串
  • 将其视为另一列加入到数据集中

Source:

your flow instance, ready to be persisted to Postgres and queried instantly.

Instead of a Separate Glue Script

这就是把 HN 从“纯文本”转化为 AI 代理或 SQL 查询能够 推理 的内容的关键。

Step 3: Indexing Into Postgres for Fast Topic Queries

所有结构化数据被收集到两个逻辑索引中:

索引内容
message_index包含原始文本和元数据的线程 + 评论
topic_index与消息关联的单独主题

收集器只需声明一次,然后导出到 Postgres:

message_index = data_scope.add_collector()
topic_index   = data_scope.add_collector()

message_index.export(
    "hn_messages",
    cocoindex.targets.Postgres(),
    primary_key_fields=["id"],
)

topic_index.export(
    "hn_topics",
    cocoindex.targets.Postgres(),
    primary_key_fields=["topic", "message_id"],
)

现在你拥有了两张可以通过 SQL 或 CocoIndex 查询处理器操作的表:

  • hn_messages – 全文搜索、内容分析、作者统计
  • hn_topics – 主题层面的分析、趋势追踪、每个主题的线程排名

Step 4: Query Handlers – From “Cool Pipeline” to Real Product

这一步标志着项目不再是单纯的 ETL 演示,而是可以真正交付的产品。

search_by_topic(topic): “显示所有 Claude 的提及”

@hackernews_trending_topics_flow.query_handler()
def search_by_topic(topic: str) -> cocoindex.QueryOutput:
    topic_table = cocoindex.utils.get_target_default_name(
        hackernews_trending_topics_flow, "hn_topics"
    )
    message_table = cocoindex.utils.get_target_default_name(
        hackernews_trending_topics_flow, "hn_messages"
    )

    with connection_pool().connection() as conn:
        with conn.cursor() as cur:
            cur.execute(
                f"""
                SELECT m.id, m.thread_id, m.author, m.content_type,
                       m.text, m.created_at, t.topic
                FROM {topic_table} t
                JOIN {message_table} m ON t.message_id = m.id
                WHERE LOWER(t.topic) LIKE LOWER(%s)
                ORDER BY m.created_at DESC
                """,
                (f"%{topic}%",),
            )

            results = [
                {
                    "id": row[0],
                    "url": f"https://news.ycombinator.com/item?id={row[1]}",
                    "author": row[2],
                    "type": row[3],
                    "text": row[4],
                    "created_at": row[5].isoformat(),
                    "topic": row[6],
                }
                for row in cur.fetchall()
            ]

    return cocoindex.QueryOutput(results=results)

在 CLI 中运行:

cocoindex query main.py search_by_topic --topic "Claude"

你会收到一个整洁的 JSON 响应,包含 URL、作者、时间戳以及出现该主题的内容片段。

get_threads_for_topic(topic): 按主题分数对线程进行排名

并非所有提及都同等重要:

  • 如果 “Rust” 出现在 线程标题 中,则为 主要 讨论。
  • 如果它埋在评论里,则为 次要 提及。

get_threads_for_topic 使用加权评分模型,优先展示主题居中的线程。

该端点为仪表盘和代理提供数据。它会返回类似下面的列表:

[
  "Claude 3.7 Sonnet",
  "OpenAI o4-mini",
  "LangChain",
  "Modal",

]

每个主题都包含其分数、最新提及时间以及讨论该主题的热门线程。

你可以将其接入:

  • 实时仪表盘,显示 “最近 N 小时的前 20 大主题”
  • Slack 机器人,每日推送 “HN 上的热点趋势” 摘要
  • 监控内部研究代理,捕捉 HN 上的最新动向

与您的技术栈相关的信号

实时运行

一旦流程定义完成,保持实时运行只需一行命令:

# On‑demand refresh
cocoindex update main

# Live mode: keeps polling HN and updating indexes
cocoindex update -L main

CocoIndex 负责:

  • 每 30 秒轮询 HN(可配置)
  • 增量同步仅有更改的线程
  • 仅在需要时重新运行 LLM 提取
  • 导出到 Postgres 并暴露查询处理器

用于调试,CocoInsight 让您可以在 UI 中探索流程、查看血缘并尝试查询:

cocoindex server -ci main
# Then open: https://cocoindex.io/cocoinsight

您可以在此基础上构建的内容(超越 “Just HN”)

ExtensionIdea
跨社区趋势追踪将 Reddit 子版块、X 列表、Discord 频道、内部 Slack 等作为额外来源;对它们的主题进行归一化,以观察想法的传播路径。
情感感知趋势分析在主题旁加入基于 LLM 的情感提取步骤;不仅跟踪 什么 正在流行,还要跟踪开发者是 喜欢 还是 讨厌
影响者与关键贡献者映射使用 author 字段查看谁发起重要讨论,以及谁的评论推动了对话的进展。
持续知识图谱将主题视为节点,线程视为边,构建一个由真实讨论链接的工具、公司和人物的图谱。
实时 AI 研究代理将代理指向基于 Postgres 的索引,让它回答诸如以下问题:
• “本周人们正在实验的顶级新向量数据库有哪些?”
• “哪些 AI 评估框架正在获得关注?”

如果你身处数据、基础设施或 AI 领域,这基本上是 一个可自我更新的 HN 信号层,供你的工具和代理查询

想自己动手试试吗?

您可以在下面链接的仓库中找到完整可运行的示例(包括流定义、Docker 设置和快速启动脚本)。

GitHub – hackernews‑trending‑topics‑cocoindex

祝您玩得开心!

概览

在官方的 HackerNews Trending Topics 示例 中,探索 ding flow definition、自定义源、查询处理程序以及 Postgres 导出,查看 CocoIndex 文档和其 GitHub 仓库

如果你最终

  • 将其指向不同的社区
  • 添加嵌入、RAG 或情感分析
  • 将其接入真实产品或代理

…一定要分享回来!

这个模式最酷的地方在于,你只需要极少的代码,就能从“原始社区噪声”转变为 实时、可查询的趋势雷达

Back to Blog

相关文章

阅读更多 »

Rapg:基于 TUI 的密钥管理器

我们都有这种经历。你加入一个新项目,首先听到的就是:“在 Slack 的置顶消息里查找 .env 文件”。或者你有多个 .env …

技术是赋能者,而非救世主

为什么思考的清晰度比你使用的工具更重要。Technology 常被视为一种魔法开关——只要打开,它就能让一切改善。新的 software,...

踏入 agentic coding

使用 Copilot Agent 的经验 我主要使用 GitHub Copilot 进行 inline edits 和 PR reviews,让我的大脑完成大部分思考。最近我决定 t...