> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 用AI查找有趣的 Hackaday 帖子
- URL: https://www.hubwiz.com/blog/find-interesting-hackaday-posts-using-ai/
- Published: 2026-02-17T13:57:00.000Z
- Updated: 2026-02-17T13:57:00.000Z
- Description: 大多数人都认为每篇 Hackaday 文章都很有趣，我花了一个周末的时间编写了一个AI工具。
- Author: admin
- Tags: APPLICATION, AI, #Import 2026-10-14 14:41

大多数人都认为每篇 [Hackaday](https://hackaday.com/?ref=hubwiz.com) 文章都很有趣，我确实喜欢偶尔浏览，但有时我会变得很忙，错过一些真正很酷的东西。我甚至错过了关于我自己项目的文章。

我花了一个周末的时间编写了一个工具，它可以：

- 每天下载 Hackaday RSS 订阅源
- 将 RSS 订阅源中的文章存储到本地数据库中
- 使用 AI LLM 根据我会喜欢的程度为它们分配分数
- 当发现新的有趣文章时向我发送电子邮件
- 有一个 Web UI，以便我可以一目了然地了解它在做什么

所有代码都[在线提供](https://github.com/scottmbaker/ai-rss-scraper?ref=hubwiz.com)，任何想尝试的人都可以使用，但对外部服务有一些先决条件/要求。

## 1、先决条件/要求

虽然软件本身是一个独立的 Golang 二进制文件，但它依赖于两个外部服务。

- 一个符合 OpenAI API 标准的提供商。我使用 [Poe](http://poe.com/?ref=hubwiz.com)，因为工作中有人向我推荐它作为与许多不同模型交互的一种方式。我试过直接使用 Google Gemini，效果很好。它应该适用于任何兼容的服务。
- 一个电子邮件发送服务，如果您希望它能够发送电子邮件。我使用一个名为 [smtp2go](http://smtp2go.com/?ref=hubwiz.com) 的服务。我发现让 gmail 做这件事真的很挑剔，我永远无法信任我的 prometheus 警报会被发送出去而不会违反某些垃圾邮件过滤规则。这也适用于这种情况。

从技术上讲，您不需要电子邮件服务，因为程序可以生成本地 html 报告，并且可以通过 http 提供结果。但是，您绝对需要 AI 服务，否则它将无法为文章评分。

## 2、评分和提示工程

AI 评分需要一个提示词。我的提示词是：

```
Scott 喜欢与复古计算机和语音合成器相关的项目。
特别是他喜欢 4004、8008、8080、8085、8086、z80 和 z8000 CPU。
他喜欢像辉光管这样的独特显示技术。
如果树莓派和微控制器项目有一些独特或复古的元素，他也会喜欢。
他喜欢修复旧的和稀有的计算机。
根据 Scott 会喜欢这个项目的程度产生 0 到 100 之间的数字分数，并提供三个关于他会喜欢什么的要点。
如果文章是关于 Scott Baker 或 smbaker 的，给它打 100 分。
标题: {{.Title}}
描述: {{.Description}}
内容: {{.Content}}

```

请注意，我添加了换行符以在博客文章中格式化得很好；实际上，在底部的模板化段之间只有换行符。我不是专业的提示词工程师，但我试着告诉它做什么，并要求它提供 0 到 100 的分数以及为什么它这样认为的理由。

在提示词的底部有三个模板替换，代码在发送提示词之前执行。这些包括文章标题、文章描述和内容。内容被修剪到 500 字节，因为这应该足以了解一篇文章的感觉。

不同的人当然会使用不同的提示词。对于模型，我选择了 *gemini-3-flash*。我问了 Claude 的推荐，它建议我切换到 *claude-3.5-sonnet*，这是我可能会考虑的。评估不同模型给出的评分是我这个项目的目标之一。

## 3、下载 RSS

这非常容易。在 github.com/mmcdole/gofeed 有一个库可以下载 RSS 文章。请注意，您收到的文章数量由服务决定。我相信 Hackaday 发送大约一天的文章数量。

您从 RSS 收到的响应可能充满了 HTML 标签。我使用了 *github.com/microcosm-cc/bluemonday* 中的一个库，通过删除 HTML 标签来清理结果。这使得使用起来更容易，而且我们真的不想将所有那些 HTML 发送到 LLM。

## 4、在数据库中存储文章

起初我打算使用 *Ent*，这是我在生产软件中通常使用的 Go ORM 层。但是，我评估了替代方案，并提议使用 *sqlite* 的简化解决方案。我们只需要处理一个数据库表，将数据库存储在本地文件中对于开发和部署都很方便。如果这是生产软件，我可能会启动 postgres 并使用 Ent，但它不是。

数据库的主要原因是建立历史记录，因为 RSS 返回的项目数量是有限的。我希望能够尝试更改提示词或更改模型。

数据库存储以下内容：

- GUID：文章的唯一标识符
- 标题：文章标题
- 链接：文章链接，以便我可以阅读它
- 描述：文章描述
- 内容：最多 4KB 的内容
- 日期：发布时间
- 分数：从 0 到 100
- 分析：为什么它与我相关的三个要点
- Feed\_url：这个 RSS 订阅源来自哪里，以防我以后想支持多个订阅源
- 模型：用于评分的模型
- 已报告：True|False，文章是否在电子邮件报告中发送给我

## 5、调用 LLM

使用 *github.com/sashabaranov/go-openai* 中的 openai 库非常简单。要连接到 LLM，我们必须给它一个其 API 所在的 URL 以及允许我们访问的令牌。然后我们采用我在上面显示的提示词模板，并替换相关的文章细节。通过 openai 库对 LLM 的实际调用看起来像这样：

```
  resp, err := client.CreateChatCompletion(
    context.Background(),
    openai.ChatCompletionRequest{
     Model: model, // 模型名称，例如 gemini-3-flash
     Messages: []openai.ChatCompletionMessage{
      {
       Role:    openai.ChatMessageRoleUser,
       Content: articlePrompt, // 我们的提示词，基于填入文章详情的模板
      },
     },
    },
  )

  if err != nil {
   log.Printf("  调用 AI 时出错: %v", err)
   continue
  }

  content := resp.Choices[0].Message.Content // 这是 LLM 的响应

```

之后，通过正则表达式查找单词 "score" 和附近的数字来提取分数。有时 LLM 的响应不包括单词 "score"，所以我只查找第一个数字。这里查询多个模型会很方便，只是为了确保模型没有产生损坏的结果并导致分数被误解。

## 6、生成报告并通过电子邮件发送

报告是从 HTML 模板生成的，只包含以前没有报告过的*新*文章。我选择将模板直接放在代码中以最小化外部依赖 — Go 的单二进制方法确实使部署和使用变得容易。

在 golang 中已经有一个内置的 net/smtp 库可以通过 SMTP 服务器处理发送邮件的重活。正如我在开头提到的，由您自己提供 SMTP 服务器。

除了发送电子邮件，您可以选择将报告写入文件。这对开发很有好处。

## 7、提供网页服务

起初我认为电子邮件界面就足够了，但能够在 GUI 中可视化浏览数据库也非常不错。这是通过典型的 golang net/http 包完成的，使用 http.ListenAndServe()。

我利用 Google Antigravity 为我设计网页，因为我的 javascript 日子已经过去了，而且我的 CSS 和 HTML 都有点生疏。我对它做的工作质量感到惊讶。

## 8、让我们做一个演示...

首先，fetch 命令可用于获取一批新的文章。

```
$ bin/ai-rss-scraper fetch
从 https://hackaday.com/blog/feed/ 获取最新项目
- 新：电动推子为您的 PC 制作了一个很棒的音量混音器
- 新：托马斯·爱迪生可能发现了石墨烯
- 新：便宜的智能戒指变成 MIDI 控制器
- 新：秘密成分
- 新：从命令行播放 YouTube
- 新：通过 Linux 内核内存压缩应对 RAM 价格挤压
- 新：卧虎藏式打字机，隐形 PC
获取完成：收到：7，已存在：0，添加：7

```

数据库是空的，RSS 订阅源提供了七篇新文章被摄取。接下来，让我们尝试评分。在这里，我们需要设置我们的 API\_KEY 来使用我们的 AI 服务，在我的情况下是 Poe。

```
$ export API_KEY=my_api_key
$ bin/ai-rss-scraper score
发现 7 篇未评分的文章
评分：电动推子为您的 PC 制作了一个很棒的音量混音器
  分数: 35
评分：托马斯·爱迪生可能发现了石墨烯
  分数: 20
评分：便宜的智能戒指变成 MIDI 控制器
  分数: 25
评分：秘密成分
  分数: 12
评分：从命令行播放 YouTube
  分数: 15
评分：通过 Linux 内核内存压缩应对 RAM 价格挤压
  分数: 30
评分：卧虎藏式打字机，隐形 PC
  分数: 55

```

我们得到了一篇分数高于 50 的文章，尽管我可能需要调整我的提示词，因为那篇"电动推子"的文章实际上非常有趣 — 这是风险之一，评分只与您设计的提示词一样好。接下来，让我们做一个列表，看看数据库是什么样的：

```
$ bin/ai-rss-scraper list
[ 35] 电动推子为您的 PC 制作了一个很棒的音量混音器 (2026-02-01)
[ 20] 托马斯·爱迪生可能发现了石墨烯 (2026-01-31)
[ 25] 便宜的智能戒指变成 MIDI 控制器 (2026-01-31)
[ 12] 秘密成分 (2026-01-31)
[ 15] 从命令行播放 YouTube (2026-01-31)
[ 30] 通过 Linux 内核内存压缩应对 RAM 价格挤压 (2026-01-31)
[ 55] 卧虎藏式打字机，隐形 PC (2026-01-31)

```

这证明我们持久化了数据。现在让我们生成一个报告。

```
$ bin/ai-rss-scraper report --out report.html
报告生成于: ./report.html
处理了 1 篇文章。
2026/01/31 19:26:59 标记 1 篇文章为已报告。

```

最后，让我们看看它生成的报告。这与通过电子邮件发送的报告相同。

![无](https://www.hubwiz.com/blog/content/images/2026/02/1-7K9pKx4wY1solrkGmoolyQ.jpeg)

报告已生成

我们可以看到，一篇分数 ≥ 50 的文章被选中，它是关于将打字机重新用作可携带计算机的那篇。LLM 列出了三个关于为什么它认为这文章对我有趣的原因。LLM 的好处是我们不必进行精确的字符串匹配。我们描述我们想要的，LLM 能够匹配它并生成分数。结果中有一些变化。如果您多次重新运行 LLM，您可能会得到另一个分数。例如，我看到同一篇文章为我评分为 55、57 和 60。

如果我们真的想小心，我们会通过多个不同的模型对文章进行评分，然后对它们进行平均，或者选择两个最一致的模型。也有一些可以使用的采样方法 — 使用低成本模型生成粗略结果，然后使用高成本模型有选择地验证结果，以确保低成本模型正确执行。我没有采取任何这些技术的步骤，因为 gemini-3-flash 采样目前似乎足够。

最后，让我展示工具如何发送电子邮件。首先，我有一个配置文件设置，其中包含所有相关的配置设置：

```
$ cat scott-config.yaml
email_to: <my-email-address>
email_from: <the-email-to-put-in-from>
email_smarthost: <address-of-smtp-server>
email_identity: <email identity>
email_username: <smtp-server-username>
email_password: <smtp-server-password>
email_subject: "hackaday blog articles"

```

然后我使用 — send-email 选项执行 report 命令：

```
$ bin/ai-rss-scraper --config scott-config.yaml report --send-email --always
使用配置文件: scott-config.yaml
2026/01/31 19:34:39 通过 <address-of-smtp-server> 向 <my-email-address> 发送电子邮件...
2026/01/31 19:34:40 电子邮件发送成功。
处理了 1 篇文章。
2026/01/31 19:34:41 标记 1 篇文章为已报告。

```

您会注意到我在上面使用了 *— always* 选项。这告诉它忽略文章以前是否被报告过，否则我们之前对文件的报告会将文章标记为已报告，而这个命令将不会发现任何要报告的内容。

## 9、Dockerfiles 和 Helm Charts

在我的家庭实验室中，我运行 Kubernetes 来运行各种服务，包括 Prometheus 和 Grafana。ai-rss-scraper 是我在 Kubernetes 下运行的另一个服务。

在仓库中检入了一个 Dockerfile，可用于构建镜像，以及一个将部署它的 helm chart。

与手动运行工具一样，您必须自定义 values.yaml 来设置 API 密钥和所有电子邮件选项。设置持久卷（hostpath 就可以）也是明智的，这样数据库不会在 pod 重启时丢失。最后，有一些选项可以将内置 Web 服务器连接到 nodeport、负载均衡器和/或 ingress。

作为参考，这就是我在 values.yaml 中放的内容：

```
config:
  apiKey: <my-api-key>
  email:
    to:  <my-email-address>
    from: <the-email-to-put-in-from>
    smarthost: <address-of-smtp-server>
    identity: <email identity>
    username: <smtp-server-username>
    password: <smtp-server-password>
    subject: "hackaday blog articles"
storage:
  className: ai-rss-storage
  hostPath:
    enabled: true
    path: "/var/ai-rss-scraper"
service:
  type: NodePort
  port: 80
  nodePort: 31305
ingress:
  enabled: true
  hosts: [ai-rss-scraper.lan]

```

## 10、结束语

我希望您喜欢这篇关于使用 AI 筛选我的 RSS 订阅源的实际应用的文章。这对我来说有点像是一个练习，是为了尝试一些新技术，包括 Google Antigravity，并看看我是否喜欢它们。

---

原文链接: [Using AI to find interesting Hackaday Posts](https://medium.com/@smbaker/using-ai-to-find-interesting-hackaday-posts-d02c6b04a5a9?ref=hubwiz.com#bypass)

汇智网翻译整理，转载请标明出处