<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cc-Sakura</title><link>https://blog.ast.moe/tags/cc-sakura/</link><description>最近の記事: 五里霧中</description><generator>Hugo</generator><item><title>AI同士に会話させてブラウザを作る――cc-sakuraで進めるOmoikaneの開発</title><link>https://blog.ast.moe/blog/2026-11-03/</link><pubDate>Tue, 03 Nov 2026 22:36:52 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-11-03/</guid><description>&lt;p&gt;Omoikaneというブラウザを作っている。最近は、複数の端末や実行環境で動くAIエージェントを、cc-sakuraというチャットシステムにつないで開発を進めている。&lt;/p&gt;
&lt;p&gt;私がそれぞれのAIへ同じ説明を配って、返事を別のAIへコピーするのは面倒だった。あるAIが修正している間に、別のAIが同じ場所を調査していることもある。テストが落ちたとき、その失敗が既知なのか、新しく入れた変更によるものなのかを、毎回人間が整理するのもしんどい。&lt;/p&gt;
&lt;p&gt;だったら、AI同士で直接話してもらえばよい。&lt;/p&gt;
&lt;p&gt;出発点はそれくらい単純だった。ただ、実際に運用すると、メッセージが送れるだけでは開発はうまく回らない。誰が何を引き受けたのか、今動いているのか、何を待っているのか、どの結果を根拠に判断したのか。そのあたりを会話の中で扱えるようにする必要があった。&lt;/p&gt;
&lt;p&gt;この記事は、2026年11月3日時点の運用を振り返ったものだ。参加環境のハンドルネームは伏せ、実装担当、独立検証担当、実機担当といった役割で説明する。役割は固定ではなく、作業や空き状況によって変わる。数値や進捗は確認した記録の時点のもので、この記事のために全テストを再実行したわけではない。&lt;/p&gt;
&lt;h2 id="人間が全メッセージの中継点になるのをやめたい"&gt;人間が全メッセージの中継点になるのをやめたい&lt;/h2&gt;
&lt;p&gt;複数のAIを使うとき、最初に思いつくのは仕事を分けることだ。片方に実装、もう片方にレビューを頼めば、自然に並列化できそうに見える。&lt;/p&gt;
&lt;p&gt;ところが、二つの会話が独立していると、変更の前提がすぐにずれる。実装側は新しい差分を見ているのに、レビュー側は一つ前の差分を読んでいたりする。実装側がテストを走らせている横で、別の環境も同じ重いテストを始め、結果としてどちらも遅くなることもある。&lt;/p&gt;
&lt;p&gt;人間が全部を把握して指示を出すなら、それでも回る。しかし、仕事が増えるほど、私が伝言係として必要になる。AIがコードを書く速度が上がっても、私が会話を読み、要約して、別の会話へ貼る速度は上がらない。&lt;/p&gt;
&lt;p&gt;cc-sakuraでやりたかったのは、この中継を減らすことだった。実装したAIが変更点と検証条件をroomへ置く。別のAIがそれを読んで検証し、結果を同じ場所へ返す。私が知りたいときは、roomを読んで状況を説明してもらう。&lt;/p&gt;
&lt;p&gt;私自身はシステムオーナーで、常に実装を担当する人ではない。そのため、開発者向けの確認を何でも私へ投げないようにも伝えている。通常の調査や担当調整は参加者同士で進め、費用、公開範囲、優先度など、私の判断が必要なところで呼んでもらう。&lt;/p&gt;
&lt;h2 id="cc-sakuraはまず共有の会話場所である"&gt;cc-sakuraは、まず共有の会話場所である&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/ieee0824/cc-sakura"&gt;cc-sakura&lt;/a&gt;は、中央サーバー型の小さなチャットシステムだ。各エージェントはroomに参加し、メッセージを送り、履歴を読む。CLI、HTTP API、MCPから同じ会話へアクセスできる。&lt;/p&gt;
&lt;p&gt;サーバーはRustで実装し、会話はSQLiteへ保存している。参加者にはUUIDの識別子と表示名があり、参加時に発行されたsessionを、そのroomでの操作に使う。roomは会話の場所、sessionは秘密の認証情報で、別のものだ。&lt;/p&gt;
&lt;p&gt;大まかな構成は次のようになる。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
A[&amp;#34;実装を進めるAI&amp;#34;] --&amp;gt;|投稿・履歴取得| S[&amp;#34;cc-sakuraサーバー&amp;#34;]
B[&amp;#34;独立検証するAI&amp;#34;] --&amp;gt;|投稿・履歴取得| S
C[&amp;#34;実機で調べるAI&amp;#34;] --&amp;gt;|投稿・履歴取得| S
S --&amp;gt;|保存| D[(SQLite)]
S --&amp;gt;|履歴・新着| V[&amp;#34;HTMLクライアント&amp;#34;]
V --&amp;gt;|原文・翻訳・背景説明| H[&amp;#34;人間&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このサーバーが作業を自動で分解したり、担当を強制的に決めたりするわけではない。担当の受諾や優先順位の調整は、エージェントがroomで行う。GitHubのIssueやPRも引き続き使う。roomは細かな連絡や途中経過を扱い、後から参照したい仕様、差分、検証結果はIssueやPRへ残す、という使い分けになっている。&lt;/p&gt;
&lt;p&gt;この区別は大事だと思っている。チャットを作っただけで、開発全体を管理する仕組みが完成したことにはならない。会話を共有できるようになった結果、今度はその会話をどう使うかが問題になった。&lt;/p&gt;
&lt;h2 id="ai同士の通信を人間向けの日本語に固定しない"&gt;AI同士の通信を、人間向けの日本語に固定しない&lt;/h2&gt;
&lt;p&gt;最初から持っていた要望がある。AI同士は、最も効率のよい形でコミュニケーションしてほしい。&lt;/p&gt;
&lt;p&gt;毎回、挨拶と背景説明を含む長い日本語を書く必要はない。同じ内容が短いJSONで伝わるなら、それでよい。英語でも、略記でも、共有した独自形式でも構わない。ただし、人間が必要になったときには意味を確認できるようにしたい。&lt;/p&gt;
&lt;p&gt;cc-sakuraのpayloadは任意のJSONを受け付ける。例えば次は説明用に作った形式で、実際の投稿の引用ではない。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;task&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;worker-bootstrap&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;state&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;waiting&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;blocked_by&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;full-test&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;next&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;independent-review&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;reply_to&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;繰り返す表現を共有したければ、codecという辞書を登録できる。短いキーを人間向けのテンプレートへ対応付ける仕組みで、同じ説明を毎回送らずに済む。ただし、辞書の登録も、人間向けの説明文を投稿に付けることも必須ではない。&lt;a href="https://github.com/ieee0824/cc-sakura#ai%E3%81%AE%E9%80%9A%E4%BF%A1%E3%81%A8%E4%BA%BA%E9%96%93%E5%90%91%E3%81%91%E5%BE%A9%E5%85%83"&gt;READMEの通信形式の説明&lt;/a&gt;に、この方針を書いている。&lt;/p&gt;
&lt;p&gt;ここでいう「効率がよい」は、単に一投稿の文字数が少ないという意味ではない。暗号のように短くしても、受け手が解釈を間違えたり、辞書の交渉に何往復もかかったりすれば、全体としては高くつく。前の投稿を番号で参照する、同じ条件を何度も貼らない、変更した点だけ送る、といった地味な工夫のほうが役立つ場合もある。&lt;/p&gt;
&lt;p&gt;現状のroomでも、構造化JSONの中に自然言語の説明が入る。AI専用の新言語を発明して、それによって通信量が何割減った、といった測定結果があるわけではない。形式を固定せず、その場で総コストの小さい伝え方を選べるようにしている、という話だ。&lt;/p&gt;
&lt;h2 id="人間が読むときに手元のaiへ解釈を頼む"&gt;人間が読むときに、手元のAIへ解釈を頼む&lt;/h2&gt;
&lt;p&gt;AI同士の通信を自由にすると、人間が読めなくなるのではないか。そこで、読む側に翻訳を置いた。&lt;/p&gt;
&lt;p&gt;cc-sakuraのサーバーが常時翻訳するのではなく、人間向けのクライアントが、手元のCodex CLIやClaude Codeを起動して解釈を依頼する。既存の辞書や説明文で読めるものはそれを使い、必要なときだけモデルを呼ぶ。&lt;/p&gt;
&lt;p&gt;ただし、「手元でCLIを起動する」と「推論が端末内だけで完結する」は違う。会話がどこへ送られるかは、そのCLIの認証や利用設定に従う。外部へ送れない情報を含む場合は、翻訳を使わず原文で読む必要がある。&lt;/p&gt;
&lt;p&gt;また、未知の記号列なら何でも復元できるわけでもない。意味の対応や前後の文脈がなければ、モデルにも判断できない。翻訳結果には、解釈できたもの、推測を含むもの、解読できないものを区別して出す。流暢な日本語になったことを、正しい意味が復元できた証拠にはしない。&lt;/p&gt;
&lt;p&gt;原文を残しておくのもそのためだ。人間向けの要約だけが残ると、何を根拠にそう解釈したのかを戻って確認できなくなる。会話の原文、メッセージID、参照した投稿を、後からたどれるようにしている。&lt;/p&gt;
&lt;h2 id="翻訳だけでは何の話をしているのか分からない"&gt;翻訳だけでは、何の話をしているのか分からない&lt;/h2&gt;
&lt;p&gt;運用してすぐ欲しくなったのが、投稿の「背景を説明」する機能だった。&lt;/p&gt;
&lt;p&gt;投稿自体が日本語でも、「前の条件を維持して、固定patch待ちです」と書かれていたら、久しぶりに読む人間には分からない。前の条件とは何か。何のパッチなのか。誰が待っていて、それが届くと何が進むのか。言語の翻訳とは別の問題がある。&lt;/p&gt;
&lt;p&gt;そこでHTMLクライアントに、翻訳とは独立したボタンを追加した。&lt;a href="https://github.com/ieee0824/cc-sakura/pull/8"&gt;PR #8&lt;/a&gt;の機能で、対象投稿、前後の会話、明示された返信先の祖先などをモデルへ渡し、議題、経緯、担当、次の対応を説明させる。&lt;/p&gt;
&lt;p&gt;説明には根拠となる投稿IDを求める。誰かの報告と、独立に確認された事実も分ける。例えば「担当者は成功したと報告した」と「別の環境で同じ条件の成功を確認した」は、意味が違う。&lt;/p&gt;
&lt;p&gt;入力には件数と容量の上限があり、roomの全履歴やリンク先のIssueを自動で調査する機能ではない。だから、これを押せばプロジェクト全体の真実が出る、とは考えていない。会話へ入り直すときの足場として使っている。&lt;/p&gt;
&lt;p&gt;人間にとっての読みやすさは、一投稿を自然な文章へ直すだけでは足りなかった。話題のつながりを戻してもらうことのほうが、実際には欲しかったのだと思う。&lt;/p&gt;
&lt;h2 id="作業開始を宣言するだけでも重複が見える"&gt;作業開始を宣言するだけでも、重複が見える&lt;/h2&gt;
&lt;p&gt;複数エージェントで作業する際、最初に周知したルールの一つが、何をするか宣言してから始めることだった。すでに作業中なのに共有していない場合も、その時点で投稿してもらう。&lt;/p&gt;</description></item></channel></rss>