Skip to content

解决方案设计

方案 A:原生集成(OpenCode 支持 Stop Hook)

架构概述

假设 OpenCode 支持 Stop Hook 机制,本方案将 Ralph-Loop 的核心逻辑移植到 OpenCode + GLM4.7 环境中,实现最小化改动的集成。

系统架构图

核心组件设计

1. Ralph 控制器(ralph_loop.sh 适配版)

基于 frankbria/ralph-claude-coderalph_loop.sh 进行适配:

主要修改点

bash
# 原始 Claude Code 调用
claude --continue -p "$(cat PROMPT.md)" --output-format json

# 适配后的 OpenCode 调用
opencode -p "$(cat PROMPT.md)" --continue-session --json-output

关键逻辑保持不变

  • 双重退出检测(completion_indicators + EXIT_SIGNAL)
  • 断路器模式
  • 速率限制

2. Stop Hook 钩子(hooks/stop-hook.sh)

使用 Claude Code 的 Stop Hook 逻辑:

bash
#!/bin/bash
# OpenCode Stop Hook

# 读取 OpenCode 输出
output_file="$1"

# 检查是否包含完成标记
completion_indicators=$(grep -oE "(complete|done|finished|ready|implemented)" "$output_file" | wc -l)

# 检查 EXIT_SIGNAL
exit_signal=$(grep -oE '"EXIT_SIGNAL":\s*(true|false)' "$output_file" | head -1 | grep -oE '(true|false)')

# 双重条件判断
if [ "$completion_indicators" -ge 2 ] && [ "$exit_signal" = "true" ]; then
    exit 0  # 允许退出
else
    exit 2  # 阻止退出,触发下一轮
fi

3. 会话管理模块

实现跨循环的上下文保持:

bash
# session_manager.sh

store_session_id() {
    local session_id="$1"
    echo "$session_id" > .ralph_session
}

get_last_session_id() {
    if [ -f .ralph_session ]; then
        cat .ralph_session
    fi
}

check_session_expiry() {
    local session_file=".ralph_session"
    if [ -f "$session_file" ]; then
        local session_time=$(stat -c %Y "$session_file")
        local current_time=$(date +%s)
        local elapsed=$((current_time - session_time))
        local expiry_seconds=$((24 * 3600))  # 24小时

        if [ $elapsed -gt $expiry_seconds ]; then
            echo "EXPIRED"
        else
            echo "ACTIVE"
        fi
    fi
}

4. 断路器监控模块

移植 Ralph 的断路器逻辑:

bash
# circuit_breaker.sh

CB_STATE="closed"
CB_NO_PROGRESS_COUNT=0
CB_SAME_ERROR_COUNT=0
CB_ERROR_PATTERN=""

check_circuit_breaker() {
    local current_output="$1"

    # 检查是否有文件变更(通过 git status)
    if git diff --quiet; then
        ((CB_NO_PROGRESS_COUNT++))
    else
        CB_NO_PROGRESS_COUNT=0  # 重置计数
    fi

    # 检查是否有重复错误
    current_error=$(extract_error_pattern "$current_output")
    if [ "$current_error" = "$CB_ERROR_PATTERN" ]; then
        ((CB_SAME_ERROR_COUNT++))
    else
        CB_ERROR_PATTERN="$current_error"
        CB_SAME_ERROR_COUNT=0
    fi

    # 判断是否应该打开断路器
    if [ $CB_NO_PROGRESS_COUNT -ge 3 ] || [ $CB_SAME_ERROR_COUNT -ge 5 ]; then
        CB_STATE="open"
        return 1  # 阻止循环
    fi

    return 0  # 允许继续
}

项目结构

research-project/
├── PROMPT.md                 # 主项目需求文档
├── @fix_plan.md             # 任务优先级列表
├── @AGENT.md                # 构建和运行说明
├── .ralph_session           # 当前会话 ID
├── .ralph_session_history   # 会话转换历史
├── hooks/
│   └── stop-hook.sh         # Stop Hook 钩子
├── src/
│   └── [研究报告内容]
├── logs/
│   └── ralph.log            # 执行日志
└── status.json              # 实时状态(用于监控)

GLM4.7 提示词适配

为 GLM4.7 设计专门的提示词模板:

markdown
# PROMPT.md

你是一个专业的研究报告生成助手,正在执行自动化研究报告生成任务。

## 任务要求

1. 根据以下研究主题生成完整的研究报告
2. 报告必须包含:研究背景、文献综述、方法论述、数据分析、结论与建议
3. 使用 Markdown 格式输出

## 完成条件

当满足以下所有条件时,设置 EXIT_SIGNAL 为 true:
- [ ] 报告结构完整,包含所有必需章节
- [ ] 文献引用不少于 20 篇
- [ ] 数据分析部分有具体数据和图表
- [ ] 语法和格式正确,无拼写错误

## 输出格式要求

在每个迭代结束时,在响应末尾添加以下状态块:

\`\`\`
RALPH_STATUS:
{
  "progress": 0-100,
  "current_task": "正在进行的任务描述",
  "EXIT_SIGNAL": false,
  "notes": "当前进展说明"
}
\`\`\`

## 当前研究主题

[在此处填写研究主题]

集成步骤

  1. 安装 Ralph(适配版)
bash
# 克隆 Ralph 仓库并修改以支持 OpenCode
git clone https://github.com/frankbria/ralph-claude-code.git
cd ralph-claude-code

# 修改配置文件以使用 OpenCode
sed -i 's/claude/opencode/g' ralph_loop.sh

# 安装
./install.sh
  1. 初始化研究项目
bash
ralph-setup my-research-report
cd my-research-report

# 编辑 PROMPT.md,填入研究主题
vim PROMPT.md
  1. 启动自动化循环
bash
# 启动带监控的循环
ralph --monitor

# 或者手动启动两个终端
# 终端 1
ralph

# 终端 2
ralph-monitor

优点与缺点

优点

  • 完整保留 Ralph 的所有保护机制(断路器、速率限制、智能退出)
  • 最小化开发工作量
  • 利用成熟的社区经验和最佳实践
  • 代码已经过 308 个测试用例验证

缺点

  • 严重依赖 OpenCode 的 Stop Hook 支持
  • 如果 OpenCode 更新导致接口变化,需要重新适配
  • 可能无法充分利用 OpenCode 的独有特性

方案 B:外部循环控制(不依赖 Stop Hook)

架构概述

如果 OpenCode 不支持 Stop Hook,使用外部 Bash 循环来实现类似功能。这个方案需要自行实现状态管理和循环控制逻辑。

系统架构图

核心组件设计

1. Shell 循环控制器

bash
#!/bin/bash
# ralph-external-loop.sh

MAX_ITERATIONS=50
LOOP_COUNT=0
MAX_API_CALLS=100
API_CALLS=0
HOURLY_LIMIT_RESET=$(date -d '+1 hour' +%s)

while [ $LOOP_COUNT -lt $MAX_ITERATIONS ]; do
    # 1. 速率限制检查
    if [ $API_CALLS -ge $MAX_API_CALLS ]; then
        current_time=$(date +%s)
        if [ $current_time -lt $HOURLY_LIMIT_RESET ]; then
            wait_seconds=$((HOURLY_LIMIT_RESET - current_time))
            echo "达到 API 速率限制,等待 $wait_seconds 秒..."
            sleep $wait_seconds
            HOURLY_LIMIT_RESET=$(date -d '+1 hour' +%s)
            API_CALLS=0
        fi
    fi

    # 2. 读取当前状态
    current_status=$(cat .current_status.json 2>/dev/null || echo '{}')

    # 3. 生成上下文提示词
    prompt=$(generate_prompt_with_context "$current_status")

    # 4. 调用 OpenCode
    output=$(opencode -p "$prompt" 2>&1)
    ((API_CALLS++))

    # 5. 保存输出
    echo "$output" > "outputs/loop_$(printf '%04d' $LOOP_COUNT).txt"

    # 6. 解析输出并更新状态
    updated_status=$(parse_output_and_update_status "$output" "$current_status")
    echo "$updated_status" > .current_status.json

    # 7. 检查完成条件
    if check_completion "$output" "$updated_status"; then
        echo "✅ 任务完成!"
        break
    fi

    # 8. 断路器检查
    if check_stagnation "$updated_status"; then
        echo "⚠️ 检测到停滞,暂停循环"
        handle_stagnation
        break
    fi

    # 9. 短暂等待后继续
    sleep 5
    ((LOOP_COUNT++))
done

echo "循环结束,总迭代次数: $LOOP_COUNT"

2. 上下文提示词生成器

bash
#!/bin/bash
# prompt_generator.sh

generate_prompt_with_context() {
    local status="$1"

    # 提取当前任务和进度
    current_task=$(echo "$status" | jq -r '.current_task // "开始新任务"')
    completed_tasks=$(echo "$status" | jq -r '.completed_tasks // []')
    pending_tasks=$(echo "$status" | jq -r '.pending_tasks // []')

    # 生成提示词
    cat << PROMPT
# 自动化研究报告生成 - 第 $(($(cat .loop_count) + 1)) 次迭代

## 当前状态
- 当前进度: $(echo "$status" | jq -r '.progress // 0')%
- 当前任务: $current_task

## 已完成任务
$(for task in $(echo "$completed_tasks" | jq -r '.[]'); do echo "- $task"; done)

## 待办任务
$(for task in $(echo "$pending_tasks" | jq -r '.[]'); do echo "- $task"; done)

## 本次迭代指令

1. 继续执行当前任务或下一个待办任务
2. 完成后更新 .current_status.json 中的任务状态
3. 如果所有任务完成,在响应末尾添加标记: <<COMPLETED>>

## 研究主题

$(cat PROMPT.md | grep -A 100 "## 当前研究主题")
PROMPT
}

3. 状态解析器与更新器

bash
#!/bin/bash
# status_parser.sh

parse_output_and_update_status() {
    local output="$1"
    local current_status="$2"

    # 提取进度信息(从输出中解析)
    progress=$(extract_progress_from_output "$output")
    current_task=$(extract_current_task_from_output "$output")

    # 更新任务列表
    completed_tasks=$(update_completed_tasks "$output" "$(echo "$current_status" | jq -r '.completed_tasks // []')")
    pending_tasks=$(update_pending_tasks "$output" "$(echo "$current_status" | jq -r '.pending_tasks // []')")

    # 生成新的状态 JSON
    cat << STATUS | jq '.'
{
  "loop_count": $(($(cat .loop_count) + 1)),
  "progress": $progress,
  "current_task": "$current_task",
  "completed_tasks": $(echo "$completed_tasks" | jq '.'),
  "pending_tasks": $(echo "$pending_tasks" | jq '.'),
  "last_update": "$(date -Iseconds)",
  "api_calls_this_hour": $API_CALLS
}
STATUS
}

check_completion() {
    local output="$1"
    local status="$2"

    # 检查完成标记
    if echo "$output" | grep -q "<<COMPLETED>>"; then
        return 0  # 完成
    fi

    # 检查所有任务是否完成
    pending_count=$(echo "$status" | jq '.pending_tasks | length')
    if [ "$pending_count" -eq 0 ]; then
        return 0  # 完成
    fi

    return 1  # 未完成
}

check_stagnation() {
    local status="$1"

    # 检查连续无进展次数
    no_progress_count=$(echo "$status" | jq '.no_progress_count // 0')

    if [ "$no_progress_count" -ge 3 ]; then
        return 0  # 检测到停滞
    fi

    return 1  # 未停滞
}

4. 监控与通知模块

bash
#!/bin/bash
# monitor.sh

display_dashboard() {
    while [ -f .current_status.json ]; do
        clear

        status=$(cat .current_status.json)

        echo "═══════════════════════════════════════"
        echo "       Ralph 外部循环监控面板"
        echo "═══════════════════════════════════════"
        echo
        echo "📊 当前进度: $(echo "$status" | jq -r '.progress')%"
        echo "🔄 迭代次数: $(echo "$status" | jq -r '.loop_count')"
        echo "📝 当前任务: $(echo "$status" | jq -r '.current_task')"
        echo
        echo "✅ 已完成任务:"
        echo "$status" | jq -r '.completed_tasks[]' | sed 's/^/  - /'
        echo
        echo "⏳ 待办任务:"
        echo "$status" | jq -r '.pending_tasks[]' | sed 's/^/  - /'
        echo
        echo "📞 API 调用: $(echo "$status" | jq -r '.api_calls_this_hour')/100 (每小时)"
        echo "⏰ 最后更新: $(echo "$status" | jq -r '.last_update')"
        echo "═══════════════════════════════════════"

        sleep 2
    done
}

项目结构

research-project/
├── PROMPT.md                 # 原始项目需求
├── PROMPT_ENHANCED.md        # 增强版提示词(带上下文)
├── ralph-external-loop.sh    # 主循环控制器
├── prompt_generator.sh       # 上下文提示词生成器
├── status_parser.sh         # 状态解析与更新
├── monitor.sh               # 监控面板
├── .current_status.json     # 当前状态(持久化)
├── .loop_count              # 循环计数器
├── .api_calls               # API 调用计数器
├── outputs/                 # 历史输出存档
│   ├── loop_0001.txt
│   ├── loop_0002.txt
│   └── ...
├── src/                     # 研究报告源文件
└── logs/                    # 执行日志
    └── ralph.log

GLM4.7 提示词增强

markdown
# PROMPT_ENHANCED.md

# 自动化研究报告生成任务

## 核心指令

你是一个专业的研究报告生成助手,正在执行自动化研究报告生成任务。

### 你的工作方式

1. **阅读上下文**:每次迭代都会收到当前状态,包括已完成和待办任务
2. **执行一个任务**:专注于完成一个具体任务,不要试图一次完成所有事情
3. **报告进展**:完成后明确说明你完成了什么
4. **请求下一个任务**:完成后建议下一步应该做什么

### 任务管理规范

当你完成一个任务时,使用以下格式报告:

✅ 已完成: [任务描述]


当你建议下一个任务时,使用以下格式:

📋 建议: [下一个任务描述]


### 完成判断标准

当满足以下所有条件时,在响应最后添加 `<<COMPLETED>>` 标记:
- [ ] 报告结构完整
- [ ] 所有章节内容充实
- [ ] 文献引用充足
- [ ] 数据分析详尽
- [ ] 格式正确无误

## 当前迭代信息

**循环次数**: {{LOOP_COUNT}}
**当前进度**: {{PROGRESS}}%

## 任务列表

### 已完成任务
{{COMPLETED_TASKS}}

### 待办任务
{{PENDING_TASKS}}

## 当前任务

{{CURRENT_TASK}}

## 原始研究需求

$(cat PROMPT.md | tail -n +$(grep -n "## 当前研究主题" PROMPT.md | cut -d: -f1))

---

**请开始执行当前任务,完成后报告进展并建议下一步。**

集成步骤

  1. 下载和配置脚本
bash
# 创建项目目录
mkdir my-research-report && cd my-research-report

# 下载脚本文件
curl -O https://raw.githubusercontent.com/your-repo/ralph-external/main/ralph-external-loop.sh
curl -O https://raw.githubusercontent.com/your-repo/ralph-external/main/prompt_generator.sh
curl -O https://raw.githubusercontent.com/your-repo/ralph-external/main/status_parser.sh
curl -O https://raw.githubusercontent.com/your-repo/ralph-external/main/monitor.sh

# 添加执行权限
chmod +x *.sh

# 创建输出目录
mkdir -p outputs logs src
  1. 初始化状态文件
bash
# 创建初始状态
cat > .current_status.json << 'EOF'
{
  "loop_count": 0,
  "progress": 0,
  "current_task": "初始化项目结构",
  "completed_tasks": [],
  "pending_tasks": [
    "创建报告目录结构",
    "撰写研究背景章节",
    "文献检索与分析",
    "撰写方法论述",
    "数据分析与可视化",
    "撰写结论与建议",
    "格式校对与排版"
  ],
  "last_update": "$(date -Iseconds)",
  "api_calls_this_hour": 0,
  "no_progress_count": 0
}
EOF

# 创建计数器文件
echo 0 > .loop_count
echo 0 > .api_calls
  1. 编写原始 PROMPT.md
bash
cat > PROMPT.md << 'EOF'
# 自动化研究报告生成

## 当前研究主题

人工智能在医疗诊断中的应用:现状、挑战与未来趋势

## 报告要求

1. 生成一份约 50 页的研究报告
2. 使用学术规范格式(APA 格式)
3. 包含文献综述、案例分析、数据分析
4. 所有内容需有可靠的来源引用
EOF
  1. 启动循环
bash
# 终端 1:启动主循环
./ralph-external-loop.sh

# 终端 2:启动监控
./monitor.sh

优点与缺点

优点

  • 不依赖 OpenCode 的特殊功能(Stop Hook)
  • 完全控制循环逻辑,易于调试和定制
  • 可以根据 GLM4.7 的特性优化提示词
  • 状态管理灵活,可以集成到现有工作流中

缺点

  • 需要大量自行开发(约 300-500 行 Bash 代码)
  • 缺少 Ralph 经过验证的保护机制
  • 需要自行编写测试用例验证可靠性
  • 维护成本较高

方案对比

对比维度方案 A(原生集成)方案 B(外部循环)
开发工作量低(适配现有代码)高(从头开发)
可靠性高(已验证)中(需测试)
依赖条件OpenCode 支持 Stop Hook无特殊依赖
功能完整性完整(所有 Ralph 特性)部分(需自行实现)
维护成本低(跟随上游更新)高(自主维护)
灵活性中(受限于 Ralph 设计)高(完全自定义)
适用场景OpenCode 支持钩子OpenCode 不支持钩子
实施时间1-2 天5-7 天

推荐方案

如果 OpenCode 支持 Stop Hook:选择 方案 A

  • 快速实施
  • 可靠性高
  • 享受社区生态

如果 OpenCode 不支持 Stop Hook:选择 方案 B

  • 不受限于 OpenCode 功能
  • 完全控制逻辑
  • 可以逐步优化

混合方案建议

考虑到 OpenCode 功能的不确定性,建议采取渐进式实施策略

阶段 1:验证(1 天)

  • 执行 Blackbox 测试验证 OpenCode 能力
  • 确认 Stop Hook 和会话管理支持情况

阶段 2:最小可行方案(2-3 天)

  • 如果支持 Stop Hook:实施方案 A 的核心功能
  • 如果不支持:实施方案 B 的基础版本(简化版)

阶段 3:功能增强(1-2 周)

  • 根据实际运行情况补充缺失功能
  • 添加监控和日志分析
  • 优化 GLM4.7 提示词

阶段 4:生产化(持续)

  • 集成到现有工作流
  • 建立自动化测试
  • 性能优化和成本控制

参考资料