Appearance
解决方案设计
方案 A:原生集成(OpenCode 支持 Stop Hook)
架构概述
假设 OpenCode 支持 Stop Hook 机制,本方案将 Ralph-Loop 的核心逻辑移植到 OpenCode + GLM4.7 环境中,实现最小化改动的集成。
系统架构图
核心组件设计
1. Ralph 控制器(ralph_loop.sh 适配版)
基于 frankbria/ralph-claude-code 的 ralph_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 # 阻止退出,触发下一轮
fi3. 会话管理模块
实现跨循环的上下文保持:
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": "当前进展说明"
}
\`\`\`
## 当前研究主题
[在此处填写研究主题]集成步骤
- 安装 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- 初始化研究项目
bash
ralph-setup my-research-report
cd my-research-report
# 编辑 PROMPT.md,填入研究主题
vim PROMPT.md- 启动自动化循环
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.logGLM4.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))
---
**请开始执行当前任务,完成后报告进展并建议下一步。**集成步骤
- 下载和配置脚本
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- 初始化状态文件
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- 编写原始 PROMPT.md
bash
cat > PROMPT.md << 'EOF'
# 自动化研究报告生成
## 当前研究主题
人工智能在医疗诊断中的应用:现状、挑战与未来趋势
## 报告要求
1. 生成一份约 50 页的研究报告
2. 使用学术规范格式(APA 格式)
3. 包含文献综述、案例分析、数据分析
4. 所有内容需有可靠的来源引用
EOF- 启动循环
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:生产化(持续)
- 集成到现有工作流
- 建立自动化测试
- 性能优化和成本控制
参考资料
- frankbria/ralph-claude-code GitHub - 方案 A 参考实现
- Bash Scripting Guide - 方案 B Bash 编程参考
- GLM-4 API Documentation - GLM4.7 接口文档
- OpenCode Documentation - OpenCode 使用指南