Claude之父AI编程技巧十二:任务自动化脚本——让AI成为可靠的长时间工作者

引言

在AI辅助开发中,我们经常面临一个挑战:如何让AI可靠地执行长时间的任务?大多数AI系统在处理复杂任务时可能会遇到上下文丢失、响应超时或状态不一致等问题。Boris Cherny提到,Claude Code可以通过脚本和自动化工具来可靠地处理长时间任务,这是如何做到的?

本文将深入探讨三种经过验证的长时间任务处理方案:使用脚本自动执行、任务状态管理,以及错误恢复机制。通过这些技术,你可以让Claude Code成为真正可靠的长时间工作者。

理解长时间运行任务

什么是长时间运行任务?

┌─────────────────────────────────────────────────────────────────┐
│                    任务持续时间分类                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  短时任务 (< 1分钟)                                               │
│  ├── 代码编辑                                                     │
│  ├── 文件搜索                                                     │
│  └── 代码审查                                                     │
│                                                                  │
│  中时任务 (1-30分钟)                                              │
│  ├── 构建项目                                                     │
│  ├── 运行测试                                                     │
│  └── 部署应用                                                     │
│                                                                  │
│  长时任务 (> 30分钟)                                              │
│  ├── 大规模重构                                                   │
│  ├── 完整项目迁移                                                 │
│  ├── 数据迁移                                                     │
│  └── 持续集成/持续部署                                            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

长时间任务面临的挑战

挑战 描述 影响
上下文丢失 长会话可能导致上下文截断 AI遗忘之前的决策
超时问题 超过API响应时间限制 任务中断
状态不一致 中途失败难以恢复 需要从头开始
资源消耗 持续运行消耗大量资源 成本上升
监控困难 难以实时了解进度 无法及时干预

方案A:任务状态管理脚本

核心概念

通过脚本自动保存任务状态,可以从中断点恢复:

┌─────────────────────────────────────────────────────────────────┐
│                    任务状态管理工作流                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  启动任务脚本                                                    │
│      ↓                                                           │
│  保存任务开始状态                                                 │
│      ↓                                                           │
│  执行任务步骤                                                     │
│      ↓                                                           │
│  每步完成后保存进度                                               │
│      ↓                                                           │
│  任务中断或完成                                                   │
│      ↓                                                           │
│  恢复时从最近状态继续                                             │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

创建任务状态管理脚本

#!/bin/bash
# task-manager.sh

TASK_NAME="$1"
TASK_STEP="$2"

STATE_DIR=".task-states"
STATE_FILE="$STATE_DIR/${TASK_NAME}.json"

# 创建状态目录
mkdir -p "$STATE_DIR"

# 保存任务状态
save_state() {
    local current_step="$1"
    local status="$2"
    local timestamp=$(date +%s)

    cat > "$STATE_FILE" << EOF
{
  "task_name": "$TASK_NAME",
  "current_step": "$current_step",
  "status": "$status",
  "timestamp": $timestamp,
  "completed_steps": [
    $(cat "$STATE_FILE" 2>/dev/null | jq -r '.completed_steps[]' 2>/dev/null || echo "")
  ]
}
EOF

    echo "状态已保存:任务=$TASK_NAME, 步骤=$current_step, 状态=$status"
}

# 标记步骤完成
mark_step_complete() {
    local step="$1"
    local current_state=$(cat "$STATE_FILE" 2>/dev/null || echo '{}')
    echo "$current_state" | jq --arg step "$step" '.completed_steps += [$step]' > "$STATE_FILE"
    echo "步骤完成:$step"
}

# 获取当前状态
get_current_state() {
    if [ -f "$STATE_FILE" ]; then
        cat "$STATE_FILE"
    else
        echo '{"task_name": "'$TASK_NAME'", "current_step": "start", "status": "not_started", "completed_steps": []}'
    fi
}

# 主逻辑
case "$TASK_STEP" in
    "save")
        save_state "$3" "$4"
        ;;
    "mark-complete")
        mark_step_complete "$3"
        ;;
    "get")
        get_current_state
        ;;
    *)
        echo "用法: $0 <任务名> <save|mark-complete|get> [参数...]"
        exit 1
        ;;
esac

使用示例

#!/bin/bash
# 迁移项目的示例

# 任务:迁移项目从Express到Fastify

# 步骤1:备份项目
echo "开始备份项目..."
./task-manager.sh migration save "backup" "running"

# 执行备份
tar -czf backup-$(date +%Y%m%d).tar.gz ./src ./package.json ./package-lock.json

./task-manager.sh migration mark-complete "backup"
./task-manager.sh migration save "backup" "completed"

# 步骤2:更新依赖
echo "更新依赖..."
./task-manager.sh migration save "update-deps" "running"

# 执行依赖更新
npm uninstall express
npm install fastify fastify-cors @fastify/formbody

./task-manager.sh migration mark-complete "update-deps"
./task-manager.sh migration save "update-deps" "completed"

# 步骤3:重构代码
echo "重构代码..."
./task-manager.sh migration save "refactor" "running"

# 执行重构
# 这里执行实际的代码重构

./task-manager.sh migration mark-complete "refactor"
./task-manager.sh migration save "refactor" "completed"

echo "迁移完成!"

恢复中断的任务

#!/bin/bash
# restore-task.sh

TASK_NAME="$1"

STATE_FILE=".task-states/${TASK_NAME}.json"

if [ ! -f "$STATE_FILE" ]; then
    echo "未找到任务状态文件:$STATE_FILE"
    exit 1
fi

CURRENT_STEP=$(cat "$STATE_FILE" | jq -r '.current_step')
STATUS=$(cat "$STATE_FILE" | jq -r '.status')

echo "恢复任务:$TASK_NAME"
echo "当前状态:$STATUS"
echo "中断步骤:$CURRENT_STEP"

# 根据中断点继续执行
case "$CURRENT_STEP" in
    "backup")
        echo "步骤1已完成,从步骤2继续..."
        ;;
    "update-deps")
        echo "步骤1-2已完成,从步骤3继续..."
        ;;
    "refactor")
        echo "步骤1-3已完成,从步骤4继续..."
        ;;
    *)
        echo "任务已完成或未知状态"
        ;;
esac

方案B:进度跟踪脚本

进度跟踪工具

#!/bin/bash
# progress-tracker.sh

TOTAL_STEPS="$1"
CURRENT_STEP="$2"
TASK_NAME="$3"

# 计算进度百分比
if [ -n "$TOTAL_STEPS" ] && [ -n "$CURRENT_STEP" ]; then
    PERCENTAGE=$((CURRENT_STEP * 100 / TOTAL_STEPS))

    # 绘制进度条
    BAR_LENGTH=50
    FILLED_LENGTH=$((CURRENT_STEP * BAR_LENGTH / TOTAL_STEPS))

    BAR=""
    for i in $(seq 1 $BAR_LENGTH); do
        if [ $i -le $FILLED_LENGTH ]; then
            BAR="${BAR}█"
        else
            BAR="${BAR}░"
        fi
    done

    # 显示进度
    echo ""
    echo "┌──────────────────────────────────────────────────────────┐"
    echo "│  任务:$TASK_NAME"
    echo "│  进度:$BAR $PERCENTAGE%"
    echo "│  步骤:$CURRENT_STEP/$TOTAL_STEPS"
    echo "└──────────────────────────────────────────────────────────┘"
    echo ""
fi

# 保存进度到文件
cat > ".progress-${TASK_NAME}" << EOF
{
  "task": "$TASK_NAME",
  "current": $CURRENT_STEP,
  "total": $TOTAL_STEPS,
  "percentage": $PERCENTAGE,
  "timestamp": $(date +%s)
}
EOF

使用示例

#!/bin/bash
# 示例:构建项目

TOTAL_STEPS=5

# 步骤1:安装依赖
echo "步骤 1/$TOTAL_STEPS:安装依赖"
./progress-tracker.sh $TOTAL_STEPS 1 "构建项目"
npm install
./progress-tracker.sh $TOTAL_STEPS 1 "构建项目"

# 步骤2:运行测试
echo "步骤 2/$TOTAL_STEPS:运行测试"
./progress-tracker.sh $TOTAL_STEPS 2 "构建项目"
npm test
./progress-tracker.sh $TOTAL_STEPS 2 "构建项目"

# 步骤3:构建前端
echo "步骤 3/$TOTAL_STEPS:构建前端"
./progress-tracker.sh $TOTAL_STEPS 3 "构建项目"
npm run build:frontend
./progress-tracker.sh $TOTAL_STEPS 3 "构建项目"

# 步骤4:构建后端
echo "步骤 4/$TOTAL_STEPS:构建后端"
./progress-tracker.sh $TOTAL_STEPS 4 "构建项目"
npm run build:backend
./progress-tracker.sh $TOTAL_STEPS 4 "构建项目"

# 步骤5:部署
echo "步骤 5/$TOTAL_STEPS:部署"
./progress-tracker.sh $TOTAL_STEPS 5 "构建项目"
npm run deploy
./progress-tracker.sh $TOTAL_STEPS 5 "构建项目"

echo "任务完成!"

方案C:错误恢复机制

重试脚本

#!/bin/bash
# retry.sh

MAX_RETRIES=3
RETRY_DELAY=5

# 执行命令并支持重试
retry_command() {
    local command="$1"
    local description="$2"
    local attempt=1

    while [ $attempt -le $MAX_RETRIES ]; do
        echo "尝试 $attempt/$MAX_RETRIES$description"

        if eval "$command"; then
            echo "✅ 成功:$description"
            return 0
        else
            echo "❌ 失败:$description"

            if [ $attempt -lt $MAX_RETRIES ]; then
                echo "⏳ 等待 $RETRY_DELAY 秒后重试..."
                sleep $RETRY_DELAY
            fi
        fi

        attempt=$((attempt + 1))
    done

    echo "❌ 超过最大重试次数:$description"
    return 1
}

# 使用示例
retry_command "npm test" "运行测试"
retry_command "npm run build" "构建项目"
retry_command "docker push myapp:latest" "推送Docker镜像"

错误处理脚本

#!/bin/bash
# error-handler.sh

set -e  # 遇到错误时退出

ERROR_LOG="errors.log"

# 记录错误
log_error() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] ERROR: $message" >> "$ERROR_LOG"
    echo "❌ 错误已记录:$message"
}

# 清理函数
cleanup() {
    echo "🧹 执行清理..."
    # 删除临时文件
    rm -f /tmp/temp-*.tmp
    # 停止后台进程
    pkill -f "background-job"
}

# 设置陷阱
trap cleanup EXIT
trap 'log_error "脚本在第 $LINENO 行出错"' ERR

# 主逻辑
echo "开始执行任务..."

# 步骤1:可能失败的操作
if ! some_fallible_command; then
    log_error "some_fallible_command 失败"
    exit 1
fi

# 步骤2:另一个可能失败的操作
if ! another_command; then
    log_error "another_command 失败"
    exit 1
fi

echo "任务完成!"

方案D:任务队列系统

简单的任务队列

#!/bin/bash
# task-queue.sh

QUEUE_FILE="task-queue.json"
LOCK_FILE="task-queue.lock"

# 锁定文件函数
lock() {
    exec 200>"$LOCK_FILE"
    flock -n 200 || exit 1
}

# 解锁文件函数
unlock() {
    flock -u 200
    exec 200>&-
}

# 添加任务到队列
enqueue() {
    local task="$1"
    local priority="${2:-0}"

    lock
    {
        # 读取现有队列
        local queue=$(cat "$QUEUE_FILE" 2>/dev/null || echo '[]')

        # 添加新任务
        local new_task=$(printf '{"task": "%s", "priority": %s, "status": "pending", "added": "%s"}' "$task" "$priority" "$(date -Iseconds)")
        queue=$(echo "$queue" | jq --argjson task "$new_task" '. + [$task]')

        # 按优先级排序
        queue=$(echo "$queue" | jq 'sort_by(.priority) | reverse')

        # 保存队列
        echo "$queue" > "$QUEUE_FILE"
    }
    unlock

    echo "✅ 任务已添加:$task"
}

# 从队列获取任务
dequeue() {
    lock
    {
        # 读取队列
        local queue=$(cat "$QUEUE_FILE" 2>/dev/null || echo '[]')

        # 获取第一个任务
        local task=$(echo "$queue" | jq -r '.[0].task // empty')

        if [ -n "$task" ] && [ "$task" != "null" ]; then
            # 移除任务
            queue=$(echo "$queue" | jq '.[1:]')
            echo "$queue" > "$QUEUE_FILE"
            echo "$task"
        else
            echo ""
        fi
    }
    unlock
}

# 标记任务完成
complete() {
    local task="$1"
    echo "✅ 任务完成:$task"
}

# 处理队列
process_queue() {
    while true; do
        local task=$(dequeue)

        if [ -n "$task" ]; then
            echo "🔄 处理任务:$task"
            eval "$task"
            complete "$task"
        else
            echo "⏳ 队列为空,等待..."
            sleep 5
        fi
    done
}

# 主逻辑
case "$1" in
    "add")
        enqueue "$2" "$3"
        ;;
    "process")
        process_queue
        ;;
    *)
        echo "用法: $0 {add <任务> [优先级] | process}"
        exit 1
        ;;
esac

使用示例

# 添加任务到队列
./task-queue.sh add "npm run build:frontend" 1
./task-queue.sh add "npm run build:backend" 1
./task-queue.sh add "npm run test" 2
./task-queue.sh add "npm run deploy" 3

# 启动队列处理器
./task-queue.sh process

方案E:定时任务脚本

定时执行

#!/bin/bash
# scheduled-task.sh

INTERVAL=60  # 每60秒执行一次

# 执行任务
run_task() {
    echo "⏰ 执行定时任务:$(date)"
    # 在这里执行你的任务
    npm run check-updates
    git pull origin main
}

# 无限循环
while true; do
    run_task
    echo "⏳ 等待 $INTERVAL 秒..."
    sleep $INTERVAL
done

使用cron

# 编辑cron任务
crontab -e

# 每小时运行一次脚本
0 * * * * /path/to/scheduled-task.sh >> /var/log/scheduled.log 2>&1

# 每天凌晨2点运行备份
0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1

方案F:检查点机制

创建检查点

#!/bin/bash
# checkpoint.sh

CHECKPOINT_DIR=".checkpoints"
TASK_NAME="$1"

mkdir -p "$CHECKPOINT_DIR"

# 创建检查点
create_checkpoint() {
    local step="$1"
    local data="$2"
    local checkpoint_file="$CHECKPOINT_DIR/${TASK_NAME}_${step}.json"

    cat > "$checkpoint_file" << EOF
{
  "task": "$TASK_NAME",
  "step": "$step",
  "data": $data,
  "timestamp": $(date +%s)
}
EOF

    echo "✅ 检查点已创建:$step"
}

# 获取检查点
get_checkpoint() {
    local step="$1"
    local checkpoint_file="$CHECKPOINT_DIR/${TASK_NAME}_${step}.json"

    if [ -f "$checkpoint_file" ]; then
        cat "$checkpoint_file"
    else
        echo '{}'
    fi
}

# 列出所有检查点
list_checkpoints() {
    ls -1 "$CHECKPOINT_DIR/${TASK_NAME}_"* 2>/dev/null | sed "s|.*/${TASK_NAME}_||" | sed 's|\.json||'
}

# 主逻辑
case "$1" in
    "create")
        create_checkpoint "$2" "$3"
        ;;
    "get")
        get_checkpoint "$2"
        ;;
    "list")
        list_checkpoints
        ;;
    *)
        echo "用法: $0 {create|get|list} <参数...>"
        exit 1
        ;;
esac

使用示例

#!/bin/bash
# 示例:数据迁移

# 步骤1:导出数据
echo "导出数据..."
./checkpoint.sh migration create "export" '{}'
mongoexport --db mydb --collection users --out users.json
./checkpoint.sh migration create "export" '{"status": "completed"}'

# 步骤2:转换数据
echo "转换数据..."
./checkpoint.sh migration create "transform" '{}'
node transform.js
./checkpoint.sh migration create "transform" '{"status": "completed"}'

# 步骤3:导入数据
echo "导入数据..."
./checkpoint.sh migration create "import" '{}'
mongoimport --db mydb --collection users --file users.json
./checkpoint.sh migration create "import" '{"status": "completed"}'

echo "迁移完成!"

自动化工具整合

Makefile

# Makefile
.PHONY: help build test deploy clean

help:
	@echo "可用命令:"
	@echo "  build   - 构建项目"
	@echo "  test   - 运行测试"
	@echo "  deploy  - 部署应用"
	@echo "  clean   - 清理临时文件"

build:
	@echo "构建项目..."
	./task-manager.sh build save "build" "running"
	npm run build
	./task-manager.sh build save "build" "completed"

test:
	@echo "运行测试..."
	./progress-tracker.sh 5 1 "测试"
	npm test
	./progress-tracker.sh 5 1 "测试"

deploy:
	@echo "部署应用..."
	retry_command "npm run deploy" "部署应用"

clean:
	@echo "清理临时文件..."
	rm -rf .task-states
	rm -rf .checkpoints
	rm -f task-queue.json

最佳实践

1. 脚本模块化

project/
├── scripts/
│   ├── task-manager.sh
│   ├── progress-tracker.sh
│   ├── retry.sh
│   └── error-handler.sh
├── Makefile
└── README.md

2. 日志记录

#!/bin/bash
# logger.sh

LOG_FILE="task.log"
LEVEL="$1"
MESSAGE="$2"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$TIMESTAMP] [$LEVEL] $MESSAGE" >> "$LOG_FILE"

3. 配置文件

{
  "task_config": {
    "max_retries": 3,
    "retry_delay": 5,
    "checkpoint_interval": 10,
    "log_level": "info"
  }
}

4. 通知机制

#!/bin/bash
# notify.sh

send_notification() {
    local message="$1"
    local channel="$2"

    case "$channel" in
        "slack")
            curl -X POST -H 'Content-type: application/json' \
                 --data "{\"text\":\"$message\"}" \
                 "$SLACK_WEBHOOK_URL"
            ;;
        "email")
            echo "$message" | mail -s "任务通知" admin@example.com
            ;;
        *)
            echo "$message"
            ;;
    esac
}

# 使用示例
send_notification "任务已完成" "slack"

常见问题与解决方案

问题1:脚本中断

症状:脚本执行过程中被中断

解决方案

  • 使用检查点机制
  • 保存任务状态
  • 支持从中断点恢复

问题2:资源耗尽

症状:长时间运行导致内存或磁盘空间不足

解决方案

  • 定期清理临时文件
  • 使用流式处理
  • 设置资源限制

问题3:并发冲突

症状:多个脚本同时访问同一资源

解决方案

  • 使用文件锁
  • 实现互斥机制
  • 避免共享状态

结语

长时间运行任务的可靠执行是AI辅助开发的高级能力。通过本文介绍的各种脚本和自动化工具,你可以让Claude Code:

  • 持续可靠地工作:即使任务需要数小时甚至数天
  • 安全地恢复:从检查点恢复,无需从头开始
  • 实时监控进度:随时了解任务状态
  • 优雅地处理错误:自动重试和恢复

Boris Cherny提到Claude Code可以通过脚本实现长时间任务,这不是魔法,而是通过合理的脚本设计和自动化机制实现的。掌握这些技术,你也可以让AI成为真正可靠的长时间工作者。

从今天开始,将你的复杂任务分解为脚本化的步骤,建立自动化的工作流。你会发现,随着脚本的完善,任务执行的可靠性会持续提升。


参考资源

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐