用 Codex /goal 做一个私人 X 信息筛选器:几小时搭起本地 AI 工作流
你有没有这种感觉:
打开 X,本来只想看几条有价值的信息。
结果十分钟过去了,看到一堆吵架、转发、情绪输出、营销号。
真正想看的东西,藏在信息泥石流里。
以前遇到这种小需求,我大概率会忍。
现在不一样了。
AI 已经快接进我的反射弧了。脑子里刚冒出一句:“这个能不能自动筛一下?”下一秒就会想:“那就做个小工具吧。”
尤其 Codex 有了 /goal 模式后,这种小工具真的可以奢侈一点。不是写个脚本凑合用,而是直接搭一个能跑、能看、能反馈、能慢慢变聪明的本地站点。
这篇就带你做一个:跑在 Mac Mini 上的私人 X Timeline 筛选器。
它会定时抓取内容,按你的关键词、偏好、重点账号打分,再分成:
- 精选:值得马上看的内容
- Inbox:普通内容,闲了再扫
- 忽略:低价值内容,少来烦我
你看内容时还能点反馈,系统会逐步贴近你的口味。
别怕,不搞大工程。几个小时能跑起来。
这个工具适合谁?
如果你有下面这些情况,基本可以直接开干:
- 每天刷 X,但经常刷完觉得空虚
- 关注账号太多,Timeline 像菜市场
- 想追 AI、产品、投资、技术动态,但不想被噪音淹没
- 有一台常开的 Mac Mini、NAS、小主机,或者云服务器
- 愿意花一点 API 成本,换回每天少刷半小时
我最推荐跑在自己的机器上。
比如 Mac Mini 常年在线,功耗低,稳定。再配一个 Tailscale,只在自己的网络里访问。安全、安静、不折腾公网域名。
最终效果长什么样?
你打开一个本地页面,比如:
https://x-filter.your-tailnet.ts.net
页面大概分三块:
1. 精选流
系统认为值得看的内容。
比如:
- 重点账号发的新工具
- 某个模型更新
- 你关注的关键词突然被多次提到
- 高质量长帖
- 和你最近反馈“有用”的主题接近的内容
2. Inbox
普通内容,不急。
这里像一个缓冲区。你可以一口气扫完,也可以几天不看。
3. 反馈按钮
每条内容旁边放几个按钮:
- 👍 有用
- 👎 没用
- ⭐ 重点关注
- 🙈 不想再看类似内容
别小看这几个按钮。
它们会让系统慢慢知道:你到底喜欢什么。
不是泛泛地“科技内容”,而是更细的东西:
- 你爱看 Claude / Codex / Cursor 的真实使用案例
- 你不爱看空喊口号的 AI 鸡血文
- 你关心开源项目,但讨厌纯转发
- 你喜欢带截图、代码、数据的内容
这才是私人筛选器的价值。
技术方案:别搞复杂,能用最重要
推荐用这套轻量组合:
X 数据源 / 第三方 API
↓
定时抓取任务
↓
SQLite 本地数据库
↓
规则打分 + 反馈权重
↓
Dashboard 页面
↓
Tailscale 内网访问
每一层都很朴素。
朴素的好处是:坏了也好修。
数据源
你可以用 X 官方 API,也可以用第三方 API。
很多第三方服务按量计费,10 美元用几个月并不夸张,取决于你的抓取频率和账号数量。
建议一开始别抓太多。
可以从这些范围开局:
- 20~50 个重点账号
- 10~30 个关键词
- 每 15~30 分钟抓一次
- 每次只取最近内容
先把闭环跑通,再扩大范围。
数据库
SQLite 足够了。
别一上来 PostgreSQL、Redis、向量库全家桶。
你只是筛 Timeline,不是造搜索引擎。
SQLite 的优势很直接:
- 单文件,备份方便
- 本地读写快
- 部署简单
- Codex 也容易改
Dashboard
页面不用花哨。
你真正需要的是:
- 列表清晰
- 打分可见
- 来源账号可见
- 能一键反馈
- 能按标签、账号、分数过滤
别一上来做炫酷 UI。
工具是拿来省脑子的,不是拿来参赛的。
数据表怎么设计?
用 SQLite 可以先建这几张表。
tweets 表
CREATE TABLE tweets (
id TEXT PRIMARY KEY,
author_id TEXT,
author_handle TEXT,
author_name TEXT,
text TEXT NOT NULL,
url TEXT,
created_at TEXT,
fetched_at TEXT,
score REAL DEFAULT 0,
bucket TEXT DEFAULT 'inbox',
raw_json TEXT
);
bucket 用来放分类结果:
featured 精选
inbox 普通
hidden 隐藏
rules 表
CREATE TABLE rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
value TEXT NOT NULL,
weight REAL NOT NULL DEFAULT 1,
enabled INTEGER NOT NULL DEFAULT 1
);
type 可以是:
keyword
author
negative_keyword
topic
feedback 表
CREATE TABLE feedback (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tweet_id TEXT NOT NULL,
action TEXT NOT NULL,
created_at TEXT NOT NULL
);
action 可以是:
like
dislike
star
hide_similar
打分规则:先用笨办法,够香
很多人做这种工具,一上来就想接大模型分类。
可以,但没必要急。
刚开始用规则打分就很好用。
示例规则:
重点账号 +5
普通关注账号 +2
命中强关键词 +4
命中普通关键词 +1
命中负面关键词 -5
内容过短 -1
包含链接 +1
包含代码片段 +2
被你点过类似内容有用 +3
被你点过类似内容没用 -3
然后按分数分桶:
score >= 8 featured
score >= 2 inbox
score < 2 hidden
一个简单 JS 版本可以这样写:
function scoreTweet(tweet, rules, feedbackStats) {
let score = 0;
const text = tweet.text.toLowerCase();
for (const rule of rules) {
if (!rule.enabled) continue;
const value = rule.value.toLowerCase();
if (rule.type === 'keyword' && text.includes(value)) {
score += rule.weight;
}
if (rule.type === 'negative_keyword' && text.includes(value)) {
score -= Math.abs(rule.weight);
}
if (rule.type === 'author' && tweet.author_handle === rule.value) {
score += rule.weight;
}
}
if (tweet.text.length < 40) score -= 1;
if (tweet.text.includes('http')) score += 1;
if (tweet.text.includes('```')) score += 2;
const authorStat = feedbackStats[tweet.author_handle];
if (authorStat) {
score += authorStat.likes * 0.5;
score -= authorStat.dislikes * 0.5;
}
return score;
}
分类函数也很简单:
function bucketByScore(score) {
if (score >= 8) return 'featured';
if (score >= 2) return 'inbox';
return 'hidden';
}
这个版本已经能干掉大量噪音。
别嫌它土。
能让你少刷 30 分钟,就是好工具。
反馈闭环:让工具慢慢懂你
反馈不需要做得很玄学。
你点一次 👍,系统记住:
- 这条内容有用
- 这个作者可能更重要
- 这类关键词可以加权
你点一次 👎,系统记住:
- 这个主题可能不值得推
- 这个作者权重可以降一点
- 某些词可能是噪音源
一个简单统计就能跑:
function buildFeedbackStats(feedbackRows, tweets) {
const stats = {};
for (const fb of feedbackRows) {
const tweet = tweets.find(t => t.id === fb.tweet_id);
if (!tweet) continue;
const handle = tweet.author_handle;
if (!stats[handle]) {
stats[handle] = { likes: 0, dislikes: 0, stars: 0 };
}
if (fb.action === 'like') stats[handle].likes += 1;
if (fb.action === 'dislike') stats[handle].dislikes += 1;
if (fb.action === 'star') stats[handle].stars += 1;
}
return stats;
}
想再聪明一点,可以让 Codex 帮你加一个“相似内容隐藏”。
不一定马上上向量数据库。
可以先用关键词近似:
- 抽取高频词
- 记录被隐藏内容里的主题词
- 新内容命中多个隐藏词就降权
够用了。
用 Codex /goal 怎么指挥?
/goal 的好处是,你不用一句一句让它改文件。
你给它一个目标,它会拆任务、看项目、改代码、跑命令、继续修。
这类小工具特别适合 /goal。
因为目标清楚,边界也清楚:
- 抓取
- 存库
- 打分
- 展示
- 反馈
- 定时任务
你可以用 5 轮 goal 把它推起来。
第 1 轮 /goal:搭项目骨架
可以直接这样写:
/goal 创建一个本地运行的 X timeline filter 项目。
技术栈用 Node.js + SQLite + 简单 Web Dashboard。
需要包含:
1. SQLite 数据库初始化
2. tweets、rules、feedback 三张表
3. 一个首页,展示 featured 和 inbox 两个列表
4. README,写清楚如何启动
项目要尽量轻量,方便跑在 Mac Mini 上。
这一轮不要塞太多需求。
先让项目能启动,页面能打开,数据库能初始化。
你要盯住三个结果:
npm install能跑npm run dev能打开- SQLite 文件能生成
跑不起来就继续让它修:
/goal 当前项目启动失败,请读取报错并修复。不要重构无关代码,只修到 npm run dev 可以正常打开首页。
第 2 轮 /goal:接入数据源
项目骨架稳了,再接 API。
/goal 为项目加入 X 数据抓取模块。
要求:
1. 从环境变量读取 API key
2. 支持配置重点账号列表和关键词列表
3. 抓取结果写入 tweets 表
4. 已存在的 tweet 不重复插入
5. 提供一个 npm run fetch 命令手动触发抓取
6. 抓取失败时记录错误日志,不要让程序崩溃
这里建议把账号和关键词放到配置文件里。
比如:
{
"authors": ["sama", "karpathy", "OpenAI"],
"keywords": ["codex", "claude", "cursor", "agent", "open source"]
}
.env 里放密钥:
X_API_KEY=你的_api_key
别把密钥写进代码。
真的,别。
第 3 轮 /goal:加入打分和分桶
数据进来了,就该筛了。
/goal 加入规则打分系统。
要求:
1. rules 表支持 keyword、negative_keyword、author 三种规则
2. 抓取新 tweets 后自动计算 score
3. 根据 score 写入 bucket:featured、inbox、hidden
4. Dashboard 上展示每条内容的 score 和命中的规则
5. 提供一个 npm run rescore 命令,可以重新计算所有历史内容
这里有个关键点:展示命中的规则。
不然你会不知道它为什么把某条内容推上来。
工具一旦黑箱,你就会开始不信它。
页面可以显示:
Score: 9.5
Matched: author:karpathy +5, keyword:agent +3, has_link +1
看着就踏实。
第 4 轮 /goal:做反馈按钮
接下来加反馈。
/goal 为 Dashboard 加入反馈功能。
要求:
1. 每条 tweet 有 like、dislike、star、hide similar 四个按钮
2. 点击后写入 feedback 表
3. like 会提高该作者和相关关键词权重
4. dislike 会降低该作者和相关关键词权重
5. star 的内容固定进入 featured
6. 操作后页面不要整页刷新,交互要轻量
反馈别设计太多按钮。
按钮越多,你越懒得点。
四个已经够了。
推荐的反馈含义
| 按钮 | 行为 | |---|---| | 👍 有用 | 同类内容加权 | | 👎 没用 | 同类内容降权 | | ⭐ 收藏 | 固定精选 | | 🙈 隐藏类似 | 强力降权 |
用几天后,你会明显感觉精选区变干净。
这感觉很爽。
像有人提前帮你把信息垃圾倒了。
第 5 轮 /goal:定时任务 + Tailscale 访问
工具能用了,还差自动跑。
/goal 加入后台定时抓取能力,并补充 Mac Mini 部署文档。
要求:
1. 支持每 30 分钟自动运行 fetch 和 rescore
2. 提供 launchd 配置示例,方便在 macOS 开机自启
3. README 增加 Tailscale 访问方式
4. 不开放公网,默认只监听本机或内网地址
5. 增加简单健康检查页面 /health
Mac 上可以用 launchd 保活。
示例 plist 大概长这样:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.local.xfilter</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/npm</string>
<string>run</string>
<string>start</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/你的用户名/projects/x-filter</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/x-filter.log</string>
<key>StandardErrorPath</key>
<string>/tmp/x-filter.err</string>
</dict>
</plist>
配好 Tailscale 后,你可以只在自己的 Tailnet 里访问。
这样手机、iPad、笔记本都能看。
但外人访问不到。
舒服。
/goal 提示词模板:直接复制改
下面这套模板可以反复用。
修 Bug 模板
/goal 当前功能有 bug。请先阅读相关代码和错误日志,再给出最小修改方案并实施。
要求:
1. 不要重写整个项目
2. 不要改无关文件
3. 修复后运行测试或启动命令验证
4. 在回复里列出修改了哪些文件,以及为什么改
加功能模板
/goal 为项目加入【功能名称】。
业务目标:【一句话写清楚】。
验收标准:
1. 【页面能看到什么】
2. 【数据库写入什么】
3. 【命令如何运行】
4. 【失败时如何处理】
限制:保持项目轻量,不引入大型依赖,不破坏现有功能。
重构模板
/goal 请整理当前项目结构,让代码更容易维护。
要求:
1. 保持功能不变
2. 把抓取、打分、数据库、页面逻辑拆开
3. 补充必要注释
4. 修改后运行现有命令确认可用
文档模板
/goal 为这个项目补充 README。
需要包含:
1. 项目用途
2. 安装步骤
3. 环境变量说明
4. 常用命令
5. Mac Mini 部署方式
6. Tailscale 访问方式
7. 常见问题排查
我建议保留的功能边界
这个工具很容易越做越大。
克制一点。
推荐保留这些:
- 抓重点账号
- 抓关键词
- 规则打分
- 精选 / Inbox / 隐藏
- 手动反馈
- 定时运行
- 本地访问
暂时别急着做这些:
- 多用户系统
- 复杂权限
- 大模型逐条总结
- 全文向量搜索
- 自动发邮件日报
- 复杂移动端 App
不是不能做。
只是早期会拖慢你。
你真正想解决的是:打开页面,看到值得看的东西。
别把小工具做成小祖宗。
避坑清单
1. API 抓取频率别太猛
一开始每 30 分钟抓一次就够。
抓太频繁,成本上去,价值没多多少。
还可能触发限制。
2. 不要把 API Key 提交到 Git
.env 加进 .gitignore。
如果已经提交过,立刻换 key。
别赌。
3. 规则要能解释
每条内容为什么进精选,要显示原因。
不然你后面调分会很痛苦。
4. 反馈按钮别太多
按钮越多,使用成本越高。
你会从“主动训练工具”,变成“懒得点”。
5. 别一开始就接大模型
LLM 分类当然香。
但每条都跑,成本和延迟都会来找你。
建议等规则版稳定后,再给精选内容做摘要。
6. 本地服务别裸奔公网
如果只是自己用,Tailscale 够了。
没必要暴露公网端口。
7. 备份 SQLite
SQLite 是单文件,很方便。
每天定时复制一份就行。
cp data/x-filter.db backups/x-filter-$(date +%F).db
小动作,救大命。
可以继续加的高级玩法
等基础版稳定后,可以慢慢加这些。
每日摘要
每天早上自动生成一页:
今天值得看的 10 条内容
3 个高频主题
5 个新工具
2 个争议话题
这时可以接大模型。
只总结精选区,不要全量总结。
省钱,也更准。
主题聚类
把相似内容聚到一起。
比如同一天很多人在讨论一个新模型,就合并成一个主题卡片。
你不用看 20 条重复转发。
重点账号异常提醒
某些账号平时很少发帖,一发就是大事。
可以单独提醒。
比如:
- 某个研究员发了新论文
- 某个产品负责人发了路线图
- 某个开源作者发布新版本
稍后读队列
对长帖加一个“稍后读”。
晚上统一看。
白天别被长帖拖走。
一个更现实的使用节奏
这个工具不是让你完全不刷 X。
它更像一个信息门卫。
我的建议是:
- 早上打开精选区,看 5 分钟
- 中午扫一眼 Inbox,顺手点反馈
- 晚上看每日摘要或稍后读
- 每周调整一次关键词和账号权重
几天后你会发现,自己不再被 Timeline 牵着走。
你想看什么,工具就把什么端上来。
剩下的噪音,放进 Inbox 慢慢冷却。
小结
Codex 的 /goal 很适合做这种私人小工具。
原因很简单:目标明确,模块独立,反馈快。
你不需要等一个完美产品出现。
你可以用几个小时,给自己搭一个刚好够用的版本。
Timeline 太乱,就做个筛选器。
资料太散,就做个整理器。
日报太烦,就做个汇总器。
AI 真正爽的地方,不是帮你写一段漂亮代码。
是你脑子里冒出一个小需求时,可以马上把它变成一个能跑的工具。
这才叫接进反射弧。