Logo
热心市民王先生

解决方案设计

技术研究 人工智能 OpenCode

理想状态:如果 OpenCode 提供以下能力,原生集成方案将可行:

方案A:原生集成方案(评估结果:不可行)

前提条件分析

理想状态:如果 OpenCode 提供以下能力,原生集成方案将可行:

  1. 任务完成钩子(Task Completion Hooks)

    // opencode.json 配置示例
    {
      "hooks": {
        "onComplete": "curl -X POST https://api.example.com/notify?status=success",
        "onFailure": "curl -X POST https://api.example.com/notify?status=error"
      }
    }
  2. 进程状态 API

    # 查询任务状态
    curl http://localhost:4096/v1/status?task_id=xyz
    
    # 强制终止任务
    curl -X DELETE http://localhost:4096/v1/tasks/xyz
  3. 会话管理 API

    # 程序化创建新会话
    curl -X POST http://localhost:4096/v1/session/new

实际评估结果

❌ 结论:方案A在当前 OpenCode 版本中不可行

经过对官方文档和实际能力的验证,OpenCode 不提供上述任何一种机制。原生集成方案无法满足需求,必须采用变通方案(Workaround)。


方案B:变通方案(推荐)

方案概述

既然 OpenCode 不提供原生支持,我们需要在 OpenCode 外部构建一层包装器(Wrapper)来实现:

  1. 进程生命周期管理 - 启动、监控、终止 opencode 进程
  2. 状态捕获与通知 - 检测进程退出并发送 Telegram 通知
  3. 会话重置机制 - 通过进程重启模拟 /new 功能

实现方式对比

我们设计了三种不同复杂度的变通方案,适用于不同的可靠性要求和资源投入。

方案B1:进程包装器(Shell Wrapper)- ⭐ 推荐

核心思想:使用 Shell 脚本包装 opencode 执行,捕获退出状态并触发后续操作。

工作流程

┌─────────────────────────────────────────────────────────────────┐
│                         仓库A (Telegram Bot)                     │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ 接收 /research  │───▶│ 解析研究主题   │───▶│ 执行包装脚本 │  │
│  │ 命令           │    │ "电动车行业"   │    │ trigger.sh  │  │
│  └─────────────────┘    └─────────────────┘    └──────┬──────┘  │
└───────────────────────────────────────────────────────┼──────────┘

                                                        ▼ SSH/本地
┌─────────────────────────────────────────────────────────────────┐
│                         仓库B (Research)                         │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ research_       │───▶│ opencode run    │───▶│ 检测退出码  │  │
│  │ wrapper.sh      │    │ "电动车..."    │    │ (0=成功)   │  │
│  └─────────────────┘    └─────────────────┘    └──────┬──────┘  │
│                                                       │         │
│  ┌────────────────────────────────────────────────────┘         │
│  ▼                                                              │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ 发送 Telegram   │───▶│ 可选:清理进程 │───▶│ 可选:新会话 │  │
│  │ 通知           │    │ pkill opencode │    │ 重启opencode │  │
│  └─────────────────┘    └─────────────────┘    └─────────────┘  │
└─────────────────────────────────────────────────────────────────┘

优点

  • ✅ 实现简单,仅需 Shell 脚本知识
  • ✅ 无额外依赖(仅需 curl、ps 等标准工具)
  • ✅ 与 OpenCode 版本无关,兼容性好
  • ✅ 可快速部署验证

缺点

  • ❌ 耦合度较高,逻辑分散在 Shell 脚本中
  • ❌ 难以处理并发任务(需要 PID 管理)
  • ❌ 错误处理相对简单
  • ❌ 无法获取 opencode 内部详细错误信息

适用场景

  • 单个或少量的研究任务
  • 对可靠性要求不是极端严格的场景
  • 需要快速实现的 MVP 阶段

方案B2:文件信号机制(File Signal)

核心思想:使用文件系统作为进程间通信媒介,通过文件创建/修改来传递状态信号。

工作流程

┌─────────────────────────────────────────────────────────────────┐
│                         仓库A (Telegram Bot)                     │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ 文件监听器      │◀───│ 检测到完成信号 │◀───│ /tmp/       │  │
│  │ (watchdog)      │    │ 文件变化       │    │ research_   │  │
│  └────────┬────────┘    └─────────────────┘    │ complete    │  │
│           │                                      │ .signal     │  │
│           │                                      └─────────────┘  │
│           ▼ SSH/本地                                             │
│  ┌─────────────────┐    ┌─────────────────┐                       │
│  │ 发送 Telegram   │    │ 触发清理/新会话 │                       │
│  │ 通知           │    │ 操作           │                       │
│  └─────────────────┘    └─────────────────┘                       │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                         仓库B (Research)                         │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ opencode run    │───▶│ 研究执行完成   │───▶│ 创建信号文件 │  │
│  │ "电动车..."    │    │ (成功或失败)   │    │ echo done > │  │
│  └─────────────────┘    └─────────────────┘    │ /tmp/...    │  │
└─────────────────────────────────────────────────────────────────┘

实现细节

仓库B的研究脚本需要添加信号写入逻辑:

#!/bin/bash
# research_task.sh

RESEARCH_TOPIC="$1"
SIGNAL_FILE="/tmp/research_complete.signal"

echo "开始研究: $RESEARCH_TOPIC"

# 执行研究
if opencode run "$RESEARCH_TOPIC"; then
    echo "success" > "$SIGNAL_FILE"
else
    echo "failure:$?" > "$SIGNAL_FILE"
fi

仓库A的文件监听器(Python 示例):

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess

class ResearchCompleteHandler(FileSystemEventHandler):
    def on_modified(self, event):
        if event.src_path == "/tmp/research_complete.signal":
            with open(event.src_path, 'r') as f:
                status = f.read().strip()
            
            if status == "done":
                subprocess.run(["python", "send_notification.py"])
                subprocess.run(["bash", "start_new_session.sh"])

# 启动监听
observer = Observer()
observer.schedule(ResearchCompleteHandler(), path='/tmp')
observer.start()

优点

  • ✅ 仓库A和仓库B解耦,通过文件系统间接通信
  • ✅ 即使仓库B的进程崩溃,信号文件仍然保留状态
  • ✅ 支持更复杂的协调逻辑(多个任务排队)

缺点

  • ❌ 需要额外的文件监控进程(消耗资源)
  • ❌ 实时性稍差(依赖文件系统事件延迟)
  • ❌ 需要处理文件锁和并发写入问题
  • ❌ 信号文件需要定期清理,否则可能积累

适用场景

  • 需要解耦仓库A和仓库B的场景
  • 研究任务执行时间较长(分钟级)
  • 需要支持任务队列和重试机制

方案B3:消息队列(Redis/Message Queue)

核心思想:使用 Redis 或消息队列作为中间件,实现可靠的异步通信。

工作流程

┌─────────────────────────────────────────────────────────────────┐
│                         仓库A (Telegram Bot)                     │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ Redis 消费者    │◀───│ 监听研究完成   │◀───│ Redis       │  │
│  │ (subscriber)    │    │ 频道消息       │    │ Channel     │  │
│  └────────┬────────┘    └─────────────────┘    └──────┬──────┘  │
│           │                                           │         │
│           ▼                                           │         │
│  ┌─────────────────┐    ┌─────────────────┐          │         │
│  │ 发送 Telegram   │    │ 触发清理/新会话 │◀─────────┘         │
│  │ 通知           │    │ 操作           │                      │
│  └─────────────────┘    └─────────────────┘                      │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                         仓库B (Research)                         │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────┐  │
│  │ opencode run    │───▶│ 研究执行完成   │───▶│ 发布消息到  │  │
│  │ "电动车..."    │    │ (成功或失败)   │    │ Redis 频道  │  │
│  └─────────────────┘    └─────────────────┘    └─────────────┘  │
└─────────────────────────────────────────────────────────────────┘

实现细节

仓库B发布消息:

import redis
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def publish_completion(status, result=None):
    message = {
        'type': 'research_complete',
        'status': status,
        'result': result
    }
    r.publish('opencode_research', json.dumps(message))

# 执行研究后
publish_completion('success' if exit_code == 0 else 'failure')

仓库A消费消息:

import redis
import json
import subprocess

r = redis.Redis(host='localhost', port=6379, db=0)

def handle_message(message):
    data = json.loads(message['data'])
    if data['type'] == 'research_complete':
        subprocess.run(['curl', '-X', 'POST', 'https://api.telegram.org/bot...'])

pubsub = r.pubsub()
pubsub.subscribe(**{'opencode_research': handle_message})
pubsub.run_in_thread()

优点

  • ✅ 高可靠性,消息持久化
  • ✅ 支持多消费者(可扩展多个仓库A实例)
  • ✅ 消息可携带丰富的元数据
  • ✅ 支持消息确认和重试机制

缺点

  • ❌ 需要部署 Redis 或消息队列服务
  • ❌ 架构复杂度最高
  • ❌ 需要处理 Redis 连接失败等异常情况
  • ❌ 运维成本增加

适用场景

  • 高并发研究任务(每秒多个任务)
  • 需要任务持久化和重试的企业级场景
  • 多个消费者需要同时接收通知的场景

优缺点对比总结

方案复杂度可靠性实时性资源消耗扩展性推荐度
B1 Shell包装器⭐ 低⭐⭐ 中⭐⭐⭐ 高⭐ 极低⭐ 低⭐⭐⭐⭐⭐
B2 文件信号⭐⭐ 中⭐⭐⭐ 高⭐⭐ 中⭐⭐ 中⭐⭐ 中⭐⭐⭐
B3 消息队列⭐⭐⭐ 高⭐⭐⭐⭐⭐ 极高⭐⭐⭐ 高⭐⭐⭐ 高⭐⭐⭐⭐⭐ 极高⭐⭐⭐

选择建议

  • 快速验证/MVP:选择方案B1
  • 生产环境/中等规模:选择方案B2
  • 企业级/大规模:选择方案B3

架构图

graph TB
    subgraph "仓库A - Telegram Bot服务"
        A1[用户发送<br/>/research 命令]
        A2[消息解析器<br/>提取研究主题]
        A3[触发器<br/>调用仓库B]
        A4[通知发送器<br/>Telegram API]
        A5[会话管理器<br/>/new 或清理]
    end
    
    subgraph "通信层"
        C1["方案B1: 直接调用<br/>subprocess.run"]
        C2["方案B2: 文件信号<br/>/tmp/*.signal"]
        C3["方案B3: 消息队列<br/>Redis Pub/Sub"]
    end
    
    subgraph "仓库B - Research执行器"
        B1[Shell包装器<br/>research_wrapper.sh]
        B2[OpenCode CLI<br/>opencode run]
        B3[GitHub提交<br/>git push]
        B4[状态报告<br/>exit code]
    end
    
    A1 --> A2
    A2 --> A3
    A3 --> C1
    A3 -.-> C2
    A3 -.-> C3
    
    C1 --> B1
    C2 -.-> B1
    C3 -.-> B1
    
    B1 --> B2
    B2 --> B3
    B2 --> B4
    B4 --> C1
    B4 -.-> C2
    B4 -.-> C3
    
    C1 --> A4
    C2 -.-> A4
    C3 -.-> A4
    
    A4 --> A5
    A5 --> A1

推荐方案

最终建议

主要推荐:方案B1(Shell包装器)

理由:

  1. 快速实施:可在1-2小时内完成开发和测试
  2. 低维护成本:无需额外服务(Redis、文件监控等)
  3. 足够可靠:对于当前描述的用例(单个研究任务),Shell 脚本的可靠性已足够
  4. 易于调试:Shell 脚本便于查看日志和排查问题

实施路线图

阶段1(第1周):方案B1实施
  ├─ 开发 research_wrapper.sh
  ├─ 集成 Telegram Bot 通知
  └─ 测试完整流程

阶段2(第2-4周):监控与优化
  ├─ 收集运行日志
  ├─ 识别边界情况
  └─ 优化错误处理

阶段3(视需求):升级到方案B2或B3
  ├─ 如果出现并发需求,升级到B2
  └─ 如果需要高可靠性,升级到B3

备选方案触发条件

  • 如果需要同时处理5个以上的并发研究任务 → 考虑方案B2
  • 如果需要99.9%以上的可靠性保证 → 考虑方案B3
  • 如果研究任务执行时间超过30分钟 → 考虑方案B2(避免SSH连接超时)