把 Claude Code 接进定时任务:无人值守跑一天,早上只看 diff

2026-09-19 Claude Code自动化CI无头模式

一个人维护一个站,重复劳动最贵。每天写情报、每周扫政策、每月出精选——这些活的共同点是:结构化、有明确验收、但很耗时候。它们恰好适合交给 Agent 夜里跑。

这篇给一套能直接抄的配置。所有命令行参数取自 Claude Code 官方 CLI 文档。

一、最小可用的一次无头调用

claude -p "检查 content/news 里最近 48 小时有没有漏更,缺就补写" \
  --output-format json \
  --max-turns 12

三个关键点:

  • -p / --print:查完就退出,不进交互界面。
  • --output-formattext / json / stream-json。要机器可读就用 json;要实时看进度(比如长任务的中间输出透传)用 stream-json,通常配合 --verbose
    claude -p --output-format stream-json --verbose "总结今天的构建日志"
  • --max-turns(仅 print 模式):这条务必写。默认无限轮,一个卡住的 Agent 能跑一整夜的 token。一到上限就退出并报错,成本封顶。

管道输入也支持:

cat logs.txt | claude -p "找出所有 5xx 并按接口归类" --output-format json

二、把权限收死:允许列表白名单

无人值守时 Agent 拿到了你本地的完整权限(能读文件、能跑命令),所以必须显式限制。

claude -p "给昨天的提交补缺失的测试用例" \
  --allowedTools "Read" "Bash(git log *)" "Bash(git diff *)" \
  --permission-mode default \
  --max-turns 8
  • --allowedTools / --allowed-tools:不需要再弹确认的工具,支持括号通配,例如 "Bash(git log *)""Bash(git diff *)""Read"
  • --disallowedTools / --disallowed-tools:反向,把某些工具从上下文里摘掉,"*" 等于全部禁掉。
  • --permission-modedefault / acceptEdits / plan / auto / dontAsk / bypassPermissions无人值守不要用 bypassPermissions——那是把”我不看”写进配置里了。要自动同意编辑用 acceptEdits,并配合下面的分支闸门。

我的习惯是两道锁

  1. 工具白名单只给 Read、指定的几条 Bash(git *)Edit;网络请求一律不给;
  2. 同时在独立的 git worktree / 分支里跑,Agent 改完只产出 diff,合并动作留给人。

第 2 条比第 1 条重要。白名单挡的是意外,分支挡的是”它自信地做错了 200 行”。

三、会话可以续跑

一次跑不完的任务不必重来:

# 用一个业务名字起会话
claude -p --session-id "$(uuidgen)" "开始重构鉴权模块" --output-format json

# 第二天接着上次继续
claude --resume auth-refactor "继续,先补测试"
  • --session-id:指定会话 ID,必须是合法 UUID。自己生成方便在脚本里追踪。
  • --resume / -r:按 ID 或名字恢复。不带参数会弹出交互式选择器(后台会话标 bg)。

定时任务里推荐用显式 UUID + 记录到日志文件,这样第 N 次跑挂了,能精确捞回那条会话接着说。

四、一个能直接用的定时任务模板

每天上午 9 点补情报,只改 content/news,跑完把结果写日志:

#!/usr/bin/env bash
set -euo pipefail
cd /path/to/project

BRANCH="auto/$(date +%Y%m%d-%H%M)"
git switch -c "$BRANCH"

claude -p "为 content/news 补今天缺失的条目,按现有文件格式写,完成后运行校验脚本" \
  --output-format json \
  --allowedTools "Read" "Edit" "Bash(git status)" "Bash(git diff)" \
  --permission-mode acceptEdits \
  --max-turns 20 \
  > "logs/claude-$(date +%F).json" 2>&1 || echo "任务失败,看日志" >&2

# 只生成 diff,不自动合并
git add -A && git diff --cached --stat
echo "改动在 $BRANCH,人工 review 后再合并"

最后三行是这套方案的全部精髓:Agent 出活,人做决定

五、四个容易踩的坑

  1. 不加 --max-turns。卡住的 Agent 会一直重试到天亮,账单先于结果到达。设一个比你认为需要的略小的数,它会更早失败、更早被你发现。
  2. --output-format json 的输出直接当汇报。它包含工具调用轨迹,很长。真正要读的是最后一条 assistant 消息,写个 jq 抽出来再入库。
  3. 让 Agent 在同一分支上直接提交。早晚会有一天它改错并推送,而你当日发现不了。分支 + diff 的成本几乎为零。
  4. 忘了它读得到你整个仓库.env、密钥文件、客户数据都在里面。跑之前确认 .gitignore 是真生效的,必要时空目录起步。

六、什么活适合这么干

适合:结构固定、验收标准明确、错了能回滚的活——补内容、生成周报、跑 lint 并修、翻译、给 PR 写摘要、检查政策更新。

不适合:要对外发请求、要花钱、要删东西、涉及真实个人信息的活。这些不是”自动化”,是”无人看管的权限”。

一句话总结这套心法:让 Agent 夜里干活,白天你只做一件事——看 diff。