PicoClaw ディープダイブ:Goで超軽量AIエージェントを作るためのフィールドガイド

Dev.to / 2026/4/28

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical UsageModels & Research

要点

  • PicoClawは、$10クラスのハードウェア上で動作し、メモリ使用量を10MB未満に抑えることを目標にした、Goベースのシングルバイナリ個人用AIエージェントで、高速起動と幅広いハード互換性を実現しています。
  • ガイドでは、エージェントループ/パイプライン、ループ途中でのステアリング(注入メッセージ)、階層型サブエージェント(SubTurn)、JSONLによる永続化とセッション管理など、PicoClawのアーキテクチャと中核コンセプトを解説します。
  • ルールベースのモデルルーティング、拡張用のフックシステム、チャットチャネル(18+)とLLMプロバイダ(30+)を統一インターフェースで扱うための抽象化といった、実装上の実用パターンも紹介されます。
  • ツール/スキルとMCP連携を含めたエージェント構築、特に「<10MBの秘密」と呼ばれるリソース効率化テクニック、さらにクロスコンパイルやシングルバイナリ配布といったデプロイ戦略も取り上げられます。
  • 全体として、GoでPicoClaw風の超軽量エージェントをゼロから再現するための実践的なフィールドガイドとして、落とし穴やプロジェクトソースを読むための推奨ルートも提示しています。

sipeed/picoclaw の背後にある原則、手法、アーキテクチャを、最初から同様のシステムを構築できるように書かれた、包括的で実行可能なガイドです。

目次

  1. PicoClaw とは何か、そしてなぜ重要か
  2. デザイン哲学
  3. ️ 高レベルアーキテクチャ
  4. 中核コンセプト #1 — エージェントループ&amp; パイプライン
  5. ️ 中核コンセプト #2 — ステアリング(ループ中のメッセージ注入)
  6. 中核コンセプト #3 — SubTurn(階層型サブエージェント)
  7. 中核コンセプト #4 — セッション&amp; JSONL 永続化
  8. 中核コンセプト #5 — ルールベースのモデルルーティング
  9. 中核コンセプト #6 — フックシステム
  10. 中核コンセプト #7 — チャネル抽象化(18+ のチャットプラットフォーム)
  11. 中核コンセプト #8 — プロバイダー抽象化(30+ の LLM)
  12. ️ 中核コンセプト #9 — ツール、スキル、MCP
  13. ⚡ リソース効率化テクニック(<10MB の秘密)
  14. クロスコンパイル&amp; シングルバイナリ配置
  15. ⚙️ 参照設定スキーマ
  16. ️ ステップバイステップ:自分だけの PicoClaw スタイルのエージェントを作る
  17. ⚠️ よくある落とし穴&amp; 学び
  18. PicoClaw ソースを辿るための推奨読書ルート

1. PicoClaw とは何か、そしてなぜ重要か

PicoClaw は 1つのバイナリで動く、Go ベースのパーソナル AI エージェントであり、$10 クラスのハードウェア(RISC-V SBC、Raspberry Pi Zero、MIPS ルータ、Termux 経由の Android、古い NanoKVM ボードなど)上で 10 MB 未満の RAM で動作します。NanoBot に強く触発されていますが、Go で「自己ブートストラップ」するように書き直されており、人間がレビューしたエージェントによって生成されたコードがおよそ 95% です。

注目すべき点は、LLM と会話できることではありません(それ自体は簡単です)。その代わり、PicoClaw は次のような条件を満たしながら実現しています:

特性 PicoClaw 一般的な Python AI スタック
メモリ使用量 < 10 MB 200 MB – 2 GB
起動時間 0.6 GHz CPU で < 1 秒 5–30 秒
配布形態 1つの静的バイナリ venv+多数のホイール
アーキテクチャ x86_64、ARM、ARM64、RISC-V、MIPS、LoongArch 主に x86_64/ARM64
チャネル 18+(Telegram、Discord、WeChat、Slack…) 通常 1〜2
LLM プロバイダー 統一インターフェース経由で 30+ 1〜3 の SDK に固定

これは「チャットボット」ではありません。ツール、MCP、サブエージェント、多チャネルのメッセージング、プロバイダーのルーティングを、第一級のサポートとして備えた ポータブルなエージェント実行環境です。

2. デザイン哲学

以下は、あらゆる設計判断を駆動する原則です。まずこれらを頭に入れてください。そうすればコードが理解しやすくなります。

2.1 デフォルトは軽量、インターフェースで拡張可能

Go を選ぶのは、静的にリンクされた小さなバイナリを生成でき、ランタイムのオーバーヘッドが小さく、GIL がなく、メモリ使用が予測可能だからです。LLM、チャネル、ツール、フック、レジストリといった可変のサブシステムをすべてインターフェースの背後に包み込み、コアループに触れることなく機能を追加できるようにします。

2.2 1つのバイナリで、あらゆるアーキテクチャへ

ユーザーが $10 の RISC-V ボードへデプロイするとき、Docker、Python のバージョン、共有ライブラリのことを考える必要があってはなりません。make build-all は、1つのツリーから Linux/amd64、ARM、ARM64、RISC-V、MIPS LE、LoongArch、Darwin ARM64、Windows、NetBSD 向けのバイナリを生成します。

2.3 追記優先の永続化(JSONL)

セッションとメモリは JSON Lines ファイルとして、さらに .meta.json というサイドカーで保存されます。追記のみ(append-only)は、クラッシュに強く、デバッグに向いています(tail -f)、そして持ち運びも簡単です。スキーマの移行は読み取り時に遅延して行われます。

2.4 ️ ルーティングデータを第一級のフィールドとして昇格させる

チャネルが chatIdsenderIdmessageId を、汎用のメタデータマップの中に埋め込むことはありません。これらは InboundMessage 上の型付きフィールドです。ルーティング、セッション、フックはいずれもこの契約に依存しています。

2.5 能力はハードコードでなく発見される

各チャネルはオプションとして MediaSenderTypingCapableReactionCapableMessageEditorWebhookHandlerHealthChecker を実装できます。マネージャは型アサーション経由でそれを調べます。新しいプラットフォームを追加してもマネージャを触る必要はありません。

2.6 まず安く、必要になったら段階的にエスカレート

ルールベースの分類器が、各ターンに 0..1 のスコアを付けます(トークン数、コードブロック、直近のツール呼び出し、添付、深さ)。しきい値未満なら「軽量(cheap)」なモデルにリクエストを送ります。しきい値を超えたら重いモデルです。これだけで、チャットの多いワークロードにおける API コストを大幅に削減できます。

2.7 ️ すべてを観測し、邪魔しない(介入)は稀に

同期的なフックポイントは 5 つ (before_llmafter_llmbefore_toolafter_toolapprove_tool) あれば十分です。その他は EventBus を通じた、読み取り専用のイベント観測です。フックは Go のプロセス内コードとしてもよいし、標準入力/標準出力経由の JSON-RPC を通じて外部プロセスとしても実装できます。

2.8 ️ 実行の途中でユーザーが考えを変えられる

ユーザーは修正を指示します。エージェントループは、ツール呼び出しのたびに、そのセッション専用の ステアリングキュー をポーリングします。次の LLM ターンの前に新しいメッセージが注入され、残っているキュー済みのツールは「queued user message によりスキップされた」という結果とともにスキップされます。そうすることで、モデルは何が実行されなかったのかを理解できます。

3. ️ 高レベルアーキテクチャ

                       ┌────────────────────────────────────────────┐
 18+ チャットチャネル ─►  │  pkg/channels  (プラットフォーム別のサブパッケージ) │
   (Telegram、          │  ─ BaseChannel、能力インターフェース       │
    Discord、…)        │  ─ Manager:レート制限、分割、リトライ        │
                       └──────────────────────┬─────────────────────┘
                                              │  InboundMessage
                                              ▼
                       ┌────────────────────────────────────────────┐
                       │  pkg/bus  (型付きイベントバス、入出力 ctx)  │
                       └──────────────────────┬─────────────────────┘
                                              ▼
                       ┌────────────────────────────────────────────┐
                       │  pkg/routing                                │
                       │  ─ Dispatch:このエージェントが担当?     │
                       │  ─ Classifier:複雑さスコア 0..1         │
                       │  ─ Light/Heavy モデルの判断             │
                       └──────────────────────┬─────────────────────┘
                                              ▼
                       ┌────────────────────────────────────────────┐
                       │  pkg/session                                │
                       │  ─ SessionScope(agent/channel/account/dim) │
                       │  ─ JSONL バックエンド+ .meta サイドカー    │
                       │  ─ Canonical key sk_v1_<sha256> + 別名      │
                       └──────────────────────┬─────────────────────┘
                                              ▼
                       ┌────────────────────────────────────────────┐
                       │  pkg/agent  (ループ)                      │
                       │                                             │
                       │  pipeline_setup → pipeline_llm →            │
                       │  pipeline_execute(tools) → pipeline_finalize│
                       │                                             │

返却形式: {"translated": "翻訳されたHTML"}

cmd/ から、3つのトップレベルのバイナリが生成されます:

  • picoclaw — エージェント本体(CLI+ヘッドレスサーバ)
  • picoclaw-launcher-tui — ターミナルUIのランチャ
  • membench — <10MB の約束を正直に守るために使う内部メモリベンチマーク

4. コアコンセプト #1 — エージェントループ&パイプライン

pkg/agent パッケージは、すべてが集約される場所です。ループは4つのパイプラインステージに分割され、それぞれが別ファイルになっています:

ファイル ステージ ジョブ
pipeline_setup.go Setup プロンプトを構築し、セッション履歴を読み込み、モデルを解決し、フックをマウントする
pipeline_llm.go LLM Call プロバイダを呼び出し、トークンをストリームし、ツール呼び出しとthinkingブロックを解析する
pipeline_execute.go Tool Execution ツール呼び出しを実行(場合により並列に)、承認を強制し、結果を記録する
pipeline_finalize.go Finalize セッションを永続化し、イベントを発行し、送信先メッセージを送って、ターンをクローズする

パイプラインの周りには、横断的なモジュールがあります:

  • turn_coord.go — ターンごとの状態マシンを管理し、軽量/重量モデルの判断を行い、プロバイダ候補を選ぶ。
  • turn_state.go / turn_context.go — 型付きのターンスコープ状態。
  • context_manager.go / context_budget.go / context_usage.go — メッセージウィンドウをモデルのトークン上限の範囲に収める。最も古いものをトリムする、要約する、あるいは予算に基づいて捨てる。
  • prompt.go / prompt_contributors.go / prompt_turn.go — 合成可能なプロンプトビルダ。各コントリビュータはスライス(システムのアイデンティティ、ツール一覧、メモリ、時間、チャネルコンテキスト)を追加する。
  • eventbus.go / events.go — 意味のあるすべてのイベント(tool_exec_startllm_requestturn_finished、…)をオブザーバへファンアウトする。
  • registry.go — エージェントレジストリ。definition.go は、1つのエージェント(名前、システムプロンプト、ツールセット、モデル、軽量候補)を説明する。

コピーするのに役立つ実行可能パターン

  1. ループを厳密な状態マシンにする。コールバックのWebにしない。 各パイプラインファイルは、ターン状態を受け取り返す1つの関数をエクスポートします。テストが容易で、トレーシングを追加しやすく、フックを注入しやすい。
  2. エージェントの定義をプレーンなデータにする。 Definition 構造体(pkg/agent/definition.go)は、名前+システムプロンプト+ツール許可リスト+プロバイダ候補+軽量候補で構成されます。YAML/JSONからの読み込みは簡単です。
  3. 「LLMに何を送るか」と「それをどう送るか」を分離する。 プロンプトコントリビュータが抽象的なメッセージリストを構築します。次のセクションのプロバイダファサードが、それをベンダ固有のJSONにマッピングします。
  4. 使用量をターン単位で追跡する。 context_usage.go は、ターンごとのトークン入/トークン出を保持します。ログを解析せずに、ターンごとの予算上限を強制したり、メータリングのイベントを発行したりできます。

5. コアコンセプト #2 — スティーリング(ミドルループへのメッセージ注入)

「ユーザはいつでもエージェントを修正できる。そのことを最優先の関心事にしなさい。」

pkg/agent/steering.go(および agent_steering.go)は、ループが4つのチェックポイントでポーリングするセッションごとのFIFOキューを実装しています:

  1. ループの初期化(最初のLLM呼び出しの前)
  2. 各ツールが完了した後
  3. 各非ツールのLLMレスポンスの後
  4. ターンのファイナライズの前

それらのどこかでキューにメッセージが存在する場合:

  • 現在のLLMレスポンス内にある、残っているすべてのツール呼び出しはスキップされます。各呼び出しは合成結果 "Skipped due to queued user message." を受け取るため、モデルは何が実行された/されなかったのかを理解できます。
  • キューのメッセージを会話に追加し、新しい user ターンとして扱います。
  • ループはLLMステージへ再エントリします。

なぜ重要か

  • 副作用の安全性。 ユーザが「そのメールは送らないで」と叫べば、直前のツールが別の何かであっても、実際にメールが止まります。
  • 計算コストの節約。 3つの3〜4秒のツール呼び出しをまとめて計画していた場合、約10秒分の作業を回避できます。
  • モデルの認識。 スキップはツール結果メッセージとして通知されるため、モデルは同じ計画を繰り返すのではなく、適応できます。

モード&制限

agentLoop.SetSteeringMode(agent.SteeringOneAtATime) // default: pop one per check
agentLoop.SetSteeringMode(agent.SteeringAll)        // drain whole queue at once

ハードキャップ:MaxQueueSize = 10 メッセージ/セッション。オーバーフローは手動の Steer() でエラーを返し、また、着信チャネルバスのドレインがそれを引き起こす場合には警告を出します。

コピーするための公開API

// 外部: 修正を注入
err := agentLoop.Steer(providers.Message{
    Role:    "user",
    Content: "実際にはXに注目して",
})

// 外部: アイドル状態のセッションを再開へ促す
resp, err := agentLoop.Continue(ctx, sessionKey, channel, chatID)

実装メモ

  • キューは正準セッションキーによってスコープされます。異なるチャットは決して互いに混ざりません。
  • メディア参照(media://...)はステアリングで失われません。プロバイダ呼び出しの前に通常のパイプラインで解決されます。
  • すでにアクティブなターンを持つセッションへの受信メッセージは、競合するターンの開始ではなく、自動的にステアリングとしてキューに積まれます。

6. 中核コンセプト #3 — SubTurn(階層型サブエージェント)

サブエージェントは、親ターンによって生成される隔離されたネストループです。pkg/agent/subturn.goで定義されています。

性質

プロパティ
最大ネスト深度 3
親ごとの最大同時数 5(セマフォでガード、30秒のタイムアウト)
デフォルトのタイムアウト 5分(親と子は独立したタイムアウトを持つ)
メッセージバッファ サブターンあたり50メッセージ(親の履歴を汚染しない)
結果の配送 pendingResultsチャネル経由で非同期(16メッセージのバッファ)
キャンセル ハードアボートは子孫へカスケード
Critical: true 親の完了後も生存し、バックグラウンドで継続

親が結果をポーリングするとき

ステアリングと同じチェックポイントです。すなわち、LLM呼び出しの直前、ツール呼び出しの直後、finalizeの直前です。これにより、ポーリング用のスレッドなしで結果処理を決定論的に保ちます。

なぜ文脈は、親のctxではなくcontext.Background()から派生させるのか

子側に独立したタイムアウトがある場合、親が早く終わったときに子が驚かないようにするためです。特定のサブターンについてカスケードキャンセルを行いたい場合は、親がcancel()を明示的に呼び出します。

コピーするパターン

// 親エージェントのループの内部
result, err := agent.SpawnSubTurn(ctx, agent.SubTurnSpec{
    AgentDef:   "researcher",
    Goal:       "主張Xの一次情報源を探す",
    Critical:   false,
    Timeout:    2 * time.Minute,
    MaxHistory: 50,
})

落とし穴

  • 孤児結果(オーファン結果)。 親が子より先に完了した場合、結果は破棄されます(テレメトリイベント付き)。子をCritical: trueにするか、明示的にawaitしてください。
  • バッファオーバーフロー。 子を5つ同時に走らせ、結果バッファが16スロットの場合、バースト的な完了がオーバーフローし得ます。サブは進捗更新ではなく、単一の最終結果を出力するよう設計してください。

7. 中核コンセプト #4 — セッション & JSONL 永続化

pkg/sessionは2つの疑問に答えます。どのメッセージが会話を共有するのか?、そしてその会話はどのように確実に(durably)保存されるのか?

7.1 SessionScope — 会話の構造化された識別子

type SessionScope struct {
    Version    string            // ScopeVersionV1
    AgentID    string            // ルーティングされたエージェント
    Channel    string            // 正規化されたチャネル名("telegram")
    Account    string            // bot/アカウント識別子
    Dimensions []string          // アクティブなパーティション次元、例: ["chat"]
    Values     map[string]string // 次元の具体的な値
}

デフォルトの次元セットは["chat"]です。つまり、「ディスパッチルールが上書きしない限り」チャットごとに1つの共有会話になります。ディスパッチルールは、topicsenderを次元セットへ昇格させることで、会話を分割または統合できます。

7.2 2つの主要な形式

形式 目的
正準(Canonical) sk_v1_<sha256> 安定して不透明。真実の出所
レガシー agent:main:direct:user123 後方互換。透過的に解決される

JSONLバックエンドは、読み書きの際にレガシーのエイリアスを正準キーへ解決します。そのため、履歴を失わずにスキームをリネームできます。

7.3 ディスク上のJSONL

セッションごとに:

  • <key>.jsonl — 行ごとに1つのproviders.Message、追記のみ(append-only)。
  • <key>.meta.json{ summary, created_at, updated_at, line_count, skip_offset, scope, aliases }

2つのファイルにしている理由は、メッセージは追記のみでクラッシュ耐性があり、メタデータはシャードごとのミューテックスのもとで上書きされる一方で、小さく保たれているため、JSONLから途中で途切れた書き込み(torn write)を復旧できるからです。

"追記を最優先する耐久性と、古くなったものの損失からの回復を前提に設計されています。"

返却形式: {"translated": "翻訳されたHTML"}

7.4 アロケータのルール

アロケータは、受信したメタデータをスコープ値に変換します:

  • space<space_type>:<space_id>
  • chat<chat_type>:<chat_id>
  • topictopic:<topic_id>
  • sender → アイデンティティ・リンクのマッピングを通じて正規化される(つまり、ユーザーのTelegram IDとSlack IDが同じ論理的な送信者に対応づけられる)

特殊ケース: Telegramのフォーラムトピックは、topicが明示的なディメンションではない場合に限り、トピックIDをチャット値に /<topic_id> として付加します — デフォルトでトピック同士の干渉(cross-talk)を防ぎます。

7.5 ⚡ 並行性

64シャードのミューテックス配列(ハッシュキー → シャード)により、無制限にミューテックスのマップを保持せずに、セッションごとの書き込みが直列化されます。これは小さいですが重要なパターンです。ロックストライピングは本質的にほぼコストがかからず、セッションストアの競合バグの99%を解決します。

7.6 移行

起動時にシステムはレガシーのJSONセッションをJSONLへ移行しようとします。移行に失敗した場合、エージェントがクラッシュループするのではなく、レガシーの SessionManager にフォールバックします。

実行可能なパターン

  • セッションキーをコンテンツアドレス指定にする(正規化されたスコープ署名に対して sha256 — ディメンション名の変更が履歴を壊さないようにします。
  • サイドカーのメタデータ は、JSONLにヘッダー行を埋め込むよりはるかに単純です。
  • ロックストライピング > 1つの大きなミューテックス > セッションごとの1つのミューテックス。 64シャードは良いデフォルトです。

8. コアコンセプト #5 — ルールベースのモデルルーティング

pkg/routing2段階のパイプラインです:

  1. エージェントのディスパッチRouter が、メッセージを処理するどのエージェント定義を使うかを選びます(チャンネル、送信者、コンテンツ、コマンド接頭辞などに対するルール)。
  2. モデルルーティング — いったんエージェントが選ばれると、RuleClassifier が、エージェントの主要(ヘビー)モデルを使うのか、グローバルに設定された安価なライトモデルを使うのかを判断します。

8.1 ⚙️ 設定

{
  "routing": {
    "enabled": true,
    "light_model": "gemini-2.0-flash",
    "threshold": 0.35
  }
}

8.2 ターンごとに抽出される特徴量

分類器は意図的に 言語非依存(キーワードリストなし)で、5つの構造的特徴量を使います:

特徴量 測定するもの
TokenEstimate 概算トークン数(CJKに配慮したルーン数え)
CodeBlockCount 最新メッセージ内のフェンスされた ` ブロック数
RecentToolCalls 直近6つの履歴エントリにおけるツール呼び出し
ConversationDepth 履歴の総長
HasAttachments メディア参照、または認識されたファイル拡張子

8.3 ⚖️ 重み付きスコアリング([0,1] にクランプ)

シグナル 重み
添付がある 1.00
コードブロックが存在する 0.40
トークン > 200 0.35
最近のツール呼び出し > 3 0.25
トークン > 50 0.15
最近のツール呼び出し 1–3 0.10
会話の深さ > 10 0.10

しきい値 0.35 では、些細なチャットは安価なままです。コード、添付、またはアクティブなツール利用があるとヘビーが起動します。長いプレーンなプロンプトは、200トークン境界を越えると閾値をまたぎます。

8.4 どこに組み込まれるか

pkg/agent/turn_coord.go は、スコアが threshold 未満のときに候補プロバイダのリストを agent.LightCandidates に差し替えます。そうでない場合は、エージェントの主要な候補セットを変更せずに使います。エージェントはそれを知りません。順序付けされたプロバイダの別のリストを受け取るだけです。

そのままコピーするパターン

  • ルーティングルールはコードではなくデータです。 JSONの中に保管してください。その後のホットリロードは os.Stat + json.Unmarshal で実現できます。
  • 各エージェントには CandidatesLightCandidates の両方があります — 主要チェーンと、安価なフォールバックチェーン。ルーティングはチェーンを選ぶだけで、チェーン内の フォールバック ロジックは汎用です(次のセクション)。

9. コアコンセプト #6 — フックスシステム

5つの同期フックポイント + 任意の読み取り専用オブザーバ。pkg/agent/hooks.gohook_mount.gohook_process.go で定義されています。

9.1 5つの同期ポイント

ステージ 許可されるアクション
before_llm continue · modify(リクエストを書き換え) · abort_turn · hard_abort
after_llm continue · modify(レスポンスを書き換え)
before_tool continue · modify(引数を書き換え) · respond(execをスキップし、結果を返す) · deny_tool
after_tool continue · modify(ツール結果を書き換え)
approve_tool 許可 / 拒否のみ

それ以外はすべて、バス上でのオブザーバのみのイベントです。

9.2 プロセス内 vs プロセス外

プロセス内(in-process): 起動時に登録されるGo関数です。シリアライズのコストはゼロです。レート制限インジェクタ、監査ログ出力、スキーマバリデータのようなビルトインに使用します。

プロセス外(out-of-process): stdio上でのJSON-RPC を話す任意のプログラムです。HookManager によって生成され、監督されます。PythonのMLによる再ランキング、シークレットのスクラブ、外部のポリシーエンジン、さらにテスト中のツールのモックにも使用できます。

9.3 JSON-RPCのフレーミング

`json
// ホストからフックへのリクエスト
{ "jsonrpc": "2.0", "id": 7, "method": "hook.before_tool", "params": { ... } }

// フック → ホスト
{ "jsonrpc": "2.0", "id": 7, "result": { "action": "respond", "result": "cached" } }

// 通知(片方向;オブザーバイベント)
{ "jsonrpc": "2.0", "method": "hook.event", "params": {"Kind": "tool_exec_start"} }
`

ライフサイクル: ホストは最初に hook.hello を呼び出し、プロトコルのバージョンと機能(capabilities)を交渉します。

9.4 ⚙️ 設定の形

`json
{
"hooks": {
"enabled": true,
"observer_timeout_ms": 200,
"interceptor_timeout_ms": 5000,
"approval_timeout_ms": 30000,
"builtins": {

"audit_log": { "enabled": true, "priority": 10, "config": {} }
},
"processes": {
"policy_check": {
"enabled": true,
"priority": 100,
"transport": "stdio",
"command": ["python3", "/srv/policy.py"],
"env": { "POLICY_FILE": "/etc/policy.yml" },
"observe": ["tool_exec_start"],
"intercept": ["before_tool", "approve_tool"]
}
}
}
}
`

9.5 ハックスの順序

まずインプロセス → 次に priority を昇順で → 最後に名前で。決定的で、理解しやすいです。

What hooks are NOT for

  • チャネル自体にメッセージを送ること(バスを使ってください)。
  • 人間の承認待ちのためにターンを停止すること(状態機械は外部で扱います)。
  • すべてのプラットフォームにまたがるメッセージの完全なインターセプト(チャネル・レベルの関心事です)。

Patterns to copy

  • フックプロトコルをバージョン付けする(hook.hello)。 18か月後の大規模なリファクタを救います。
  • オブザーバは厳格なタイムアウトで実行する(例: 200ms)。遅いオブザーバは、ターンを停止させるのではなく「スキップ」へ静かに劣化します。
  • respond アクションにより、フックがツール出力を 偽装できる。 キャッシュ、モック、上書き—レジストリに触れずに行えます。

10. Core Concept #7 — Channel Abstraction(18以上のチャットプラットフォーム)

pkg/channelsGo におけるケイパビリティ(能力)ベースの多相の教科書的な例です。

10.1 契約

各プラットフォームのサブパッケージは BaseChannelbase.go)を埋め込み、最小のインターフェースを実装します。各プラットフォームは init() で自分自身をファクトリとして自己登録します:

`go
func init() {
channels.Register("telegram", New)
}
`

registry.go が唯一の真実のソースです。マネージャは特定のプラットフォームをインポートしません。

10.2 ケイパビリティ・インターフェース(任意)

`go
type MediaSender interface { SendMedia(...) error }
type TypingCapable interface { ShowTyping(...) error }
type ReactionCapable interface { React(...) error }
type PlaceholderCapable interface { SendPlaceholder(...) (id string, err error) }
type MessageEditor interface { Edit(...) error }
type WebhookHandler interface { HandleWebhook(http.ResponseWriter, *http.Request) }
type HealthChecker interface { Check(ctx context.Context) error }
`

マネージャは if c, ok := ch.(MediaSender); ok { ... } のようにしてチャネルを調べます。あるプラットフォームに VoiceCapable を追加しても、他の誰にも影響しません。

10.3 ️ メタデータの袋ではなく、第一級のフィールド

InboundMessagepkg/bus 内)で、ルーティング情報を型付けされたフィールドとして持ち上げます:

`go
type InboundMessage struct {
Peer Peer // platform + chat + topic
MessageID string
Sender SenderInfo // canonical identity("telegram:42")
Body string
Media []MediaRef
ReceivedAt time.Time
}
`

これは pkg/session.Allocatorpkg/routing.Router が依存している契約です。最初から設計に組み込んでください—後付けはつらいです。

10.4 ️ マネージャによる中央集約型のオーケストレーション

マネージャ(プラットフォームではなく)が所有します:

  • チャネルごとのレート制限つきワーカキュー
  • 送信メッセージの分割split.go)—長い返信は、プラットフォームの1メッセージあたりの上限を下回るように、文/単語の境界で分割します。
  • 一時的なエラーに対するリトライ(バックオフ付き)errors.go / errutil.go によって分類されます。
  • タイピング/リアクションのインジケータ:長いターンに対する透明な装飾として扱います。

プラットフォームは単一チャンクを送る方法だけを知っています。高度な処理はその上で行われます。

10.5 アイデンティティの正規化

pkg/identity は、正規の "platform:id" 形式と、複数プラットフォームのユーザを1つの論理的な送信者にまとめるアイデンティティ・リンクのテーブルを定義します。これにより、チャネル横断のメモリと一貫したルーティングが可能になります。

Patterns to copy

  • 空のインポートによる自己登録: メインバイナリは _ "yourapp/channels/telegram" するだけで、そのチャネルが利用可能になります。レジストリの配線は不要です。
  • ゴッド・インターフェース上の任意メソッドより、ケイパビリティ・インターフェース。 12番目のプラットフォームで変な要件が来たとき、きっと自分を褒めることになります。
  • errors.go におけるセンチネル(番兵)エラー:文字列を解析せずに、マネージャがリトライするかドロップするかを判断できるようにします。

11. Core Concept #8 — Provider Abstraction(30以上のLLM)

pkg/providersfactory + facade パターンを中心に構築されています。

11.1 レイアウト

`plaintext
pkg/providers/
factory.go // 登録と、名前によるプロバイダの生成
factory_provider.go
cli_facade.go // 「CLI」形のプロバイダ向けの統一ファサード
httpapi_facade.go // HTTP形のプロバイダ向けの統一ファサード
oauth_facade.go // OAuthフロー向けの統一ファサード
cooldown.go // 認証/クォータエラー時の、プロバイダごとのクールダウン
ratelimiter.go // プロバイダごとのトークンバケット
fallback.go // 次の候補へのフォールバック(責任の連鎖)
error_classifier.go // network/auth/rate/server/unknown
types.go // Message, ContentBlock, ToolCall, Usage, …

anthropic/ // Anthropic Messages API
anthropic_messages/ // 代替パス(例: サーバ側ツール)
openai_compat/ // OpenAI + あらゆるAPI互換ベンダ
openai_responses_common/
azure/ // Azure OpenAI 固有
bedrock/ // AWS Bedrock
httpapi/ // 汎用のHTTPフォールバック
oauth/ // デバイスフロー
cli/ // ローカルCLIプロバイダ(Ollamaスタイル)
common/ // 共通のメッセージユーティリティ・ヘルパ
messageutil/
protocoltypes/
`

11.2 プロバイダ・インターフェース(概念)

プロバイダは以下を公開します:

  • Send(ctx, request) (response, error)(チャネルによるストリーミング)
  • Capabilities()(ツール? 画像? 考える能力? コンテキストウィンドウ? ストリーミング?)
  • Name()Model()

エージェントのループは特定のプロバイダをインポートしません。ルーティング層から返される候補リストの中から選びます。

11.3 ️ 信頼性スタック(ほとんどのプロジェクトが見落とす部分)

プロバイダ呼び出しが失敗したとき、ラッパは次を参照します:

  1. error_classifier — 認証? レート制限? ネットワークの一時的な不具合? 5xx?
  2. cooldown — 認証/クォータの場合、このプロバイダをN分間利用不可としてマークします。
  3. ratelimiter — 契約上のTPM/RPMを超えないためのトークンバケット。
  4. fallback — チェーン内で次の候補を試します(重い → 軽い、またはプライマリ → セカンダリ鍵)。

エージェントはこれを知りません。チェーンが尽きるまでの間に、どちらかがレスポンスを返すか、失敗が確定して諦める、という1つの論理的な「送信」だけを見ます。

Patterns to copy

  • プロバイダ設定は protocol/model の文字列です。例: "openai/gpt-5.4", "anthropic/claude-opus-4-7"。設定を編集するだけで差し替えでき、再コンパイルは不要です。
  • 返却形式: {"translated": "翻訳されたHTML"}
  • APIキーは別の .security.yml に保管し、config.json に入れない。ファイル権限を分けられるので、バグ報告の際により簡単に情報を精査・削除できます。
  • 分類器の役目は「リトライするかしないか」を判断すること。 リトライを各プロバイダに焼き込まないでください。そうすると分岐していきます。

12. ️ コアコンセプト #9 — ツール、スキル、そしてMCP

「LLM呼び出しの他に、エージェントができること」を3つの層に分けます:

12.1 ツール — ビルトイン、インプロセス

pkg/tools/:

  • fs/ — 読み書き、一覧取得、グロブ。
  • shell.go(Unix/Windows のバリアント)— プロセスの実行。
  • hardware/ — デバイスとのやり取り(USB、GPIO、カメラ;SBCに適しています)。
  • integration/ — アウトバウンドHTTP、Web検索(DuckDuckGo、Brave、Tavily、Baidu)。
  • shared/ — 複数カテゴリで使われる共有ヘルパー。
  • registry.go — ツールを登録します。Get(name)List()、スキーマを公開します。
  • toolloop.go — 1ターン内でのツール実行をオーケストレーションします(並列安全、承認フックの統合付き)。
  • search_tool.go — 「Xをするツールを見つける」ための、第一級のツールセレクタ。
  • spawn.go / spawn_status.go — 長時間実行される子プロセスの管理。

12.2 スキル — インストール可能なプラグイン

pkg/skills/:

  • 2つのレジストリバックエンド: clawhub_registry.go(カスタムハブ)、github_registry.go(適切なマニフェストを持つ任意のリポジトリ)。
  • installer.go — 取得、検証、ディスク上に具現化。
  • loader.go — 実行時に読み込み。
  • provider_factory.go — スキルはプロバイダ設定を同梱できます。
  • search_cache.go — レジストリの検索結果はキャッシュされます。
  • config_bridge.go — スキルの設定は、親ファイルに漏れない形でランタイム設定にマージされます。

スキルとは本質的に、(ツール | フック | プロバイダ設定 | プロンプト | ドキュメント)をひとまとめにしたパッケージで、名前でインストールでき、きれいに削除できるものです。

12.3 MCP — Model Context Protocol

pkg/mcp/:

  • manager.go — MCPサーバへの接続を管理し、エージェントにそれらのツール/リソース/プロンプトを提供します。
  • isolated_command_transport.go各MCPサーバを隔離されたプロセスで起動し、標準入出力上でJSON-RPCを通信します。1つのバグのあるサーバがエージェントをクラッシュさせるのを防ぎます。
  • manager_test.go — テストカバレッジ。

agent_mcp.gopkg/agent 内)は、MCPで発見されたツールを、ターンごとのツールリストに組み込みます。モデルの観点では、MCPツールとビルトインツールは区別できません。

パターンとしてコピーすべきもの

  • ビルトインツールは小さく保ち、監査できるようにする。 意欲的なもの(ブラウザ自動化、決済)は、MCPまたはスキルの背後に置きます。
  • MCPトランスポートの隔離は譲れない。 MCPサーバを「信頼できない子プロセス」として扱ってください。
  • ツールには、データとしてのスキーマ、説明、承認フラグがある。Goの条件分岐ではありません。スキルやMCPに再利用するのも、単にそれらを列挙するだけの話になります。

13. ⚡ リソース効率化テクニック(<10MBの秘密)

0.6GHzのRISC-Vで<10MBを達成するのは、魔法ではなくエンジニアリングです。使っているテクニック:

13.1 Goの選択

  • 静的リンク: 共有ライブラリのフットプリントがありません。
  • JIT/インタプリタなし。 Pythonの起動コストがありません。
  • -ldflags="-s -w" は、バイナリからシンボルテーブルとDWARF情報を取り除きます(約30%サイズ削減)。
  • -trimpath はファイルシステムのパスを削除します。
  • UPX(任意):フラッシュ容量が乏しい基板向けの追加圧縮。

13.2 最小限のゴルーチン表面積

典型的な並行システムは、何千ものゴルーチンを起動します。PicoClawはそれを引き締めます:アクティブなチャネルリスナーごとに1つ、アクティブなターンごとに1つ、実行中のサブターンごとに1つ(最大5×N)、起動したフックプロセスごとに1つ、MCPトランスポートごとに1つ。ゴルーチンは安価ですが、それぞれスタックを持つため、数え上げて管理してください。

13.3 どこでも上限付きキュー

  • ステアリングキュー:10
  • SubTurn 結果バッファ:16
  • 親ごとの同時 SubTurns:5
  • チャネルマネージャのワーカキュー:プラットフォームごとに設定

上限付きキューによって、「メモリバグ」を「却下されたリクエスト」に変えられます。監視してチューニングできます。

13.4 バッファリングではなくストリーミング

LLMの応答はトークンごとにストリーミングされます。起動したプロセスからのツール出力も行ごとにストリーミングされます。大きな応答が、メモリ上に丸ごと存在することはありません。

13.5 JSONL の追記のみ永続化

定数メモリでの書き込み;読み取りは行イテレータです。毎ターンごとに O(n) のJSONオブジェクト再読み込みはしません。

13.6 遅延初期化

チャネル、フック、スキルのレジストリは、設定で有効化されたときにのみ初期化されます。無効なサブシステムは、ゼロの割り当て(アロケーション)に寄与します。

13.7 回帰(リグレッション)ゲートとしての membench

cmd/membench はリポジトリに同梱されています。ピークRSSを測定する合成ワークロードです。PRが予算を破った場合、CIがそれを検知します。

13.8 アーキテクチャを意識したパッチ

Ingenic X2600 / NaN2008 カーネル上の MIPS LE の場合、Makefile が ビルド後に ELF の e_flags をオフセット36でパッチします。これをしないと、カーネルがバイナリを拒否します。教訓:リンクが完了した(リンカが終了した)時点でクロスコンパイルが終わっているとは限りません。

14. クロスコンパイル & シングルバイナリ配布

14.1 ビルドマトリクス(make build-all

OS GOARCH Notes
linux amd64
linux arm (GOARM=7) Pi Zero 2 W(32-bit)
linux arm64 Pi Zero 2 W(64-bit)、ほとんどのモダンなSBC
linux riscv64 LicheeRV-Nano、MaixCAM
linux mipsle NaN2008 カーネル向け:ビルド後に ELF フラグをパッチ
linux loong64 LoongArch
darwin arm64 Apple Silicon
windows amd64
netbsd amd64 / arm64

特殊ターゲット:

  • build-pi-zero → 32-bit と 64-bit の Pi Zero 2 W バンドル。
  • build-android-bundle → JNI ライブラリを含むユニバーサル APK(エージェントは APK 内でネイティブサービスとして動作します)。
  • build-whatsapp-native → ネイティブな WhatsApp ブリッジを追加。
  • build-launcher / build-launcher-tui → Web/TUI のコントロールパネル。

14.2 ️ バージョンスタンピング

`shell
go build -ldflags "-s -w \
-X main.version=$(VERSION) \
-X main.commit=$(COMMIT) \
-X main.date=$(DATE)"
`

picoclaw --version を実行すると、スタンプされた値が表示されます — トリアージに欠かせません。

14.3 シングルバイナリ配布

ランチャ(Web または TUI)は、小さなスーパー バイザで、次を行います:

  1. プラットフォームを検出し、適切なバイナリを選びます。
  2. それを ~/.picoclaw/ に配置します。
  3. 起動し、設定用にローカルのブラウザを http://localhost:18800 にプロキシします。

エンドユーザはランチャをダブルクリックするだけで動きます。エージェントは実行されます。パッケージマネージャも、Docker も、Python も不要です。

15. ⚙️ 参照設定スキーマ

返却形式: {"translated": "翻訳されたHTML"}

config.example.json の注釈付き抜粋:

`jsonc
{
// エージェントが上書きしない場合に使用されるデフォルト設定
"defaults": {
"workspace": "~/.picoclaw/workspace",
"model_name": "openai/gpt-5.4",
"max_iterations": 25,
"max_input_tokens": 128000,
"max_output_tokens": 4096
},

// プロバイダ候補。APIキーは .security.yml に置きます(ここには置きません)。
"models": [
{ "name": "openai/gpt-5.4", "endpoint": "https://api.openai.com/v1" },
{ "name": "anthropic/claude-opus-4-7", "endpoint": "https://api.anthropic.com" },
{ "name": "google/gemini-2.0-flash" },
{ "name": "ollama/qwen3", "endpoint": "http://localhost:11434" }
],

// 安価なモデルからのルーティング(Cheap-first)。
"routing": {
"enabled": true,
"light_model": "google/gemini-2.0-flash",
"threshold": 0.35
},

// チャンネルごとの設定。多くはデフォルトで無効化されています。
"channels": {
"telegram": { "enabled": false, "token": "" },
"discord": { "enabled": false, "token": "" },
"slack": { "enabled": false, "bot_token": "", "app_token": "" },
"matrix": { "enabled": false },
"wechat": { "enabled": false }
},

// ツールの表面(Tool surface)。
"tools": {
"web_search": { "enabled": true, "providers": ["duckduckgo", "brave", "tavily"] },
"shell": { "enabled": true, "approval_required": true },
"fs": { "enabled": true, "root": "~/.picoclaw/workspace" },
"cron": { "enabled": true }
},

// 外部MCPサーバ。各サーバはそれぞれ独立したプロセス内で動作します。
"mcp": {
"servers": {
"filesystem": { "command": ["mcp-server-fs"], "enabled": true }
}
},

// スキルのマーケットプレイス。
"skills": {
"registries": {
"clawhub": { "enabled": true, "url": "https://hub.picoclaw.io" },
"github": { "enabled": true }
},
"installed": []
},

// フック:プロセス内のビルトイン + 外部プロセス。
"hooks": {
"enabled": true,
"observer_timeout_ms": 200,
"interceptor_timeout_ms": 5000,
"approval_timeout_ms": 30000,
"builtins": {
"audit_log": { "enabled": true, "priority": 10 }
},
"processes": {}
},

// 健康状態(liveness)レポートとオートスケールのシグナル用のハートビート。
"heartbeat": { "interval_seconds": 30 },

// Web UI ゲートウェイ。
"gateway": { "host": "127.0.0.1", "port": 18800 }
}
`

コンパニオンファイル:

`yaml

.security.yml -- 別ファイル、別の権限

openai:
api_key: sk-...
anthropic:
api_key: sk-ant-...
telegram:
token: 1234:ABC...
`

16. ️ 手順で学ぶ: 自分だけの PicoClaw スタイル・エージェントを作る

実用的な12ステップのロードマップ。各ステップで、実行可能な成果物が得られます。

ステップ 1 — スケルトンリポジトリ

`plaintext
yourapp/
cmd/yourapp/main.go # エントリ
pkg/
agent/
bus/
channels/
config/
providers/
routing/
session/
tools/
Makefile
config/config.example.json
.security.example.yml
`

main.go は設定を読み込み、Manager を構築し、os.Signal でブロックします。現時点ではそれ以外は何もしません。

ステップ 2 — 型付きメッセージバス

InboundMessageOutboundMessage を、第一級の PeerSenderMessageID とともに定義します。購読者ごとのキューを上限付きで持つファンアウトディスパッチャとして pkg/bus/bus.go を実装してください。

ステップ 3 — 1つのチャンネル: stdin/stdout

stdin から行を読み取り、InboundMessage を発行し、OutboundMessage を出力する stdio チャンネルを実装します。これは開発用のハーネスです。Telegram トークンは不要です。

ステップ 4 — 1つのプロバイダ: OpenAI互換

openai_compat プロバイダを構築します。ストリーミング対応にします。Provider インターフェイスを定義し、Send(ctx, req) (<-chan Chunk, error) を用意します。

ステップ 5 — 最小のエージェントループ

pkg/agent/pipeline_*.go。セットアップ → LLM → 実行(現時点ではツールは未使用)→ 決着(finalize)。システムプロンプトはハードコードします。これでエンドツーエンド、いま「hello」と入力すればストリームされた返信が得られるはずです。

ステップ 6 — JSONL 上のセッション

pkg/session を構築します。標準キー、JSONL バックエンド、.meta.json サイドカー、64シャードのミューテックス。これで、会話が実行をまたいで永続化されます。

ステップ 7 — ️ ツールレジストリ

pkg/tools/registry.goGetListSchema() で実装します。2つのツールを追加: fs.readweb.fetch。解析したツール呼び出しに対して pipeline_execute がそれらを呼び出すように配線します。

ステップ 8 — ️ 操舵(Steering)

セッションごとの FIFO キュー + 4つのポーリングポイントを追加します。エージェントがツールを実行している最中にフォローアップを送ってテストしてください。残りのツールは、明示的な「Skipped(スキップ)」のツール結果でスキップされる必要があります。

ステップ 9 — フック

5つのフックポイント + オブザーバイベントを定義します。まずはプロセス内登録を実装し、プロセス内パスが固まったら JSON-RPC の stdio プロセスフックを追加します。

ステップ 10 — ルーティング

5つの特徴量と重み付きスコアリングを持つ分類器として pkg/routing を追加します。設定に light_model を追加します。安価なチャットが light model に行くことを確認してください。

ステップ 11 — 2つ目のチャンネル + 能力(capability)インターフェース

Telegram を追加します。MediaSenderTypingCapableWebhookHandler の能力インターフェースを定義してください。リトライ/分割/レート制限を manager.go に移動します。Telegram チャンネル自体はおよそ200行程度に収めるべきです。

ステップ 12 — クロスコンパイル & 配布(ship)

`makefile
build-all:
\tGOOS=linux GOARCH=amd64 go build -ldflags="-s -w -trimpath" -o dist/yourapp-linux-amd64 ./cmd/yourapp
\tGOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w -trimpath" -o dist/yourapp-linux-armv7 ./cmd/yourapp
\tGOOS=linux GOARCH=arm64 go build -ldflags="-s -w -trimpath" -o dist/yourapp-linux-arm64 ./cmd/yourapp
\tGOOS=linux GOARCH=riscv64 go build -ldflags="-s -w -trimpath" -o dist/yourapp-linux-riscv64 ./cmd/yourapp
\tGOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags="-s -w -trimpath" -o dist/yourapp-linux-mipsle ./cmd/yourapp
\tGOOS=darwin GOARCH=arm64 go build -ldflags="-s -w -trimpath" -o dist/yourapp-darwin-arm64 ./cmd/yourapp
`

du -h dist/* を実行してください。バイナリはシングル桁のMBになるはずです。さらに membench の実行で、ピークRSSが目標(例: 10 MB)を下回り続けることを確認します。

その後、追加します: SubTurns(ステップ13)、MCP(14)、スキルマーケットプレイス(15)、Web ランチャ(16)、他のチャンネル(17〜N)。

17. ⚠️ よくある落とし穴 & 学び

これらは、PicoClaw のドキュメント内で明示されているか、あるいはその設計上の選択によって暗に示されている罠です。

返却形式: {"translated": "翻訳されたHTML"}
落とし穴 軽減策
無制限のファンアウトによるゴルーチンリーク 有界キュー + スコープごとの errgroup(ターン、セッション、チャネル)
チャネル間のメモリ・クロストーク sha256(scope) から得る正規のセッションキー — 文字列を連結しない。
フォーラム/トピックチャットが1つの会話にマージされる トピックが明示的なディメンションでない場合、チャット値に /<topic_id> を追記する。
ユーザーの訂正後のツール側の副作用 ステアリング到達時点以降の残りツールをスキップし、明示的なスキップ結果を出力する。
親をクラッシュさせる孤児の SubTurn 結果 16スロットの結果バッファ + 必ず完了すべき作業には Critical: true
context.Background() と親 ctx の混同 SubTurn API で明示的に文書化し、デフォルトは独立したタイムアウトにする。
平文の設定ファイル内の API キー 2つのファイル: config.json + 権限をより厳格にした .security.yml
メモリ退行が混入する membench を出荷し、CI でゲートする。
カーネルに拒否される MIPS LE バイナリ ビルド後、オフセット 36 で ELF の e_flags をパッチする。
フックがターンをブロックする クラスごとのタイムアウト: observer 200ms、interceptor 5s、approval 30s。
プロバイダーを追加すると再ビルドが必要になる プロバイダー設定は protocol/model の文字列;工場(factory)は実行時にディスパッチする。
セッション間でのスキーマのドリフト JSONL バックエンドでの遅延移行;適用済みの「migrations」を編集しない — 新しいものを追記する。
ルーティングルールがコードに埋もれている ルーティングはデータ — JSON のルール + 機能(features)。ホットリロードに対応しやすい。
リトライロジックをそれぞれが30チャネルで複製している リトライ/分割/レート制限を manager.go に集約する;チャネルは1つのチャンクだけ送る。
MCP サーバーのバグがエージェントを殺す isolated_command_transport を通じて、各 MCP サーバーを隔離されたプロセスで起動する。
セッションストア周りに1つのミューテックス ハッシュ(key) に対して 64 シャードのミューテックス配列。

18. PicoClaw ソースを通る推奨リーディングパス

この順でファイルを読めば、アーキテクチャの全体像が素早くつながります:

  1. cmd/picoclaw/main.go — 起動シーケンス。
  2. pkg/bus/types.go — システム全体を流れる型付きメッセージ契約。
  3. pkg/agent/definition.go — データとしての「エージェントとは何か」。
  4. pkg/agent/pipeline.gopipeline_setup.gopipeline_llm.gopipeline_execute.gopipeline_finalize.go — ループ。
  5. pkg/agent/turn_coord.go — ルーティング、プロバイダー、ステアリングを結び付ける頭脳。
  6. pkg/agent/steering.go — プロジェクト内で最もコピーしたくなる単一の概念。
  7. pkg/agent/subturn.go — サブエージェントのセマンティクス。
  8. pkg/session/manager.go + jsonl_backend.go + allocator.go — 永続状態。
  9. pkg/routing/router.go + classifier.go + features.go — 安価で先に試すルーティング。
  10. pkg/agent/hooks.go + hook_mount.go + hook_process.go — 拡張性。
  11. pkg/channels/manager.go + base.go + interfaces.go — チャネルの抽象化。
  12. pkg/providers/factory.go + cooldown.go + fallback.go + error_classifier.go — プロバイダー信頼性スタック。
  13. pkg/tools/registry.go + toolloop.go — ツール実行。
  14. pkg/mcp/manager.go + isolated_command_transport.go — MCP 連携。
  15. pkg/skills/registry.go + installer.go — プラグインマーケットプレイス。
  16. Makefile — クロスコンパイルの行列、ELF パッチ、バージョンスタンプ。
  17. docs/architecture/*.md — ステアリング、サブターン、セッション、ルーティング、フックの公式な物語。

TL;DR — 1ページでわかるレシピ

  1. Go を使う。 静的バイナリ、小さい RSS、アーキテクチャを問わず一貫。
  2. 型付きメッセージバス:ファーストクラスの PeerSenderMessageID を持つ。
  3. パイプライン化されたエージェントループ:セットアップ → LLM → ツール → ファイナライズ、ターンの状態用ストラクチャを伴って。
  4. ステアリング:セッションごとの FIFO キューを 4つのチェックポイントでポーリング;スキップされたツールには明示的な結果を返す。
  5. SubTurns:深さ ≤ 3、並行性 ≤ 5、独立したタイムアウト、必ず完了すべきものには Critical フラグ。
  6. セッション:構造化された SessionScope → 正規化された sk_v1_<sha256> キー、JSONL + .meta.json、64シャードのロック。
  7. ルーティング:5つの構造的特徴を持つ classifier、重み付けスコア、light_model はしきい値未満。
  8. フック:5つの同期ポイント + observer イベント。プロセス内または標準入出力上での JSON-RPC、クラスごとのタイムアウト。
  9. チャネル:それぞれが独自のサブパッケージ内にあり、BaseChannel を埋め込み、インターフェースで任意の機能を宣言。リトライ/分割/レート制限はマネージャが担当する。
  10. プロバイダー:ファクトリ + ファサード + クールダウン + レートリミッタ + フォールバック + error_classifier。protocol/model の文字列で構成し、秘密情報は .security.yml に置く。
  11. ツール / MCP / スキル:組み込み向けはインプロセスツール;信頼できない外部ツールには MCP(隔離トランスポート);スキルはレジストリからインストール可能なバンドルとして扱う。
  12. 有界キュー、ストリーミング、遅延初期化、-ldflags="-s -w"-trimpathmembench の退行ゲート。
  13. amd64/arm/arm64/riscv64/mipsle + Darwin + Windows + NetBSD へクロスコンパイル;MIPS ELF の e_flags をパッチ;バイナリを自動選択するランチャを出荷。

§16 の手順 1〜12 を順番にビルドし、§17 のパターンで検証すれば、PicoClaw クラスのエージェントが手に入ります。

もし役に立ったなら、 かコメントを残してください!また、この投稿が誰かの助けになりそうなら、ぜひ共有してください!どうもありがとうございます!