一个人运营博客、Telegram 频道、公众号三个出口,最痛的不是写不出内容,是写不过来——每天有价值的 AI 资讯太多了,手动翻译、改写、配图、排版、分发,一条链走完半天就没了。于是我把它拆成了一条管线,起名 LynxPipe:素材进,成品文章出,博客/TG/公众号三头分发,中间的"会动的逻辑"全收拢在一个仓里。
这篇文章把这条管线的架构、组件、路由、踩坑全拆给你看。
整条链路先看一眼
| |
一句话定位:博客仓只留 Hugo 内容,所有"会动的逻辑"住在 LynxPipe 仓里。这个拆法是为了让内容站保持瘦身——改管线不用碰内容,改内容不用碰管线。2026-08-09 起,管线脚本从 blog-lxlynx/scripts、lynxhot/scripts、lynxWechat 三处收拢沉淀进 LynxPipe。
触发层:cron + systemd timer
| 触发器 | 时间 | 跑什么 | 写入日志 |
|---|---|---|---|
| cron | 0 5,12,17 * * * | run_pipeline.sh 全链路一轮(抓→写→审→卡→构建→部署→分发→git) | runtime/pipeline.log + pipeline-err.log |
| cron | */30 * * * * | distribute.py 增量分发(不重新抓取/构建,只推新文章) | runtime/pipeline.log |
| cron | 17 */3 * * * | vendor_watch.py 厂商博客监控 | runtime/vendor_watch.log |
| 手动 | — | publish.sh(网页编辑器 lynxBlogEdit:1314 的"发布上线"按钮也调它) | runtime/pipeline.log |
| systemd timer | */30 | freebie-funnel.service 独立 TG 白嫖管线(见下文) | journalctl |
| systemd timer | 每日 | blog-image-check.service 博客图片巡检 | journalctl |
有个改动值得提:早期文档写"每天 9/15/21 点、每轮 2 篇",2026-08-11 起实改为 5/12/17 点、每轮 limit 10——取消条数限制(10 是防停机积压后单轮爆量烧 LLM 配额的安全上限,超出留源里下轮再写,不丢)。
输入层:RSS + 厂商博客,两条腿走路
光靠 RSS 不够。RSS 拿到的是聚合资讯,但"Anthropic/DeepMind/DeepSeek/智谱(z.ai)这些厂商自己的博客"往往是第一手消息,而且它们很多是单页应用(SPA),RSS 抓不到正文。
所以输入层两条腿:
news_rss.py:12 个 RSS 源 + AI 关键词过滤(白名单/黑名单),把噪音砍掉再进管线。自 lynxhot 收拢。vendor_watch.py:用 Playwright 渲染厂商博客列表页,抓正文,再交给同一个gen_article()改写。每 3 小时跑一次。
两条腿汇到同一个入口去重,后面流程一样。这样"聚合资讯"和"厂商一手"都能进,又不重复。
处理层:写稿、二审、渲染卡
这一层是管线的肉,三个动作串起来:
- 抓取 + 去重:
seen.json记已处理过的,抓过的不再进。 - LLM 写中英双语稿:走一个本地 OpenAI 兼容网关(CPA,:8317),模型
gpt-5.5,一次出中英两版。每轮设了 10 篇上限——不是限制产能,是防停机积压后单轮爆量烧光 LLM 配额。 - 独立二审
lynx_reviewer.py:写完不直接发,另起一个 LLM 镜头过一遍,查三件事——事实红线、夸大标题、中英是否一致。审查器异常时放行不卡管线,但留痕到runtime/reviews.jsonl。这是个权衡:宁可带病过也别让审查器挂掉整条线,事后看审计日志补救。
构建部署:Hugo + Cloudflare Pages
博客是 Hugo 站,部署到 Cloudflare Pages(project: blog-lxlynx)。流程是:中英互同步 → 封面 PNG 转 WebP(q82,省体积) → hugo --minify 构建 → wrangler pages deploy 推上去。
中英同步有个细节:中文新于英文超过 60 秒才重翻。不是每次都全量重译——那样一篇改一个字就重译整篇,token 烧不起。sync_en.py 还用 PRESERVE_KEYS/EXCLUDE_FILES 保护特殊文件不被误翻。
分发层:按"板块"订阅,不是全量推
这是我比较得意的设计。早期版本是"新文章全量推所有平台",结果 TG 被刷屏、公众号被限流、论坛被当成搬运号反感。后来改成路由表:
每个平台订阅某些板块,而不是收所有文章。文章按 frontmatter 的 categories 匹配板块,只发往订阅了该板块的平台。改分发范围只改一个 YAML(distribute_routes.yaml),不动代码。
6 个板块分区(写文章 frontmatter categories 从这里取):
| 板块 | 中文 | 英文 | 定位 |
|---|---|---|---|
ai-tools | AI 工具 | AI Tools | ① 引流主力(工具/账号/提链) |
security | 安全技术 | Security | ② 支付/安全(品牌) |
automation | 自动化 | Automation | ③ Agent/自动化(差异化) |
ai-news | AI 资讯 | AI News | ④ 行业资讯(铺量,管线自动) |
products | 产品落地 | Products | ⑤ 产品(品牌) |
deep-research | 深度研究 | Deep Research | 手写长文(白皮书/选型) |
平台路由(订阅哪些分区 + 用哪种语言 + 每轮上限):
| 平台 | 订阅分区 | 语言 | 每轮上限 | 说明 |
|---|---|---|---|---|
| Telegram @Lx_groups | ai-tools, ai-news | zh | 令牌桶(一波 3 条,超出每 50 分 1 条) | 经 lynxtg CF Worker;08-09 取消条数上限 |
| LinuxDo | security, automation, ai-tools | zh | 1 | Discourse API;技术社区吃深度 |
| 公众号 凌序之心 | ai-news, products, security, deep-research | zh | 2 | 直发限流 3h;草稿箱不限速;绝不自动群发 |
| X(未来) | ai-tools, automation, ai-news | en | 3 | 英文版正好喂 X |
| 小红书(未来) | ai-tools | zh | 1 | 教程向改写 |
几个关键取舍:
- 公众号只进草稿箱,绝不自动群发。群发永远人工后台点。这是红线——自动群发翻车没法撤。
- Telegram 取消了条数上限,改令牌桶:一波直推 3 条,超出每 50 分钟放 1 条。既不刷屏又不卡队列。
- LinuxDo 每轮只 1 篇,而且只发"原理拆解"角度的合规内容,技术社区吃深度不吃资讯搬运。
- 非板块分类(如"公告")不匹配任何分区,不会被分发。
仓内组件清单
| 路径 | 职责 |
|---|---|
scripts/pipe_config.py | 中枢配置:BLOG_ROOT / RUNTIME,env 可覆盖 |
scripts/news_rss.py | RSS 源配置 + AI 关键词过滤(自 lynxhot 收拢) |
scripts/feed_pipeline.py | 资讯管线主程序:抓取→去重→CPA 写稿→二审→渲染卡 |
scripts/lynx_reviewer.py | 发文前独立 LLM 二审;异常放行不卡管线;留痕 reviews.jsonl |
scripts/lynxcard_client.py | LynxCard 渲染客户端(只走 HTTP,无文件耦合) |
scripts/vendor_watch.py | 厂商官方博客监控(playwright 抓 SPA) |
scripts/sync_en.py | 中英互同步:中文新于英文 >60s 重翻;保护特殊文件 |
scripts/distribute.py | 多平台分发中枢;按 distribute_routes.yaml 路由 |
scripts/distribute_routes.yaml | 分发路由表:平台订阅分区而非全量,改范围只改 YAML |
scripts/png2webp.py | 封面 PNG→WebP 增量转换(q82) |
scripts/article_images.py | 文章配图 |
scripts/backfill_images.py | 回填缺失图片 |
scripts/publish.sh | 手动发布:同步英文→WebP→构建→部署→分发→git 备份 |
scripts/run_pipeline.sh | cron 自动管线:feed→构建→部署→分发→git 归档 |
scripts/wechat/ | 公众号模块:push_article.py 推草稿箱;update_draft.py 原地改(不删不重推,media_id 不变);wechat_cover.py 封面+HTML;wechat_draft.py 草稿箱 API;wechat_writer.py LLM 爆文写稿器 |
tools/ | 一次性迁移脚本归档(路径引用已失效,仅历史参考) |
runtime/ | 运行时状态(gitignored) |
外部服务依赖拓扑
| |
- CPA(:8317):OpenAI 兼容 LLM 网关,写稿/审查/翻译/写稿器全走它。
- LynxCard(:8790):通用卡片渲染服务,TG/小绿书/博客共用,保持独立。
- lynxtg(CF Worker):通用 TG 推送出口,白嫖频道等多项目共用,保持独立。
- hugo(
~/bin/hugo)+ wrangler(fnm Node 全局):构建与部署。 - Tavily:web-verify 联网核查(下节讲)。
另一条独立管线:freebie-funnel 的 5 层漏斗
上面讲的是博客资讯管线。还有一条独立的 TG 白嫖频道管线(freebie-funnel),不在 LynxPipe 仓里,但同属一个内容生态。它干的事不一样:抓各路免费 AI 资源(限免 API、白嫖额度、开源工具),挑值得推的,推到频道。
| |
主力模型 agnes-2.0-flash,整合模型 glm-5.2-fast-preview,经 worker /push 路由推频道。
为什么这么多层?因为白嫖频道的内容质量直接决定留存——推一条垃圾,掉几个粉。漏斗越深,误推越少。代价是每条候选要烧好几次 LLM,但省下来的"掉粉成本"远高于 token 成本。
防假精确:一道联网核查闸门(重点)
这是最近踩的坑催出来的,值得单独讲。
某天 funnel 推了一条"特斯拉车机接入豆包大模型"的资讯。核心新闻是真的(16 个源交叉证实),但文案里写了个版本号 2026.14.11。我看着眼生,联网一查——实际是 2026.14.13(新浪源标题原文可证)。LLM 把版本号"记错了",还编得特别自信,人眼根本看不出来。这叫假精确:模型编造一个看似权威的精确数字(版本号、日期)来显得可信。
问题在于:我已有的审查层(二审、层4事实核对)都抓不到这种假精确——它们查的是格式自洽、有没有夸大,不是"这个版本号在网上对不对"。LLM 自判查不了事实真伪。
于是加了一道联网核查闸门,分两 tier:
| Tier | 是什么 | 生效状态 |
|---|---|---|
| Tier1 | 层4"事实核对"镜头扩到版本号/日期/备案(LLM 自判,无需 key) | ✅ 立即生效 |
| Tier2 | webVerifyAndSanitize 推送前 Tavily 联网核查(fail-open:无 key/报错/无精确声明原样放行) | ✅ 已激活(复用既有 Tavily key,端到端验证 API 200) |
这套"防假精确"的逻辑我写进了全局纪律:凡是要发的内容含具体事实点(版本号/日期/合作/价格),先联网核查再发,不能只靠 LLM 自判兜底。LLM 自判查的是自洽,不是真伪。
运行时状态文件(runtime/,gitignored)
| 文件 | 用途 |
|---|---|
seen.json | feed_pipeline 抓取去重池 |
dist_seen.json | distribute 分发去重池(改名须同步 key) |
vendor_seen.json | vendor_watch 去重池 |
dist_rate.json | distribute 令牌桶限流 |
reviews.jsonl | lynx_reviewer 二审审计留痕 |
dist.env | secret:WORKER_URL / RUN_KEY / TAVILY_API_KEY 等 |
pipeline.log / pipeline-err.log | 主管线日志 |
vendor_watch.log / backfill.log | 各组件日志 |
红线
- 公众号只进草稿箱,绝不自动群发;群发永远人工后台操作。
- 博客不引流 pay.153.ink(非本人网站);国内媒体(公众号/微信)不上站。
runtime/、.env、data/全部 gitignored,凭据绝不入库。
踩坑血泪(完整 8 条)
- 公众号 IP 白名单:公众号
/token报 40164 就是出口 IP 不在白名单。本机跑着 Clash 8899 代理,出口 IP 浮动,公众号 token 接口必挂。解法是 wechat 模块脚本级清掉 proxy 环境变量,让它走直连。 - 浏览器 UA:Python urllib 默认 UA 会被 Cloudflare 拦(403/1010)。所有对外请求必须带浏览器 UA,别用默认。
- Hugo 的
data/目录:运行时状态文件一度放在博客仓的data/下,结果 Hugo 把它当配置解析,构建直接挂。教训:状态一律放管线仓的runtime/,别碰 Hugo 的约定目录。 - git 判变更:判断"有没有新内容要提交",要用
git status --porcelain不能用git diff --quiet——新文章是 untracked 文件,diff看不见,会误判成无变更跳过提交。 - 管道退出码:
wrangler | tail要配set -o pipefail,否则部署失败被吞还显示成功。 - sync_en 全量重译:批量改内容文件时,必须垫高英文文件的 mtime,否则同步逻辑判定"中文比英文新"会全量重译,token 瞬间烧光;文章改名必须同步
dist_seen.json的 key。 - wrangler PATH 失配:wrangler 装在 fnm(Node 版本管理器)的全局 bin 里,cron 的默认 PATH 不含它,部署直接
command not found。而且硬编码 Node 版本号会在 fnm 升级后失效(实案:v22.22.2 升 v24.18.0 后连续 3 天部署全挂)。解法是通配匹配$HOME/.local/share/fnm/node-versions/*/installation/bin。 - wrangler 部署凭证:cron 非交互环境必须显式给
CLOUDFLARE_API_TOKEN+CLOUDFLARE_ACCOUNT_ID+ 代理 8899,否则报"In a non-interactive environment, it’s necessary to set a CLOUDFLARE_API_TOKEN",部署全挂(08-10/08-11/08-14 多次实案)。
哲学总结
搭完这条管线,几个设计取向想清楚分享:
- 会动的逻辑收拢到一个仓,内容站保持瘦身。改管线不碰内容,改内容不碰管线。
- 按板块订阅分发,不是全量推。每个平台吃的内容不一样,一刀切全量推必然刷屏或被限流。
- 漏斗宁可过度:白嫖频道 5 层漏斗看着奢侈,但误推一次的掉粉成本远高于多烧几次 token。
- 防假精确要联网,不能靠 LLM 自判。LLM 自判查自洽不查真伪,版本号/日期这种假精确人眼难辨,必须联网核查兜底。
- fail-open:任何核查/审查环节挂掉都不能阻塞管线,宁可带病过也别停,事后看审计日志补救。
这套管线跑稳定之后,我一个人撑住三个出口的日常更新,省下来的时间用来写深度长文和做真正需要人脑判断的事。
感谢 Linux.do 和 OpenClaw 社区在搭建过程中提供的思路和工具支持。
延伸阅读:
