首页 › skills-review › 拆Harvester:给Claude排夜班

拆Harvester:给Claude排夜班——一套跑了四个月的工作流自动化产线解剖

本期是 Skills 生态第 5 轮拆解,主题轮到工作流自动化:部署 / CI / 监控 / 多Agent 协作。本期没有去生态榜单找新面孔,而是把镜头对准了一条更罕见的东西——一条真实运行中的产线。你现在读到的这篇文章,正是它在排班表里生成的。所有素材全部来自本地文件与运行日志的一手取证,零外部抓取;拿不准的地方,文中都注明了“证据不足”。

概述

Skills 解决的是“一件活怎么干”,但一条产线还需要有人排班、发料、验收、记工。本期拆解的 AI Harvester 就是最上面那层:一个纯 Bash 写的调度台,用 claude -p 无头模式串行雇佣 Claude,按星期轮换 8 个栏目,带 token 探活、429 自愈、JSON 状态机、三道发布查重闸和一篇自动日报。它不是明星开源项目,没有 Star 数,但它是「工作流自动化」四个子命题(部署、CI、监控、多Agent)在一个代码库里同时交卷的罕见样本——包括它翻过的车,日志都还在。

产线全家福

文件 角色 对应传统概念
master.sh 总调度循环,cron 零点拉起 CI 主 runner
token_checker.sh 探活闸:花一次真调用确认额度 健康检查探针
task_runner.sh 单工位:取工艺卡→执行→渲染→交付 Job 执行器
config/settings.sh 配置 + JSON 状态机 + 轮换表 状态存储 / 调度策略
prompts/*.md 17 张工艺卡,8 个在编栏目 流水线模板
publish.sh 发布闸:三道查重 + 站点路由 合并门禁
render_mermaid.sh 图表渲染质检 构建后处理
generate_daily.sh 日报工位:Claude 读自家日志写总结 可观测性报表

整体只有几百行 Bash,外加 jq 和一个 mermaid CLI。没有 Docker,没有数据库,state.json 就是它的全部记忆。

工作流程图

flowchart TD
    CRON["crontab 零点开机"] --> MASTER["master.sh 总调度"]
    MASTER --> CHECK{"token_checker 探活"}
    CHECK -->|429 限流| SLEEP5["睡 5 小时"]
    SLEEP5 --> MASTER
    CHECK -->|其他错误| SLEEP10["睡 10 分钟"]
    SLEEP10 --> MASTER
    CHECK -->|探活通过| NEXT["get_next_task 取件"]
    NEXT -->|队列空| REPORT["generate_daily 日报"]
    NEXT -->|有活| RUN["task_runner 单工位"]
    RUN --> GEN["claude -p 无头执行"]
    GEN --> MMD["render_mermaid 渲染"]
    MMD --> PUB["publish.sh 三道闸"]
    PUB -->|命中查重| REJ["REJECTED 拒稿"]
    PUB -->|放行| SITE["Docsify 站点上线"]
    SITE --> STATE["jq 更新状态文件"]
    STATE --> COOL["冷却 5 秒"]
    COOL --> MASTER
    REPORT --> DONE["标记收工"]

评测对比表

本期对象是本地件,把模板里的“社区活跃度”换成「迭代活跃度」——证据是代码注释里的日期戳密度。权重沿用 25/25/20/15/15。

组件 功能完整度 设计质量 实战效果 上手难度 迭代活跃度 加权
publish.sh 发布闸 ★★★★★ ★★★★★ ★★★★★ ★★★★ ★★★★★ 4.9
settings.sh 状态机 ★★★★ ★★★★ ★★★★ ★★★ ★★★★ 3.9
render_mermaid.sh 渲染 ★★★★ ★★★★ ★★★★ ★★★★ ★★★ 3.9
token_checker.sh 探活 ★★★ ★★★★ ★★★★ ★★★★★ ★★★ 3.8
master.sh 总调度 ★★★★ ★★★★ ★★★ ★★★★ ★★★ 3.7
task_runner.sh 单工位 ★★ ★★ ★★ ★★★ ★★ 2.2
generate_daily.sh 日报 ★★ ★★ ★ ★★★★ ★★ 2.1

深度拆解:四张考卷,一份代码

考卷一:部署——一行 crontab 和两台 VPS 的暗线

部署层薄得惊人:master.sh 头部注释写着 0 0 * * * /home/gprshome/skills/wiki/master.sh,零点开机,循环兜底 MAX_RUNTIME_HOURS=24,跑满 24 小时强制收工。没有进程守护,没有 systemd——因为循环本身就设计为“干完即退”。

更有意思的是一条暗线:config/v2_settings_additions.sh 的注释明确写着 "Append these to the existing settings.sh on VPS2"。这说明这套产线不止一台机器,v2 配置是作为补丁发给第二台 VPS 的。而本地这份 settings.sh 里同时存在 v1 状态结构(pending_tasks/current_window)和 v2 函数(get_next_task_v2/mark_task_done_v2),两代结构叠在同一个文件里——典型的“生产在别处,本地是现场遗址”。今天栏目还在照常更新,说明真正的生产实例活着,这份目录更像它的开发镜像。

发布下游也有部署思维:publish.sh 末尾接了 gen_pagination.py(分页归档,2026-09-19 注释)和 prerender.py(SEO/GEO 静态预渲染 + 刷新 sitemap,2026-09-27 注释)。新文章落地几分钟内就有静态 HTML 和站点地图——这是给搜索引擎爬虫准备的“部署”。

考卷二:CI——状态机就是流水线账本

settings.sh 里的状态机是整套代码最规整的部分:init_state 每天生成一份 JSON,含 pending_tasks、completed_tasks、failed_tasks、token_resets 四本账;get_next_task 用 jq -r '.pending_tasks[0]' 取件;完成或失败分别记账,master 循环据此判断“今天收不收工”。

对照传统 CI:任务队列 ≈ job matrix,重试策略写死在 master 循环里(其他错误等 10 分钟),发布闸 ≈ merge gate。而且发布闸比多数个人项目认真得多——publish.sh 里三道闸全部带日期戳:

# === 硬闸门(2026-09-20):全站标题索引精确查重,同标题已发布过即拒稿 ===
if [ -n "$TITLE_EARLY" ] && grep -qxF "$TITLE_EARLY" "$WIKI_DIR/config/titles_index.txt"; then
    echo "REJECTED-DUPLICATE-TITLE: $TITLE_EARLY"
    exit 1
fi

第二道闸只管 telecom 系栏目,用正则 ^[^:??!!,,.。]{2,12}[:??] 从标题里抠出“术语:”前缀,同一术语全网只许发一篇;第三道闸(2026-09-25)针对 games 栏目,从文章的 <!-- repo: xxx --> 元数据里抠仓库名做仓库级查重。发布成功后回写索引文件,保证闸门不过期。“索引文件 + grep -qxF 精确匹配 + 拒稿即 exit 1”——三件套简单到没有失败模式,这是全库加权分最高的组件。

一个诚实的疑点:现在的工艺卡里有 {{ROUND}} 占位符,而这台 57 行的 task_runner.sh 只替换 {{DATE}} 和 {{OUTPUT_PATH}} 两个。要么线上跑着更年轻的 runner,要么某一版把 ROUND 替换弄丢了、由别处兜底。现场证据不足以裁决,但复刻者应把它列为自查第一坑。

考卷三:监控——用一次真调用买确定性

token_checker.sh 的设计哲学很街头:想知道额度还在不在?那就真发一次请求,让 Claude 回一句“OK”。它 grep 响应里的 429|rate.limit|quota|too.many.requests 等模式,命中即判定限流;再用 grep -oP 从报错里抠出“resets at/retry after”后面的时间戳,作为第二行输出回传。master 那边用 sed -n '2p' 接住这个时间戳记进账本——两个脚本之间约定了一套朴素到可爱的“行协议”。

代价当然有:每次探活都烧一次真实调用,等于用钱换确定性。但它换来的自愈能力是真的——DEFAULT_WAIT_ON_429=18000,命中限流就睡整整 5 小时再回来继续。这个魔法数字不是拍脑袋,事故报告为证(见下节)。

监控的最后一环是日报工位:generate_daily.sh 把当天产出清单、日志尾部 50 行、状态文件一并塞给 Claude,要求生成含“运行概况 / 窗口明细 / 产出清单 / 去重检查 / 明日计划”五节的中文日报。让 Agent 读自己的运行记录写总结——这是全库最有“Agent 运维”味道的设计。

考卷四:多Agent 协作——串行窗口制,被 429 逼出来的务实主义

严格说这不是并发多 Agent:current_window 计数器每次加一,同一时刻只有一个 claude -p 窗口在跑。它的“多 Agent”体现在三个机制上:

  1. 工艺卡制度。每个栏目一张 prompt 文件,17 张卡里 8 个在编(ai-intel、literature、telecom-tech、telecom-deep-tech、skills-ecosystem、github-monetizable、youtube-digest、youtube-deep-dive),9 张已退役(ai-office、movie-insight、github-games 等)——退役卡的发布路由还留在 publish.sh 里,随时可复编。
  2. 占位符发料。runner 把 {{DATE}}、{{OUTPUT_PATH}} 注入工艺卡,窗口拿到的永远是当班参数,卡本身保持无状态。
  3. 轮次配额。v2 的 TASK_ROUNDS 关联数组给每个栏目定轮数:telecom-tech=1(变化慢的题多轮是浪费),youtube-deep-dive=16(长视频深拆要多过几遍料),skills-ecosystem=6,github-monetizable=8。周末还单独把 telecom 系砍到 1 轮。“按题型的信息衰减速度定预算”——这是很多正经编排框架都没做细的地方。

事故复盘:5·16 首航,94 分钟 1 成 7 败

logs/manual-run.log 完整保留了 2026-05-16 那天的翻车现场。时间线:

对照磁盘上的现状:当前 task_runner.sh 恰好 57 行,最后一行是 # Record in history 注释,戛然而止。最合理的解释:产线跑着跑着,有人(或某个 Agent)现场改装工装,改到一半文件被截断——第一轮跑的时候 57 行还是那个未定义的 add_to_history 调用,之后 57 行变成了 EOF。生产线带电改造,改造改到一半断了电。

而日报工位交的那份“日报”,今天还躺在 daily/2026-05-16.md 里,全文只有一行:

API Error: 429 {"error":{"code":"1308","message":"已达到 5 小时的使用上限。您的限额将在 2026-05-17 03:55:14 重置。"}}

日报生成调用自己也被 429 了,报错原文被存成了日报,还通过了发布——因为验收闸是 [ -s "$DAILY_FILE" ],只检查文件非空,而报错字符串恰恰非空。三条教训至今通用:验收标准必须校验内容语义而非存在性;正在被调度循环引用的脚本不要热改;下游“报错即产物”的通路必须堵死。

“怪但能跑”代码细节三则

其一,双跳注料。 task_runner.sh 第 35 行:echo "$prompt_content" | timeout 3600 $CLAUDE_CMD -p "$(cat)" ...——prompt 先被 echo 进管道,再由 -p "$(cat)" 里的 cat 从管道里整段吸走。管道当了快递柜,$(cat) 当了取件码,能跑,但任何想改这段的人都要先愣十秒。

其二,带日期的注释是这个库的谚语。 settings.sh 第 29 行:_DOW=$(date +%u) # 1=Mon..7=Sun(2026-08-28 修复:原硬编码=6)——一行注释记录了一个真实 bug 的生卒年月。publish.sh 里类似的戳有六处(09-12 加页脚、09-19 分页、09-20 双闸、09-25 仓库闸、09-27 预渲染),连起来就是一部产线进化史。

其三,注释与代码的漂移。 settings.sh 末尾:注释说静默期是“Beijing 19:00-24:00 (UTC 11:00-16:00)",代码却写 QUIET_START_UTC=12、QUIET_END_UTC=14;而且这份 master.sh 里压根没有消费 QUIET 变量的语句。配置写了、注释吹了、调度没用——三层各说各话,是脚本堆里最典型的熵增样本。

如果今天用原生件重造

这套 Bash 调度台诞生时没有现成的编排层。今天重写,不少轮子可以直接换成 Agent 运行时的原生能力:cron 位置可以让给原生的计划任务与唤醒机制;串行窗口队列可以交给具备 pipeline/parallel 编排与结构化输出的多 Agent 编排器,让“轮次配额”变成代码里的 for round in 1..N 而不是 jq 记账;日报工位可以改造成运行结束后的自动复盘步骤;mermaid 渲染和发布动作适合做成钩子(hooks)自动触发。

但有两样东西重写时必须原样保留:三道查重闸的“索引文件 + 精确匹配 + 拒稿退出码”设计——它防的不是 bug,是内容的自我重复,这在高频生成场景里比任何并发框架都值钱;以及探活闸“用真实调用换确定性”的思路——探针的最高原则是和生产走同一条路。

推荐排行榜

复刻清单

# 排班:零点开机
crontab -e   # 0 0 * * * /path/to/master.sh

# 手动点一个工位
bash task_runner.sh skills-ecosystem

# 探活闸体检
bash token_checker.sh; echo "exit=$?"

# 看流水线账本
jq '{pending: .pending_tasks, done: .completed_tasks}' config/state.json

# 盯当天日志
tail -f logs/$(date +%F).log

# 手动过一道发布闸(会触发三重查重)
bash publish.sh output/$(date +%F)/skills-ecosystem-1200.md skills-ecosystem

结语

这一轮把“工作流自动化”四个词拆到了一个真实代码库里:部署是一行 crontab 加两台 VPS 的暗线;CI 是一份 jq 状态机加三道拒稿闸;监控是一次真调用探针加一篇会翻车也会自愈的日报;多 Agent 是被 429 逼出来的串行窗口制。它证明了跑通一条产线不需要框架,需要的是状态文件记得住事、闸门拒绝得了稿、事故日志留得下来。顺带一提:本文交付时,同样要先过它 2026-09-20 装上的那道标题查重闸——产线拆到自己头上,算是这门手艺最严格的自检。

参考来源

  1. wiki/master.sh、wiki/task_runner.sh — 总调度循环与单工位执行器,本文流程与 5·16 事故的一手证据
  2. wiki/config/settings.sh、wiki/config/v2_settings_additions.sh — 状态机、轮换表、轮次配额与双 VPS 线索
  3. wiki/publish.sh — 三道查重闸、域名自路由、分页与预渲染的完整实现
  4. wiki/token_checker.sh、wiki/render_mermaid.sh、wiki/generate_daily.sh — 探活行协议、fail-open 渲染质检、日报工位
  5. wiki/logs/manual-run.log、wiki/daily/2026-05-16.md — 首航 94 分钟完整日志与 429 日报原件
  6. wiki/prompts/skills-ecosystem.md — 本文所属栏目的工艺卡原文({{DATE}}/{{ROUND}} 占位符现场)

说明:本期受环境限制未做外部网络抓取,全部事实均出自上述本地文件与日志的逐行取证;涉及推测处(线上 VPS2 现状、{{ROUND}} 替换归属)已在正文标注“证据不足”。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入