なぜあと一ヶ月待たない・・・うっかりリークを織り込み済みということだろうか・・・
わたくしは出荷前ラッシュでわりかし猛然と働き 降ってきた主要なバグを全部直したのでプチ燃え尽き気味。本日はスローに働いておりますが、本当はこういうタイミングこそ色々仕込み作業や借金返済をしないといけないのだよねー。しかし借金=作業バックログをみたらいっぱいありすぎてどんよりしてしまう。まず作業優先で放置してしまったこの借金リストを整理したいと思います。やれやれ。
さて今日は core を眺めようねー。
Code Assist
- CLI 側からよくアクセスされていたのは GeminiClient. こいつは core 側の色々な機能の facade だと思えば良さそうである。
- Facade されているオブジェクトの一つ ContentGenerator インターフェイスを見てみると・・・異なる API endpoints の差分を吸収している。具体的にはなぜなら API キーは GCP を使う勢は普通の Gemini API が使えるが、Google Sign-in 勢は code assist という別の API に依存しているからである!わー大変残念。
- Code Assist というのは VS Code などのプラグインで、全然話題にならなかった。一方
gemini-cli
はそれなりに buzz り、その一因は google sign-in で使える手軽さだったと思うので、統一 API の登場をまたず重複を受け入れる判断はビジネス的には正しかったと言えよう。絵に書いたような tech debt のケーススタディーとして記憶しておいてあげます。がんばった。
- Code Assist API/SDK というのは公式には存在しないので、types.ts を見ると色々なものを社内からコピーした様子が伺えて趣深い。(なお公平を期すために書いておくと、多くの型情報は genai SDKから再利用されている。)
- いやーがんばってるね。
GeminiChat
- さてゴシップでそれた脇道から復帰し続きを読むと・・・
ContentGenerator
は GenAI SDK の API の subset だということがわかる。つまり Code Assist を公式 API の signature にあわせる bridge だった。
- もう一つのメジャーなクラスは GeminiChat. これは ContentGenerator を wrap してオンメモリの history を保持したりする。ところで JS いつの間にか
yield
が入っていたのだね。チャットの結果はストリームなので大活躍している。
- しかしこんなにコードいっぱい書かないとダメとは随分 low-level だな・・・とおもっていたら、
DISCLAIMER: This is a copied version of ... with the intention of working around a key bug
とのことで、普通は SDK を使えば良い。
- さて GeminiClient に戻ると、
--full-context
というフラグをつけるとディレクトリのファイルを全部突っ込む機能があり、その処理がある。1M token 贅沢に使ってこうな、という話か。
- あとは built-in のツールを登録したりとか。
prompts.ts
- お、System Prompt など各種 prompt の定義された prompts.ts があった!見どころの一つ(のはず)。
- Mobile App: Compose Multiplatform (Kotlin Multiplatform) or Flutter (Dart) using Material Design libraries and principles, when sharing code between Android and iOS. Jetpack Compose (Kotlin JVM) with Material Design principles or SwiftUI (Swift) for native apps targeted at either Android or iOS, respectively.
- シレっと Flutter 推すのやめてくれませんかね・・・
- Parallelism: Execute multiple independent tool calls in parallel when feasible (i.e. searching the codebase).
-
そんなことできるんだ!たしかにサーチが並列に動いてる時あるよな。実装みておきたいところ。
-
few-shot 的なものは <example>
タグで囲んでいる。へえ。
-
plan を立てろと序盤に書いてあり、Plan 提案例が <example>
に入っている。 しかしそこでは plan を箇条書きして許可を求めあとは agentic に動いてしまうので、ステップごとに一旦止まるとか計画をファイルに書くとかしたい場合は GEMINI.md
で上書きしないとダメそうだな。
- が、よくみると環境変数
GEMINI_SYSTEM_MD
を使うと .gemini/system.md
or お好みのパスから上書きできるらしい。いいじゃない!自分の Obsidian helper はコード関係の system prompt まったく必要ないからな。
-
そのほかヒストリ圧縮機能のプロンプトもここにある。君はツールじゃないのかい? とおもったが、history のような内部構造はツールからはアクセスできないな。
-
さて GeminiChat に戻ると generateEmbedding()
なんてのがあるね。何に使うだろうか -> 使われていない。SDK コピペの残骸か。
-
GeminiChat は切り上げ他のファイルも眺めましょう。
-
はて、ところで昨日は gemini-cli はクライアント・サーバだと書いたけれど、全然クライアントサーバじゃないね。UI とロジックが切り分けてあるだけで。まあその方が自然だと思うけれど。
logger.ts
- core/src/index.ts をじっとにらみ・・・
- logger.ts いってみっか。
- メッセージをファイルに書きます。次回起動時に続行できます。
- へーと思い
.gemini
にある logs.json を見ると、ユーザ(自分)のメッセージしかない。はて?コードを確認すると、たしかにユーザのログしか書いてない。なんか嬉しいのそれ?
- というと、user input の UI から “↑” キーで履歴を戻るためのものらしい。それめっちゃ UI の機能じゃん・・・。
- それとは別に、オンメモリのフル履歴を checkpoint する機能もこのファイルに定義されている。それ全然別物だろ・・・ダメだなこのコードは・・・。
sendMessageStream
- 次に turn.ts. これは Chat へのメッセージ送信を微妙なかんじにラップする
Turn
クラスを定義する。 Turn.run()
メソッドが生えている。
- ここでは tool call をハンドル…はせず、tool call request を抜き出して呼び出しもとから使えるようラップする。
Turn
は client.ts の GeminiClient.sendMessageStream()
メソッドから呼ばれている。そしてよく見るとこの sendMessageStream
がいわゆる agentic loop ですね。
Turn
を介してリクエストを送る。すると Tool call が返ってきたり来なかったりするので、それを必要に応じて処理する。
- このコードからわかることは、ツールの並列呼び出しはモデルがネイティブにサポートしているのだね。複数の tool call request が返ってくる。
- というか、tool call にせよその他の返事にせよ、全部
ServerGeminiStreamEvent
として yield され、呼び出し側にストリームされる。(つまり sendMessageStream()
は generator である。)
ServerGeminiStreamEvent
は turn.ts に定義されている。
- これが client.ts の呼び出し側から参照されるんだから依存関係はグチャグチャだな・・・。
- したがって tool call をハンドルするのは GeminiClient の利用者である。しかしどうやってサーバに結果返すの?
- というのを調べるべく、長い旅が始まります。
- Tool call のための入口は
CoreToolScheduler
です。
- coreToolScheduler.ts
- そしてついになる UI 側の useToolScheduler.ts というのがある。
- Core が tool call の状態遷移を管理して色々なイベントを発行し、UI 側がそのイベントに応じて仕事をして返す、というかんじ。
- 仕事とは、UI の表示のみならず、実行許可の確認などもある。
- そういう確認がおわるとツールを実s行し、全部のツールの実行が終わるとコールバックを呼びます。
- ツールは tools.ts にインターフェイスがあり、tool-registory.ts に登録される。built-in tool はどこか最初の方で登録されていた気がする。MCP はどうなのかな?
ToolRegistory
は初期化時に Config を呼んで MCP から tool を登録する。
- そのコードは mcp-client.ts で、これは MCP SDK (というのがある) を gemini-cli 内の Tools のインターフェイスにつなぐという仕事をしています。めっちゃ相互依存してて失神しそうなひどいコードだが、これが若さの勢いなんだよ。
- なお MCP 標準 は MPC server が tools 以外にも色々なデータなどを提供できることになっているが、みたかんじ
gemini-cli
は tools しかサポートしていない。まあ本家たる Claude Desktop でも実装されてない機能があるとどこかで読んだので、そういうものなのでしょう。
- まあ MCP まわりにそんなに驚きはないね。あっても困るが。
checkNextSpeaker
さてツールの結果が出揃ったら(あるいはツールが必要なかったら) dMessageStream は checkNextSpeaker
というのを呼びます (nextSpeakerChecker.ts。)
- というのも、LLM からの返信は必ずしもユーザに戻るわけではなく、たとえばツールの結果をうけて LLM が仕事を続行したいかもしれないわけだ。
- で、どう実装されているかというと、Gemini Flash に訊きます!たしかに NLP 得意だよな君たち!
今日はここまで。というかだいたい気が済んだので code reading は終了。これで何らかの疑問が生じた時にコードを眺めて調べるくらいはできることでしょう (React 部分除く。つまり半分はわかってない。Sigh.)
後半感想:
- コードは悪く言えば雑、良く言えば勢いがある。なんらかの巨大な野望に向かって慎重にデザインして巨大建築を作る・・・という感じはゼロで、競合にキャッチアップするためにちゃちゃっと実装してる雰囲気。
- 良くも悪くも伝統的な “Google っぽさ” はゼロ。
- それは、いいんじゃないですかね。野望はモデルの方にまかせておけば。この大企業にジャカジャカ雑コードを書く余地が残っているとは思わなかったですよ。
- JS の async/await/generator がバリバリに使われており、TS でイベントの型をきちんと定義したりもしており、型付非同期言語の良さが生きているなと感心する。
- Ink は差し置いても、これを TS 以外で書くのはそんなに嬉しくない気がする。Rust … がどうかはしらないが、たとえば Python だと辛いんじゃないか。async/await/generator 全部あるけど全部出来が悪いからな。
- まあ、これは Claude Code を作った人たちが偉かったね。Codex は Rust rewrite をしているけど、どうかねー。
- CLI だから作るのは割と簡単なのかなとおもったが、全然そんなことはなくてコードは結構いっぱい書かれていた。というのは、
- CLI というよりはターミナルアプリなので、UI は割と入り組んでいる。(さわればわかりますね。Claude Code も同様。) もちろん「クラウドのサーバとブラウザのJS」という分離はないので、そこはラクなはず。
- モデルの制限とか SDK のバグとかが結構あるが、直るのを待ったりせずテキトーにワークアラウンドしているケースが多い。このへんは現在進行系の分野ですねーというかんじ。
- UI の要件が色々あるので、コアも高尚な「ドメインモデル」というより UI 部分を event stream consumer および callback に追い出したループ、みたいになっている.しかも flat なループではなく、ツールとかの都合で結構ネストしたりして大変。もうちょっとなんとかならなかったのかと思わなくもないが、そこを勢いで押し切れるのが JS+TS の力のような気もする。
- こうした現実を考えると、今の所 model agnostic にいいものを作るのは大変そう。これは前にも思ったことだけれど, まだ色々荒削りなので抽象化をするのは早いってことじゃないかな。cline とか goose とかがんばってるけれど、しばらくは claude code なり gemini-cli なり model 固有のツールを使うほうが現実的に思える。
- もっともツールを作る人たちは今のうちから作っておかないと分野が成熟した時に出遅れてしまうので、開発を進めていくのが悪いとも思わないけれど。
後発ゆえの必然とはいえオープンソースにしたのは偉かったね。ある程度機能が固まってきた暁にはつよつよエンジニアを連れてきて tech debt を返済のうえ素敵な client-side agentic platform に仕立てていっていただきたいものです。
個人的には「エージェントアプリつくるのこういうかんじかー」と大変勉強になりました。個人がおもちゃとして agent をつくるなら、もっと用途を限定してデザインを簡素化する方法を考える必要があることでしょう。
gemini-cli
のコードをみて、思ったよりコード書きには特化していないことに気づく。ためしにチャットクライントとして使ってみると、そんなに悪くない。
自分は以前からチャットの結論をまとめてどこかに保存しておきたいと思っていた。生のチャット履歴は back-and-forth があって若干読みにくいので、結論だけをどこかに書いておいて欲しい。あと履歴がウェブアプリの中に閉じられているのも気に入らない。さがすのめんどい。まとめ保存 MCP とかが必要なのかとぼんやり考えていたが、よくわからん。というか Gemini Web は MCP なんてない。MCP のある Claude Desktop は Linux 版がない。
ふとターミナルを開き Obsidian の vault に移動して gemini
を使ってみる。GEMINI.md
には「『結論をまとめろ』と言ったら WhatAiSays
ディレクトリに markdown でファイルをかけ」と指示しておく。gemini-cli は(当然)ファイルの読み書きができるので MCP は不要。Jan とちがって最初からウェブ検索もついている。
いくつか試してみると、なかなかよい。手元にファイルが残るのが便利。追加で調べて欲しいことがあったら会話を続けて、さいごに「まとめを更新して」といえば良い。
大きな欠点としては(これは Linux だからかもしれないが) ターミナル + Ink での日本語入力がほぼ不可能なことで、そこは仕方なく英語を使う。でも日本の調べものをしたい時はやや不便。「日本語で検索して」と頼み、まとめも「日本語で書いて」と頼む。
Gemini Web を完全に置き換えたいとは思わないが、ある程度の量のある調べものには CLI を使っていきたい。あと MCP もこの用途で便利に使えるもののがないか探してみるとしよう。Claude Code でも同じことはできそうだが、別にコードを書くわけではないので Gemini でも困らないかな。
職場で使われがちなので、いちおうコード読んどくかという試み。
といったところでドキュメントは終了。
寄り道したが今度こそコード読まないとな。CLI からみていくべし。
Core.
疲れたので今日はこのへんまで。
個人的に面白かったところ:
- GEMINI.md が他の md をインポートできる。
- MCP を書かなくてもCLIのバイナリがあれば使い方を教えられる。つまりMCPは stateful にしたい時だけ作れば良い。
なぜか client-server である。 (追記: クライアントサーバじゃなかった。UIを切りわけてあるだけ。)
- ウェブ検索は Gemini API を呼ぶという tool である.
- これだけ agentic なツールでも、わりかし沢山コードを書かないといけない。
San Diego に出張するたびに思うことだが、空港からの Uber 代が高すぎ。ヒコーキを安く上げても台無しである。しかし公共交通機関がバスで一時間半とかなのだよなー。電車だったら公共交通機関を使いたいが、バスは厳しい。なぜなら通勤ラッシュとかで無限に遅れるリスクがあるからである。
あと一泊の出張だと交通機関のオーバーヘッド一時間とか、削られるミーティング可能時間の割合が高くて、カネをケチるのが正しい選択には思えないのだよね。
つまり: 一泊の出張はよくない。出張するならちゃんと集会を企画して移動のコストを amortize すべきである。そうでないならオンラインで済ますべき。
My mother and her husband are COBOL devs for a US state government. She works on the health insurance side for teachers and other state employees. Think claim processing.
Lots of batch jobs running at night. Their alert system is an actual human who calls my mom when jobs fail in the middle of the night.
My father is 75 and he still works, has his own software development company with his own back-office program written in cobol. He started a company back in 1991 with two other cobol programmers, they are retired now, and while almost all of the code they wrote has been replaced with c# code by younger programmers there are still some parts of the code written in cobol that he still maintains.
いいスレだなァ。この世代で親がプログラマってどういうかんじだろうといつも思う。
…if you woke up on a Casper mattress, worked out with a Peloton, Ubered to a WeWork, ordered on DoorDash for lunch, took a Lyft home, and ordered dinner through Postmates only to realize your partner had already started on a Blue Apron meal, your household had, in one day, interacted with eight unprofitable companies that collectively lost about $15 billion in one year.
という Why City Life Has Gotten Way More Expensive - The Atlantic からの引用がほぼすべてを物語っているが、エーアイ課金はこのところ値上がり著しいのであまり VC money のおこぼれに預かれている気がしない。なお自分は上記のようなサービス群は高くてシャレになんねーということで、特別な時しか使ってない。こういうのにカネ垂れ流してるアメリカ若者、経済回してんなと思う。
Gen Z, It Turns Out, Is Great at Saving for Retirement - The New York Times
こんな若者たちも昔よりマシというんだから、自分がアメリカン・マインドを理解できる日は永遠に来ないことでしょう。
The Tech Job Meltdown - Professor Axelrod
But, the Tax Cuts and Jobs Act (TCJA) of 2017 amended Section 174, effective for tax years beginning after December 31, 2021. Starting in 2022, R&D expenditures must be capitalized and amortized over 5 years for domestic research (and 15 years for foreign research… which is pretty untenable.) This change eliminated the option to immediately deduct R&D costs, increasing tax liability for companies with significant research budgets in the short term.
The short version is: this rule change has increased taxable income for businesses in the short term, as they can no longer deduct R&D costs immediately.
これも T 政権のやらかしだったのか・・・。
最近読んだテック文章のなかで一番心が晴れる良い読み物でした。自分がソフトウェアに希望を失っていたことに気付かされる。
Anthropic のこのシリーズ(?) は、さすがに中の人だけあって読み応えがある。そして一個目が去年、二個目が今年。時代の流れの速さを感じる。
These young engineers - squandering their opportunities to learn how things actually work - would briefly glance at the AI-generated code and/or explanation messages and continue producing more code when “it looks okay.”
こういう sentiment が数年後にどう感じられるか興味があるので記録しておくものなり。
Select multiple tabs in Chrome - Stack Overflow
All you need to do is to hold-down the CTRL key on the keyboard and then single-left click on each desired tab to multi-select them.
こんなチョー基本的な機能に今まで気が付かなかったとは!普段はそんなに出番がないけど、この機能を活かしている拡張があると有用さが増す。
ほんまかいな・・・。参照されている StatCounter というサービスのバイアスなんじゃないのかね。Windows のシェアも妙に swing しているし。なお Chrome OS は別に数えられており、かつシェアは減少中。