Featured image of post 一个人撑起多渠道:我的 AI 内容自动化管线 LynxPipe 是怎么搭的

一个人撑起多渠道:我的 AI 内容自动化管线 LynxPipe 是怎么搭的

从 RSS 采集到博客/Telegram/公众号三头分发,一条用 LLM 串起来的内容管线——含 5 层漏斗、防'假精确'的联网核查闸门、完整组件清单与踩坑血泪。

一个人运营博客、Telegram 频道、公众号三个出口,最痛的不是写不出内容,是写不过来——每天有价值的 AI 资讯太多了,手动翻译、改写、配图、排版、分发,一条链走完半天就没了。于是我把它拆成了一条管线,起名 LynxPipe:素材进,成品文章出,博客/TG/公众号三头分发,中间的"会动的逻辑"全收拢在一个仓里。

这篇文章把这条管线的架构、组件、路由、踩坑全拆给你看。

整条链路先看一眼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
RSS 源(12 个) + 厂商官方博客监控
  feed_pipeline.py
    抓取 → 去重(seen.json) → CPA LLM 写中英双语稿
    → lynx_reviewer.py 独立二审(事实红线/夸大标题/中英一致)
    → lynxcard_client.py 渲染文章卡(HTTP 调 LynxCard:8790)
  blog-lxlynx 内容仓(Hugo,外部仓库)
    sync_en.py 中英互同步 → png2webp.py → hugo 构建
    → wrangler 部署 Cloudflare Pages
  distribute.py 按 distribute_routes.yaml 路由分发(dist_seen.json 去重)
    ├→ Telegram @Lx_groups(经 lynxtg CF Worker)
    ├→ 公众号草稿箱(scripts/wechat/ 模块)
    └→ LinuxDo Discourse
  git 归档(blog 仓 push)

一句话定位:博客仓只留 Hugo 内容,所有"会动的逻辑"住在 LynxPipe 仓里。这个拆法是为了让内容站保持瘦身——改管线不用碰内容,改内容不用碰管线。2026-08-09 起,管线脚本从 blog-lxlynx/scripts、lynxhot/scripts、lynxWechat 三处收拢沉淀进 LynxPipe。

触发层:cron + systemd timer

触发器时间跑什么写入日志
cron0 5,12,17 * * *run_pipeline.sh 全链路一轮(抓→写→审→卡→构建→部署→分发→git)runtime/pipeline.log + pipeline-err.log
cron*/30 * * * *distribute.py 增量分发(不重新抓取/构建,只推新文章)runtime/pipeline.log
cron17 */3 * * *vendor_watch.py 厂商博客监控runtime/vendor_watch.log
手动publish.sh(网页编辑器 lynxBlogEdit:1314 的"发布上线"按钮也调它)runtime/pipeline.log
systemd timer*/30freebie-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 小时跑一次。

两条腿汇到同一个入口去重,后面流程一样。这样"聚合资讯"和"厂商一手"都能进,又不重复。

处理层:写稿、二审、渲染卡

这一层是管线的肉,三个动作串起来:

  1. 抓取 + 去重:seen.json 记已处理过的,抓过的不再进。
  2. LLM 写中英双语稿:走一个本地 OpenAI 兼容网关(CPA,:8317),模型 gpt-5.5,一次出中英两版。每轮设了 10 篇上限——不是限制产能,是防停机积压后单轮爆量烧光 LLM 配额。
  3. 独立二审 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-toolsAI 工具AI Tools① 引流主力(工具/账号/提链)
security安全技术Security② 支付/安全(品牌)
automation自动化Automation③ Agent/自动化(差异化)
ai-newsAI 资讯AI News④ 行业资讯(铺量,管线自动)
products产品落地Products⑤ 产品(品牌)
deep-research深度研究Deep Research手写长文(白皮书/选型)

平台路由(订阅哪些分区 + 用哪种语言 + 每轮上限):

平台订阅分区语言每轮上限说明
Telegram @Lx_groupsai-tools, ai-newszh令牌桶(一波 3 条,超出每 50 分 1 条)经 lynxtg CF Worker;08-09 取消条数上限
LinuxDosecurity, automation, ai-toolszh1Discourse API;技术社区吃深度
公众号 凌序之心ai-news, products, security, deep-researchzh2直发限流 3h;草稿箱不限速;绝不自动群发
X(未来)ai-tools, automation, ai-newsen3英文版正好喂 X
小红书(未来)ai-toolszh1教程向改写

几个关键取舍:

  • 公众号只进草稿箱,绝不自动群发。群发永远人工后台点。这是红线——自动群发翻车没法撤。
  • Telegram 取消了条数上限,改令牌桶:一波直推 3 条,超出每 50 分钟放 1 条。既不刷屏又不卡队列。
  • LinuxDo 每轮只 1 篇,而且只发"原理拆解"角度的合规内容,技术社区吃深度不吃资讯搬运。
  • 非板块分类(如"公告")不匹配任何分区,不会被分发。

仓内组件清单

路径职责
scripts/pipe_config.py中枢配置:BLOG_ROOT / RUNTIME,env 可覆盖
scripts/news_rss.pyRSS 源配置 + AI 关键词过滤(自 lynxhot 收拢)
scripts/feed_pipeline.py资讯管线主程序:抓取→去重→CPA 写稿→二审→渲染卡
scripts/lynx_reviewer.py发文前独立 LLM 二审;异常放行不卡管线;留痕 reviews.jsonl
scripts/lynxcard_client.pyLynxCard 渲染客户端(只走 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.shcron 自动管线: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)

外部服务依赖拓扑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
                 LynxPipe 管线
   ┌─────────┬───────┼───────┬─────────┬────────┬─────────┐
   ▼         ▼       ▼       ▼         ▼        ▼         ▼
 CPA:8317  LynxCard  lynxtg   hugo    wrangler  微信API   Tavily
 LLM网关   :8790     Worker   ~/bin   fnm Node  草稿箱    联网核查
 写稿/审/  卡片渲染  CF Worker  hugo   CF Pages   (绝不    (web-verify)
 翻译/写稿 TG/小绿    通用TG出口        部署       群发)
          书/博客    多项目共用       blog-lxlynx
   │                                          共用         ▲
   ├── agnes-2.0-flash(funnel 主力)             │          │
   └── glm-5.2-fast-preview(funnel 整合)        └──────────┘
                                                  web-verify
  • 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、白嫖额度、开源工具),挑值得推的,推到频道。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
采集源(各免费 AI 资源 RSS/站点)
层1 · 14 视角筛选   (基础门槛 + 14 个专盯镜头,砍掉 90%)
层2 · 7 镜头评审     (每条 ×7 角度取均分)
层3 · 3 风格文案     (同一素材各写 3 版,复审挑最好的)
层4 · 3 镜头复审     (事实核对 / 夸大 / 合规,全过才放行)
    │  ← 含 Tier1 事实核对镜头(版本号/日期/备案)
层5 · 1 整合         (glm-5.2-fast-preview 挑最佳定稿)
🛡 webVerifyAndSanitize 闸门(Tier2 Tavily 联网核查)
    │  抽版本号/日期/合作声明 → Tavily 搜 → LLM judge
    │  矛盾换正确值 / 查无跳过不推 / fail-open
worker /push 路由 → LynxCard 卡片 + 推送 + quota
📢 @Lx_groups TG 频道

主力模型 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)✅ 立即生效
Tier2webVerifyAndSanitize 推送前 Tavily 联网核查(fail-open:无 key/报错/无精确声明原样放行)✅ 已激活(复用既有 Tavily key,端到端验证 API 200)

这套"防假精确"的逻辑我写进了全局纪律:凡是要发的内容含具体事实点(版本号/日期/合作/价格),先联网核查再发,不能只靠 LLM 自判兜底。LLM 自判查的是自洽,不是真伪。

运行时状态文件(runtime/,gitignored)

文件用途
seen.jsonfeed_pipeline 抓取去重池
dist_seen.jsondistribute 分发去重池(改名须同步 key)
vendor_seen.jsonvendor_watch 去重池
dist_rate.jsondistribute 令牌桶限流
reviews.jsonllynx_reviewer 二审审计留痕
dist.envsecret:WORKER_URL / RUN_KEY / TAVILY_API_KEY 等
pipeline.log / pipeline-err.log主管线日志
vendor_watch.log / backfill.log各组件日志

红线

  • 公众号只进草稿箱,绝不自动群发;群发永远人工后台操作。
  • 博客不引流 pay.153.ink(非本人网站);国内媒体(公众号/微信)不上站。
  • runtime/.envdata/ 全部 gitignored,凭据绝不入库。

踩坑血泪(完整 8 条)

  1. 公众号 IP 白名单:公众号 /token 报 40164 就是出口 IP 不在白名单。本机跑着 Clash 8899 代理,出口 IP 浮动,公众号 token 接口必挂。解法是 wechat 模块脚本级清掉 proxy 环境变量,让它走直连。
  2. 浏览器 UA:Python urllib 默认 UA 会被 Cloudflare 拦(403/1010)。所有对外请求必须带浏览器 UA,别用默认。
  3. Hugo 的 data/ 目录:运行时状态文件一度放在博客仓的 data/ 下,结果 Hugo 把它当配置解析,构建直接挂。教训:状态一律放管线仓的 runtime/,别碰 Hugo 的约定目录。
  4. git 判变更:判断"有没有新内容要提交",要用 git status --porcelain 不能用 git diff --quiet——新文章是 untracked 文件,diff 看不见,会误判成无变更跳过提交。
  5. 管道退出码:wrangler | tail 要配 set -o pipefail,否则部署失败被吞还显示成功。
  6. sync_en 全量重译:批量改内容文件时,必须垫高英文文件的 mtime,否则同步逻辑判定"中文比英文新"会全量重译,token 瞬间烧光;文章改名必须同步 dist_seen.json 的 key。
  7. 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
  8. 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 社区在搭建过程中提供的思路和工具支持。

延伸阅读: