1. 从一条 X 推文到一次微信提醒:Codex 重置雷达配置全流程拆解
Codex 重置雷达是一套把公开 X 推文转成微信订阅消息的监控链路,核心能力是“认人、认事、只提醒一次”。它适合两类人:一类是重度使用 Codex 的开发者,想第一时间知道额度或配额是否重置;另一类是正在学事件驱动架构的工程师,想找一个真实可跑通的小项目练手。整条链路并不复杂,但真正落地时会卡在三个地方:怎么判断一条推文是“新事件”而不是“内容变化”、怎么保证同一件事不重复打扰、微信发送失败时事件怎么不丢。这篇就把这三道关拆开,给你一份可以直接复制的config.toml和settings.json骨架,再带你手动触发一次,确认从推文到提醒的完整通路能跑通。
我试过把这套链路拆成采集器和小程序服务两个进程,中间用一个内部接口交接,踩过的坑基本都集中在“事件去重”和“失败补偿”上。下面按落地顺序讲,你可以边看边改配置。
2. TaoToken 前置:先把模型调用和 Key 准备好
雷达本身不依赖大模型也能跑,但如果你想让它在判断“这条推文是不是 Codex 重置信号”时更稳一点,可以接一层语义判断。这时候就需要一个稳定的模型调用入口。TaoToken 提供统一的 API 地址,兼容常见的 OpenAI 风格调用,配置里只需要改base_url和api_key两个字段。
官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台创建 Key 即可。API 地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接填到配置里就行。
如果你只是先跑通“推文到微信”这条链路,模型判断可以先留空,等链路通了再补。但如果你打算长期跑,建议把 Key 管理好,别硬编码在代码里。控制台地址:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,API Keys 管理页:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
注意:Key 只放在服务端环境变量或本地配置文件里,不要提交到公开仓库。采集器和小程序服务用同一个 Key 时,建议拆成两个,方便单独吊销。
3. 可复制配置:config.toml 与 settings.json 骨架
整条链路有两个配置文件:采集器用config.toml,小程序服务用settings.json。先给采集器的骨架。
# config.toml —— 采集器配置 [source] # 监控的公开 X 账号,这里以 Tibo 为例 handle = "thsottiaux" # 只接受指向 status 的链接 url_pattern = "^https://x\\.com/thsottiaux/status/\\d+$" # 采集间隔,单位秒 poll_interval = 90 [event] # 判断新事件的核心字段:最新重置时间 reset_time_field = "latest_reset_at" # 去重键优先级:先推文 ID,再重置时间 dedup_keys = ["tweet_id", "reset_time"] # 历史记录文件,用于比对 history_file = "./data/history.jsonl" [handoff] # 交接给小程序服务的内部接口 endpoint = "http://127.0.0.1:8787/internal/events" # 短重试次数 retry_times = 3 # 重试间隔,单位秒 retry_backoff = [2, 5, 10] # 交接失败后的本地补偿队列 compensate_file = "./data/compensate.jsonl" [model] # 可选:语义判断,不填则跳过 base_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" model = "gpt-4o-mini" enabled = false再给小程序服务的骨架。
{ "server": { "host": "127.0.0.1", "port": 8787, "internal_token": "change-me-internal" }, "wechat": { "appid": "your-appid", "secret": "your-secret", "template_id": "your-template-id", "page": "pages/radar/index" }, "queue": { "pending_file": "./data/pending.jsonl", "max_retry": 5, "retry_backoff": [5, 15, 60, 180, 600] }, "dedup": { "store_file": "./data/sent.jsonl" } }两个文件里最关键的是dedup_keys和retry_backoff。前者决定“同一件事只提醒一次”能不能成立,后者决定“微信暂时发不出去,事情会不会丢”。internal_token是采集器和小程序服务之间的共享密钥,别用默认值。
4. 验证请求:手动触发一次完整通路
配置写好后,先别急着开定时任务,手动触发一次,确认推文到提醒能跑通。分三步。
第一步,启动小程序服务。
python -m radar.server --config ./settings.json看到listening on 127.0.0.1:8787就说明服务起来了。
第二步,手动跑一次采集器,只处理一条测试推文。
python -m radar.collector --config ./config.toml --once --dry-run--once表示只跑一轮,--dry-run表示不真正发送,只打印判断结果。正常输出类似:
[source] fetched 1 candidate [event] tweet_id=1234567890 reset_time=2025-01-01T10:00:00Z [event] is_new=true reason=reset_time_newer [handoff] dry-run, skip post如果is_new=false,说明历史记录里已经有这条,去重生效了,这是对的。
第三步,去掉--dry-run,真正交接一次。
python -m radar.collector --config ./config.toml --once采集器会 POST 到http://127.0.0.1:8787/internal/events,小程序服务收到后写入待发送队列,后台任务再调微信订阅消息接口。你手机上应该能收到一条“发现新的 Codex 重置公开信号”。如果没收到,先看服务日志里有没有wechat send ok,再看pending.jsonl里事件是否还在。
提示:第一次验证时,把
poll_interval调大一点,避免定时任务和手动触发撞在一起,日志会乱。
5. 本篇常见错排查
错误一:is_new一直是 false,但明明是新推文。大概率是reset_time_field对不上。采集器解析出来的字段名和配置里写的不一致,比对时就会拿空值去比,结果永远不新。解决方法是先--dry-run打印原始记录,确认字段名,再改配置。
错误二:微信发送报40001或40003。这是 access_token 或 appid/secret 的问题。检查settings.json里的appid和secret是否和微信后台一致,access_token 是否有缓存过期。服务里建议做一层 token 缓存,别每次发送都重新获取。
错误三:事件交接失败后丢了。看compensate.jsonl有没有写入。如果没写,说明retry_times用完后直接抛异常了,没进补偿队列。检查handoff段的compensate_file路径是否有写权限。
错误四:同一件事收到两条提醒。去重键没生效。检查dedup_keys里的tweet_id是否真的从推文里解析出来了,有些转发或引用推文的 ID 和原推不同,需要单独处理。另外sent.jsonl如果被清空,去重也会失效。
错误五:模型判断开启后采集变慢。每条推文都调一次模型,延迟会叠加。建议只在“来源核对通过、但重置时间判断模糊”时才调模型,别每条都调。模型对话入口:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
6. 长期跑起来:把雷达变成值班员
链路跑通一次之后,接下来就是让它稳定跑。采集器建议用 systemd 或 supervisor 托管,小程序服务的后台发送任务单独起一个 worker,别和 HTTP 服务混在一个进程里。补偿队列要定期清理,已经成功发送的事件从pending.jsonl里删掉,避免文件无限增长。
如果你打算长期跑,并且想让雷达在判断信号时更聪明一点,可以看看 Coding Plan,把模型调用额度固定下来,避免临时 Key 过期导致判断链路断掉:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。Claude Code 相关的接入配置也可以参考:https://taotoken.net/claude-code?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。
最后留一个实用技巧:把history.jsonl和sent.jsonl分开存,前者记录所有采集到的信号,后者只记录已提醒的。这样排查“为什么没提醒”时,先看 history 里有没有,再看 sent 里有没有,两步就能定位是采集问题还是发送问题。