docs: reorganize developer documentation
This commit is contained in:
parent
e6c458021c
commit
9dcbd4c3e0
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
|||
/target
|
||||
.direnv
|
||||
/result
|
||||
/.direnv
|
||||
/.worktree
|
||||
*.local*
|
||||
.env
|
||||
.worktree
|
||||
|
|
|
|||
32
README.md
32
README.md
|
|
@ -1,5 +1,31 @@
|
|||
# 夜居 | Yoi agent
|
||||
# Yoi
|
||||
|
||||
夜居(Yoi)は不休のエージェントループを回すためのエージェントプラットフォーム。
|
||||
Yoi is an agent runtime for building, running, and orchestrating LLM Pods while preserving explicit history, scoped capabilities, and developer-controlled workflows.
|
||||
|
||||
ワークフローを統括し、四六時中電力を消費し、イテレーションします。
|
||||
Runtime surfaces use `yoi`, `.yoi`, `~/.yoi`, and `YOI_*`.
|
||||
|
||||
## Documentation map
|
||||
|
||||
Start with [`docs/README.md`](docs/README.md). The repository documentation is intentionally small: it should explain current design intent and development workflow, not preserve every old plan or external research note.
|
||||
|
||||
Important entry points:
|
||||
|
||||
- [`docs/design/overview.md`](docs/design/overview.md) — architecture and crate ownership map.
|
||||
- [`docs/design/context-history.md`](docs/design/context-history.md) — the rules for history, context, and prompt-cache-safe inputs.
|
||||
- [`docs/design/pod-session-state.md`](docs/design/pod-session-state.md) — why Pod metadata, session logs, and live runtime hints are separate.
|
||||
- [`docs/development/work-items.md`](docs/development/work-items.md) — work item and ticket workflow.
|
||||
- [`docs/development/validation.md`](docs/development/validation.md) — validation expectations.
|
||||
|
||||
## Development
|
||||
|
||||
This repository dogfoods Yoi to develop Yoi. Work is tracked through `work-items/` and `./tickets.sh`; git history plus work item files are the authoritative record of state changes.
|
||||
|
||||
Common local checks:
|
||||
|
||||
```sh
|
||||
./tickets.sh doctor
|
||||
git diff --check
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
E2E testing with real spawned processes is not yet designed. Avoid adding compatibility layers or broad formatting churn only to reduce short-term edit size; prefer clear boundaries and type safety.
|
||||
|
|
|
|||
30
crates/client/README.md
Normal file
30
crates/client/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# client
|
||||
|
||||
## Role
|
||||
|
||||
`client` contains reusable socket-client and runtime-command mechanics for talking to Pods from CLI/TUI code.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- one-shot Pod socket client behavior
|
||||
- request/reply delivery mechanics
|
||||
- runtime command construction below the product façade
|
||||
- shared attach/status probing helpers used by higher layers
|
||||
|
||||
Does not own:
|
||||
|
||||
- product command names (`yoi`)
|
||||
- Pod state authority (`pod`, `pod-store`, `session-store`)
|
||||
- UI rendering (`tui`)
|
||||
- Worker turn semantics (`llm-worker`)
|
||||
|
||||
## Design notes
|
||||
|
||||
The client boundary lets `tui` and `yoi` share Pod communication without making library crates depend on the product binary. Socket clients should drain connect-time snapshot/alert traffic before sending a method or deciding status.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
- [`../../docs/design/overview.md`](../../docs/design/overview.md)
|
||||
|
|
@ -1,9 +1,25 @@
|
|||
# daemon
|
||||
|
||||
Pod のライフサイクルを管理する常駐デーモン。未実装。
|
||||
## Role
|
||||
|
||||
## 依存クレート
|
||||
`daemon` is reserved for future long-lived Pod lifecycle management.
|
||||
|
||||
- `manifest` — マニフェスト設定
|
||||
- `protocol` — 通信プロトコル型
|
||||
- `tokio` — 非同期ランタイム
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- daemon-specific lifecycle coordination when that design is implemented
|
||||
|
||||
Does not own today:
|
||||
|
||||
- current Pod socket serving (`pod`)
|
||||
- normal CLI/TUI startup (`yoi`, `tui`)
|
||||
- live registry mechanics already handled elsewhere (`pod-registry`)
|
||||
|
||||
## Design notes
|
||||
|
||||
This crate exists as a placeholder. Do not route current runtime authority through it until there is a concrete daemon design and work item.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
|
|
|
|||
28
crates/lint-common/README.md
Normal file
28
crates/lint-common/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# lint-common
|
||||
|
||||
## Role
|
||||
|
||||
`lint-common` contains shared linting primitives for structured project records.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- reusable frontmatter/slug/path validation helpers
|
||||
- common lint diagnostic shapes used by higher-level crates
|
||||
|
||||
Does not own:
|
||||
|
||||
- memory-specific policy (`memory`)
|
||||
- workflow-specific policy (`workflow`)
|
||||
- product CLI command shape (`yoi`)
|
||||
- work item script behavior (`tickets.sh`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Keeping common lint mechanics here avoids duplicating low-level validation while leaving each record type's policy in its owning crate.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/memory-knowledge.md`](../../docs/design/memory-knowledge.md)
|
||||
- [`../../docs/development/work-items.md`](../../docs/development/work-items.md)
|
||||
|
|
@ -1,9 +1,27 @@
|
|||
# llm-worker-macros
|
||||
|
||||
Rust メソッドを LLM 呼び出し可能なツールとして自動登録する手続きマクロクレート。引数構造体・Tool トレイト実装・ToolDefinition を自動生成する。
|
||||
## Role
|
||||
|
||||
## 公開マクロ
|
||||
`llm-worker-macros` provides procedural macros for declaring Rust methods as LLM-callable tools.
|
||||
|
||||
- `#[tool_registry]` — impl ブロックに付与し、内部の `#[tool]` メソッドを一括処理
|
||||
- `#[tool]` — メソッドをツールとしてマーク
|
||||
- `#[description = "..."]` — 引数に説明を付与(JSON Schema の description に反映)
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- compile-time generation of tool argument structures and definitions
|
||||
- small macro conveniences around tool descriptions and schemas
|
||||
|
||||
Does not own:
|
||||
|
||||
- runtime permission decisions
|
||||
- filesystem scope checks
|
||||
- tool execution policy
|
||||
- model/tool-loop orchestration
|
||||
|
||||
## Design notes
|
||||
|
||||
Macros reduce boilerplate, but they must not imply capability. A generated tool definition is still subject to manifest permissions, Pod scope, and runtime policy.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,32 @@
|
|||
# llm-worker
|
||||
|
||||
LLM との対話を管理する低レベル基盤クレート。会話履歴、ツール実行、イベントストリーミング、ライフサイクルフックを統合した `Worker` 抽象を提供する。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`llm-worker` owns provider-independent model turn orchestration over committed history, tools, callbacks, retries, continuation, pruning, and compaction boundaries.
|
||||
|
||||
### コア
|
||||
## Boundaries
|
||||
|
||||
- `Worker<C, S>` — LLM 対話の中央管理(ターン実行、ツール呼び出し、キャンセル)
|
||||
- `WorkerConfig` / `WorkerResult` / `WorkerError` — 設定・実行結果・エラー
|
||||
- `Item` / `ContentPart` / `Role` — 会話履歴の構成要素
|
||||
Owns:
|
||||
|
||||
### モジュール
|
||||
- Worker history mutation and append contracts
|
||||
- tool-call loop semantics
|
||||
- pre-stream retry and stream-started continuation policy
|
||||
- pruning/compaction coordination from the Worker perspective
|
||||
- provider-neutral events/callbacks/interceptors
|
||||
|
||||
- `llm_client` — プロバイダ抽象(`LlmClient` トレイト、`Request`, `RequestConfig`, Anthropic/OpenAI/Gemini/Ollama 実装)
|
||||
- `tool` — ツール定義・実行(`Tool` トレイト、`ToolDefinition`, `ToolOutput`, サイズ判定による Inline/Stored 切替)
|
||||
- `tool_server` — ツール登録・ルックアップ(`ToolServer`, `ToolServerHandle`)
|
||||
- `hook` — 実行フローへの介入ポイント(`Hook` トレイト、`PreToolCall`, `PostToolCall`, `OnTurnEnd` など)
|
||||
- クロージャベースイベント購読(`Worker::on_text_block()`, `on_tool_use_block()`, `on_usage()` 等)
|
||||
- `timeline` — イベントストリームのディスパッチ(`Handler` トレイト、各ブロックコレクター)。パワーユーザー向けに `timeline_mut()` も提供
|
||||
- `event` — ストリーミングイベント型(`Event`, `BlockStart`, `BlockDelta` など)
|
||||
- `state` — 型状態パターンによるキャッシュ保護(`Mutable` / `CacheLocked`)
|
||||
cratesの整理Add READMEsRE to all crates@@
|
||||
Does not own:
|
||||
|
||||
- Pod names, sockets, process lifecycle, or scope delegation (`pod`)
|
||||
- product CLI shape (`yoi`)
|
||||
- provider catalog and secret resolution (`provider`, `secrets`)
|
||||
- durable Pod current state (`pod-store`)
|
||||
|
||||
## Design notes
|
||||
|
||||
The Worker is where turn lifecycle belongs because it sees history, in-flight usage, partial output, and tool-call state. It should not receive context-only volatile facts; model-affecting inputs must first be appended to history.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
- [`../../docs/design/compaction.md`](../../docs/design/compaction.md)
|
||||
- [`../../docs/design/provider-model-boundary.md`](../../docs/design/provider-model-boundary.md)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,31 @@
|
|||
# manifest
|
||||
|
||||
Pod の宣言的設定を TOML マニフェストとして定義・パースするクレート。モデル設定、ワーカー設定、ディレクトリスコープ制約を記述できる。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`manifest` resolves reusable profile/configuration inputs into the concrete runtime Manifest used to create or restore Pods.
|
||||
|
||||
- `PodManifest` — Pod 設定全体(`from_toml()` でパース)
|
||||
- `PodMeta` — Pod メタデータ(名前、pwd)
|
||||
- `ModelConfig` — LLM モデル設定(scheme、base_url、model_id、auth)
|
||||
- `SchemeKind` — wire scheme 種別(`Anthropic`, `OpenaiChat`, `OpenaiResponses`, `Gemini`)
|
||||
- `AuthRef` — 認証参照(`None`, `ApiKey { env, file }`, `CodexOAuth`)
|
||||
- `WorkerManifest` — ワーカー設定(システムプロンプト、生成設定、reasoning)
|
||||
- `ScopeConfig` / `ScopeRule` / `Permission` — allow / deny の宣言的スコープ設定
|
||||
- `Scope` — 実行時スコープ。`from_config(&ScopeConfig, pwd)` で構築し、`is_readable` / `is_writable` / `permission_at` で問い合わせる
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- Profile and Manifest data structures
|
||||
- source/partial/resolved configuration layering
|
||||
- path and prompt-resource resolution
|
||||
- tool permission and filesystem scope configuration types
|
||||
- model/provider references as configuration records
|
||||
|
||||
Does not own:
|
||||
|
||||
- provider HTTP clients or secret lookup implementation (`provider`, `secrets`)
|
||||
- Pod lifecycle (`pod`)
|
||||
- product CLI parsing (`yoi`)
|
||||
- generated memory records (`memory`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Profiles are reusable recipes; resolved Manifests are runtime contracts. Keep runtime-bound fields such as Pod name, concrete delegated scope, sockets, session pointers, and raw secrets out of reusable Profiles.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/profiles-manifests-prompts.md`](../../docs/design/profiles-manifests-prompts.md)
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
|
|
|
|||
31
crates/memory/README.md
Normal file
31
crates/memory/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# memory
|
||||
|
||||
## Role
|
||||
|
||||
`memory` owns generated memory, Knowledge records, staging/consolidation mechanics, linting, and audit observations.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- memory/Knowledge record parsing and validation
|
||||
- memory lint subcommand backend behavior
|
||||
- staging and consolidation file mechanics
|
||||
- audit log observation writes
|
||||
- generated memory prompt support where it belongs below the CLI
|
||||
|
||||
Does not own:
|
||||
|
||||
- authoritative project records (`work-items/`, git history)
|
||||
- normal Pod turn orchestration (`llm-worker`)
|
||||
- product CLI command shape (`yoi`)
|
||||
- curated workflow definitions (`workflow`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Memory is useful context, not authority. It should preserve small durable preferences and rationale without duplicating tickets, docs, or implementation reports.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/memory-knowledge.md`](../../docs/design/memory-knowledge.md)
|
||||
- [`../../docs/development/work-items.md`](../../docs/development/work-items.md)
|
||||
30
crates/pod-registry/README.md
Normal file
30
crates/pod-registry/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# pod-registry
|
||||
|
||||
## Role
|
||||
|
||||
`pod-registry` tracks live Pod process ownership and delegated scope locks at runtime.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- machine-local live Pod registration
|
||||
- collision detection for running Pod names
|
||||
- delegated scope lock bookkeeping
|
||||
- registry cleanup hooks for stopped or unreachable children
|
||||
|
||||
Does not own:
|
||||
|
||||
- durable Pod metadata (`pod-store`)
|
||||
- replayable session logs (`session-store`)
|
||||
- socket protocol definitions (`protocol`)
|
||||
- project work item state
|
||||
|
||||
## Design notes
|
||||
|
||||
The registry is a runtime coordination mechanism. It can help decide whether a Pod is live or colliding, but durable visibility/restoration should be backed by Pod metadata when possible.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
30
crates/pod-store/README.md
Normal file
30
crates/pod-store/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# pod-store
|
||||
|
||||
## Role
|
||||
|
||||
`pod-store` owns current Pod metadata keyed by Pod name.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- persisted Pod metadata files
|
||||
- current active/pending session pointers
|
||||
- resolved manifest snapshots for restoration
|
||||
- parent-visible spawned-child metadata
|
||||
- restoration labels and diagnostics derived from metadata
|
||||
|
||||
Does not own:
|
||||
|
||||
- replayable conversation logs (`session-store`)
|
||||
- live process locks or socket reachability (`pod-registry`, `client`)
|
||||
- product CLI behavior (`yoi`)
|
||||
- model turn execution (`llm-worker`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Pod metadata is intentionally thin. It should answer current-state questions without duplicating transcripts or becoming a second session log.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
|
|
@ -1,22 +1,32 @@
|
|||
# pod
|
||||
|
||||
独立したエージェント実行単位「Pod」を実装するクレート。LLM ワーカーセッションをマニフェスト設定・ファイルスコープ制約と組み合わせ、Unix ソケット経由の双方向通信で操作可能にする。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`pod` turns an `llm-worker` Worker into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Pod metadata integration.
|
||||
|
||||
### コア
|
||||
## Boundaries
|
||||
|
||||
- `Pod<C, St>` — LLM ワーカーセッション + マニフェスト + スコープのラッパー(`run()`, `resume()`, `from_manifest()`)
|
||||
- `PodRunResult` — 実行結果(`Finished`, `Paused`)
|
||||
- `PodError` — エラー型
|
||||
Owns:
|
||||
|
||||
### 制御
|
||||
- Pod lifecycle and socket protocol serving
|
||||
- Worker construction around a resolved Manifest
|
||||
- session-store and pod-store coordination
|
||||
- built-in tool registration under scope/policy
|
||||
- spawned-child orchestration hooks
|
||||
|
||||
- `PodController` — Pod ライフサイクルを管理するアクター(`spawn()` でタスク起動)
|
||||
- `PodHandle` — Pod への操作ハンドル(`send()`, `subscribe()`)
|
||||
- `PodSharedState` / `PodStatus` — 共有状態(`Idle`, `Running`, `Paused`)
|
||||
Does not own:
|
||||
|
||||
### ランタイム
|
||||
- provider-specific wire formats (`provider` / `llm-worker` clients)
|
||||
- product CLI parsing (`yoi`)
|
||||
- TUI display authority (`tui`)
|
||||
- current-state storage schema outside Pod metadata (`pod-store`)
|
||||
|
||||
- `RuntimeDir` — `$XDG_RUNTIME_DIR/yoi/{pod_name}/` 配下のランタイムディレクトリ管理(ステータス・履歴のアトミック書き込み)
|
||||
- `SocketServer` — Pod Protocol 用 Unix ソケットサーバー
|
||||
## Design notes
|
||||
|
||||
A Pod is runtime authority, not UI state. It should commit model-visible events through history/session paths and keep current Pod-name state in Pod metadata rather than in transient runtime files.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,31 @@
|
|||
# protocol
|
||||
|
||||
クライアントとPod間の通信プロトコルを定義するクレート。Unix ソケット上で JSON Lines として送受信されるメッセージ型を提供する。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`protocol` defines the JSONL message boundary between Pod clients and Pod servers.
|
||||
|
||||
- `Method` — クライアント→Pod のコマンド(`Run`, `Resume`, `Cancel`)
|
||||
- `Event` — Pod→クライアント のイベント(`TurnStart`, `TextDelta`, `ToolCallStart`, `Usage`, `Error` など)
|
||||
- `TurnResult` — ターン完了状態(`Finished`, `Paused`)
|
||||
- `ErrorCode` — エラー分類(`AlreadyRunning`, `ProviderError`, `ToolError` など)
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- transport-neutral method/event/result types
|
||||
- request/reply and broadcast event shapes
|
||||
- protocol error categories shared by clients and servers
|
||||
|
||||
Does not own:
|
||||
|
||||
- Unix socket implementation details (`client`, `pod`)
|
||||
- TUI rendering (`tui`)
|
||||
- Worker history semantics (`llm-worker`)
|
||||
- durable storage (`session-store`, `pod-store`)
|
||||
|
||||
## Design notes
|
||||
|
||||
The exact enum variants are code authority. The README should describe the boundary, not duplicate every message shape.
|
||||
|
||||
Protocol events can inform UI and orchestration, but durable state changes still need to flow through Pod/session/metadata records.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,33 @@
|
|||
# provider
|
||||
|
||||
マニフェストの `ModelManifest` から適切な `LlmClient`(`HttpTransport<S>`)を構築するファクトリクレート。プロバイダ / モデルカタログの解決、API キーの local secret store / 明示ファイル解決、scheme ↔ auth の整合検証を担う。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`provider` resolves model/provider configuration and constructs provider-specific LLM clients for `llm-worker`.
|
||||
|
||||
- `build_client(manifest: &ModelManifest) -> Result<Box<dyn LlmClient>, ProviderError>` — ref / inline を受け取り、カタログ解決 → `HttpTransport<S>` 構築までを行う
|
||||
- `build_client_from_config(config: &ModelConfig) -> Result<Box<dyn LlmClient>, ProviderError>` — 解決済み `ModelConfig` から構築
|
||||
- `catalog::resolve_model_manifest(&ModelManifest) -> Result<ModelConfig, ResolveError>` — ref / inline を `ModelConfig` へ解決(build せずに参照のみ欲しいケース向け)
|
||||
- `catalog::{load_providers, load_models}` — builtin + user override を解決したカタログ
|
||||
- `ProviderError` / `CatalogError` / `catalog::ResolveError` — エラー種別
|
||||
## Boundaries
|
||||
|
||||
## 責務
|
||||
Owns:
|
||||
|
||||
- プロバイダ / モデルカタログの builtin (`resources/{providers,models}/builtin.toml`) と user override (`$XDG_CONFIG_HOME/yoi/{providers,models}.toml`) の解決
|
||||
- `ModelManifest` の ref 形を `(provider, model_id)` に split し、`ModelConfig` へ展開
|
||||
- `AuthRef::SecretRef` / `AuthRef::ApiKey` を `ResolvedAuth::ApiKey` に解決(通常は local secret store、低レベル manifest では明示ファイルも可)
|
||||
- `AuthRef::None` / `AuthRef::CodexOAuth` の解決
|
||||
- `Scheme::required_auth()` と `ResolvedAuth` の妥当性検証(非対応組合せは構築エラー)
|
||||
- capability は manifest 明示 > model catalog > provider.default_capability > `Scheme::default_capability()` の順で解決
|
||||
- context window は manifest 明示 > model catalog > provider.default_context_window > builtin fallback の順で解決し、inline model でも `context_window` で override できる
|
||||
- builtin and user provider/model catalog resolution
|
||||
- model reference expansion into concrete model config
|
||||
- auth reference resolution through supported mechanisms
|
||||
- provider/scheme capability and context-window metadata
|
||||
- provider-specific client construction
|
||||
|
||||
Does not own:
|
||||
|
||||
- Worker turn lifecycle (`llm-worker`)
|
||||
- secret storage internals (`secrets`)
|
||||
- Pod lifecycle (`pod`)
|
||||
- product CLI parsing (`yoi`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Provider API facts drift. Keep wire-format, auth, catalog, and capability differences here so Worker semantics remain stable.
|
||||
|
||||
Codex OAuth is a separate integration from normal provider secret refs because its local file shape and lifecycle differ.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/provider-model-boundary.md`](../../docs/design/provider-model-boundary.md)
|
||||
- [`../../docs/design/profiles-manifests-prompts.md`](../../docs/design/profiles-manifests-prompts.md)
|
||||
|
|
|
|||
30
crates/secrets/README.md
Normal file
30
crates/secrets/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# secrets
|
||||
|
||||
## Role
|
||||
|
||||
`secrets` provides the local secret reference store used by provider and tool configuration.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- provider-independent secret id to value lookup
|
||||
- modest plaintext-at-rest reduction and integrity checks
|
||||
- secret store file format and validation
|
||||
|
||||
Does not own:
|
||||
|
||||
- provider-specific auth protocol (`provider`)
|
||||
- Codex OAuth local integration shape (`provider`)
|
||||
- prompting or model context
|
||||
- work item or diagnostic redaction policy outside its API surface
|
||||
|
||||
## Design notes
|
||||
|
||||
The store is not a high-assurance keychain. It exists to avoid scattering plaintext credentials through config files and logs, not to provide strong local adversary protection.
|
||||
|
||||
Secret values must stay out of diagnostics, Debug output, CLI/TUI output, work items, docs, session logs, model context, and persisted plaintext files.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/provider-model-boundary.md`](../../docs/design/provider-model-boundary.md)
|
||||
29
crates/session-metrics/README.md
Normal file
29
crates/session-metrics/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# session-metrics
|
||||
|
||||
## Role
|
||||
|
||||
`session-metrics` records usage and memory/session metrics that are useful for diagnostics and maintenance.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- metric record types and persistence helpers
|
||||
- explicit memory usage/read/reference observations where applicable
|
||||
- lightweight diagnostic data that should not become model context by itself
|
||||
|
||||
Does not own:
|
||||
|
||||
- prompt context packing (`llm-worker`)
|
||||
- generated memory contents (`memory`)
|
||||
- provider billing semantics (`provider`)
|
||||
- UI status rendering (`tui`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Metrics are observations. They may guide compaction, memory effectiveness analysis, or UX, but they are not authoritative conversation history and should not smuggle hidden state into model input.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/memory-knowledge.md`](../../docs/design/memory-knowledge.md)
|
||||
- [`../../docs/design/compaction.md`](../../docs/design/compaction.md)
|
||||
|
|
@ -1,29 +1,32 @@
|
|||
# llm-worker-persistence
|
||||
# session-store
|
||||
|
||||
Worker のセッション永続化を提供するクレート。追記専用の JSONL ログとして状態遷移を記録し、ログの再生によってセッションを完全に復元する。大きなツール出力は Blob ストアに分離保存する。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`session-store` owns replayable append-only session logs.
|
||||
|
||||
### セッション
|
||||
## Boundaries
|
||||
|
||||
- `Session<C, St>` — Worker をラップした永続化セッション(`run()`, `resume()`, `fork()`, `fork_at()`)
|
||||
- `SessionId` — UUID v7 によるセッション識別子
|
||||
- `SessionConfig` — 永続化設定(イベントトレース記録の有無)
|
||||
Owns:
|
||||
|
||||
### ストア
|
||||
- session identifiers and segment lineage
|
||||
- JSONL log entries for replayable conversation/runtime history
|
||||
- restoring Worker/session state from committed records
|
||||
- schema surfaces that should make drift compile-visible
|
||||
|
||||
- `Store` トレイト — 永続化バックエンド抽象(`append`, `read_all`, `list_sessions`)
|
||||
- `FsStore` — ファイルシステム上の JSONL ストア実装
|
||||
- `BlobStore` トレイト — Blob ストレージ抽象(`store`, `load`)
|
||||
- `FsBlobStore` — ファイルシステム上の Blob ストア実装
|
||||
- `BlobOutputProcessor` — ToolOutputProcessor 実装(小さい出力はインライン、大きい出力は Blob 保存)
|
||||
Does not own:
|
||||
|
||||
### ログ
|
||||
- current Pod-name metadata (`pod-store`)
|
||||
- live process/socket discovery (`pod-registry`, `client`)
|
||||
- UI state (`tui`)
|
||||
- generated memory summaries (`memory`)
|
||||
|
||||
- `LogEntry` — セッションログのエントリ型(`SegmentStart`, `UserInput`, `AssistantItem`, `ToolResult`, `SystemItem`, `TurnEnd` など)
|
||||
- `RestoredState` — ログ再生で復元された状態
|
||||
- `collect_state()` — ログエントリ列から状態を復元する関数
|
||||
## Design notes
|
||||
|
||||
### ツール
|
||||
A session log records what happened. It is not the current Pod registry and should not be queried as the only source of "what does Pod X mean now?"
|
||||
|
||||
- `InspectTool` — Blob 内容を取得する組み込みツール(行範囲・配列スライス・キー指定セレクタ対応)
|
||||
Prefer explicit current log variants over broad legacy compatibility when schema changes; hidden compatibility can make future replay bugs silent.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
|
|
|
|||
30
crates/tools/README.md
Normal file
30
crates/tools/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# tools
|
||||
|
||||
## Role
|
||||
|
||||
`tools` implements built-in tools and shared tool execution helpers used by Pods.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- built-in filesystem, web, memory, and Pod-management tool implementations where applicable
|
||||
- bounded tool output formatting
|
||||
- scope-aware file operation helpers
|
||||
- tool-facing diagnostics suitable for history/model consumption
|
||||
|
||||
Does not own:
|
||||
|
||||
- manifest permission policy definition (`manifest`)
|
||||
- Worker tool-loop semantics (`llm-worker`)
|
||||
- Pod lifecycle decisions (`pod`)
|
||||
- UI presentation (`tui`)
|
||||
|
||||
## Design notes
|
||||
|
||||
A tool implementation must assume model input is untrusted. Permission policy, scope checks, output bounding, and redaction are part of the safety boundary, not optional UI behavior.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
- [`../../docs/design/memory-knowledge.md`](../../docs/design/memory-knowledge.md)
|
||||
|
|
@ -1,10 +1,30 @@
|
|||
# tui
|
||||
|
||||
Pod と対話するためのターミナル UI クライアント。Unix ソケット経由で Pod に接続し、チャット形式でユーザー入力の送信・アシスタント応答の表示・ツール実行の監視を行う。
|
||||
## Role
|
||||
|
||||
## 公開型
|
||||
`tui` implements terminal UI clients for interacting with one or more Pods.
|
||||
|
||||
- `App` — アプリケーション状態(メッセージ履歴、入力バッファ、スクロール位置)
|
||||
- `Message` / `MessageKind` — 表示メッセージ(User, Assistant, Tool, Error, Status)
|
||||
- `PodClient` — Pod との Unix ソケット通信クライアント(`connect()`, `send()`, `next_event()`)
|
||||
- `draw()` — ratatui によるUI描画関数
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- terminal rendering and input handling
|
||||
- local composer state and UI affordances
|
||||
- single-Pod attach/restore screens
|
||||
- multi-Pod dashboard presentation
|
||||
|
||||
Does not own:
|
||||
|
||||
- durable transcript authority (`session-store`)
|
||||
- Pod current state (`pod-store`)
|
||||
- Pod lifecycle policy (`pod`)
|
||||
- product CLI ownership (`yoi`)
|
||||
|
||||
## Design notes
|
||||
|
||||
The TUI should display committed events and Pod snapshots rather than inventing durable state. Local input history and optimistic UI affordances are editing conveniences; they must not become hidden model context.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
|
|
|
|||
29
crates/workflow/README.md
Normal file
29
crates/workflow/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# workflow
|
||||
|
||||
## Role
|
||||
|
||||
`workflow` owns project-authored workflow record parsing, validation, and invocation metadata.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- workflow file schema/linting
|
||||
- workflow discovery from configured workflow locations
|
||||
- typed workflow metadata used by runtime/tooling layers
|
||||
|
||||
Does not own:
|
||||
|
||||
- generated memory records (`memory`)
|
||||
- work item file lifecycle (`tickets.sh`, `work-items/`)
|
||||
- Pod orchestration decisions (`pod`, workflows executed by agents)
|
||||
- product CLI command shape (`yoi`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Workflows are curated project assets. They should not be mixed with generated memory, and invoking a workflow should not bypass work item authority or scope policy.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/development/workflows.md`](../../docs/development/workflows.md)
|
||||
- [`../../docs/design/profiles-manifests-prompts.md`](../../docs/design/profiles-manifests-prompts.md)
|
||||
30
crates/yoi/README.md
Normal file
30
crates/yoi/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# yoi
|
||||
|
||||
## Role
|
||||
|
||||
`yoi` is the product CLI facade. It owns the installed binary name, top-level argument parsing, normal TUI launch, and product subcommands such as `yoi pod` and `yoi memory lint`.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- product command shape and user-facing CLI entry points
|
||||
- profile/default selection for ordinary startup
|
||||
- wiring library crates into the executable
|
||||
- headless maintenance commands that are part of the product surface
|
||||
|
||||
Does not own:
|
||||
|
||||
- Pod runtime internals (`pod`)
|
||||
- socket client mechanics (`client`)
|
||||
- model turn orchestration (`llm-worker`)
|
||||
- TUI rendering/runtime implementation (`tui`)
|
||||
|
||||
## Design notes
|
||||
|
||||
Keeping product CLI ownership here prevents lower crates from depending on the binary name or parsing top-level arguments. Runtime launch should use typed command boundaries, not shell-command strings.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/overview.md`](../../docs/design/overview.md)
|
||||
- [`../../docs/design/profiles-manifests-prompts.md`](../../docs/design/profiles-manifests-prompts.md)
|
||||
41
docs/README.md
Normal file
41
docs/README.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Yoi documentation
|
||||
|
||||
This directory contains maintained developer documentation for Yoi. Its job is to preserve design intent that is hard to recover from code alone.
|
||||
|
||||
It is not a dumping ground for external research, old plans, API inventories, or ticket history. Those belong in local notes, work item artifacts, code, or git history.
|
||||
|
||||
## Reading order
|
||||
|
||||
1. [`design/overview.md`](design/overview.md) — the system map.
|
||||
2. [`design/context-history.md`](design/context-history.md) — the highest-risk invariant: inputs that affect the model must be committed to history before they enter context.
|
||||
3. [`design/pod-session-state.md`](design/pod-session-state.md) — Pod identity, replayable session logs, current metadata, and live process hints.
|
||||
4. [`design/profiles-manifests-prompts.md`](design/profiles-manifests-prompts.md) — reusable Profiles, resolved Manifests, and prompt resources.
|
||||
5. [`design/tool-permissions-scope.md`](design/tool-permissions-scope.md) — tool policy and filesystem scope.
|
||||
6. [`design/memory-knowledge.md`](design/memory-knowledge.md) — generated memory, Knowledge, and audit records.
|
||||
7. [`development/work-items.md`](development/work-items.md) — how project work is recorded and reviewed.
|
||||
8. [`development/validation.md`](development/validation.md) — how to check changes.
|
||||
|
||||
## What belongs here
|
||||
|
||||
Keep documentation when it records a stable design boundary, a non-obvious rationale, or a workflow that future changes must respect.
|
||||
|
||||
Examples that belong:
|
||||
|
||||
- Why Pod metadata is not the session log.
|
||||
- Why `Profile` and resolved `Manifest` are different layers.
|
||||
- Why context-only event injection is forbidden.
|
||||
- Why child Pod notifications are hints rather than completion proof.
|
||||
|
||||
## What does not belong here
|
||||
|
||||
Do not keep material only because it once helped a ticket.
|
||||
|
||||
Examples that should be removed or moved to `docs/.local/` / work item artifacts:
|
||||
|
||||
- External project comparisons.
|
||||
- Provider API snapshots, prices, or model tables.
|
||||
- Old implementation plans that are no longer the design authority.
|
||||
- Public type or method inventories that drift with code.
|
||||
- Debug notes that are useful only for one investigation.
|
||||
|
||||
`docs/.local/` is intentionally outside the maintained documentation surface.
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
# Yoi アーキテクチャ
|
||||
|
||||
## プロジェクトの目的
|
||||
|
||||
複数の LLM エージェント(Pod)が独立プロセスとして並行動作し、自律的に分業・統括できるエンジン。シングルエージェントの LLM リグとの差別化は、タスクを別 Pod に委譲し結果を集約する**オーケストレーション**を LLM 自身に行わせる点にある。
|
||||
|
||||
## 設計原則
|
||||
|
||||
### 層の分離と方向性
|
||||
|
||||
各クレートは下位層に依存し、上位層を知らない。上位層は下位層の API を丸ごと隠蔽せず、制約が必要な部分だけをラップして提供し、それ以外は下位層を直接使わせる。
|
||||
|
||||
### 宣言した層が解決する
|
||||
|
||||
ある層が構成を宣言として受け取ったなら、その解決もその層の責務。マニフェストに `[model] ref = "anthropic/claude-sonnet-4-6"`(あるいは `scheme = "anthropic"` + `model_id = ...` の inline 形式)と書いた以上、`ModelManifest` → `LlmClient` の変換は yoi 側(`crates/provider`)が行う。逆に llm-worker が `LlmClient` trait だけを受け取るのは正しい — llm-worker はプロバイダの選択を宣言として受け取っていないから。
|
||||
|
||||
### 概念の追加は不在が問題になってから
|
||||
|
||||
概念を先に作らない。「これがないと今の問題が解けない」と言えるまで追加しない。拡張ポイントはドキュメントに記録するが、実装はしない。
|
||||
|
||||
### 最小の構造化で最大の自由度
|
||||
|
||||
yoi は環境再現・コンテナ管理・VCS 統合などを自身の責務としない。Pod が動くホストの fs 上で活動する主体を提供し、それにコンテキストを与えてスポーンさせられる仕組みを付与する。構築する環境はユーザー次第。
|
||||
|
||||
## Pod
|
||||
|
||||
独立したエージェントの実行単位。llm-worker の Worker をラップし、マニフェストによる宣言的構成、ディレクトリスコープ、Pod 名に紐づく永続状態を加える。
|
||||
|
||||
- 1 live Pod process は 1 Pod name/current state を所有する
|
||||
- 会話の永続化は `session-store` の `session_id` / `segment_id` ログ、Pod 名の current state は `pod-store` metadata が担う
|
||||
- マニフェストまたは profile から完結構築できる
|
||||
- scope で読み取り・書き込み可能なパスを制限する
|
||||
- 独立した socket サーバーを持ち、Client (TUI / GUI) が接続して操作する
|
||||
|
||||
### 実行ループ
|
||||
|
||||
```
|
||||
Client → Method::Run { input }
|
||||
→ Worker: user message 追加 → LLM リクエスト
|
||||
→ LLM 応答: text → Client に Event::TextDelta で配信
|
||||
→ LLM 応答: tool_use → Interceptor (pre_tool_call) → Tool 実行 → Interceptor (post_tool_call) → Hook
|
||||
→ tool result を履歴に追加 → 次の LLM リクエスト
|
||||
→ ターン終了: Hook (on_turn_end) → Client に Event::RunEnd
|
||||
```
|
||||
|
||||
### Interceptor と Hook の分離
|
||||
|
||||
- **Interceptor**: Worker の内部制御フロー。context / history の直接操作が可能。Pod の内部機構(compaction トリガー、notification 注入)が使う。外部に公開しない
|
||||
- **Hook**: Pod の公開 API。read-only のサマリ情報のみを受け取り、制御フロー判断(continue / skip / abort / pause)を返す。将来スクリプト言語に安全に公開可能
|
||||
- 実行順序: Interceptor → Hook。Interceptor が context を整えた結果を Hook が観測する
|
||||
|
||||
### コンテキスト管理
|
||||
|
||||
- **Prune**: 古い tool result の content を除去(summary は残す)。LLM request context だけを加工し、永続 history 本体は変更しない
|
||||
- **Compact**: 履歴 prefix を要約し、同じ `session_id` 配下の新 `segment_id` へ rotate する。`SegmentStart.compacted_from` が元 segment を参照し、Pod metadata の active pointer は新 segment を指す
|
||||
- サーキットブレーカー: compact が3回連続失敗したら無効化
|
||||
|
||||
## Protocol
|
||||
|
||||
Pod の制御・監視に使う JSONL ベースのメッセージプロトコル。トランスポートに依存しない。正確な wire enum は `crates/protocol/src/lib.rs::{Method, Event}` を正とし、この節はカテゴリの目次として扱う。
|
||||
|
||||
- **Client → Pod (`Method`)**
|
||||
- turn 制御: `Run`, `Resume`, `Cancel`, `Pause`, `Shutdown`
|
||||
- context/session 制御: `Compact`, `ListRewindTargets`, `RewindTo`
|
||||
- typed injection / child lifecycle: `Notify`, `PodEvent`
|
||||
- client 補助: `ListCompletions`
|
||||
- Pod visibility / restore: `ListPods`, `RestorePod`
|
||||
- **Pod → Client (`Event`)**
|
||||
- accepted input / history seed: `Snapshot`, `UserMessage`, `SystemItem`, `SegmentRotated`
|
||||
- generation stream: `TurnStart`, `TurnEnd`, `LlmCallStart`, `LlmCallEnd`, retry/continuation events, `Text*`, `Thinking*`, `ToolCall*`, `ToolResult`, `Usage`, `RunEnd`
|
||||
- control replies: completions, rewind, visible Pod list / restore results
|
||||
- operational status: `Status`, `Alert`, `MemoryWorker`, `Compact*`, `Error`, `Shutdown`
|
||||
- リクエストとレスポンスの紐付けを一般化した RPC にはしない。多くの状態は broadcast event と Pod status で観測する
|
||||
- 一部の reply(例: completions)は要求 socket にだけ返る。broadcast event と request-local reply の違いは enum variant のコメントを正とする
|
||||
- 操作の競合は先勝ち(run 中に別の run → `AlreadyRunning` エラー)
|
||||
|
||||
## マニフェストとファクトリ
|
||||
|
||||
### PodManifest
|
||||
|
||||
Pod の宣言的構成。TOML で記述。
|
||||
|
||||
```toml
|
||||
[pod]
|
||||
name = "agent"
|
||||
|
||||
[model]
|
||||
ref = "anthropic/claude-sonnet-4-6"
|
||||
|
||||
[worker]
|
||||
instruction = "$yoi/default"
|
||||
max_tokens = 4096
|
||||
temperature = 0.3
|
||||
|
||||
[[scope.allow]]
|
||||
target = "/abs/path"
|
||||
permission = "write"
|
||||
```
|
||||
|
||||
`[model]` は `ref = "<provider>/<model_id>"` でプロバイダ / モデルカタログを引く短縮形と、`scheme` / `model_id` / `auth` を直書きする inline 形式の両方を受ける。カタログは `resources/{providers,models}/builtin.toml` を builtin、`<config_dir>/{providers,models}.toml` を user override として解決する(`<config_dir>` の解決ルールは `manifest::paths` 参照)。詳細は `docs/pod-factory.md` と `crates/provider/README.md`。
|
||||
|
||||
### Manifest / profile 入力
|
||||
|
||||
通常の Pod 起動は Lua profile discovery/default から `PodManifest` を生成する。bundled `builtin:default` が fallback default で、user/project `profiles.toml` は profile registry と default selection だけを担う。user/project `manifest.toml` の ambient cascade は通常起動では使わない。
|
||||
|
||||
`yoi pod --manifest <PATH>` は explicit one-file compatibility/debug input で、指定 TOML 1 枚だけに builtin defaults を merge し、`PodManifestConfig -> PodManifest` の required validation を通す。
|
||||
|
||||
`PodFactory` の user/project/overlay API は低レベル構成部品として残るが、CLI の通常起動 path では generic TOML overlay を公開しない。
|
||||
|
||||
### Instruction とプロンプト資産
|
||||
|
||||
`worker.instruction` はファイル参照。3 層の prefix addressing でプロンプト資産を解決:
|
||||
|
||||
- `$yoi/...` — バイナリ同梱(`resources/prompts/`、`include_dir!` で埋め込み)
|
||||
- `$user/...` — `<config_dir>/prompts/`(`manifest::paths` で解決)
|
||||
- `$workspace/...` — `<project>/.yoi/prompts/`
|
||||
|
||||
テンプレートは minijinja で評価。`{% include "$yoi/common/tool-usage" %}` のようにプロンプト間で参照可能(prefix なしの include は現在のファイルからの相対解決)。
|
||||
|
||||
レンダリング結果の末尾に scope summary と AGENTS.md(あれば)がコード側で固定付加される。ユーザーテンプレートからはこれらに触れない。
|
||||
|
||||
## Scope
|
||||
|
||||
Pod が操作できるファイルパスの制御。
|
||||
|
||||
- `allow` ルールで読み取り・書き込みを許可、`deny` ルールで制限
|
||||
- effective permission = allow - deny
|
||||
- `recursive = false` で直下のみに制限可能(summary に `[non-recursive]` マーカー)
|
||||
- scope 排他: Pod 間の write 衝突は runtime registry / scope lock で検出する。child Pod へ委譲した write scope は親の effective scope から delegated-out deny として差し引かれ、child 停止・prune 時に reclaim される
|
||||
|
||||
## セッション永続化
|
||||
|
||||
- `session-store` は append-only JSONL segment log。1 `LogEntry` = 1 行
|
||||
- `SessionId` は論理会話の fork-tree root、`SegmentId` はその中の現在の書き込み先 segment
|
||||
- fresh conversation だけが新 `SessionId` を作る。compact / fork は同じ `SessionId` 配下に新 `SegmentId` を作り、`SegmentStart.{compacted_from,forked_from}: SegmentOrigin` で出自を持つ
|
||||
- segment log の先頭は `LogEntry::SegmentStart`。以降に `Invoke`, `UserInput`, `AssistantItem`, `ToolResult`, `SystemItem`, `TurnEnd`, `RunCompleted` / `RunErrored`, `ConfigChanged`, `LlmUsage`, `Extension` などを append する
|
||||
- replay は segment log から Worker state を再構成する。lineage は entry hash ではなく `SegmentOrigin.at_turn_index` と segment id で参照する
|
||||
- Pod 名の durable current state(active pointer、resolved manifest snapshot、spawned child delegation/reclaim)は `pod-store` metadata が担う。socket path や runtime mirror は liveness authority ではない
|
||||
|
||||
## 組み込みツール
|
||||
|
||||
正確な callable set と description は ToolRegistry / manifest permission / scope / profile に依存する。高レベルには以下のカテゴリを持つ。
|
||||
|
||||
| カテゴリ | 例 | 概要 |
|
||||
|---|---|---|
|
||||
| File / shell | `Read`, `Write`, `Edit`, `Glob`, `Grep`, `Bash` | workspace ファイル操作と shell 実行。file tools は `ScopedFs` と read-before-edit tracker を通る。`Bash` は permission policy と出力退避で制御する |
|
||||
| Task | `TaskCreate`, `TaskUpdate`, `TaskList`, `TaskGet` | セッション内の短期 task 状態管理 |
|
||||
| Memory / Knowledge | `MemoryQuery`, `MemoryRead`, `MemoryWrite`, `MemoryEdit`, `MemoryDelete`, `KnowledgeQuery` | manifest の memory 設定が有効な時に登録される durable memory / knowledge 操作 |
|
||||
| Pod orchestration | `SpawnPod`, `SendToPod`, `ReadPodOutput`, `StopPod`, `ListPods`, `RestorePod` | child / visible Pod の起動・通信・停止・一覧・復元 |
|
||||
| Web | `WebSearch`, `WebFetch` | manifest で明示設定された provider と local secret-store reference 経由の bounded web access |
|
||||
|
||||
すべての tool call は manifest tool permission と scope/policy のチェックを通る。ファイル write scope、Pod delegation、memory layout、web provider 設定はそれぞれ別の authority を持ち、UI 表示だけで権限を広げない。
|
||||
166
docs/branding.md
166
docs/branding.md
|
|
@ -1,166 +0,0 @@
|
|||
# Branding notes
|
||||
|
||||
## Rename rationale
|
||||
|
||||
The project must move away from the name `insomnia`. Kong already operates a well-known developer product named Insomnia, with overlapping surfaces such as an API/developer tool product, an `insomnia` package/binary, documentation, plugins, CLI tooling, and `.insomnia` project data. Even without making a legal judgment here, keeping `insomnia` would create unnecessary user confusion and distribution risk.
|
||||
|
||||
The rename should not be a shallow package-name change. The public product name, CLI command, data/config directory names, environment variable prefix, documentation language, and repository-facing terminology should converge on the new name.
|
||||
|
||||
## Product identity
|
||||
|
||||
This project is not just an AI chat client. It is a local agent runtime for delegating long-running development work while preserving state, context, authority boundaries, and resumability.
|
||||
|
||||
The product promise is:
|
||||
|
||||
> You can leave the keyboard, and the system remains present with your work.
|
||||
|
||||
That includes:
|
||||
|
||||
- running and restoring Pods;
|
||||
- preserving session history, memory, workflows, and WorkItems;
|
||||
- coordinating delegated work through scoped permissions;
|
||||
- keeping human review and approval points explicit;
|
||||
- making long-running or interrupted work observable and resumable.
|
||||
|
||||
The name should therefore avoid generic AI-agent vocabulary such as `agent`, `forge`, `sentinel`, or `autopilot`. It should express presence, custody, watchfulness, and delegated work without implying reckless full autonomy.
|
||||
|
||||
## Proposed name: Yoi
|
||||
|
||||
The leading candidate is **Yoi**, from the archaic Japanese word **夜居**.
|
||||
|
||||
`夜居` means staying up or being present at night, including the sense of night duty or remaining at the workplace until late. This preserves the original emotional source of `Insomnia`—AI-induced sleeplessness and the desire for something to keep watch on the user's behalf—while moving the framing from a personal condition to a product role.
|
||||
|
||||
`Insomnia` described what happened to the user.
|
||||
|
||||
`Yoi` describes what the system does instead:
|
||||
|
||||
> It stays with the work after the user leaves.
|
||||
|
||||
## Why Yoi fits
|
||||
|
||||
### Short and tool-shaped
|
||||
|
||||
`yoi` is short, easy to type, and works naturally as a command name:
|
||||
|
||||
```text
|
||||
yoi
|
||||
yoi pod
|
||||
yoi memory
|
||||
yoi workflow
|
||||
```
|
||||
|
||||
It also maps cleanly to project/runtime surfaces:
|
||||
|
||||
```text
|
||||
.yoi/
|
||||
~/.yoi/
|
||||
YOI_*
|
||||
```
|
||||
|
||||
### Japanese but not overly literal
|
||||
|
||||
The name has the same kind of compact Japanese-derived shape as names like Hono or Shiki. It does not sound like a generic enterprise AI product, and it leaves room for the project to develop its own vocabulary.
|
||||
|
||||
### Presence rather than automation
|
||||
|
||||
The important word in `夜居` is not only night, but **居**: to be there.
|
||||
|
||||
That matches the system's architecture. Pods, sessions, memory, workflow state, WorkItems, and runtime metadata remain present so the user can return to them. The product is less about a single model response and more about maintaining a durable place where delegated work can continue.
|
||||
|
||||
### Keeps the original story without keeping the problem
|
||||
|
||||
The old name centered sleeplessness. The new name centers the replacement for sleeplessness: a system that remains at the worksite on the user's behalf.
|
||||
|
||||
A useful internal phrasing is:
|
||||
|
||||
> Insomnia was the problem. Yoi is the night presence that makes it unnecessary.
|
||||
|
||||
## Handling ambiguity
|
||||
|
||||
`Yoi` is intentionally short and has Japanese homophones or near associations such as `良い`, `宵`, and `酔い`. This ambiguity is acceptable if the official spelling and explanation consistently anchor the name as **Yoi (夜居)**.
|
||||
|
||||
The ambiguity should be handled as follows:
|
||||
|
||||
- Use `Yoi` for the product name in English-facing text.
|
||||
- Introduce it as `Yoi (夜居)` in README and high-level docs.
|
||||
- Use copy centered on presence, delegated work, and returning later.
|
||||
- Do not lean into alcohol-related `酔い` wordplay.
|
||||
- Do not over-explain the name in every document; establish the origin once and then use `Yoi` normally.
|
||||
|
||||
## Candidate tagline directions
|
||||
|
||||
Possible English taglines:
|
||||
|
||||
- `Yoi is a local AI agent runtime that stays with your work after you leave.`
|
||||
- `Yoi keeps a presence at your desk while your agents work.`
|
||||
- `Yoi lets you delegate long-running development work without staying at the keyboard.`
|
||||
|
||||
Possible Japanese descriptions:
|
||||
|
||||
- `Yoi(夜居)は、あなたが離れたあとも仕事場に居続けるローカル AI エージェント実行環境です。`
|
||||
- `Yoi は、Pod、履歴、メモリ、ワークフローを保ち、長く続く開発作業の委譲を支えます。`
|
||||
|
||||
## Rename surfaces to update
|
||||
|
||||
If `Yoi` is adopted, the rename should cover at least:
|
||||
|
||||
- product name: `Insomnia` -> `Yoi`;
|
||||
- CLI command: `insomnia` -> `yoi`;
|
||||
- workspace directory: `.insomnia/` -> `.yoi/`;
|
||||
- user data directory: `~/.insomnia/` -> `~/.yoi/`;
|
||||
- environment variables: `INSOMNIA_*` -> `YOI_*`;
|
||||
- Nix package/app names;
|
||||
- Cargo package names where public-facing;
|
||||
- prompt and resource text;
|
||||
- docs, reports, AGENTS instructions, tickets, and release material;
|
||||
- socket/runtime path labels and diagnostics where user-visible.
|
||||
|
||||
The public identity should move cleanly to Yoi; do not preserve `insomnia` through compatibility aliases.
|
||||
|
||||
## Adoption checks
|
||||
|
||||
The required check for this project is Nixpkgs package-name availability, because the project is expected to be distributed as a Nix package.
|
||||
|
||||
Checked on 2026-06-01:
|
||||
|
||||
- Nixpkgs: `nix search nixpkgs '^yoi$' --json` returned no package.
|
||||
- Nixpkgs: `nix eval nixpkgs#yoi.meta.name --raw` reported that `nixpkgs` does not provide `yoi`.
|
||||
- Nixpkgs: a direct `<nixpkgs>` attribute check returned `false` for `builtins.hasAttr "yoi" pkgs`.
|
||||
- Nixpkgs: nearby existing attributes include `yo` and `yoink`, but not `yoi`.
|
||||
|
||||
This satisfies the current required availability check for adopting `Yoi`.
|
||||
|
||||
A broader package-manager pass was also performed on 2026-06-01. Results:
|
||||
|
||||
- crates.io: `https://crates.io/api/v1/crates/yoi` returned 404; no exact crate found by `cargo search`.
|
||||
- npm: exact package `yoi` exists. `npm view yoi` reports `name: yoi`, `version: 1.9.15`, description `Easy (but powerful) NodeJS Server`, created in 2013 and modified in 2022. No `bin` field was reported. This blocks using the exact npm package name without owner coordination, but does not appear to be a CLI-name conflict.
|
||||
- PyPI: `https://pypi.org/pypi/yoi/json` returned 404.
|
||||
- Homebrew formula: `https://formulae.brew.sh/api/formula/yoi.json` returned 404.
|
||||
- Homebrew cask: `https://formulae.brew.sh/api/cask/yoi.json` returned 404.
|
||||
- Arch official packages: exact name search returned no package.
|
||||
- AUR: `https://aur.archlinux.org/rpc/v5/info/yoi` returned no result.
|
||||
- Alpine packages: package-name search returned no exact `yoi` package.
|
||||
- Debian packages: package-name search returned no exact `yoi` package.
|
||||
- Fedora packages: `https://src.fedoraproject.org/rpms/yoi` returned 404.
|
||||
- Gentoo packages: exact package pages checked under likely categories returned 404.
|
||||
- MacPorts: package search returned no exact `yoi` port.
|
||||
- Snap: Snapcraft API reported `No snap named 'yoi' found in series '16'.`
|
||||
- Flathub: direct appstream lookup did not find an app named `yoi`.
|
||||
- Scoop Main bucket: `bucket/yoi.json` returned 404.
|
||||
- winget-pkgs: `manifests/y/yoi` returned 404.
|
||||
- Chocolatey community package page returned 404.
|
||||
- Docker Hub: `library/yoi` and `yoi/yoi` returned 404.
|
||||
- RubyGems: `https://rubygems.org/api/v1/gems/yoi.json` returned 404.
|
||||
- NuGet: `https://api.nuget.org/v3-flatcontainer/yoi/index.json` returned 404.
|
||||
- Maven Central: artifact/group search did not show an exact `yoi` package identity.
|
||||
- Packagist: `https://packagist.org/packages/yoi/yoi.json` returned 404.
|
||||
|
||||
The main package-manager concern found is the stale npm package. Since Nixpkgs is the required distribution target and npm distribution is not currently required, this does not block adopting `Yoi`, but it should be recorded as a known name collision if npm distribution is ever planned.
|
||||
|
||||
Optional broader checks before a public release may still include:
|
||||
|
||||
- GitHub repository/name availability;
|
||||
- domain availability such as `yoi.dev`, `yoi.rs`, `yoi.sh`, or `getyoi.dev`;
|
||||
- trademark search results in relevant jurisdictions.
|
||||
|
||||
The main risk of `Yoi` is not semantic ambiguity but short-name collision. The required Nixpkgs check is clear, and the broader package-manager pass is acceptable apart from the npm package-name collision.
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
# コンテキスト圧縮 (Compaction)
|
||||
|
||||
長時間実行エージェントのコンテキストウィンドウ管理。
|
||||
Prune(段階的な出力除去)と Compact(履歴の要約置換)の2層で対処する。
|
||||
|
||||
---
|
||||
|
||||
## 全体像
|
||||
|
||||
```
|
||||
[各リクエストの合間]
|
||||
LLM リクエスト
|
||||
↓
|
||||
PruneHook (pre_llm_request)
|
||||
→ 古い ToolResult の content を除去(summary は残す)
|
||||
→ リクエストコンテキストのみ操作、history 本体は不変
|
||||
↓
|
||||
CompactInterceptor (pre_llm_request) ← safety net
|
||||
→ input_tokens > request_threshold なら Yield
|
||||
→ Worker が WorkerResult::Yielded で正常終了
|
||||
↓
|
||||
Pod::handle_worker_result
|
||||
→ turn 結果を現在 segment log に記録
|
||||
→ compact() で同じ session_id 内の新 segment へ rotate → resume()
|
||||
|
||||
[ターンの合間 — 次の Pod::run 冒頭]
|
||||
Pod::try_pre_run_compact ← proactive
|
||||
→ input_tokens > post_run_threshold なら compact() (best-effort)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prune
|
||||
|
||||
古い ToolResult の `content` を `None` に置換し、`summary` だけ残す。
|
||||
|
||||
### 設計判断
|
||||
|
||||
- **条件付き実行**: 推定トークン節約量が `min_savings` を超えた場合のみ。KV キャッシュの無駄な無効化を避ける
|
||||
- **リクエストコンテキストのみ操作**: history 本体は変更しない。Prune 状態を Pod が保持し、LLM リクエスト構築時に反映する
|
||||
- **保護境界**: 直近 `prune_protected_tokens` 相当の suffix は残す。turn 数ではなく usage history 由来の token estimate で境界を引くため、単発の長い tool loop でも古い `ToolResult.content` が候補になる
|
||||
- **冪等**: `content: None` のアイテムはスキップ
|
||||
|
||||
### ToolOutput の構造
|
||||
|
||||
```rust
|
||||
pub struct ToolOutput {
|
||||
pub summary: String, // 常に残る。「何をしたか」の最低限の情報
|
||||
pub content: Option<String>, // Prune 対象。None に置換するだけ
|
||||
}
|
||||
```
|
||||
|
||||
`Tool::execute()` は `Result<ToolOutput, ToolError>` を返す。
|
||||
`From<String>` で自動変換可能(小さい出力は summary のみ、大きい出力は summary + content)。
|
||||
巨大な出力はフレームワークの責務外。ツール側がファイルに退避し、content に見取り図を置く。
|
||||
|
||||
---
|
||||
|
||||
## Compact
|
||||
|
||||
### 用語
|
||||
|
||||
- **run = turn**: 同じ概念。1 ユーザープロンプト → 完了までの単位
|
||||
- **リクエスト**: 1 turn 内で投げる個別の LLM 呼び出し。ツール使用で 1 turn に複数リクエストが発生する
|
||||
- **リクエストの合間**: 1 turn 内、次の LLM リクエストを投げる前の地点
|
||||
- **ターンの合間**: turn が完了して次の turn を待つ状態
|
||||
|
||||
### トリガー(2段階の閾値)
|
||||
|
||||
1. **ターンの合間 (次 turn 冒頭)**: `try_pre_run_compact()` で `input_tokens > post_run_threshold` → best-effort
|
||||
2. **リクエストの合間 (CompactInterceptor)**: `pre_llm_request` で `input_tokens > request_threshold` → `PreRequestAction::Yield`
|
||||
|
||||
**ターンの合間が proactive (小さい閾値)**:
|
||||
turn が完了した地点はタスクの自然な区切り。ここで先を見越して早めに compact する。
|
||||
マニフェストの `threshold` が対応。
|
||||
|
||||
**リクエストの合間は safety net (大きい閾値)**:
|
||||
turn 内部でリクエストの合間にチェックするのは「暴走的に膨張した場合のみ止める」用途。
|
||||
マニフェストの `request_threshold` が対応。通常は発動しない。
|
||||
|
||||
**両閾値は manifest で個別指定する**。過去の設計では 9/8 倍で自動導出していたが、
|
||||
比率に根拠がなかったため廃止。両方が `Option<u64>` で、片方だけの設定も可能
|
||||
(そちら側のチェックだけが有効になる)。
|
||||
|
||||
### Yield の仕組み
|
||||
|
||||
`PreRequestAction::Yield` は Worker のターンループを正常終了させる汎用プリミティブ。
|
||||
Worker は Compact を知らない。「Interceptor が yield と言ったので抜ける」だけ。
|
||||
Pod が Yielded を検知して compact → resume する。
|
||||
|
||||
```
|
||||
PreRequestAction::Yield → WorkerResult::Yielded → Pod が compact → resume
|
||||
PreRequestAction::Cancel → WorkerError::Aborted → エラー
|
||||
```
|
||||
|
||||
### 安全機構
|
||||
|
||||
- **サーキットブレーカー**: 3回連続 compact 失敗で無効化 (`CompactState::disabled`)
|
||||
- **Thrash 検出**: compact 直後に再び閾値超過 → `PodError::CompactThrash`
|
||||
- **Yield 前後の永続化**: Worker が `Yielded` で抜けた turn は現在 segment log に記録してから compact する。compact が失敗しても元 segment の状態は残る
|
||||
|
||||
### セッション管理
|
||||
|
||||
compact は「新 SessionId を作る」操作ではなく、同じ `session_id` 配下で active `segment_id` を切り替える操作。
|
||||
|
||||
```
|
||||
session_id = abc-123
|
||||
|
||||
segment old:
|
||||
SegmentStart { origin: fresh/fork/... }
|
||||
[...entries...]
|
||||
RunCompleted { result: Yielded }
|
||||
|
||||
segment new:
|
||||
SegmentStart { compacted_from: SegmentOrigin { segment_id: old, at_turn_index: N } }
|
||||
[system prompt]
|
||||
[system: 構造化要約]
|
||||
[system: retained tail / auto-read / references]
|
||||
...以後の turn を append
|
||||
```
|
||||
|
||||
`SegmentStart.compacted_from` / `forked_from` が lineage を追跡する。Pod 名からの resume は `pod-store` metadata の active pointer が現在の `(session_id, segment_id)` を指し、conversation/history の replay は `session-store` の segment log から行う。
|
||||
|
||||
---
|
||||
|
||||
## compact 後の history 構造
|
||||
|
||||
全て system message(`Item::Message { role: System }`)として注入。
|
||||
|
||||
```
|
||||
[system prompt] ← 不変
|
||||
[system: 構造化要約] ← compact worker の出力
|
||||
[system: auto-read ファイル群] ← read_required の結果
|
||||
[system: リファレンス一覧] ← reference の結果
|
||||
[直近 N トークン分の生の会話] ← pruned 状態で保持
|
||||
```
|
||||
|
||||
### 直近の保護: トークンベース
|
||||
|
||||
ターン単位ではなくトークン数ベースで直近の会話を保護する。
|
||||
|
||||
ターン単位の問題: 自走エージェントは1ターン内で多数のリクエストを回す。
|
||||
1ターンが膨大に長くなり、保護量がターン長に依存してしまう。
|
||||
トークン数ベースなら、ターンの長さに関わらず一定量の直近コンテキストが保持される。
|
||||
|
||||
```toml
|
||||
[compaction]
|
||||
threshold = 80000 # ターンの合間 (proactive)
|
||||
request_threshold = 90000 # リクエストの合間 (safety net)
|
||||
prune_protected_tokens = 8000 # prune から保護する末尾 token budget
|
||||
retained_tokens = 8000 # compact 後に生のまま残す末尾 token budget
|
||||
|
||||
overview_target_tokens = 8000 # compact worker 初期 overview の通常目標
|
||||
overview_warning_tokens = 16000 # 超えたら警告・trace、compact は続行
|
||||
overview_deadline_tokens = 40000 # 超えたら粗い overview へ fallback
|
||||
|
||||
worker_context_max_tokens = 50000 # compact worker session 全体の hard limit
|
||||
finish_warning_remaining_tokens = 8000 # 残りが少ないため write_summary へ進める勧告
|
||||
final_reserve_tokens = 4000 # 最終 summary/closing turn 用 reserve
|
||||
worker_max_turns = 20 # compact worker 自身の tool loop 上限
|
||||
|
||||
summary_target_tokens = 1500 # write_summary の目標サイズ
|
||||
summary_max_tokens = 3000 # write_summary の hard validation
|
||||
auto_read_budget_tokens = 8000 # compact 後に注入する file content 合計上限
|
||||
result_context_max_tokens = 24000 # 新 session 初期 context の dry-run validation
|
||||
```
|
||||
|
||||
`compact_*` prefix の旧 key は互換 alias として読み取るが、`[compaction]` 内の新規 key は prefix なしを正とする。
|
||||
初期 overview の target/warning は効率のための目安で、通常は hard error にしない。deadline 超過時も、可能なら deterministic に粗い overview へ fallback して compact の完走を優先する。
|
||||
|
||||
### Auto-Read とリファレンス
|
||||
|
||||
2段階のファイル参照:
|
||||
|
||||
- **Read** (`mark_read_required`): 全文/範囲指定でコンテキストに注入
|
||||
- **Reference** (`add_reference`): 「読んだことがある」とだけ伝える。必要なら再読要求
|
||||
|
||||
auto-read の system message:
|
||||
```
|
||||
[Auto-read file: src/main.rs:42-142]
|
||||
fn main() {
|
||||
let config = Config::load();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
リファレンスの system message:
|
||||
```
|
||||
[Referenced files — read before compaction, contents not included]
|
||||
- src/config.rs (read during task setup)
|
||||
- tests/integration_test.rs (read during test implementation)
|
||||
Use read_file to access current contents if needed.
|
||||
```
|
||||
|
||||
auto-read も通常の history 内 system message なので、将来の Prune/Compact 対象になる。
|
||||
|
||||
---
|
||||
|
||||
## compact worker
|
||||
|
||||
要約生成とファイル選定を行う使い捨て Worker。Pod は compact 対象 prefix を全文投入せず、User / Assistant / System を優先した bounded overview と tool index を初期 input として渡す。Tool call arguments、tool result full content、reasoning body は初期 input には載せない。
|
||||
|
||||
初期 overview は `overview_target_tokens` を目標にする。`overview_warning_tokens` を超えた場合は警告・trace を記録して続行し、`overview_deadline_tokens` を超えた場合は粗い deterministic overview へ fallback する。Compact の目的は完走なので、初期 input が少し大きいだけでは hard error にしない。
|
||||
|
||||
### ツール
|
||||
|
||||
```
|
||||
read_file(path, offset?, limit?) — ファイルを読んで判断する
|
||||
mark_read_required(path, offset?, limit?) — auto-read 対象として指定
|
||||
add_reference(path) — リファレンスとして追加
|
||||
write_summary(text) — 構造化要約を出力/上書き(最後の呼び出しが採用)
|
||||
```
|
||||
|
||||
### フロー
|
||||
|
||||
1. Pod が `tools::Tracker::recent_files(5)` で最近触られたファイルを抽出(デフォルトリファレンス)
|
||||
2. compact worker にプロンプトとして渡す:
|
||||
- bounded overview / index(User / Assistant / System 優先)
|
||||
- デフォルトリファレンスの一覧
|
||||
- TaskStore snapshot
|
||||
3. compact worker が自律的に:
|
||||
- search_session_log / read_session_items で bounded overview から漏れた compact 対象履歴を必要範囲だけ探索
|
||||
- read_file で各ファイルを読み、必要性を判断
|
||||
- mark_read_required / add_reference で指定
|
||||
- write_summary で構造化要約を出力(呼び直し可)
|
||||
4. CompactWorkerInterceptor が worker session 全体の context occupancy を監視する:
|
||||
- `finish_warning_remaining_tokens` 到達時に「探索を切り上げて write_summary へ進め」と Worker history に永続化される warning を挿入し、人間向け warning も出す
|
||||
- `final_reserve_tokens` を割った後は `write_summary` 以外の探索 tool call に synthetic error を返し、最終 summary の余白を守る
|
||||
- `worker_context_max_tokens` 超過は最後の hard stop
|
||||
5. ターン終了時に write_summary 未呼び出し or read_required 空(かつファイル操作履歴がある場合)→ 追加プロンプトで促す
|
||||
6. `summary_max_tokens` と `result_context_max_tokens` で compact 結果を検証してから新 segment を作り、Pod metadata の active pointer を更新する
|
||||
|
||||
### 構造化要約の要件
|
||||
|
||||
**目的**: auto-read でコードは別途載せるので、要約は意思決定・コンテキスト・方向性の記録に特化する。
|
||||
|
||||
**含めるべき内容:**
|
||||
1. 何を、なぜやったか — 意思決定の記録。具体的な型名・関数名で言及
|
||||
2. ユーザーの指示・フィードバックの原文 — ニュアンス保持。重要なもののみ
|
||||
3. 発生した問題と解決策 — 同じ轍を踏まない
|
||||
4. 今どこにいて次に何をするか — compact 前後の一貫性
|
||||
|
||||
**含めないもの:**
|
||||
- コードの全文(auto-read が担う)
|
||||
- 変更の diff(git がある)
|
||||
- 中間のやりとりの詳細(最終結論だけ)
|
||||
|
||||
### 要約フォーマット(5セクション、1000-2000 トークン目安)
|
||||
|
||||
```
|
||||
## Completed Tasks
|
||||
### (タスク名)
|
||||
- 完了した作業(具体的な型名・ファイル名で)
|
||||
- 注意点 / 発覚した事実
|
||||
|
||||
## Active Task
|
||||
### (タスク名)
|
||||
- 目標
|
||||
- 現状(何が済んで何が未着手か)
|
||||
- 次のステップ
|
||||
|
||||
## Key Decisions
|
||||
- (判断内容) — (理由)
|
||||
|
||||
## User Directives
|
||||
- 「(ユーザー発言の原文)」 — 重要な指示・フィードバックのみ
|
||||
|
||||
## Current Work
|
||||
(直前に何をしていたか。2-3行)
|
||||
```
|
||||
|
||||
セッション中に複数タスクが切り替わっている前提で設計。
|
||||
完了タスクは簡潔に(注意点・事実のみ)、進行中タスクは十分な詳細で。
|
||||
|
||||
---
|
||||
|
||||
## 設計の背景
|
||||
|
||||
### Claude Code からの知見
|
||||
|
||||
Claude Code の compaction 実装(`docs/ref/claude-code-compaction.md`)を参考に、以下を取り入れた:
|
||||
|
||||
- **条件付き Prune** (`min_savings`): KV キャッシュ無効化コストとのトレードオフ。`clear_at_least` パターン
|
||||
- **サーキットブレーカー**: 連続失敗の無限ループ防止
|
||||
- **2段階のファイル参照** (Read vs Referenced): Claude Code は compact 後に「Referenced file」(内容省略)と「Read」(内容保持)を区別する。サイズと必要性で判断
|
||||
- **要約の具体性**: 型名・関数名・ユーザー発言原文を保持。抽象的な要約は役に立たない
|
||||
|
||||
### 設計原則との対応
|
||||
|
||||
- Prune は Worker 層の拡張。新しい trait は不要(設計原則3)
|
||||
- Compact は Pod 層の制御。Worker は Yield を知るが Compact は知らない
|
||||
- CompactInterceptor は Decorator パターンで HookInterceptor をラップ。既存の Hook 機構を壊さない
|
||||
33
docs/design/compaction.md
Normal file
33
docs/design/compaction.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Compaction
|
||||
|
||||
Compaction exists because long-running Pods need durable continuity without sending the entire transcript forever.
|
||||
|
||||
## Pruning vs compaction
|
||||
|
||||
Pruning is request-local packing of existing history. It chooses what to include for one model request while preserving the underlying committed log.
|
||||
|
||||
Compaction is a durable transition. It creates a new summarized state and should be recorded so later turns can explain why older details are no longer directly present.
|
||||
|
||||
## Token-budget protection
|
||||
|
||||
Safety checks should use effective backend/context limits, including in-flight usage. Cached input tokens still occupy context even if upload displays subtract them.
|
||||
|
||||
When a provider returns an exact `input_total_tokens` measurement for the whole prompt shape, Yoi can treat it as authoritative and estimate only incremental growth after that measurement. It should not fake a system/history split or tune thresholds just to mask estimator bugs.
|
||||
|
||||
## After compaction
|
||||
|
||||
Compaction output is not automatically safe. The post-compact context must be revalidated before the next request.
|
||||
|
||||
A `just_compacted` flag must not bypass safety checks. It is easy for a compact summary, retained tail, or prompt resource change to still exceed a context limit.
|
||||
|
||||
## Large sessions
|
||||
|
||||
Large-session compaction should not send an entire prefix transcript as the summary input. Prefer bounded overview/index inputs plus exploration, then keep the retained tail small and explicit.
|
||||
|
||||
This keeps compaction cost predictable and avoids turning a context-recovery mechanism into the largest prompt in the session.
|
||||
|
||||
## History integrity
|
||||
|
||||
Compaction should preserve persisted reasoning history and avoid serializing unverified hidden reasoning context. Trace and metrics can count request shape and reasoning items without smuggling hidden provider state into model input.
|
||||
|
||||
The important property is explainability: after compaction, records should still show what summary replaced which older context and why future turns can rely on it.
|
||||
49
docs/design/context-history.md
Normal file
49
docs/design/context-history.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Context and history
|
||||
|
||||
Any input that can influence the model across a turn must be committed to history before it is placed in model context.
|
||||
|
||||
This rule protects both explainability and prompt-cache behavior. If the model reacts to a context-only insertion that is not in history, later turns cannot explain why the assistant made that decision, and cache anchors become harder to reason about.
|
||||
|
||||
## Allowed context transformations
|
||||
|
||||
A context transformation is acceptable when it is reproducible from durable Pod state and does not introduce new volatile facts.
|
||||
|
||||
Examples:
|
||||
|
||||
- Pruning old history according to persisted/session-derived state.
|
||||
- Truncating oversized tool result content while preserving the committed result boundary.
|
||||
- Adding prompt-cache anchors derived from stable context structure.
|
||||
- Rendering the same materialized system prompt string after compaction.
|
||||
|
||||
These transformations change how context is packed, not what happened.
|
||||
|
||||
## Forbidden context injection
|
||||
|
||||
Do not insert turn-crossing information directly into context without first appending it to `worker.history`.
|
||||
|
||||
Forbidden examples:
|
||||
|
||||
- Delivering a `Notify` or `PodEvent` only as a temporary context note.
|
||||
- Adding a `<system-reminder>` that explains behavior but is not persisted.
|
||||
- Rewriting old messages to include new facts.
|
||||
- Letting UI/controller-only state become model-visible without a committed record.
|
||||
|
||||
If new information should affect the model, append it to history and commit it. `history.json` / session persistence follows from the Worker history path.
|
||||
|
||||
## Prompt cache implications
|
||||
|
||||
Yoi optimizes for predictable prompt shape, not only small requests. Hidden context insertions make cache behavior hard to reproduce because the model-visible input can change without a corresponding history record.
|
||||
|
||||
Ordinary new inputs are not the problem: user messages, committed events, compacted summaries, and tool results are expected additions to history. The avoidable problem is changing prior or side-channel context in a way that is neither durable nor explainable from records.
|
||||
|
||||
## Pruning and compaction
|
||||
|
||||
Pruning is a request-time packing operation that chooses which existing history to include under a token budget. Compaction is a durable history operation that creates a new summarized state and must be committed.
|
||||
|
||||
After compaction, context safety must be revalidated. A flag such as `just_compacted` must not suppress safety checks, because compact output can itself be too large or malformed for the next request.
|
||||
|
||||
## UI and orchestration consequences
|
||||
|
||||
The TUI should display committed events rather than inventing transcript blocks. Orchestration should treat child notifications as prompts to inspect state, not as state themselves.
|
||||
|
||||
This keeps future turns able to answer: "what did the agent know, when did it know it, and where is that recorded?"
|
||||
38
docs/design/memory-knowledge.md
Normal file
38
docs/design/memory-knowledge.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Memory and Knowledge
|
||||
|
||||
Yoi memory is generated context, not project authority.
|
||||
|
||||
The authoritative record for work is still code, git history, work item files, tickets, session logs, and explicit user instruction. Memory helps the agent retrieve durable preferences and prior rationale, but it must not replace the records that made those facts true.
|
||||
|
||||
## Record types
|
||||
|
||||
- `summary.md` is resident background context for normal Pods.
|
||||
- `decisions/` stores durable decisions that are useful across turns.
|
||||
- `requests/` stores durable user requests and preferences.
|
||||
- `.yoi/knowledge/` stores curated Knowledge records when available.
|
||||
- `_logs/` stores append-only audit observations.
|
||||
- `_staging/` is generated candidate state before consolidation.
|
||||
|
||||
Generated `.yoi/memory` is personal/generated state in this repository. Curated workflow/Knowledge assets may be tracked separately when intended.
|
||||
|
||||
## What memory should not do
|
||||
|
||||
Memory should not duplicate authoritative project records. Do not copy ticket threads, TODO lists, implementation reports, or full docs into memory merely to make them resident.
|
||||
|
||||
The useful memory is the small part that changes future behavior: a policy, a rationale, a user preference, or a durable conclusion that would otherwise be hard to find.
|
||||
|
||||
## Lookup policy
|
||||
|
||||
Agents should use memory and Knowledge when the request depends on prior decisions, historical rationale, project workflow, or durable preferences.
|
||||
|
||||
Agents should not query memory every turn. Local repository files, current user instructions, command output, and tickets are more authoritative for exact current state.
|
||||
|
||||
## Audit and mutation
|
||||
|
||||
Memory extraction/consolidation writes append-only observations under `_logs`. No-op and idle notices belong there rather than in user-facing UI.
|
||||
|
||||
Memory mutation should be explicit work or part of the configured memory maintenance path. Casual edits during unrelated tasks make memory harder to trust.
|
||||
|
||||
## Language
|
||||
|
||||
Memory follows configured memory language policy. Conversation prose follows the user's language unless configured otherwise.
|
||||
33
docs/design/overview.md
Normal file
33
docs/design/overview.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Design overview
|
||||
|
||||
Yoi is organized around one rule: durable authority must live in explicit records, while runtime conveniences remain reconstructable hints.
|
||||
|
||||
That rule shapes the crate split. The runtime can restart, attach, compact, or delegate work without relying on hidden controller state, and developer tooling can inspect the records that explain why an agent acted.
|
||||
|
||||
## Core layers
|
||||
|
||||
- `yoi` owns the product CLI and top-level command shape. It is the façade that wires profile selection, memory linting, and normal TUI launch.
|
||||
- `pod` turns a `Worker` into a named runtime entity with scope, session persistence, protocol handling, tools, and Pod metadata integration.
|
||||
- `llm-worker` owns model-facing turns: history append, retries, continuation, pruning/compaction mechanics, tool loops, and provider-independent callbacks.
|
||||
- `session-store` owns replayable append-only conversation/session logs.
|
||||
- `pod-store` owns current Pod metadata keyed by Pod name.
|
||||
- `protocol` defines the socket message boundary between clients and Pods.
|
||||
- `client` contains reusable one-shot socket/runtime-command mechanics so lower crates do not depend on the product CLI.
|
||||
- `manifest` resolves Profiles, Manifests, model/provider references, scopes, prompts, and tool permission policy into a runtime contract.
|
||||
- `tools` implements built-in tools with bounded output and policy-aware execution.
|
||||
- `memory` owns generated memory, Knowledge records, linting, staging, and audit observations.
|
||||
- `tui` is a UI over Pod authority; it should not invent durable state.
|
||||
|
||||
## Why these boundaries exist
|
||||
|
||||
The Worker should not know process identity, Pod names, live sockets, spawned children, or UI state. It should know how to run an LLM turn over committed history and tools.
|
||||
|
||||
The Pod should not make provider-specific wire decisions. It coordinates runtime identity, persistence, scope, and protocol delivery around a Worker.
|
||||
|
||||
The TUI should not be an alternate source of truth. It may queue local input, show optimistic affordances, and render snapshots, but durable state comes from Pod/session records.
|
||||
|
||||
The CLI should own product command shape. Other crates should expose library APIs and typed runtime commands rather than re-parsing product arguments.
|
||||
|
||||
## Documentation boundary
|
||||
|
||||
Design docs explain the constraints that code must preserve. They should not duplicate every public type, exact schema field, or command output; those are owned by code, tests, and work items.
|
||||
39
docs/design/pod-session-state.md
Normal file
39
docs/design/pod-session-state.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Pod, session, and state authority
|
||||
|
||||
Yoi separates replayable history from current Pod identity because they answer different questions.
|
||||
|
||||
A session log answers: "what happened and what can be replayed?" Pod metadata answers: "what does this Pod name currently refer to?" Live sockets and registries answer only: "what seems reachable right now?"
|
||||
|
||||
## Session logs
|
||||
|
||||
Session JSONL is the durable replay record. It contains committed user inputs, assistant items, tool results, system/runtime events that must explain later behavior, segment boundaries, and persisted effective snapshots needed to understand a run.
|
||||
|
||||
The session log should be append-oriented and schema drift should be compile-visible. Compatibility shims that silently reinterpret old plural/current entries make future readers less safe.
|
||||
|
||||
Session logs do not own current Pod-name state. A historical session can be replayable without being the active session for a Pod name.
|
||||
|
||||
## Pod metadata
|
||||
|
||||
Pod metadata is the current-state layer keyed by Pod name. It records active/pending session pointers, resolved manifest snapshots, current delegation metadata, spawned-child visibility, and restoration information.
|
||||
|
||||
This avoids reconstructing current Pod state by scanning every session log. It also gives `--pod <name>`, TUI resume, `ListPods`, and `RestorePod` a single current authority.
|
||||
|
||||
Pod metadata should stay thin. It is not a second transcript, and it should not duplicate model conversation content.
|
||||
|
||||
## Live runtime hints
|
||||
|
||||
Sockets, process registries, and runtime files are liveness hints. They are useful for attach, status probing, and fast discovery, but they are not final proof that work completed or that a Pod's state changed durably.
|
||||
|
||||
A reachable pending Pod should be visible even if durable logs have not materialized yet. Missing restore labels should degrade labels and diagnostics, not hide a live attachable Pod.
|
||||
|
||||
## Spawned children and delegation
|
||||
|
||||
Parent-visible children are sourced from Pod metadata, not from a transient runtime mirror. Restoring a parent should reconstruct reachable children where possible and keep stopped-but-restorable children visible when metadata supports it.
|
||||
|
||||
Delegated write scope is a capability loan. Stopping, shutting down, or pruning a child must reclaim the parent's effective write permissions while preserving explicit base denies.
|
||||
|
||||
## Notifications are not authority
|
||||
|
||||
Pod completion notifications are UX hints. Before treating delegated work as complete, inspect queryable evidence: child output, session/log state, worktree status, diffs, and validation output.
|
||||
|
||||
This is why orchestration code should expose state-aware operations such as `ListPods` and `RestorePod`, rather than letting a background alert decide workflow state by itself.
|
||||
48
docs/design/profiles-manifests-prompts.md
Normal file
48
docs/design/profiles-manifests-prompts.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Profiles, Manifests, and prompts
|
||||
|
||||
Profiles are reusable recipes. Resolved Manifests are runtime contracts. Prompt resources are managed assets. Keeping those layers separate prevents runtime state from leaking into reusable configuration.
|
||||
|
||||
## Profiles
|
||||
|
||||
A Profile describes how a Pod should normally be built: worker language, model/provider selectors, prompt choices, tool policy defaults, and other reusable preferences.
|
||||
|
||||
A Profile should not contain runtime-bound fields:
|
||||
|
||||
- `pod.name`
|
||||
- concrete delegated `scope.allow`
|
||||
- sockets or process identifiers
|
||||
- session pointers
|
||||
- restored spawned-child state
|
||||
- raw secret values
|
||||
|
||||
Those fields depend on one run, one parent, or one machine. Putting them in a reusable Profile makes reuse unsafe.
|
||||
|
||||
Yoi is Lua Profile first. Lua gives project/user authors a controlled recipe layer through host-provided `require("yoi")` modules without treating Nix as runtime authoring. Nix remains useful for packaging and development records.
|
||||
|
||||
## Manifests
|
||||
|
||||
A resolved Manifest is the concrete contract used to create or restore a Pod. It carries defaults, resolved paths, permissions, scope, prompt references, provider/model decisions, and runtime identity.
|
||||
|
||||
Source/partial layers may omit fields. Resolved manifests should be explicit enough that Pod creation does not depend on ambient configuration later changing under it.
|
||||
|
||||
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup should select a Profile through `profiles.toml` / builtin defaults rather than ambient manifest cascades.
|
||||
|
||||
## Spawned Pods
|
||||
|
||||
`SpawnPod.profile` is optional and resolves through defaults when omitted. The only concrete capability delegation in the tool call is `SpawnPod.scope`, and it must be a subset of the parent's effective scope.
|
||||
|
||||
`inherit` derives reusable settings from the parent's resolved Manifest while replacing child identity and delegated scope. It should not blindly reuse the parent's original Profile source or runtime state.
|
||||
|
||||
## Prompt resources
|
||||
|
||||
Prompts live under `resources/prompts` so builtins, project overrides, and user overrides have one asset boundary.
|
||||
|
||||
The prompt layer should explain policy and behavior, but it should not smuggle volatile state into model context. Runtime facts that affect later turns must still go through history.
|
||||
|
||||
Builtin resources should be embedded at compile time. User/project profiles, explicit profile paths, prompt overlays, provider/model overrides, and explicit manifests remain filesystem-based.
|
||||
|
||||
## Why this separation matters
|
||||
|
||||
Without this split, configuration becomes unreproducible: a Profile might accidentally depend on a parent Pod's socket, a prompt override might act like hidden state, or a restored Pod might observe different defaults than the run that created it.
|
||||
|
||||
The boundaries make it clear which information is reusable authoring, which is resolved runtime contract, and which is durable run history.
|
||||
45
docs/design/provider-model-boundary.md
Normal file
45
docs/design/provider-model-boundary.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Provider and model boundary
|
||||
|
||||
The Worker should be provider-independent. Provider-specific wire formats, auth mechanisms, model catalogs, reasoning knobs, and terminal error shapes belong below or beside it, not inside ordinary turn orchestration.
|
||||
|
||||
## Worker responsibility
|
||||
|
||||
`llm-worker` owns turn lifecycle:
|
||||
|
||||
- committed history append
|
||||
- tool loops
|
||||
- retry before stream start
|
||||
- continuation after safe partial output
|
||||
- pruning/compaction decisions
|
||||
- provider-neutral callbacks and events
|
||||
|
||||
It should not know where a Codex OAuth token lives, how a provider encodes reasoning effort, or which environment variables a provider once supported.
|
||||
|
||||
## Provider responsibility
|
||||
|
||||
Provider/model layers own:
|
||||
|
||||
- resolving model aliases and builtin model metadata
|
||||
- provider-specific request/response conversion
|
||||
- auth integration
|
||||
- context/window metadata
|
||||
- terminal error classification
|
||||
- trace points around HTTP/auth/body/SSE lifecycle
|
||||
|
||||
External API facts change quickly. Repository docs should record Yoi's boundary decisions, not snapshots of vendor documentation.
|
||||
|
||||
## Secrets
|
||||
|
||||
Local credentials use explicit secret references. Secret values must not appear in diagnostics, Debug output, CLI/TUI output, work items, docs, session logs, model context, or persisted plaintext store files.
|
||||
|
||||
Codex OAuth remains a separate integration because its existing `auth.json` / `CODEX_HOME` shape is not the same as normal provider secret refs.
|
||||
|
||||
## Error and trace semantics
|
||||
|
||||
Provider terminal errors that are displayed live, such as context-length failures, must persist as errored runs rather than successful empty turns.
|
||||
|
||||
Event trace sidecars are optional parsed lifecycle traces. They are not complete raw SSE logs and should not become a hidden source of model context.
|
||||
|
||||
## Why this boundary exists
|
||||
|
||||
Provider APIs drift. If provider details leak into Worker logic, every new model behavior risks changing core orchestration. Keeping the boundary explicit lets Yoi adapt provider integrations while preserving stable turn semantics.
|
||||
37
docs/design/tool-permissions-scope.md
Normal file
37
docs/design/tool-permissions-scope.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Tool permissions and scope
|
||||
|
||||
Yoi treats tools as explicit capabilities. Model-visible tool names are not permission by themselves; the resolved Manifest and Pod scope decide whether a call is allowed.
|
||||
|
||||
## Permission policy
|
||||
|
||||
Tool permissions are built into PreToolCall policy.
|
||||
|
||||
- `allow` permits the call to proceed.
|
||||
- `deny` rejects only that call with a synthetic error result.
|
||||
- `ask` fails closed until a real approval/resume protocol exists.
|
||||
|
||||
Failing closed matters because an unresolved approval state is not the same as permission. A future approval flow must be able to pause and resume the same unresolved call, not merely ask the model to retry later.
|
||||
|
||||
## Filesystem scope
|
||||
|
||||
Filesystem scope is separate from tool allow/deny. A file tool may be registered and permitted, but the concrete path still must be inside readable or writable scope.
|
||||
|
||||
Symlinks do not grant extra authority. Access decisions should be made on the canonical target when it exists, and broken or out-of-scope links should produce diagnostics rather than escaping scope.
|
||||
|
||||
Directory traversal tools should not follow symlink directories as a way around scope. If an external checkout is needed, add its real path to read scope.
|
||||
|
||||
## Delegation
|
||||
|
||||
Child Pods receive an explicit subset of the parent's scope. Delegation is a capability loan, not a copy of all parent authority.
|
||||
|
||||
When a child stops, shuts down, or is pruned as unreachable, delegated write permissions must be reclaimed. Explicit base denies remain in force.
|
||||
|
||||
## Tool output
|
||||
|
||||
Tool output should be bounded before it enters history/model context. The system may truncate or summarize mechanical output boundaries, but it should not hide the fact that a tool call happened or fabricate successful results.
|
||||
|
||||
Network tools such as WebSearch/WebFetch are disabled/unconfigured by default, fail closed, and need explicit manifest/profile configuration. Their outputs are untrusted content and must remain bounded.
|
||||
|
||||
## Why this design exists
|
||||
|
||||
LLM tool calls are suggestions from an untrusted planner. Yoi can let the model propose operations while keeping final authority in manifest policy, scope checks, and durable records.
|
||||
26
docs/development/dogfooding.md
Normal file
26
docs/development/dogfooding.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Dogfooding Yoi
|
||||
|
||||
This repository is developed with Yoi itself. Dogfooding is valuable because it exposes orchestration, memory, TUI, and workflow problems under real use.
|
||||
|
||||
## What to record
|
||||
|
||||
When a tool limitation, workflow obstacle, or model-facing policy problem blocks work, record it under `docs/report/` or a work item artifact. Do not turn every minor annoyance into a maintained design doc.
|
||||
|
||||
A report is useful when it explains:
|
||||
|
||||
- what the agent/user tried to do
|
||||
- what made the work unsafe, confusing, or slow
|
||||
- what design boundary was missing
|
||||
- what evidence was observed
|
||||
|
||||
## Runtime command caveat
|
||||
|
||||
After rebuilding and restarting during dogfooding, `current_exe()` can point at a deleted binary path. Use typed runtime-command configuration and the development-only `YOI_POD_RUNTIME_COMMAND` executable override rather than reviving shell-command overrides.
|
||||
|
||||
## Multi-Pod work
|
||||
|
||||
Use child Pods for scoped tasks and reviews, but keep orchestration decisions in visible project records. Do not merge, close, or clean up merely because a child notification arrived.
|
||||
|
||||
## Secrets and logs
|
||||
|
||||
Do not put secrets, private prompts, or ignored secret-like file contents into diagnostics, work items, docs, session logs, or model context. During broad audits, existence/path checks are enough unless the user explicitly asks to inspect content.
|
||||
24
docs/development/environment.md
Normal file
24
docs/development/environment.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Environment boundary
|
||||
|
||||
Environment variables are a minimized runtime boundary. Prefer explicit profile/manifest configuration and secret references over ambient process state.
|
||||
|
||||
## Why minimize environment variables
|
||||
|
||||
Ambient environment is hard to audit: it can differ between shells, services, spawned Pods, tests, and restored processes. If important runtime behavior depends on it, reproducing a session becomes harder.
|
||||
|
||||
Yoi keeps environment variables for narrow bootstrap and development cases, while normal provider credentials and runtime configuration should be explicit records.
|
||||
|
||||
## Principles
|
||||
|
||||
- Distinguish data/runtime paths from resource/config paths.
|
||||
- Prefer embedded builtin resources over installed runtime resource directories.
|
||||
- Use explicit secret refs for provider and WebSearch credentials.
|
||||
- Keep dev-only executable overrides clearly named and documented.
|
||||
- Avoid shell-command parser overrides for runtime Pod launch.
|
||||
- Tests should prefer typed fixtures/injection and mutate process environment only around thin env-reader behavior.
|
||||
|
||||
## Current surface
|
||||
|
||||
Use `YOI_*` for current environment variables. Old project prefixes should not be reintroduced.
|
||||
|
||||
`YOI_POD_RUNTIME_COMMAND` is a development-only executable-path override for typed `yoi pod` launch. It is not a general shell-command override.
|
||||
47
docs/development/validation.md
Normal file
47
docs/development/validation.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Validation
|
||||
|
||||
Validation should match the change. Do not run expensive broad checks just to look thorough, but do not skip checks that prove the changed boundary still works.
|
||||
|
||||
## Docs-only changes
|
||||
|
||||
Minimum checks:
|
||||
|
||||
```sh
|
||||
./tickets.sh doctor
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Useful stale-surface sweeps:
|
||||
|
||||
```sh
|
||||
STALE_DOC_PATTERN='TODO[.]md|obsolete doc dirs|old ticket surface'
|
||||
rg -n "$STALE_DOC_PATTERN" README.md docs crates/*/README.md --glob '!docs/report/**'
|
||||
```
|
||||
|
||||
## Code changes
|
||||
|
||||
Prefer targeted tests first, then broader checks when the touched boundary is central.
|
||||
|
||||
Common checks:
|
||||
|
||||
```sh
|
||||
cargo test --workspace
|
||||
cargo test -p <crate>
|
||||
cargo check --workspace
|
||||
```
|
||||
|
||||
Avoid repository-wide formatting churn when a validation failure is caused by pre-existing unrelated formatting.
|
||||
|
||||
## Work item checks
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
./tickets.sh doctor
|
||||
```
|
||||
|
||||
Use work item review to verify that the implementation satisfies the ticket, not only that the diff looks plausible.
|
||||
|
||||
## Current limitation
|
||||
|
||||
End-to-end tests that spawn real processes are not yet designed. When changing Pod restoration, socket behavior, or orchestration, compensate with targeted unit/integration tests and concrete manual evidence in the implementation report.
|
||||
43
docs/development/work-items.md
Normal file
43
docs/development/work-items.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Work items
|
||||
|
||||
Yoi project work is tracked through `work-items/` and `./tickets.sh`. Git history plus work item files are the authoritative state-transition record.
|
||||
|
||||
Do not treat ad-hoc chat summaries, memory records, or Pod notifications as the final source of project state.
|
||||
|
||||
## Basic commands
|
||||
|
||||
```sh
|
||||
./tickets.sh create --title "..." [--slug slug] [--kind task] [--priority P2] [--label a,b]
|
||||
./tickets.sh list [--status open|pending|closed|all]
|
||||
./tickets.sh show <id-or-slug>
|
||||
./tickets.sh comment <id-or-slug> [--role comment|plan|decision|implementation_report] [--file path]
|
||||
./tickets.sh review <id-or-slug> --approve|--request-changes [--file path]
|
||||
./tickets.sh status <id-or-slug> open|pending|closed
|
||||
./tickets.sh close <id-or-slug> [--resolution text|--file path]
|
||||
./tickets.sh doctor
|
||||
```
|
||||
|
||||
## Granularity
|
||||
|
||||
One work item should describe a complete change that can be explained as a feature, behavior, design decision, or maintenance outcome when closed.
|
||||
|
||||
Avoid tickets that only mirror an implementation step unless that step is independently reviewable and useful. Phase/step lists inside a ticket are execution order, not a separate dependency system.
|
||||
|
||||
## Contents
|
||||
|
||||
A useful work item states:
|
||||
|
||||
- background and motivation
|
||||
- requirements
|
||||
- acceptance criteria
|
||||
- relevant constraints
|
||||
- review/implementation reports when work is submitted
|
||||
- final resolution when closed
|
||||
|
||||
Keep long research dumps out of the item body. Put necessary artifacts under the ticket's `artifacts/` directory and summarize the conclusion in the thread.
|
||||
|
||||
## Workflow
|
||||
|
||||
Create or refine the work item before opening a separate implementation worktree. When using child Pods, the orchestrator should provide a scoped task and later verify concrete evidence: diff, tests, worktree status, and child output.
|
||||
|
||||
Closing a ticket means the repository records are ready, not merely that a child Pod announced completion.
|
||||
36
docs/development/workflows.md
Normal file
36
docs/development/workflows.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Workflows and orchestration
|
||||
|
||||
Yoi development uses workflows to make multi-step agent work repeatable without hiding authority in chat state.
|
||||
|
||||
Project-authored workflows live under `.yoi/workflow/`. Generated memory lives under `.yoi/memory/`; the two should not be mixed.
|
||||
|
||||
## Workflow role
|
||||
|
||||
A workflow should define how to coordinate work. It should not become a private implementation branch, an unreviewed design decision, or a replacement for work items.
|
||||
|
||||
Current workflow themes include:
|
||||
|
||||
- preflight before delegating uncertain ticket work
|
||||
- worktree setup and cleanup
|
||||
- sibling coder/reviewer Pod orchestration
|
||||
- human-gated maintenance and merge readiness
|
||||
|
||||
## Child Pods
|
||||
|
||||
Spawned Pods are useful for scoped implementation, review, or exploration. They are not independent project authorities.
|
||||
|
||||
A parent/orchestrator must verify:
|
||||
|
||||
- child output via `ReadPodOutput`
|
||||
- live/restorable state via Pod tools when relevant
|
||||
- worktree status and diff
|
||||
- validation command output
|
||||
- work item requirements and acceptance criteria
|
||||
|
||||
Notifications are hints to inspect state. They are not proof of completion.
|
||||
|
||||
## Merge and close responsibility
|
||||
|
||||
Unless explicitly authorized otherwise, final merge, cleanup, design-boundary decisions, and ticket closure remain the orchestrator/human responsibility.
|
||||
|
||||
Child Pods may commit in delegated worktrees when the workflow allows it, but the merge-ready dossier should make the final decision auditable from repository records.
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
# 環境変数ポリシー
|
||||
|
||||
Yoi では、プロセス境界で本当に必要な場合を除き、環境変数の利用を避ける。新しい ambient な入力を増やすより、明示的な profile / manifest / config file / typed secret reference / CLI argument を優先する。
|
||||
|
||||
それでも、path discovery、runtime directory、外部 provider の credential 慣習のために、一部の環境変数はまだサポートしている。この文書に載せた通常 runtime 用の環境変数は公開 surface として扱う。ただし、fallback 変数は独立した設定項目ではなく、対応する main key の解決順の一部として扱う。開発・テスト都合だけの環境変数は、通常ユーザー向け configuration として扱わない明確な escape hatch に限る。
|
||||
|
||||
## 原則
|
||||
|
||||
- 同じ情報を profile、manifest、config、CLI input で明示的に渡せるなら、便利ショートカットとして新しい環境変数を追加しない。
|
||||
- 永続的な project state を環境変数に置かない。解決済みの状態は適切な store に保存する。
|
||||
- 通常の TUI / Pod runtime は `.env` ファイルを暗黙に load しない。example が局所的に `dotenv` を使うことはあっても、application startup が project の `.env` を勝手に読むべきではない。
|
||||
- 生の secret value を generated log、work item、trace output、docs に出さない。環境変数名の記述はよいが、値は書かない。
|
||||
- path/location 用の環境変数、provider credential 用の移行互換、test/build-only の環境変数を混ぜない。
|
||||
- test が process environment を変更する必要がある場合は、guard で直列化し、元の値を復元する。Rust の process environment は global state である。
|
||||
|
||||
## Path / resource discovery
|
||||
|
||||
Path 系の環境変数は論理的な key ごとに立項する。`XDG_*` や `HOME` は単独の設定 surface ではなく、該当 key の fallback として読む。
|
||||
|
||||
| 論理 key | Main env | Fallback / 解決順 | 用途と位置付け |
|
||||
| --- | --- | --- | --- |
|
||||
| `home` | `YOI_HOME` | なし | config / data / runtime をまとめて sandbox する root override。設定を細かく分けるより、test や isolated run ではまずこれを使う。 |
|
||||
| `config_dir` | `YOI_CONFIG_DIR` | `$YOI_HOME/config` → `$XDG_CONFIG_HOME/yoi` → `$HOME/.config/yoi` | 人が書く設定・override の置き場。`profiles.toml`、prompt override、model/provider override など。 |
|
||||
| `data_dir` | `YOI_DATA_DIR` | `$YOI_HOME` → `$HOME/.yoi` | プログラムが書く永続データの置き場。session log、Pod metadata など、再起動後も restore / replay の根拠になるもの。通常ユーザー向けの primary knob ではなく、test や isolated data store 用の advanced override。 |
|
||||
| `runtime_dir` | `YOI_RUNTIME_DIR` | `$YOI_HOME/run` → `$XDG_RUNTIME_DIR/yoi` → `$HOME/.yoi/run` | socket、pid/status file、live registry mirror など、再起動で捨ててよい runtime state の置き場。 |
|
||||
|
||||
空の path 環境変数は、`manifest::paths` では原則として unset 相当に扱う。
|
||||
|
||||
### `data_dir` と `runtime_dir` の違い
|
||||
|
||||
`data_dir` は durable data のための場所である。消すと session history、Pod metadata、restore/replay の根拠が失われる。
|
||||
|
||||
`runtime_dir` は process coordination のための場所である。socket、pid/status file、live Pod registry mirror などを置き、プロセス終了や再起動で stale になり得る。runtime state は durable authority ではない。session log と Pod metadata が durable source であり、runtime file は mirror / hint である。
|
||||
|
||||
このため、socket や pid file を `data_dir` に置かない。永続データと揮発 runtime state は分ける。
|
||||
|
||||
### Builtin assets と `config_dir`
|
||||
|
||||
Builtin profiles and catalogs are embedded in the binary at build time. User/project-owned overrides remain under `config_dir` and project `.yoi/` files such as `profiles.toml`; package runtime resource lookup is not a supported configuration surface.
|
||||
|
||||
## Credential と外部 auth
|
||||
|
||||
Provider API key と WebSearch credential は、通常の runtime では環境変数から読まない。`yoi keys` で local secret store に論理 id を追加し、profile / manifest / provider catalog / web config がその id を明示的に参照する。
|
||||
|
||||
```toml
|
||||
[model]
|
||||
ref = "anthropic/claude-sonnet-4-6"
|
||||
auth = { kind = "secret_ref", ref = "providers/anthropic/default" }
|
||||
|
||||
[web]
|
||||
enabled = true
|
||||
|
||||
[web.search]
|
||||
provider = "brave"
|
||||
api_key_secret = "web/brave/default"
|
||||
```
|
||||
|
||||
Store の user-visible schema は `id -> value` だけであり、store は provider 名や kind を解釈しない。自動的な provider-name-to-secret-id lookup は行わず、設定が secret id を選ぶ。
|
||||
|
||||
On-disk store は `<data_dir>/secrets/store.json`。secret value は軽量な obfuscation と integrity check で plaintext config / log / terminal output に出にくくするが、OS keychain や passphrase vault ではない。data directory と source code を読める local user に対する強い保護は主張しない。
|
||||
|
||||
| 変数 / pattern | 用途 | 備考 |
|
||||
| --- | --- | --- |
|
||||
| `CODEX_HOME` | Codex OAuth `auth.json` の場所。 | 外部互換用の入力。fallback は `$HOME/.codex`。Codex OAuth は local secret store とは別の structured integration のまま維持する。 |
|
||||
|
||||
通常 runtime が `.env` を暗黙に load することはない。credential UX のために implicit `.env` loading を追加しないこと。
|
||||
|
||||
## Development-only escape hatches
|
||||
|
||||
これらは dogfooding / self-rebuild / fixture などの開発運用だけの逃げ道であり、通常ユーザー向けの configuration surface ではない。profile、manifest、CLI option の代替として案内しない。
|
||||
|
||||
| 変数 | Context | 備考 |
|
||||
| --- | --- | --- |
|
||||
| `YOI_POD_RUNTIME_COMMAND` | 開発中に起動中の `yoi` binary が rebuild され、`std::env::current_exe()` が `target/debug/yoi (deleted)` のような stale path を返す場合の Pod runtime executable override。 | Unset または empty の場合は既定どおり current executable に `pod` prefix argument を付けて起動する。Non-empty の場合は値を executable path としてそのまま使い、`pod` prefix argument は常に自動追加する。shell parsing や argument splitting は行わないため、値に flags や `pod` を含めない。 |
|
||||
|
||||
## Build / example variables
|
||||
|
||||
これらは通常の application configuration ではない。
|
||||
|
||||
| 変数 | Context | 備考 |
|
||||
| --- | --- | --- |
|
||||
| `CARGO_MANIFEST_DIR`, `OUT_DIR` | build / test resource lookup。 | build script や fixture/resource lookup で使う。 |
|
||||
| `PATH` | test / dev command lookup。 | helper executable を探す場合だけ使う。 |
|
||||
| `TMPDIR` | shell script / test。 | `tickets.sh` が temporary file に使う。 |
|
||||
| `RUST_LOG` | example / dev diagnostics。 | example CLI が tracing setup 経由で読む場合がある。 |
|
||||
| `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY` などの provider example vars | `llm-worker` や Pod example / fixture recorder。 | example code が `dotenv::dotenv().ok()` を呼ぶことがある。通常の `yoi` runtime startup には適用されない。 |
|
||||
|
||||
## 整理方針
|
||||
|
||||
環境変数に関わるコードを触る場合は、以下を優先する。
|
||||
|
||||
1. fallback / precedence の test は、process environment を読ませず、直接入力を渡せる小さな pure helper で検証する。
|
||||
2. path resolution は `manifest::paths` に集約し、path precedence rule を別の場所で重複実装しない。
|
||||
3. test が process environment を変更するのは、process env から読む thin wrapper 自体を検証する場合や、subprocess isolation に必要な場合に限る。
|
||||
4. credential source は resolved config 上で明示し、process-env convention を増やすより typed secret reference を使う。
|
||||
5. fallback env は独立した設定項目として増やさず、対応する main key の解決順として文書化する。
|
||||
6. 空の env value は、変数 category に応じて unset / invalid のどちらとして扱うかを一貫させ、新しい supported variable を追加する場合は挙動を文書化する。
|
||||
7. 外部 process integration が env inheritance / filtering を必要とする場合は、ambient な inherited process state に頼らず、明示的な policy boundary として設計する。
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# File references and symlinks
|
||||
|
||||
FileRef resolution and file tools follow symlinks only after the resolved target passes the Pod scope check. A symlink placed inside the workspace does not grant access to the target by itself.
|
||||
|
||||
Recommended external-reference workflow:
|
||||
|
||||
- Prefer adding the real external project path, such as a local external checkout, to the Pod read scope when the Pod is started or spawned.
|
||||
- If a workspace symlink is used, the symlink target still must be inside readable scope. For writes, the resolved target must be inside writable scope.
|
||||
- If a relative symlink is broken, recreate it with the correct relative target from the symlink's parent directory, or use an absolute symlink.
|
||||
- Directory traversal tools such as Glob and Grep do not follow symlink directories. Use the resolved target directory directly when it is in read scope.
|
||||
|
||||
This preserves symlink escape safety: access decisions are made on the canonicalized target whenever the target exists, and broken or out-of-scope symlinks are rejected with diagnostics that include the original path and target where possible.
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
# Manifest profiles
|
||||
|
||||
Profiles are reusable Lua-authored recipes for generating a Yoi runtime manifest. The Rust resolver evaluates a selected `.lua` profile in-process, validates that it is Profile-shaped rather than a complete Manifest, then binds runtime values such as Pod name and concrete scope to produce the persisted `PodManifest` snapshot.
|
||||
|
||||
Profiles are intentionally not authority-bearing manifests. `pod.name`, concrete `scope.allow` / `scope.deny`, runtime directories, sockets, active session state, and raw secret material do not belong in reusable profiles. Use `yoi keys` to store provider/WebSearch credentials, then reference explicit secret ids such as `auth = { kind = "secret_ref", ref = "providers/anthropic/default" }` or `web.search.api_key_secret = "web/brave/default"`. Use `--manifest` when you need the explicit low-level complete Manifest escape hatch.
|
||||
|
||||
## Minimal profile
|
||||
|
||||
```lua
|
||||
local profile = require("yoi.profile")
|
||||
local models = require("yoi.models")
|
||||
local scope = require("yoi.scope")
|
||||
|
||||
return profile {
|
||||
slug = "coder",
|
||||
description = "Example coding Pod",
|
||||
|
||||
model = models.catalog("codex-oauth/gpt-5.5"),
|
||||
worker = {
|
||||
reasoning = "high",
|
||||
},
|
||||
scope = scope.workspace_write(),
|
||||
}
|
||||
```
|
||||
|
||||
Run an explicit path with:
|
||||
|
||||
```sh
|
||||
yoi pod --profile ./coder.lua
|
||||
# or through the TUI fresh-spawn dialog
|
||||
yoi --profile ./coder.lua
|
||||
```
|
||||
|
||||
`--profile` accepts an explicit path, `path:<path>`, a discovered profile name, `default`, or a source-qualified name such as `project:coder`, `user:coder`, or `builtin:coder`. Path-like values containing `/`, starting with `.`, or ending in `.lua` are explicit paths. ``.nix` paths are no longer supported as profiles and fail with a diagnostic that points users at Lua profiles or `--manifest`.
|
||||
|
||||
`--profile` conflicts with `yoi pod --manifest` and with restore/session/adopt modes. Use `--profile-pod-name <name>` when a launcher needs a creation-time Pod name override without invoking `--pod` restore semantics. Profile evaluation is a creation-time path; Pod resume restores saved Pod state/resolved snapshots rather than re-evaluating the profile source.
|
||||
|
||||
## Controlled Lua environment
|
||||
|
||||
Profiles run in a restricted Lua VM. Host virtual modules are available through controlled `require`:
|
||||
|
||||
- `require("yoi")`
|
||||
- `require("yoi.profile")`
|
||||
- `require("yoi.models")`
|
||||
- `require("yoi.compact")`
|
||||
- `require("yoi.scope")`
|
||||
|
||||
Profile-local modules may be required by dotted names such as `require("shared")` or `require("shared.models")`; those resolve only under the selected profile file's directory. Unsafe/unrestricted Lua facilities such as `os`, `io`, `debug`, `package`, `dofile`, and `loadfile` are unavailable by default.
|
||||
|
||||
## Profile discovery
|
||||
|
||||
Profile discovery is separate from runtime manifest merging. User/project `profiles.toml` files may declare profile registry metadata, but those files are application/project UX configuration and are not merged into the selected profile artifact.
|
||||
|
||||
Example project config at `.yoi/profiles.toml`:
|
||||
|
||||
```toml
|
||||
default = "coder"
|
||||
|
||||
[profile]
|
||||
coder = "profiles/coder.lua"
|
||||
reviewer = "profiles/reviewer.lua"
|
||||
```
|
||||
|
||||
Table entries can carry descriptions:
|
||||
|
||||
```toml
|
||||
[profile.coder]
|
||||
path = "profiles/coder.lua"
|
||||
description = "Project coding assistant"
|
||||
```
|
||||
|
||||
Relative registry paths are resolved against the `profiles.toml` file that declares them. Discovery checks bundled builtin profiles, then the user registry at `<config_dir>/profiles.toml`, then the nearest project registry at `.yoi/profiles.toml`. The bundled `builtin:default` profile is the fallback default when no user/project registry declares another default. Later defaults override earlier defaults, so a project default wins over a user default, and either wins over the builtin default. Unqualified defaults resolve within the declaring source by default. Unqualified ambiguous names fail closed:
|
||||
|
||||
```sh
|
||||
yoi --profile coder # fails if both user:coder and project:coder exist
|
||||
yoi --profile project:coder # source-qualified selection
|
||||
yoi --profile default # selected registry default
|
||||
```
|
||||
|
||||
The fresh-spawn TUI also uses discovery. The new Pod dialog defaults to the selected registry default, normally `builtin:default` unless a user/project registry overrides it. `Tab`/`Down` cycles forward through discovered profiles and `Shift-Tab`/`Up` cycles backward; there is no ambient manifest-cascade opt-out. Passing `yoi --profile <selector>` opens the same new Pod dialog with that selector selected and leaves Pod-name editing unchanged.
|
||||
|
||||
## One-file manifests
|
||||
|
||||
`yoi pod --manifest <PATH>` remains as an explicit compatibility/debug path. It reads exactly that TOML file, resolves relative paths against the file's parent directory, merges builtin defaults, and validates through the same `PodManifestConfig -> PodManifest` boundary as profile artifacts. It does not load user or project `manifest.toml` files and conflicts with `--profile`.
|
||||
|
||||
Ambient user/project `manifest.toml` cascade startup has been removed. Normal fresh spawns use profile discovery/default selection, with `profiles.toml` acting only as a profile registry/default selector.
|
||||
|
||||
## Resolver contract
|
||||
|
||||
A Lua profile should return either `profile { ... }` or a plain table containing Profile fields. The resolver converts reusable fields such as `model`, `worker`, `compaction`, `memory`, `web`, `permissions`, `session`, and scope intent into a concrete Manifest. Runtime Pod name and concrete scope authority are supplied by launch context, then the resolved Manifest snapshot is persisted for restore.
|
||||
|
||||
Profile and one-file manifest CLI paths currently use builtin prompt assets only. `$yoi/...` instruction refs work; `$user/...` and `$workspace/...` prompt refs need a future explicit prompt-loader source design instead of reviving ambient manifest discovery.
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
# AI Maintainer Workflow 設計
|
||||
|
||||
## 目的
|
||||
|
||||
AI maintainer は、yoi リポジトリの開発を継続的に進めるための orchestration role である。単発の `/auto-maintain` より広く、設計相談、work item 整理、実装委譲、レビュー、運用課題の記録、改善提案を一つの maintainer loop として扱う。
|
||||
|
||||
`/auto-maintain` はこの設計の限定実行形であり、`tickets.sh` / `work-items/` から小さな実装作業を選んで実装・レビューを orchestration する Workflow に留まる。
|
||||
|
||||
## 正本と権限境界
|
||||
|
||||
現在の作業管理の正本は `tickets.sh` と `work-items/` である。古い `TODO.md` / legacy `tickets/` directory を前提にした運用は superseded。
|
||||
|
||||
- work item の作成・コメント・レビュー・状態変更・完了は原則 `./tickets.sh` 経由で行う
|
||||
- `work-items/{open,pending,closed}/<id>/item.md` は要件・受け入れ条件の正本
|
||||
- `thread.md` は計画・判断・レビュー・実装報告の時系列 thread
|
||||
- `resolution.md` は close 時の完了記録
|
||||
- 時系列と状態遷移の最終根拠は git history
|
||||
- `docs/report/` は観測・所感・改善候補の記録であり、最新仕様の authority ではない
|
||||
- `.yoi/memory` は個人/生成 state であり、project record の正本ではない
|
||||
|
||||
AI maintainer は project record を勝手に膨らませない。明確な実装単位は work item 化し、小粒な所見は `KNOWN_ISSUES.md`、ドッグフーディング上の障壁やツール問題は `docs/report/` に記録する。
|
||||
|
||||
## Role model
|
||||
|
||||
### Maintainer Pod
|
||||
|
||||
人間と対話する親 Pod。設計判断、作業選定、scope 委譲、レビュー統括、merge / close の判断を担う。
|
||||
|
||||
主な責務:
|
||||
|
||||
- `work-items/`, `KNOWN_ISSUES.md`, `docs/`, git history, worktree 状態を読んで現在地を把握する
|
||||
- 作業可能な work item と設計相談が必要な work item を分ける
|
||||
- child worktree / child Pod に実装を委譲する
|
||||
- 実装結果を work item の背景・要件・受け入れ条件に照らして review する
|
||||
- review / close / merge の branch placement を守る
|
||||
- 人間に判断が必要な境界で止まる
|
||||
|
||||
Maintainer Pod は「便利だから」project record を書き換えない。新規 work item 作成、既存 work item の大幅変更、close、merge、commit などは、ユーザー指示または会話上の合意に基づいて行う。
|
||||
|
||||
### Implementation Pod
|
||||
|
||||
狭い write scope を持つ作業者。対象 worktree と work item を渡され、その範囲で実装・検証・報告を行う。
|
||||
|
||||
制約:
|
||||
|
||||
- 指定 scope 外を編集しない
|
||||
- `.yoi` や main workspace の control-plane record を勝手に編集しない
|
||||
- work item / review / close は maintainer の責務として扱う
|
||||
- 実装報告には変更点、検証、未解決点を含める
|
||||
|
||||
### Reviewer Pod
|
||||
|
||||
原則 read-only。実装 diff と対象 work item を読み、妥当性を確認する。
|
||||
|
||||
見るべき点:
|
||||
|
||||
- work item の要件・受け入れ条件を満たしているか
|
||||
- 設計を歪めていないか
|
||||
- 不必要な後方互換性や局所最適な変更を作っていないか
|
||||
- テストが適切か
|
||||
- ドキュメント / work item / known issue の更新が必要か
|
||||
|
||||
## Workflow modes
|
||||
|
||||
### Mode 0: Design consultation
|
||||
|
||||
設計や方針を相談するだけの mode。コードや work item は編集しない。
|
||||
|
||||
使う場面:
|
||||
|
||||
- 要件がまだ曖昧
|
||||
- work item 化する前に合意したい
|
||||
- 複数案の設計判断が必要
|
||||
|
||||
### Mode 1: Project record maintenance
|
||||
|
||||
`work-items/`, `KNOWN_ISSUES.md`, `docs/`, `docs/report/` など control-plane record だけを編集する。実装コードは触らない。
|
||||
|
||||
例:
|
||||
|
||||
- work item の作成・詳細化
|
||||
- review / implementation report / resolution の追記
|
||||
- stale docs の修正
|
||||
- known issue の追加・削除
|
||||
|
||||
新規 work item 作成や大幅変更は、人間の合意後に行う。
|
||||
|
||||
### Mode 2: Implementation orchestration
|
||||
|
||||
Maintainer Pod が child worktree と implementation Pod を作り、実装と review を回す。親は scoped delegation と project record authority を保持する。
|
||||
|
||||
基本手順:
|
||||
|
||||
1. 対象 work item を main workspace で作成・詳細化し commit する
|
||||
2. orchestrator が child worktree を作る
|
||||
3. implementation Pod に対象 worktree と task を渡す
|
||||
4. implementation Pod が実装・検証・報告する
|
||||
5. reviewer Pod が diff と work item を読む
|
||||
6. 必要なら修正を戻す
|
||||
7. maintainer が merge-ready dossier を作る
|
||||
|
||||
### Mode 3: Maintainer-managed completion
|
||||
|
||||
人間が明示的に許可した場合に、親 Pod が commit / merge / close まで行う。
|
||||
|
||||
条件:
|
||||
|
||||
- 対象 work item が明確
|
||||
- scope と worktree が分離されている
|
||||
- review 結果が approve または残件が明示されている
|
||||
- validation が通っている
|
||||
- close / merge してよいというユーザー指示または会話上の合意がある
|
||||
|
||||
push はしない。破壊的 git 操作は明示指示なしに行わない。
|
||||
|
||||
## Work item lifecycle
|
||||
|
||||
### 作成
|
||||
|
||||
```
|
||||
./tickets.sh create --title "..." [--slug slug] [--kind task] [--priority P2] [--label a,b]
|
||||
```
|
||||
|
||||
作成後、必要なら `item.md` を詳細化し、背景・要件・受け入れ条件を明確にする。worktree を使う場合は、対象 work item を作成・詳細化して commit してから branch/worktree を切る。
|
||||
|
||||
### コメント / 計画 / 判断
|
||||
|
||||
```
|
||||
./tickets.sh comment <id-or-slug> --role plan --file path
|
||||
./tickets.sh comment <id-or-slug> --role decision --file path
|
||||
./tickets.sh comment <id-or-slug> --role implementation_report --file path
|
||||
```
|
||||
|
||||
thread は後から読まれる project record なので、実装ログをだらだら貼るより、判断理由・検証結果・残件を簡潔に残す。
|
||||
|
||||
### レビュー
|
||||
|
||||
```
|
||||
./tickets.sh review <id-or-slug> --approve --file path
|
||||
./tickets.sh review <id-or-slug> --request-changes --file path
|
||||
```
|
||||
|
||||
レビューは diff の確認だけではない。work item の前提・要件・受け入れ条件が実装で満たされているか、コードベースを歪めていないかを確認する。
|
||||
|
||||
### 完了
|
||||
|
||||
```
|
||||
./tickets.sh close <id-or-slug> --resolution "..."
|
||||
```
|
||||
|
||||
close は `work-items/closed/` へ移動し、`resolution.md` と thread entry を残す。close commit には resolution と関連 project record 更新を含める。
|
||||
|
||||
## Branch / worktree placement
|
||||
|
||||
main workspace は orchestration / docs / work item lifecycle / merge の control plane。実装差分は原則 child worktree に隔離する。
|
||||
|
||||
```
|
||||
main/develop workspace:
|
||||
work item 作成・詳細化 commit
|
||||
child worktree 作成
|
||||
|
||||
child worktree feature branch:
|
||||
implementation commit(s)
|
||||
validation
|
||||
|
||||
main/develop workspace:
|
||||
reviewer Pod に read-only 依頼
|
||||
merge / close / cleanup commit
|
||||
```
|
||||
|
||||
child Pod に write scope を渡している間、親 Pod は同じ path を編集しない。必要なら `StopPod` で scope を回収してから project record を更新する。
|
||||
|
||||
## Human approval boundaries
|
||||
|
||||
AI maintainer は以下で人間に戻す。
|
||||
|
||||
- 要件から複数の設計方針が自然に導ける
|
||||
- public API / protocol / manifest / durable data format を変える
|
||||
- 大規模 rename / directory 移動 / repository-wide format を伴う
|
||||
- work item の作成・大幅変更・close について合意がない
|
||||
- reviewer が request-changes しており、修正方針が自明でない
|
||||
- validation が失敗し、原因が対象変更か既存問題か不明
|
||||
- destructive git operation や push が必要
|
||||
|
||||
## Reports / Knowledge / Memory
|
||||
|
||||
- `docs/report/`: ドッグフーディングで感じた障壁、改善案、ツール問題の記録。明確な作業単位になったら work item 化する
|
||||
- `.yoi/knowledge`: curated project knowledge。正本ではなく補助 context
|
||||
- `.yoi/memory`: generated/personal state。project record の代替にしない
|
||||
- `KNOWN_ISSUES.md`: ticket 化するほどではないが、次に近所を触る時に拾いたい小粒所見
|
||||
|
||||
## Future extension points
|
||||
|
||||
### WorkItemStore API
|
||||
|
||||
現在は `tickets.sh` + repo-managed `work-items/` を正本とする。将来、remote maintainer hub や issue tracker へ差し替えるなら、WorkItem / Thread / Event / Artifact を扱う store interface を追加し、Git/path layout を上位 workflow に漏らさない形にする。
|
||||
|
||||
### Maintainer doctor
|
||||
|
||||
merge 前 / close 前に以下を検査する command。
|
||||
|
||||
- 対象 work item が存在する
|
||||
- acceptance criteria と implementation report が対応している
|
||||
- review status が明示されている
|
||||
- validation log がある
|
||||
- work item lifecycle と branch placement が矛盾していない
|
||||
|
||||
### Multi-maintainer coordination
|
||||
|
||||
複数 maintainer Pod が同じ repository を扱う場合は、work item / branch / scope の lease が必要。現状は人間と git history が最終調停者。
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# LLM 履歴永続化 plan(superseded)
|
||||
|
||||
> Status: superseded / historical note.
|
||||
>
|
||||
> この文書は、`session-store` / segment log / Pod metadata 分離が入る前の検討メモ。現在の実装仕様として読まないこと。現行仕様は `docs/architecture.md` の「セッション永続化」と `crates/session-store` / `crates/pod-store` / `crates/pod` を正とする。
|
||||
|
||||
## 現在の要点
|
||||
|
||||
- 会話・worker replay の authority は `session-store` の append-only JSONL segment log。
|
||||
- fresh conversation は新しい `SessionId` を作る。
|
||||
- compact / fork / rewind は同じ `SessionId` 配下の `SegmentId` を切り替える。
|
||||
- segment 出自は `LogEntry::SegmentStart` 内の `SegmentOrigin`(`compacted_from` / `forked_from`)で表す。
|
||||
- Pod 名からの current state は `pod-store` metadata が持つ。
|
||||
- active `(SessionId, SegmentId)` pointer
|
||||
- `resolved_manifest_snapshot`
|
||||
- spawned child delegation / reclaim state
|
||||
- runtime socket path / registry mirror は live/derived state であり、conversation history や Pod-name current state の durable authority ではない。
|
||||
|
||||
## Superseded な旧前提
|
||||
|
||||
この旧 plan は以下の前提を含んでいたため、現在の仕様とは一致しない。
|
||||
|
||||
- session を単一ファイル・単一系列として扱う前提。
|
||||
- compact/fork で新 SessionId を作る前提。
|
||||
- entry hash を replay lineage の主参照にする前提。
|
||||
- Pod 名 current state と conversation log authority を分離しない前提。
|
||||
|
||||
履歴としては有用だが、実装時の根拠には使わない。新しい変更を検討する場合は、まず current code と work item を読むこと。
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
# LLM プロバイダサポート方針
|
||||
|
||||
## Context
|
||||
|
||||
Yoi が利用する LLM プロバイダとその認証方式を決める。従量 API 課金の心理的負担を避け、定額サブスク枠(ChatGPT Codex / Ollama Cloud)を活かせる構成にする。
|
||||
|
||||
詳細な現状調査は `docs/ref/llm-provider-landscape.md` と `docs/ref/llm-pricing-2026-04.md` を参照。
|
||||
|
||||
## 決定事項
|
||||
|
||||
### 第一級サポート(専用アダプタ)
|
||||
|
||||
| プロバイダ | scheme | 認証 | 用途 |
|
||||
|---|---|---|---|
|
||||
| **Ollama** | scheme/anthropic 流用(v0.14+ `/v1/messages`) | なし(ダミー) | ローカル + `:cloud` サフィックスでクラウド中継。`localhost:11434` で統一 |
|
||||
| **Codex OAuth** | scheme/openai_responses | `~/.codex/auth.json` | Codex CLI と同じ認証ストアを使う Responses 経路 |
|
||||
| **Anthropic API** | scheme/anthropic | API key | 従量課金経路のみ |
|
||||
|
||||
Ollama は独自 scheme を作らず `scheme/anthropic` を base_url 差し替えで流用。`/v1/chat/completions` は stream+tools バグ (#9092) のため使わない。`cache_control` / `tool_choice` / `metadata` / `count_tokens` は Ollama 非対応のため送らない。
|
||||
|
||||
### 二次サポート(OpenAI 互換共通枠)
|
||||
|
||||
`scheme = "openai_chat"` + `base_url` + API key を宣言するプロバイダカタログエントリ(`resources/providers/builtin.toml`)1 つで以下を収容。個別モデルはモデルカタログ(`resources/models/builtin.toml`)側で列挙する:
|
||||
|
||||
- OpenRouter
|
||||
- xAI (Grok)
|
||||
- Groq
|
||||
- Together / Fireworks / DeepInfra
|
||||
- BLACKBOX
|
||||
- 任意の OpenAI 互換エンドポイント
|
||||
|
||||
### 非サポート
|
||||
|
||||
- **Claude Pro/Max OAuth 経路** — Anthropic が 2026-01-09 にサーバ側でブロック、2026-02-19 に第三者ツール経由の利用制限を明文化。第一級機能としては採用しない
|
||||
- **`claude -p` CLI fork** — 専用 API integration ではないため実装しない
|
||||
|
||||
## 根拠
|
||||
|
||||
- **Codex OAuth は Codex CLI 互換の認証経路として扱う**: Codex CLI は Apache-2.0 で公開されており、同じ Responses 系 wire behavior に寄せる
|
||||
- **Anthropic API は従量だが代替なし**: Pro/Max OAuth 経路の制限後、Claude 系を使うには API key 経路のみ
|
||||
- **Ollama は `:cloud` で透過**: `ollama signin` で Ed25519 鍵登録後、`localhost:11434` 経由でクラウドモデルが使える。ローカルデーモンが署名付き中継
|
||||
- **OpenAI 互換は汎用アダプタ 1 本**: ルーター系は後追いで数を増やしやすい宣言型設計、実装コスト最小
|
||||
|
||||
## 実装原則
|
||||
|
||||
- 認証ストアを読むアダプタ(`~/.codex/auth.json` 等)は **llm-worker 直下に置かず上位層に配置**。llm-worker は低レベル基盤に留める方針(`feedback_llm_worker_scope.md`)と整合
|
||||
- モデル列挙は **auto_discover と宣言型の両輪**。Ollama は `/api/tags` で自動、OpenAI 互換枠はモデルカタログ(`resources/models/builtin.toml` + `<config_dir>/models.toml` の user override、`<config_dir>` は `manifest::paths` で解決)で宣言
|
||||
- UI のプロバイダ選択肢も第一級 → 二次の優先順位で並べる
|
||||
- **`ollama launch yoi` 対応を視野に**、env 注入(`ANTHROPIC_BASE_URL` / `OPENAI_BASE_URL` 等)で起動設定を受け入れる作り
|
||||
|
||||
## 機能方針
|
||||
|
||||
### プロバイダ側の高次ツールは使用しない
|
||||
|
||||
`web_search` / `code_interpreter` / `computer_use` / Live Search 等はプロバイダ依存を避けるため不採用。yoi の自前 Tool 層で統一。fallback や routing もワーカー側で管理するため OpenRouter の `transforms` / `provider routing` も使わない。
|
||||
|
||||
### 必須 capability
|
||||
|
||||
- **tool calling**: parallel tool calls 含めて必須
|
||||
- **reasoning**: 必須。内部表現は共通型 `ReasoningControl { effort, budget_tokens }` に正規化し、scheme アダプタで各社形式(`reasoning.effort` / `thinking.budget_tokens` / `reasoning_format`)に投影。DeepSeek の `reasoning_content` 別フィールドは Thinking block として正規化
|
||||
|
||||
### Capability は 5 軸
|
||||
|
||||
`ModelCapability` として以下を持つ:
|
||||
|
||||
1. tool calling(parallel 可否含む)
|
||||
2. structured output (`json_object` / `json_schema` / Grammar)
|
||||
3. reasoning(effort / budget_tokens / 出力形式)
|
||||
4. vision
|
||||
5. prompt caching(下記)
|
||||
|
||||
### Prompt caching は 2 値
|
||||
|
||||
```
|
||||
prompt_caching: CacheStrategy, // Explicit { max_breakpoints } / Auto
|
||||
```
|
||||
|
||||
- **Explicit**: Anthropic。scheme が `cache_control` マーカー挿入
|
||||
- **Auto**: それ以外全部。scheme は何もしない(サーバ側自動 prefix と「何もしない」は呼び出し側から同一)
|
||||
- 「安定 prefix を先頭に寄せる」正規化は両方共通で適用
|
||||
|
||||
### Streaming
|
||||
|
||||
現状の `BlockStart / BlockDelta / BlockStop / BlockAbort` + `DeltaContent::{Text, Thinking, InputJson}` 設計を維持。変更不要。
|
||||
|
||||
ToolCall 引数が delta で来ないプロバイダ(Gemini)は scheme アダプタで「BlockStart → InputJson(全体 1 回) → BlockStop」の**擬似ストリーム化**で吸収。
|
||||
|
||||
Ollama は Ollama 側の OpenAI 互換エンドポイント (`/v1/chat/completions`) に stream+tool バグ (#9092) があるため、**Anthropic Messages API 互換エンドポイント (`/v1/messages`) に寄せる** (`scheme/anthropic` を `base_url` 差し替えで流用、`cache_control` / `tool_choice` は送らない)。
|
||||
|
||||
## Scope 外
|
||||
|
||||
- 各アダプタのクレート構成・データ型・API 境界は別 plan/ticket で切り出す
|
||||
- プロバイダ選択 UI とモデル一覧管理の設計も別扱い
|
||||
- OpenAI Responses API の stateful (`previous_response_id`) 対応は将来対応、`BlockMetadata` の拡張が必要
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# AI maintainer 用 WorkItem / Thread 抽象メモ
|
||||
|
||||
> Status: mostly implemented / historical design memo.
|
||||
>
|
||||
> この文書は `tickets.sh` + `work-items/` MVP の前に書かれた設計メモ。現在の運用上の正本は `AGENTS.md` の Work item / Ticket 運用と、実際の `tickets.sh` / `work-items/{open,pending,closed}`。古い `TODO.md` / `tickets/` 前提は superseded。
|
||||
|
||||
## 現在実現済みの部分
|
||||
|
||||
- WorkItem 相当: `work-items/{open,pending,closed}/<id>/item.md`
|
||||
- Thread 相当: `thread.md`
|
||||
- Event 相当: `tickets.sh comment/review/status/close` が append する thread entry
|
||||
- Artifact 相当: `artifacts/` directory
|
||||
- Resolution 相当: close 時の `resolution.md`
|
||||
- ID: timestamp + slug 形式
|
||||
- Doctor: `./tickets.sh doctor`
|
||||
|
||||
`work-items/` は repo-managed な project coordination record であり、`.yoi/memory` はその代替ではない。
|
||||
|
||||
## 現在も残る設計余地
|
||||
|
||||
- Rust crate / Store interface としての WorkItemStore
|
||||
- LeaseStore / Run tracking / maintainer inbox
|
||||
- remote maintainer hub / GitHub Issues などへの backend 差し替え
|
||||
- TUI 統合
|
||||
|
||||
これらは必要になった時点で改めて work item 化する。
|
||||
|
||||
## 現在の運用参照
|
||||
|
||||
- `AGENTS.md` — Work item / Ticket の運用ルール
|
||||
- `tickets.sh` — 実際の操作コマンド
|
||||
- `work-items/` — 現在の project-visible coordination data
|
||||
- `docs/plan/ai-maintainer.md` — AI maintainer workflow の現行設計
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
# Memory effectiveness improvement plan
|
||||
|
||||
## Context
|
||||
|
||||
`docs/plan/memory.md` で定義した永続化・検索・extract / consolidation の基盤は実装済みだが、現状の workspace memory を見る限り、実用上の効果はまだ弱い。
|
||||
|
||||
現在の主要な症状:
|
||||
|
||||
- `knowledge/*` が空で、通常 Pod が自発的に参照できる discovery 面がない
|
||||
- `memory/summary.md` は「Always-on サマリ」と設計されているが、通常 Pod の system prompt へ常駐注入されていない
|
||||
- consolidation は `KnowledgeCandidateReport::empty()` を受け取り、prompt 上も「候補レポートが空なら新規 Knowledge を作るな」としているため、Knowledge の cold-start が起きない
|
||||
- `decisions/*` と `requests/*` は記録としては残るが、description / resident injection を持たず、後続 turn で自然に読まれにくい
|
||||
- bundled prompt に Yoi 開発固有の ticket / TODO 運用を前提にした shadow 禁止が入り、一般ユーザー workspace の管理文脈を過剰に落とす可能性がある
|
||||
|
||||
この状態で usage metrics を先に実装しても、「使われていない / 発見されない memory」を測るだけになり、Knowledge 化候補や保護閾値の信号が十分に育たない。先に memory が読まれ、Knowledge が最低限生まれる経路を作る。
|
||||
|
||||
## Proposal
|
||||
|
||||
Usage metrics の前に、memory effectiveness の最小改善として以下を行う。
|
||||
|
||||
1. `summary.md` を通常 Pod の resident context に入れる
|
||||
2. Knowledge の cold-start gate を緩める
|
||||
3. bundled memory prompts を project-management 手法に依存しない形へ汎用化する
|
||||
4. 明示参照 (`#<slug>`) の実用経路を整える
|
||||
5. 既存 decisions / requests から Knowledge へ昇格できる整理経路を用意する
|
||||
|
||||
この順序により、metrics 実装時には「実際に読まれる対象」と「昇格候補になりうる Knowledge / memory record」が存在する状態になる。
|
||||
|
||||
## 1. Summary resident injection
|
||||
|
||||
### Problem
|
||||
|
||||
`docs/plan/memory.md` では `memory/summary.md` を Always-on サマリとしている。しかし実装上、通常 Pod の system prompt に載るのは `model_invokation: true` の Knowledge description / Workflow description であり、summary は `MemoryRead(kind=summary)` しない限り読まれない。
|
||||
|
||||
これは設計と実装のズレであり、memory の中心 record が通常 turn に効かない原因になっている。
|
||||
|
||||
### Direction
|
||||
|
||||
通常 Pod の system prompt trailing section に `summary.md` の本文または圧縮表示を載せる。
|
||||
|
||||
- `[memory]` が有効で、`memory/summary.md` が存在する場合だけ注入する
|
||||
- consolidation / internal disposable Worker には注入しない
|
||||
- 既存の `Resident knowledge` とは別 section にする
|
||||
- summary の frontmatter は除外し、本文のみを載せる
|
||||
- summary 本文の linter 上限は 20,000 chars だが、resident 注入では別途 soft cap を持つか、当初は summary 自体を 1-5k tokens に保つ prompt 方針に依存する
|
||||
|
||||
### Expected effect
|
||||
|
||||
通常 Pod は少なくとも現在の project memory の要約を毎 turn 見られる。Knowledge が空でも memory subsystem の効果がゼロになりにくい。
|
||||
|
||||
## 2. Knowledge cold-start gate
|
||||
|
||||
### Problem
|
||||
|
||||
現行 consolidation は `KnowledgeCandidateReport::empty()` を渡し、prompt も空レポート時の新規 Knowledge 作成を禁止している。これは usage metrics 完成後の gate としては妥当だが、cold-start では永久に Knowledge が生まれない。
|
||||
|
||||
### Direction
|
||||
|
||||
Knowledge 作成 gate と resident injection gate を分離する。
|
||||
|
||||
- 新規 Knowledge 作成:
|
||||
- consolidation が high-confidence に再利用価値を判断できる場合は許可する
|
||||
- default は `model_invokation: false`
|
||||
- `description` は「本文要約」ではなく「何の知識で、いつ読むべきか」を書かせる
|
||||
- `model_invokation: true` への昇格:
|
||||
- usage metrics / 人間判断 / 将来の評価指標に基づく
|
||||
- cold-start 自動作成時には原則 ON にしない
|
||||
- GC / drop 保護:
|
||||
- 引き続き usage metrics の明示 invoke frequency を使う
|
||||
|
||||
つまり、metrics が無い間も Knowledge は蓄積できるが、system prompt 常駐コストを増やす判断は保守的にする。
|
||||
|
||||
### Expected effect
|
||||
|
||||
Knowledge が空のまま固定される問題を避ける。metrics 実装前でも、検索可能な reusable knowledge が増える。
|
||||
|
||||
## 3. Prompt generalization
|
||||
|
||||
### Problem
|
||||
|
||||
bundled memory prompts は、Yoi 自身の ticket / TODO / worktree 運用を一般ユーザーへ押し付けている。ticket はこのプロジェクトの管理手法であり、ユーザー workspace の正本や作業管理の形は project ごとに異なる。
|
||||
|
||||
### Direction
|
||||
|
||||
Default prompt では特定の管理手法名を禁止対象として列挙しない。
|
||||
|
||||
残すべき一般原則:
|
||||
|
||||
- 既存の authoritative record を逐語的に mirror しない
|
||||
- ファイル操作ログや VCS 履歴そのものを memory に再保存しない
|
||||
- ただし、将来の作業判断に効く project-management 上の制約・優先順位理由・プロセス決定・ recurring pattern は抽象化して保存してよい
|
||||
- Yoi 自身の ticket shadow 回避は bundled default ではなく workspace / user prompt override で表現する
|
||||
|
||||
### Expected effect
|
||||
|
||||
Memory extraction / consolidation が、ユーザー workspace の管理文脈を過剰に捨てなくなる。
|
||||
|
||||
## 4. Explicit slug invocation path
|
||||
|
||||
### Problem
|
||||
|
||||
設計上は `#<slug>` で Knowledge を参照するが、現状は LLM が `KnowledgeQuery` / `MemoryRead` を自発的に呼ぶことに寄っている。ユーザーが明示的に `#slug` を書いても、専用の展開経路が弱い。
|
||||
|
||||
### Direction
|
||||
|
||||
段階的に実装する。
|
||||
|
||||
1. user input から `#<slug>` を検出する
|
||||
2. exact slug で `knowledge/<slug>.md` を解決する
|
||||
3. 解決できた本文を user turn の context として history に append してから LLM に渡す
|
||||
4. 解決失敗は system-reminder / synthetic note ではなく、history に残る通常の input artifact として扱う
|
||||
|
||||
この実装では、プロジェクト指示の「history に残らない context input 禁止」に従い、展開結果を必ず `worker.history` に commit する。context だけへの差し込みはしない。
|
||||
|
||||
### Expected effect
|
||||
|
||||
ユーザーが必要な Knowledge を明示参照でき、metrics 実装後の明示 invoke 計測点にもなる。
|
||||
|
||||
## 5. Existing record promotion
|
||||
|
||||
### Problem
|
||||
|
||||
現状の memory には decisions と requests があるが、いくつかは reusable knowledge に近い。これらが decisions のままだと resident discovery や KnowledgeQuery の対象になりにくい。
|
||||
|
||||
### Direction
|
||||
|
||||
consolidation の tidy / consolidation に、既存 `decisions/*` / `requests/*` を Knowledge へ昇格する経路を追加する。
|
||||
|
||||
- 同一内容を copy するのではなく、Knowledge として再利用可能な抽象に rewrite する
|
||||
- 元 decision は必要なら残す。Knowledge 側には `last_sources` として直近 source を持つ
|
||||
- 新規 Knowledge 作成は §2 の cold-start gate に従う
|
||||
- decision の「判断履歴」と Knowledge の「再利用可能な運用知識」を混同しない
|
||||
|
||||
### Expected effect
|
||||
|
||||
既存 memory の価値を引き出し、metrics 前に検索対象を増やせる。
|
||||
|
||||
## Ordering
|
||||
|
||||
推奨順序:
|
||||
|
||||
1. Prompt generalization
|
||||
- 既に `tickets/memory-prompts-remove-ticket-policy.md` として起票済み
|
||||
- prompt が不要に情報を捨てる状態を先に止める
|
||||
2. Summary resident injection
|
||||
- 既存 `summary.md` が即座に通常 Pod へ効く
|
||||
3. Knowledge cold-start gate
|
||||
- consolidation が Knowledge を生めるようにする
|
||||
4. Existing record promotion
|
||||
- 既存 decisions / requests から Knowledge を立ち上げる
|
||||
5. Explicit slug invocation path
|
||||
- Knowledge が存在する状態で明示参照経路を整える
|
||||
6. Usage metrics
|
||||
- 明示 invoke / resident cost / protection threshold / Knowledge candidate report を実装する
|
||||
|
||||
## Interaction with usage metrics
|
||||
|
||||
Usage metrics は不要ではない。役割を以下に限定すると妥当性が高い。
|
||||
|
||||
- `model_invokation: true` へ昇格する判断材料
|
||||
- resident injection cost と使われ率の観測
|
||||
- tidy / GC での drop / 大幅圧縮保護
|
||||
- Knowledge candidate report の補助信号
|
||||
|
||||
一方、metrics を Knowledge 作成そのものの唯一 gate にすると cold-start が壊れる。Knowledge 作成は high-confidence agent judgement、resident 化と保護は metrics、という分担にする。
|
||||
|
||||
## Risks / open questions
|
||||
|
||||
### Summary injection cost
|
||||
|
||||
`summary.md` を常駐させると system prompt budget を消費する。summary が肥大化した場合に備え、以下のどちらかを選ぶ必要がある。
|
||||
|
||||
- linter / consolidation prompt で summary を 1-5k tokens に保つ運用を先行する
|
||||
- resident injection 側に hard truncation / warning を入れる
|
||||
|
||||
初期は前者でよいが、truncation されるなら LLM に明示した方がよい。
|
||||
|
||||
### Knowledge noise
|
||||
|
||||
cold-start gate を緩めると Knowledge が増えすぎる可能性がある。対策として、新規作成時の `model_invokation: false` default、update 優先、description 品質指示、tidy step の noisy 分類を使う。
|
||||
|
||||
### Prompt override boundary
|
||||
|
||||
Yoi 自身の ticket shadow 回避を完全に消すと、この repository の memory は作業ログ寄りに戻る可能性がある。これは bundled default ではなく workspace prompt override で解くべきで、default prompt の責務ではない。
|
||||
|
||||
### `#<slug>` history representation
|
||||
|
||||
明示参照展開をどの Item / Segment として history に残すかは別途詰める必要がある。原則は「LLM が見たものは history に残す」。context-only injection は使わない。
|
||||
|
||||
## Validation criteria
|
||||
|
||||
この計画が妥当かは、以下で判断する。
|
||||
|
||||
- 新規 workspace で Knowledge が 0 件のまま固定されない
|
||||
- 通常 Pod が `summary.md` の内容を tool call なしで参照できる
|
||||
- bundled prompts が ticket / TODO / worktree 等の特定管理手法を前提にしない
|
||||
- Knowledge の `description` が discovery 面として機能する
|
||||
- `model_invokation: true` は uncontrolled に増えない
|
||||
- usage metrics 実装時に、計測対象となる明示参照 / resident cost / Knowledge records が存在する
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
# メモリ機構の prompt 要件
|
||||
|
||||
## Context
|
||||
|
||||
`docs/plan/memory.md` で決めた memory の挙動を、実装時に prompt 要件へ落とすための補助ページ。ここでは「個別タスクをこなすベーシックな system / developer 指示」の上に追加で必要になる、memory 固有の指示だけをまとめる。
|
||||
|
||||
前提は **tool-based + agentic**。専用 schema で挙動を閉じ込めるのではなく、基本は sub-Worker に CRUD tool を渡し、prompt と post-write 検証で振る舞いを制約する。例外は Workflow で、`docs/plan/workflow.md` の通り自動書き込みはしない。
|
||||
|
||||
## 決定事項
|
||||
|
||||
### 共通原則
|
||||
|
||||
memory 関連 prompt は種別を問わず、最低限以下を共有する:
|
||||
|
||||
- **source を推論しない**。`session_id` や entry range は wrapper / 呼び出し側が機械付与し、LLM が捏造しない
|
||||
- **rewrite は許可**するが、情報損失は最小化する。既存の主張・根拠・参照を保ったまま整理・圧縮する
|
||||
- **単純 append を優先しない**。既存 record に統合できるなら update を優先する
|
||||
- **session 固有の進行状態を書かない**。長期参照価値のある内容だけを memory に残す
|
||||
- **既存 docs と重複保存しない**。`AGENTS.md`、`docs/plan/*`、固定運用文書に既にある内容を再保存しない
|
||||
- **特定の project-management 手法を前提にしない**。既定 prompt は issue tracker / task board / planning doc / changelog / version-control history / generated report などを「正確な内容の authoritative record」として一般化して扱う。memory はそれらの本文・status 変化・短命 identifier をそのまま複製する parallel ledger にはしない。一方で、今後の作業に役立つ持続的な project-management 事実、workflow 制約、優先度判断の根拠、複数 item にまたがる学びは採用してよい
|
||||
- **空出力を許容**する。保存価値が無ければ「何も追加しない」を正当な結果として扱う
|
||||
|
||||
### extract: 活動抽出 prompt
|
||||
|
||||
extract は「派生物を作る」段階ではなく、「起きたことを抽出する」段階として縛る:
|
||||
|
||||
- 対象は `decisions`、`discussions`、`attempts`、`requests` の候補に限る
|
||||
- Knowledge 化、summary rewrite、slug 命名、`model_invokation` 判断は行わない
|
||||
- 一回限りの雑談、浅い質問、長期参照価値の薄い進行ログは返さなくてよい
|
||||
- 出力は schema 準拠の構造化データのみ。自由文の補足説明で schema 外情報を足さない
|
||||
- 対象が無ければ空配列を返す
|
||||
|
||||
ノイズ防御として、抽出時点で以下を除外する:
|
||||
|
||||
- `attempts`: authoritative record の保守や外部 lifecycle の移動そのものだけを表す操作は除外。残すのはその record 自体からは復元できない情報 (ビルド/テスト結果、外部 API 応答、観測されたバグ再現、後段の判断材料となる設計実験結果、持続的な process lesson) に限る
|
||||
- `discussions`: 当日中に陳腐化する一過性 triage (直近の scheduling、status 確認、短命な順序決めなど) は除外。session を越えて意味を持つ論点 (アーキテクチャの trade-off、恒常的な workflow 制約、再来する問い) のみ残す
|
||||
- `decisions`: rationale が「この session で X をした」になるものは除外。設計 / 方針 / process / 取り組み方の根拠でない記録は decision ではなく作業ログ。ただし、作業管理上の持続的な方針や抽象化は decision として扱える
|
||||
- authoritative project record の title / body / checklist / raw status / 短命 identifier を本文に複製しない。正確な status mirror や identifier と組み合わせないと意味を成さない記録は採用しない
|
||||
|
||||
### consolidation: 統合 + 整理 prompt
|
||||
|
||||
consolidation は既存 `memory/*`、`knowledge/*`、staging を見て、統合 step と整理 step を 1 セッション内で続けて回す。両 step に共通する原則:
|
||||
|
||||
- 入力には staging の活動ログ、既存 `memory/*`(summary / decisions / requests)の全文、Knowledge 化候補レポート、整理材料(使用頻度メトリクス、Linter Warn、`replaced` chain、sources 過多情報)を含める
|
||||
- 既存 `knowledge/*` は prompt に埋めず、Knowledge 検索ツール経由で agent が必要分を引く。まず候補レポートの source や staging の話題に近い slug を検索し、ヒットした slug / description / kind / `model_invokation` を見て適合先を探す
|
||||
- 新規作成より update を優先し、既存 slug に自然に統合できる場合は新規 file を増やさない
|
||||
- Decisions / Requests は staging の `source` をそのまま使い、LLM が `sources` を組み立てない
|
||||
- summary は必要なときだけ rewrite し、常に 1-5k tokens 目安に圧縮する
|
||||
- Decision の置き換えは `status: replaced` と `replaced_by` で表現する
|
||||
- 人間編集との不整合が見える rewrite は避け、衝突しそうなら保守的に統合する
|
||||
|
||||
統合 step の追加指示:
|
||||
|
||||
- staging の活動ログを decisions / requests / summary / Knowledge update に落とし込む
|
||||
- staging の field ごとに宛先を分ける:
|
||||
- `decisions` (staging) → `memory/decisions/`。設計 / 方針 / 取り組み方の判断のみ。「この session で X をした」型は drop
|
||||
- `requests` (staging) → `memory/requests/`
|
||||
- `attempts` (staging) → 既定は drop。memory に `attempts/` フォルダは設けない。複数 attempts に通底する持続的な傾向だけ `summary.md` に 1 行で圧縮する例外あり
|
||||
- `discussions` (staging) → 設計 / 方針に決着していれば `decisions/` に統合、未決着でも問い自体が持続的なら `summary.md` に 1 行、それ以外は drop。`decisions/` に「議論した」だけの未決着メモを作らない
|
||||
- Knowledge 新規作成は候補レポート掲載 source 由来に限る(詳細は §Consolidation: Knowledge 書き込み prompt)
|
||||
|
||||
整理 step の追加指示(統合 step 完了後、余力で実行):
|
||||
|
||||
- 既存 record 群を `outdated`、`superseded`、`unused`、`noisy` の観点で評価し、なぜ整理対象なのかを分類する
|
||||
- 明示 invoke の保護閾値超過 record は drop / 大幅圧縮の対象外とする
|
||||
- `similar-slug`、`sources-overflow`、`replaced` 滞留は主に `superseded` または `noisy` の材料として扱う
|
||||
- merge / split / trim / drop の理由を git diff から読める形で残す
|
||||
- 直接削除してよいが、git で可逆である前提に甘えすぎず、誤判定しやすいものは merge / trim を優先する
|
||||
|
||||
### consolidation: Knowledge 書き込み prompt
|
||||
|
||||
Knowledge の新規作成 / 更新では、consolidation 全体の原則に加えて以下を明示する:
|
||||
|
||||
- 採択ラインは「このプロジェクト / ユーザーに対して再度参照価値のある事実・ルール・ノウハウ」に限る
|
||||
- 一回限りの判断や議論は Decisions に留め、繰り返し参照される抽象化だけを Knowledge に上げる
|
||||
- 新規作成は Knowledge 化候補レポートに載った source から派生する場合に限る
|
||||
- 既存 Knowledge は Knowledge 検索ツールで検索し、ヒットした slug / description / kind / `model_invokation` を見て、適合先があるなら必ず update を優先する
|
||||
- 新規 slug は「既存に適合先が無い」と説明できるときだけ作る
|
||||
- Knowledge は `kind` を frontmatter に持ち、少なくとも「用語 / 運用方針 / ルール / 事実 / ノウハウ」のどこに寄るかを明示する
|
||||
- `last_sources` は入力で与えられた source を使い、推論で補完しない
|
||||
- `description` は「何の知識か / いつ使うか」が短く分かる文にする
|
||||
- description だけで対象範囲が分からない粒度や、複数主題を抱えた file は避ける
|
||||
- `model_invokation` ON/OFF は頻度・常駐コストの判断材料を踏まえて慎重に扱い、初期値は OFF とする
|
||||
- `#<slug>` 参照を書く場合は、実在 record への参照だけを使う
|
||||
- `AGENTS.md` や `docs/` に既に固定化されたルールの写しは作らない
|
||||
- 保存価値が無ければ Knowledge を何も追加しない
|
||||
|
||||
### 監査 LLM prompt
|
||||
|
||||
初期範囲では専用の監査 LLM は持たない(`memory.md` §書き込み経路と Linter / §将来検討 参照)。意味破壊の抑制は consolidation prompt 側の情報損失最小化指示と git diff レビューに寄せる。後から 2 層目として挟む際の入力・check 項目・pass-fail 返却形式はそのときに詰める。
|
||||
|
||||
## 関連
|
||||
|
||||
- `docs/plan/memory.md`: memory 全体方針
|
||||
- `docs/plan/workflow.md`: workflow を自動書き込みの例外にしている理由
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
# メモリ機構の方針
|
||||
|
||||
## Context
|
||||
|
||||
Yoi がユーザーのプロジェクトに対して提供するメモリ機構。プロジェクトの暗黙知蓄積と同じ失敗を繰り返さないための記憶が目的。エージェントに連続するアイデンティティや自己意識を持たせる方向は対象外。
|
||||
|
||||
リサーチは `docs/ref/memory-systems.md`。前提として、**レポジトリがファイルシステム上にある**ケースで設計する(越境・バックエンド抽象は Scope 外)。
|
||||
|
||||
prompt 要件の整理は `docs/plan/memory-prompts.md` に切り出した。
|
||||
|
||||
Workflow(`/<slug>` で呼び出される制約付き作業フロー)は別 plan に切り出した。`docs/plan/workflow.md` 参照。
|
||||
|
||||
## 決定事項
|
||||
|
||||
### 記録対象の 4 種
|
||||
|
||||
本ドキュメント以下のパスはすべて **`<workspace_root>/.yoi/`** からの相対表記。`.yoi/` は manifest / prompts と同じく workspace に紐付く yoi コンテンツのルートで、memory もこの規約に従う。`workspace_root` 既定は Pod の pwd。
|
||||
|
||||
| 種別 | パス | 備考 |
|
||||
| ---------------- | ---------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| Always-on サマリ | `memory/summary.md` | 1-5k tokens 目安 |
|
||||
| Decisions | `memory/decisions/<slug>.md` | `status: open \| resolved \| replaced` で未決議論も保持、置き換え時は `replaced_by: <slug>` |
|
||||
| Requests | `memory/requests/<slug>.md` | ユーザー submit の構造化要約 |
|
||||
| Knowledge | `knowledge/<slug>.md` | `#slug` で注入。`kind` で大まかな型だけ持ち、本文は Markdown 自由記述 |
|
||||
|
||||
- `<slug>` は kebab-case(内容を要約した短い識別子)。**ファイル名そのものが ID**、frontmatter に別途 `id` field は持たない
|
||||
- **1 件 1 ファイル**。append-only な複数エントリログファイルは作らない
|
||||
- **同一 slug の衝突は新規作成禁止**。既存があれば update(Linter で検証、sub-Worker は read→edit に切り替え)
|
||||
- 同主題の content 進化 = 上書き update + git log で履歴追跡
|
||||
- 別主題が古い主題を置き換える場合のみ、別 slug で新規作成し古い方に `status: replaced` + `replaced_by: <新 slug>` を記録
|
||||
- extract の中間ストアとして `memory/_staging/<id>.json` を使う(短命、consolidation 完了で cleanup。ここは衝突回避と順序のため UUIDv7 可)
|
||||
- Raw session log は既存 `session-store` で保持する。memory 対象外、参照経路のみ
|
||||
|
||||
### Knowledge の呼び出し制御
|
||||
|
||||
agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#<slug>` でユーザー / LLM から注入参照する。名前空間は**フラット**、slug は kebab-case(小文字英数とハイフン)。
|
||||
|
||||
| フラグ | 意味 | デフォルト |
|
||||
| ---------------- | ------------------------------------------------------- | ---------- |
|
||||
| `model_invokation` | description が model context に載り、モデルが自発的に参照判断できる | **OFF** |
|
||||
| `user_invocable` | ユーザーが `#<slug>` で明示的に呼べる | **ON** |
|
||||
|
||||
Knowledge は consolidation が自律的に新規作成 / 更新 / フラグ切替を行う前提。毎回の人間承認 gate は設けない(実効性が低い)。保護は 3 段で担保:
|
||||
|
||||
- **採択 gate**: Knowledge 新規作成は使用頻度メトリクスの Knowledge 化候補レポート(後述)に載った source から派生する場合に限る。閾値未満のうちは decisions / requests に留める
|
||||
- **Linter**: 構造違反を watch(詳細は後述)。意味破壊の自動検出は初期は持たず、挙動を見てから監査 LLM 層を追加する(将来検討)
|
||||
- **OS ファイル権限**: 人間が書き換えさせたくない record は `-r--` にしてロック。consolidation の write は OS レベルで弾かれる
|
||||
|
||||
Workflow も同じフラグ仕様(`workflow.md` 参照)。per-record 保護フラグを提供する拡張は将来検討、初期は OS 権限で足りる。
|
||||
|
||||
### retrieval 経路
|
||||
|
||||
Knowledge / memory を LLM に渡す経路は以下で固定。採択基準(次節)と表裏で、引ける前提がないと採択しても無意味になる。
|
||||
|
||||
- **Knowledge 検索ツール**: frontmatter 含めた全文検索。通常 Pod と consolidation Pod の両方に渡す
|
||||
- Input: `query`(自由文字列)。オプションで `slug`(完全一致 1 件返し、`#<slug>` 解決に使う)、`kind` filter
|
||||
- Output: `{ slug, kind, description, model_invokation, excerpt }` の配列。`excerpt` はマッチ箇所の前後数行
|
||||
- 対象は `knowledge/*.md`。派生 index ファイルは持たず実ファイルを都度スキャン
|
||||
- ソートは初期 grep の出現順、FTS / vector 導入時に関連度へ切り替え(将来検討)
|
||||
- ヒット件数上限と excerpt 行数は設定で tune
|
||||
- **memory 検索ツール**: `memory/{summary,decisions,requests}/*.md` 対象。spec は Knowledge 検索ツールと同型。§使用頻度メトリクスの観測経路と同一視する
|
||||
- **更新は memory 専用 Tool + Linter**: Knowledge / memory への write/edit は `memory` クレートが提供する専用 Tool 経由のみ。汎用 CRUD(`tools` クレートの Write/Edit)は memory/knowledge 配下を触らない(Pod が Scope で deny)。Linter は memory tool 内で pre-write 検証として走り、違反は `ToolError::InvalidArgument` で LLM に返る。詳細は §書き込み経路と Linter
|
||||
- **常駐注入**: メモリを消費する主体は通常 Pod。`model_invokation: ON` な record の description を通常 Pod の system prompt に常駐注入する。consolidation prompt には入れない
|
||||
- 予算はシステムプロンプト全体の予算に含める(`memory_summary.md` の 5k 枠とは別管理にしない)
|
||||
- 超過時の件数キャップ / 優先順位ルールは、description 1024 chars 上限で通常は収まる前提。ON record 数が増えたら追加する
|
||||
- **consolidation の Knowledge アクセス**: 全 Knowledge 本文を prompt に埋めず、Knowledge 検索ツール + memory 専用 Tool を agent に渡して自律探索させる(詳細は §Consolidation)
|
||||
- **`#<slug>` 補完 / 自動呼び出し(大枠のみ、実装は段階的)**:
|
||||
- `#<slug>` は検索ツールの slug 完全一致経路で本文が展開される
|
||||
- 補完 UI(slug サジェスト)は TUI 側。`user_invocable: false` は候補除外
|
||||
- 自動呼び出しは、常駐注入された description をモデルが見て必要と判断すれば検索ツールを呼ぶ形で成立する。専用の auto-invoke 経路は別途用意しない
|
||||
|
||||
### Knowledge の採択基準
|
||||
|
||||
Knowledge は「保存する価値があるか」だけでなく、「あとで見つけて再利用できるか」で評価する。最低限の基準は以下:
|
||||
|
||||
- **slug は入口**。短く、何の知識か推測でき、`#<slug>` や検索で指名しやすいものを優先する
|
||||
- **description は discovery 面そのもの**。本文の要約ではなく、「何の知識で、どんな時に読むべきか」を短く示す
|
||||
- **1 file = 1 主題**。description だけで対象範囲が分かる粒度に寄せる。細分化しすぎる slug 乱立も避ける
|
||||
- **update 優先**。新情報は既存 slug に畳み込み、自然な適合先がない時だけ新規 slug を作る
|
||||
- **昇格ライン**は「このプロジェクト / ユーザーで再度参照する価値のある事実・ルール・ノウハウ」。一回限りの判断や議論は decisions / requests に留める
|
||||
- **`model_invokation` ON は別判断**。重要度だけでなく、description だけで「どんな時に読むべきか」が伝わるものに限る
|
||||
|
||||
### 書き込み経路と Linter
|
||||
|
||||
`memory/*` / `knowledge/*` への write/edit は **`memory` クレート提供の memory 専用 Tool(read / write / edit)** に集約する。汎用 CRUD(`tools` クレートの Write/Edit)はこれらのディレクトリに触らない(Pod が Scope で deny に落とすことで構造的に担保)。
|
||||
|
||||
Linter は memory tool 内で **pre-write 検証** として走り、違反は `ToolError::InvalidArgument` で返す。LLM は通常の tool error フローで違反内容を読み、自己修正する。Interceptor 拡張・retry message 注入・違反カウンタは持たない(worker 層の max iteration が暴走を止める)。Linter は frontmatter / slug / 参照整合などの機械的ルールを見る。
|
||||
|
||||
人間編集(エディタ / git commit)は tool 層を経由しないため Linter を通らない。`memory::Linter` は pure 関数として export し、CLI / pre-commit hook 経路を後で別チケットで用意する。
|
||||
|
||||
意味破壊(rewrite で既存の主張・根拠が落ちる、Knowledge の記述主題がズレる等)の自動検出は初期範囲に含めない。consolidation prompt 側の情報損失最小化指示と git diff レビューで運用し、実使用で顕在化したら監査 LLM 層を後から挟む(将来検討)。
|
||||
|
||||
Linter ルールは 2 系統:
|
||||
|
||||
**静的 error**(memory tool が `ToolError::InvalidArgument` で返し、LLM が tool error フローで自己修正):
|
||||
|
||||
- frontmatter 必須 field
|
||||
- Decisions / Requests: `created_at`, `updated_at`, `sources`
|
||||
- Knowledge: `kind`, `description`, `model_invokation`, `user_invocable`, `last_sources`, `created_at`, `updated_at`
|
||||
- Summary: `updated_at`(optional: `last_rewritten_from_range`)
|
||||
- Workflow パス(`.yoi/workflow/`)への書き込み禁止(sub-Worker context のみ、人間編集は除外)
|
||||
- 同 slug での新規作成禁止(既存があれば update に切り替えるサイン)
|
||||
- `#<slug>` 参照が実在ファイルを指す
|
||||
- `replaced_by: <slug>` が実在 record を指す
|
||||
- Decisions の `status` は enum `open | resolved | replaced`
|
||||
- `model_invokation: true` の record は description 文字数上限(agentskills 準拠 1024 chars)
|
||||
- 種別ごとの char 硬上限(具体値は運用で調整、設定ファイルで tune)
|
||||
|
||||
**膨張抑制 Warn**(error ではなく改善ヒント、sub-Worker は task 余力があれば対応):
|
||||
|
||||
- 重要度 × char の天秤: 低重要度で char 使用過多な record に「圧縮余地あり」Warn
|
||||
- sources 累積: 配列が閾値超過で「直近 N 件に絞り過去は git log に委ねる候補」Warn
|
||||
- 類似 slug 乱立: 近似 slug の集合に「merge 候補」Warn
|
||||
|
||||
Workflow 保護は専用 tool schema のトリックではなく Linter ルールで担保するため、人間が規則を読んで理解できる。OS ファイル権限や Scope 上の特別な保護機構は設けず、`memory/` 配下を write allow する以上の細工はしない。衝突は git で解決する前提。
|
||||
|
||||
### 自動化(extract / consolidation)メカニズム
|
||||
|
||||
**extract / consolidation の 2 段構成**。extract は頻繁に発火して活動を raw event として抽出、consolidation は蓄積時のみ発火して永続化形式に統合する。参考: Codex Memories の extract/consolidation 構造。
|
||||
|
||||
#### Extract: 活動抽出
|
||||
|
||||
- **Trigger**: activity tokens の累積閾値(cumulative input tokens since last pointer)。tool call カウントは不採用(ツールカスタマイズ非依存・大小重みづけのため)
|
||||
- **実行主体**: 既存 compact と同じ Worker spawn 機構を再利用。Pod は立てない
|
||||
- **入力**: 前回 extract 以降の session log 範囲。処理済み境界の pointer は session log 側に保持し、寿命を session と揃える。session-store のドメイン純度を保つため、汎用拡張点 `LogEntry::Extension { domain, payload }`(domain = `"memory.extract"`)に寄せ、session-store は memory ドメインを知らない。Tool result は raw `content` ではなく表示用 `summary` だけを render し、巨大な tool output を extract input に載せない
|
||||
- **出力**: JSON schema で**活動ログ**の候補配列を返す。Knowledge 等の派生物は consolidation が活動ログから導出するので、extract では純粋な「起きたこと」に絞る
|
||||
- `decisions`: 判断したこと(選択肢 + 選んだ + 根拠)
|
||||
- `discussions`: 議論したこと(トピック + 論点)
|
||||
- `attempts`: 試したこと(試行 + 結果 + 成否)
|
||||
- `requests`: ユーザー submit の構造化要約(意図 / 対象 / 要約)
|
||||
- **抽出対象がなければ空配列を返してよい**(Hermes の "Nothing to save." と同系。頻繁発火を許容する前提)
|
||||
- **書き込み先**: `memory/_staging/<id>.json`
|
||||
- LLM 出力(活動ログ JSON)は pod 側ラッパーが `source: { session_id, range: [start_entry, end_entry] }` を**機械付与**して wrap。LLM には source を推論させない
|
||||
- **実行保証**: extract worker 自身の input occupancy cap は設けない。未処理 range が大きい場合でも pointer 以降の最大範囲を渡し、LLM/API/tool failure のときだけ pointer を進めない
|
||||
- **モデル**: `memory.extract_model`。軽量だが文脈理解できる中堅クラス(Haiku / 4o-mini / Flash 相当)を想定
|
||||
- **Compact との順序**: 同一 turn 完了後の post-run チェックで extract を **compact より前** に走らせる。compact は history を組み替えるので、extract の入力範囲(session log 上の entry index)は compact 前のほうが安定する
|
||||
- **並走防止 (extract 同士)**: Pod 上の `extract_in_flight` フラグで in-flight 中の新規 trigger を skip。完了時点で閾値超過していれば直ちに次回を発火し、新 pointer 以降の最大範囲を回収する(pending 状態は保持しない=完了時の閾値再評価で coalesce 相当の挙動を成立させる)
|
||||
|
||||
#### Consolidation: 永続化への統合 + 整理
|
||||
|
||||
- **Trigger**: staging の累積ファイル数 or bytes が閾値超過
|
||||
- **実行主体**: extract を終えた pod が consolidation Worker を spawn。並走防止は staging 配下の進行状況ファイル(後述)で担保
|
||||
- **入力**: 起動時スナップショットで確定した consumed ID list 分の staging エントリ(活動ログ + `source`)+ 既存 `memory/*`(summary / decisions / requests)の全文 + **Knowledge 化候補レポート**(後述の使用頻度メトリクスから機械集計、閾値超過の source 一覧)+ **整理材料**(明示 invoke の使用頻度メトリクス、Linter Warn、`replaced` chain、sources 過多情報)。既存 `knowledge/*` は全文を prompt に埋めず、Knowledge 検索ツール経由で agent が必要分を引く
|
||||
- **処理**: sub-Worker に **memory 専用 Tool(read / write / edit、Linter 内蔵)+ Knowledge 検索ツール + memory 検索ツール** を渡し、agentic に以下を自律判断:
|
||||
- **統合**: 新規 decisions / requests を 1 件 1 ファイルで追加。`sources` は staging の `source` をコピー(LLM 推論ではない)
|
||||
- 活動ログから派生する Knowledge(用語定義 / 運用方針 / ルール / 事実 / ノウハウ)を新規作成 or 既存 patch。**新規作成は候補レポート掲載の source から派生する場合に限る**。`kind` を frontmatter に持ち、`last_sources` を更新
|
||||
- summary を必要に応じて rewrite
|
||||
- **整理(余力 step)**: 既存 record 群を §評価カテゴリ で評価し、保護閾値外の対象を drop / merge / split / trim / rewrite。Linter Warn で検出した類似 slug 乱立 / sources 過多 / `replaced` 滞留はここで収斂させる
|
||||
- **書き込み先**: `memory/*` と `knowledge/*`。Workflow 禁止は Linter で担保(`workflow.md` 参照)
|
||||
- **完了処理**: consumed ID list の staging のみ cleanup(実行中に extract が追加した分は残す)。consolidation 完了時に staging に新着があれば次を発火(Coalesce)
|
||||
- **モデル**: `memory.consolidation_model`。reasoning 系
|
||||
|
||||
##### 並走防止
|
||||
|
||||
- 場所: staging 配下に 1 ファイル(名前・形式は未定)
|
||||
- 中身: 動作中の Pod 識別子 + **consumed ID list**(この consolidation run が起動時スナップショットで確定した staging エントリ ID の列)
|
||||
- 占有ルール: そのファイルが存在し、示された Pod が動作している間、そのプロセスが排他占有
|
||||
- 実行中に extract が staging に追加したエントリは触らず、次回 consolidation(Coalesce)に委ねる
|
||||
- cleanup は consumed ID list のエントリのみ削除、追加分は残す
|
||||
- クラッシュ時は consumed ID list から処理途中を特定できる。重複作成は同一 slug update に自然収束
|
||||
- 占有の実現方法(pid 存在確認 / flock / 他)は未定
|
||||
|
||||
#### consolidation agent への原則
|
||||
|
||||
`memory/` 配下は人間も git 経由で編集する。consolidation prompt で以下を明示:
|
||||
|
||||
- **rewrite は許可**。既存内容と新規情報を統合・再構成して情報密度を上げることを優先。単純 append(追記で増やすだけ)は避ける
|
||||
- rewrite 時は**情報損失を最小化**する: 既存の主張・根拠・sources を保持。表現を整理・短縮しても、含まれている要素は落とさない
|
||||
- Decision の置き換えは `status: replaced` + `replaced_by: <slug>` で表現、直接削除しない
|
||||
- 整理 step での drop は許可。ただし保護閾値(§判断ルール)超過 record は drop / 大幅圧縮の対象外。誤判定しやすいものは merge / trim を優先
|
||||
- 各 record の整理理由は `outdated | superseded | unused | noisy` の §評価カテゴリ で説明可能にし、git diff から読み取れる粒度の操作にする
|
||||
- Knowledge は既存 record 群の slug / description / kind / `model_invokation` を入口に適合先を探し、自然に統合できるなら新規 slug を増やさない
|
||||
- 人間編集は git diff で顕在化する前提。整合しない rewrite は避け、衝突時は git で解決
|
||||
|
||||
#### Offer 経路
|
||||
|
||||
Memory record の書き込みは consolidation が自律判断し、Offer は設けない(Knowledge 含む)。人間承認経路が必要なのは以下:
|
||||
|
||||
- Workflow 関連の offer(新規作成 / 改善 / `model_invokation` ON 化)は `workflow.md` 参照
|
||||
|
||||
#### Compact との関係
|
||||
|
||||
基本分離(memory は独立トリガー、compact は `input_tokens` 既存閾値のまま)。compact で失われる session log の raw は **extract が compact より前に走ることで staging に保全**される(§Extract §Compact との順序 参照)。consolidation を compact に同期させる義務はなく、staging 累積閾値で独立に発火する。
|
||||
|
||||
### 整理(GC 相当)の扱い
|
||||
|
||||
consolidation は rewrite 許可で情報統合寄りの働きをするが、それでも残る以下の課題は **consolidation の余力 step で同じ agent が処理**する(独立 trigger / 独立 Agent は持たない):
|
||||
|
||||
- 重要度の低い record が累積する
|
||||
- 類似 slug が乱立する(Linter Warn で検出したものをまとめて処理)
|
||||
- `replaced` が溜まり続けて grep / 注入時のノイズになる
|
||||
- sources 累積
|
||||
- 現状と不整合になった record、実質的に置き換え済みの record、使われていない record、形がノイズ化した record の整理
|
||||
|
||||
他プロジェクトの GC 設計の横断比較は `docs/ref/memory-systems.md` §8。
|
||||
|
||||
#### 操作粒度
|
||||
|
||||
整理 step は consolidation 統合 step と同じ memory 専用 Tool(read / write / edit、内部で pre-write Linter)を使う。operation 粒度は自然にサポートされる(専用 API は用意しない):
|
||||
|
||||
- **ファイル単位**: 丸ごと drop、複数ファイルの merge、1 ファイルの分割(split)
|
||||
- **ファイル内の部分削除**: 本文の一部節・箇条を削除 or 圧縮。frontmatter の `sources` 古いエントリの trim も含む
|
||||
|
||||
#### 評価カテゴリ
|
||||
|
||||
整理対象 record は一律に「stale」とみなさず、少なくとも次の 4 カテゴリで評価する:
|
||||
|
||||
- `outdated`: 以前は妥当だったが、現在の実装・方針・運用と不整合になっている
|
||||
- `superseded`: 別 record が実質的な正本になっており、元の record は置き換え済みに近い
|
||||
- `unused`: 誤りではないが、明示 invoke や検索でほとんど参照されずノイズ化している
|
||||
- `noisy`: 内容自体は有効でも、粒度・重複・冗長さ・sources 過多などで discovery / retrieval を悪化させている
|
||||
|
||||
これらは **保護条件ではなく整理理由の分類**。保護条件は別に持ち、その上で `drop / merge / split / trim / rewrite` のどれを選ぶかをこのカテゴリで説明可能にする。
|
||||
|
||||
#### 使用頻度メトリクス
|
||||
|
||||
時間単位は実時間を使わない(LLM スループット向上で陳腐化の意味が変わるため)、累積 input token で正規化する。
|
||||
|
||||
**観測経路**: `memory/*` / `knowledge/*` への読み取りは §retrieval 経路 で定義した memory 検索ツール / Knowledge 検索ツール(既存 built-in の grep / read とは別に用意)経由に揃える。invoke 計測はツール内でフックし、`#<slug>` / `/<slug>` / 明示検索呼び出しを同一経路に集約する。
|
||||
|
||||
**カウント対象**:
|
||||
|
||||
- **明示 invoke**: 検索ツール経由の読み取り / `#<slug>` / `/<slug>` を n回/Mtoken でスコア化
|
||||
- **`model_invokation` 注入**: 注入は context 常駐コストで、「載っているだけ」か「使われた」かを統計上区別不能。明示 invoke の分子には含めず、**コスト側(注入した record に対する消費 input tokens)として別途記録**する。使われ率 ratio や ON/OFF 判断の材料として後段で使う
|
||||
- ファイル token 数
|
||||
|
||||
**記録先**: staging とは独立。invoke event を UUID + Stats 形式で workspace 側に記録し、session データが失われても統計が残るようにする。具体 schema・フォーマットは未定。
|
||||
|
||||
**累積方式**(後集計アプローチ): 上記 invoke 記録に対して最大 10 回前の invoke から現在までの時系列窓でフィルタして集計する。
|
||||
|
||||
**Knowledge 化候補レポート**: consolidation 統合 step が入力に受け取る、Knowledge 新規作成 gate 用の機械集計。対象は `memory/*` 配下の record(extract 成果物である decisions / requests / 既存 knowledge)で、明示 invoke 頻度が閾値超過のものを列挙する。spike 除外のため、同一 session 内の連続参照は 1 count に丸め、複数 session での再参照を要件とする。閾値の具体値は運用で調整、設定ファイルで tune。
|
||||
|
||||
#### 判断ルール
|
||||
|
||||
- 保護閾値: **明示 invoke** の `frequency >= 1.0 invokes/Mtoken` の record は drop / 大幅圧縮の対象外(初期値 1.0、workspace 設定でカスタマイズ可)。`model_invokation` 注入による常駐は計数対象外(別指標として後段で参照)
|
||||
- 整理 step の評価カテゴリは `outdated | superseded | unused | noisy` を使う。単一 record が複数カテゴリに該当してもよい
|
||||
|
||||
### ファイル形式
|
||||
|
||||
- frontmatter + Markdown 本文。全 record 共通: `created_at`, `updated_at`
|
||||
- ファイル名(slug)がそのまま識別子、frontmatter に `id` / `name` field は持たない
|
||||
- source トレーサビリティ(session log への逆引き、粒度は `session_id` + entry range):
|
||||
- Decisions / Requests: `sources: [{session_id, range: [start, end]}, ...]` 永続化(update 時は追記累積)
|
||||
- Knowledge: `last_sources: [{session_id, range}, ...]`(最新更新時のみ、過去履歴は git log で追う)
|
||||
- Summary: optional `last_rewritten_from_range`(なしでも可)
|
||||
- Knowledge 固有: `kind`, `description`, `model_invokation`, `user_invocable`
|
||||
- Knowledge の保存先は `knowledge/<slug>.md`。`memory/` とは兄弟ディレクトリに分ける
|
||||
- Decisions 固有: `status: open | resolved | replaced`、置き換え時は `replaced_by: <slug>`
|
||||
- extract staging: `memory/_staging/<id>.json`(JSON、1 件 1 ファイル、consolidation 完了で削除。短命なので UUIDv7 可)。pod 側ラッパーが `source` を機械付与して LLM 出力と wrap
|
||||
- Workflow の frontmatter は `workflow.md` 参照
|
||||
|
||||
## Scope 外
|
||||
|
||||
- ネットワーク越境での memory 同期 — `network-peering.md` で扱う範疇。本設計はプロジェクトスコープ固定
|
||||
|
||||
### 将来検討(運用で必要性が見えたら追加)
|
||||
|
||||
- 監査 LLM 層(意味破壊検出)の導入 — 初期は静的 Linter のみで運用し、consolidation の rewrite で情報損失・主題ズレが実運用で顕在化したら memory tool 内の検証パイプラインに 2 層目として追加。入力 / check 項目 / pass-fail 返却形式は導入時に詰める
|
||||
- Vector index / FTS5 等の検索索引 — 初期は grep で足りる想定。ファイル数増加で検索が重くなったら検討
|
||||
- `model_invokation` offer の自動判定ロジック — 初期は人間が手動で切り替え
|
||||
- 過去 session を cross-session で検索する UI
|
||||
- consolidation を担う常駐 daemon 化 — オンデマンド + lock 方式で始める。必要性が出たら upgrade path として daemon 化
|
||||
- Deterministic promotion(OpenClaw 型 scoring + ゲート)— 初期は consolidation agent の LLM 判断に委ねる。運用実績で出力を評価してから、成熟カテゴリから scoring 導入
|
||||
- Shallow request の自動除外判定 — 初期は extract prompt で「些細な質問は返さなくてよい」と指示する程度。精緻な filter は後
|
||||
|
||||
### 別 plan / ticket で扱う
|
||||
|
||||
- 具体クレート構成・API 境界
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# マルチエージェントを統括するインタフェース・ワークフロー・UI/UXの設計
|
||||
|
||||
基本的には [./ai-maintainer.md] を用いて使うものと考えている
|
||||
|
||||
ワークスペース上のすべてのPodをリスト化し、リストから直に各Podにアタッチし、進捗を確認可能にする。
|
||||
|
||||
とか言ってたら
|
||||
May 11, 2026にAnthropicからなんか出てた↓
|
||||
[https://claude.com/blog/agent-view-in-claude-code]
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
# ネットワーク越しの Pod 協働(将来設計ノート)
|
||||
|
||||
本ドキュメントは将来の設計方針を記録するものであり、実装チケットではない。
|
||||
|
||||
---
|
||||
|
||||
## 動機
|
||||
|
||||
ローカルの workspace が Pod 間の発見・通知・scope 会計を提供するが、
|
||||
これは 1 台のマシンに閉じている。複数マシンにまたがるプロジェクト
|
||||
(モノレポの異なる部分を別マシンで触る、CI マシンの Pod と開発マシンの
|
||||
Pod が連携する等)では、マシン間のメッセージングが必要になる。
|
||||
|
||||
## 設計原則
|
||||
|
||||
- **中央サーバーを作らない**。各マシンが主権を持ち、P2P でやり取りする
|
||||
- **ローカル workspace を歪めない**。ネットワーク層は workspace の上に
|
||||
乗る「他の workspace への郵便」であり、workspace 自体を分散化しない
|
||||
- **ファイルシステムはマシンローカル**。cross-host の scope 分譲は
|
||||
行わない。協働はメッセージベースのタスク委譲で完結する
|
||||
- **SSH を transport に使う**。開発者の手元に既にあるインフラで、
|
||||
鍵管理・暗号化・認証が追加投資なしで手に入る
|
||||
|
||||
## アドレッシング
|
||||
|
||||
Pod のネットワークアドレスは `yoi.pod-name@host` の形式を**論理的な
|
||||
宛先表記**として使う。実際の SSH 接続がこの文字列そのままで行えるかは
|
||||
transport 方式に依存する(後述)。
|
||||
|
||||
- `yoi` = SSH ユーザー名(固定)
|
||||
- `host` = 相手マシンのホスト名 or IP
|
||||
- `pod-name` = 送信先の Pod 名(相手マシン上のローカル workspace 内で一意)
|
||||
|
||||
推奨構文は **`yoi@host:pod-name`**(git 方式)。
|
||||
詳細は後述の「アドレッシング構文」を参照。
|
||||
|
||||
## アドレッシング構文
|
||||
|
||||
論理的な宛先表記として **`yoi@host:pod-name`** を推奨する。
|
||||
git の `git@github.com:user/repo` と同じ構文で:
|
||||
|
||||
- SSH ユーザーは `yoi` 固定(動的ユーザー名が不要)
|
||||
- `:` 以降がルーティング情報(Pod 名)
|
||||
- クライアント側が `yoi@host:pod-name` をパースし、
|
||||
`ssh yoi@host "yoi-route pod-name"` に変換する
|
||||
|
||||
git がこの方式で `git-upload-pack user/repo` にルーティングしている
|
||||
のと同じ仕組み。OS レベルの設定(NSS モジュール等)が一切不要で、
|
||||
ユーザー 1 つ + ForceCommand(またはシェルスクリプト)だけで動く。
|
||||
|
||||
## SSH transport の選択肢
|
||||
|
||||
### A. 単一ユーザー + コマンド引数
|
||||
|
||||
```
|
||||
ssh yoi@host send pod-name "message"
|
||||
```
|
||||
|
||||
- 相手マシンにシステムユーザー `yoi` を 1 つ作る
|
||||
- `authorized_keys` に接続元 Pod の公開鍵を登録
|
||||
- ForceCommand または shell スクリプトが第一引数 (`send`) と
|
||||
第二引数 (`pod-name`) を解釈してローカル workspace のレジストリから
|
||||
Pod の socket を引き、メッセージをルーティング
|
||||
- **導入コスト最低**。ユーザー 1 つ + スクリプト 1 つで動く
|
||||
- 宛先が引数に入るので `yoi.pod-name@host` の見た目にはならない
|
||||
|
||||
### B. 鍵ベースルーティング(gitolite 方式)
|
||||
|
||||
```
|
||||
ssh yoi@host # 使った鍵でどの Pod 宛か判別
|
||||
```
|
||||
|
||||
- `~yoi/.ssh/authorized_keys` に Pod ごとのエントリ:
|
||||
```
|
||||
command="yoi-route pod-a",no-port-forwarding,... ssh-ed25519 AAAA... pod-a@remote
|
||||
command="yoi-route pod-b",no-port-forwarding,... ssh-ed25519 AAAA... pod-b@remote
|
||||
```
|
||||
- SSH 接続時に使われた鍵が `command=` で指定されたルーティング先を決定
|
||||
- gitolite / Gitea / Gogs で実証済みのパターン
|
||||
- 接続元は `ssh yoi@host` だけ。**鍵が宛先を決める**
|
||||
- クライアント側 SSH config で alias を作れば見た目を整えられる:
|
||||
```
|
||||
Host pod-a.host-b
|
||||
HostName host-b
|
||||
User yoi
|
||||
IdentityFile ~/.config/yoi/keys/pod-a
|
||||
```
|
||||
- 鍵の登録が相互に必要(Pod A が Pod B に送るなら、B のマシンの
|
||||
authorized_keys に A の公開鍵 + route 先を登録)
|
||||
|
||||
### C. 動的ユーザー名
|
||||
|
||||
```
|
||||
ssh yoi.pod-name@host
|
||||
```
|
||||
|
||||
- `yoi.pod-name` を OS レベルで有効なユーザー名として解決する:
|
||||
- NSS (Name Service Switch) モジュールを書くか `libnss-extrausers` を利用
|
||||
- PAM モジュールで認証をフック
|
||||
- `sshd_config` で `Match User yoi.*` → `ForceCommand` でルーティング
|
||||
- **最も直感的なアドレッシング**だが OS レベルの設定が必要
|
||||
- yoi をインストールするだけでは動かない(管理者権限での設定が要る)
|
||||
- コンテナ環境ではやりやすい(ユーザー管理を自由にできる)
|
||||
|
||||
### 推奨
|
||||
|
||||
**MVP は A(単一ユーザー + コマンド引数)** から始める。設定が最小で
|
||||
動くものが作れる。将来 B(鍵ベースルーティング)に進化させると
|
||||
セキュリティが強化される(Pod 単位での接続制御が可能)。
|
||||
C は UX は最高だが導入コストが高く、必要が明確になるまで見送る。
|
||||
|
||||
## ファイルシステムの境界
|
||||
|
||||
Pod はローカルのファイルシステムだけを操作する。ネットワーク越しの
|
||||
Pod 協働では:
|
||||
|
||||
- **scope はマシンローカルに閉じる**。host_a の `/src` と host_b の
|
||||
`/src` は別物。cross-host の scope 分譲は行わない
|
||||
- **協働はタスク委譲**。「このリポジトリでこの変更をして結果を教えて」
|
||||
というメッセージで、相手の Pod がローカルに実行する
|
||||
- **sshfs / NFS 等のネットワークファイルシステムは使わない**。
|
||||
透過的なリモートファイルアクセスは壊れやすく、scope モデルと相性が悪い
|
||||
|
||||
## ローカル workspace との関係
|
||||
|
||||
| 層 | スコープ | 役割 |
|
||||
|---|---|---|
|
||||
| Workspace | 1 台のマシン | Pod 発見 / scope 会計 / 通知バス |
|
||||
| Network peers | マシン間 | メッセージング / タスク委譲 / 結果通知 |
|
||||
|
||||
- Network peers は workspace の**上に**乗る。workspace を置き換えない
|
||||
- ローカルの Pod 間通信は引き続き workspace の Unix socket バスを使う
|
||||
- リモートの Pod への通信だけ SSH transport を経由する
|
||||
- Pod から見ると「ローカル peer に送る」と「リモート peer に送る」は
|
||||
同じ API で、transport 層が切り替わるだけ(が理想)
|
||||
|
||||
## broadcast の扱い
|
||||
|
||||
ローカル workspace は Unix socket で 1 本の bus を持てたが、
|
||||
ネットワーク越しには共有 bus が無い。
|
||||
|
||||
- 各マシン(または各 Pod)が **known-peers リスト** を持つ
|
||||
- broadcast = known-peers を iterate して個別送信
|
||||
- 規模が数十 Pod なら十分実用的
|
||||
- 将来的に gossip protocol で peer 発見を自動化できるが、
|
||||
MVP では手動登録(`yoi peer add pod-a@host-b`)で十分
|
||||
|
||||
## 受信側のルーティング
|
||||
|
||||
SSH 接続を受けた側が、宛先 Pod のローカル socket にルーティングする
|
||||
仕組みが必要。
|
||||
|
||||
```
|
||||
[SSH 接続] → yoi-route <pod-name>
|
||||
↓
|
||||
workspace registry を参照
|
||||
↓
|
||||
/run/yoi/.../pod-name.sock に転送
|
||||
↓
|
||||
Pod が受信・処理
|
||||
```
|
||||
|
||||
`yoi-route` は:
|
||||
1. workspace のレジストリを読んで pod-name の socket path を引く
|
||||
2. socket に接続してメッセージを中継
|
||||
3. 応答を SSH 接続に返す
|
||||
|
||||
workspace が複数ある場合のルーティング(どの workspace の
|
||||
pod-name か)は追加の設計が必要。Pod 名がマシン上で globally
|
||||
unique であれば workspace を指定しなくて済む。
|
||||
|
||||
## Daemon-less リモート Pod 生成(SSH-only モデル)
|
||||
|
||||
リモートホスト上の Pod 生成は **daemon 無しで SSH だけで成立する**。
|
||||
remote 側に必要なのは `yoi` バイナリと SSH アクセスのみ。
|
||||
|
||||
### 前提
|
||||
|
||||
- yoi は環境再現(git clone, コンテナ構築等)を自身の責務としない。
|
||||
作業対象のファイルがリモートに既にあるか、ユーザーが任意の手段で
|
||||
用意する前提(git clone, rsync, 手動配置、CI の checkout 等)
|
||||
- yoi が転送するのは**セッション(会話履歴)と manifest overlay**
|
||||
だけ。コードベースの同期は外部に委ねる
|
||||
- コンテナ内で動かすか bare metal で動かすかも yoi は問わない。
|
||||
`yoi` バイナリが動くホストの fs 上で活動する主体がある、
|
||||
それだけが前提
|
||||
|
||||
### フロー
|
||||
|
||||
```
|
||||
host_a (spawner) host_b (remote)
|
||||
Pod A (pod binary + ssh のみ)
|
||||
│
|
||||
├── ssh: session データを転送 ────────→ ファイル書き込み
|
||||
├── ssh: profile / one-file manifest 入力を転送 ─→ 必要ならファイル書き込み
|
||||
├── ssh: `yoi pod --profile ... &` ───────→ Pod プロセス起動、socket 作成
|
||||
├── ssh -L: socket を tunnel ─────────→ Pod B の unix socket
|
||||
│
|
||||
└── localhost:tunnel に接続 ──────────→ Method::Run / Event stream
|
||||
(以降はローカル Pod と同じ protocol)
|
||||
```
|
||||
|
||||
### コマンドイメージ
|
||||
|
||||
```bash
|
||||
# 1. session + profile/manifest input を転送
|
||||
ssh yoi@host-b "mkdir -p ~/workspaces/task-123/store"
|
||||
tar cz session/ | ssh yoi@host-b "tar xz -C ~/workspaces/task-123/store"
|
||||
scp profile.lua yoi@host-b:~/workspaces/task-123/profile.lua
|
||||
|
||||
# 2. Pod を起動(detach)
|
||||
ssh yoi@host-b "yoi pod --store ~/workspaces/task-123/store \
|
||||
--profile ~/workspaces/task-123/profile.lua &"
|
||||
|
||||
# 3. socket を tunnel で引っ張る
|
||||
ssh -L /tmp/pod-b.sock:/run/yoi/task-123/pod.sock yoi@host-b
|
||||
|
||||
# 4. あとは /tmp/pod-b.sock にローカルと同じ protocol で繋ぐ
|
||||
```
|
||||
|
||||
spawner の `SpawnPod` ツールがこの一連を内部で実行する。LLM から
|
||||
見たら「ツールを呼んだら Pod ができた」だけ。
|
||||
|
||||
### なぜこれで足りるか
|
||||
|
||||
- **protocol は変わらない**: SSH tunnel の向こう側は普通の Pod socket。
|
||||
ローカルの Pod と同じ `Method` / `Event` でやり取りする
|
||||
- **scope は host ごとに独立**: cross-host の scope 分譲はそもそも
|
||||
成立しないので、workspace の scope 会計は remote には関係しない
|
||||
- **通知**: SSH tunnel が繋がっている限り `Event` stream がそのまま
|
||||
流れる。tunnel が切れたら再接続する
|
||||
- **環境構築は yoi の責務外**: git clone するか rsync するかは
|
||||
Pod の instruction で指示するか、事前に用意されている前提
|
||||
|
||||
### daemon が必要になるケース
|
||||
|
||||
SSH-only モデルの制約が、daemon 導入の動機になる:
|
||||
|
||||
- **Pod 一覧の取得**: remote の runtime_dir を SSH 越しに `ls` する
|
||||
必要がある(daemon がいればレジストリ API で済む)
|
||||
- **Pod の生存監視**: tunnel が切れたら再接続するまで状態不明
|
||||
(daemon がいれば health check を引き受ける)
|
||||
- **複数の spawner が同じ remote Pod に繋ぐ**: tunnel の共有が面倒
|
||||
(daemon がいれば multiplexing できる)
|
||||
- **workspace サービス(registry / 通知バス)の remote 提供**:
|
||||
SSH-only モデルではリモート側に workspace サービスが無い
|
||||
|
||||
これらは **MVP では問題にならず**、daemon は「便利にしたくなった
|
||||
ときの upgrade path」として位置づける。
|
||||
|
||||
### リモート側のディレクトリ構成
|
||||
|
||||
```
|
||||
/home/yoi/ ← yoi システムユーザーの home
|
||||
├── workspaces/
|
||||
│ ├── <task-or-project-id>/ ← workspace ごとのルート
|
||||
│ │ ├── repo/ ← ユーザーが用意した作業ファイル群
|
||||
│ │ └── store/ ← session store(spawner から転送)
|
||||
│ └── ...
|
||||
└── .ssh/
|
||||
└── authorized_keys ← 接続元 Pod の公開鍵
|
||||
```
|
||||
|
||||
- `yoi` システムユーザーが SSH 接続先 + ファイル所有者
|
||||
- `repo/` 配下の準備は yoi の責務外(git clone, rsync 等は
|
||||
ユーザーや instruction が指示)
|
||||
- `store/` は spawner がセッションデータを書き込む場所
|
||||
|
||||
## セキュリティの考慮
|
||||
|
||||
- SSH の鍵認証がベースライン。パスワード認証は使わない
|
||||
- Pod 単位の鍵ペアにより、接続制御の粒度を Pod レベルにできる(B 方式)
|
||||
- `authorized_keys` の `command=` で実行可能な操作を制限できる
|
||||
(`no-port-forwarding`, `no-pty` 等)
|
||||
- 将来的に Pod 間の trust relationship を定義する仕組みが要るが、
|
||||
それは本ドキュメントの範囲外
|
||||
|
||||
## 未解決の論点
|
||||
|
||||
- SSH transport の具体的な message protocol(JSON-RPC? 独自? protocol crate の拡張?)
|
||||
- 非同期メッセージの扱い(相手が offline のとき queue するか、fail するか)
|
||||
- peer 登録の自動化(workspace 内の Pod が自動で peer list を共有する等)
|
||||
- workspace が複数ある環境での pod-name 解決
|
||||
- Pod の migration(あるマシンから別のマシンへ Pod を移す)の可能性と scope の扱い
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
完全なサンドボックスを求めるなら、Dockerコンテナ内で動かすのが一番
|
||||
|
||||
---
|
||||
|
||||
コード実行環境なら↓
|
||||
Denoが出してたサンドボックス -> https://deno.com/deploy/sandbox
|
||||
Firecracker microVM で作ってるっぽい。
|
||||
|
||||
---
|
||||
|
||||
エージェントをサンドボックスに転送する場合、ディレクトリ全体をマウントして動かすか?Podもその内部で動作させるか?
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
# 複数ツール動的読み込み機構の設計
|
||||
|
||||
## Context
|
||||
|
||||
Yoi はエージェントが扱うツール数の増加 (built-in tools + MCP サーバ + ユーザ定義) を想定する必要がある。すべてを upfront に context へ展開すると以下が問題になる:
|
||||
|
||||
- **入力トークン消費**: 30-50 ツールで 10-20K tokens を消費しうる (Anthropic 公式ガイド)
|
||||
- **ツール選択精度の低下**: 数十個を超えるとモデルの tool selection accuracy が落ちる
|
||||
- **KV cache 効率**: ローカル推論では prefill コストが重く、prefix が動くと再計算が走る
|
||||
|
||||
Claude Code が採用する deferred tools 機構 (`docs/ref/claude-code-deferred-tools.md`) と OpenAI Harmony のアプローチ (`docs/ref/tool_approach_comparison.md`) を比較すると、**全モデルで同じ deferred 方式は通用しない**。モデルファミリごとに戦略を切り替えられる抽象が必要。
|
||||
|
||||
## 決定事項
|
||||
|
||||
### 二層分離: Registry / ContextRenderer
|
||||
|
||||
ツール抽象を以下の二層に分ける。両者の責務を厳密に切り離す。
|
||||
|
||||
| 層 | 責務 | 単一の真実 |
|
||||
|---|---|---|
|
||||
| Registry | ツール実装・schema・名前解決・引数バリデーション | 全ツール常時登録 |
|
||||
| ContextRenderer | モデルへ渡す prompt にどの tool 定義を、どの形式で、どこに置くか | モデル戦略ごとに差し替え |
|
||||
|
||||
**重要**: バリデーションは **Registry 側で実引数 vs 登録 schema の照合**だけで行う。「context にスキーマテキストが現れているか」は検証条件にしない (Claude Code 実演で確認済み: `claude-code-deferred-tools.md` §10)。これにより、ContextRenderer がどんな戦略で schema を見せていようと、registry の真実性が一本化される。
|
||||
|
||||
### 戦略 (RenderStrategy) のモデル系統別マッピング
|
||||
|
||||
| モデル系統 | 戦略 | 根拠 |
|
||||
|---|---|---|
|
||||
| Claude 系 (Anthropic API + ローカル Anthropic 互換) | **Deferred** + ToolSearch 相当 | XML+JSON のテキスト表現で tool_result からも schema 注入可。prefix 安定 |
|
||||
| OpenAI 系 (gpt-oss / Harmony / Responses) | **Upfront** + MCP-style dispatcher | namespace ブロックが構造化されており、後追い注入が訓練分布外。汎用 dispatcher で外部解決 |
|
||||
| Hermes / Qwen / Llama 等独自系 | **Upfront** または **Rolling Developer Message** | モデル個別 chat template に従い、必要なら境界で書き換え |
|
||||
|
||||
### 戦略を決める軸
|
||||
|
||||
`RenderStrategy` は以下の組み合わせで表現:
|
||||
|
||||
- **配置**: `SystemPrompt` (固定) / `DeveloperMessage` (cache 境界) / `ToolResultStream` (Claude 流注入)
|
||||
- **発見手段**: `AlwaysVisible` / `MetaTool { search, describe }` / `Static`
|
||||
- **変更時の cache 影響**: `PrefixStable` / `RewindToBoundary`
|
||||
|
||||
Claude 系 = `(ToolResultStream, MetaTool, PrefixStable)`、OpenAI 系 = `(DeveloperMessage, Static, RewindToBoundary)` または `(SystemPrompt + dispatcher, AlwaysVisible, PrefixStable)`。
|
||||
|
||||
## 設計詳細
|
||||
|
||||
### Registry インターフェース
|
||||
|
||||
```rust
|
||||
pub trait ToolRegistry {
|
||||
fn list(&self) -> Vec<ToolMeta>; // 名前+description のみ
|
||||
fn schema(&self, name: &str) -> Option<&ToolSchema>;
|
||||
fn dispatch(&self, name: &str, args: Value) -> Result<ToolResult, DispatchError>;
|
||||
}
|
||||
```
|
||||
|
||||
- `list()` は常に全ツール返す (戦略は ContextRenderer 側の責務)
|
||||
- `schema()` は ToolSearch 相当の動線で使用
|
||||
- `dispatch()` は schema 照合+実行。**context に schema text があるかは見ない**
|
||||
|
||||
### ContextRenderer インターフェース
|
||||
|
||||
```rust
|
||||
pub trait ContextRenderer {
|
||||
fn initial_render(&self, registry: &dyn ToolRegistry) -> InitialContext;
|
||||
fn on_tool_load(&self, name: &str, registry: &dyn ToolRegistry) -> Option<ContextDelta>;
|
||||
fn parse_call(&self, raw_output: &str) -> Result<ToolCall, ParseError>;
|
||||
fn format_result(&self, name: &str, result: &ToolResult) -> String;
|
||||
}
|
||||
```
|
||||
|
||||
戦略ごとに実装を差し替える:
|
||||
|
||||
- `ClaudeDeferredRenderer`: 初期 prompt に core tools のみ展開、`tool_search` メタツールを常設、ロード時は tool_result として `<function>{schema}</function>` を流す
|
||||
- `HarmonyUpfrontRenderer`: developer メッセージに namespace で全 tool 展開、ロード概念なし
|
||||
- `HarmonyDispatcherRenderer`: namespace は `call_mcp(server, tool, args)` だけ、サブツール解決は外部 MCP
|
||||
- `RollingDeveloperRenderer`: 一定境界 (compaction 等) で developer メッセージを再描画。cache 損失は境界で吸収
|
||||
|
||||
### Validation / Retry レイヤ
|
||||
|
||||
ツール呼び出しの失敗ハンドリングは ContextRenderer / Registry の上に置く独立層:
|
||||
|
||||
```rust
|
||||
pub struct ToolDispatcher<R: ToolRegistry, C: ContextRenderer> {
|
||||
registry: R,
|
||||
renderer: C,
|
||||
retry_policy: RetryPolicy,
|
||||
}
|
||||
```
|
||||
|
||||
責務:
|
||||
|
||||
1. モデル出力をパース (`renderer.parse_call`)
|
||||
2. registry で schema 照合 → invalid なら error tool_result を返す
|
||||
3. dispatch → 結果を `renderer.format_result` で整形してモデルへ
|
||||
4. malformed 出力時は error フィードバックして同一ターン内修正を促す
|
||||
|
||||
これは `tool_approach_comparison.md` §4 で議論した「プロバイダ側がやっている (フォーマット規約 / バリデーション / リトライ / 訓練投資)」のうち、**ローカルモデル向けには (4) が効かないため (1)-(3) を自前で組む**ことに対応する。
|
||||
|
||||
### KV cache / prompt cache 整合
|
||||
|
||||
戦略の選択は cache 効率に直結する:
|
||||
|
||||
- **PrefixStable 戦略 (Claude Deferred / Dispatcher パターン)**: 初期 prefix が固定。ToolSearch 結果や dispatcher 経由の動的解決は **会話末尾の tool_result** に積まれるため、前方プレフィックスが揺らがない
|
||||
- **RewindToBoundary 戦略 (Rolling Developer)**: tool セット変更が cache 全消し。compaction 境界に同期させて損失を抑える
|
||||
|
||||
Anthropic API の `prompt caching` は Explicit (cache_control) で、`llm_providers.md` §Prompt caching の `CacheStrategy::Explicit { max_breakpoints }` と整合する。ローカル推論の KV cache は基本 prefix-only のため `Auto` 相当。両方とも「安定 prefix 設計」に効く。
|
||||
|
||||
## 根拠
|
||||
|
||||
- **Registry vs Context 分離**: Claude Code の実演で「context に schema があるかは validation に無関係」と判明 (`claude-code-deferred-tools.md` §10)。同じ抽象で Claude / OpenAI / ローカル系を統一できる
|
||||
- **戦略の差し替え可能性**: Harmony は構造化トークン+namespace 前提で、Claude の deferred 方式が直接通用しない (`tool_approach_comparison.md` §1, §2)。モデル系統ごとの戦略切り替えは避けられない
|
||||
- **MCP-style dispatcher**: OpenAI が MCP 統合で採用している方向。namespace に汎用 entry point だけ置き、サブツール解決を外部化する。upfront にせず、訓練分布も逸脱しない
|
||||
- **検証レイヤを別層に**: モデル側のフォーマットの強さ (Harmony の特殊トークン) と弱さ (Claude の正規表現パース) で fail mode が違うため、ContextRenderer に閉じ込めずに上位で統一的に扱う
|
||||
|
||||
## 実装原則
|
||||
|
||||
- **registry は llm-worker の上位層に置く** (低レベル基盤に留める方針: `feedback_llm_worker_scope.md`)
|
||||
- **MCP サーバ統合は registry のバックエンドの一つ**として扱い、Harmony 側 dispatcher と内部実装を共有
|
||||
- **ContextRenderer の選択は ProviderScheme と一対多**: `scheme/anthropic` → ClaudeDeferred、`scheme/openai_responses` → HarmonyUpfront 等。`llm_providers.md` のプロバイダカタログにレンダラ指定を載せる
|
||||
- **ToolSearch 相当は MetaTool として実装**: registry 側に `__tool_search` / `__tool_describe` を登録し、ContextRenderer の戦略によって core tools に含めるか否かを決める
|
||||
- **テスト**: registry のバリデーションが context schema 有無に依存しないことを property test で保証する
|
||||
|
||||
## Scope 外
|
||||
|
||||
- 個別の MCP プロトコル実装 / サーバ連携の詳細
|
||||
- 各モデル固有の chat template レンダリング (Hermes / Qwen / Llama 等の差分は別 ticket)
|
||||
- Tool 結果の構造化出力 (citation / file reference 等) のスキーマ
|
||||
- Tool 並列実行・依存解決・cancellation
|
||||
- ユーザ定義ツールの permission 管理 (sandbox.md と別)
|
||||
|
||||
## 参考
|
||||
|
||||
- `docs/ref/claude-code-deferred-tools.md` — Claude Code の deferred tools 機構と実演による検証
|
||||
- `docs/ref/tool_approach_comparison.md` — Anthropic / OpenAI のツール呼び出しアプローチ比較
|
||||
- `docs/plan/llm_providers.md` — プロバイダ抽象とスキーム / capability 設計
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
# Workflow の方針
|
||||
|
||||
## Context
|
||||
|
||||
Workflow は制約付きの強制的な作業フロー。`/<slug>` で明示的に呼び出し、依存 Knowledge を context に inject してから実行する。Knowledge(`#<slug>`)は `docs/plan/memory.md` 側で定義。
|
||||
|
||||
## 決定事項
|
||||
|
||||
### 呼び出しと依存
|
||||
|
||||
- 呼び出し: `/<slug>`
|
||||
- 名前空間はフラット、slug は kebab-case(小文字英数とハイフン)
|
||||
- frontmatter `requires: [knowledge-slug, ...]` で依存 Knowledge を slug 参照
|
||||
- 実行時は依存 Knowledge 本文を context に inject してから Workflow 本文を実行
|
||||
|
||||
### 呼び出し制御フラグ
|
||||
|
||||
| フラグ | 意味 | デフォルト |
|
||||
| ---------------- | ------------------------------------------------------- | ---------- |
|
||||
| `auto_invoke` | description が LLM context に載り、LLM が自発的に呼べる | **OFF** |
|
||||
| `user_invocable` | ユーザーが `/<slug>` で明示的に呼べる | **ON** |
|
||||
|
||||
`auto_invoke` の ON 化は人間の判断、または consolidation からの offer 経由のみ。同じ制御は Knowledge 側(`memory.md`)でも採用。
|
||||
|
||||
### 格納先とファイル形式
|
||||
|
||||
- `.yoi/workflow/<slug>.md`(ファイル名 = slug がそのまま識別子、`name` field は持たない)
|
||||
- `.yoi/memory/` は session-derived state 専用、Workflow は配置しない
|
||||
- frontmatter + Markdown 本文
|
||||
- frontmatter フィールド: `description`, `auto_invoke`, `user_invocable`, `requires`
|
||||
|
||||
### 生成・更新ポリシー
|
||||
|
||||
Workflow は**人間が書く**、または consolidation が offer して人間が承認する。自動書き込みは禁止:
|
||||
|
||||
- consolidation(`memory.md` 参照)の write tool schema に `workflow` カテゴリを含めないことで構造的に担保
|
||||
- 新規作成 / 手順追加・更新は `Event::Notification` で提案し、人間承認で反映
|
||||
|
||||
### Offer 契機
|
||||
|
||||
consolidation が以下を検出した場合、Client に Notification を投げる:
|
||||
|
||||
- 再利用価値ある手続きの Workflow 化(新規作成)
|
||||
- 既存 Workflow への改善提案(手順追加・更新)
|
||||
- `auto_invoke` ON 化(頻繁に `user_invoke` されているものを検出)
|
||||
|
||||
## Scope 外
|
||||
|
||||
### 恒久除外(本設計方針として採用しない)
|
||||
|
||||
- Workflow の自律生成(offer までで留める。LLM が勝手に新規 Workflow を生成する経路は設けない)
|
||||
|
||||
### 将来検討(運用で必要性が見えたら追加)
|
||||
|
||||
- DSL 化や step 粒度の制約 — 初期は Markdown 本文そのまま実行
|
||||
- Workflow 実行中の中断・再開・トランザクション管理
|
||||
- 品質検証フロー: empirical prompt tuning pattern(`docs/ref/memory-systems.md` §6)相当の**新規 subagent 試走 + 構造化報告**を Workflow に適用。判定対象は本文の不明瞭点・裁量補完・要件達成率。Knowledge 単体の検証は設けず、`requires` 経由で Workflow から使われる前提で間接回収。SKILL 的用途(Workflow 経由しない `#knowledge`)は人間レビューに委ねる
|
||||
|
||||
## 関連
|
||||
|
||||
- `memory.md`: Knowledge 定義、extract/consolidation、Offer の配送経路
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
# Pod Factory: Profile resolution and prompt assets
|
||||
|
||||
`PodFactory` は、選択された Profile または明示的な one-file Manifest から、検証済みの `PodManifest` と `PromptLoader` を生成する境界である。通常の fresh spawn は profile discovery/default selection を使い、user/project `manifest.toml` の ambient cascade は使わない。
|
||||
|
||||
`PodManifest` は Pod 起動に必要な完全な runtime recipe で、Pod 名、具体的な scope、解決済みパス、model/provider 設定、worker 設定などを含む。Profile はその前段の再利用可能な recipe template であり、Pod 名や concrete scope authority など runtime-bound な値は resolver が起動入力から埋める。
|
||||
|
||||
---
|
||||
|
||||
## 起動モード
|
||||
|
||||
| モード | 入力 | 用途 | ambient manifest cascade |
|
||||
|---|---|---|---|
|
||||
| Profile | `--profile <selector>` または省略時 default | 通常の fresh spawn。Lua profile を解決して runtime Manifest を作る | 使わない |
|
||||
| One-file Manifest | `--manifest <path>` | デバッグ/互換用の完全 Manifest escape hatch | 使わない |
|
||||
| Restore/attach | `--pod` / `--session` 等 | 保存済み Pod state / session snapshot から再開 | profile を再評価しない |
|
||||
| SpawnPod internal | hidden `--spawn-config-json` | 親 Pod が子用 config を解決して渡す | 使わない |
|
||||
|
||||
`profiles.toml` は profile registry/default selection のための UX 設定であり、Pod Manifest 層ではない。Profile の中身へ merge されない。
|
||||
|
||||
## Profile resolution
|
||||
|
||||
Profile は Lua で書かれる。Rust resolver は selected profile を restricted Lua VM 内で評価し、返り値が Profile-shaped であることを検証してから `PodManifest` に変換する。
|
||||
|
||||
```lua
|
||||
local profile = require("yoi.profile")
|
||||
local models = require("yoi.models")
|
||||
local scope = require("yoi.scope")
|
||||
local compact = require("yoi.compact")
|
||||
|
||||
local model = models.catalog("codex-oauth/gpt-5.5")
|
||||
|
||||
return profile {
|
||||
slug = "coder",
|
||||
description = "Project coding profile",
|
||||
|
||||
model = model,
|
||||
worker = {
|
||||
reasoning = "high",
|
||||
},
|
||||
compaction = compact.ratio {
|
||||
threshold = 0.8,
|
||||
request = 0.9,
|
||||
},
|
||||
scope = scope.workspace_write(),
|
||||
}
|
||||
```
|
||||
|
||||
Profile が表現してよいのは reusable な recipe fields である。
|
||||
|
||||
- model selection / provider policy
|
||||
- worker settings / reasoning
|
||||
- compaction policy
|
||||
- memory / web policy
|
||||
- permissions / tool policy
|
||||
- session diagnostics policy
|
||||
- scope intent such as `scope.workspace_write()`
|
||||
|
||||
Profile に入れてはいけないもの:
|
||||
|
||||
- `pod.name`
|
||||
- concrete `scope.allow` / `scope.deny`
|
||||
- runtime directories, sockets, callback addresses
|
||||
- active/pending/session/pod-store runtime state
|
||||
- resolved absolute paths that bind the profile to one machine/workspace
|
||||
- raw resolved secret material
|
||||
|
||||
これらが必要な場合は、resolver の runtime input または `--manifest` の explicit Manifest path で扱う。
|
||||
|
||||
## Profile selector semantics
|
||||
|
||||
`--profile` は以下を受け付ける。
|
||||
|
||||
| 形 | 意味 |
|
||||
|---|---|
|
||||
| 省略 / `default` | registry default を使う。通常は bundled `builtin:default` |
|
||||
| `<name>` | unqualified profile name。ambiguous なら fail closed |
|
||||
| `builtin:<name>` / `user:<name>` / `project:<name>` | source-qualified selector |
|
||||
| `path:<path>` / `./profile.lua` / `/abs/profile.lua` | 明示 path profile |
|
||||
|
||||
`.nix` profile files are no longer supported. Reusable profiles are Lua; complete low-level recipes belong behind `--manifest`.
|
||||
|
||||
Discovery は bundled builtin profiles、user registry (`<config_dir>/profiles.toml`)、project registry (`<project>/.yoi/profiles.toml`) を読む。後段の default が前段の default を上書きするため、project default は user/default builtin より優先される。unqualified ambiguous names は source-qualified suggestion を出して失敗する。
|
||||
|
||||
Example `.yoi/profiles.toml`:
|
||||
|
||||
```toml
|
||||
default = "coder"
|
||||
|
||||
[profile]
|
||||
coder = "profiles/coder.lua"
|
||||
reviewer = "profiles/reviewer.lua"
|
||||
|
||||
[profile.orchestrator]
|
||||
path = "profiles/orchestrator.lua"
|
||||
description = "Project orchestrator"
|
||||
```
|
||||
|
||||
Relative registry paths are resolved against the `profiles.toml` file that declares them.
|
||||
|
||||
## Controlled Lua environment
|
||||
|
||||
Profile evaluation runs with controlled host-provided `require`.
|
||||
|
||||
Host virtual modules:
|
||||
|
||||
- `require("yoi")`
|
||||
- `require("yoi.profile")`
|
||||
- `require("yoi.models")`
|
||||
- `require("yoi.compact")`
|
||||
- `require("yoi.scope")`
|
||||
|
||||
Profile-local modules can be reused with dotted names such as `require("shared")` or `require("shared.models")`; they resolve only under the selected profile file's directory. Unsafe/unrestricted Lua facilities such as `os`, `io`, `debug`, unrestricted `package`, `dofile`, `loadfile`, `load`, and `collectgarbage` are unavailable by default.
|
||||
|
||||
## One-file Manifest mode
|
||||
|
||||
`--manifest <path>` reads exactly one TOML file, resolves relative paths against that file's parent directory, applies builtin defaults, and validates through `PodManifestConfig -> PodManifest`. It does not load user/project `manifest.toml` files and conflicts with `--profile`.
|
||||
|
||||
Use `--manifest` only when you need the complete low-level Manifest escape hatch or a focused debug fixture.
|
||||
|
||||
## Builtin defaults
|
||||
|
||||
Base defaults that are independent of profile choice live in Rust constants under `crates/manifest/src/defaults.rs` and in `PodManifestConfig::builtin_defaults()`. The default role profile is embedded from `resources/profiles/default.lua` at build time and is discovered as `builtin:default`.
|
||||
|
||||
デフォルト値を変更するときは、次のどちらを変更するのかを明確にする。
|
||||
|
||||
- all manifests/profiles の baseline default: Rust defaults
|
||||
- ordinary dogfooding/default role: embedded `builtin:default` profile sourced from `resources/profiles/default.lua`
|
||||
|
||||
## Path resolution
|
||||
|
||||
Profile-local Lua module paths are resolved relative to the selected profile file's directory and are constrained to that directory tree.
|
||||
|
||||
Manifest paths in one-file Manifest mode are resolved relative to the manifest file's parent directory before validation. `scope.*.target` and auth file paths must be absolute by the time `PodManifest` is constructed; a remaining relative path indicates a resolver bug and returns `ResolveError::RelativePath`.
|
||||
|
||||
Pod cwd is not part of the Profile. It is the process `current_dir()` for manual startup, or the parent-selected `Command::current_dir` for spawned Pods.
|
||||
|
||||
## Unknown fields and type errors
|
||||
|
||||
Profile validation is stricter than old ambient manifest cascade semantics at the top-level boundary. Complete-Manifest/runtime-authority fields such as `manifest`, `config`, `pod`, and concrete `scope.allow`/`scope.deny` are diagnosed rather than silently treated as reusable profile data.
|
||||
|
||||
One-file Manifest deserialization keeps the Manifest compatibility behavior: unknown fields may warn/ignore where the Manifest schema allows it, while type mismatches are hard errors with file/path context.
|
||||
|
||||
## Prompt assets
|
||||
|
||||
`worker.instruction` refers to a prompt asset used as the main system prompt body. Import-map prefixes:
|
||||
|
||||
| Prefix | Resolution |
|
||||
|---|---|
|
||||
| `$yoi` | bundled `resources/prompts/` (`include_dir!`) |
|
||||
| `$user` | `<config_dir>/prompts/` |
|
||||
| `$workspace` | `<project>/.yoi/prompts/` |
|
||||
|
||||
`.md` extension can be omitted, e.g. `$yoi/default` resolves to `resources/prompts/default.md`. Missing files are hard errors; prefixes do not fall through.
|
||||
|
||||
Profile and one-file Manifest CLI paths currently use builtin prompt assets only for initial loader construction. `$yoi/...` works; `$user/...` and `$workspace/...` prompt refs need a future explicit prompt-loader source design instead of reviving ambient manifest discovery.
|
||||
|
||||
The rendered instruction body is followed by fixed Rust-provided sections for working boundaries and, when present, `AGENTS.md`. User templates cannot remove the scope section.
|
||||
|
||||
## `yoi pod` CLI
|
||||
|
||||
Normal fresh startup uses profile discovery/default selection:
|
||||
|
||||
```text
|
||||
yoi pod [--profile <selector>] [--profile-pod-name <name>] [-s/--store <path>]
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|---|---|
|
||||
| `--profile <selector>` | Select a Lua profile. Omitted means registry default, normally `builtin:default` |
|
||||
| `--profile-pod-name <name>` | Fresh-spawn runtime `pod.name` override for profile resolution |
|
||||
| `-s, --store <path>` | Session persistence directory, default `<data_dir>/sessions/` |
|
||||
|
||||
Restore/attach uses Pod/session state and does not re-evaluate profile sources.
|
||||
|
||||
```text
|
||||
yoi pod --pod <name>
|
||||
yoi pod --session <uuid>
|
||||
```
|
||||
|
||||
Spawn children use hidden `--spawn-config-json`, `--adopt`, and `--callback <path>` flags. These are internal handoff details used by `SpawnPod` after the parent has allocated scope and prepared the child config.
|
||||
|
||||
`SpawnPod.profile` accepts registry/default selectors and the special `inherit` selector. Typical orchestration calls are `SpawnPod(profile = "project:coder")` for implementation work, `SpawnPod(profile = "project:reviewer")` for independent review, optionally `SpawnPod(profile = "project:orchestrator")` for lower-level coordination, or `SpawnPod(profile = "inherit")` when the child should reuse the parent role configuration. The explicit `SpawnPod.scope` remains the only delegated filesystem capability; selected or inherited profile scope is replaced by the tool argument.
|
||||
|
||||
## Programmatic boundary
|
||||
|
||||
New code should resolve profiles through the profile resolver and then construct Pods from the resulting `PodManifest` and `PromptLoader`. One-file Manifest helpers remain for tests/debugging. Avoid reintroducing user/project manifest cascade APIs as normal startup behavior.
|
||||
|
||||
```rust
|
||||
let (manifest, loader) = resolve_profile_or_manifest(cli_inputs)?;
|
||||
let pod = Pod::from_manifest(manifest, store, loader).await?;
|
||||
```
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# Reasoning / Thinking 制御
|
||||
|
||||
manifest の `[worker]` セクションで `reasoning` を指定すると、scheme が provider 各社の wire 形式に投影する。文字列なら **effort label**、数値なら **thinking budget tokens** として扱う。yoi 側は値の妥当性を検証せず、未知ラベルや provider が拒む値は API 応答で初めて検出される。
|
||||
|
||||
## 書き方
|
||||
|
||||
```toml
|
||||
[worker]
|
||||
reasoning = "medium" # effort label
|
||||
```
|
||||
|
||||
```toml
|
||||
[worker]
|
||||
reasoning = 4096 # thinking budget tokens (i32)
|
||||
```
|
||||
|
||||
未指定なら wire request に reasoning / thinking 関連フィールドは出さない。
|
||||
|
||||
## Provider ごとの受け入れ形式
|
||||
|
||||
| Provider / scheme | 受け入れる形式 | 投影先 |
|
||||
|---|---|---|
|
||||
| OpenAI Chat Completions (`openai_chat`) | effort label のみ | `reasoning_effort` |
|
||||
| OpenAI Responses (`openai_responses`) | effort label のみ | `reasoning: { effort, summary: "auto" }` |
|
||||
| Anthropic (`anthropic`) | budget tokens のみ | `thinking: { type: "enabled", budget_tokens }` |
|
||||
| Gemini (`gemini`) | budget tokens のみ | `generation_config.thinking_config.thinking_budget` |
|
||||
|
||||
`ModelCapability::reasoning` (`ReasoningSupport::{Effort, BudgetTokens, Both}`) と request 側の variant が一致しないときは、その scheme は wire に何も載せない(capability gating)。例: Anthropic に `reasoning = "medium"` を渡しても黙って drop される。
|
||||
|
||||
## Effort label
|
||||
|
||||
`ReasoningEffort` の既知 variant は `minimal` / `low` / `medium` / `high` / `xhigh`。これら以外の文字列は `Other(String)` として provider にそのまま渡る(OpenAI 側の独自ラベルや将来追加に対応)。
|
||||
|
||||
## Budget tokens
|
||||
|
||||
signed integer (`i32`) として扱う。Gemini の `-1`(dynamic budget)のような特殊値も型変換なしで通る。範囲チェックは provider に任せる。
|
||||
|
||||
## 設定例
|
||||
|
||||
OpenAI o-series:
|
||||
|
||||
```toml
|
||||
[model]
|
||||
ref = "openai/gpt-5"
|
||||
|
||||
[worker]
|
||||
reasoning = "high"
|
||||
```
|
||||
|
||||
Anthropic extended thinking:
|
||||
|
||||
```toml
|
||||
[model]
|
||||
ref = "anthropic/claude-sonnet-4-6"
|
||||
|
||||
[worker]
|
||||
reasoning = 8192
|
||||
```
|
||||
|
||||
Gemini dynamic thinking:
|
||||
|
||||
```toml
|
||||
[model]
|
||||
ref = "gemini/gemini-2.5-pro"
|
||||
|
||||
[worker]
|
||||
reasoning = -1
|
||||
```
|
||||
|
||||
## `max_tokens` との関係
|
||||
|
||||
`[worker] max_tokens` は scheme ごとに wire field 名も意味論も異なる。reasoning モデルで併用するときは特に注意:
|
||||
|
||||
| Provider / scheme | wire field | `max_tokens` の意味 |
|
||||
|---|---|---|
|
||||
| OpenAI Chat (`openai_chat`) | `max_completion_tokens`(Ollama 互換は legacy `max_tokens`) | reasoning tokens を **含む** 合計上限 |
|
||||
| OpenAI Responses (`openai_responses`) | `max_output_tokens` | reasoning tokens を **含む** 合計上限 |
|
||||
| Anthropic (`anthropic`) | `max_tokens`(必須) | thinking tokens を **含む** 合計上限 |
|
||||
| Gemini (`gemini`) | `generationConfig.maxOutputTokens` | visible のみ。thinking tokens は **別計上** |
|
||||
|
||||
OpenAI / Anthropic で `max_tokens` を小さく取りつつ高 effort / 大 budget の reasoning を立てると、reasoning に枠を食われて visible output が空で返ることがある。Gemini は別計上なのでこの事故は起きない。
|
||||
|
||||
codex-oauth (ChatGPT backend) 経路では `max_output_tokens` が `Unsupported parameter` で 400 を返すため、`openai_responses` scheme は `send_max_output_tokens=false` で wire に載せない。manifest に `max_tokens` を書いても黙って落ちるが、scheme の `validate_config` が `ConfigWarning` を返すので worker 起動時に通知される。
|
||||
|
||||
## 範囲外
|
||||
|
||||
- UI プリセット(Low / Medium / High → 各 provider 値)の変換テーブル
|
||||
- provider ごとの推奨 budget レンジ
|
||||
- reasoning / thinking 出力 block のログ・再送・表示ポリシー
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
# Claude Code コンテキスト管理リファレンス
|
||||
|
||||
調査日: 2026-04-12
|
||||
|
||||
## 概要
|
||||
|
||||
Claude Code は3層構造のコンテキスト管理を行う。
|
||||
安価な局所操作から順に適用し、必要に応じてより重い操作にエスカレーションする。
|
||||
|
||||
```
|
||||
Tier 1: MicroCompaction(ローカル、API コスト 0)
|
||||
↓ それでもコンテキストが大きい場合
|
||||
Tier 2: AutoCompact(API ベース要約、自動発動)
|
||||
↓ ユーザーが明示的に要求
|
||||
Tier 3: Full Compact(/compact コマンド)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tier 1: MicroCompaction
|
||||
|
||||
**API 呼び出しなし**のローカル操作。個別のアイテムを軽量に刈り込む。
|
||||
|
||||
### 対象
|
||||
|
||||
| 対象 | 操作 |
|
||||
|------|------|
|
||||
| 古いツール結果 | プレースホルダに置換(`"stored on disk, retrievable by path"`) |
|
||||
| base64 画像 | 古いメッセージから除去 |
|
||||
| 50K 文字超のツール出力 | ディスクに退避、パスで参照 |
|
||||
| thinking ブロック | 直近ターン以外は除去 |
|
||||
|
||||
### 条件付き実行(cache-aware)
|
||||
|
||||
ツール結果クリア機能 `clear_tool_uses` には `clear_at_least` パラメータがある。
|
||||
|
||||
**「十分なトークンを削れる場合にだけ実行」** という判断を行い、
|
||||
キャッシュ無効化コスト > 節約トークン数 となるケースを避ける。
|
||||
|
||||
これは Yoi における条件付き Prune の直接的な先行事例。
|
||||
|
||||
### キャッシュへの影響
|
||||
|
||||
- ツール結果をクリアすると**プレフィクスが変わり、KV キャッシュが無効化される**
|
||||
- Claude Code はこれを認識した上で、`clear_at_least` 閾値で損益を管理
|
||||
- **14種のキャッシュ無効化ベクター**を `promptCacheBreakDetection.ts` で追跡
|
||||
- system prompt を cached/uncached セクションに分離(`SYSTEM_PROMPT_DYNAMIC_BOUNDARY`)
|
||||
- "sticky latch" パターンでモード切替によるキャッシュ破壊を防止
|
||||
|
||||
---
|
||||
|
||||
## Tier 2: AutoCompact
|
||||
|
||||
**API ベースの要約生成**。コンテキスト使用量が閾値を超えると自動発動。
|
||||
|
||||
### トリガー
|
||||
|
||||
- コンテキスト使用量が**ウィンドウの約 83.5%** に到達
|
||||
- 200K ウィンドウの場合、約 167K トークン
|
||||
- `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` 環境変数で調整可能(1-100)
|
||||
- 約 33K トークンのバッファを確保(要約処理 + 応答生成用)
|
||||
|
||||
### 要約生成
|
||||
|
||||
- **最大 20,000 トークン**の構造化要約を生成
|
||||
- 要約中は**ツール無効化**(`tools: []`)で副作用を防止
|
||||
- `<analysis>` タグで chain-of-thought 推論を行い、最終要約から推論部分を除去
|
||||
|
||||
### 要約の構造(9セクション)
|
||||
|
||||
```
|
||||
1. Primary Request and Intent(元の要求と意図)
|
||||
2. Key Technical Concepts(重要な技術概念)
|
||||
3. Files and Code Sections(ファイルとコードセクション)
|
||||
4. Errors and Fixes(エラーと修正)
|
||||
5. Problem Solving(問題解決の過程)
|
||||
6. All User Messages(ツール結果以外の全ユーザーメッセージ)
|
||||
7. Pending Tasks(未完了タスク)
|
||||
8. Current Work(現在の作業)
|
||||
9. Optional Next Steps(次のステップ案)
|
||||
```
|
||||
|
||||
加えて「直近の会話からの直接引用」を要求し、作業の継続性を保証する。
|
||||
|
||||
### サーキットブレーカー
|
||||
|
||||
- 3回連続で AutoCompact が失敗すると、セッション残りで compaction を無効化
|
||||
- 定数: `MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3`
|
||||
- コンテキスト再充填が3回連続で即座に発生(thrash loop)した場合もエラーで停止
|
||||
|
||||
### サーバーサイド Compaction API
|
||||
|
||||
Anthropic は `compact-2026-01-12` beta でサーバーサイド compaction を提供:
|
||||
|
||||
```
|
||||
1. input_tokens が閾値超過
|
||||
2. Claude が要約を生成(ツール無効)
|
||||
3. 要約が `compaction` ブロックとして assistant メッセージに挿入
|
||||
4. 次のリクエストで compaction ブロック以前のメッセージが自動ドロップ
|
||||
```
|
||||
|
||||
`pause_after_compaction` オプション: compaction 後に `stop_reason: "compaction"` で一旦返し、
|
||||
クライアントがファイル・プラン・メモリ等を再注入してから継続できる。
|
||||
|
||||
---
|
||||
|
||||
## Tier 3: Full Compact(`/compact`)
|
||||
|
||||
ユーザーが明示的に発動する完全圧縮。
|
||||
|
||||
### 圧縮後に再注入されるもの
|
||||
|
||||
| 再注入対象 | 上限 |
|
||||
|------------|------|
|
||||
| 要約 | 約 10K トークン |
|
||||
| 直近アクセスファイル | 最大5ファイル、各 5,000 トークン |
|
||||
| アクティブプラン | 全量 |
|
||||
| CLAUDE.md / MEMORY.md | 全量 |
|
||||
| 関連スキルスキーマ | 最大 25K トークン |
|
||||
| フック結果 | 全量 |
|
||||
| 直近メッセージ | 約 20 メッセージ |
|
||||
|
||||
圧縮後の作業バジェットは約 50,000 トークンにリセットされる。
|
||||
|
||||
---
|
||||
|
||||
## Anthropic Prompt Caching の仕様
|
||||
|
||||
Claude Code のコンテキスト管理を理解するために必要な、プロバイダ側のキャッシュ仕様。
|
||||
|
||||
### プレフィクスベース
|
||||
|
||||
- リクエストの先頭から `cache_control` ブレークポイントまでの内容がキャッシュ対象
|
||||
- **1バイトでも変わるとそこから先は全てミス**
|
||||
- キャッシュは因果的(causal): アイテム N の KV が変わるとN 以降すべて再計算
|
||||
|
||||
### A を送った後に A+B を送った場合
|
||||
|
||||
- **A のキャッシュは保持される**(独立した TTL)
|
||||
- A+B のリクエストで A はキャッシュから読まれ、B だけ新規処理
|
||||
- **複数のキャッシュエントリが共存**する
|
||||
|
||||
### TTL
|
||||
|
||||
| タイプ | 時間 | 書き込みコスト |
|
||||
|--------|------|----------------|
|
||||
| ephemeral(デフォルト) | 5分(ヒットで更新) | 基本入力の 1.25倍 |
|
||||
| 拡張 | 1時間 | 基本入力の 2倍 |
|
||||
|
||||
読み取り: 基本入力の **0.1倍**(90% 割引)。1回のキャッシュヒットで書き込みコストを回収。
|
||||
|
||||
### 最小キャッシュ可能トークン
|
||||
|
||||
| モデル | 最小トークン |
|
||||
|--------|-------------|
|
||||
| Opus 4.6 / 4.5 | 4,096 |
|
||||
| Sonnet 4.6 | 2,048 |
|
||||
| Sonnet 4.5 / 4 / 3.7, Opus 4.1 / 4 | 1,024 |
|
||||
| Haiku 4.5 / 3 | 4,096 |
|
||||
|
||||
### ブレークポイント
|
||||
|
||||
- 最大 **4個** の明示的ブレークポイント
|
||||
- 自動キャッシング: 最後のキャッシュ可能ブロックにブレークポイントを自動配置
|
||||
- **20ブロック lookback**: ブレークポイントから20ブロック以上離れたキャッシュは検索されない
|
||||
|
||||
### キャッシュ階層と無効化
|
||||
|
||||
```
|
||||
tools → system → messages
|
||||
```
|
||||
|
||||
上位の変更は下位すべてを無効化:
|
||||
- tools 変更 → tools + system + messages 全て無効
|
||||
- system 変更 → system + messages 無効
|
||||
- messages 変更 → messages のみ無効
|
||||
|
||||
---
|
||||
|
||||
## OpenAI / Gemini との比較
|
||||
|
||||
| | Anthropic | OpenAI | Gemini |
|
||||
|---|---|---|---|
|
||||
| 制御 | 明示的 `cache_control` | 完全自動 | 明示 + 暗黙 |
|
||||
| 書き込みコスト | +25% / +100% | **無料** | 標準入力レート |
|
||||
| 読み取り割引 | 90% | 50-90% | 90% |
|
||||
| TTL | 5分(更新あり)/ 1h | 5-10分 / 最大1h / 24h | デフォルト1h / 任意 |
|
||||
| ヒット保証 | 確定的 | 確率的 | 明示=確定 / 暗黙=確率的 |
|
||||
| 最小トークン | 1,024-4,096 | 1,024 | 1,024-4,096 |
|
||||
|
||||
### 共通原則
|
||||
|
||||
- **全プロバイダでプレフィクスベース**
|
||||
- A を送った後に A+B を送ると、A のキャッシュは保持され再利用される
|
||||
- プレフィクスの途中を変更するとそこから先は再計算
|
||||
- これが Prune のキャッシュコストの根本原因
|
||||
|
||||
---
|
||||
|
||||
## OpenCode の Prune 関連 Issue
|
||||
|
||||
OpenCode(sst/opencode)でも Prune とキャッシュの問題は未解決。
|
||||
|
||||
| Issue | 内容 |
|
||||
|-------|------|
|
||||
| [#3917](https://github.com/sst/opencode/issues/3917) | Prune のドキュメント要求。**キャッシュ無効化が caveat として明記** |
|
||||
| [#21208](https://github.com/sst/opencode/issues/21208) | 固定閾値(40k/20k)が 1M コンテキストモデルに対して不適切 |
|
||||
| [#20826](https://github.com/sst/opencode/issues/20826) | Prune で tool output 消去 → 孤立した tool_call で API エラー |
|
||||
| [#19081](https://github.com/sst/opencode/issues/19081) | thinking 除去で KV キャッシュが毎ターン無効化 |
|
||||
| [#4416](https://github.com/sst/opencode/issues/4416) | キャッシュトークン二重カウントで premature compaction |
|
||||
| [#2945](https://github.com/sst/opencode/issues/2945) | Compaction でワーキングコンテキスト全喪失 |
|
||||
| [#6535](https://github.com/sst/opencode/issues/6535) | サブエージェントが compaction 後にシステムプロンプトを喪失 |
|
||||
|
||||
**「cache-aware な prune は未解決問題」** がコミュニティの共通認識。
|
||||
サードパーティの Dynamic Context Pruning プラグインも「prune はキャッシュプレフィクスを壊す」と認めている。
|
||||
|
||||
---
|
||||
|
||||
## Yoi 設計への示唆
|
||||
|
||||
### 1. Prune は条件付きで実行すべき
|
||||
|
||||
Claude Code の `clear_at_least` パターン:
|
||||
- 「削れるトークン数がキャッシュ再計算コストを上回る場合にだけ prune」
|
||||
- 無条件 prune はキャッシュを無駄に壊す
|
||||
|
||||
### 2. Compact は必須
|
||||
|
||||
- AutoCompact(83.5% 閾値)+ サーキットブレーカー(3回失敗で停止)は堅実な設計
|
||||
- 要約後の再注入(ファイル、プラン、メモリ)で作業継続性を確保
|
||||
- Anthropic のサーバーサイド compaction API は将来的に利用可能
|
||||
|
||||
### 3. キャッシュ階層を意識した設計
|
||||
|
||||
- system prompt は静的部分と動的部分を分離
|
||||
- ツール定義の変更はキャッシュ全体を無効化する → 動的ツール登録/削除はキャッシュコストがある
|
||||
- messages 部分の変更は messages キャッシュのみ影響
|
||||
|
||||
### 4. 既知の落とし穴
|
||||
|
||||
- Prune で ToolCall の対応する ToolResult を消すと API エラー(OpenCode #20826)
|
||||
- キャッシュトークンの二重カウントで premature compaction(OpenCode #4416)
|
||||
- Compaction 後のコンテキスト喪失(計画、サブエージェント、スキーマ)
|
||||
- Thrash loop(compaction 直後に再び閾値超過)の検出と停止が必要
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
# LLM 料金サマリ
|
||||
|
||||
調査日: 2026-04-19。料金は頻繁に改定されるため一次ソースで再確認。
|
||||
|
||||
## 定額サブスク
|
||||
|
||||
| サービス | 月額 | 含まれる上限 | 実用性メモ |
|
||||
|---|---|---|---|
|
||||
| Claude Pro | $20 | Sonnet 中心、5h rolling + 週次 | Opus 4.7 はほぼ使えない |
|
||||
| Claude Max 5x | $100 | Pro×5、Sonnet 常用可 | Sonnet をガッツリ書くなら最低ライン |
|
||||
| Claude Max 20x | $200 | Pro×20、Opus 4.7 解禁 | Opus をエージェント用途で回すならここ |
|
||||
| ChatGPT Plus | $20 | Codex 5h で GPT-5.4 が 20–100 msg | 長時間 agent loop では枯渇 |
|
||||
| ChatGPT Pro | $100 | Codex 5h で 100–500 msg、**2026-05-31 まで 2x プロモ**で 600–3000 | Max 20x 相当の常用級 |
|
||||
| Copilot Free | $0 | 50 premium/月、Haiku 4.5 / GPT-5 mini | お試し |
|
||||
| Copilot Pro | $10 | 300 premium/月、GPT-5 mini/4.1/4o は乗数 0x(無制限扱い) | コスパ◎ |
|
||||
| Copilot Pro+ | $39 | 1,500 premium/月、Opus 4.7 含む全モデル | 定額で Opus 4.7 最安 |
|
||||
| Cursor Pro | $20 | $20 クレジット、fast 500/月、slow 無制限 | 2025/6 に usage-based へ移行 |
|
||||
| Cursor Pro+ | $60 | Pro×3 クレジット | 詳細は公式ダッシュボード |
|
||||
| Cursor Ultra | $200 | Pro×20 クレジット、Privacy Mode | Max 20x 競合 |
|
||||
| Zed Pro | $10 | トークン課金、BYOK 可 | 実質「安い BYOK フロント」 |
|
||||
| Windsurf Pro | $20(2026/3 値上げ) | クレジット制、Claude 系のみ BYOK 可 | Teams/Ent は BYOK 不可 |
|
||||
| Ollama Pro | $20(年 $200) | 5h + 7d、Free×50、3 並列 | `:cloud` モデル (`gpt-oss:120b`, `deepseek-v3.1:671b` 等) |
|
||||
| Ollama Max | $100 | Pro×5、10 並列 | ローカル + クラウド併用 |
|
||||
| BLACKBOX Pro/Plus/Max | $10 / $20 / $40 | FUP 非公開 | Unlimited の実態不明 |
|
||||
| xAI SuperGrok | $30 | Grok 4 + 2M ctx(Web/アプリ) | **API 枠なし** |
|
||||
| xAI SuperGrok Heavy | $300 | Grok 4 Heavy 独占(Web/アプリ) | **API 枠なし** |
|
||||
| X Premium+ | $40 | Grok Web 利用 | **API 枠なし** |
|
||||
|
||||
## 無料枠・従量(少額)
|
||||
|
||||
- **OpenRouter**: プリペイド方式、手数料 5.5%(暗号通貨 5%)。**BYOK は月 100 万 req 無料**、超過後は元コストの 5%。`:free` モデルは 50 req/日、$10 以上入金で 1000 req/日
|
||||
- **Google AI Studio (Gemini 2.5 Pro)**: 5 RPM / **100 RPD** / TPM 250k 共有。2025/12 に大幅減枠
|
||||
- **GitHub Models (GPT-4o 等)**: 10 RPM / 50 RPD / 8k in, 4k out / 2 並列
|
||||
- **Cerebras Free**: 30 RPM / **1M tokens/日**(Llama 3.3 70B, Qwen3, gpt-oss-120B)
|
||||
- **Groq Free**: サインアップのみ(CC 不要)。Llama 3.3 70B は 30 RPM / 12k TPM / 1k RPD / 100k TPD、モデル別に上限異なる。**Developer tier**(要 CC)で Free の約 10× + Batch/Flex 解放 + daily cap 撤廃。月額プランは Enterprise のみ
|
||||
- **DeepSeek API**: V3 $0.14/$0.28, R1 $0.55/$2.19 /1M tok。オフピーク 50–75% 割引、キャッシュ 90% 割引
|
||||
- **Moonshot Kimi**: Adagio 無料プランあり。K2.5 API は $0.60/$2.50 /1M tok
|
||||
- **Together / Fireworks / DeepInfra**: サインアップクレジットのみ、恒久無料枠なし
|
||||
|
||||
## 従量 API の単価比較(コーディング向け)
|
||||
|
||||
| プロバイダ | モデル | 入/出 ($/1M tok) | Ctx | 備考 |
|
||||
|---|---|---|---|---|
|
||||
| Anthropic | Haiku 4.5 | $1.00 / $5.00 | 200k | 軽量・高速 |
|
||||
| Anthropic | Sonnet 4.6 | $3.00 / $15.00 | 200k | 精度上位級 |
|
||||
| Anthropic | Opus 4.7 | $5.00 / $25.00 | 200k | エージェント最強(旧 $15/$75 から値下げ) |
|
||||
| xAI | grok-code-fast-1 | $0.20 / $1.50 | 256k | SWE-bench 70.8%、142 tok/s |
|
||||
| xAI | grok-4-1-fast | $0.20 / $0.50 | 2M | 長文向け |
|
||||
| xAI | grok-4 | $3.00 / $15.00 | 256k | 旗艦(旧) |
|
||||
| Groq | GPT-OSS 120B | $0.15 / $0.60 | 128k | ~500 tok/s(LPU) |
|
||||
| Groq | Kimi K2-0905 | $1.00 / $3.00 | 256k | open モデル最強級 |
|
||||
| Groq | Llama 3.3 70B | $0.59 / $0.79 | 131k | 汎用 |
|
||||
| Groq | Qwen3 32B | $0.29 / $0.59 | 131k | 汎用 |
|
||||
| DeepSeek | V3 | $0.14 / $0.28 | 128k | オフピーク 50–75% 引、cache 90% 引 |
|
||||
| DeepSeek | R1 | $0.55 / $2.19 | 128k | 推論系最安 |
|
||||
| Moonshot | Kimi K2.5 API | $0.60 / $2.50 | — | 無料 Adagio プランあり |
|
||||
|
||||
プロンプトキャッシュ: Anthropic / xAI / Groq すべて対応(50–90% 引)。長い system prompt を使うエージェントでは実効単価が大きく下がる。
|
||||
|
||||
## 従量回避派の推奨構成
|
||||
|
||||
### A. Claude Max 単騎 ($100)
|
||||
Sonnet 4.6 中心で Claude Code + Web を併用。物足りなければ Max 20x ($200) に上げて Opus 4.7 を解禁。
|
||||
|
||||
### B. Copilot Pro+ + 無料枠併用 ($39)
|
||||
1,500 premium/月 + GPT-5 mini/4.1/4o が乗数 0x 無制限。溢れを Gemini 2.5 Pro (100 RPD) / Cerebras (1M tok/日) に逃がす。Opus 4.7 は乗数 7.5x = 実質 200 回/月なので温存必須。
|
||||
|
||||
### C. OpenRouter 少額 + Ollama Cloud + 無料枠 ($20–30)
|
||||
OpenRouter に $10 入金で `:free` が 1000 req/日に拡張。Ollama Pro ($20) で `gpt-oss:120b` / `deepseek-v3.1:671b`。補助に GitHub Models / Groq / Cerebras。BYOK 持ちなら月 100 万 req 無料の BYOK トンネルも。
|
||||
|
||||
### D. ChatGPT Pro Codex プロモ ($100、~2026-05-31)
|
||||
Codex 2x プロモで Max 20x 相当の実行量。プロモ終了後(6月以降)は通常枠 100–500/5h に戻る点は要確認。
|
||||
|
||||
### E. ハイブリッド最安 ($30)
|
||||
Zed Pro ($10) + Ollama Pro ($20) + 無料枠フル動員(Gemini / Cerebras / GitHub Models / Groq)。Zed は Sonnet をトークン課金、高頻度タスクはローカル/Ollama Cloud へ流す。
|
||||
|
||||
## 注記(要確認)
|
||||
|
||||
- Claude Code の週次上限の具体値: Anthropic 公式ヘルプに正確な数値記載なし。Max 20x で Opus 4.7 が「概ね 24–40 時間/週」との二次情報あり
|
||||
- Codex Pro 5x/20x の詳細と プロモ終了後の挙動
|
||||
- BLACKBOX Unlimited の実スループット(公式非開示)
|
||||
- Cursor Pro+ / Ultra のクレジット実効回数(モデル API コスト依存で一律換算不能)
|
||||
|
||||
## 一次ソース
|
||||
|
||||
- https://claude.com/pricing
|
||||
- https://developers.openai.com/codex/pricing
|
||||
- https://github.com/features/copilot/plans
|
||||
- https://docs.github.com/en/copilot/concepts/billing/copilot-requests
|
||||
- https://ollama.com/pricing
|
||||
- https://openrouter.ai/pricing
|
||||
- https://openrouter.ai/announcements/1-million-free-byok-requests-per-month
|
||||
- https://ai.google.dev/gemini-api/docs/rate-limits
|
||||
- https://docs.github.com/github-models/prototyping-with-ai-models
|
||||
- https://console.groq.com/docs/rate-limits
|
||||
- https://api-docs.deepseek.com/quick_start/pricing
|
||||
- https://docs.x.ai/developers/models
|
||||
- https://grok.com/plans
|
||||
- https://groq.com/pricing
|
||||
- https://console.groq.com/docs/rate-limits
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
# LLM プロバイダ統合の外部事例
|
||||
|
||||
調査日: 2026-04-19。プロバイダ認証経路・`ollama launch` 等の時事的項目は陳腐化が早い。数値・URLは一次ソースで再確認すること。
|
||||
|
||||
## 各ハーネスのプロバイダ対応方式
|
||||
|
||||
### Zed
|
||||
- ネイティブ: Anthropic / OpenAI / Google / Ollama / DeepSeek / Mistral / OpenRouter / Vercel AI Gateway
|
||||
- `openai_compatible` スロットで任意プロバイダ追加
|
||||
- OpenRouter は**宣言型**: `available_models[]` に `max_tokens` / `supports_tools` 等の capability を書く。自動 discovery ではない
|
||||
- Ollama は `auto_discover: bool` でローカル tag 自動列挙
|
||||
- https://zed.dev/docs/ai/llm-providers
|
||||
|
||||
### OpenCode (sst/opencode)
|
||||
- Vercel AI SDK + Models.dev で 75+ プロバイダ
|
||||
- 認証は `~/.local/share/opencode/auth.json` に統一保存(OAuth / APIキー / その他の3種別)
|
||||
- **2026-03-19 に Anthropic OAuth 対応を削除**(PR #18186)。詳細は後述
|
||||
- ChatGPT ブラウザ認証 (`/connect`) は存続
|
||||
- https://opencode.ai/docs/providers/
|
||||
|
||||
### OpenClaw
|
||||
- Peter Steinberger らの個人向け self-hosted AI(OpenCode と別物)
|
||||
- 30+ プロバイダ対応
|
||||
- Anthropic 独自 OAuth 実装は縮小、現在は `claude -p` (Claude Code CLI) の subprocess 再利用を推奨
|
||||
- OpenRouter は「OpenAI 互換プロキシ」扱いで固有機能 (serviceTier, prompt-cache hints) は転送しない
|
||||
- https://docs.openclaw.ai/concepts/model-providers
|
||||
|
||||
### Aider / Cline / Roo / Continue.dev
|
||||
- Aider は LiteLLM 経由、他は直叩き
|
||||
- BLACKBOX 等マイナー系は OpenAI 互換枠で収容するのが一般パターン
|
||||
|
||||
## 認証経路の現状
|
||||
|
||||
### Anthropic (Claude Pro / Max) ── 封鎖済み
|
||||
- 2026-01-09: Anthropic がサーバ側で Pro/Max OAuth トークンに `This credential is only authorized for use with Claude Code and cannot be used for other API requests` の制限導入
|
||||
- 2026-02-19: Claude Code の Legal & Compliance ページに "Authentication and credential use" セクション追加。「OAuth 認証は Claude Code 等の通常利用のため専用」「Agent SDK を含む第三者製ツールで Free/Pro/Max 資格情報を経由するのは許可しない」と明記
|
||||
- 2026-03-19: OpenCode が PR #18186 で以下を削除
|
||||
- `packages/opencode/src/session/prompt/anthropic-20250930.txt`(Claude Code 風システムプロンプト)
|
||||
- `opencode-anthropic-auth@0.0.13` ビルトインプラグイン
|
||||
- `claude-code-20250219` beta ヘッダ
|
||||
- 代替検討: `claude -p` (Claude Code の headless mode) を subprocess で呼ぶ方式。ACP ではなく素朴な CLI fork であり、yoi では採用しない
|
||||
- https://code.claude.com/docs/en/legal-and-compliance
|
||||
- https://github.com/sst/opencode/pull/18186
|
||||
|
||||
### OpenAI (Codex CLI / Responses)
|
||||
- Codex CLI は Apache-2.0 で公開されている。yoi の Codex OAuth 経路は、Codex CLI と同じ Responses 系 wire behavior に寄せる
|
||||
- Codex CLI の認証ストアと conversation header / request compression / SSE behavior を参考にする
|
||||
- OpenCode の `/connect` で ChatGPT ブラウザ認証が通る
|
||||
- コミュニティ評価: 「Anthropic は walled garden、OpenAI はむしろ取り込みに来た」
|
||||
- https://github.com/openai/codex/discussions/8338
|
||||
- https://developers.openai.com/codex/auth
|
||||
|
||||
### CLI fork 方式 (`claude -p`)
|
||||
- `claude --print` / `claude -p` は Claude Code の非対話(headless)モード。プロンプトを stdin/引数で受け stdout に返す
|
||||
- **ACP ではなく素朴な subprocess 呼び出し**
|
||||
- OpenClaw と OpenCode コミュニティフォーク (`griffinmartin/opencode-claude-auth`) が採用
|
||||
- yoi では専用 API integration ではないため採用しない
|
||||
|
||||
## Ollama の統合機構
|
||||
|
||||
### `ollama launch <tool>`
|
||||
- v0.14.0 (2026-01) 以降、Ollama サーバ本体に **Anthropic Messages API 互換レイヤー**が組み込まれた
|
||||
- 別プロセスのプロキシは起動しない。Ollama コア (`localhost:11434`) が `/v1/messages` 相当を喋る(streaming / system prompt / tool calling / extended thinking / vision 対応)
|
||||
- `ollama launch claude` はサブコマンドバイナリを fork し、子プロセスに env を注入
|
||||
- `ANTHROPIC_BASE_URL=http://localhost:11434`
|
||||
- `ANTHROPIC_AUTH_TOKEN=ollama`
|
||||
- `ANTHROPIC_API_KEY=`(空)
|
||||
- 設定ファイル書き換えやシェル rc 操作はしない
|
||||
- `ANTHROPIC_BASE_URL` はハードコードで `OLLAMA_HOST` を見ない(issue #13936)
|
||||
- サブコマンド `claude` / `codex` / `opencode` / `droid` / `clawdbot` は「ツール毎の env 注入テンプレート」の集合体
|
||||
- 例: `opencode` は `OPENCODE_CONFIG_CONTENT` に JSON を流し込み OpenCode 側で deep-merge
|
||||
- `codex` は OpenAI 互換 (`/v1/chat/completions`) を向けるので Anthropic 系ではなく OpenAI 系 env を設定
|
||||
- https://ollama.com/blog/launch
|
||||
- https://ollama.com/blog/claude
|
||||
- https://github.com/ollama/ollama/issues/13936
|
||||
|
||||
### Ollama クラウド
|
||||
- `ollama signin` は OAuth ではなく **Ed25519 鍵ペア登録**。`~/.ollama/id_ed25519`(秘密鍵)と `.pub`(公開鍵)をローカル保存、ブラウザで `ollama.com/connect` から公開鍵をアカウントに紐付け
|
||||
- ルーティング: `server/routes.go` の `GenerateHandler` / `ChatHandler` がモデル名をパースし、`:cloud` サフィックスなら `server/cloud_proxy.go` 経由で `ollama.com:443` に中継
|
||||
- 署名: タイムスタンプ付きチャレンジを Ed25519 秘密鍵で署名し `Authorization` ヘッダに載せる
|
||||
- レスポンスは 32KB バッファで chunk 中継
|
||||
- **クライアントは `localhost:11434` のままでよい**。ローカル Ollama デーモンが透過プロキシ役を果たす
|
||||
- `ollama.com/api` を直叩きしたいときのみ `OLLAMA_API_KEY` を別途発行
|
||||
- https://github.com/ollama/ollama/blob/main/server/cloud_proxy.go
|
||||
|
||||
## OpenAI 互換プロバイダ事例
|
||||
|
||||
「共通 OpenAI 互換枠」で収容する代表的プロバイダ。`base_url` + API key 差し替えで動く。
|
||||
|
||||
### xAI (Grok)
|
||||
- `base_url`: `https://api.x.ai/v1`(OpenAI SDK 互換、Anthropic SDK 互換レイヤーも提供)
|
||||
- 主要モデル:
|
||||
- `grok-code-fast-1` — 256K ctx、$0.20/$1.50 /1M tok、SWE-bench 70.8%、142 tok/s(Sonnet の 1/10 以下の単価)
|
||||
- `grok-4-1-fast` — 2M ctx、$0.20/$0.50 /1M tok(長文向け差別化)
|
||||
- `grok-4` — 256K ctx、$3.00/$15.00 /1M tok
|
||||
- プロンプトキャッシュあり(50–75% 引)、Batch API 50% 引
|
||||
- 認証は API key のみ、OAuth なし
|
||||
- サブスク(X Premium+ / SuperGrok / SuperGrok Heavy)は UI 専用で **API 枠を含まない**
|
||||
- Zed / OpenCode / Cline / Cursor に第一級サポートあり
|
||||
- https://docs.x.ai/developers/models
|
||||
|
||||
### Groq
|
||||
- `base_url`: `https://api.groq.com/openai/v1`(OpenAI SDK 互換)
|
||||
- 特徴: LPU で TTFT <100ms、モデル別 TPS 500–1000(`gpt-oss:120b` で 500 tok/s 等)
|
||||
- 主要モデル:
|
||||
- GPT-OSS 120B — $0.15/$0.60 /1M tok、128K ctx
|
||||
- Kimi K2-0905 — $1.00/$3.00、256K ctx(open モデル最強クラス)
|
||||
- Llama 3.3 70B Versatile — $0.59/$0.79、131K ctx
|
||||
- Qwen3 32B — $0.29/$0.59、131K ctx
|
||||
- tool_use / JSON mode / streaming すべて OpenAI 互換
|
||||
- プロンプトキャッシュ 50% 引、Batch API 50% 引
|
||||
- **Qwen3-Coder-480B は未提供**。Coder 用途は Together / Cerebras 等で補完する前提
|
||||
- Cerebras 比較: GPT-OSS 120B で Cerebras ~3000 tok/s vs Groq ~476 tok/s。throughput は Cerebras、TTFT は Groq
|
||||
- 月額サブスクは Enterprise のみ、通常は従量
|
||||
- https://groq.com/pricing
|
||||
- https://console.groq.com/docs/api-reference
|
||||
|
||||
### BLACKBOX AI
|
||||
- `base_url`: `https://cloud.blackbox.ai/api` (OpenAI 互換 REST)、Bearer `bb_*` トークン
|
||||
- 主要ハーネスに専用サポートなし。共通枠で登録するのが実用
|
||||
- Unlimited プランは FUP 非公開で実態不明
|
||||
- https://docs.blackbox.ai/api-reference/authentication
|
||||
|
||||
### OpenRouter
|
||||
- `base_url`: `https://openrouter.ai/api/v1`
|
||||
- 多数のプロバイダを単一 API で束ねる(料金は passthrough + プリペイド手数料 5.5%)
|
||||
- BYOK: 月 100 万 req 無料、超過で元コストの 5%
|
||||
- `:free` モデル 50 req/日($10 入金で 1000 req/日)
|
||||
- https://openrouter.ai/docs
|
||||
|
||||
## プロバイダ独自拡張
|
||||
|
||||
各プロバイダが OpenAI 互換エンドポイントに載せている独自機能。詳細は `llm-pricing-2026-04.md` と合わせて参照。
|
||||
|
||||
### OpenAI 本家
|
||||
- `/v1/responses` stateful API、`previous_response_id`、`reasoning.effort` (minimal/low/medium/high/xhigh)、`reasoning.summary: "auto"`、`store=false` / ZDR 組織で強制
|
||||
- 高次ツール: `web_search` / `code_interpreter` / `computer_use`
|
||||
- `max_tokens` → `max_completion_tokens` (o系) → `max_output_tokens` (Responses) のリネーム
|
||||
|
||||
### xAI (Grok)
|
||||
- OpenAI + Anthropic 両 SDK 互換(**Anthropic 互換は deprecated**、native 推奨)
|
||||
- `reasoning_effort: low/high` は Grok-3 mini 系のみ、Grok-4 系は常時 reasoning
|
||||
- **Deferred Chat Completions** で `request_id` による非同期取得
|
||||
- Live Search
|
||||
|
||||
### Groq
|
||||
- `service_tier: on_demand / flex / auto`(flex は 10x rate、失敗許容、paid 限定)
|
||||
- **prompt caching は自動・追加料金なし・コード変更なし**
|
||||
- `reasoning_format` で reasoning の提示方式制御(JSON 出力との両立)
|
||||
|
||||
### DeepSeek
|
||||
- `deepseek-reasoner` はレスポンスに **`reasoning_content` 別フィールド**
|
||||
- **reasoner では function calling 非対応**
|
||||
- 入力に `reasoning_content` を含めると 400
|
||||
- 自動 prefix cache (64 tok 単位、hit で 90% 割引)
|
||||
|
||||
### Cerebras / Together AI
|
||||
- JSON schema / tool calling / multi-turn tool calling / streaming + structured を全モデル対応
|
||||
|
||||
### Fireworks
|
||||
- **Grammar mode (BNF)** が独自、任意出力形式を制約可能
|
||||
|
||||
### OpenRouter
|
||||
- `provider` でルーティング/フォールバック、`transforms` で自動中抜き
|
||||
- **`reasoning` 統一インターフェース**で各社差を吸収
|
||||
|
||||
### Ollama
|
||||
- `/v1/chat/completions` は **"experimental"** 明記
|
||||
- **stream + tools で単一ブロック返却のバグ** (#9092)
|
||||
- context size は Modelfile の `num_ctx` で固定(API 側で設定不可)
|
||||
- native `/api/chat` の方が機能フル
|
||||
- v0.14+ で `/v1/messages` (Anthropic 互換) も追加
|
||||
|
||||
### Azure OpenAI
|
||||
- URL が `{resource}/openai/deployments/{name}/chat/completions?api-version=...`、**model_id ではなく deployment 名**
|
||||
- content filter が `finish_reason: content_filter` や 400 で観測
|
||||
|
||||
## Capability 軸
|
||||
|
||||
モデル/プロバイダごとの機能差を表現する軸。**プロバイダ側高次ツール (web_search / code_interpreter / computer_use / Live Search) は yoi では使用しない方針**のため capability 軸から除外。
|
||||
|
||||
### 1. tool calling
|
||||
parallel tool calls 可否、tool_choice 対応度。DeepSeek reasoner のような「reasoner + tool 非対応」ケースあり。
|
||||
|
||||
### 2. structured output
|
||||
- `json_object` のみ
|
||||
- `json_schema` 対応
|
||||
- Grammar (Fireworks 独自)
|
||||
|
||||
### 3. reasoning
|
||||
- `reasoning.effort` (OpenAI / xAI)
|
||||
- `thinking.budget_tokens` (Anthropic)
|
||||
- `reasoning_content` 別フィールド出力 (DeepSeek)
|
||||
- `reasoning_format` (Groq)
|
||||
- OpenRouter は `reasoning` に統一投影
|
||||
|
||||
### 4. vision
|
||||
モデル依存度が大
|
||||
|
||||
### 5. prompt caching
|
||||
呼び出し側ロジックから見て 2 値に集約:
|
||||
- **Explicit**: `cache_control` マーカー挿入(Anthropic のみ)
|
||||
- **Auto**: scheme はマーカー挿入しない(OpenAI / DeepSeek / Groq の自動 prefix も、サーバ側で何もしないケースも同じ扱い)
|
||||
|
||||
### 付随パラメータ
|
||||
- `max_tokens` 名の差(`max_tokens` / `max_completion_tokens` / `max_output_tokens`)、Anthropic は必須
|
||||
- stateful (`previous_response_id`) は OpenAI Responses のみ
|
||||
- streaming SSE の delta 粒度・usage 到達タイミング差
|
||||
|
||||
## ToolCall streaming の差異
|
||||
|
||||
| プロバイダ | ToolCall 引数の streaming |
|
||||
|---|---|
|
||||
| Anthropic | `input_json_delta` partial streaming — 安定 |
|
||||
| OpenAI (chat) | `tool_calls[i].function.arguments` partial — 安定、parallel 時は複数 index 並走 |
|
||||
| OpenAI Responses | reasoning item が別構造(`summary[]` / `encrypted_content`) |
|
||||
| xAI / Groq / DeepSeek | OpenAI 互換、chat と同じ |
|
||||
| Gemini | function_call は **delta なし、一括で返る** |
|
||||
| Ollama `/v1` | **stream + tools で delta が欠けるバグ** (#9092) |
|
||||
| Ollama `/api/chat` | native API、安定 |
|
||||
|
||||
→ Gemini / Ollama `/v1` は scheme アダプタで「BlockStart → InputJson(全体 1 回) → BlockStop」の**擬似ストリーム化**で共通化可能。
|
||||
|
||||
## yoi での採用方針
|
||||
|
||||
### 第一級サポート(専用アダプタ)
|
||||
- **Ollama API** — ローカル + `:cloud` サフィックスで透過的にクラウド中継。エンドポイントは `localhost:11434` で統一
|
||||
- **Codex OAuth** — `~/.codex/auth.json` を読み、Codex CLI 互換の Responses 経路として扱う。conversation header / compression / SSE behavior は公開実装に合わせる
|
||||
- **Anthropic API** — 従量 API key 経路のみ
|
||||
|
||||
### 二次サポート(共通 OpenAI 互換枠)
|
||||
- `provider_kind: openai_compatible` + `base_url` + `available_models[]` の共通スロットで OpenRouter / xAI / Groq / Together / Fireworks / DeepInfra / BLACKBOX 等を一括収容
|
||||
- ルーター系は後追いで追加しやすい宣言型設計
|
||||
|
||||
### 非サポート
|
||||
- **Claude Pro/Max OAuth 経路** — 2026-01-09 サーバ側ブロック、2026-02-19 に第三者ツール経由の利用制限を明文化。第一級機能としては採用しない
|
||||
- `claude -p` CLI fork も専用 API integration ではないため実装しない
|
||||
|
||||
### 実装原則
|
||||
- 認証アダプタ(外部 CLI の認証ストアを読む類)は llm-worker 直下ではなく上位アダプタ層に配置。llm-worker は低レベル基盤に留める原則(project memory)と整合
|
||||
- モデル列挙は `auto_discover` と宣言型の両輪。Ollama は自動、ルーター系は宣言
|
||||
- `ollama launch yoi` 対応を視野に入れ、env 注入 (`ANTHROPIC_BASE_URL` / `OPENAI_BASE_URL` 等) で起動設定を受け入れる作り
|
||||
|
|
@ -1,567 +0,0 @@
|
|||
# エージェント向けメモリ機構の外部事例
|
||||
|
||||
調査日: 2026-04-21。本ドキュメントはユーザー依頼の3ソース(OpenAI Codex Chronicle / Shann³ の "AI Knowledge Layer" スレッド / Nous Research Hermes Agent)を中心に、直近で公開されたメモリ機構をまとめ、yoi に入れる際の比較材料とすることを目的とする。2026年前半は各所からメモリ実装が同時多発的に登場しているので、「どの事例がどのレイヤを担っているか」を見失わないよう、各節で**何を記憶するか/いつ書くか/どう引き出すか/何に保存するか**を揃えて整理する。
|
||||
|
||||
数値・URLは一次ソースで再確認すること。挙動は研究プレビュー段階のものが多く、変わる前提で読む。
|
||||
|
||||
---
|
||||
|
||||
## 1. OpenAI Codex — Memories & Chronicle
|
||||
|
||||
Codex CLI に 2026-03 頃から追加された "Memories" 機能と、2026-04-15 頃に macOS 向け研究プレビューとして乗った "Chronicle"。スクリーン内容まで観測対象に広げた点が目新しいが、**コアは素朴な extract / consolidation の 2 段パイプライン**で、実装の示唆が多い。
|
||||
|
||||
### データフロー(memories 本体)
|
||||
|
||||
セッション開始時に走る 2-step パイプライン。`codex-rs/core/src/memories/` を直接読み確認した挙動:
|
||||
|
||||
- **extract — Extraction**(`extraction implementation`): 対象 rollout ごとにモデル呼び出しで **JSON schema 強制**の `StageOneOutput { raw_memory, rollout_summary, rollout_slug }` を返させる(`#[serde(deny_unknown_fields)]`)。`CONCURRENCY_LIMIT = 8` で並列実行、結果は SQLite の `stage1_outputs` テーブルに格納。`StateRuntime` の job leasing で duplicate work 防止
|
||||
- モデル: `gpt-5.4-mini` / Low reasoning(`memories.extract_model` で override 可)
|
||||
- テンプレ: `codex-rs/core/templates/memories/stage_one_system.md` + `stage_one_input.md`
|
||||
- **consolidation — Consolidation**(`consolidation implementation`): **singleton** として走る(`jobs` テーブルで `kind = 'memory_consolidate_global'`, `job_key = 'global'` を `wx` 相当に claim)。`ConsolidationInputSelection` で前回 baseline との **added / retained / removed** 差分を計算し、その差分を prompt に埋めて sub-agent に投入
|
||||
- 出力は **自由形式 Markdown**(JSON schema なし)。sub-agent が memory_root を cwd に持ち、`MEMORY.md` / `memory_summary.md` / `skills/<name>/` を直接書き換える
|
||||
- モデル: `gpt-5.4` / Medium reasoning(`memories.consolidation_model` で override 可)
|
||||
- Heartbeat 90s / lease 3600s、失敗時 `retry_remaining` デフォルト 3
|
||||
- テンプレ: `codex-rs/core/templates/memories/consolidation.md`(836 行の詳細 instructions)
|
||||
- 生成タイミングは「スレッドが十分アイドルになってから」(age + idle window で判定)
|
||||
- `memory_summary.md` が **5,000 tokens cap** で system prompt に注入される(`MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT`)
|
||||
|
||||
**非対称の肝**: 抽出 (extract) は structured output で分類を強制、統合 (consolidation) は sub-agent に自由書き込みさせる。分類ブレを extract で封じ、統合の柔軟性は consolidation の agentic 判断に委ねる、という役割分担。yoi の extract / consolidation 設計の直接の元ネタ。
|
||||
|
||||
### 保存場所 / 形式
|
||||
|
||||
- `$CODEX_HOME/memories/`(既定 `~/.codex/memories/`)配下に Markdown。
|
||||
- `memory_summary.md` を中心に「summaries / durable entries / recent inputs / supporting evidence」の Markdown を束ねる構成。
|
||||
- Chronicle 派生メモリは `$CODEX_HOME/memories_extensions/chronicle/` に分離。
|
||||
- スクリーンキャプチャ中間物は `$TMPDIR/chronicle/screen_recording/` に置かれ、running 中 6h で自動削除。サーバー側には保存しない(法的義務時を除く)。
|
||||
- **Extension resource retention は決定論的 7 日 hard-coded**(`EXTENSION_RESOURCE_RETENTION_DAYS = 7`, `memories/extensions.rs:11`)。filename embedded timestamp (`%Y-%m-%dT%H-%M-%S`) が cutoff より古い `.md` リソースを consolidation 直前に物理削除し、削除リストを `removed_extension_resources` として consolidation prompt に渡す(agent はそれを見て派生メモリを抹消)。
|
||||
|
||||
### 設定
|
||||
|
||||
- `memories.generate_memories` / `memories.use_memories`: 生成 / 注入の on-off。
|
||||
- `memories.extract_model`: extract に使う軽量モデル。
|
||||
- `memories.consolidation_model`: consolidation のマージ側モデル(既定で reasoning 系)。
|
||||
- セッション内 `/memories` コマンドでスレッド単位に無効化可能。
|
||||
- シークレットは生成時に自動 redaction。
|
||||
|
||||
### Chronicle 固有
|
||||
|
||||
- 画面スクリーンショット + OCR テキスト + タイミング + ローカルファイルパスを入力に、**サンドボックス化した Codex セッション**をバックグラウンドで回して Markdown メモリを吐く。
|
||||
- 「Chronicle は rate limit を激しく消費する」「画面由来の prompt injection リスクが増える」「ローカル unencrypted」が公式に注意書きされている。Pro / macOS / 非EU+UK+CH 限定。
|
||||
- 可搬性の観点では、生成物が単なる Markdown ファイルでユーザーが手編集できる点が重要。
|
||||
|
||||
### 設計上の示唆
|
||||
|
||||
- **圧縮は reasoning モデルで非同期に走らせる**: 会話ループの応答性を落とさず、コストも分離する。
|
||||
- **中間テーブル(SQLite)と最終出力(Markdown)の 2 段構成**: 解析容易性と人間編集性を両立。
|
||||
- **5k tokens 固定注入**: 動的 retrieval ではなく "常駐 summary" として使う素朴さ。
|
||||
- **拡張はサブディレクトリで**: `memories_extensions/<name>/` というパターンで観測チャンネルを増やす構造。
|
||||
|
||||
一次ソース:
|
||||
- https://developers.openai.com/codex/memories
|
||||
- https://developers.openai.com/codex/memories/chronicle
|
||||
- https://deepwiki.com/openai/codex/3.7-memory-system
|
||||
- https://9to5mac.com/2026/04/20/codex-for-mac-gains-chronicle-for-enhancing-context-using-recent-screen-content/
|
||||
|
||||
---
|
||||
|
||||
## 2. Shann³ "AI Knowledge Layer"
|
||||
|
||||
https://x.com/shannholmberg/status/2044111115878326444 で提唱している、**エージェントより先に読ませる知識層**という枠組み。エンジニア向けというよりマーケター / コンテンツ運用者向けだが、構造はかなり yoi に転用しやすい。
|
||||
|
||||
### 2 層構造
|
||||
|
||||
- **Knowledge Base Layer (KBL) — 動的**
|
||||
- 生素材(tweet / 記事 / ブックマーク / PDF / ノート / ボイスメモ)を raw フォルダへ投げ込む。
|
||||
- 別エージェントが読んで種類別に分類、相互参照つきの構造化 wiki ページ化、マスターインデックスを維持。
|
||||
- 毎日少しずつ賢くなる前提。
|
||||
|
||||
- **Brand Foundation (BF) — 静的**
|
||||
- 声のルール / 視覚スタイル / ポジショニング / オーディエンス定義 / 禁則語などを**ユーザーだけが編集**。
|
||||
- エージェントは生成前に必ず BF を読む。書き換えはしない。
|
||||
- 別ソースの続編 ("Ronin runs 10 social accounts ... 17 markdown files") でも同じパターン。KBL + BF + エージェント 1 本で 10 アカウント運用している。
|
||||
|
||||
### ルーツ: Karpathy の llm-wiki
|
||||
|
||||
Shann³ の構成は Karpathy が 2026-04 に公開した `llm-wiki` gist をそのまま応用したもの。構造は 3 層:
|
||||
|
||||
- `raw/` — 人間のみ書き込む生ソース。
|
||||
- `wiki/` — LLM 所有、page / index / glossary / log を自動維持。1 ソース取り込みで 10-15 ページが同時更新される。
|
||||
- `CLAUDE.md`(または `AGENTS.md` / `GEMINI.md`)— ページ種別・ワークフロー・フォーマット・健全性チェックルールを定義するスキーマ。
|
||||
|
||||
運用は3種:
|
||||
|
||||
- **ingest**: 新規ソース投入 → 要約ページ作成 → index 更新 → entity/concept 更新 → `log.md` 追記。
|
||||
- **query**: wiki を検索し citation 付きで答える。意義ある合成は `syntheses/` として保存。
|
||||
- **lint**: 矛盾・stale claim・孤立ページ・参照抜け・データ欠落の定期点検。
|
||||
|
||||
`SamurAIGPT/llm-wiki-agent` がこのパターンを具体化しており、ディレクトリ構造が示唆的(なお `/wiki-lint` のプロダクション実装としては OpenClaw `memory-wiki` extension があり、§4 / §9 で触れる):
|
||||
|
||||
```
|
||||
wiki/
|
||||
├── index.md (全ページのカタログ)
|
||||
├── log.md (append-only 作業記録)
|
||||
├── overview.md (生きた合成ビュー)
|
||||
├── sources/ (原文1つ = 1サマリ)
|
||||
├── entities/ (人・会社・プロジェクト)
|
||||
├── concepts/ (考え方・フレーム・手法)
|
||||
└── syntheses/ (query 回答をページ化)
|
||||
graph/
|
||||
├── graph.json (SHA256 キャッシュ付きノード/エッジ)
|
||||
└── graph.html (vis.js で可視化)
|
||||
```
|
||||
|
||||
スラッシュコマンド例:
|
||||
```
|
||||
/wiki-ingest raw/papers/my-paper.md
|
||||
/wiki-query "what are the main themes?"
|
||||
/wiki-lint
|
||||
/wiki-graph
|
||||
```
|
||||
|
||||
### LLM Wiki vs RAG という論点
|
||||
|
||||
MindStudio の比較記事が要点をまとめている。
|
||||
|
||||
- **LLM Wiki が有利**: 文書 100 本未満、構造化コンテンツ、精度・監査性重視、速度(Git で versioning できる)。
|
||||
- **RAG が必要**: 1,000 本以上、非構造テキスト、open-ended な質問、未知のドメイン。
|
||||
- 決定論的 wikilink retrieval と、SHA256 キャッシュで差分のみ再処理。
|
||||
|
||||
### 設計上の示唆
|
||||
|
||||
- **可変 KBL と不変 BF の分離**が強い。yoi なら前者が Chronicle 的な自動メモリ、後者が `AGENTS.md` / 人間のガイド。
|
||||
- **retrieval を埋め込みではなく決定論的 wikilink でやる**アプローチは、少量ドメインで意外と強い。
|
||||
- エージェントに書かせる `log.md` と `index.md` の append-only 運用は、後で diff / git で検証しやすい。
|
||||
|
||||
一次ソース:
|
||||
- https://x.com/shannholmberg/status/2044111115878326444
|
||||
- https://x.com/shannholmberg/status/2043307903822844087 (Ronin / 17 markdown files)
|
||||
- https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f
|
||||
- https://github.com/SamurAIGPT/llm-wiki-agent
|
||||
- https://www.mindstudio.ai/blog/llm-wiki-vs-rag-knowledge-base
|
||||
|
||||
---
|
||||
|
||||
## 3. Nous Research Hermes Agent
|
||||
|
||||
Self-improving agent を名乗るフレーム。メモリ周りは **3 層 + クローズドな学習ループ**。
|
||||
|
||||
### 3 層メモリ
|
||||
|
||||
- **Persistent Memory**: `MEMORY.md` / `USER.md` の Markdown + SQLite + FTS5 による過去セッション全文検索。char limit が hard-coded で **`MEMORY.md` 2,200 chars / `USER.md` 1,375 chars**(合計 ≒ 1,300 tokens)、system prompt 起動時スナップショット
|
||||
- **Skill Library**: 複雑タスクの帰結として skill 化。`agentskills.io` 標準準拠(§5)、`~/.hermes/skills/<name>/SKILL.md` に保存。`skill_manage` tool (action: create/patch/edit/delete/write_file/remove_file) 経由で agent が自ら CRUD する
|
||||
- **User Model (Honcho)**: "dialectic user modeling" でセッションを跨いでユーザー像を蓄積(外部 Honcho サービス連携)
|
||||
|
||||
### 学習ループ(`run_agent.py` 実装より)
|
||||
|
||||
既存 ref の「5 + tool call で自律生成」「15 tool call ごと reflection」は誤り。実装を読むと:
|
||||
|
||||
- `_skill_nudge_interval = 10`(デフォルト、設定可)で **skill review**、`_iters_since_skill` がツール呼び出しのたび increment
|
||||
- memory nudge は別に **10 turns** で発火
|
||||
- 閾値超過で **background thread に別 AIAgent を fork**(max_iterations = 8)、review prompt を与える
|
||||
- レビュー本体は **1 LLM call** の自由形式、続いて agent が必要なら `skill_manage` / `memory` tool を呼んで書き戻す
|
||||
- review prompt 原文(`_SKILL_REVIEW_PROMPT`, `run_agent.py:2738-2746`):
|
||||
> Review the conversation above and consider saving or updating a skill if appropriate.
|
||||
> Focus on: was a non-trivial approach used to complete a task that required trial and error...
|
||||
> **If nothing is worth saving, just say 'Nothing to save.' and stop.**
|
||||
- 末尾の "Nothing to save." 指示が肝。頻繁発火でも中身ゼロの場合は NOP で抜ける設計。yoi extract の「空配列許容」の直接ソース
|
||||
|
||||
### 書き込み機構
|
||||
|
||||
- `skill_manage` tool: OpenAI function-calling schema。frontmatter YAML 検証、security scan、atomic write (temp + `os.replace()`)
|
||||
- `memory` tool: target = `memory | user`、action = `add | replace | remove`。fcntl/msvcrt で file lock、entry-based rewrite
|
||||
- **char limit 超過時は `add` を hard reject**(`memory_tool.py:248-259`)。エラーメッセージに `Memory at {current}/{limit} chars. Adding this entry would exceed the limit. Replace or remove existing entries first.` と明記し、LLM 側に「自分で削減してから再追加」を強制する。GC は完全に LLM agentic(決定論的 eviction は無い)、人間 offer や scoring も無い、非常にシンプルな設計
|
||||
- FTS5: `messages_fts` virtual table + INSERT/DELETE/UPDATE trigger で auto-sync、CJK は LIKE フォールバック
|
||||
|
||||
### 実装観点
|
||||
|
||||
- 主要ディレクトリ: `agent/` `skills/` `tools/` `gateway/` `hermes_cli/` `tui_gateway/` `cron/` `plugins/` `optional-skills/` など
|
||||
- バックエンド抽象: local / Docker / SSH / Daytona / Singularity / Modal。Daytona / Modal ではアイドル時 hibernation で永続化コストを削る
|
||||
- OpenClaw からの移行: `hermes claw migrate`
|
||||
|
||||
### 設計上の示唆
|
||||
|
||||
- **"skill"= procedural memory として episodic / semantic から分離する**のは yoi にとっても綺麗。Claude Code の skill と接続できる余地がある。
|
||||
- **FTS5 + LLM 要約ハイブリッド**で、ベクタを入れずにそこそこ回せる事例として参考価値が高い(少量ドメインなら LLM Wiki 論と同じ示唆)。
|
||||
- **Honcho 的 user model** を semantic profile として固定注入する運用は、Codex の memory summary と形式的に同じ。
|
||||
|
||||
一次ソース:
|
||||
- https://github.com/NousResearch/hermes-agent
|
||||
- https://github.com/NousResearch/hermes-agent/blob/main/README.md
|
||||
- https://hermes-agent.nousresearch.com/docs/
|
||||
- https://hermes-agent.nousresearch.com/
|
||||
|
||||
---
|
||||
|
||||
## 4. 周辺事例(比較のため)
|
||||
|
||||
### Letta / MemGPT
|
||||
|
||||
- 4 区画: **Message Buffer**(直近)、**Core Memory**(in-context の編集可能 block: user prefs / persona)、**Recall Memory**(全履歴の検索)、**Archival Memory**(ベクトル or グラフ DB に外部化された宣言的知識)。
|
||||
- OS メタファ: context window = RAM、外部ストア = Disk。エージェントが function call で階層間を移動させる。
|
||||
- memory block = `{label, description, value, char_limit}`。
|
||||
- **sleep-time agent** が idle 中に非同期で block を書き換える。主要 agent と **別 process で並列**に走り、`core_memory_replace` / `core_memory_append` / archival tool 群を呼んで **直接 memory block を書き換える**。主 agent のターン処理をブロックしないため、「mid-session で memory 品質を上げる」のが特徴。
|
||||
- context 限界近くで要約による eviction → 再帰要約で古いメッセージを累進圧縮。**eviction 判断は context window fill の決定論的しきい値**で発火するが、要約の内容生成は LLM。evicted message は recursive summary として memory block に残り、古いほど重みが相対的に下がる。
|
||||
|
||||
### Cloudflare Agent Memory (2026-04 Agents Week)
|
||||
|
||||
- 分類: **Fact / Event / Instruction / Task**(Task のみベクトル索引除外)。
|
||||
- 基盤: Durable Objects(プロファイル毎の SQLite、FTS・supersession chain・tx write を担う)、Vectorize(セマンティック)、Workers AI(Llama 4 Scout: 構造化、Nemotron 3: 自然文合成)。
|
||||
- **Supersession chain は決定論的**(公式 blog 再確認): Fact / Instruction は classification pipeline で **normalized topic key** が振られ、同 key の新 memory が来ると旧 memory を **supersede(物理削除せず forward pointer で版チェーン化)**。LLM 判断は入らない。superseded 側の vector は並行して削除され、新 vector が upsert される。SHA-256 content-addressed ID で `INSERT OR IGNORE` による冪等 dedupe も併用。
|
||||
- API:
|
||||
```javascript
|
||||
const profile = await env.MEMORY.getProfile("my-project");
|
||||
await profile.ingest([messages], { sessionId });
|
||||
await profile.remember({ content, sessionId });
|
||||
const results = await profile.recall("query");
|
||||
```
|
||||
- Ingest は 10k 文字チャンク + 2 メッセージ重複、並行 4 チャンク。Verification が 8 種類(entity / temporal / factual 支援)。
|
||||
- Retrieval は 5 経路並行(FTS Porter stemming / exact fact-key / raw message / ベクトル直 / HyDE)を Reciprocal Rank Fusion でマージ。fact-key 優先、recency タイブレーク。時制クエリは regex + 算術で決定論的処理。
|
||||
- SHA-256 content-addressed ID で冪等。
|
||||
- 「メモリはあなたのもの」エクスポート前提。
|
||||
|
||||
### LinkedIn Cognitive Memory Agent (CMA)
|
||||
|
||||
- 3 層: **Episodic**(対話履歴)/ **Semantic**(構造化事実)/ **Procedural**(学習済みワークフロー)。
|
||||
- 共有メモリ基盤として planner / reasoner / executor 複数エージェントが同じ層を参照。
|
||||
- Recent context retrieval + semantic search + summarization compaction で階層管理。
|
||||
- 高 stake 用途は human validation フローを挟む。
|
||||
|
||||
### OpenClaw
|
||||
|
||||
Peter Steinberger が 2025-11 に出したメッセージングファースト agent。2026-02 に本人は OpenAI 入社、プロジェクトは foundation へ移譲。Hermes Agent の `hermes claw migrate` はここからの移行導線。設計は **Markdown ファイルのみに全状態を持つ**という極端な透明性志向で、yoi の「ファイル + git」方針との親和性が最も高い。
|
||||
|
||||
Agent Workspace(`~/.openclaw/workspace/`)構成:
|
||||
|
||||
| ファイル | 役割 |
|
||||
|----------|------|
|
||||
| `AGENTS.md` | 運用指示とメモリ使用ガイドライン |
|
||||
| `SOUL.md` | ペルソナ・口調・境界 |
|
||||
| `USER.md` | ユーザー識別と呼称の好み |
|
||||
| `IDENTITY.md` | エージェント名・絵文字 |
|
||||
| `TOOLS.md` | ローカルツールのメモ |
|
||||
| `MEMORY.md` | 長期記憶(durable facts / preferences / decisions)。DM セッション起動時に常駐ロード |
|
||||
| `memory/YYYY-MM-DD.md` | 日次ノート。**当日 + 前日のみ**自動ロード |
|
||||
| `DREAMS.md` | dreaming sweep の人間レビュー用サマリ(任意) |
|
||||
| `skills/` | workspace 固有 skill override(最高優先) |
|
||||
|
||||
エージェントが触るツール:
|
||||
|
||||
- `memory_search`: 文言違いでも引ける semantic search
|
||||
- `memory_get`: ファイル or 行範囲の読み取り
|
||||
|
||||
メモリ更新のタイミング(`extensions/memory-core/src/` 実装より):
|
||||
|
||||
- **Compaction 前の silent turn**: `before_agent_reply` フックで `DREAMING_SYSTEM_EVENT_TEXT` を検出、`runShortTermDreamingPromotionIfTriggered` が発火。エージェントへの write-back 指示ではなく、**後述の dreaming pass をインラインで走らせる**仕組み
|
||||
- **Dreaming pass (optional)**: 実体は **3 passes** (Light + Deep + REM)、cron default `"0 3 * * *"` UTC
|
||||
- **Light**: 最近の recall / daily / session signals を short-term store に ingest + narrative 生成(**subagent LLM call 1 回**、`NARRATIVE_SYSTEM_PROMPT` で poetic な dream diary)
|
||||
- **Deep**: `applyShortTermPromotions()` が promotion を決定。**LLM call ゼロ**、以下の 6 重みスコアと 3 ゲートで機械判断
|
||||
- 重み: frequency (0.24) / relevance (0.30) / diversity (0.15) / recency (0.15) / consolidation (0.10) / conceptual (0.06) + pass boost
|
||||
- ゲート: `minScore 0.8` / `minRecallCount 3` / `minUniqueQueries 3`
|
||||
- **REM**: テーマ反映 + narrative LLM call 1 回
|
||||
- promotion 通過項目のみ `MEMORY.md` に append、`DREAMS.md` に人間レビュー用 diary
|
||||
- **書き込み制約**: `MEMORY.md` は hardcoded path、`memory/YYYY-MM-DD.md` のみ許可(regex `SHORT_TERM_PATH_RE`)。エージェントへ expose されているのは `memory_search` / `memory_get` の **read-only**。書き込みは系統内部のみ
|
||||
- **Lock**: `memory/.dreams/short-term-promotion.lock` を `wx` フラグで exclusive create、60s stale 検出 + 10s wait timeout、in-process の Map も併用
|
||||
- **モデルが "覚えている" のはディスクに書かれた内容だけ**、という明示ポリシー。隠れた state 無し
|
||||
|
||||
**yoi にとって重要**: consolidation を LLM 依存から切り離せる見本。narrative は subagent が生成するが、promotion の判断は純機械(scoring)。yoi の plan では Scope 外(consolidation は当面 agent 委任)だが、成熟したカテゴリから決定論的 promotion に差し替える upgrade path の参考になる。
|
||||
|
||||
**GC 観点の追加詳細**(`extensions/memory-core/src/short-term-promotion.ts:1518-1652` 実装より):
|
||||
|
||||
- `applyShortTermPromotions()` は **append 専用**。gate 通過候補の snippet を `MEMORY.md` 末尾に `<!-- openclaw-memory-promotion:<hash> --> ` marker 付きで追記するだけで、既存 `MEMORY.md` ブロックの書き換えや削除は一切行わない
|
||||
- **重複回避**: marker set を先読みし、既に書かれた key はスキップ
|
||||
- **汚染検出**: `isContaminatedDreamingSnippet()` で dreaming narrative prompt が snippet に混入している候補を promotion 前に弾く(再帰汚染防止)
|
||||
- **日次ノートの decay は物理削除ではなく search score 減衰のみ**(`memory/temporal-decay.ts`)。`halfLifeDays = 30` で `exp(-ln2/HL * age)` を score に乗じる。対象は `memory/YYYY-MM-DD.md` 形式のファイル限定で、`MEMORY.md` や topic ファイルは evergreen 扱い(減衰しない)
|
||||
- **自己参照汚染の退避**: `dreaming-repair.ts` は dreaming narrative が session corpus や `DREAMS.md` に逆流した場合を検出し、該当ファイルを `.openclaw-repair/dreaming/<timestamp>/` に **rename で退避**(削除ではない)。lint と同じく audit-first の GC スタイル
|
||||
- `MEMORY.md` 側の GC(重複 block の統合、stale record の drop 等)は **この extension には存在しない**。書き込みは promotion の append のみで、ユーザーが手で消すか `memory-wiki` lint のレポート経由で扱う
|
||||
|
||||
OpenClaw は「**削除は人間、script は append と退避まで**」という強い原則が貫かれている。
|
||||
|
||||
設計上の示唆:
|
||||
|
||||
- Workspace = git リポジトリ 1 本で完結、配置もフラット。yoi の pod workspace 概念にそのまま借用できる。
|
||||
- 秘密は workspace **外**の `~/.openclaw/` 側(auth / credentials / session transcripts / managed skills)に退避する分離設計は、pod sandbox 境界を越えない運用の手本になる。
|
||||
- `memory/YYYY-MM-DD.md` の日次切り分け + 「当日+前日のみ load」は、時系列の自然減衰を Markdown で素直に表現できる良い pattern。
|
||||
|
||||
### LangChain Agent Builder memory(参考)
|
||||
|
||||
- 記事は 301 redirect のため未取得。`https://www.langchain.com/blog/how-we-built-agent-builders-memory-system` に原文あり。semantic / episodic / procedural 三分類は共通。
|
||||
|
||||
一次ソース:
|
||||
- https://www.letta.com/blog/agent-memory
|
||||
- https://blog.cloudflare.com/introducing-agent-memory/
|
||||
- https://www.infoq.com/news/2026/04/linkedin-cognitive-memory-agent/
|
||||
- https://github.com/openclaw/openclaw/blob/main/docs/concepts/memory.md
|
||||
- https://docs.openclaw.ai/concepts/agent-workspace
|
||||
- https://en.wikipedia.org/wiki/OpenClaw
|
||||
|
||||
---
|
||||
|
||||
## 5. Agent Skills 標準(procedural memory の実装単位)
|
||||
|
||||
Hermes / OpenClaw / Claude Code / OpenAI Codex / Cursor / GitHub Copilot 等が揃って採用している **`agentskills.io` オープン標準**。Anthropic が 2025-12 に発表し、2026-03 時点で事実上の共通フォーマットになっている。memory 設計の「手続き的記憶 (procedural memory)」のほぼ全てがこの単位で流通するので、yoi でも独自フォーマットを避けて素直にこれに乗るのが合理的。
|
||||
|
||||
### SKILL.md の最小仕様
|
||||
|
||||
```
|
||||
skill-name/
|
||||
├── SKILL.md # 必須: frontmatter + Markdown 本文
|
||||
├── scripts/ # 任意: 実行可能コード
|
||||
├── references/ # 任意: 詳細ドキュメント
|
||||
└── assets/ # 任意: テンプレ・画像・lookup など
|
||||
```
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: pdf-processing
|
||||
description: Extract PDF text, fill forms, merge files. Use when handling PDFs.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: example-org
|
||||
version: "1.0"
|
||||
---
|
||||
```
|
||||
|
||||
frontmatter 仕様:
|
||||
|
||||
| Field | 必須 | 制約 |
|
||||
|-------|------|------|
|
||||
| `name` | ○ | 64 字以下、小文字英数とハイフンのみ、親ディレクトリ名と一致 |
|
||||
| `description` | ○ | 1024 字以下。**何をするか + いつ使うか**の両方を含める |
|
||||
| `license` | ― | 任意 |
|
||||
| `compatibility` | ― | 500 字以下。env 依存(特定 CLI / Python 3.14+ 等)がある場合 |
|
||||
| `metadata` | ― | 任意 key/value |
|
||||
| `allowed-tools` | ― | 実験的。space-separated `Bash(git:*) Read` 等 |
|
||||
|
||||
### Progressive disclosure(これが核)
|
||||
|
||||
Skill は 3 段でロードされる:
|
||||
|
||||
1. **Metadata (~100 tokens)**: `name` + `description` のみ、全 skill が起動時に常駐
|
||||
2. **Instructions (<5k tokens 推奨)**: skill が発動した時に本文がロード
|
||||
3. **Resources**: `scripts/` `references/` `assets/` は必要時のみ個別に読まれる
|
||||
|
||||
→ **常駐コストは「名前 + 一行説明」だけ**で、本体は遅延ロード。これがメモリ設計における宣言的知識 (LLM Wiki の `index.md` 常駐) と完全に同型で、**知識 / skill を同じ「目次のみ常駐・本体リンクで遅延」モデルに統一できる**。
|
||||
|
||||
### Claude Code の拡張
|
||||
|
||||
標準 + α として Claude Code が独自に足している項目:
|
||||
|
||||
- `disable-model-invocation: true`: ユーザーしか起動できない(deploy / commit 等の副作用ありタスクに使う)
|
||||
- `user-invocable: false`: エージェントしか起動しない(背景知識用。`/` メニューから隠れる)
|
||||
- `paths: ["src/**/*.ts"]`: **ファイル glob マッチ時のみ自動発動**。関心スコープを暗黙に絞る
|
||||
- `context: fork` + `agent: Explore`: サブエージェントの fork context で実行(isolated context)
|
||||
- `allowed-tools`: skill 有効時だけ事前承認されたツール
|
||||
- `hooks`: skill ライフサイクルにフックを紐付け
|
||||
- `$ARGUMENTS` / `${CLAUDE_SESSION_ID}` / `${CLAUDE_SKILL_DIR}` による文字列置換
|
||||
- `` !`<cmd>` `` でプリプロセッサとしてシェルを走らせ、結果を本文に差し込む
|
||||
|
||||
Claude Code の skill 格納階層(上から優先):
|
||||
|
||||
```
|
||||
enterprise (managed) > personal (~/.claude/skills/) > project (.claude/skills/) > plugin (<plugin>/skills/)
|
||||
```
|
||||
|
||||
プラグイン skill のみ `plugin-name:skill-name` の namespace を持つ。Live change detection(ディレクトリ監視)と monorepo 対応のネストディレクトリ自動検出もある。
|
||||
|
||||
Skill content のライフサイクルは重要で、**一度発動すると rendered 内容が会話に単一メッセージとして入り、以後再読み込みされない**。auto-compaction 時は各 skill の直近 invocation だけが冒頭 5,000 tokens 維持され、全 skill 合算 25,000 tokens の予算内で新しい順に残す。つまり「skill の本体は session 中ずっと context に居座る」前提で書く必要がある。
|
||||
|
||||
### yoi への示唆
|
||||
|
||||
- **procedural memory は SKILL.md で表現**する。`.claude/skills/` に人間手入れの skill を置き、エージェントが自動生成する手続きは別ディレクトリ(例: `memory/skills/`)で分離、混ざらないようにする。BF/KBL 分離の原則に一致。
|
||||
- **`paths:` によるスコープ絞り**は、前回議論した「Pod の所属スコープ = ディレクトリ階層」と自然に噛む。`paths: ["crates/protocol/**"]` の skill は protocol スコープの pod でだけ発動、という運用が素直にできる。
|
||||
- **progressive disclosure の 3 段構造**をメモリ本体にも適用する。知識ファイル先頭に 1 行 description を YAML で持たせ、`index.md` に description だけ集約、本体は wikilink で遅延ロード、という揃え方が可能。
|
||||
- 独自フォーマットを作らない。Hermes / OpenClaw / Claude Code のエコシステム skill をそのまま引き込める。
|
||||
|
||||
一次ソース:
|
||||
- https://agentskills.io/specification
|
||||
- https://code.claude.com/docs/en/skills
|
||||
- https://github.com/anthropics/skills
|
||||
- https://developers.openai.com/codex/skills
|
||||
|
||||
---
|
||||
|
||||
## 6. プロンプト・スキルの継続的チューニング (empirical prompt tuning pattern)
|
||||
|
||||
メモリや skill の**中身を腐らせない**側の話。公開されている prompt tuning pattern として、agent-facing な指示を新規 subagent に実行させ、実行者の自己申告と指示側メトリクスを突き合わせて反復改善する方法がある。yoi のように skill を蓄積する設計では、**書いた直後に客観的に試す**仕組みが無いと品質が崩れていく。ここへの素直な当てはめ材料として記録。
|
||||
|
||||
### 基本思想
|
||||
|
||||
> Agent 向けテキスト指示(skill / slash command / task プロンプト / CLAUDE.md 節 / コード生成プロンプト)を、**バイアスを排した別の実行者**に動かしてもらい、**両面(実行者の自己申告 + 指示側メトリクス)**で評価して反復改善する。
|
||||
|
||||
書き手が自分で評価すると "こういう意図だったから" のバイアスで通ってしまう。だから毎反復ごとに **新規サブエージェント** に dispatch して、結果を構造化報告させる。
|
||||
|
||||
### 7 ステップ
|
||||
|
||||
1. description と body の整合性チェック(description 1024 字制約もここで効く)
|
||||
2. baseline: 要件チェックリスト + シナリオ(典型 1 + edge 1-2)を固定
|
||||
3. 新規 subagent で実行
|
||||
4. 両面評価
|
||||
5. 最小 patch で 1 箇所だけ直す
|
||||
6. 新規 subagent で再実行
|
||||
7. 収束判定
|
||||
|
||||
### 実行 AI に出させるレポート形式
|
||||
|
||||
- 成果物
|
||||
- 要件達成([critical] は最低ライン明記)
|
||||
- **不明瞭だった点**
|
||||
- **裁量で補完した箇所**
|
||||
- 再試行回数
|
||||
|
||||
### 指示側メトリクス(機械抽出)
|
||||
|
||||
Claude Code の Task tool 戻り値から:
|
||||
- `tool_uses` 数
|
||||
- `duration_ms`
|
||||
- 要件チェックリスト達成率(critical / non-critical 別)
|
||||
|
||||
### 収束判定
|
||||
|
||||
> 連続 2 回で新規不明瞭点ゼロ、精度の伸び ≤3pt、step 分散 ±10%、実行時間 ±15%
|
||||
|
||||
シナリオは hold-out を用意して過適合検出、評価基準の後付け修正は無効化扱い。
|
||||
|
||||
### クリティカルな制約
|
||||
|
||||
> 毎回**新規** AI を dispatch すること。同じセッション再利用は前回の指摘を学習してしまい、指標が腐る。
|
||||
|
||||
### yoi への示唆
|
||||
|
||||
- **skill や lessons を新規追加した直後に、同じ yoi ハーネス内の別 pod で実行して評価**する自動フロー("skill doctor" 的な存在)を作れる。これは yoi が pod factory を持っている点と相性がいい。
|
||||
- 失敗ログを書いた後、「同じ失敗が再現しないか」を新規 pod で試走する検証ステップが、構造的に**メモリ整備の一部**に組み込める。skill 化しない失敗ログでも有効。
|
||||
- 評価指標を自前で定義しておくと、後で他人(or 未来の自分)が skill を更新した時に腐敗検知できる。
|
||||
- 実体は skill 自身として配布されている例がある。yoi のメンテ用 skill セットのテンプレにも応用できる。
|
||||
|
||||
一次ソースは公開 sanitize branch では省略する。
|
||||
|
||||
---
|
||||
|
||||
## 7. パターン抽出
|
||||
|
||||
上記を一枚にすると、2026 年現在のメモリ実装はだいたい以下の次元の組み合わせに収まる。
|
||||
|
||||
| 次元 | 主な選択肢 |
|
||||
|------|-----------|
|
||||
| 分類軸 | 宣言的知識 / エピソード / 手続き(skill) / プロファイル の 2〜4 層 |
|
||||
| 抽出タイミング | ターン内(write-back)/ セッション終了(rollup)/ 完全非同期(sleep-time agent, bg chronicle) |
|
||||
| 圧縮方式 | 要約 eviction / recursive summarization / consolidation pass / 差分更新 wiki |
|
||||
| 保存形式 | Markdown ファイル群 / SQLite (+FTS) / Vector store / Graph DB |
|
||||
| 取り出し方式 | 常駐注入(summary fixed) / 決定論的 wikilink / FTS / ベクトル / HyDE / RRF 融合 |
|
||||
| ユーザー編集性 | 不可視 / 読取のみ / 手編集前提 |
|
||||
| スコープ | per-user / per-project / shared across agents |
|
||||
|
||||
yoi で意思決定すべきポイントはこの対応表:
|
||||
|
||||
- **Pod / Agent の「skill」概念を Hermes 風に明示すべきか**。現状の controller.rs には "sub-agent spawn" はあるが、skill を書き出して再利用する仕組みは無い。
|
||||
- **Codex Chronicle 風の "consolidation モデルを別途設定" 構成**は、yoi の llm provider policy(Ollama / Codex OAuth / Anthropic)と相性が良い。軽量 extract と重い consolidation を別プロバイダに張れる。
|
||||
- **LLM Wiki パターンを採用する場合**、既に `docs/` と `tickets/` が Markdown + git で運用されているので、`memory/` ディレクトリを足して Git で可観測にしておくのが自然。RAG やベクトル化より先に、wikilink / index.md / log.md で足りるか見極めるべき。
|
||||
- **storage 層**: SQLite は既存 crate 構成にもフィット。Cloudflare Agent Memory / Codex の SQLite + 最終 Markdown の 2 段は移植しやすい。
|
||||
- **prompt injection 対策**: Chronicle が注意書きしている通り、観測チャンネルを増やすと攻撃面が広がる。yoi では pod の sandbox 境界とメモリ生成を同じ境界で括る必要がある。
|
||||
|
||||
---
|
||||
|
||||
## 8. GC 機構の横断比較
|
||||
|
||||
`docs/plan/memory.md` §GC は「consolidation とは別経路で memory を再評価し、drop / merge / split / `replaced` chain 整理を行う」ことを決めた段階で、判断主体と処理種別の仕様をこれから詰める。本節は他プロジェクトの GC 設計を共通の 6 軸で並べて、yoi で採るべき型の材料とする。
|
||||
|
||||
### 8.1 比較表
|
||||
|
||||
対象の行は「その system が実装している GC 機構」単位で、同じプロジェクトが複数機構を持つ場合は複数行にした。
|
||||
|
||||
| # | プロジェクト / 機構 | 対象 | トリガー | 判断主体 | 処理種別 | 人間介入点 | 履歴保持 |
|
||||
|---|---------------------|------|----------|----------|----------|-----------|---------|
|
||||
| 1 | Codex consolidation | `MEMORY.md` block / `memory_summary.md` / `skills/*` | session idle + age | **LLM agentic**(sub-agent) | drop / merge / split / rewrite、removed thread id に紐づく block を **部分削除**(block 丸ごとは消さない、thread-local 記述のみ除去) | 無し(`/memories` で thread 単位 opt-out のみ) | git 任意、memory_root は単なる Markdown |
|
||||
| 2 | Codex extension resource retention | `memories_extensions/<name>/resources/*.md` | consolidation 実行直前に cron 相当 | **決定論** | **物理削除** (filename timestamp cutoff) | 無し | 完全消去、consolidation prompt に removed list を通知 |
|
||||
| 3 | Codex stage1 pruning | `stage1_outputs` SQLite row | consolidation 後 / 容量超過 | **決定論** | `selected_for_consolidation = 0` の古い row を cutoff + `LIMIT` で DELETE | 無し | SQL 完全削除 |
|
||||
| 4 | Hermes `memory` tool | `MEMORY.md` / `USER.md` のエントリ | **char limit (2,200 / 1,375) 超過時の add 拒否** | **LLM agentic**(エラー受けて自分で `replace` / `remove` を呼ぶ) | drop / rewrite | 無し(all-agent) | ディスク消去(file lock で tx 化) |
|
||||
| 5 | Hermes background review | entry / skill | turn / iter カウンタ(10 デフォルト) | **LLM agentic**(fork agent、max_iterations = 8) | add / update / delete をレビュー判断、`Nothing to save.` で no-op | 無し | same as 4 |
|
||||
| 6 | OpenClaw `applyShortTermPromotions` | promotion candidate → `MEMORY.md` | Deep dreaming phase | **決定論**(6 重み合算 + 3 ゲート、LLM ゼロ) | **append のみ**(`<!-- openclaw-memory-promotion:<hash> -->` marker、既存 block は触らない) | 無し | 追記のみ、削除系統は別 |
|
||||
| 7 | OpenClaw temporal decay | `memory/YYYY-MM-DD.md` | search 呼び出し毎 | **決定論** | **物理削除せず search score 減衰**(half-life 30d) | 無し | ファイル残置、検索順位だけ沈む |
|
||||
| 8 | OpenClaw dreaming-repair | `DREAMS.md` / session-corpus / session-ingestion | 手動 + audit CLI | **決定論** | **archive 退避**(`.openclaw-repair/dreaming/<timestamp>/` に rename)、完全削除しない | 退避後は手動判断 | archive ディレクトリに rename で保持 |
|
||||
| 9 | memory-wiki `/wiki-lint`(OpenClaw extension、SamurAIGPT/llm-wiki-agent と Karpathy llm-wiki の production 実装) | wiki page / claim | `/wiki-lint` 手動 or cron 想定 | **決定論**(issue 検出ロジック) + その後の **human/LLM 任意判断** | **削除ゼロ**、`reports/lint.md` に issue を書き出すのみ。修正は別コマンド / 人間が行う | 全出力点が人間承認 | 原ファイル無変更、report は上書き |
|
||||
| 10 | Cloudflare Agent Memory supersession | Fact / Instruction row(Durable Object SQLite) | 新 memory ingest 時 | **決定論**(normalized topic key が一致した瞬間) | **supersede**(旧 row を forward pointer で版チェーン化、物理削除せず)、vector は非同期 delete + new upsert | 無し(全自動) | SQLite に旧版残置、version chain で参照可能 |
|
||||
| 11 | Letta sleep-time agent | in-context memory block | 非同期 idle process | **LLM agentic** | `core_memory_replace` / `core_memory_append` / archival 移動で block 書き換え | 無し | 書き換え後の block が保存、旧 block は失われる |
|
||||
| 12 | Letta recursive summarization | message buffer | **context window fill の決定論閾値** | 発火は決定論 / 要約内容は LLM | evict + 再帰要約(古いほど重み減) | 無し | 要約に畳み込まれる(原メッセージは消失) |
|
||||
| 13 | LinkedIn CMA(公開情報レベル) | Episodic / Semantic / Procedural 各層 | 未公開 | 未公開 | **summarization による compaction**(階層別の drop 仕様は非公開、"cache invalidation is one of the hardest problems" と明示) | **高 stake 用途は human validation** を挟む | 非公開 |
|
||||
|
||||
### 8.2 軸別の知見
|
||||
|
||||
**対象(何が GC されるか)の粒度差**:
|
||||
|
||||
- 最小粒度の Hermes(entry)と Cloudflare(row)は、LLM がエントリ単位で `remove` / `replace` する設計に寄る。
|
||||
- 中粒度の Codex(`MEMORY.md` block)は、block 内を thread id で部分削除しながら必要なら split / rewrite する agentic 寄り。
|
||||
- 最大粒度の OpenClaw / memory-wiki は **ファイル単位でのみ削除 / 退避** し、file 中身は LLM / 人間に委ねる。`docs/plan/memory.md` の「1 件 1 ファイル」方針とは **OpenClaw の粒度が最も近い**。
|
||||
|
||||
**トリガー(いつ GC するか)の 4 パターン**:
|
||||
|
||||
1. **容量超過 hard reject**(Hermes): 追加要求を失敗で返して LLM に自律対処を強制。**決定論的 trigger + agentic 処理**で、設計最小コスト。
|
||||
2. **session idle + age**(Codex consolidation): 人間の活動終了を待って非同期、最もユーザー体感を壊しにくい。
|
||||
3. **cron / scheduled sweep**(OpenClaw dreaming default `0 3 * * *`, Codex extension retention): 定期的・予測可能。人間 review との組み合わせがしやすい。
|
||||
4. **ingest 時の即時**(Cloudflare supersession): 書き込みの tx 内で完結、後続 GC 走査が要らない。topic key 設計が前提。
|
||||
|
||||
yoi の plan は (2) consolidation で rewrite 許可を置きつつ、GC は (3) 方向で別経路という構造。これは Codex / OpenClaw の両方と整合する。
|
||||
|
||||
**判断主体の 3 系統**:
|
||||
|
||||
- **決定論のみ**: Codex retention / stage1 pruning / OpenClaw temporal decay / Cloudflare supersession / lint 検出。条件がはっきりしているもの(age / key 一致 / 構造的 issue)は決定論が強い。
|
||||
- **決定論 scoring → 閾値 gate → 機械適用**: OpenClaw Deep promotion。LLM の揺れを除き、コストも LLM コールゼロ。ただし対象が append 側のみで、削除には使われていない。
|
||||
- **LLM agentic**: Codex consolidation / Hermes review / Letta sleep-time。判断の柔軟性(block 内部分削除、context 依存の merge)を LLM に委ねる。
|
||||
|
||||
`docs/plan/memory.md` は consolidation が LLM agentic、GC も暫定的に **LLM agentic + Linter Warn 併用**としている。完全に一致する事例は **Codex consolidation の consolidation prompt**(836 行)で、「removed thread id を `MEMORY.md` から部分削除し、blockに他の thread が残っている場合は split / rewrite して保持」という手続きを自然言語で指示している。**yoi は Linter 側に警告カテゴリ(類似 slug / `replaced` 滞留 / sources 過多 / stale)を先に定義し、GC 実行の agent プロンプトはそれを入力にする**構造が素直。
|
||||
|
||||
**処理種別の選択肢**:
|
||||
|
||||
- `drop / merge / split / rewrite` の組み合わせは Codex consolidation が最も自由度高く、Hermes もそれに近い(entry 粒度)。
|
||||
- `replaced` chain の整理は **Cloudflare だけが自動で版チェーン維持**、他は LLM 任せ。yoi は decision record に `replaced_by` を入れているので、Cloudflare 方式の forward pointer 概念を **人間可読な `replaced_by:` frontmatter** で既に踏襲している。GC 時に chain をどこまで短く畳むか(長大な `a → b → c → d` を `a → d` に圧縮するか)は未決定論点で、Cloudflare は圧縮せず chain を保持する設計。
|
||||
- **`split` は Codex だけが明示**。block 内に複数 thread id が混ざった場合に thread id 単位で分ける。yoi の「1 件 1 ファイル」方針では split = ファイル分割となり、主題の粒度判断は GC agent に委ねる必要がある。
|
||||
|
||||
**人間介入点の 3 段**:
|
||||
|
||||
- 完全無介入: Codex / Cloudflare / Letta / Hermes
|
||||
- audit-first(issue を surface し、人間が決断): memory-wiki lint / OpenClaw dreaming-repair
|
||||
- high-stake 限定 gate: LinkedIn CMA
|
||||
|
||||
yoi の plan は「人間 offer 承認を併用」なので **audit-first に寄る**のが自然。lint 相当の Warn を Linter で出し、LLM consolidation / GC がそれを消費する前に人間が承認 / 拒否できる UI を提供する構造。memory-wiki lint は `reports/lint.md` というシンプルな Markdown 出力なので、そのまま `memory/reports/gc-lint.md` 相当を tick off する実装が参考になる。
|
||||
|
||||
**履歴保持の 3 モデル**:
|
||||
|
||||
1. **物理削除**: Codex retention / stage1 / Hermes / Letta(要約で畳む分は実質消失)
|
||||
2. **archive 退避(rename)**: OpenClaw dreaming-repair
|
||||
3. **forward pointer / tombstone**: Cloudflare supersession
|
||||
|
||||
yoi は **git 管理下に memory を置く**前提なので、物理削除を選んでも git log で復元できるのが強み。`replaced_by:` frontmatter が forward pointer の役割を果たしているので、Cloudflare 型と git を足した「**現物は物理削除、frontmatter pointer で chain を参照、git で history**」が最も設計コストに合う。archive 退避は git を前提にすると冗長。
|
||||
|
||||
### 8.3 yoi の GC 仕様を詰めるときの示唆
|
||||
|
||||
1. **GC trigger は 2 系統に割る**。(a) 決定論: Linter Warn 群 + age / count / size 閾値の sweep、(b) LLM 判定: consolidation とは別 prompt で Linter の issue リストを入力に渡す。両方が `memory/reports/gc-*.md` 相当を書き、それを次回の GC run が読む、というフィードバックループが OpenClaw lint / Codex consolidation input selection の両方と整合する。
|
||||
2. **Linter に「GC 候補検出」カテゴリを足す**。memory-wiki の lint issue code が参考になる: `stale-page`(90d 超)/ `stale-claim` / `low-confidence` / `orphan` / `duplicate-id` / `broken-wikilink` / `contradiction-present` / `open-question`。yoi 固有の追加候補: `similar-slug`(類似 slug 乱立、既に plan に記載)/ `replaced-chain-long`(`replaced_by` が 3 段以上)/ `sources-overflow`(1 record の sources が閾値超)/ `knowledge-invoke-frequency-low`(`user_invoke` が一定期間ゼロ)。
|
||||
3. **処理は rewrite 優先、削除は `status: replaced` 経由**(既に plan 方針と一致)。forward pointer は Cloudflare 流、ただし chain 圧縮ルール(例: 「chain が n 段超えたら中間を drop、端のみ残す」)を決めるかは別論点。Cloudflare は圧縮しない、yoi は git があるので圧縮してよい。
|
||||
4. **char limit は採用しない方が筋が良い**。Hermes の hard limit + LLM self-rewrite は設計最小だが、yoi は 1 record 1 file なのでファイル内 size 制約は薄く、file 数による grep コストの方が支配的になる。file 数閾値 → GC trigger の方が yoi の形に合う。
|
||||
5. **決定論 scoring を後から差し込む余地を残す**。OpenClaw Deep pass のような「頻度 / 関連度 / 多様性 / 時間減衰 / 整合性 / 概念」の 6 重み + 閾値は、agent LLM の出力が運用で評価可能になった段階で部分的に差し替える upgrade path として最適。初期は consolidation LLM + Linter Warn で十分。
|
||||
6. **削除は git commit 単位で可逆**という前提を明示する。プロジェクトメモリは git 管理下なので、GC が誤って drop してもユーザーは revert できる。これは Codex が持っていない利点で、GC agent の判断を多少攻めても安全マージンがある。
|
||||
|
||||
一次ソース:
|
||||
- Codex consolidation: `github.com/openai/codex/codex-rs/core/src/memories/consolidation implementation`, `core/templates/memories/consolidation.md`
|
||||
- Codex retention / stage1 pruning: `github.com/openai/codex/codex-rs/core/src/memories/extensions.rs:11-139`, `codex-rs/state/src/runtime/memories.rs:290-331, 333-464`
|
||||
- Hermes char limit reject: `github.com/NousResearch/hermes-agent/tools/memory_tool.py:211-266`
|
||||
- Hermes review spawn: `github.com/NousResearch/hermes-agent/run_agent.py:2727-2830`
|
||||
- OpenClaw promotion apply: `github.com/openclaw/openclaw/extensions/memory-core/src/short-term-promotion.ts:1518-1652`
|
||||
- OpenClaw temporal decay: `github.com/openclaw/openclaw/extensions/memory-core/src/memory/temporal-decay.ts`
|
||||
- OpenClaw dreaming-repair: `github.com/openclaw/openclaw/extensions/memory-core/src/dreaming-repair.ts:70-165`
|
||||
- memory-wiki lint: `github.com/openclaw/openclaw/extensions/memory-wiki/src/lint.ts`, `claim-health.ts:6-7`(`WIKI_AGING_DAYS = 30`, `WIKI_STALE_DAYS = 90`)
|
||||
- Cloudflare supersession: https://blog.cloudflare.com/introducing-agent-memory/
|
||||
- Letta sleep-time: https://docs.letta.com/guides/agents/memory/, https://www.letta.com/blog/sleep-time-compute, arxiv 2504.13171
|
||||
- Letta recursive summary: https://www.letta.com/blog/agent-memory
|
||||
- Karpathy llm-wiki lint: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f
|
||||
- SamurAIGPT/llm-wiki-agent `/wiki-lint`: https://github.com/SamurAIGPT/llm-wiki-agent
|
||||
- LinkedIn CMA: https://www.infoq.com/news/2026/04/linkedin-cognitive-memory-agent/
|
||||
|
||||
---
|
||||
|
||||
## 9. 未調査 / 次に掘るべき項目
|
||||
|
||||
- Letta `memory block` の Rust 実装例・永続化形式。
|
||||
- `agentskills.io` の CRDT 的バージョニング方針(標準は version metadata を任意 key にしている。実運用でどう衝突解決するかは未整備)。
|
||||
- Hermes の Honcho 連携の実体(外部サービス API 越しの dialectic user modeling、repo には prompt と API 呼び出しのみ)。
|
||||
- LinkedIn CMA の層別 GC 仕様(公開情報では compaction-by-summarization までしか開示されていない)。
|
||||
- Cloudflare supersession chain の圧縮ポリシー(chain 長の上限、vector GC と row GC の tx 境界)。
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
# LLMのReasoningコンテキスト管理仕様 比較レポート
|
||||
|
||||
**対象**: Claude (Anthropic) / ChatGPT (OpenAI) / Ollama
|
||||
**作成日**: 2026年5月4日
|
||||
**目的**: 各LLMプロバイダがReasoning(思考トレース)をマルチターン会話でどのように扱うかを整理し、実装時の注意点をまとめる。
|
||||
|
||||
---
|
||||
|
||||
## 1. はじめに
|
||||
|
||||
Reasoning対応モデルは、最終応答の前に「思考プロセス」を生成する。この思考はユーザーに見せる/見せない、次ターンに残す/残す、ツール使用中に保持する/しない、といった扱いがプロバイダごとに大きく異なる。本レポートでは Claude / ChatGPT / Ollama の3プラットフォームについて、コンテキスト管理の仕様を比較する。
|
||||
|
||||
主要な論点は以下の3つ:
|
||||
|
||||
1. **思考の表現形式** — どのフィールド/ブロックに格納されるか
|
||||
2. **マルチターン保持** — 次のユーザーターンに進んだとき思考は残るか
|
||||
3. **ツール使用との関係** — ツール呼び出しの前後で思考をどう扱うか
|
||||
|
||||
---
|
||||
|
||||
## 2. Claude (Anthropic)
|
||||
|
||||
### 2.1 表現形式
|
||||
|
||||
Claudeは API レスポンスに専用の `thinking` ブロックを返す。コンテンツブロック配列の中に `type: "thinking"` の要素として並び、最終応答は `type: "text"` ブロックとして別に格納される。
|
||||
|
||||
`thinking` ブロックには `signature` という暗号化フィールドが付与され、改ざん検知や正当性確認に使われる。これは Claude 4 系列で長くなり、API 利用者は値を解釈・パースしてはならない仕様。
|
||||
|
||||
### 2.2 マルチターン保持の挙動
|
||||
|
||||
モデル世代によってデフォルトが分かれる:
|
||||
|
||||
- **Opus 4.5+ / Sonnet 4.6+**: 過去ターンの thinking ブロックがコンテキストに**保持される**(入力トークンとしてカウント)
|
||||
- **それ以前の Opus/Sonnet および全 Haiku**: 過去ターンの thinking ブロックは**剥離される**(コンテキストに加算されない)
|
||||
|
||||
これは Anthropic 側の方針転換で、新世代では「推論の連続性」を優先する設計に変わった。実効コンテキストウィンドウの計算式は次のようになる:
|
||||
|
||||
```
|
||||
context_window = (current input tokens − previous thinking tokens) + (thinking tokens + encrypted thinking tokens + text output tokens)
|
||||
```
|
||||
|
||||
### 2.3 ツール使用との関係
|
||||
|
||||
ツール使用時は仕様が厳密になる。`tool_use` → `tool_result` → 続きのアシスタント応答という流れが**同一論理ターン**として扱われ、この間 thinking ブロックは**必ず保持して API に渡し戻す**必要がある。これはモデルの推論連続性を維持するため。
|
||||
|
||||
ただし境界条件として、tool_result ではない通常の user メッセージが入った時点で、それまでの thinking ブロックは無視・剥離される。つまり「新しいユーザー発話」がリセットポイントになる。
|
||||
|
||||
### 2.4 制御API
|
||||
|
||||
`clear_thinking_20251015` という context-editing 戦略により、開発者側で保持ポリシーを上書きできる:
|
||||
|
||||
```python
|
||||
context_management={
|
||||
"edits": [{
|
||||
"type": "clear_thinking_20251015",
|
||||
"keep": {"type": "thinking_turns", "value": 2}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
`keep.value` で「直近Nターン分の thinking だけ残す」といった粒度で指定可能。新世代モデルで thinking 保持がデフォルトになったため、コンテキスト圧迫を避けたい場合に使う。
|
||||
|
||||
---
|
||||
|
||||
## 3. ChatGPT (OpenAI)
|
||||
|
||||
### 3.1 表現形式
|
||||
|
||||
OpenAIの設計思想は Claude/Ollama と大きく異なり、**生のreasoningトークンはユーザーに見せない**。安全性上の理由から、reasoning は要約された形式(reasoning summaries)でのみ可視化される。
|
||||
|
||||
API レスポンスには `ResponseReasoningItem` という型のオブジェクトが含まれ、これは ID とオプションの暗号化コンテンツを持つが、本文は黒箱として扱われる。raw reasoning を `reasoning summary` 以外の方法で抽出しようとする試みは利用規約違反となる可能性がある。
|
||||
|
||||
### 3.2 マルチターン保持の挙動
|
||||
|
||||
ここが最も特殊。**Chat Completions API と Responses API で挙動が違う**。
|
||||
|
||||
#### Chat Completions API (旧来)
|
||||
|
||||
reasoning トークンは各ターンの後に破棄される。次ターンには引き継がれない(input/output tokens のみが次ステップに送られる)。これは o1 系列の初期設計。
|
||||
|
||||
#### Responses API (推奨)
|
||||
|
||||
ステートフル管理が可能。reasoning items を次のリクエストに引き継ぐには2方式ある:
|
||||
|
||||
1. `previous_response_id` パラメータで過去のレスポンスを参照
|
||||
2. `response.output` の全アイテムを次の `input` に手動で渡す
|
||||
|
||||
ステートレス利用(`store=false`、ZDR組織)の場合は `include=["reasoning.encrypted_content"]` を指定すれば暗号化された推論コンテンツを受け取り、次リクエストに渡すことで推論を引き継げる。Yoi は履歴から復元した reasoning item を通常の API message として扱い、独自の turn-boundary filtering はしない。
|
||||
|
||||
同一ターン内の function-call loop でも、`reasoning item → function_call → function_call_output → 次の Responses request` の連続性を保つため、履歴上の reasoning item は通常の API message として保持する。ToolResult は wire 上で user 側 item に見えるが、reasoning item の削除境界としては扱わない。
|
||||
|
||||
#### モデル世代差
|
||||
|
||||
- **o1 / o3-mini / o1-mini / o1-preview**: フォローアップリクエストで reasoning items は常に無視される(input に含めても)
|
||||
- **o3 / o4-mini 以降**: function call に隣接する一部の reasoning items はコンテキストに含まれ、ツール使用の連続性を改善する
|
||||
|
||||
ZDR (Zero Data Retention) を有効にした組織でも、暗号化機構により reasoning items を OpenAI 側に保存せずに API リクエスト間で再利用できるようになっている。
|
||||
|
||||
### 3.3 ツール使用との関係
|
||||
|
||||
o3 / o4-mini 以降では、function call をチェーン・オブ・ソート内で直接呼べるようになっており、reasoning tokens がリクエスト・ツール呼び出しを跨いで保持される。これにより複数ステップのエージェント的タスクでインテリジェンスが向上し、コスト/レイテンシも削減される(推論キャッシュの再利用)。
|
||||
|
||||
### 3.4 制御API
|
||||
|
||||
`reasoning.effort` パラメータで思考量を `low` / `medium` / `high` で指定可能(o1-mini は非対応)。`max_output_tokens` で reasoning + 最終出力の合計トークンを制限できる。
|
||||
|
||||
---
|
||||
|
||||
## 4. Ollama
|
||||
|
||||
### 4.1 表現形式
|
||||
|
||||
Ollamaはローカル実行プラットフォームで、モデルごとに思考タグの規約が異なる。レスポンスでは `message.thinking`(chat エンドポイント)または `thinking`(generate エンドポイント)に推論トレース、`message.content` / `response` に最終回答が分離されて格納される。
|
||||
|
||||
内部的にはモデル固有のパーサが動く:
|
||||
|
||||
- **Qwen3Parser** — Qwen 系の thinking タグとツール呼び出しタグを処理
|
||||
- **DeepSeek3Parser** — DeepSeek の推論出力に最適化
|
||||
- **Harmony** — GPT-OSS モデル用、low/medium/high の段階的 thinking レベルをサポート
|
||||
|
||||
ハードコードされたパーサがないモデルでは `thinking.InferTags` がプロンプトテンプレートをスキャンし、`<think>...</think>` などのデリミタを推測する。
|
||||
|
||||
### 4.2 マルチターン保持の挙動
|
||||
|
||||
**Ollama自体はthinking履歴を管理しない**。これは設計上の重要な特徴で、メッセージ配列を組み立てるクライアント側の責任になる。
|
||||
|
||||
モデル側のテンプレート設計レベルで「履歴に思考を残すな」と明示しているケースが多い。例えば Gemma 4 系は公式ドキュメントで明示的に「マルチターン会話では過去のモデル出力には最終応答のみを含めるべきで、過去のモデルターンの思考は次のユーザーターンの前に追加してはいけない」と指示している。DeepSeek-R1 や Qwen3 も同様の前提で訓練されている。
|
||||
|
||||
したがって標準的な実装パターンは「**次ターン送信時に thinking フィールドを落とし、content だけ履歴に積む**」となる。Ollama が thinking を別フィールドに分離しているのは、ユーザーが履歴構築時に簡単に捨てられるようにする意図もある。
|
||||
|
||||
### 4.3 ツール使用との関係
|
||||
|
||||
ツール呼び出しループで思考を保持したい場合、`clear_thinking=false` を上流APIで設定することで `<think>` ブロックを会話コンテキストに保持できる。OllamaはThinkValueが nil または true のときこれを自動処理する。
|
||||
|
||||
実用上は Open WebUI などのクライアント実装が参考になる。Open WebUI は同一ターン内のツール呼び出しでは reasoning コンテンツを保持し、`<think>...</think>` タグでシリアライズして次のAPIコール時の assistant メッセージの content フィールドに含める方式を採っている。
|
||||
|
||||
### 4.4 制御API
|
||||
|
||||
主要な制御は以下:
|
||||
|
||||
- `think` パラメータ — boolean(true/false)、ただし GPT-OSS は `"low"` / `"medium"` / `"high"` の文字列のみ受け付ける
|
||||
- CLI: `--think` / `--think=false` / `--hidethinking`(思考はするが表示しない)
|
||||
- サーバー起動時: `ollama serve --reasoning-parser deepseek_r1` でパーサを明示
|
||||
|
||||
サポート判定は `supportsThinking` 関数が `config.json` を見て、glm4moe / deepseek / qwen3 などの既知アーキテクチャか確認する仕組み。
|
||||
|
||||
---
|
||||
|
||||
## 5. 比較表
|
||||
|
||||
### 5.1 仕様サマリー
|
||||
|
||||
| 観点 | Claude | ChatGPT (OpenAI) | Ollama |
|
||||
|---|---|---|---|
|
||||
| **思考の可視性** | 完全可視(生のthinking) | 要約のみ可視(生は黒箱) | 完全可視(モデル次第でタグ形式) |
|
||||
| **思考の格納先** | 専用ブロック (`type: "thinking"`) | `ResponseReasoningItem` | `message.thinking` フィールド |
|
||||
| **暗号化/署名** | signature付き | encrypted_content(オプション) | なし |
|
||||
| **デフォルトの履歴保持** | Opus 4.5+/Sonnet 4.6+: 保持<br>それ以前: 剥離 | Chat Completions: 破棄<br>Responses: 引き継ぎ可 | クライアント責任<br>(モデル指示は剥離が主流) |
|
||||
| **ツール使用中の保持** | 必須(同一ターン内) | o3/o4-mini で関連部分自動保持 | `clear_thinking=false` で制御 |
|
||||
| **管理層** | API側がマネージド | API側がマネージド | クライアント側で実装 |
|
||||
| **思考量制御** | `clear_thinking_20251015`戦略 | `reasoning.effort` (low/medium/high) | `think`パラメータ (bool / level) |
|
||||
|
||||
### 5.2 設計思想の違い
|
||||
|
||||
| プロバイダ | 思想 |
|
||||
|---|---|
|
||||
| **Claude** | 「マネージドな推論ブロック」として抽象化。署名付きで改ざん耐性を持たせ、API側で保持/剥離を判断 |
|
||||
| **ChatGPT** | 「思考は黒箱、要約だけ見せる」。安全性とIP保護優先で、推論の引き継ぎはAPI仕様(Responses)でハンドル |
|
||||
| **Ollama** | 「プロンプトに混ぜるタグの開閉とパース」という素朴な仕組み。クライアントの自由度が高いが責任も大きい |
|
||||
|
||||
---
|
||||
|
||||
## 6. 実装上の注意点
|
||||
|
||||
### 6.1 共通の落とし穴
|
||||
|
||||
- **モデル世代差を見落とす** — Claude も OpenAI も、世代によってデフォルトが大きく異なる。バージョン固定や挙動確認は必須
|
||||
- **ツール使用ループでの推論喪失** — Claude/Ollama 共に、ツール使用中に thinking を落とすと推論連続性が壊れる。同一ターン内は必ず保持する
|
||||
- **トークンコスト** — Claude 新世代では thinking が入力トークンとして加算される。長い対話では `clear_thinking_20251015` でのトリミングが必要
|
||||
|
||||
### 6.2 プロバイダ別の推奨パターン
|
||||
|
||||
**Claude を使うとき**
|
||||
- 4.5+/4.6+ ではデフォルトで thinking が残るため、長期会話ではコンテキスト圧迫に注意
|
||||
- 旧世代との互換コードを書く場合は、両方のデフォルト挙動を意識する
|
||||
- ツール使用時は受け取った thinking ブロックを**完全な形で**渡し戻す
|
||||
|
||||
**ChatGPT を使うとき**
|
||||
- 新規実装は **Responses API** を選ぶ(Chat Completions は推論引き継ぎが弱い)
|
||||
- ZDR組織でも `reasoning.encrypted_content` で推論を引き継げる。履歴上の reasoning item は通常の API message として扱い、独自の turn-boundary filtering はしない
|
||||
- raw reasoning の抽出を試みない(規約違反の可能性)
|
||||
|
||||
**Ollama を使うとき**
|
||||
- クライアント側で thinking フィールドの扱いを明示的に決める
|
||||
- モデル毎のテンプレート規約を確認する(Gemma 4 のように「履歴に残すな」指示があるモデルもある)
|
||||
- サーバー起動時に `--reasoning-parser` を適切に設定する
|
||||
|
||||
---
|
||||
|
||||
## 7. まとめ
|
||||
|
||||
3プラットフォームのReasoning管理は、それぞれ異なる優先事項を反映している:
|
||||
|
||||
- **Claude** は「推論の透明性と連続性」を最重視し、API側でリッチなマネジメントを提供
|
||||
- **ChatGPT** は「安全性とIP保護」を最重視し、生の推論をユーザーから隠蔽しつつ機能性は Responses API で担保
|
||||
- **Ollama** は「ローカル実行と柔軟性」を最重視し、クライアントに最大限の制御権を委ねる
|
||||
|
||||
実装者にとっては、「次のユーザーターンに進んだ時点で何が剥がれるか」を**プロバイダ × モデル世代 × API選択**の3軸で把握しておくことが重要。特にツール使用を伴うエージェント構築では、各プラットフォームの推論保持機構を正しく使わないと、推論連続性の喪失によりインテリジェンスが大幅に低下する。
|
||||
|
||||
---
|
||||
|
||||
## 参考資料
|
||||
|
||||
- Anthropic: [Building with extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking)
|
||||
- Anthropic: [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing)
|
||||
- OpenAI: [Reasoning models guide](https://developers.openai.com/api/docs/guides/reasoning)
|
||||
- OpenAI Cookbook: [Better performance from reasoning models using the Responses API](https://cookbook.openai.com/examples/responses_api/reasoning_items)
|
||||
- Ollama: [Thinking capability docs](https://docs.ollama.com/capabilities/thinking)
|
||||
- Ollama Blog: [Thinking](https://ollama.com/blog/thinking)
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
# Yoi × OpenCode 比較レポート
|
||||
|
||||
## 概要
|
||||
|
||||
Yoi(Rust製エージェントプラットフォーム、基礎実装段階)と OpenCode(TypeScript/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 + OpenTUI(Terminal 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: Prune(Hook ベース)
|
||||
PreLlmRequest Hook で古いツール出力を削除
|
||||
設計原則3に従い、新しい抽象は作らない
|
||||
|
||||
consolidation: Compact(Agent ベース)
|
||||
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 Agent(build, plan)と Subagent(explore, 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 で十分 |
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
# リファレンス: ローカルファイル操作ツール仕様
|
||||
|
||||
Claude Code がエージェントとして使うファイル操作ツールの仕様。
|
||||
Pod のツール設計の参考資料。
|
||||
|
||||
## Read
|
||||
|
||||
ファイルを読む。画像・PDF・Jupyter notebook にも対応。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `file_path` | string | 必須 | 絶対パス |
|
||||
| `offset` | integer | 任意 | 読み開始行番号(0始まり) |
|
||||
| `limit` | integer | 任意 | 読み取り行数(offset と併用) |
|
||||
| `pages` | string | 任意 | PDF用ページ範囲 (例: `"1-5"`)。最大20ページ/回 |
|
||||
|
||||
- デフォルトで先頭から最大2000行
|
||||
- 出力は行番号付き(1始まり)
|
||||
- ディレクトリは読めない
|
||||
|
||||
## Write
|
||||
|
||||
ファイルを新規作成、または全体上書き。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `file_path` | string | 必須 | 絶対パス |
|
||||
| `content` | string | 必須 | 書き込む内容全体 |
|
||||
|
||||
- 既存ファイルは上書き
|
||||
- 既存ファイルを Write する前に Read が必要(未読だとエラー)
|
||||
|
||||
## Edit
|
||||
|
||||
既存ファイルの部分的な文字列置換。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `file_path` | string | 必須 | 絶対パス |
|
||||
| `old_string` | string | 必須 | 置換対象(ファイル内で一意であること) |
|
||||
| `new_string` | string | 必須 | 置換後(old_string と異なること) |
|
||||
| `replace_all` | boolean | 任意 | `true` で全出現箇所を置換。デフォルト `false` |
|
||||
|
||||
- 事前に Read が必要(未読だとエラー)
|
||||
- `old_string` がファイル内で一意でないとエラー(`replace_all: true` 除く)
|
||||
|
||||
## Glob
|
||||
|
||||
ファイルパターン検索。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `pattern` | string | 必須 | glob パターン (例: `"**/*.rs"`) |
|
||||
| `path` | string | 任意 | 検索ディレクトリ。省略時はカレント |
|
||||
|
||||
- 結果は更新日時順ソート
|
||||
|
||||
## Grep
|
||||
|
||||
ファイル内容の正規表現検索(ripgrep ベース)。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `pattern` | string | 必須 | 正規表現パターン |
|
||||
| `path` | string | 任意 | 検索対象。省略時はカレント |
|
||||
| `glob` | string | 任意 | ファイルフィルタ (例: `"*.rs"`) |
|
||||
| `type` | string | 任意 | ファイル種別 (例: `"rust"`, `"py"`) |
|
||||
| `output_mode` | enum | 任意 | `"files_with_matches"` (デフォルト) / `"content"` / `"count"` |
|
||||
| `-n` | boolean | 任意 | 行番号表示 (content モード、デフォルト `true`) |
|
||||
| `-i` | boolean | 任意 | 大文字小文字無視 |
|
||||
| `-A` | number | 任意 | マッチ後の行数 |
|
||||
| `-B` | number | 任意 | マッチ前の行数 |
|
||||
| `-C` | number | 任意 | マッチ前後の行数 |
|
||||
| `multiline` | boolean | 任意 | 複数行マッチ。デフォルト `false` |
|
||||
| `head_limit` | number | 任意 | 出力件数制限。デフォルト 250 |
|
||||
| `offset` | number | 任意 | 先頭N件スキップ |
|
||||
|
||||
## Bash
|
||||
|
||||
シェルコマンド実行。
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
|---|---|---|---|
|
||||
| `command` | string | 必須 | 実行するコマンド |
|
||||
| `description` | string | 任意 | コマンドの説明 |
|
||||
| `timeout` | number | 任意 | タイムアウト ms (最大 600000、デフォルト 120000) |
|
||||
| `run_in_background` | boolean | 任意 | バックグラウンド実行 |
|
||||
|
||||
- 作業ディレクトリはコマンド間で永続
|
||||
- シェル状態(変数・エイリアス)は永続しない
|
||||
|
||||
## 設計上の特徴
|
||||
|
||||
- **パスは全て絶対パス**
|
||||
- **Read → Edit/Write の順序制約**: 未読ファイルの編集を防ぐ安全弁
|
||||
- **専用ツール優先**: cat/grep/find/sed の代わりに専用ツールを使う
|
||||
- **冪等性**: Edit は差分ベース、Write は全体上書き
|
||||
- **ワークフロー**: Glob/Grep で探索 → Read で確認 → Edit で変更
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
# Anthropic Claude と OpenAI ChatGPT におけるツール呼び出しアプローチの比較
|
||||
|
||||
## 概要
|
||||
|
||||
LLMをエージェントとして動かす上で、ツール呼び出し(function calling / tool use)の仕組みはモデルの実用性を大きく左右する。ChatGPTもClaudeも、最終的に開発者へ返るのは構造化されたJSONレスポンスである点は共通している。しかし、その裏側でモデルが実際に生成しているトークン列、ツールを定義する構文、思考過程の扱い方には明確な違いがある。本レポートでは、両者のアプローチを4つの観点から整理する。
|
||||
|
||||
---
|
||||
|
||||
## 1. 内部出力フォーマット — XML風タグ vs 特殊制御トークン
|
||||
|
||||
最も根本的な違いは、モデルが生のテキストとして何を出力しているかにある。
|
||||
|
||||
**Claude (Anthropic)** は、`<function_calls>`、`<invoke>`、`<parameter>` といったXML風のタグでツール呼び出しを表現する。これらはトークナイザーから見れば普通のテキストトークンであり、APIサーバー側が正規表現でパースして構造化データに変換する。Anthropic公式のドキュメントでも、この出力は厳密なXMLとして扱われるわけではなく、正規表現パースを前提に設計されている、と明記されている。
|
||||
|
||||
**OpenAI (ChatGPT / gpt-oss)** は、Harmonyと呼ばれるレスポンスフォーマットを採用しており、`<|start|>`、`<|message|>`、`<|channel|>`、`<|call|>`、`<|end|>`、`<|return|>` といった**特殊制御トークン**でメッセージ構造を区切る。これらは見た目こそタグ風だが、トークナイザー内では1つの専用IDを持つ単一トークンとして扱われる。つまり、テキストを後からパースしているのではなく、トークンレベルで構造が埋め込まれている。
|
||||
|
||||
この違いは設計哲学の差を反映している。Claudeは可読性とトレーニングデータとの親和性を重視した「テキスト寄り」のアプローチであり、OpenAIはトークンレベルで構造を保証する「プロトコル寄り」のアプローチといえる。
|
||||
|
||||
---
|
||||
|
||||
## 2. ツール定義の構文 — JSON Schema vs TypeScript風
|
||||
|
||||
開発者がツールをモデルに教える際の書き方も大きく異なる。
|
||||
|
||||
**Claude** ではツールはJSON Schemaで定義する。`name`、`description`、`input_schema` を持つオブジェクトの配列をAPIに渡し、APIサーバーが内部でシステムプロンプトを構築してモデルに提示する。JSON Schemaという既存の標準仕様にそのまま乗っているため、他のJSON処理エコシステムとの相性が良い。
|
||||
|
||||
**OpenAI** はHarmonyフォーマットの中で、ツール定義をTypeScript風の型構文で記述する。関数は `namespace functions { ... }` というブロックでまとめられ、各関数は `type get_current_weather = (_: { location: string, format?: "celsius" | "fahrenheit" }) => any;` のように定義される。コメントが説明文として機能する。
|
||||
|
||||
開発者がOpenAI APIを叩く際にはJSON Schema形式で渡せるが、サーバー内部で `harmony` ライブラリがこれをTypeScript風構文に変換してからモデルに渡している。最終的にモデルが「読む」のはTypeScript風の表現である。これは、コード補完タスクで大量のTypeScriptを学習しているLLMにとって型シグネチャの方が認識しやすい、という経験的判断に基づくと考えられる。
|
||||
|
||||
---
|
||||
|
||||
## 3. 引数のフォーマット
|
||||
|
||||
ツール呼び出し時の引数の渡し方は、両者で似てはいるが微妙に違う。
|
||||
|
||||
**Claude** では、文字列やスカラー値はそのままタグ内に書き、リストやオブジェクトなどの複合型はJSONとして埋め込む、というハイブリッド方式を取る。例えば `<parameter name="city">Tokyo</parameter>` のようにシンプルな値は素のテキストで、配列やネストした構造はJSONで表現される。
|
||||
|
||||
**OpenAI (Harmony)** では、引数全体を一括してJSONで渡す。`<|channel|>commentary to=functions.get_current_weather <|constrain|>json<|message|>{"location":"San Francisco"}<|call|>` というように、`<|message|>` 以降に丸ごとJSONオブジェクトを置き、`<|call|>` トークンで実行要求を確定する。`<|constrain|>json` は出力をJSONに制約することを示すヒントとして機能する。
|
||||
|
||||
エンジニアリング的には、JSONで統一する方がパースが単純で予測可能だが、Claudeのハイブリッド方式は単純な値の場合のトークン消費を抑える効果がある。
|
||||
|
||||
---
|
||||
|
||||
## 4. 思考過程の分離
|
||||
|
||||
エージェントモデルでは、思考と最終応答とツール呼び出しを分離する仕組みが重要になる。両者ともこれに対応しているが、構造化の度合いが異なる。
|
||||
|
||||
**Claude** は `thinking` ブロックを持ち、Extended Thinkingモードでは推論内容を専用ブロックに格納する。基本的には「思考」と「応答」の2層構造である。
|
||||
|
||||
**OpenAI (Harmony)** は **チャネル** という、より明確に分離された3層構造を採用している。
|
||||
|
||||
- `analysis` チャネル — 生のchain-of-thought。安全性のトレーニングを受けておらず、ユーザーには通常見せない。
|
||||
- `commentary` チャネル — ツール呼び出しや、ユーザーに見せても良い計画的なコメント。
|
||||
- `final` チャネル — ユーザー向けの最終応答。
|
||||
|
||||
このチャネル分離により、開発者は推論プロセスをログとして残しつつ、ユーザーには `final` チャネルだけを表示する、といった運用が容易になる。
|
||||
|
||||
---
|
||||
|
||||
## 5. 開発者から見た最終的なレスポンス
|
||||
|
||||
ここまで内部の差を見てきたが、API経由で開発者が受け取るレスポンスは両者ともJSONである。
|
||||
|
||||
- Claudeは `tool_use` タイプのコンテンツブロックとしてツール呼び出しを返す。
|
||||
- OpenAIは `tool_calls` 配列としてツール呼び出しを返す。
|
||||
|
||||
つまり、開発者の視点では「JSONを送ってJSONを受け取る」点は同じであり、内部表現の違いはアプリケーション層には漏れない。違いが顕在化するのは、ファインチューニング、推論サーバーの自前構築、デバッグでモデルの生出力を観察する場合などに限られる。
|
||||
|
||||
---
|
||||
|
||||
## 比較表
|
||||
|
||||
| 観点 | Claude (Anthropic) | ChatGPT (OpenAI / Harmony) |
|
||||
|---|---|---|
|
||||
| 構造の区切り | XML風タグ(通常のテキストトークン) | 特殊制御トークン(単一トークン) |
|
||||
| パース方式 | 正規表現ベース | トークンレベルで構造化 |
|
||||
| ツール定義の表現 | JSON Schema | TypeScript風型構文 + namespace |
|
||||
| 引数のフォーマット | スカラはそのまま、複合型はJSON | 全てJSON |
|
||||
| 思考の分離 | thinkingブロック(2層) | analysis / commentary / final(3層チャネル) |
|
||||
| API応答 | tool_useブロック(JSON) | tool_calls配列(JSON) |
|
||||
| 設計の傾向 | テキスト寄り・既存標準活用 | プロトコル寄り・トークン専用化 |
|
||||
|
||||
---
|
||||
|
||||
## 考察
|
||||
|
||||
両社のアプローチの違いは、それぞれの強みとトレードオフを反映している。
|
||||
|
||||
Claudeのアプローチは、JSON SchemaとXML風タグという既存の表記法を活用しており、ツールチェーンの相互運用性が高い。一方で、生成された出力が「壊れた」場合(タグの閉じ忘れなど)のパース失敗リスクは構造的に存在する。
|
||||
|
||||
OpenAIのHarmonyは、特殊トークンによってトークンレベルで構造が保証されるため、フォーマットの破綻が起きにくい。チャネル分離のような細かい構造化も自然に実現できる。一方で、独自プロトコルであるため、モデルを使う側のスタックがHarmonyを正しく扱う必要があり、外部ツールとの統合に追加実装が必要になる場面もある(実際、TensorRT-LLMやvLLMなどでHarmonyトークンの取り扱いに関する問題が報告されている)。
|
||||
|
||||
エージェント開発者にとって重要なのは、これらの差は通常APIの抽象化に隠されている、という点である。ただし、ローカルで重み付きモデルを動かす、ファインチューニングを行う、エージェントの動作をデバッグする、といった一段深い作業に踏み込む場合には、内部表現の理解が直接実装に効いてくる。
|
||||
|
||||
---
|
||||
|
||||
## 注記
|
||||
|
||||
本レポートで示したOpenAI側の詳細は、主にオープンウェイトモデル `gpt-oss` 向けに公開されているHarmonyフォーマットの仕様に基づく。商用のGPT-4o / GPT-5などの内部表現は完全には公開されていないが、HarmonyはOpenAIのResponses APIを模倣するように設計されており、商用モデルもおおむね類似した構造を採用していると推測される。
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
# Zed モノレポのワークスペース・クレート規則
|
||||
|
||||
[zed-industries/zed](https://github.com/zed-industries/zed) のソースコードを読み解くための、ワークスペース構成とクレート分割に関する規則のまとめ。
|
||||
|
||||
---
|
||||
|
||||
## 1. ワークスペース構成
|
||||
|
||||
### 単一ワークスペース
|
||||
|
||||
- リポジトリ直下の `Cargo.toml` が `[workspace]` を持ち、`crates/` 以下のすべてのクレート(200個超)を members として束ねる単一ワークスペース。
|
||||
- `default-members = ["crates/zed"]` が指定されており、ルートで `cargo run` するとエディタ本体が起動する。
|
||||
- `Cargo.lock` はルートに1つだけ。すべてのクレートで共有される。
|
||||
- ルートに `rust-toolchain.toml` / `clippy.toml` / `rustfmt.toml` / `.cargo/` を置き、ワークスペース全体に共通設定を効かせる。
|
||||
|
||||
### 内部依存は `[workspace.dependencies]` に集約
|
||||
|
||||
ルート `Cargo.toml` に **すべての内部クレートを path 指定で列挙** する:
|
||||
|
||||
```toml
|
||||
[workspace.dependencies]
|
||||
acp_thread = { path = "crates/acp_thread" }
|
||||
action_log = { path = "crates/action_log" }
|
||||
agent = { path = "crates/agent" }
|
||||
agent_ui = { path = "crates/agent_ui" }
|
||||
anthropic = { path = "crates/anthropic" }
|
||||
# ... 200個以上続く
|
||||
```
|
||||
|
||||
外部クレート(serde, tokio, フォーク版 calloop など)も同じ場所に集約され、バージョンや git rev を一元管理する。
|
||||
|
||||
### 各クレートは `.workspace = true` で参照
|
||||
|
||||
個々の `crates/<name>/Cargo.toml` ではバージョン番号もパスも書かない:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
gpui.workspace = true
|
||||
project.workspace = true
|
||||
serde.workspace = true
|
||||
```
|
||||
|
||||
これにより:
|
||||
- バージョン更新やフォーク差し替えがルート1ファイルで完結
|
||||
- 同じ依存が複数バージョンに分裂する事故が起きない
|
||||
- 新しいクレートの追加は「フォルダ作成 → ルートに1行追加 → 使う側で `name.workspace = true`」だけ
|
||||
|
||||
### `publish = false`
|
||||
|
||||
ワークスペース全体で `publish = false`。crates.io には公開されないため、`editor` や `project`、`language` のような一般名詞を平気で使える。
|
||||
|
||||
---
|
||||
|
||||
## 2. クレート命名規則
|
||||
|
||||
### 表記ルール
|
||||
|
||||
- **`snake_case` で統一**。ハイフンや CamelCase は使わない。
|
||||
- **全部小文字**、略語も小文字(`lsp`, `rpc`, `acp`, `aws`, `ui`)。
|
||||
- **ディレクトリ名 = `package.name` = `[workspace.dependencies]` のキー** で完全一致。grep しやすさを優先。
|
||||
|
||||
### 命名パターン
|
||||
|
||||
#### 1. コア基盤は1語の名詞
|
||||
|
||||
`zed` / `gpui` / `editor` / `project` / `workspace` / `language` / `theme` / `ui` / `lsp` / `rpc` / `audio` / `assets` / `askpass`
|
||||
|
||||
#### 2. 機能ファミリーは「共通プレフィックス + サフィックス」
|
||||
|
||||
```
|
||||
agent / agent_ui / agent_ui_v2 / agent_settings / agent_servers
|
||||
auto_update / auto_update_ui / auto_update_helper
|
||||
assistant_text_thread / assistant_slash_command / assistant_slash_commands
|
||||
acp_thread / acp_tools
|
||||
```
|
||||
|
||||
サフィックスの慣習:
|
||||
|
||||
| サフィックス | 役割 |
|
||||
|---|---|
|
||||
| `_ui` | 機能のビュー/ウィジェット層(コアロジックとは分離) |
|
||||
| `_settings` | 設定スキーマと読み込み |
|
||||
| `_servers` | 外部プロセス連携アダプタ |
|
||||
| `_helper` | 補助バイナリ |
|
||||
| `_v2` | 既存版と並行する新実装の隔離 |
|
||||
| `_tools` | デバッグ/開発者向けユーティリティ |
|
||||
|
||||
#### 3. 外部プロトコル/ベンダー連携はその名前そのまま
|
||||
|
||||
`anthropic` / `bedrock` / `aws_http_client` のように、相手のサービスや仕様の名前をそのまま使う。`acp_*` (Agent Client Protocol) のようにプロトコルの略号を頭に付けて系列化するのも同様。
|
||||
|
||||
---
|
||||
|
||||
## 3. クレート分割の方針
|
||||
|
||||
### `zed` クレートは薄いシェル
|
||||
|
||||
`crates/zed/` の中身は最小限:
|
||||
|
||||
- `main.rs` — エントリポイント、CLI 引数、シングルインスタンス処理、クラッシュハンドラ、パス初期化
|
||||
- `zed.rs` — グローバル action ハンドラ登録、`initialize_workspace` でのパネル組み立て
|
||||
- `build.rs` / `RELEASE_CHANNEL`
|
||||
|
||||
`app.run(...)` の中身は **200個以上のクレートの `init(cx)` を正しい順序で呼ぶだけ**:
|
||||
|
||||
```rust
|
||||
settings::init(cx);
|
||||
theme::init(cx);
|
||||
client::init(&client, cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
editor::init(cx);
|
||||
project_panel::init(cx);
|
||||
agent_ui::init(fs, client, prompt_builder, languages, cx);
|
||||
git_ui::init(cx);
|
||||
// ...
|
||||
```
|
||||
|
||||
`zed` は依存グラフの頂点に立つ唯一のクレートで、機能の置き場所ではなく **配線盤 (wiring)** として存在する。**「どこに置くか迷ったら `zed` 以外のどこかに置く」** が鉄則。
|
||||
|
||||
### 境界を切る基準
|
||||
|
||||
#### ① レイヤー(下から上への単方向依存)
|
||||
|
||||
```
|
||||
gpui ← UI フレームワーク
|
||||
↓
|
||||
text / language / fs / rpc / settings / ← ドメインの基本型
|
||||
theme / ui
|
||||
↓
|
||||
project / lsp / git / terminal / ← サービス層
|
||||
multi_buffer
|
||||
↓
|
||||
editor / workspace ← 中核機能
|
||||
↓
|
||||
project_panel / git_ui / agent_ui / ← 個別機能 (パネル/ビュー)
|
||||
diagnostics / search / ...
|
||||
↓
|
||||
zed ← 配線だけ
|
||||
```
|
||||
|
||||
下のレイヤーは上のレイヤーを知らない。逆方向の拡張点は trait(`workspace::Item`、`workspace::Panel` など)として下層が公開し、上層が実装する。
|
||||
|
||||
#### ② ロジックと UI の分離
|
||||
|
||||
GPUI 依存を上層に閉じ込めるため、ロジックと UI を別クレートに切る:
|
||||
|
||||
| ロジック | UI |
|
||||
|---|---|
|
||||
| `agent` | `agent_ui` |
|
||||
| `auto_update` | `auto_update_ui` |
|
||||
| `git` | `git_ui` |
|
||||
| `project` | `project_panel` |
|
||||
|
||||
UI 側はロジック側を知るが、ロジック側は UI 側を知らない。
|
||||
|
||||
#### ③ `Project` と `Workspace` の二分
|
||||
|
||||
Zed の設計で最も象徴的な分割:
|
||||
|
||||
- **`project`** — worktree、LSP、ファイルシステム、Git といったサービスを束ねる。ヘッドレスでも動く側。
|
||||
- **`workspace`** — ペイン分割、ドック、パネル、ステータスバーといったウィンドウ表示を束ねる。GPUI に強く依存する側。
|
||||
|
||||
これにより `Project` をリモートマシンで走らせ `Workspace` をローカルで走らせる、というリモート開発が素直に成立する。
|
||||
|
||||
#### ④ trait 境界 + Fake 実装
|
||||
|
||||
下層の重要な抽象は trait で公開し、テストでは Fake を差し込む:
|
||||
|
||||
- `fs::Fs` ↔ `FakeFs`
|
||||
- `git::GitRepository` ↔ Fake 実装
|
||||
- `language::LanguageRegistry`
|
||||
- `gpui::Element`、`workspace::Item`/`Panel`
|
||||
|
||||
trait が置かれているクレートがそのまま境界になる。「どこまでがプラグイン可能か」がクレート一覧から読み取れる。
|
||||
|
||||
#### ⑤ 外部世界との接点ごとにクレートを切る
|
||||
|
||||
新規追加で既存クレートを編集しなくて済むように、外部依存ごとに独立させる:
|
||||
|
||||
- `lsp` / `dap` / `rpc` (プロトコル層)
|
||||
- `anthropic` / `bedrock` / `open_ai` (LLM プロバイダごと)
|
||||
- `aws_http_client` (特定の HTTP クライアント実装)
|
||||
- `acp_thread` / `acp_tools` (Agent Client Protocol)
|
||||
- `extension_host` + `extension_api` (拡張機能 WASM サンドボックス境界)
|
||||
|
||||
#### ⑥ コンパイル時間最適化
|
||||
|
||||
頻繁に編集される `editor` のような大きいクレートを切り離すことで、並列ビルドとインクリメンタルコンパイルの効率を上げる。ルート `Cargo.toml` には単一ファイルクレートに `codegen-units = 1` を効かせる調整も含まれる。
|
||||
|
||||
---
|
||||
|
||||
## 4. 「core」クレートを作らない
|
||||
|
||||
Zed には `core` / `common` / `shared` / `zed_core` といったクレートが**意図的に存在しない**。「コア」になりそうなものは機能軸で水平に分割される:
|
||||
|
||||
| ありがちな "core" の中身 | Zed での置き場所 |
|
||||
|---|---|
|
||||
| 基本データ型 (Rope, Point, Anchor) | `text` |
|
||||
| ファイルシステム抽象 | `fs` (`Fs` trait + `FakeFs`) |
|
||||
| 設定の読み書き | `settings` |
|
||||
| テーマ・色 | `theme` |
|
||||
| 共通 UI コンポーネント | `ui` |
|
||||
| 汎用ユーティリティ関数 | `util` |
|
||||
| RPC/シリアライズ | `rpc` / `proto` |
|
||||
| ロギング | `zlog` |
|
||||
| 言語抽象 | `language` |
|
||||
|
||||
### `core` を作らない理由
|
||||
|
||||
1. **ビルドグラフの直列化を避ける** — `core` を作るとほぼ全クレートがそこに依存し、1行触るたびに数百クレートが再コンパイルされる。並列ビルドの利点が消える。
|
||||
2. **"ゴミ捨て場" 化の防止** — `core` や `common` は置き場所に迷ったコードが流れ込む磁石になり、責務が肥大化して循環依存の温床になる。Zed は **「迷ったら新しいクレートを作る」** 方向に振る。
|
||||
3. **依存方向のドキュメント化** — クレートを細かく分けると `Cargo.toml` を見るだけで何に依存し何に依存していないかが一覧できる。`core` があるとこの情報量がゼロになる。
|
||||
|
||||
### 例外的な `util`
|
||||
|
||||
`crates/util` は雑多な汎用ヘルパ(`ResultExt`, paths, debouncer 等)が入る "ややコアっぽい" クレートだが:
|
||||
|
||||
- 名前は `core` ではなく `util` (補助関数の入れ物の慣習)
|
||||
- GPUI にも `editor` にも依存しない、完全な下層
|
||||
- trait や中核データ型は置かない (それは `text` や `fs` の役目)
|
||||
|
||||
責務を「補助関数集」に限定することでゴミ捨て場化を防いでいる。
|
||||
|
||||
---
|
||||
|
||||
## 5. まとめ
|
||||
|
||||
Zed のクレート分割の優先順位:
|
||||
|
||||
1. **`zed` には何も入れない** — 機能は必ず別クレートに押し出し、`zed` は init 呼び出しと CLI と main だけを持つ
|
||||
2. **下から上への単方向依存** — 上層が必要な拡張点は trait で下層に公開する
|
||||
3. **ロジック / UI を分ける** — `xxx` と `xxx_ui` のペア、GPUI 依存を上層に閉じ込める
|
||||
4. **`Project` と `Workspace` を分ける** — ヘッドレス/リモート実行可能性のため
|
||||
5. **外部世界 (プロトコル・ベンダー・拡張) ごとにクレートを切る** — 新規追加を「ファイル追加だけ」で済ませる
|
||||
6. **Fake が置けるところに trait を置く** — テスト境界 = アーキテクチャ境界
|
||||
7. **`core` を作らない** — 万能箱の代わりに責務を限定した小さな箱を用意する
|
||||
|
||||
クレート数の多さは管理コストではなく、**境界を守るためのコスト払い**である。
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
# Anthropic Messages API: `max_tokens` パラメータ仕様
|
||||
|
||||
Source: https://platform.claude.com/docs/en/api/messages
|
||||
Retrieved: 2026-04-28
|
||||
|
||||
---
|
||||
|
||||
## 1. パラメータ名
|
||||
|
||||
`max_tokens`
|
||||
|
||||
`max_output_tokens` ではない。
|
||||
|
||||
## 2. 必須か任意か
|
||||
|
||||
**必須 (required)**。
|
||||
|
||||
`POST /v1/messages` のボディパラメータとして必須指定。
|
||||
yoi の現在の実装(`max_tokens: u32`、未指定時 4096 にフォールバック)は仕様と合致している。
|
||||
|
||||
## 3. 型・範囲
|
||||
|
||||
- 型: integer (number)
|
||||
- 意味: 生成を停止する前に出力できるトークンの上限。モデルはこの値に達する前に停止することもある(上限の指定であり、保証値ではない)
|
||||
- モデルごとに最大値が異なる:
|
||||
- Claude Opus 4.6 / 4.7: 最大 128k トークン
|
||||
- Claude Sonnet 4.6 / Haiku 4.5: 最大 64k トークン
|
||||
- Message Batches API + beta ヘッダ `output-300k-2026-03-24`: 最大 300k トークン(Opus 4.7, 4.6, Sonnet 4.6)
|
||||
|
||||
## 4. Extended Thinking との組み合わせ制約
|
||||
|
||||
Source: https://platform.claude.com/docs/en/build-with-claude/extended-thinking
|
||||
|
||||
- `thinking.budget_tokens` は必ず `max_tokens` **未満** でなければならない
|
||||
- thinking トークンは `max_tokens` の上限に含まれてカウントされる
|
||||
- `budget_tokens` の最小値は **1,024 トークン**
|
||||
- 例外: ツールを伴う interleaved thinking では `budget_tokens` が `max_tokens` を超えることが許容される(予算がコンテキストウィンドウ全体に対して適用されるため)
|
||||
- `max_tokens` が 21,333 を超える場合はストリーミングが必須
|
||||
- Claude Opus 4.6 / Sonnet 4.6 以降では `budget_tokens` は非推奨になり、代わりに `effort` パラメータによる adaptive thinking が推奨されている
|
||||
|
||||
## 5. ドキュメント URL
|
||||
|
||||
- Messages API リファレンス: https://platform.claude.com/docs/en/api/messages
|
||||
- Extended Thinking ガイド: https://platform.claude.com/docs/en/build-with-claude/extended-thinking
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
# Google Gemini API: `maxOutputTokens` パラメータ仕様
|
||||
|
||||
Source: https://ai.google.dev/api/generate-content
|
||||
Source (thinking): https://ai.google.dev/gemini-api/docs/thinking
|
||||
Source (Gemini 2.5 Flash): https://ai.google.dev/gemini-api/docs/models/gemini-2.5-flash
|
||||
Source (Gemini 2.5 Pro): https://ai.google.dev/gemini-api/docs/models/gemini-2.5-pro
|
||||
Retrieved: 2026-04-28
|
||||
|
||||
---
|
||||
|
||||
## 1. パラメータ名と位置
|
||||
|
||||
`generationConfig.maxOutputTokens`
|
||||
|
||||
リクエストボディのトップレベルではなく、`generationConfig` オブジェクト内に配置する。
|
||||
SDK では `GenerateContentConfig(max_output_tokens=...)` として渡す。
|
||||
|
||||
## 2. 必須 / 任意
|
||||
|
||||
**任意 (optional)**。省略時はモデルのデフォルト上限が適用される。
|
||||
|
||||
## 3. 型と範囲
|
||||
|
||||
- 型: `integer`
|
||||
- モデル別の最大値:
|
||||
- `gemini-2.5-flash`: 最大 **65,536** トークン
|
||||
- `gemini-2.5-pro`: 最大 **65,536** トークン
|
||||
- 最小値の公式明記はないが、正の整数を指定する。
|
||||
|
||||
## 4. thinking トークンとの関係
|
||||
|
||||
- `maxOutputTokens` が制限するのは**最終レスポンスの出力トークン数のみ**。thinking トークンは `usageMetadata.thoughtsTokenCount` として別途計上され、`maxOutputTokens` のカウントには含まれない。
|
||||
- thinking トークンの制御には `generationConfig.thinkingConfig.thinkingBudget` を用いる。
|
||||
- Gemini 2.5 Flash / Pro: `128`〜`32768` トークン、`0` で thinking 無効化(モデルによる)、`-1` で動的
|
||||
- 課金は「output tokens + thinking tokens」の合算。
|
||||
- `maxOutputTokens` と `thinkingBudget` は独立したパラメータであり、両方を同時に指定できる。
|
||||
|
||||
> **注意**: 2025年10月時点で `gemini-2.5-flash` において `max_output_tokens` が無視されるバグが報告されており、Google 側が修正をロールアウトした経緯がある。最新モデルで想定通りに機能するか実測で確認することを推奨。
|
||||
|
||||
## 5. ドキュメント URL
|
||||
|
||||
- API リファレンス (GenerationConfig): https://ai.google.dev/api/generate-content#v1beta.GenerationConfig
|
||||
- Thinking ガイド: https://ai.google.dev/gemini-api/docs/thinking
|
||||
- Gemini 2.5 Flash モデル仕様: https://ai.google.dev/gemini-api/docs/models/gemini-2.5-flash
|
||||
- Gemini 2.5 Pro モデル仕様: https://ai.google.dev/gemini-api/docs/models/gemini-2.5-pro
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
# `llm-worker` vs Rust LLM ライブラリ群 比較レポート
|
||||
|
||||
調査日: 2026-04-26
|
||||
対象: `crates/llm-worker` (本プロジェクト) / `rig` / `genai` / `swiftide`
|
||||
|
||||
調査方法: 各リポジトリを ローカルに取得し、ソース(README, Cargo.toml, src/, examples/)を直接読解。
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR
|
||||
|
||||
```
|
||||
低レイヤ ←──────────────────────────────────────→ 高レイヤ
|
||||
HTTP wrapper Worker/Loop Agent Framework Pipeline/RAG
|
||||
───────────── ──────────── ──────────────── ──────────────
|
||||
genai ●●●●● (out-of-scope)
|
||||
llm-worker ●●●●● (一部)
|
||||
rig ●●● ●●●●● ●●●
|
||||
swiftide ●● ●●●● ●●●●● (主軸)
|
||||
```
|
||||
|
||||
- **`genai`** … マルチプロバイダの「統一 chat client」。エージェントループ・状態管理は明示的にスコープ外。
|
||||
- **`llm-worker`** … Worker = 「LLM 対話の中央実行器」。低レベル(HTTP)と高レベル(Agent / RAG / Pipeline)の中間に位置するレイヤを精緻に作っている。
|
||||
- **`rig`** … Agent / Tool / Pipeline / VectorStore を一通り揃えた汎用フレームワーク。Provider 数とエコシステムが最大の武器。
|
||||
- **`swiftide`** … 一次目的はストリーミング Indexing / Query パイプライン。Agent はあとから足された二の矢で、`async-openai` / `async-anthropic` をラップ。
|
||||
|
||||
要点: **`llm-worker` の独自性は「型状態でのキャッシュ保護」「Interceptor による上位層への決定委譲」「Prune を context projection として非破壊で扱う」の 3 点**。これらは他 3 者の誰も持っていない。一方、Provider カバレッジ・VectorStore・Pipeline DAG では rig に大差で負ける。
|
||||
|
||||
---
|
||||
|
||||
## 1. スコープ宣言(各プロジェクトが自分を何と呼んでいるか)
|
||||
|
||||
| プロジェクト | 自称 | 提供する | 提供しない(明示・暗黙) |
|
||||
|---|---|---|---|
|
||||
| **llm-worker** | "LLM との対話を管理する低レベル基盤クレート" | Worker による turn 実行、Tool 実行、stream イベント、Interceptor、cache 保護 | RAG / Embedding / VectorStore / Pipeline DAG / 永続化 |
|
||||
| **rig** | "Ergonomic & modular library for building LLM applications" | Agent / Tool / Pipeline / Vector / Embedding / OTel | (ほぼ全部入り。明示的な non-goal は薄い) |
|
||||
| **genai** | "Standardizing chat completion APIs across major AI services" | Chat (sync/stream)、Tool 定義、Embedding | **Agent loop / 会話状態管理を明示的に out-of-scope** |
|
||||
| **swiftide** | "Fast, streaming indexing, query, and agentic LLM applications" / "data pipeline" | Indexing pipeline / Query state machine / Agent / Hook | (LLM HTTP は外部 SDK = `async-openai` 等に委譲) |
|
||||
|
||||
`llm-worker` のスコープ宣言は他より控えめで、「何かの上位層から呼ばれる前提」が読み取れる(`Interceptor` の存在がそれを裏付ける)。
|
||||
|
||||
---
|
||||
|
||||
## 2. レイヤ階層と被り
|
||||
|
||||
`Worker` が担っているレイヤを基準にすると、各プロジェクトの「同じ層」と「上下層」は次のように整理できる。
|
||||
|
||||
### 同じ層(== 競合)
|
||||
|
||||
| 機能 | llm-worker | rig | genai | swiftide |
|
||||
|---|---|---|---|---|
|
||||
| Provider 抽象 (HTTP) | ◎ 自前 (4) | ◎ 自前 (20+) | ◎ 自前 (18+) | ○ 外部 SDK ラップ |
|
||||
| Streaming イベント正規化 | ◎ | ◎ | ◎ | ○ |
|
||||
| Tool 定義 + 実行ループ | ◎ | ◎ | △ (定義のみ、loop は呼び出し側) | ◎ |
|
||||
| Multi-turn loop | ◎ | ◎ | ✕ | ◎ |
|
||||
| Hook / Interceptor | ◎ Interceptor + closure | ◎ PromptHook | △ Resolver のみ | ◎ 6+ Hook |
|
||||
| 履歴管理 | ◎ Item / ContentPart | ◎ Message | △ ChatRequest を呼び出し側で append | ◎ MessageHistory trait |
|
||||
|
||||
### 上下層(== 補完関係)
|
||||
|
||||
| 機能 | llm-worker | rig | genai | swiftide |
|
||||
|---|---|---|---|---|
|
||||
| Embedding / VectorStore | ✕ | ◎ 10+ | △ (Embed のみ) | ◎ 9+ |
|
||||
| RAG | ✕ | ◎ | ✕ | ◎ |
|
||||
| Pipeline DAG | ✕ | ◎ `parallel!` / `try_op` | ✕ | ◎ Indexing pipeline |
|
||||
| 永続化 | ✕ | △ | ✕ | ◎ MessageHistory swap |
|
||||
| OTel / Observability | △ tracing のみ | ◎ GenAI Semantic Conv | △ | ◎ Langfuse 等 |
|
||||
| 型状態でのキャッシュ保護 | **◎ 唯一** | ✕ | ✕ | ✕ |
|
||||
|
||||
---
|
||||
|
||||
## 3. コアの抽象を並べる
|
||||
|
||||
### `llm-worker::Worker<C, S>`
|
||||
- `C: LlmClient`(プロバイダ)、`S: WorkerState`(`Mutable` | `Locked` の sealed trait)
|
||||
- ライフサイクル: `Mutable` で履歴/プロンプト編集 → `lock()` → `Locked` で `run()` / `resume()` → `unlock()` で戻る
|
||||
- `state.rs:1-61`: `Locked` 中は履歴 append-only、`set_system_prompt` 等は型レベルで呼べない
|
||||
- `prune.rs:66-118`: `prunable_indices()` + `project()` で context のみ縮約。永続履歴は不変 = "context projection"
|
||||
|
||||
### `rig::Agent` + `PromptRequest`
|
||||
- 状態機械型: `max_turns`、`extended_details()` で usage / message tracking
|
||||
- `PromptHook` trait: `on_completion_call`, `on_completion_response`, `on_tool_call` (skip 可), `on_tool_result`, `on_text_delta`, …
|
||||
- `ToolSet` + `ToolEmbedding` で RAG 可能なツール
|
||||
- Pipeline DAG: `Op` trait + `parallel!` macro
|
||||
|
||||
### `genai::Client`
|
||||
- `exec_chat(model, ChatRequest, options) -> ChatResponse`
|
||||
- `ChatStreamEvent { Start, Chunk, ReasoningChunk, ToolCallChunk, End }`
|
||||
- Adapter pattern: `to_web_request_data` / `to_chat_response` / `to_chat_stream`
|
||||
- モデル名の prefix で provider 推論 (`gpt-` → OpenAI, `claude-` → Anthropic)
|
||||
- 状態管理は呼び出し側(`ChatRequest` を毎ターン clone & append)
|
||||
|
||||
### `swiftide-agents::Agent`
|
||||
- Builder: `llm`, `context`, `tools`, `toolboxes`, `hooks`
|
||||
- `AgentContext` trait + `DefaultContext` (AtomicUsize で completions ポインタ管理)
|
||||
- State enum: `Pending` / `Running` / `Stopped(StopReason)`
|
||||
- Tool execution: `LocalExecutor` と MCP (Model Context Protocol)
|
||||
- Provider 層は `async-openai` 0.33+ / `async-anthropic` 0.6 を流用
|
||||
|
||||
---
|
||||
|
||||
## 4. 三大独自点(`llm-worker` だけが持っているもの)
|
||||
|
||||
### 4.1 型状態によるキャッシュ保護 — `state::WorkerState`
|
||||
|
||||
```
|
||||
Worker<C, Mutable> Worker<C, Locked>
|
||||
├─ history_mut() ✓ ├─ history_mut() ✗ (compile error)
|
||||
├─ set_system_prompt() ✓ ├─ set_system_prompt() ✗
|
||||
├─ register_tool() ✓ ├─ run() / resume() ✓
|
||||
└─ lock() ─────────────────→ │
|
||||
└─ unlock() ──────────→ Mutable
|
||||
```
|
||||
|
||||
- **何を防ぐか**: KV cache の prefix が変わるような編集を `Locked` 中に行うこと(=次の `run()` で cache miss を引く事故)
|
||||
- **誰がやっているか**: rig / genai / swiftide のいずれもキャッシュ整合性は「呼び出し側のお気持ち」レベル。genai は `CacheControl::Ephemeral / Stable` enum を持つが、それはマーク用であって状態機械ではない
|
||||
- **意義**: production の Anthropic / OpenAI prompt cache 利用で、誤った `set_system_prompt` が静かに課金を倍増させる事故を**コンパイル時に潰せる**
|
||||
|
||||
### 4.2 Interceptor による上位層への決定委譲 — `interceptor.rs`
|
||||
|
||||
`Interceptor` trait が持つフック点:
|
||||
- `on_prompt_submit` → `PromptAction`
|
||||
- `pre_llm_request` → `PreRequestAction`
|
||||
- `pre_tool_call` → `PreToolAction`
|
||||
- `post_tool_call` → `PostToolAction`
|
||||
- `on_turn_end` → `TurnEndAction`
|
||||
|
||||
**rig の `PromptHook` との違い**: rig のフックは「観察 + skip/abort のフラグ」。`llm-worker` は **戻り値が ActionEnum** で、上位層が次の挙動を選択できる(例: `PreToolAction::Override(custom_result)`)。これは `Worker` を「自律ループ」と「上位 orchestrator から駆動される実行器」の両方として使える設計上の選択。
|
||||
|
||||
### 4.3 Context Projection による prune — `prune.rs`
|
||||
|
||||
- 削るのは `Item::ToolResult { content }` の中身だけ。`Item` 自体は履歴に残す(summary は維持)
|
||||
- `project()` は **イミュータブル変換** で永続履歴を変えない
|
||||
- savings 推定は意図的に上位層に委譲(`prune.rs` 末尾コメント)
|
||||
- swiftide も MessageHistory に `Summary` メッセージで似たことをやるが、**永続履歴を直接置換**する方式。`llm-worker` の方が rollback / 再生 / debugging に強い
|
||||
|
||||
---
|
||||
|
||||
## 5. 各競合の「これは llm-worker に勝っている」点
|
||||
|
||||
### rig
|
||||
- **Provider 数**: 20+(OpenAI / Anthropic / Gemini / Bedrock / Vertex / Azure / Groq / DeepSeek / Cohere / Mistral / OpenRouter / Together / xAI / Perplexity / HuggingFace …)
|
||||
- **VectorStore**: MongoDB / LanceDB / Neo4j / Qdrant / SQLite / SurrealDB / Milvus / ScyllaDB / S3Vectors / HelixDB
|
||||
- **Pipeline DAG**: `parallel!` macro / `try_op` / agent_ops(Agent を Op として組み立て可能)
|
||||
- **OTel**: GenAI Semantic Convention に完全準拠
|
||||
- **WASM**: `rig-core` のみ WASM 互換
|
||||
- **更新頻度**: 直近 20 commit が 1〜2 週間以内
|
||||
|
||||
### genai
|
||||
- **Provider 数**: 18+
|
||||
- **薄さ**: 6,539 LOC / 35 deps で multi-provider client が完成している(`llm-worker` の `llm_client/` は 1.5k LOC で 4 provider)
|
||||
- **Native protocol サポート**: Anthropic reasoning, Gemini thinking, OpenAI strict mode, OpenAI Responses API の `previous_response_id`/`store` を素通し
|
||||
- **Tool call streaming**: `ToolCallChunk` が分離イベント
|
||||
- **Resolver** で auth / model / endpoint をフレキシブルに差し替え
|
||||
|
||||
### swiftide
|
||||
- **Indexing pipeline**: streaming loader → transformer → chunker → storage の fluent パイプライン
|
||||
- **Query state machine**: Pending → Retrieved → Answered の型状態(くしくも型状態だが対象が違う)
|
||||
- **Hook 粒度**: `before_all` / `before_completion` / `after_completion` / `before_tool` / `after_tool` / `on_new_message` / `on_stop` 等 6+
|
||||
- **MCP サポート**: ToolExecutor として LocalExecutor と MCP の両方
|
||||
- **MessageHistory trait** で永続化バックエンド(Redis 等)に差し替え可能
|
||||
- **既存 SDK の活用**: `async-openai` / `async-anthropic` をベースに薄く乗せる戦略
|
||||
|
||||
---
|
||||
|
||||
## 6. 各競合の「これは llm-worker の方が良い」点
|
||||
|
||||
### vs rig
|
||||
- **キャッシュ整合性**: rig は `extended_details()` で usage を観測できるが、prefix 変更を**防止**するメカニズムはない
|
||||
- **依存の薄さ**: rig-core 25.5K LOC + 大量 sub-crate vs llm-worker 5.8K LOC
|
||||
- **Worker の単一責務**: rig の `Agent` は LLM 呼び出し + Tool + (optional) RAG までを抱え込む。`llm-worker` は orchestrator を分離
|
||||
|
||||
### vs genai
|
||||
- **Tool 実行ループ**: genai は呼び出し側で書く必要がある(README/example で明示)
|
||||
- **会話履歴の構造化**: genai は `ChatRequest` を毎ターン clone する素朴な API
|
||||
- **Interceptor**: genai は `Resolver` で初期化時のカスタマイズのみ。実行中フックなし
|
||||
|
||||
### vs swiftide
|
||||
- **Provider 直接実装**: swiftide は `async-openai` 0.33 / `async-anthropic` 0.6 に依存しており、その上流に出来る/出来ないが縛られる。`llm-worker` は HTTP まで自前で握る
|
||||
- **Agent loop の中核設計**: swiftide-agents は `DefaultContext` の AtomicUsize ベースで素朴。型状態保護なし
|
||||
- **依存の少なさ**: swiftide-agents 単体でも `async-openai` を呼ぶ前提
|
||||
|
||||
---
|
||||
|
||||
## 7. 「ライブラリ化したとき競合するか」の解像度
|
||||
|
||||
| 相手 | 競合度 | 理由 |
|
||||
|---|---|---|
|
||||
| **rig** | 🟥 高 | 同じ "agent + tool + multi-provider" を狙う層。rig は既にエコシステムを持っており、正面衝突は不利 |
|
||||
| **genai** | 🟨 中 | 層が違う(client vs worker)。むしろ `llm-worker` の `llm_client/` を捨てて genai に乗る選択肢がある |
|
||||
| **swiftide** | 🟩 低 | 一次目的が違う(pipeline)。agent の中核実行を `llm-worker` に置き換える "embed" 戦略すら成立し得る |
|
||||
|
||||
---
|
||||
|
||||
## 8. ポジショニング案(仮にライブラリ化するなら)
|
||||
|
||||
`llm-worker` がぶつからない・かつ需要がある場所を考えると、次のような立ち位置が現実的:
|
||||
|
||||
> **"Production-grade LLM worker primitive — 型状態でキャッシュ整合性を守り、Interceptor で上位層 (rig / swiftide / 自社 orchestrator) から駆動できる低レベル実行器"**
|
||||
|
||||
具体的な打ち出し方:
|
||||
1. **キャッシュ整合性をコンパイル時に保証する唯一のクレート** という明確な売り文句。Anthropic の prompt cache 課金事故で困った人を狙う
|
||||
2. **Tool 実行ループ + Interceptor** をプリミティブとして提供し、上位フレームワークから plug できることを主張
|
||||
3. **HTTP まで自前** はやめて、`llm_client/` を外す or feature 化し、`genai` バックエンドを optional 提供する選択肢も検討に値する(実装コストを 1.5k LOC 削れる)
|
||||
4. **Pipeline / RAG / VectorStore は提供しない** ことを明示。rig / swiftide の代替を狙わないことで、共存ストーリーが書ける
|
||||
|
||||
---
|
||||
|
||||
## 9. ライブラリ化の判断材料(再)
|
||||
|
||||
- **需要がある層か**: yes。rig / swiftide / genai は揃って "もう一段下のキャッシュ整合性プリミティブ" を持っていない
|
||||
- **既存と被るか**: 上記 3 案でかわせる
|
||||
- **維持コスト**: API 安定化 + provider 追従が恒常的に乗る。`llm_client/` を外すか genai に寄せるかでだいぶ軽くなる
|
||||
- **タイミング**: yoi 本体がリリースされ、`Worker` の API が stress test を受ける前に公開すると、後から破壊的変更を強いられる。**先に yoi をリリースして、production 使用例として参照させてからライブラリ化する**方が安全
|
||||
|
||||
---
|
||||
|
||||
## 付録: 主要ファイルへの参照
|
||||
|
||||
### llm-worker
|
||||
- `crates/llm-worker/src/lib.rs:1-59` — モジュール構成
|
||||
- `crates/llm-worker/src/worker.rs:101-191` — Worker<C, S>
|
||||
- `crates/llm-worker/src/state.rs:1-61` — Mutable / Locked 型状態
|
||||
- `crates/llm-worker/src/interceptor.rs:1-147` — Interceptor trait
|
||||
- `crates/llm-worker/src/prune.rs:66-118` — context projection
|
||||
- `crates/llm-worker/src/llm_client/{auth,client,event,transport,types}.rs`
|
||||
- `crates/llm-worker/src/tool_server.rs:27-52` — Deferred 登録
|
||||
|
||||
### rig
|
||||
- `github.com/0xPlaygrounds/rig/rig/rig-core/src/{agent,completion,tool,pipeline,vector_store,streaming}/mod.rs`
|
||||
|
||||
### genai
|
||||
- `github.com/jeremychone/rust-genai/src/lib.rs:1-25`
|
||||
- `.../src/client/client_impl.rs:62-67`
|
||||
- `.../src/chat/chat_request.rs:10-34`
|
||||
- `.../src/chat/chat_stream.rs:11-86`
|
||||
- `.../src/adapter/adapter_types.rs:11-59`
|
||||
|
||||
### swiftide
|
||||
- `github.com/bosun-ai/swiftide/swiftide-agents/src/agent.rs:45-100`
|
||||
- `.../swiftide-agents/src/hooks.rs`
|
||||
- `.../swiftide-core/src/chat_completion/traits.rs`
|
||||
- `.../swiftide-indexing/src/pipeline.rs`
|
||||
- `.../swiftide-query/src/`
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
# OpenAI Chat Completions API — 出力トークン数制御パラメータ仕様
|
||||
|
||||
- **Source**: https://platform.openai.com/docs/api-reference/chat/create
|
||||
- **Supplementary**: https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/reasoning
|
||||
- **Retrieved**: 2026-04-28
|
||||
|
||||
---
|
||||
|
||||
## 1. `max_tokens` と `max_completion_tokens` の関係
|
||||
|
||||
| パラメータ | 状態 | 対応モデル |
|
||||
|---|---|---|
|
||||
| `max_tokens` | **Deprecated** | GPT-3.5, GPT-4 系など旧モデルでは動作する |
|
||||
| `max_completion_tokens` | 現行・推奨 | 全モデル(旧モデルにも後方互換あり) |
|
||||
|
||||
- `max_tokens` は o1 系以降では **受け付けられない**(エラーまたは無視)。
|
||||
- 変更の背景: 旧来の `max_tokens` は「返却トークン = 生成トークン = 課金トークン」を前提にしていた。o1 系で推論トークン(reasoning tokens)が導入されたことでこの前提が崩れ、新パラメータが設計された。
|
||||
- `max_completion_tokens` は旧モデルでも機能するため、**新規実装では `max_completion_tokens` を使うべき**。
|
||||
|
||||
## 2. 必須か任意か
|
||||
|
||||
- **任意(optional)**。
|
||||
- 指定しない場合はモデルのコンテキスト上限まで生成する(デフォルト: `null`)。
|
||||
|
||||
## 3. 型と範囲
|
||||
|
||||
- **型**: `integer | null`
|
||||
- **範囲**: `1` 以上、モデルのコンテキストウィンドウの残りトークン数以下。上限値はモデルごとに異なり、ドキュメント上に固定の最大値は明示されていない。
|
||||
- `null` を渡すと制限なし(モデル上限に従う)。
|
||||
|
||||
## 4. Reasoning モデルでの reasoning tokens のカウント
|
||||
|
||||
- `max_completion_tokens` の上限には **reasoning tokens(推論トークン)を含む**。
|
||||
- reasoning tokens: モデルが内部で生成するが API レスポンスには含まれない隠しトークン。
|
||||
- 課金対象は reasoning tokens + visible output tokens の合計。
|
||||
- レスポンスの `usage.completion_tokens_details.reasoning_tokens` で内訳を確認できる。
|
||||
- したがって、`max_completion_tokens = 5000` と設定しても、推論に多くのトークンを使った場合、目に見える出力は 5000 より少なくなる。
|
||||
|
||||
## 5. Ollama の OpenAI compat API での扱い(補助情報)
|
||||
|
||||
- Ollama の `/v1/chat/completions` は現時点で **`max_tokens` のみを公式サポート**している(内部的に `num_predict` にマッピング)。
|
||||
- `max_completion_tokens` サポートは Issue #7125 / PR #14464 で議論中だが、2026-04-28 時点では公式ドキュメント上に記載なし。
|
||||
- **Ollama に対しては `max_tokens` を使う**のが安全な選択。ただし将来的に `max_completion_tokens` に移行される見込み。
|
||||
|
||||
## 6. ドキュメント URL
|
||||
|
||||
- [OpenAI Chat Completions API Reference](https://platform.openai.com/docs/api-reference/chat/create)
|
||||
- [Azure OpenAI Reasoning Models (GPT-5, o3, o1)](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/reasoning)
|
||||
- [Ollama OpenAI Compatibility](https://docs.ollama.com/api/openai-compatibility)
|
||||
- [Ollama Issue #7125 — max_completion_tokens support](https://github.com/ollama/ollama/issues/7125)
|
||||
- [OpenAI Community — Why max_tokens changed to max_completion_tokens](https://community.openai.com/t/why-was-max-tokens-changed-to-max-completion-tokens/938077)
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# OpenAI Responses API — `max_output_tokens` Parameter
|
||||
|
||||
- **Source**: https://platform.openai.com/docs/api-reference/responses/create
|
||||
- **Retrieved**: 2026-04-28
|
||||
|
||||
---
|
||||
|
||||
## 1. パラメータ名
|
||||
|
||||
`max_output_tokens` — 正しい。Chat Completions API の `max_tokens` / `max_completion_tokens` とは別物。
|
||||
|
||||
## 2. 必須 / 任意
|
||||
|
||||
**任意 (optional)**。省略時のデフォルトは `inf`(モデルが許容する最大出力トークン数)。
|
||||
|
||||
## 3. 型と範囲
|
||||
|
||||
| 項目 | 値 |
|
||||
|---|---|
|
||||
| 型 | `integer` または文字列 `"inf"` |
|
||||
| 最小値 | `1` |
|
||||
| 最大値 | モデルごとの最大出力トークン数(例: gpt-4.1 系は 32,768) |
|
||||
| デフォルト | `inf` |
|
||||
|
||||
上限に達した場合、レスポンスの `status` が `"incomplete"` になり、`incomplete_details.reason` が `"max_output_tokens"` にセットされる。
|
||||
|
||||
## 4. Reasoning tokens との関係 / Reasoning モデルとの組合せ制約
|
||||
|
||||
`max_output_tokens` は **reasoning tokens を含む** 合計生成トークン数の上限として機能する。
|
||||
公式ガイド (https://platform.openai.com/docs/guides/reasoning) には以下の記述がある:
|
||||
|
||||
> "You can limit the total number of tokens the model generates (including both reasoning and final output tokens) by using the max_output_tokens parameter."
|
||||
|
||||
**実用上の注意点:**
|
||||
- モデルが内部思考に多数の reasoning tokens を消費した後に上限に達すると、visible output が一切返らずに打ち切られる場合がある。
|
||||
- コスト制御目的には `reasoning.effort` (`"low"` など) の使用が推奨される。`max_output_tokens` はあくまで暴走抑止のガードとして位置づける。
|
||||
- o シリーズなど reasoning モデルでは `reasoning.max_tokens` (別パラメータ) で reasoning 専用の上限を設定できる場合もある。
|
||||
|
||||
## 5. Codex CLI 互換 Responses 経路における取り扱い
|
||||
|
||||
この経路は公式 Responses API のパラメータをすべて受け付けるわけではなく、`max_output_tokens` を **サポートしないパラメータとして 400 エラーで拒否する**。
|
||||
|
||||
LiteLLM の調査 (https://github.com/BerriAI/litellm/issues/21193) によれば、この経路が受け付けるパラメータは以下に限られる:
|
||||
|
||||
```
|
||||
model, input, instructions, stream, store, include,
|
||||
tools, tool_choice, reasoning, previous_response_id, truncation
|
||||
```
|
||||
|
||||
`max_output_tokens`, `max_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `user`, `metadata`, `context_management` はすべて拒否される(実観測でも `temperature` 同梱リクエストは `{"detail":"Unsupported parameter: temperature"}` を返す)。
|
||||
|
||||
Codex CLI 自身も `config.toml` の `model_max_output_tokens` を API リクエストに載せない実装になっており (https://github.com/openai/codex/issues/4138)、これはバグではなく ChatGPT backend の制約に対する回避策と解釈できる。同 CLI は `temperature` / `top_p` も送出しない。
|
||||
|
||||
本リポジトリでは `OpenAIResponsesScheme` の `send_max_output_tokens` / `send_sampling_params` フラグでこれらの送出を一括制御し、`provider/src/lib.rs` 内で `AuthRef::CodexOAuth` 指定時に両方 `false` にする。
|
||||
|
||||
## 6. ドキュメント URL
|
||||
|
||||
- 公式 API リファレンス: https://platform.openai.com/docs/api-reference/responses/create
|
||||
- Reasoning ガイド: https://platform.openai.com/docs/guides/reasoning
|
||||
- Codex CLI issue (max_output_tokens 未送信): https://github.com/openai/codex/issues/4138
|
||||
- LiteLLM issue (ChatGPT backend 拒否パラメータ一覧): https://github.com/BerriAI/litellm/issues/21193
|
||||
- OpenAI Community (reasoning tokens 上限): https://community.openai.com/t/limiting-maximum-number-of-reasoning-tokens/1285430
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# OpenAI Responses API — `prompt_cache_key` Parameter
|
||||
|
||||
- **Source**: https://platform.openai.com/docs/api-reference/responses/create
|
||||
- **Retrieved**: 2026-05-02
|
||||
|
||||
---
|
||||
|
||||
## 1. パラメータ名
|
||||
|
||||
`prompt_cache_key` — Responses API のリクエスト body に載せる任意の文字列キー。
|
||||
プロンプトキャッシュのスコープを明示する。
|
||||
|
||||
## 2. 必須 / 任意
|
||||
|
||||
**任意 (optional)**。
|
||||
|
||||
公式 OpenAI Responses API では省略しても automatic prefix matching が
|
||||
走るためキャッシュは効く。一方 ChatGPT backend (codex-oauth /
|
||||
`https://chatgpt.com/backend-api/codex/responses`) では **明示キーが
|
||||
無いと事実上ヒットしない**(後述)。
|
||||
|
||||
## 3. ChatGPT backend (`codex-oauth`) でのキャッシュ挙動
|
||||
|
||||
実観測(`019de419-...` セッション、171 turn / 累計入力 22.2M token)で
|
||||
`cache_read_tokens` が全 turn 0 だった。最新セッション (`019de48f-...`,
|
||||
5 turn) でも 0。
|
||||
|
||||
原因:
|
||||
|
||||
1. ChatGPT backend のプロンプトキャッシュは org / project 単位で
|
||||
ハッシュ衝突する設計
|
||||
2. `prompt_cache_key` を送らないと、複数 conversation のリクエストが
|
||||
同じハッシュ空間に積み上がり、prefix が他 conversation で
|
||||
上書きされてヒット率が落ちる
|
||||
3. Codex CLI の実装はこれを認識しており、conversation_id を毎リクエスト
|
||||
送って自分専用の名前空間にキャッシュさせている:
|
||||
|
||||
```rust
|
||||
// codex-rs/core/src/client.rs:853
|
||||
let prompt_cache_key = Some(self.client.state.conversation_id.to_string());
|
||||
```
|
||||
|
||||
## 4. 公式 OpenAI API での挙動
|
||||
|
||||
公式エンドポイント (`https://api.openai.com/v1/responses`) では、
|
||||
明示キーが無くても automatic prefix matching が走る。明示キーを
|
||||
送ることで、複数 client / 複数 organization が同じ prefix を共有する
|
||||
シナリオ(マルチテナント等)で意図しないヒット混線を避ける用途
|
||||
で使う。少なくとも害は無いので両 backend で同じ値を送って良い。
|
||||
|
||||
## 5. yoi での運用
|
||||
|
||||
- `Request::cache_key: Option<String>` を provider-agnostic な
|
||||
キャッシュヒントとして持つ。`cache_anchor` (Anthropic 用 prefix
|
||||
index) と並立する別概念。
|
||||
- `OpenAIResponsesScheme::build_request` で
|
||||
`request.cache_key.clone()` を `prompt_cache_key` に投影。
|
||||
`None` のときは body にキー自体が載らない
|
||||
(`#[serde(skip_serializing_if = "Option::is_none")]`)。
|
||||
- pod 側は LLM 呼び出し時に `SessionId.to_string()` を渡す。
|
||||
主 Run / compactor / extract / consolidate worker のすべてが
|
||||
同じ `session_id` を使うので、pod 内の派生 worker が prefix を
|
||||
共有しているところでヒットが期待できる。
|
||||
- 他 scheme (`anthropic`, `gemini`, `openai_chat`) は
|
||||
`Request::cache_key` を未参照のまま無視する。
|
||||
|
||||
## 6. Fork との関係
|
||||
|
||||
`session-store::fork` / `fork_at` はいずれも新 `SessionId` を発行する。
|
||||
新 fork = 新 cache_key とする(素直に `SessionId.to_string()` を渡す)。
|
||||
|
||||
fork 直後の cache 明示ヒットは失われるが、OpenAI Responses は
|
||||
automatic prefix matching も走るため完全に冷えるわけではない。
|
||||
fork 越しに親の cache_key を継承して明示ヒットも残す最適化は
|
||||
別チケット扱い。
|
||||
|
||||
## 7. Compaction との関係
|
||||
|
||||
compaction は session_id を入れ替える (`create_compacted_session`)。
|
||||
compact 直後に worker の `cache_key` も新 session_id で更新するため、
|
||||
post-compact turn は extract / consolidate worker と同じ namespace で
|
||||
動く。compact 自体は prefix を大幅に書き換えるので、明示キー継続の
|
||||
有無に関わらずヒット率は元から低い。
|
||||
|
||||
## 8. ドキュメント URL
|
||||
|
||||
- 公式 API リファレンス: https://platform.openai.com/docs/api-reference/responses/create
|
||||
- Codex CLI 実装 (conversation_id を prompt_cache_key に渡す):
|
||||
https://github.com/openai/codex/blob/main/codex-rs/core/src/client.rs
|
||||
- ChatGPT backend のサポートパラメータ (LiteLLM issue):
|
||||
https://github.com/BerriAI/litellm/issues/21193
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
# DeltaDB 調査メモ(Zed Industries)
|
||||
|
||||
調査日: 2026-05-01
|
||||
出典: Zed 公式ブログ「Sequoia Backs Zed's Vision for Collaborative Coding」を中心に、二次情報・関連記事を補強。
|
||||
|
||||
## 1. 何か
|
||||
|
||||
DeltaDB は Zed Industries が開発中の **operation-based version control system / synchronization engine** である。Zed エディタの Series B(Sequoia Capital 主導、$32M)と同時に発表された、同社の次フェーズの中核プロダクト。
|
||||
|
||||
ひとことで言うと「Git の commit ベースを置き換えるのではなく、**コミット間の "あらゆる編集操作"** を粒度として保持する VCS」。CRDT を用いてリアルタイムに変更を記録・同期し、Git と相互運用可能な設計を採る。
|
||||
|
||||
> "DeltaDB ... uses CRDTs to incrementally record and synchronize changes as they happen ... designed to interoperate with Git" — Zed Blog
|
||||
|
||||
## 2. 動機(解こうとしている問題)
|
||||
|
||||
LLM/AI エージェント時代のコーディングで、Git の粒度が粗すぎることが課題と捉えられている。
|
||||
|
||||
- **会話/意図がコードから剥がれる**: PR コメントやチャットでのやり取り、AI への指示・修正・pivot は、コードが変わると参照先を失い、文脈ごと失われる。
|
||||
- **スナップショットでは追えない**: Git は commit という離散点しか持たない。エージェントとの "continuous dialogue" や、commit 未満の編集ステップを履歴として扱えない。
|
||||
- **同時編集の衝突**: 複数の AI エージェント+人間がリアルタイムに編集する時、従来の merge conflict モデルは機能しにくい。
|
||||
- **コード位置の永続参照**: コードがリファクタや改名で移動するたび、URL 形式のパーマリンクや議論の固定ピンが切れる。
|
||||
|
||||
これらは OpenAI の Sean Grove が指摘した「進化する仕様・プロンプトをどう track するか」という課題感とも重なるとされている。
|
||||
|
||||
## 3. 技術的な構成要素
|
||||
|
||||
### 3.1 Operation-based design(vs. snapshot-based)
|
||||
|
||||
- Git の **commit = snapshot** に対し、DeltaDB は **operation = 編集 1 個** を一次データとして保持する。
|
||||
- 「every operation, not just commits」を track する。
|
||||
- スナップショットは operation log から導出可能な派生物として位置付けられる(VCS 系では "operational" / "patch theory" 系の系譜:Pijul, Fossil, darcs と同じ家系の発想)。
|
||||
|
||||
### 3.2 CRDT による同期
|
||||
|
||||
- 並行編集の整合性を CRDT(Conflict-free Replicated Data Types)で取る。
|
||||
- これは Zed エディタの multiplayer editing で既に実戦投入されている技術の延長:
|
||||
- **Logical location**: 編集をオフセットではなく `(insertion id, offset)` のアンカーで表現し、操作を可換にする。
|
||||
- **Replica ID + sequence number**: 中央が replica id を一度割り当てれば、以降は各 replica が独立に一意 ID を生成可能。
|
||||
- **Tombstone**: 削除はテキストを物理削除せず墓標として残し、論理位置解決を保つ。
|
||||
- **Lamport timestamp / vector timestamp**: 因果順序を尊重し、並行挿入の可視性を制御。
|
||||
- **Per-user undo map**: 単一スタックではなく操作 ID → カウントの map で、ユーザーごとに独立 undo を実現。
|
||||
- DeltaDB はこの editor 内 CRDT を、**ファイル横断・リポジトリ規模・永続化**にスケールさせるレイヤと読み解ける。
|
||||
|
||||
### 3.3 Character-level permalink
|
||||
|
||||
- 「あらゆるコード変換を生き延びる文字単位のパーマリンク」を提供する。
|
||||
- スナップショット時刻のファイル+行番号ではなく、CRDT のアンカー(insertion id ベース)に対して URL 的な参照を発行できるため、リネーム・リフォーマット・移動でも切れない。
|
||||
- ユースケース: 議論/レビュー/AI への指示/設計メモを、特定の文字位置に永続的に固定する。
|
||||
|
||||
### 3.4 Git との相互運用
|
||||
|
||||
- リプレースではなく interop 前提。
|
||||
- 既存 Git リポジトリを保ったまま段階導入できる戦略で、エンタープライズ採用のハードルを下げる狙い。
|
||||
- 推測: operation log → Git commit へのフラット化/ Git commit → operation log への lift が可能な層を持つはず(公式の実装仕様は未公開)。
|
||||
|
||||
## 4. Zed エディタとの関係
|
||||
|
||||
- DeltaDB は Zed の中で「人間 × 人間」「人間 × AI エージェント」「AI × AI」の協働基盤になる。
|
||||
- Zed が既に持つ multiplayer editing(CRDT)を、**editor session の寿命を超えて永続化・分散同期**する位置づけ。
|
||||
- 「terminal interface、local IDE、web-based agent tool」の 3 系統の良さを束ねた統合 GUI を作る、という Zed の方針の中核。
|
||||
- 「コードベースを生きた、辿れる履歴(a living, navigable history)として扱う」というビジョンの実装手段。
|
||||
|
||||
## 5. ビジネス/ライセンス
|
||||
|
||||
- Zed 本体と同様に **オープンソース+ optional paid managed service** モデル。
|
||||
- Series B($32M、Sequoia 主導)の調達は DeltaDB 開発を主目的の一つとしている。報道では累計 $42M 規模との表記もある。
|
||||
- 競合として GitHub と比較する論調も出てきている(例: Hypeburner が "GitHub Competitor" と表現)。ただし公式は「Git と interop」を強調しており、置換戦略ではなく上位レイヤ戦略。
|
||||
|
||||
## 6. 既知の不明点(現時点で公開されていない情報)
|
||||
|
||||
- 具体的なデータモデル/ストレージフォーマット(log の物理表現、圧縮、GC、tombstone 回収など)。
|
||||
- Git ↔ DeltaDB ブリッジの双方向変換の詳細(特に rebase、cherry-pick、shallow clone との整合)。
|
||||
- ブランチ・マージのモデル(patch theory ライクな順序非依存マージか、Git ライクなブランチ概念を載せるか)。
|
||||
- スケーラビリティ特性(モノレポ、巨大履歴、多数 replica)。
|
||||
- アクセス制御・権限モデル(CRDT の特性上、後付けが難しい領域)。
|
||||
- リリース時期、API 仕様、SDK の有無。
|
||||
|
||||
これらは公開ロードマップが出るまで判断保留。
|
||||
|
||||
## 7. 関連技術/系譜
|
||||
|
||||
- **Operation-based / patch-based VCS**: darcs, Pijul, Fossil。理論的には patch theory/category-theoretic merge。
|
||||
- **CRDT 系コラボエディタ**: Google Docs, Figma, Zed multiplayer。Yjs / Automerge は CRDT ライブラリの代表。
|
||||
- **永続的位置参照**: Tree-sitter ベースの semantic anchor、Sourcegraph の SCIP、`git-blame` の line tracking。DeltaDB は CRDT identity を使うため理論的にこれらより堅牢な anchor を提供できる。
|
||||
- **AI エージェント協働基盤**: OpenAI の "evolving spec" 議論、Anthropic の Computer Use 系、各社の MCP。DeltaDB は「エージェント↔コード↔人間の対話」を VCS 層で受ける狙い。
|
||||
|
||||
## 8. 自プロジェクト(yoi)への含意メモ
|
||||
|
||||
参考材料として残す(採用の可否ではない)。
|
||||
|
||||
- LLM エージェントのインタラクション履歴をコードに永続的に紐付けたい場面(例: 「この修正はどの会話・どのプロンプトから来たか」)で、character-level permalink の発想は流用余地がある。
|
||||
- ScopedFs を将来スクリプティング言語に公開する計画(memory: project_scopedfs_scripting)と組み合わせる際、エージェントの編集系列を operation log として残すかどうかは設計判断ポイント。
|
||||
- 短期的には DeltaDB そのものを依存に取り込む選択肢はないが、「commit 未満の粒度をどう持つか」という設計議論の参照点として有用。
|
||||
|
||||
## 9. 参考リンク
|
||||
|
||||
- [Sequoia Backs Zed's Vision for Collaborative Coding — Zed Blog](https://zed.dev/blog/sequoia-backs-zed) — 一次情報源
|
||||
- [How CRDTs make multiplayer text editing part of Zed's DNA — Zed Blog](https://zed.dev/blog/crdts) — DeltaDB の基盤となる Zed の CRDT 実装解説
|
||||
- [Partnering with Zed — Sequoia Capital](https://sequoiacap.com/article/partnering-with-zed-the-ai-powered-code-editor-built-from-scratch/) — 投資側の論点
|
||||
- [Zed Raises $32M in Series B, Pivots to DeltaDB — Hypeburner](https://hypeburner.com/blog/news/zed-deltadb)
|
||||
- [Zed Raises $32M ... Unveils DeltaDB — Menlo Times](https://www.menlotimes.com/post/zed-raises-32-million-in-series-b-to-build-next-gen-operation-based-version-control-unveils-deltad)
|
||||
- [Zed Industries Raises $32M ... DeltaDB — CXO Digital Pulse](https://www.cxodigitalpulse.com/zed-industries-raises-32-million-to-redefine-ai-powered-code-collaboration-with-deltadb/)
|
||||
- [DeltaDB From Zed — shapeof.com (August Mueller)](https://shapeof.com/archives/2025/8/deltadb_from_zed.html) — 第三者の所感
|
||||
- [Conflict-free replicated data type — Wikipedia](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
# システムプロンプトテンプレート
|
||||
|
||||
Pod のシステムプロンプトは minijinja テンプレート。first turn 直前に1回だけ render され、以降のターンおよび compact 後も同じ文字列を使い続ける。
|
||||
|
||||
## マニフェスト記法
|
||||
|
||||
既存の `[worker].system_prompt` をそのままテンプレート文字列として解釈する。新フィールドは無い。
|
||||
|
||||
```toml
|
||||
[worker]
|
||||
system_prompt = """
|
||||
You are operating in {{ cwd }}.
|
||||
Today is {{ date }}.
|
||||
|
||||
{{ scope.summary }}
|
||||
|
||||
Available tools: {{ tools | join(", ") }}
|
||||
{% if files.agents_md is defined -%}
|
||||
|
||||
{{ files.agents_md }}
|
||||
{%- endif %}
|
||||
"""
|
||||
```
|
||||
|
||||
構文は minijinja (Jinja2 互換) のサブセット。未定義変数の参照は `UndefinedBehavior::Strict` により render エラーになる。`{{` のリテラル出力は `{{ '{{' }}` で逃がす。
|
||||
|
||||
## 組み込み変数
|
||||
|
||||
| キー | 型 | 内容 |
|
||||
|---|---|---|
|
||||
| `date` | string | `YYYY-MM-DD` (UTC) |
|
||||
| `time` | string | `HH:MM:SS` (UTC) |
|
||||
| `datetime` | string | RFC3339 (UTC, 秒精度) |
|
||||
| `cwd` | string | Pod の絶対 pwd |
|
||||
| `scope.readable` | list\<string\> | allow ルールの全パス (Read 以上) |
|
||||
| `scope.writable` | list\<string\> | allow ルールのうち Write のパス |
|
||||
| `scope.summary` | string | "Readable:\n - ...\nWritable:\n - ..." 形式の整形済み文字列 |
|
||||
| `tools` | list\<string\> | 登録済みツール名の sort 済み一覧 |
|
||||
| `files` | map\<string, string\> | 外部ファイル。AGENTS.md 等の供給先として予約。空の場合もあるので `is defined` でガードする |
|
||||
|
||||
`files` は常に存在する (本体が空 Map のことはあっても `files` 自体が未定義にはならない) が、個別キー (`files.agents_md` 等) は供給元次第で未定義になり得る。
|
||||
|
||||
## 評価モデル
|
||||
|
||||
テンプレートの実体化 (render) は遅延評価。プロジェクト内の他の遅延初期化パターン (tool factory / hook builder) と同じ形に揃えている。
|
||||
|
||||
### タイミング
|
||||
|
||||
1. `Pod::from_manifest` 時点: テンプレート文字列を `SystemPromptTemplate::parse` で **構文検査のみ** 行い、`Pod.system_prompt_template: Option<SystemPromptTemplate>` に保持する。この時点で Worker 側の system_prompt は `None`、segment log の head もまだ作られない。
|
||||
|
||||
2. `Pod::run` / `Pod::resume` 初回呼び出し冒頭 (`ensure_system_prompt_materialized`):
|
||||
1. `worker.tool_server_handle().flush_pending()` で pending な tool factory を materialize して tool 名を確定させる
|
||||
2. 現在時刻・cwd・scope・tool 名を集めて `SystemPromptContext` を作る
|
||||
3. `template.render(ctx)` で文字列化して `worker.set_system_prompt(rendered)` を呼ぶ
|
||||
4. `Pod.system_prompt_template = None` (`Option::take()` で構造的に1回性を保証)
|
||||
|
||||
3. その直後の `ensure_segment_head` が現在 segment の entry count を見て初回なら `SegmentStart` を append し、materialize 後の system_prompt を segment log に焼き付ける。
|
||||
|
||||
### 1回性の保証
|
||||
|
||||
- `Pod.system_prompt_template` は `Option<SystemPromptTemplate>` で、materialize 時に `take()` する。2 ターン目以降はフィールドが `None` なので `ensure_system_prompt_materialized` は早期 return し、再 render は発生しない。
|
||||
- compact は Worker の system_prompt フィールドを触らない (pod.rs の `compact` は `w.get_system_prompt()` を読み取って新 segment に引き継ぐだけ)。そのため compact 前後で同じ文字列が流れ続ける。
|
||||
- 統合テスト `compact_preserves_system_prompt` が実装で直接検証している。
|
||||
|
||||
### 責務分離
|
||||
|
||||
テンプレート機構は **Pod 層** に閉じる。`llm-worker` はテンプレートの存在を知らず、`Worker::set_system_prompt(String)` で render 済みの文字列を受け取るだけ。llm-worker 側に入った唯一の変更は `ToolServerHandle::flush_pending` を `pub` に昇格させたこと (tool 名を先取りするため)。
|
||||
|
||||
## エラー処理
|
||||
|
||||
- **構文エラー**: `Pod::from_manifest` の parse 段階で検出 → `PodError::InvalidSystemPromptTemplate { source: SystemPromptError::Parse }` で Pod の生成自体が失敗する。
|
||||
- **render エラー** (未定義変数など): first turn 直前の `ensure_system_prompt_materialized` で検出 → `PodError::SystemPromptRender { source: SystemPromptError::Render }` で初回 `Pod::run` が失敗する。
|
||||
- フォールバックはしない。どちらも fail-fast で Pod 起動を止める。
|
||||
|
||||
## 供給元の拡張
|
||||
|
||||
`SystemPromptContext.files: BTreeMap<String, String>` は本チケットの範囲では常に空だが、key 空間として予約してある。AGENTS.md 取り込み (別チケット) では `ensure_system_prompt_materialized` 内で `files` を埋めるだけで拡張できる。テンプレート側・エンジン側の変更は不要。
|
||||
|
||||
## 関連ファイル
|
||||
|
||||
- `crates/pod/src/system_prompt.rs` — `SystemPromptTemplate` / `SystemPromptContext` / `SystemPromptError`
|
||||
- `crates/pod/src/pod.rs` — `Pod.system_prompt_template` フィールド、`ensure_system_prompt_materialized`、`ensure_segment_head` の初回 append ロジック
|
||||
- `crates/manifest/src/scope.rs` — `Scope::summary` / `readable_paths` / `writable_paths`
|
||||
- `crates/session-store/src/segment_log.rs` — `LogEntry::SegmentStart` / segment replay entries
|
||||
- `crates/llm-worker/src/tool_server.rs` — `ToolServerHandle::flush_pending` (pub)
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
# TUI キーバインド
|
||||
|
||||
`crates/tui` の対話画面で効くキー一覧。`crates/tui/src/main.rs:handle_key` を単一情報源とし、このドキュメントは人間向けの目次。
|
||||
|
||||
## 入力編集
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| 文字キー | カーソル位置に挿入(未割り当ての `Ctrl`+文字キーは無視) |
|
||||
| `Ctrl-A` | 入力欄全体の先頭へ |
|
||||
| `Backspace` | カーソル直前を削除(ペーストプレースホルダは 1 回で全削除) |
|
||||
| `Delete` | カーソル直後を削除(同上) |
|
||||
| `Left` / `Right` | カーソル移動 |
|
||||
| `Up` / `Down` | 通常は論理行単位の上下移動。入力欄の先頭/末尾では送信済み composer history を browse |
|
||||
| `Home` / `End` | 現在行の行頭 / 行末へ |
|
||||
| `Alt-Enter` | 改行を挿入(Input を複数行化) |
|
||||
| `Esc` | completion popup があれば閉じる |
|
||||
|
||||
### ペーストプレースホルダ
|
||||
|
||||
ブラケットペーストで入力されたテキストは入力バッファ上では不可分な
|
||||
プレースホルダ `[Clipboard #N | X chars, Y lines]` として表示される。
|
||||
実テキストは裏で保持され、送信時に `#N` ラベル無しで展開されて Pod に渡る。
|
||||
カーソルはプレースホルダ内部には入れず、`Backspace` / `Delete` 1 回で
|
||||
プレースホルダ全体が消える。`#N` の通し番号は TUI プロセス起動中で連番。
|
||||
|
||||
### Completion
|
||||
|
||||
`@` file ref / `#` knowledge ref / `/` workflow invocation などは completion popup を開く。
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| `Up` / `Down` | completion 候補を移動 |
|
||||
| `Tab` | 選択候補をテキストとして適用 |
|
||||
| `Enter` | committable な候補は chip 化して空白を追加。directory などはテキスト適用して drill-in |
|
||||
| `Esc` | popup を閉じる |
|
||||
| 空白文字 | exact match なら chip 化してから空白を挿入 |
|
||||
|
||||
## 送信(Enter)
|
||||
|
||||
| 状態 | 入力あり | 入力空 |
|
||||
|---|---|---|
|
||||
| Idle | `Method::Run` で新ターン開始 | no-op |
|
||||
| Paused | `Method::Run`(前ターンは割り込み終了として扱い、新ターンとして開始) | `Method::Resume`(前ターンの続きを再開) |
|
||||
| Running | 入力は TUI 側 queue に積まれ、現在 turn の終了後に自動送信 | no-op |
|
||||
|
||||
### Running 中の queue
|
||||
|
||||
Running 中に入力ありで Enter すると、現在の provider stream には割り込まず、typed input segments を TUI-local queue に積む。`RunResult::Finished` / `LimitReached` 後に先頭の queued input が次の `Method::Run` として自動送信される。`Paused` / `RolledBack` では自動送信しない。
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| `Alt-Q` | queue 先頭を composer に戻す(composer が空の時のみ) |
|
||||
| `Alt-C` | queued input を全消去 |
|
||||
|
||||
### Paused からの挙動
|
||||
|
||||
Paused 中に Enter すると、入力の有無で 2 通り:
|
||||
|
||||
- **空**: 前ターンを中断した地点から **Resume**。LLM はそのまま続きを書く(partial text は破棄済み)、未実行の tool があれば実行して続行
|
||||
- **入力あり**: 前ターンは「割り込み終了」扱いとなり、新ターンとして **Run**。Pod 側では
|
||||
1. 未応答 `tool_use` があれば synthetic `tool_result("[Interrupted by user]")` で閉じる
|
||||
2. `[The previous turn was interrupted by the user]` system note を履歴に挿入
|
||||
3. 入力を新しい user メッセージとして append
|
||||
4. ターン開始
|
||||
|
||||
## Command mode
|
||||
|
||||
composer が空の通常入力で `:` を押すと command mode に入る。command mode 中は composer が command line として扱われる。
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| `Enter` | command を実行 |
|
||||
| `Esc` | command mode を終了 |
|
||||
| `Backspace` | 文字を削除。空なら command mode を終了 |
|
||||
| `Ctrl-U` | command input をクリア |
|
||||
| `Tab` | command completion を適用 |
|
||||
| `Up` / `Down` | command completion 候補があれば移動、なければカーソル上下 |
|
||||
|
||||
主な command は `:help`, `:noop`, `:compact`, `:rewind` / `:rollback`。`:compact` は idle 時だけ即時 compaction を要求し、`:rewind` / `:rollback` は rewind target picker を開く。
|
||||
|
||||
## 履歴ビューのナビゲーション
|
||||
|
||||
履歴ビューは全画面 TUI の上段にあり、TUI 内部で全ブロックを state として
|
||||
保持してスクロール可能。スクロールバック(端末側の履歴)ではなく TUI の
|
||||
自前バッファを動かす点に注意。
|
||||
|
||||
| キー / 操作 | 動作 |
|
||||
|---|---|
|
||||
| `Shift-Up` / `Shift-Down` | 1 論理行スクロール |
|
||||
| mouse wheel | 数行単位でスクロール |
|
||||
| `PageUp` / `PageDown` | 1 ページスクロール(task pane が開いている時は task pane をスクロール) |
|
||||
| `Ctrl-[` / `Ctrl-]` | 前 / 次のターン先頭へジャンプ |
|
||||
| `Ctrl-Home` | 履歴の先頭へ |
|
||||
| `Ctrl-End` | 履歴の末尾へ(末尾追従モードを再開) |
|
||||
|
||||
### 末尾追従
|
||||
|
||||
デフォルトは「末尾追従」モード:新しいイベント到着で自動的に最新行が
|
||||
画面下に固定される。ユーザーが上方向にスクロールした瞬間に追従は解除され、
|
||||
その位置で固定される(新着イベントが来ても画面は動かない)。再び追従に
|
||||
戻すには `Ctrl-End`。追従解除中はステータスバー右に `↑ scrolled` が点く。
|
||||
|
||||
### ターン単位ジャンプ
|
||||
|
||||
`Ctrl-[` / `Ctrl-]` は TurnHeader ブロックの位置にスクロールオフセットを
|
||||
合わせる。多数のツール呼び出しが挟まった長いターンでも前後ターンの先頭に
|
||||
1 発で戻れる。末尾ターンから `Ctrl-]` を押すと末尾追従に復帰する。
|
||||
|
||||
## 表示モード / 補助 pane
|
||||
|
||||
履歴各ブロックの密度を 3 段階で切り替える。現在のモードはステータスバー
|
||||
右端に `[mode]` として表示される。
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| `Ctrl-O` | `detail` → `normal` → `overview` → `detail` の順に循環 |
|
||||
| `Ctrl-T` | task pane を開閉 |
|
||||
|
||||
- **detail**: 全ブロック完全表示。ツールブロックは結果本体も全量
|
||||
- **normal**: 完了ブロックは概ね 5–6 行に圧縮、実行中のツールブロックは detail と同じ扱い
|
||||
- **overview**: 各ブロック 1 行に畳む(ツールブロックは `ToolResult.summary` 1 行、長文 AssistantText は先頭 1 行 + 省略記)
|
||||
|
||||
モード切替は全体に一括適用。個別ブロックの開閉は持たない。
|
||||
|
||||
## Pod 制御
|
||||
|
||||
| キー | Running 中 | Idle / Paused |
|
||||
|---|---|---|
|
||||
| `Ctrl-X` | queued input を消去して `Method::Cancel`(進行中ターンを破棄 → Idle) | `Method::Shutdown`(Pod を終了) |
|
||||
| `Ctrl-C` | `Method::Pause`(進行中ターンを中断 → Paused) | 1 回目 warn、3 秒以内の 2 回目で TUI 終了(Pod は残る) |
|
||||
| `Ctrl-D` | TUI 終了(Pod は残る、Pause しない) | TUI 終了(Pod は残る) |
|
||||
| `Ctrl-R` | rewind picker 要求は拒否される | rewind target picker を開く |
|
||||
|
||||
### Cancel と Pause の違い
|
||||
|
||||
- **Cancel** は「ターンを捨てる」: 進行中の LLM リクエスト・未完了 tool を打ち切り、状態は Idle。続きは Resume できない
|
||||
- **Pause** は「止めるけど続けられるように」: 同じく打ち切るが状態は Paused、空 Enter で Resume 可能
|
||||
|
||||
Running 中に割り込みたい場合、ほとんどのケースで `Ctrl-C`(Pause)が自然。Ctrl-X(Cancel)は明示的に破棄したい時(LLM が暴走した時など)用。Pod を終了したい場合は、先に Running ではない状態(Idle / Paused)にしてから `Ctrl-X` で Shutdown する。
|
||||
|
||||
### Rewind picker
|
||||
|
||||
`Ctrl-R` または `:rewind` / `:rollback` で Pod に `Method::ListRewindTargets` を送り、main area に rewind target picker を開く。picker 中の操作は以下。
|
||||
|
||||
| キー | 動作 |
|
||||
|---|---|
|
||||
| `Up` / `Down` | target を移動 |
|
||||
| `Enter` | 選択 target へ `Method::RewindTo`。復元された user input は composer に戻る |
|
||||
| `Esc` | picker を閉じる |
|
||||
|
||||
Running 中の rewind request は拒否される。Paused 中は picker を開けるが、実際の apply は idle で composer が空の時だけ行う。
|
||||
|
||||
### Ctrl-C と Ctrl-D の終了 UX
|
||||
|
||||
- Ctrl-X Running 中: queued input を消去して `Method::Cancel`。終了したい場合は、明示的にターンを止めて Idle に戻してからもう一度 `Ctrl-X`
|
||||
- Ctrl-X Idle / Paused: `Method::Shutdown` を送って Pod を終了
|
||||
- Ctrl-C Running 中: 1 回目で即 Pause(破壊的ではない)
|
||||
- Ctrl-C Idle / Paused: 1 回目で warn メッセージ、3 秒以内の 2 回目で TUI 終了(Pod は残る)
|
||||
- Ctrl-D: 状態に関わらず即 TUI 終了(Pod は残る)。Running 中でも Pause / Cancel / Shutdown は送らない
|
||||
|
||||
`Ctrl-X` は Running 中だけ Cancel、Idle / Paused では Shutdown。`Ctrl-C` は Running 中だけ Pod に `Method::Pause` を送り、それ以外では Pod は落とさず TUI プロセスだけ抜ける。`Ctrl-D` は常に Pod へ制御メソッドを送らず TUI プロセスだけ抜ける。
|
||||
|
||||
TUI のダイアログから Pod を起動する経路では、起動した Pod は TUI の子プロセスとして管理・終了されず、独立したプロセスとして残る。TUI 終了後は `yoi <pod-name>` で再接続できる。
|
||||
|
||||
## 履歴メモ
|
||||
|
||||
- `Ctrl-R` はかつて Resume 専用だったが、空 Enter での Resume に統合された。現在の `Ctrl-R` は rewind picker の導線
|
||||
- かつて存在した `Esc`(TUI 終了)は、`Ctrl-C` の 2 連打 UX に統合されたため廃止。現在の `Esc` は popup / picker / command mode のキャンセル用途
|
||||
- かつて `Ctrl-D` は Pod に `Method::Shutdown` を送っていたが、TUI だけを抜けるデタッチ操作に変更された
|
||||
- 旧 inline viewport 時代は履歴スクロールを端末側スクロールバックに任せていたため TUI 内のスクロールキーは存在しなかった。全画面 alt screen への移行で `Shift-Up/Down` ほかのナビゲーションキーが追加された
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
```
|
||||
gap |
|
||||
task(if some)| 8 tasks - pending: 2, inprogress:1, completed:5
|
||||
|-----------------------------------------------------------
|
||||
status |● yoi idle 42.1k / 200k (21%)
|
||||
input |>
|
||||
actionbar | ↑ scrolled [normal]
|
||||
```
|
||||
|
||||
status 右端は常に session context usage を `<tokens> / <window> (<pct>%)` 形式で表示する。mode / scrolled などの操作状態は actionbar に寄せる。
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1775036866,
|
||||
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||
"lastModified": 1779560665,
|
||||
"narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||
"rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# Implementation report
|
||||
|
||||
## Summary
|
||||
|
||||
Standardized crate README files as thin responsibility-boundary documents.
|
||||
|
||||
## Changes
|
||||
|
||||
- Rewrote existing stale README files to remove public-type inventories and outdated architecture claims.
|
||||
- Added concise README files for important crates that lacked them:
|
||||
- `client`
|
||||
- `lint-common`
|
||||
- `memory`
|
||||
- `pod-registry`
|
||||
- `pod-store`
|
||||
- `secrets`
|
||||
- `session-metrics`
|
||||
- `tools`
|
||||
- `workflow`
|
||||
- `yoi`
|
||||
- Each crate README now follows the same shape:
|
||||
- Role
|
||||
- Boundaries
|
||||
- Design notes
|
||||
- See also
|
||||
- README files link to maintained `docs/design/` or `docs/development/` docs instead of duplicating long design explanations.
|
||||
|
||||
## Validation
|
||||
|
||||
- All crates under `crates/*` have a README.
|
||||
- `rg` sweep for stale public-type headings, old crate title, accidental edit text, and old product names in crate README files => no output.
|
||||
- `./tickets.sh doctor` => `doctor: ok`
|
||||
- `git diff --check` => passed
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
id: 20260601-110026-crate-readme-boundaries
|
||||
slug: crate-readme-boundaries
|
||||
title: Standardize crate README responsibility boundaries
|
||||
status: open
|
||||
kind: task
|
||||
priority: P2
|
||||
labels: [docs]
|
||||
created_at: 2026-06-01T11:00:26Z
|
||||
updated_at: 2026-06-01T11:09:35Z
|
||||
assignee: null
|
||||
legacy_ticket: null
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
The crate README files are uneven and several important crates have no README at all. They should be standardized as thin responsibility-boundary documents rather than API references.
|
||||
|
||||
This work item follows `docs-information-architecture` and should link crate README files to the maintained design/development docs where appropriate.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Each crate README should explain the crate's role, boundaries, key entry points, and relevant design docs.
|
||||
- README files should avoid public type inventories, method lists, and implementation details that are likely to drift.
|
||||
- Important crates without README files should get concise responsibility-boundary README files.
|
||||
- Existing stale names or stale architectural claims should be removed.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- Existing crate README files follow a consistent role/boundary-oriented shape.
|
||||
- Important crates without README files have a short README.
|
||||
- Crate README files link to `docs/design/` or `docs/development/` instead of duplicating long design content.
|
||||
- Validation includes `./tickets.sh doctor` and `git diff --check`.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<!-- event: create author: tickets.sh at: 2026-06-01T11:00:26Z -->
|
||||
|
||||
## Created
|
||||
|
||||
Created by tickets.sh create.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: hare at: 2026-06-01T11:09:35Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
# Implementation report
|
||||
|
||||
## Summary
|
||||
|
||||
Standardized crate README files as thin responsibility-boundary documents.
|
||||
|
||||
## Changes
|
||||
|
||||
- Rewrote existing stale README files to remove public-type inventories and outdated architecture claims.
|
||||
- Added concise README files for important crates that lacked them:
|
||||
- `client`
|
||||
- `lint-common`
|
||||
- `memory`
|
||||
- `pod-registry`
|
||||
- `pod-store`
|
||||
- `secrets`
|
||||
- `session-metrics`
|
||||
- `tools`
|
||||
- `workflow`
|
||||
- `yoi`
|
||||
- Each crate README now follows the same shape:
|
||||
- Role
|
||||
- Boundaries
|
||||
- Design notes
|
||||
- See also
|
||||
- README files link to maintained `docs/design/` or `docs/development/` docs instead of duplicating long design explanations.
|
||||
|
||||
## Validation
|
||||
|
||||
- All crates under `crates/*` have a README.
|
||||
- `rg` sweep for stale public-type headings, old crate title, accidental edit text, and old product names in crate README files => no output.
|
||||
- `./tickets.sh doctor` => `doctor: ok`
|
||||
- `git diff --check` => passed
|
||||
|
||||
|
||||
---
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Implementation report
|
||||
|
||||
## Summary
|
||||
|
||||
Reorganized maintained repository documentation around current Yoi design intent and development workflow.
|
||||
|
||||
## Changes
|
||||
|
||||
- Replaced the root `README.md` with a concise developer entry point.
|
||||
- Added `docs/README.md` as the documentation map and contribution boundary.
|
||||
- Added maintained design docs under `docs/design/`:
|
||||
- `overview.md`
|
||||
- `pod-session-state.md`
|
||||
- `context-history.md`
|
||||
- `profiles-manifests-prompts.md`
|
||||
- `tool-permissions-scope.md`
|
||||
- `memory-knowledge.md`
|
||||
- `compaction.md`
|
||||
- `provider-model-boundary.md`
|
||||
- Added maintained development docs under `docs/development/`:
|
||||
- `work-items.md`
|
||||
- `workflows.md`
|
||||
- `validation.md`
|
||||
- `dogfooding.md`
|
||||
- `environment.md`
|
||||
- Moved old top-level docs, `docs/plan/`, `docs/ref/`, and `docs/research/` material into ignored `docs/.local/old-docs/` so the repository documentation surface no longer presents it as current authority.
|
||||
|
||||
## Validation
|
||||
|
||||
- `./tickets.sh doctor` => `doctor: ok`
|
||||
- `git diff --check` => passed
|
||||
- stale-surface grep over `README.md`, maintained `docs/`, and crate READMEs excluding `docs/report/` and `docs/.local/` => no output
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
id: 20260601-110026-docs-information-architecture
|
||||
slug: docs-information-architecture
|
||||
title: Reorganize documentation information architecture
|
||||
status: open
|
||||
kind: task
|
||||
priority: P2
|
||||
labels: [docs]
|
||||
created_at: 2026-06-01T11:00:26Z
|
||||
updated_at: 2026-06-01T11:09:35Z
|
||||
assignee: null
|
||||
legacy_ticket: null
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
The repository documentation still mixes current design intent, historical plans, external research, and implementation notes. The next documentation pass should make the maintained docs answer why Yoi is shaped this way, while moving or deleting research/log material that is not useful as stable developer documentation.
|
||||
|
||||
This work item covers the top-level documentation information architecture only. Crate-level README cleanup is tracked separately by `crate-readme-boundaries`.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Keep repository documentation focused on current Yoi design intent and development workflow.
|
||||
- Prefer durable Why over implementation details that code, tickets, or git history already own.
|
||||
- Do not preserve stale plan/research material merely because it exists.
|
||||
- Treat `docs/.local/` as the home for private or non-authoritative notes that should not be part of the public documentation surface.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- Root `README.md` points developers to the maintained documentation map.
|
||||
- Maintained design docs live under `docs/design/` and cover the major Yoi design boundaries.
|
||||
- Maintained development workflow docs live under `docs/development/`.
|
||||
- `docs/ref/` and `docs/research/` are no longer part of the repository documentation surface.
|
||||
- `docs/plan/` is removed or reduced so old plans are not mistaken for current design authority.
|
||||
- Validation includes `./tickets.sh doctor` and `git diff --check`.
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!-- event: create author: tickets.sh at: 2026-06-01T11:00:26Z -->
|
||||
|
||||
## Created
|
||||
|
||||
Created by tickets.sh create.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: hare at: 2026-06-01T11:09:35Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
# Implementation report
|
||||
|
||||
## Summary
|
||||
|
||||
Reorganized maintained repository documentation around current Yoi design intent and development workflow.
|
||||
|
||||
## Changes
|
||||
|
||||
- Replaced the root `README.md` with a concise developer entry point.
|
||||
- Added `docs/README.md` as the documentation map and contribution boundary.
|
||||
- Added maintained design docs under `docs/design/`:
|
||||
- `overview.md`
|
||||
- `pod-session-state.md`
|
||||
- `context-history.md`
|
||||
- `profiles-manifests-prompts.md`
|
||||
- `tool-permissions-scope.md`
|
||||
- `memory-knowledge.md`
|
||||
- `compaction.md`
|
||||
- `provider-model-boundary.md`
|
||||
- Added maintained development docs under `docs/development/`:
|
||||
- `work-items.md`
|
||||
- `workflows.md`
|
||||
- `validation.md`
|
||||
- `dogfooding.md`
|
||||
- `environment.md`
|
||||
- Moved old top-level docs, `docs/plan/`, `docs/ref/`, and `docs/research/` material into ignored `docs/.local/old-docs/` so the repository documentation surface no longer presents it as current authority.
|
||||
|
||||
## Validation
|
||||
|
||||
- `./tickets.sh doctor` => `doctor: ok`
|
||||
- `git diff --check` => passed
|
||||
- stale-surface grep over `README.md`, maintained `docs/`, and crate READMEs excluding `docs/report/` and `docs/.local/` => no output
|
||||
|
||||
|
||||
---
|
||||
Loading…
Reference in New Issue
Block a user