实施方案详解
方案 A(原生集成)与方案 B(外部循环控制)的详细实施步骤、代码示例、跨平台适配策略及各平台具体实施指南
1. 方案概述
| 方案 | 适用平台 | 实施周期 | 复杂度 | 可控性 |
|---|---|---|---|---|
| 方案 A:原生集成 | Claude Code(确认支持 Stop Hook) OpenCode(验证后) | 1-2 天 | 低 | 中 |
| 方案 B:外部循环 | 所有平台(通用方案) | 5-7 天 | 中 | 高 |
2. 方案 A:原生集成
2.1 适用条件
必须满足:
- 平台原生支持 Stop Hook 机制
- 支持
--continue或等效会话保持功能 - 支持 JSON 或结构化输出(可选但推荐)
2.2 实施步骤
步骤 1:下载并适配 Ralph
cd ~
git clone https://github.com/frankbria/ralph-claude-code.git
cd ralph-claude-code
git checkout -b platform-adaptation
cp ralph_loop.sh ralph_loop.sh.original
步骤 2:修改 Ralph 配置
# 修改 ralph_loop.sh
build_opencode_command() {
local prompt="$1"
local cmd="opencode"
cmd="$cmd --continue-session"
cmd="$cmd -p \"$prompt\""
echo "$cmd"
}
步骤 3:创建 Stop Hook 钩子
mkdir -p hooks
cat > hooks/stop-hook.sh << 'EOF'
#!/bin/bash
set -euo pipefail
OUTPUT_FILE="$1"
MIN_COMPLETION_INDICATORS=2
DEBUG=${DEBUG:-false}
log() {
if [ "$DEBUG" = "true" ]; then
echo "[$(date)] [HOOK] $*" >&2
fi
}
main() {
local output_file="$1"
log "Stop Hook triggered"
if [ ! -f "$output_file" ]; then
log "Output file not found"
exit 0
fi
local output_content=$(cat "$output_file")
local completion_indicators=$(echo "$output_content" | \
grep -oEi "complete|done|finished|ready|implemented" | wc -l)
local exit_signal="false"
if echo "$output_content" | grep -qE '"EXIT_SIGNAL":\s*true'; then
exit_signal="true"
fi
log "Indicators: $completion_indicators, Signal: $exit_signal"
if [ "$completion_indicators" -ge "$MIN_COMPLETION_INDICATORS" ] && \
[ "$exit_signal" = "true" ]; then
log "✅ Completion met, allowing exit"
exit 0
else
log "❌ Completion not met, blocking exit"
exit 2
fi
}
main "$@"
EOF
chmod +x hooks/stop-hook.sh
步骤 4-7:初始化项目、配置、启动循环
# 4. 初始化项目
ralph-setup my-research-project
cd my-research-project
# 5. 配置 PROMPT.md、@fix_plan.md、@AGENT.md
# (详见下文配置示例)
# 6. 启动循环
ralph --monitor
# 7. 监控与调试
ralph-monitor
tail -f logs/ralph.log
2.3 项目结构
my-research-project/
├── PROMPT.md # 项目需求文档
├── @fix_plan.md # 任务优先级列表
├── @AGENT.md # 构建和运行说明
├── .ralph_session # 当前会话 ID
├── .ralph_session_history # 会话转换历史
├── status.json # 实时状态
├── hooks/
│ └── stop-hook.sh # Stop Hook 钩子
├── src/ # 项目源文件
├── logs/ # 执行日志
└── docs/generated/ # 生成的文档
3. 方案 B:外部循环控制
3.1 适用场景
- 平台不支持 Stop Hook 机制
- 需要完全控制循环逻辑
- 需要深度定制和集成
3.2 核心组件实现
组件 1:Shell 循环控制器
#!/bin/bash
# ralph-external-loop.sh
set -euo pipefail
MAX_ITERATIONS=${MAX_ITERATIONS:-50}
MAX_API_CALLS=${MAX_API_CALLS:-100}
HOURLY_LIMIT_DURATION=${HOURLY_LIMIT_DURATION:-3600}
LOOP_DELAY=${LOOP_DELAY:-5}
# 颜色定义
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
init() {
echo -e "${GREEN}初始化 Ralph 外部循环...${NC}"
[ ! -f .loop_count ] && echo 0 > .loop_count
[ ! -f .api_calls ] && echo 0 > .api_calls
[ ! -f .hour_limit_reset ] && echo $(($(date +%s) + HOURLY_LIMIT_DURATION)) > .hour_limit_reset
mkdir -p outputs logs src
if [ ! -f .current_status.json ]; then
cat > .current_status.json << 'INIT'
{
"loop_count": 0,
"progress": 0,
"current_task": "初始化项目",
"completed_tasks": [],
"pending_tasks": ["创建目录", "撰写背景", "文献综述"],
"api_calls_this_hour": 0,
"no_progress_count": 0
}
INIT
fi
echo -e "${GREEN}✅ 初始化完成${NC}"
}
read_status() {
if [ -f .current_status.json ]; then
cat .current_status.json
else
echo '{}'
fi
}
update_status() {
local status="$1"
echo "$status" | jq '.last_update = "'"$(date -Iseconds)"'"' > .current_status.json
}
generate_prompt() {
local status="$1"
local loop_count=$(echo "$status" | jq -r '.loop_count // 0')
local progress=$(echo "$status" | jq -r '.progress // 0')
local current_task=$(echo "$status" | jq -r '.current_task // ""')
local completed_tasks=$(echo "$status" | jq -r '.completed_tasks // []')
local pending_tasks=$(echo "$status" | jq -r '.pending_tasks // []')
cat << PROMPT
# 自动化任务 - 第 $((loop_count + 1)) 次迭代
## 当前状态
- 进度: ${progress}%
- 循环次数: $loop_count
- 当前任务: $current_task
## 已完成任务
$(if [ "$completed_tasks" != "[]" ]; then
echo "$completed_tasks" | jq -r '.[]' | sed 's/^/ - /'
else
echo " (无)"
fi)
## 待办任务(前 5 项)
$(if [ "$pending_tasks" != "[]" ]; then
echo "$pending_tasks" | jq -r '.[]' | head -5 | sed 's/^/ - /'
else
echo " (无)"
fi)
完成后使用以下格式报告进展:
\`\`\`
RALPH_UPDATE:
{
"completed_task": "完成的任务名称",
"next_task": "建议的下一个任务",
"progress": 新的进度百分比(0-100)
}
\`\`\`
如果所有任务完成,添加: <<COMPLETED>>
## 原始需求
$(cat PROMPT.md 2>/dev/null || echo "请查看 PROMPT.md")
PROMPT
}
check_completion() {
local output="$1"
if echo "$output" | grep -q "<<COMPLETED>>"; then
return 0
fi
local pending_count=$(read_status | jq '.pending_tasks | length')
if [ "$pending_count" -eq 0 ]; then
return 0
fi
return 1
}
parse_and_update_status() {
local output="$1"
local current_status="$2"
local update_block=$(echo "$output" | grep -A 10 "RALPH_UPDATE:" | tail -n +2)
if [ -n "$update_block" ]; then
local completed_task=$(echo "$update_block" | jq -r '.completed_task // ""' 2>/dev/null)
local next_task=$(echo "$update_block" | jq -r '.next_task // ""' 2>/dev/null)
local new_progress=$(echo "$update_block" | jq -r '.progress // 0' 2>/dev/null)
local updated_status="$current_status"
if [ -n "$completed_task" ] && [ "$completed_task" != "null" ]; then
updated_status=$(echo "$updated_status" | \
jq --arg ct "$completed_task" '.completed_tasks += [$ct]' | \
jq --arg ct "$completed_task" '.pending_tasks -= [$ct]')
fi
if [ -n "$next_task" ] && [ "$next_task" != "null" ]; then
updated_status=$(echo "$updated_status" | \
jq --arg nt "$next_task" '.current_task = $nt')
fi
if [ "$new_progress" -gt 0 ] 2>/dev/null; then
updated_status=$(echo "$updated_status" | \
jq --argjson lp "$new_progress" '.progress = $lp')
fi
local current_loop=$(echo "$updated_status" | jq -r '.loop_count // 0')
updated_status=$(echo "$updated_status" | \
jq --argjson lc "$((current_loop + 1))" '.loop_count = $lc')
echo "$updated_status"
else
echo "$current_status" | jq '.loop_count += 1'
fi
}
check_rate_limit() {
local api_calls=$(cat .api_calls)
local hour_reset=$(cat .hour_limit_reset)
local current_time=$(date +%s)
if [ "$api_calls" -ge "$MAX_API_CALLS" ]; then
if [ "$current_time" -lt "$hour_reset" ]; then
local wait_seconds=$((hour_reset - current_time))
echo -e "${YELLOW}达到 API 速率限制,等待 $wait_seconds 秒...${NC}"
sleep "$wait_seconds"
echo 0 > .api_calls
echo $(($(date +%s) + HOURLY_LIMIT_DURATION)) > .hour_limit_reset
fi
fi
}
main_loop() {
init
echo -e "${GREEN}═══════════════════════════════════════${NC}"
echo -e "${GREEN} Ralph 外部循环启动${NC}"
echo -e "${GREEN}═══════════════════════════════════════${NC}"
echo ""
while true; do
local loop_count=$(cat .loop_count)
if [ "$loop_count" -ge "$MAX_ITERATIONS" ]; then
echo -e "${RED}达到最大循环次数 ($MAX_ITERATIONS),退出${NC}"
break
fi
check_rate_limit
local current_status=$(read_status)
local prompt=$(generate_prompt "$current_status")
local current_task=$(echo "$current_status" | jq -r '.current_task // "未知"')
local progress=$(echo "$current_status" | jq -r '.progress // 0')
echo -e "${GREEN}[循环 $((loop_count + 1))/$MAX_ITERATIONS]${NC} 进度: ${progress}% | 任务: $current_task"
# 执行 AI 调用
local output
if command -v opencode &> /dev/null; then
output=$(opencode -p "$prompt" 2>&1)
elif command -v claude &> /dev/null; then
output=$(claude -p "$prompt" 2>&1)
else
echo -e "${RED}错误:未找到支持的 AI 工具${NC}"
exit 1
fi
echo "$output" > "outputs/loop_$(printf '%04d' $loop_count).txt"
local api_calls=$(cat .api_calls)
((api_calls++))
echo "$api_calls" > .api_calls
local updated_status=$(parse_and_update_status "$output" "$(read_status)")
update_status "$updated_status"
if check_completion "$output"; then
echo ""
echo -e "${GREEN}✅ 任务完成!${NC}"
break
fi
((loop_count++))
echo "$loop_count" > .loop_count
echo -e "${YELLOW}等待 $LOOP_DELAY 秒...${NC}"
sleep "$LOOP_DELAY"
echo ""
done
echo ""
echo -e "${GREEN}循环结束,总迭代次数: $loop_count${NC}"
cat .current_status.json | jq '.'
}
main
4. 跨平台适配策略
4.1 平台检测脚本
#!/bin/bash
# detect_platform.sh
detect_platform() {
if command -v claude &> /dev/null; then
echo "claude-code"
return 0
fi
if command -v opencode &> /dev/null; then
echo "opencode"
return 0
fi
if command -v codebuddy &> /dev/null; then
echo "codebuddy"
return 0
fi
echo "unknown"
return 1
}
detect_stop_hook() {
local platform="$1"
case "$platform" in
"claude-code")
echo "yes"
;;
"opencode")
echo "unknown"
;;
*)
echo "no"
;;
esac
}
main() {
local platform=$(detect_platform)
echo "检测到的平台: $platform"
if [ "$platform" != "unknown" ]; then
local stop_hook=$(detect_stop_hook "$platform")
echo "Stop Hook 支持: $stop_hook"
if [ "$stop_hook" = "yes" ]; then
echo "推荐方案: A(原生集成)"
else
echo "推荐方案: B(外部循环)"
fi
fi
}
main
4.2 统一的 AI 调用接口
#!/bin/bash
# ai_adapter.sh
call_ai() {
local prompt="$1"
local platform="${AI_PLATFORM:-$(detect_platform)}"
case "$platform" in
"claude-code")
claude -p "$prompt"
;;
"opencode")
opencode -p "$prompt"
;;
"codebuddy")
codebuddy --prompt "$prompt"
;;
*)
echo "错误:未知的平台 '$platform'" >&2
return 1
;;
esac
}
detect_platform() {
if command -v claude &> /dev/null; then
echo "claude-code"
elif command -v opencode &> /dev/null; then
echo "opencode"
elif command -v codebuddy &> /dev/null; then
echo "codebuddy"
else
echo "unknown"
fi
}
5. 方案对比
| 维度 | 方案 A(原生集成) | 方案 B(外部循环) |
|---|---|---|
| 开发工作量 | 低(1-2 天) | 高(5-7 天) |
| 可靠性 | 高(已验证) | 中(需测试) |
| 依赖条件 | OpenCode 支持 Stop Hook | 无特殊依赖 |
| 功能完整性 | 完整(所有 Ralph 特性) | 部分(需自行实现) |
| 维护成本 | 低(跟随上游更新) | 高(自主维护) |
| 灵活性 | 中(受限于 Ralph 设计) | 高(完全自定义) |
6. 推荐实施路径
阶段 1:验证(1 天)
- 执行 Blackbox 测试验证平台能力
- 确认 Stop Hook 和会话管理支持情况
阶段 2:MVP(2-3 天)
- 根据验证结果选择方案 A 或 B
- 实现核心循环功能
阶段 3:生产化(1-2 周)
- 添加监控和日志分析
- 优化提示词
- 集成到现有工作流