yoi/docs/ref/opencode-comparison.md
2026-06-01 18:49:23 +09:00

313 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Yoi × OpenCode 比較レポート
## 概要
YoiRust製エージェントプラットフォーム、基礎実装段階と OpenCodeTypeScript/Bun製AIコーディングアシスタント、本番稼働レベルの設計を比較し、Yoiの基礎設計に取り込めるパターンを特定する。
---
## 1. アーキテクチャ概観
### Yoi現状
```
yoi (stub)
└─ yoi-core Pod / Controller / Protocol / SocketServer
└─ llm-worker-persistence Session永続化JSONL + Blob
└─ llm-worker Worker / Tool / Hook / Subscriber
└─ llm-worker-macros #[tool] / #[tool_registry]
```
- 実行単位は **Pod**(独立したエージェント)
- Unix Domain Socket + JSONL で外部通信
- 状態遷移: Idle → Running → Paused/Idle
- 永続化: append-only JSONL ログ
### OpenCode
```
packages/opencode (server) Session / Provider / Tool / Permission / Agent / Bus
packages/app (TUI) SolidJS + OpenTUITerminal UI
packages/sdk (client) Hono OpenAPIから自動生成
packages/desktop (Tauri) Web UIラッパー
```
- 実行単位は **Session**会話。Session内で **Agent** が切り替わる
- HTTP/SSE/WebSocket で外部通信
- SQLite + Drizzle ORM で永続化
- Effect.ts による依存注入・リソース管理
---
## 2. 設計判断の比較
| 観点 | Yoi | OpenCode | 評価 |
|------|----------|----------|------|
| **DI** | ジェネリクス `<C: LlmClient, St: Store>` | Effect Service + Layer | Yoi: コンパイル時保証。OpenCode: 実行時合成の柔軟性。方向性は正しい |
| **状態管理** | `RwLock<PodStatus>` + ファイル書き出し | SQLite + Event Bus + SSE | Yoi: 軽量で正しい。DBは将来の選択肢 |
| **プロトコル** | 自前 JSONL (Method/Event) | Hono HTTP API + SSE | Yoi: Unix Socketに最適化。目的が違う |
| **ツール** | `Tool` trait + マクロ生成 | Zod schema + execute関数 | 同等のアプローチ。マクロの方が型安全 |
| **フック** | `Hook<K: HookEventKind>` trait 10種 | Plugin hooks (before/after) | Yoi: 型安全で粒度が細かい。OpenCode: 動的で拡張しやすい |
| **永続化** | JSONL append-only + Blob | SQLite + Drizzle ORM | 方向性が異なる。両方とも正当な選択 |
| **プロバイダ** | 4種Anthropic/OpenAI/Gemini/Ollama | 20種+ai-sdk経由 | 数は後から追加できる。抽象は同レベル |
---
## 3. OpenCodeから取り込むべき設計パターン
### 3.1 パーミッションシステム(重要度: 高)
**OpenCodeの設計:**
- ツール実行前に **パターンベース** の権限チェック(`*.env` → deny、`src/**` → allow
- 3段階: `deny``allow``ask`(ユーザーに確認)
- 「always」応答でパターンを永続的に許可
**Yoiへの示唆:**
Yoiには `Scope`(書き込みディレクトリ制約)があるが、これは静的な境界。
ツール単位の動的パーミッションが欠落している。
```
提案: PreToolCall Hook でパーミッション評価を行う
```
- **設計原則3再発明しない** に沿って、新しいtrait は作らない
- `PreToolCall` Hook として実装し、Podマニフェストにルールを宣言
- ルール定義は Scope の拡張ではなく、独立した概念として追加
```toml
# マニフェスト拡張案
[[permission]]
tool = "bash"
pattern = "rm *"
action = "deny"
[[permission]]
tool = "file_write"
pattern = "*.env"
action = "deny"
```
**今すぐ実装すべきか:** まだ。ツールが実装されてから。
ただし、**Hook で差し込める設計になっている** ことは確認済み。
拡張ポイントとして docs/pod.md の表に追加する価値あり。
---
### 3.2 ツール出力のトランケーション(重要度: 高)
**OpenCodeの設計:**
- 50KB / 2000行を超える出力を切り捨て
- 切り捨て分はファイルに保存7日間保持
- LLMには「出力が大きすぎた。`grep` や `read` で絞り込め」とヒント
**Yoiの現状:**
- `llm-worker` に Tool Output の Inline/Stored 閾値800 bytesがある
- Stored 出力は Blob Storage に退避し、要約を自動生成
**比較:**
Yoi の方が洗練されている(要約生成まで組み込み済み)。
ただし OpenCode の「ヒント付きトランケーション」は追加の視点として有用。
**取り込み案:**
- Stored 出力時に「元データの場所と推奨アクション」を要約に含める規約
- これは llm-worker のツール出力設計に自然に統合できる
- 現状の `tool-output-design.md` の Auto-Summarization に、推奨アクションのヒント生成を追加する余地がある
---
### 3.3 コンテキスト圧縮Compaction重要度: 高)
**OpenCodeの設計:**
1. **Prune**: 古いツール出力を除去直近40Kトークンは保護
2. **Compact**: オーバーフロー時に専用エージェントで要約を生成
- 構造化要約: Goal / Instructions / Discoveries / Accomplished / Files
3. **Replay**: 圧縮後に前回のユーザーメッセージを再送して作業継続
**Yoiの現状:**
- Worker は history をそのまま保持
- コンテキスト管理の仕組みは未実装
**これは重要な欠落。** 長時間実行エージェントである Yoi にとって、コンテキスト管理はコア機能。
**取り込み案:**
```
extract: PruneHook ベース)
PreLlmRequest Hook で古いツール出力を削除
設計原則3に従い、新しい抽象は作らない
consolidation: CompactAgent ベース)
OnTurnEnd Hook でトークン数をチェック
閾値超過時に要約生成を挿入
Workerのresume機構で作業を継続
```
**設計の要点:**
- Prune は `PreLlmRequest` Hook で history を変更するMutable state で可能)
- Compact は Pod レベルの制御Controller が要約 Pod を起動)
- OpenCode の「構造化要約フォーマット」は良い規約 → system prompt に含める
---
### 3.4 Event Bus / Typed Events重要度: 中)
**OpenCodeの設計:**
- 型付きイベント定義Zod スキーマ)
- Instance スコープ + Global スコープの二段バス
- `publish` / `subscribe` / `subscribeAll` の3操作
**Yoiの現状:**
- `broadcast::Sender<Event>` による単一チャネル
- Event enum で型安全
- Pod 単位のスコープのみ
**比較:**
Yoi の broadcast channel は Pod 単位では十分。
ただし、**複数 Pod の協調**Supervisor段階で Global Bus が必要になる。
**取り込み案:**
- 現時点では不要設計原則4
- Supervisor 実装時に参考にする設計として記録
- OpenCode の「Instance スコープ → Global 伝播」パターンは、Pod スコープ → Supervisor 伝播に自然に対応
---
### 3.5 スナップショットシステム(重要度: 中)
**OpenCodeの設計:**
- 内部 git リポジトリでファイル変更を追跡
- ツール実行前にスナップショット取得
- `restore` / `revert` / `diff` 操作
**Yoiの現状:**
- Scope書き込み制約はあるが、変更追跡・復元は未実装
**取り込み案:**
- ファイル操作ツールの実装時に、git ベースのスナップショットを組み込む
- `PreToolCall` / `PostToolCall` Hook で自然に差し込める
- OpenCode の sparse checkout アプローチ(変更ファイルのみ追跡)は効率的
**今すぐ実装すべきか:** まだ。ファイル操作ツールの実装後。
---
### 3.6 Agent/Subagent パターン(重要度: 中)
**OpenCodeの設計:**
- Primary Agentbuild, planと Subagentexplore, generalの区別
- Agent ごとにモデル・パーミッション・プロンプトを個別設定
- `steps` パラメータでサブエージェントの反復回数を制限
- 親セッションのコンテキストを子に渡す
**Yoiの現状:**
- Pod は独立実行単位。Pod 間通信は未実装
- 拡張ポイント表に「Supervisor」として記載
**比較:**
OpenCode の Agent は Session 内のモード切り替え。
Yoi の Pod は完全に独立したプロセス。
**取り込み案:**
- OpenCode の `steps`(最大反復回数)は Pod マニフェストに追加する価値あり
- `[worker]` セクションに `max_turns` を追加
- Worker の `OnTurnEnd` Hook で制御
- Agent テンプレート(名前 + モデル + プロンプト + パーミッション)は
マニフェストがすでにこの役割を果たしている → 追加不要
- Subagent パターンは Supervisor の責務 → 設計原則5に従い Pod 内部には入れない
---
### 3.7 Config 階層(重要度: 低〜中)
**OpenCodeの設計:**
- Managed → Remote → Global → Env → Project → Workspace の6段階
- 配列フィールドはマージ(上書きではなく結合)
- Plugin の出自を追跡PluginOrigin
**Yoiの現状:**
- マニフェストTOMLのみ。階層なし
**取り込み案:**
- Daemon 実装時にグローバル設定が必要になる
- OpenCode の「マージ戦略の明示」は参考になる
- 単純上書き vs 配列結合 vs 深いマージ
- 現時点ではマニフェスト一本で正しい設計原則4
---
### 3.8 LSP 統合(重要度: 低)
**OpenCodeの設計:**
- ファイル拡張子でサーバーを自動選択
- ワークスペースルートを marker ファイルから検出
- 遅延初期化(必要時にのみ起動)
- graceful degradationサーバーなし → 無視)
**Yoiの現状:**
- LSP の言及なし
**取り込み案:**
- コーディングエージェントとして使う場合にのみ必要
- ツールとして実装する際に OpenCode のパターンを参考にする
- Hook ベースの差し込みではなく、Tool 実装として提供
---
## 4. 設計思想の根本的な違い
### Yoi: 「Pod は独立した実行単位」
- 各 Pod が完結したプロセス
- 協調は外部Supervisorが行う
- Unix の哲学に近い
### OpenCode: 「Session が状態を持ち、Agent が振る舞いを切り替える」
- Session 内で Agent を動的に切り替え
- Session がコンテキストの境界
- アプリケーションの哲学に近い
**この違いは意図的であり、変える必要はない。**
Yoi のアプローチは長時間自律実行に適しており、Pod の独立性がフォールトトレランスと拡張性の基盤になる。
---
## 5. 優先度付きアクション項目
### 今すぐ設計に反映(基礎段階で重要)
| # | 項目 | 理由 | 実装場所 |
|---|------|------|----------|
| 1 | **max_turns をマニフェストに追加** | 暴走防止。OpenCodeのstepsに相当 | WorkerManifest |
| 2 | **コンテキスト圧縮の設計文書** | 長時間実行のコア要件。Hook ベースの Prune + Compact 方針を固める | docs/ |
| 3 | **パーミッションの拡張ポイント記録** | Pod 設計の拡張ポイント表にパターンベースのパーミッションを追加 | docs/pod.md |
### ツール実装時に取り込む
| # | 項目 | 理由 |
|---|------|------|
| 4 | ツール出力ヒント生成 | Stored 出力時に推奨アクションを含める |
| 5 | スナップショットgit ベース) | ファイル操作ツールと組み合わせ |
| 6 | パーミッション Hook | PreToolCall で動的権限チェック |
### Supervisor/Daemon 実装時に取り込む
| # | 項目 | 理由 |
|---|------|------|
| 7 | Global Event Bus | 複数 Pod 間のイベント伝播 |
| 8 | Config 階層 | グローバル設定 + プロジェクト設定のマージ |
---
## 6. 取り込まないもの(と理由)
| 項目 | 理由 |
|------|------|
| Effect.ts 的な DI | Rust のジェネリクス + trait がすでにこの役割。再発明になる |
| SQLite 永続化 | JSONL append-only は Pod の独立性と相性が良い。変える理由がない |
| HTTP API サーバー | Unix Socket + JSONL は Pod のユースケースに最適。Daemon 層で HTTP を追加する選択肢はある |
| Session 内 Agent 切り替え | Pod = 独立実行単位 の設計方針と矛盾。Subagent は Supervisor の責務 |
| SolidJS TUI | Rust には ratatui 等がある。技術スタックの違い |
| Plugin システム | 設計原則4に反する。Hook で十分 |