ナンモワカランアザラシ

Ringed seal, clueless still

AIエージェントとうまく距離をとるためのソフトウェアを作っている

github.com

もともとのモチベーションは、作業の密度を上げたいことだった。仕事のload averageを上げるためにqueue機構を導入するという意図を込めてworqloadと名付けた。作っていくうちに、設計意図が「AIと密に連携したくない」になっていった。

特徴的な設計は次の通りである。

  • 対称対話インターフェースではなく、レポートとフィードバックの非対称非同期インターフェース
  • AIによる生ゴミを見ないための強制推敲
  • 判断を人間に仰ぐためのエスカレーション

レポートとフィードバック

人間がエージェントにinitial prompt を与えたら、あとは非同期にやり取りをする。
AIはよきときにレポートを提出する。人間はよきときにフィードバックを与える。AIはよきときにフィードバックをpullして作業の軌道修正をする。

人間とAIが対称な位置にいる対話UIだと、相手の出力を待ったり遮ったりする必要がある。これがめんどくさい。なので非同期にやり取りをすることにした。

また、anchor機能があり、これはレポートの特定の行数を指定してその部分に関するフィードバックを与えることができる。気に入らない言い回しがあるときに毎回コピペして引用して嫌味を言うのが面倒なので、相手のレポートに対して部分指定ができるようにした。

さらに、コードベース上では、現状のコードファイルやそのdiffに対してもanchorができる。現状のdiffをレポートとして扱い、その一部分にanchorしてフィードバックすることで、コードレビューがやりやすくなった。

強制推敲

レポート形式のメリットの一つが、「推敲」ができることにあると思っている。

現在のほとんどのテキスト生成AIは逐次生成である。どれだけthinkingをさせても発話時に「勘で喋る」ような挙動がよく見られる。そこで成果物を一度ファイルにして、それを読ませたうえで考えさせてpatchさせることにより推敲を達成した。 たとえば一度目のレポートは必ずrejectして推敲させる指示を自動で出すなどができる。カスタマイズもできて、reject時に与える観点promptを自分で作れたり、禁止ワードを設定することもできる。私は「可能性」という語を禁止している。「可能性」がレポート上に表れた際には「可能性という語が統計事実を指す場面以外で使われる場合、そのほとんどがただの調査不足である。どれが事実でどれが仮説か、その仮説を棄却するために必要な作業を実施したのか自問しろ。」というフィードバックが自動で与えられる。

エスカレーション

事前に決定ロジックが定められていない場合、最終的な決定は人間がすべきである。その際にはレポートの特殊ケースとして「エスカレーション」ができるようになっている。レポートは人間が何かアクションをしなくてもいいもの、エスカレーションは人間による何かしらの返答が必要になるものという区分けをしている。

現状のつらみ

worqload自体をworqload越しに開発することで十分なドッグフーディングができている。かなり快適になってきた。しかしまだ解決しない課題がある。

  • worqloadのプロトコルがモデルに十分に伝わらない
    • レポートを出さずにターンを終了したり、エスカレーションでなく通常レポートで判断を仰いできたり
    • 特に安いモデルだとこうなりがち
  • 推敲が下手
    • 推敲ができるようになったとはいえ、AIは人間が読みやすいレポートを書くのが下手くそである。最近のお気に入りプロンプトは「何を出力しないかによって品性が表れます。上品であれ。」である。

TUIコードリーダがほしい

イデアスケッチ。

現代では、もはやコードエディタでコードエディットをすることがなくなった。 大抵の場合、ターミナルを左右に分割してどちらかにエージェントプロセス、どちらかにエディタプロセスを立てている。エディタはコードを編集するためでなくコードを確認するために使っている。全体像を把握したり、エージェントが変更を加えた箇所の整合性をチェックしたり、diffを見たりといった具合だ。これをするためにはエディット機能はもはやtoo muchなのである。nvimでiを押してインサートモードに入る必要がない。

そこで、コードリーダだけあればいいのではないかと思った。従来のエディタからエディット機能をなくしてリードに必要な機能を足したようなものがほしい。できればターミナルで開きたいのでTUIで動くとうれしい。ほしい機能を列挙してみる。

  • ファイルを指定してその内容を表示する
  • ディレクトリ・ファイルの親子関係を一覧できる
  • コードジャンプ
  • lintエラー表示
  • シンボル同士の依存関係を可視化する
  • git diffをいい感じに(GitHub PRのdiffみたいに左右分かれて出るとうれしい)
  • git周りの操作ができる
  • パッケージ間の依存関係を可視化する
  • コードやファイルを選択してGitHub permlinkをクリップボードにコピー
  • コードを選択してクリップボードにコピー
  • テストがどこまでカバーしているかの可視化
  • テスト実行と結果表示

こんな感じだろうか。可視化がむずかしそうだと思った。それ以外は既存ツールの組み合わせで十分に実現できるが可視化だけどうすればいいかの見当がついていない。

オブザーバビリティデータへのクエリと可視化を標準化する

イデアスケッチ。

生成AIをトラブルシュートに使うためのあれこれを考えている。
あれこれやらせてみて、壁はクエリと可視化にあると感じた。

まず、クエリについて。現状だと多くのクラウドプラットフォームで、Web API経由でクエリしやすいような設計になっていない。また、クラウドプラットフォームごとに、クエリ言語が異なることも大いなる壁として立ちはだかっている。たとえばGoogle CloudのCloud LoggingではWeb API経由だとまともにクエリが機能しない。また、MQLとPromQLとでの内部の混乱が見られる。MQLって本当に廃止されるんですか?
AWS CloudWatchでもDatadogでも似たような傾向にある。これでは、AIエージェントを自立させてテレメトリデータをクエリさせて一次対応させることができない。

いま考えていることとしては、基本的にすべてSQLでいいのではないかということだ。OpenObserveというプロダクトを私は気にかけている。parquetでデータを保存することでデータの保存を安く済ませることに長けているらしい。parquetということはつまりカラムナーだな。ここまでくればあとはDuckDBの領分なのではないか?SQLなら十分に枯れている。現代AIの得意領域だろう。列志向データウェアハウスに対してもLIMIT 10とかしやがる癖があるがご愛敬だ。

また、可視化も鬼門である。インサイトを得るにはグラフにするのが手っ取り早いことが多い。現代AIなら画像から情報を得ることもできる。しかし可視化のためのリクエスト形式が統一されていない、あるいはAIフレンドリー(≒システムフレンドリー)でなければその恩恵は得られない。可視化インターフェースのよい統一方法はまだ私には思いついていない。何がいいんだろうなあ。すぐにAIが習得できて、うまく枯れてくれそうなものがいい。 Vegaとggplotを今知った。どちらもGrammer of Graphicsという思想の上にあるらしい。原著『The Grammar of Graphics』を読もうかと思ったけど2.5万円するらしいので躊躇......。

『ゼロトラストネットワーク 第2版 ―境界防御の限界を超えるためのセキュアなシステム設計』を読んだ

www.oreilly.co.jp

いわゆる「ザリガニ本」だ。「ゼロトラスト」概念と具体的な戦略の二つを大局的に理解するのによい本だと思った。

ざっくりとした自分の理解を書いていく。

境界モデルのペインから発展した形でゼロトラスト概念が広まっていった。ゼロトラストはモデルというより設計哲学である。「すべてのリクエストは検証されるべきである」。

ゼロトラストネットワークを実現するためのシステムを構成するために、複数のコンポーネントが出てきた。実際に認可を試行するエンフォースメント、定められたポリシーにリクエストを照らし合わせて認可判断をするポリシーエンジン、リクエストの内容から信頼度(トラストスコア)を弾き出すトラストエンジン、データを永続化するためのデータストア。これをゼロから自分ですべて実装・運用すると骨が折れると思った。

バイス、実行主体(アイデンティティ)、アプリケーション、トラフィック、検証すべき要素は複数ある。そのいずれも、手放しに信用すべきではない。コストとトレードオフにはなるが、一発食らってしまったときの損害がバカでかいのでそのリスクは高く見積もって防御に投資すべきである。

「トラストアンカー」概念の現実味が興味深かった。「すべて検証するべきである」が「これだけは信頼する」対象がトラストアンカーである。信頼錨。コギト・エルト・スムを想起した。それ以上疑ってもしょうがないので、信頼する起点を決めなければ何も考えることができない。

さまざまな境界モデルがハッカーに殺されていく中でゼロトラストモデルは伸長していくだろうが、どの企業にどのような需要があるのかはあまりわからない。まず投資の必要性を啓蒙するところからでは?

ユーザーからのシグナルを信頼性指標として取り入れる

思考のスケッチ。

SRE、サイト信頼性エンジニアリングのプラクティスにおいては大抵の場合にサービスレベル指標を測定する。サービスレベル指標は、SLI、Service Level Indicatorの和訳である。こう見るとServiceと対応する日本語ってないのだね。

では何のためにSLIを測定するかというと、リリース頻度と信頼性のバランスをとりたい、とかビジネスチーム内での共通言語として使える客観指標がほしい、とか世の中にはいろいろ書かれているが、畢竟、ユーザーに満足してほしいからだ。
「信頼性こそがあらゆるプロダクトの基本的な機能」とGoogleは表現した。基本的な機能が満たされていないのに別の新機能を作ってもユーザーには届かないだろうという意味だ。まず信頼性ありき。
さてこのSLIとしてよく使われるのは、可用性としてエラーレートやパーセンタイルレイテンシ、また完全性としてデータ耐久性である。後者はS3のeleven nine, 99.999999999%が有名だ。
これらSLIに目標を設けてSLOとすれば、SLOを達成することがユーザーが満足することの必要条件であると見なせる。

そう、「必要条件であると見なせる」だけである。十分条件であるかはわからない。必要条件を満たしていると断言することも難しい。
データがクライアントのもとに100%の確率で0.1s以内に届いても、例えばUIデザインの不具合によってユーザーが正しくサービスを使えないがゆえに不満かもしれない。そもそもユーザーはレイテンシを気にしないかもしれない。その測定しているSLIが「正しく測定できている」かどうかもわからない。適当にクエリ書いてみたらそれっぽい数字になったのでこれをSLIとしよう!なんてことはよくあるのではないだろうか。

ここから一歩進んで、ユーザーの満足の十分条件により近い指標条件を見つけたい。
それにはユーザーの生の声を得るのが手っ取り早そうだ。

この考えに沿ったプラクティスとして、CRE、Customer Reliability Engineeringという技法があることを私は知っている。 GoogleGoogle の新しい専門職 : CRE が必要な理由で次のように書いている。

今、この時代において、真に適切なサポート チームのミッションはたった 1 つです。それは、お客様の不安をゼロにすることなのです。 (中略) 不安は 1 を信頼性で割った数値に等しい

私は「ユーザーの満足を高める」と書いたが、Googleは「ユーザーの不安を低める」としている。さらに、GoogleはCREはSREが開発チームに対して厳しい要求をするように、お客様に対して厳しい要求をすることによってお客さまの環境の信頼性を高めて不安を減らすのだと主張している。なかなかに苛烈だと感じる。Cloud Platformならではの考え方かもしれない。サービス提供者として考えるのなら、やはりユーザーが不安を感じないようにシステムを設計することに尽くしたい。その中の一策として、ユーザー環境を監視して信頼性が低まるような事態があればユーザーに対してアラートを出すなどはよい施策かもしれない。CREとSREとはダブりのない集合ではなく、SREがCREを含んでいるように私には思える。Site Reliabilityをどうにかするためにお客様を監視することは自然な考えだと思う。

具体的な方法には、お問い合わせ指標化とクライアントモニタリングが思いつく。

不満を感じている人のn%がお問い合わせをしてくださると仮定する。アクティブユーザー全体に対する、不具合に関するお問い合わせを出したユーザー数の比率を出す。一定期間内のこの比率がo未満であれば、システムは健全であると捉える。実際にはそんなに確実な指標にはならないだろうが、「エラーレート」も同じように不確実なものだろう。とりあえず可視化してみる分には損をしないだろう。

クライアントモニタリングの方が技術的にはやりがいがある。まず想起するのはDatadogのRUM, Realtime User Monitoringだ。クライアントにエージェントを仕込んで、ブラウザやネイティブアプリでの操作履歴を収集する。クライアントでのエラー率や、「不満度合い」を指標として抽出する。「不満度合い」については定義がむずかしそうだが、たとえば、同じページを何度も行き来する行動や、マウスカーソルが荒ぶったりしていたらそのユーザーは不満を抱えている可能性が高いと考えられないだろうか。そのような「不満率」をモニタリングして、システム変更によって上がったり下がったりする様子を眺めるとインサイトが得られると思う。ビジネスとしてそれをやる意義を説得しやすくするために、会員解約率やアカウントのアクティブ率と紐づけるとおもしろいかもしれない。

以上のようにCREとしてやられている作業をSREの領域に持ち込むことでより高度な信頼性エンジニアリングを実現したい。

エラーバジェットがなくなるまでリリースしまくる

SREの仕事の方法のアイデアとして書く。

直近の仕事では、リリースされて1年も経っていない若いプロダクトに関わっている。このようなプロダクトでは、信頼性が問題になることが少ない。コードベースは(比較的)きれいだし、インフラやCI/CDも最新のプラクティスに沿っている。ここまで「壊れたら直して二度と壊れなくする」でお賃金をいただいてきた身からすると、何をやるといいかなあと少し手持無沙汰な感じを抱いている。

SREは高速なリリースとサービスの信頼性のバランスをとるための技術である。エラーバジェットが余りまくりであるなら、やるべきはリリース頻度の増強だろう。有り体に言えば、「壊れるまでリリースしつづける」。これを実現するためには何が必要だろうか。

大きく分ければ、正確なエラーバジェットの測定と、リリースまでの壁を取っ払いつづけることだろう。

前者は信頼性を守るために必要である。何をもって「壊れている」とみなすか。サーバーがエラーを返したらダメか。それだけではないだろう。お金を扱うサービスなら、その計算方法やトランザクションの整合性は機能として信頼できる必要がある。多くの文脈においてはSREingは非機能要件を相手にしてきた。しかし当たり前だが、機能要件は機能として必要である。「信頼性こそがあらゆるプロダクトの基本的な機能」とGoogleは書いていたが、非機能は非機能であって機能ではない。プロダクトが正しく機能しているかどうかを測定するためには、何を正しいとするかを決める必要がある。
測定指標として、「お問い合わせ」を使うとどうだろうか。信頼とは、意図した通りに動いてくれることである。意図しない動きをしないことである。友人に金を貸せるのは、その友人が意図通りに金を返してくれるという信頼があるからだ。プロダクトにおける「意図」の主語は、ユーザーである。信頼できるかどうかは、ユーザーによって決まる。そのため、ユーザーが「意図通りに機能しない」と言えば、それは信頼性が低いのである。開発者にとっての意図は自動テストで担保するのを前提として、ユーザーにとっての意図はユーザーに聞くしかない。「お問い合わせ」のデータを信頼性指標として使うことを提案する。具体的に何をどのように指標化するかは別記事で書きたい。

後者のリリースまでの壁について書く。開発は、時間がかかる。新機能を提案して、要件をすり合わせて、設計して、コードを書いて、レビュワーがそれを理解して、CIが通ればマージしてCDによってリリースされる。大まかなリリースフローはこのようだろう。
この中で時間の圧縮が比較的簡単そうなのは、コードを書く・CI・CDだろうか。技術者目線だと、ここはいくらでも技術によって圧縮できそうだ。
コードを書くについては2026年には完全に自動化されるだろう。達成したいことがあって、テストを書いて実装することでknown-knownの領域を増やしていく。「達成したいこと」のなかでunkownの領域がゼロになったらコード書きは終わりだ。   一つ飛ばしてCDの高速化は、これは簡単なチューニング問題に思える。現代のリリースエンジニアリングの粋を集めて「がんばれば」終わりだ。
CIの高速化はすごくやりがいがありそうだ。現代のCIはとにかく肥大しがちだと思う。私は過去のCIを知らないのだけど。事故があるたびにポストモーテムとして統合対象を増やしている。今日も一日ご安全に。ヨシッ。その代償にリリースまでの速度が下がる。対策としては、並列化と削除が考えられる。コンピューティングリソースを横に並べられるだけ並べれば時間を支配できる。コンピュートが時間課金の現代でも、1+1+1+1と1*4の値段は同じだ。人間の脳みそは並列稼働しないので、人間以外の脳みそは並列稼働した方が得だ。削除の方がむずかしい。歴史的に生まれた防壁としてのテストがほとんどだろうから、過去に敬意を払えば払うほど削除の心理的ハードルが上がる。簡単なsmall testについては、カバレッジを計算して、削除前後で変わらないよねと示せばよさそう。medium, largeテストを減らすのは難しいぞ~~~。本来はsmall testですべてカバーするのがよいという根拠で、medium, largeなテストを削るべきだろうか?それこそ、信頼性が下がるまでは削って、エラーバジェットを割ったら割ったところから戻していく、とか。この手の削除作業は自動でやられるべきだと思う。人間が提案して人間が決断するのだと先のとおり、情緒が入る。エラーバジェットを見て自動でテストを減らすくんが定期稼働してほしい。 次に難しそうなのは、「レビュワーがコードを理解する」ことだろうか。これは、そもそもレビューの意義から再構築するのがよさそう。これまでに聞いたことのあるレビューの目的は、「コード品質の維持向上」「enbugのガードレール」「共同所有」このあたりだ。コード品質の維持向上については、linterやformatterに寄せていくのがよさそう。コーディング規約の領域を増やしていくことで対応する。いわゆるReadable Code的な「明瞭なことばづかいをする」「一つの関数は一つのことを」みたいなやつも、現代LLMが頑張れる。いわゆるクリーンアーキテクチャ的な、内側のドメインモデルから外側の技術詳細に向けて依存関係を整備するようなコーディングについても同様にLLMが十分にやれそうだ。関数同士の依存について簡単に可視化できると人間もうれしいかもね。enbugのガードレールについては、もうテストを書くしかないのではないだろうか。レビュワーは書かれたテストを見て、テストケースが十分か判断する。コードがテスタブルになっていればそれだけテストコードもリーダブルになる。あと残る仕事は共同所有である。俗人性の排除と換言できそうだ。何か障害があったときに、「ああそういえばあの辺を変えていたな」と関係者が判断できる。新しくコードを書くときに「ああそういえばあの人が便利なメソッドを生やしていたな」と思い出せる。LLMに委託できるだろうか。障害対応文脈でもコーディング文脈でも十分にできそうな気がしてきた。シンプルに、コンテキストウィンドウに直近n PRのdiffを与えれば済む。ここまでできていれば、あとは人間が「達成したいこと」すなわち要件を理解していればテストコードだけ見てレビューは終わりだ。 ここまでくると、新機能提案、要件すり合わせについては、もうやりたい人が自分でコーディングをやるのが一番手っ取り速いんじゃないだろうか。アジャイルチームで言う「顧客」が自分でつくるのだ。そうするのが一番速い。
残った仕事は設計である。現代の生成AIだとまだアーキテクティングは専門家がやるべきである。あなたがもし2026年初頭の最新AIに技術設計相談をして、「AIはなんてすばらしいんだ!」と感じたのなら、あなたは技術の専門家ではない。この「技術」を任意の分野に置き換えても多分よい。

何事も速い方がおもしろい。もっと速くできる。超高速なリリースに向けてやっていきたい。

事実、演繹、仮説

自分のものの考え方をテキストにして外部から利用しやすくしたい。

会社で「フックアップ」を役割として求められてからずっともやもやしていたことに決着をつける。
「フックアップ」は日本語訳したら「引き上げ」だと思う。上から目線っぽくてイヤな感じがする。自分が上にいないと引き上げることはできないからね。無知なお前らに啓蒙してやる、みたいな態度はとりたくない。
そうではなく自分の考えをライブラリとしてまとめておくことによって、だれかが目前の断崖に呆然としているときに登るための道具の一つになれればうれしい。

また、クリフトンストレングスの結果レポートに、「お前の考えていることはそのままでは他人には伝わらないのでちゃんと道筋を説明しろ」と何度も何度も書かれていた。私はクリフトンストレングスを2年ごしに2回受けてほぼ同じ結果が出たのでクリフトンストレングスのことを信仰している。結果レポートには複数の"資質"と呼ばれるものの説明が書かれる。もろもろ総合した内容がそれぞれの"資質"についてのアドバイスとして書かれるらしいが、「考えたことを説明しろ」と複数の箇所に書かれていた。それだけ、必要なことなのだろうと思う。
なんだかんだ「ちょっと考えればわかるだろ」と思ってきたけど、いろいろな人たちと交流してようやく、他人は自分とは違うということが分かってきた。

自分は口頭でしゃべるよりもキーボードで文章を練る方がより伝わるコンテンツをつくれると思う。
このブログは技術tipsを書き留める場として利用してきたけど、このような抽象的な話をエッセイとして書き記す場としても使っていこうと思う。


この一年で「どれが事実でどれが推測ですか」と100回は書いた。誇張でなく少なくとも100だと思う。

生成AIをさまざまな仕事のエージェントとして利用するようになってから1年が経とうとしている。2025年は元年ってやつだったのだろう。
エージェントには自然言語で指示をする。明確な指示をするためには自分の考えていることを浮き彫りにしなければいけない。エージェントは、インターネットからデータを集めた集合体だと思っていいだろう。エージェントの捉え方と自分の指示が食い違ったら、それは自分と世界の違いであると考えるのが妥当だと思う。

AIは、事実と推測を混同しがちである。つまり世界も事実と推測を混同しがちだと思っていいだろう。知恵袋に「~かもしれません」と書かれた回答はない。どんなに嘘でもすべて自信満々に断言されている。
どれだけ必死にpre-promptに「事実は根拠とともに断定せよ。事実から妥当な推論によって導かれることは演繹として述べろ。推測は推測として述べろ。」と書き殴っても、エージェントは事実と推測の区別がつかない。演繹と推測の区別はもっとつかない。なんなら推測と推論を同じ語義だと思っていやがる。

事実は、自分が、見えること聞こえること思うこと感じること、である。「それ以上否定できないこと」と言い換えていい。コギト・エルト・スム。我思う故に我あり。事実はそこまでだ。「~に...が書いてある(ように見えた)」までが事実だ。それ以外は事実ではない。

演繹は、命題から推論できることだ。ここで言う「推論」は推測のことではない。人間はいつか死ぬ、ソクラテスは人間だ、ならばソクラテスは死ぬ。これが演繹である。「人間はいつか死ぬ」は「Aが人間の集合に属するならばAはいつか死ぬものの集合に属する」という命題に言い換えられる。 「ソクラテスは人間だ」は、「ソクラテスは人間の集合に属する(ように私には思える)」という命題である。命題二つから妥当な推論がなされる。いわゆる三段論法というやつだ。

AIには、「事実、演繹、仮説」とよく言い聞かせている。上記の事実と演繹以外はすべて仮説だ。仮説は「~かもしれない」と語られるべきである。断言された仮説のことを、強いことばだが「妄言」と呼んでいる。妄言が出てくるとコンテキストが汚染されるし私の脳内も汚染されるのでできる限り早く打ち切った方がいいと思っている。仮説と括ったものは、帰納仮説だとか抽出された仮説だとか推測だとかさまざまに分類したり言い換えられたりできる。しかし「帰納」と書くと数学的帰納法による証明と語が被る。「抽出された仮説」(abductionってやつ)は一般語彙でない。「推測」と表現するとAIはそれを「妥当な推論」と同値だと思いやがるので使いたくない。なのでひとまとめに「仮説」と表現している。

トラブルシューティングをするときは、情報のサバンナから事実と演繹を丁寧に切り抜いてそこを安全地帯とする。それ以外の仮説は荒野と捉えて開拓している。仮説は、事実の収集によって支持あるいは棄却できる。どのような事実が得られたらその仮説を支持あるいは棄却できるか考える。反例探しだ。ちなみに仮説は支持するよりも棄却する方が簡単なので、棄却できそうな仮説に言い換えると開拓が進みやすい。「棄却できない」を積み重ねることによって、「支持できるかもしれない」仮説として扱えるようになる。

AIにエラーメッセージをぽん、と投げる。AIは適当なことをしゃべる。「どれが事実ですか」と聞く。AIが適当なことをしゃべる。「もう一度聞きます。どれが事実ですか。事実の定義は最初に与えたとおりです」と聞く。ようやく事実のみが返ってくる。「残りの仮説を棄却あるいは支持できるためにはどのような事実が必要ですか」と聞く。AIは最初の仮説に沿って修復作業を始めようとする。焦って止める。「もう一度聞きます(略)」と聞く。「じゃあそれを一つずつやってください。いいですか、ひとつずつです。まず最初の一つだけ」と指示する。3つの切り分け作業がすべて終えられた状態で報告が返ってくる。溜息をつく。