ふつうのウェブエンジニア向けRust入門書『RustによるWebアプリケーション開発 設計からリリース・運用まで』

私たちがRustを学ぶ際の障壁

ウェブエンジニアにとってRustを学ぶ際の最大の障壁は、その適用分野がもともと高度である点です。
Rustは主にシステムプログラミングやC++の置き換え、ビルドツールの最適化といった専門的な領域で活用されています。
これらの分野に馴染みがないウェブ開発者にとって、Rustのエコシステムを学ぶことは簡単ではありません。
本書はそのようなエンジニアが、既存の仕事の道具をRustに置き換える時のガイドとして最適です。
システムの一部をマイクロサービスに切り出すケースや新規プロジェクトのバックエンドでRustの採用を検討したりする場面で活用できます。
そのため、この書籍はウェブのバックエンドエンジニアがRustエコシステムを実践的に学ぶための最適なリソースといえるでしょう。

本書の特徴

本書の特徴は、バックエンド開発者が普段行う作業をRustに置き換えて学ぶことができる実践的な内容にあります。
その中心は、ハンズオン形式でシンプルなCRUDアプリケーション(蔵書管理システム)のバックエンドWeb APIを開発するというアプローチです*1
本書は、C++で行うシステムプログラミングの置き換えやWasmを用いたブラウザで動くアプリケーション機能の実装、またRust言語そのものの体系的なリファレンスではありません。
ウェブ開発という領域で、よりリアルワールド志向で、フロントエンドやインフラチームとの協調を念頭に置いた現場のチーム開発に即した内容を重視しています。

Rust自体の副読本の併用を推奨

本書は、シンタックスレベルでのRustの基本的な言語機能についての理解を前提としています。
そのため、Rust自体をはじめて使う読者へは副読本の併用が推奨されます。
私自身は、Oreillyから出版されている『プログラミングRust』を活用しました。
この本は、Rustの細かい構文や仕組みについてもっとも詳細に解説されている一冊だと思います。
また、テスト駆動開発(TDD)の第一人者であるt-wadaさんもこの書籍でRustの理解が捗ったと言及されています。

speakerdeck.com

システム全体の技術スタック

本書で提案されているシステム全体の技術スタックは、リアルワールドのプロジェクトにおいて採用されやすい最大公約数的な構成となっています。
バックエンドでは、非同期ランタイムのデファクトスタンダードであるtokioをベースにしたWebフレームワークのAxumを使用し、データベース操作にはsqlx、ビルドツールとしてcargo-make、そしてテストにはcargo-nextestをはじめから用いてcargo-workspaceのモノレポ構成でスタートします。
フロントエンドにはReact系のNext.jsを採用していますが、バックエンドのAPIとは分離されているので他のフレームでも問題なく適応可能です。
本書内ではフロントエンドの実装解説はありませんが、バックエンドAPIをシンプルに利用するGUI構築を念頭に置いており、理解するのは容易です。
インフラ部分では、バックエンドのデプロイにAWS App Runner、フロントエンドにAWS Amplify、そのIaCツールとしてTerraformを使用します。
書籍内にデプロイ関係の詳細な解説はありませんが、運用・監視のための設計に関する説明が後半に含まれています。
現実の新規プロジェクトでもそのまま参考にしやすい実践的な提案と言えるでしょう。

完成形のコードがGitHubリポジトリで公開されており、フロントエンドやインフラ構築コードも含まれているため、読者が自分でAWS環境にデプロイ可能な形になっています。

本書の実装範囲のリポジトリ:
https://github.com/rust-web-app-book/rusty-book-manager

フロントエンドを含むデプロイのドキュメント:
https://github.com/rust-web-app-book/rusty-book-manager-template

独自のレイヤードアーキテクチャの提案

本書の最大の特徴は、独自のアプリケーションレイヤーを通じて、Web APIの構築に焦点を絞った独自のレイヤードアーキテクチャを提案している点です。
広義のレイヤードアーキテクチャは、プレゼンテーション層からインフラストラクチャ層まで、システムを外から内へと水平に層を分けるアーキテクチャパターンです。
このアプローチでは、たとえばレイヤー毎にモジュールやディレクトリ構造を分け、依存先の参照ルールをチーム内で統一することで、プロジェクトの保守性や可読性の向上を目指します。
書籍『ソフトウェアアーキテクチャの基礎 ―エンジニアリングに基づく体系的アプローチ』ではレイヤードアーキテクチャは「小規模なコードベースに対してもコストパフォーマンスの良い形で適用可能な点」が評価されています。
本書では、モジュールの境界をapi, kernel, adapterのレイヤーごとに設定し、registryにrepositoryのインターフェイスを定義して依存します。
それをCargo Workspaceを用いて各レイヤーを個別にビルドできるように設計されています。

このアーキテクチャの考え方は、Rustに限らず、さまざまな薄いフレームワーク(マイクロフレームワーク)でも適用できる点で興味深いです。
たとえば、GoやPythonのFastAPI、TypeScriptのHonoなどで実装している時にも同じ課題を抱えます。
これらのマイクロフレームワークは、MVCのように機能ごとのモジュール構成をあらかじめ提案するのではなく、設計方法に意見を持たない構造であるため、開発者が自らアーキテクチャを設計し、実装していく必要があります。
なので、フレームワークのコミュニティごとに解決するミドルウェアやライブラリが活発に開発されています。
この自由度の高さにより、言語ごとの特性を活かした構成が可能になるため、本書ではRustのエコスステムを学ぶ上でも優れたアプローチといえます。
本書の設計思想は先のとうり現場での実践を想定しているので、著者陣が「今仕事でRustを書くならこんな構成」という意見を提供してくれていると言えるでしょう。

とはいえ、まだRustを使ったWeb開発にはデファクトなパターンが存在しないため、読者もそれを考慮する必要があるでしょう。
実際、本書の刊行後にも著者のhelloyukiさんによって改善案が検討されており、たとえばエンティティを横断するロジックをService層に集約する構成が提案されています。

speakerdeck.com

このService層の考え方についても、各コミュニティには異なる流派があり、DDD(ドメイン駆動設計)やマイクロサービスの実践においてさまざまなアプローチが存在しておもしろい点です。

おわりに

『RustによるWebアプリケーション開発 設計からリリース・運用まで』は、現場のエキスパートによる実践的な知見が詰まった一冊です。 Rustを学びたいが、どこから手をつければよいかわからないというバックエンドエンジニアにとって、最適な入門書といえるでしょう。

PS: 電子版が出るそうです

*1:似ている本:詳解Go言語Webアプリケーション開発のRust版のような構成です

How to be a tech influencer.(2021)を技術ブログ論として読むと良かった

テック業界で影響力を持つためには

lethain.com

"How to be a tech influencer."には、「テック業界で影響力を持つためにはどうすればいいのか?」という相談者の問いから「質にフォーカスした少数のコンテンツでプレステージ(評判)を築くのが効果的」との説明が述べられています。

この記事を書いたのは、『エレガントパズル』や『スタッフエンジニア』の著者であるWill Larsonで、彼はエンジニアリング組織の技術リーダーシップの専門家としても知られています。
近著『The Engineering Executive's Primer』の「Chapter 12: Building Personal and Organizational Prestige」には、このブログ記事の内容が含まれており、組織の技術トップにおいて個人および組織の評判を築くことの重要性が掘り下げられています。

プレステージ(評判)とは

ここでいうプレステージとは、周囲の人々があなたやあなたの会社について良いイメージを持っている状態を指します。
本記事では固有のニュアンスを持っているのでプレステージという言葉をそのまま使います。
これはブランディングやマーケティングのような能動的な活動とは異なり、優れた仕事や質の高いコンテンツを通じて自然に形成される他者の意識です。
このように築かれた評判は、キャリアや人材獲得においても有利に働き、長期的に信頼される存在となる基盤を提供するとされています。

影響力と技術ブログ

私はこれを技術ブログ論としての側面から注目して、Will Larsonが影響力の解釈を「人々に業界にある既存の考え方や価値観の変化を促せること」としている点に感銘を受けました。
これは私が普段ブログ執筆で達成したいことと近く、同じレベルにはまったく達していないものの(名前は似ているが)、目標として繰り返し参考にしたいと感じています。
つまり知名度ではなく影響力を持つためには質の高いコンテンツを制作するための戦略が求められているわけです。

プレステージを構築する方法

プレステージを構築するためには、すでに評判の良い集団に参加するのが最も近道だと説明されています。
例えば、開発者体験が良いイメージのある企業ランキングの上位企業に所属して、企業の持つ発信力の一員として名前を出して参加する、ということが該当するかもしれません。
しかし、居住地や環境の制約から、それを実現できる人ばかりではありません。 あいにく私もそのような機会がありませんでした。
そこで、再現性の高い方法として質の高いコンテンツを作ることに注力しましょう。
コンテンツは量産するよりも少数でも質の高いものを作るべきであり、さらにそのコンテンツにアクセスしやすくするのがキーポイントです。

影響力の指標をどう計測するのか

そもそも影響力の適切な指標を持つのは難しい問題です。
それをどのように測定するかについては、多くの議論の余地があります。
PVやフォロワー数、売上、投稿数といった数値は一般的な指標として使われがちですが、それらはしばしば誤った指標とされています。
なぜなら、これらの数値はコンテンツの質や影響そのものを必ずしも反映しないからです。
Will Larsonは「コンテンツが人々の行動や考え方にどのような変化をもたらしたかや、あなたにアドバイスや意見を求めてくる人の数は、影響力を測るための重要な指標といえるでしょう。 」と言及しています。
私はこれを読んで今までまるで関心のなかった匿名質問受付サービスの有効活用法を見出しました。
確かに私の思う影響力の高い人々は匿名質問受付サービスに熱心に回答している気がします。
そして私は質問が全然来たことはないので影響力のなさを痛感しました。

質の高いコンテンツを作るために

質の高いコンテンツを作るには、普遍的なテーマと独自性のある視点が必要です。
これらの要素が揃うことで、コンテンツは長く参照されるものとなります。
特に、繰り返し議論されているようなニーズのあるトピックを選ぶことが重要です。
たとえば、日本のテック業界で多く言及されている「質とスピード」に関する講演は、普遍的なテーマを扱いながらも独自性を持った視点を提示しており、質の高いコンテンツの好例だといえます。
このようなコンテンツは、議論を呼び続ける力を持ち、時代を超えて価値を提供し続けます。

自分に合った形式で発信する

そして、自分に合った形式で発信します。 Will Larsonは、ブログやカンファレンスでの講演といった形式が最適と説明しています。
十分に短く細かい試行錯誤のフィードバックループが回しやすいからです。
これらに比べてポッドキャストや書籍は反復のサイクルが長く、難易度の高い形式といえます。
私自身、ポッドキャストを聞いたり本を読むことは大好きですが、それを自分で作るとなるとハードルが高く、未だに手を出せていません。
それでも、fukabori.fmなど現にポッドキャストを中心に質の高いエンジニアリングのコンテンツで影響力を発揮しているといえるクリエイターも存在します。
自分に適した形式を選ぶことでコンテンツを持続的に発信しやすくなる点は大切だと考えています。

プレステージを構築するためには数年の期間が必要

プレステージを構築するには、少量の質の高いコンテンツを数年かけて繰り返し発信することが必要です。
しかし、プレステージは受動的な結果であるため、活動時間に対して定量的な成果が得られにくい特性があります。
それゆえ、持続する努力が不可欠です。 そのためには、標準を定めて一球入魂の姿勢で取り組むことにしています。
実際に、当サイトでさえも一時的なバズで消費される記事と、それを超えて繰り返し参照され言及され続ける記事*1に二分化していることからも、持続性と質がいかに重要であるかが分かります。

デリバリーの戦略

作ったコンテンツを多くの人に見てもらうには、デリバリーにも戦略が求められます。
その目的は、ターゲット層がコンテンツにアクセスしやすい環境を整えることにあります。
これに関してできることはたとえば、ターゲット層が参加しているコミュニティに自分も積極的に関わったり、年齢層に応じて適切なSNSを選択したりすることが挙げられます。
私は個人開発者や特定のOSSユーザーが集まるコミュニティや、シニア層が多い「はてな」、若年層が集まる「X」クラスタなどを意識的に活用するすることにしています。
また、評判の掛け算を生み出すために、コンテンツをシェアしてくれる読者を作ることもその助けになります。
私にとっては、@mizchiさんや@azu_reさんのような、以前から認知があり業界内で評判の高い人によるシェアが、このサイトへの多方面から多くの人が訪れるケースになっています。
さらに、質を保ちながら継続的に発信を続けることで、認知度を着実に高めることができます。
私はとくに文章を書く習慣が途絶えると、質を担保する前提である執筆活動ごと生活の中からなくなりやすい性格のため、「日記を書く」というリマインダータスクを毎日設定し、発信を習慣化しています。

最後に:批判との付き合い方

あなたの発信に対して多少の反対意見が来るのは、既存の思考を変革するものである証拠として、むしろ良い兆候といえる、とWill Larsonもコメントしています。 これには同意です。
ただし、短期的な議論を呼ぶことを目的とした内容や表現に手を染めるのは望ましくありません。 それでも自覚してコントロールは難しく、私も意図せずこの状態に陥り、後から反省しています。
私の場合、影響力の度合いはウェブ上でどのような反応があるかを指標として活用しています。
むしろ何も反応はないがとりあえずシェアされる、というのが十分な行動を引き出せなく不甲斐なさを感じる場面です。
賛成する感想を持つ人がいた場合、それは自分が届けたい読者のリストに追加すべき対象になります。
一方で、異なった考えを持つ人がいたら、その人の立場を理解しようと努めます。
また、正確に伝わっていない人がいた場合には、コンテンツの内容や記述方法を見直し、内容に誤りを発見したら加筆や訂正を行います。
このように、周囲の反応を活かしながら改善を続けることで、より多くの人に影響を与えるコンテンツを目指していきたいですね。

*1:繰り返し参照され言及され続ける記事: https://laiso.hatenablog.com/entry/nope-sql とか

私的Chromeカスタム検索エンジン利用頻度ランキング

Google Chromeのカスタム検索エンジン機能は非常に便利です。

support.google.com

設定したキーワードをアドレスバーに入力するだけで、指定したサイト内で直接検索ができるようになります。
例えば、「yt」をYouTubeの検索キーワードとして設定すれば、アドレスバーに「yt キーワード」と入力するだけでYouTube上で検索が完了します。
いちいちサイトにアクセスして検索フォームに入力する手間が省け、キーボード操作のみで素早く目的の情報に辿り着けます。
私はこの機能を頻繁に利用しており、使えない環境ではストレスを感じるほどです。

集計について

そこで、この機能の活用を布教したり、まだ知らないカスタム検索エンジン設定について他の人から教えてもらったりするために、普段使用しているChromeの設定を集計してみました。そこからトップ20のカスタム検索エンジンを抜粋し、その利用用途や背景を紹介します。
集計方法としては、Chromeプロファイルの内部データベースを参照することで、各設定の利用回数を確認することが可能です。
むしろ、公式なエクスポート機能は見当たらず、一部の拡張機能で対応できるようですが、私はそれを使用していません。
このデータベースはSQLiteファイルとして保存されており、以下のパスにあります。ただし、Chromeが起動中はデータベースがロックされているため、一旦Chromeを終了し、SQLiteクライアントを用いて開く必要があります。

# macOS
❯ sqlite3  ~/Library/Application\ Support/Google/Chrome/Default/Web\ Data

sqlite> select * from keywords order by usage_count desc;

第1位:Google(ja)

short_name keyword url
Google(ja) ja https://www.google.co.jp/search?q={%s}&lr=lang_ja

Google検索が利用頻度の第1位にランクインしています。
なぜ専用のカスタム検索エンジンに登録しているかというと、英語のキーワードで日本語のみの検索結果を得たいときに便利だからです。
"javascript"や"llm"関連の技術用語で情報を探すことが多く、このような検索では日本語のコンテンツを優先したいことも多いため、この設定が役立っています。
その結果、自然と使用頻度が最も高くなり、納得の第1位と言えます。

第2位:Twitter Search (ja)

short_name keyword url
Twitter Search (ja) twj https://twitter.com/search?q=lang%3Aja+{%s}&src=typd

Google検索のX(元Twitter)版です。技術用語の検索頻度が高いので納得の第2位ですね。

第3位:perplexity.ai

short_name keyword url
perplexity.ai pe https://www.perplexity.ai/?q={%s}

ウェブ検索した結果をサマリして聞きたいことに関する直接の回答を文章とソースで教えてくれます。質問が明確な時に利用します。 以前有料版を使ってレビューを書きましたが、現在は無料版ユーザーです。

laiso.hatenablog.com

英語で検索したいときは質問を英語で打ち込みます。毎回翻訳ツールを使うのは長期的にみて効率が悪いため自作GPTで瞬間英作文の機会として活用しています。

chatgpt.com

第4位:Twitter

short_name keyword url
Twitter tw https://twitter.com/search?q={%s}&src=typd

英語で Twitter(元X)をざっと検索したい時に使います。

第5位:Hatena::Bookmark

short_name keyword url
Hatena::Bookmark b http://b.hatena.ne.jp/search/tag?safe=on&q={%s}

過去に話題になった記事を検索したい時に利用します。2000年代からのユーザーたちの登録データの蓄積が強みです。一般的なウェブ検索では到達しづらいに記事も発見できます。IT系の話題に偏っています。英語版はHackerNewsかDaily.devですね。

第6位:amazon.co.jp

short_name keyword url
amazon.co.jp a http://www.amazon.co.jp/s/ref=nb_sb_noss_1?field-keywords={%s}

書籍や商品情報を検索します。技術書が多いですね。

第7位:Google(US)

short_name keyword url
Google(US) en https://www.google.co.jp/search?q={%s}&lr=lang_en

日本語とは逆に英語のみで検索したい時に利用します。

第8位:Google Books

short_name keyword url
Google Books gb http://www.google.com/search?q={%s}&tbm=bks&tbo=1&hl=ja&qscrl=1

Googleが出版社から提供を受けている書籍のスキャンデータから検索できます。登録されている本は限られています。キーワードをもとに本を探したいときにプレビューまで見られて便利です。一般的なキーワードでウェブ検索より信頼性の高い情報をもとめて検索することもあります。

第9位:Wikipedia (ja)

short_name keyword url
Wikipedia (ja) w http://ja.wikipedia.org/w/index.php?search={%s}

日本語Wikipediaから検索します。百科事典的に使います。脚注を辿って出どころに近い情報を探すリンク集としても便利です。

第10位:1

short_name keyword url
1 1 https://www.google.com/search?q={%s}&tbs=qdr:y&qscrl=1

「1」は直近1年のGoogle検索結果を見るためのショートカットです。技術情報はよく期間を絞りたくなるため利用します。

第11位:Android Market

short_name keyword url
Android Market am https://play.google.com/store/search?q={%s}&c=books

Play Storeから検索します。Androidアプリはウェブからインストールしているのと、読み上げ機能の優秀なPlay Booksで一部の書籍を購入しているので使います。

第12位:GitHub Code

short_name keyword url
GitHub Code gc https://github.com/search?q={%s}&ref=simplesearch&type=Code&utf8=✓

GitHubのコード検索です。ライブラリの関数名で検索し、実際のOSSでどのように使われているか参考にしています。代替として https://sourcegraph.com/search もちょっといいと思っている。

第13位:Google US around 1 year

short_name keyword url
Google US around 1 year en1 https://www.google.com/search?q={%s}&lr=lang_en&tbs=lr:lang_1en,qdr:y

直近1年のGoogle検索結果を見るためのショートカットの英語版です。

第14位:amazon.com

short_name keyword url
amazon.com com http://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords={%s}

Amazon.comの方で検索します。洋書用ですね。

第15位:EIJIRO Web

short_name keyword url
EIJIRO Web e http://eow.alc.co.jp/{%s}/UTF-8/?ref=sa

アルクの英辞郎ウェブで例文を調べます。ローカルの辞書も持っているので単語の意味レベルだとそっちを使っています。

第16位:Goo辞書

short_name keyword url
Goo辞書 goo http://dictionary.goo.ne.jp/srch/jn/{%s}/m0u/

日本語辞書として使います。記憶の曖昧な特定の言葉の正確な使い方を調べるときに使います。

short_name keyword url
GPT search gpt https://chatgpt.com/?q={%s}

ニューフェイスです。ChatGPTがウェブソース検索に対応したのでperplexityと比較するために試しています。公式Chrome拡張は検索バーを乗っ取るので入れない方がいいですね。

第18位:GPTs

short_name keyword url
GPTs gp https://www.google.co.jp/search?q=site%3A%2F%2Fchat.openai.com%2Fg%2F+{%s}&lr=lang_en

公開されているGPTsを探します。他人の作ったGPTが便利と思った場面は実はあまりなく、自分で普段使っているプロンプトテンプレートを収めて置く場所としてGPTsは便利に使っています。

第19位:PyPI

short_name keyword url
PyPI pypi https://www.google.com/search?q=site%3Apypi.python.org%2Fpypi%2F+{%s}

Pythonライブラリを探します。他にnpmやgem版があります。

第20位:HB Entry

short_name keyword url
HB Entry h https://www.google.co.jp/search?q=site%3Ab.hatena.ne.jp/entry/%20{%s}&ie=UTF-8&sa=Search&cad=h&qscrl=1

はてなブックマークの検索よりGoogleインデックスから検索した方がいいのではと思っていた時期に登録したやつ。今はあまり使っていないです。

その他

  • Kagi Search
  • Google Scholar
  • 中国語ページのみ検索
  • MDN
  • HackerNews
  • Unix manpages
  • Mac Developer Library など

今年のLinuxデスクトップ元年はひと味違う。それは、Omakubがあるからだ。

omakub.org

DHHとApple

もともと20年来のMacユーザーでTextMate原人であったDHHだが、HEYアプリが何度もAppStoreでリジェクトされた出来事を通じてか、次第にApple反転アンチ化してきた。
元々オルタネイティブな嗜好を持つDHHだったが、脱クラウド運動などの、近年の彼らのビックテックとの闘争にもつながっている。
年初あたりから自社の支給端末であったMacBookのリプレイスを検討し始め、Windows+WSL2やVSCode*1 、そしてLinuxデスクトップの利用を模索し始めていた。
最終的にUbuntuデスクトップを「安住の地」とし、そのセットアップの知見をすべてOmakubに込めたのだ。
エディタをNeovimに*2、スマホもSamsungに乗り換え*3、そしてFrameworkのラップトップを購入。
「ラップトップ買ってきましたUbuntu環境構築エントリ」を実行可能なOSSで公開してしまう男、それがDHH。

world.hey.com

Omakubの概要

Omakubは、ウェブ開発向けのデスクトップ環境を自動で構成してくれる「オマカセUbuntuセットアップツール」だ。
オマカセ(Omakase)という言葉は、アメリカの寿司レストランでシェフにコース料理の選定を丸投げするスタイルを指す*4
Omakubが寿司屋の大将のように、新鮮なドキュメントツールがありますよとObsidianをデフォルトでインストールしてくる。
さらにちゃっかりHEYやBasecampも組み込まれてくる。

Omakubは、Linuxディストリビューションや他のセットアップフレームワークとは異なり、セットアップそのものを提供するツールだ。
その構造は、AnsibleHomebrew Cask、古のPuppetベースのBoxen*5を彷彿とさせるが、大きな違いは「ノーコンフィギュレーション」にある。
ユーザーは最低限の対話シェルで応答するだけで、x86_64なUbuntu(GNOME環境)が決め打ちされ、シェルスクリプトがもりもりと実行される仕組みだ。
インストールするアプリケーションからデスクトップのテーマや具体的な設定まで全て決まっており、ユーザーはほぼ手間なく環境を構築できるというコンセプトを持つ。
欲しいものを追加で入れたり、不要なものは外すこともできるが、基本的にはOmakubが提供する環境を受け入れることが前提だ。

www.youtube.com

実際のセットアップ体験

ちょうど家でゴロゴロしていたら「Linuxデスクトップ・・作りてえ・・」という気分になったので、実際にセットアップを試みた。
DHH信者の私にとっては推しのインフルエンサーがプロデュースするECサイトで服を買うような感覚だ。
まず、格安のmini PC(Intel Celeron 2コアというヤマダ電機で埃をかぶっていそうなマシン)を購入し、USBメモリにUbuntuのISOファイルを焼いてBIOSから起動、SSDにUbuntuをクリーンインストールした。
その後、wget経由でシェルからOmakubを実行して自動セットアップを進めたが、期待に反して完全自動とはいかなかった。
途中でdockerグループへの追加に失敗して止まり、手動で追加して再実行する必要があった。
さらに、GNOME Shell Extensionsのインストール時にも確認ダイアログが表示され、エンターキーを連打して進めなければならなかった。

PS:全くお勧めではないですけど格安mini PCはこれです。

デスクトップ環境の評価

何はともあれ無事セットアップ成功して結果として、macOS風のデスクトップができあがった。
かつてのLinuxデスクトップのmacOS風テーマはどこか統一感に欠け、「これじゃない」感が強かったが、おそらくUbuntuやGNOMEコミュニティのデベロッパーたちの長年の努力によって、現在はかなり洗練されているものになっていて、驚かされた。
macOSそのものではないが、代わりとなる美しいデスクトップ環境(DE)に仕上がっており、Omakubが提供するオルタナティブとして十分に魅力的だと思う。
このことからOmakubは(DHHがそうであったように)長年Macでウェブ開発していたような開発者層の乗り換え先として意図していると思われる。

開発環境の構成

Omakubのコアとなるインストールアプリケーションは、主にCLIオペレーションとウェブの開発環境の構築に焦点を当てている。
まず、ターミナルエミュレータとして「Alacritty」が導入され、シェルやTUIツールのウィンドウ基板(btopとか)として利用される。
また、画面分割に「Zellij」が導入され、Alacritty内で子ウィンドウを管理する(screenやtmuxの代替となる)。
エディタは「Neovim」が採用されており、ほぼLazyVimそのままだ。
DHH自体はIDEやコード補完をいっさい使わないらしい*6。ル・マンレーサーにCopilotは不要なのだ(助手席がないからな)。
さらに、デスクトップアプリが存在しない場合はGoogle Chromeのショートカットが作成される。
ビックテックはゆるせねえがChromeショートカットは便利でPWA最高(Firefoxにはこの機能がなかった)。

開発ツールとしては、デフォルトでRailsとNode.jsがMise経由で入る。Rustはrustupから、PHPはapt経由で入る。最新版のみだが、必要ならMiseで手動で追加すればいい。
データベースにはMySQLとRedisを用い、ホストでアプリケーションを実行しながらDBのみDockerコンテナで起動する開発スタイルになってる。


さあ、あなたもOmakubを始めよう。

manual.omakub.org

最近作ってる地味LLMコマンドラインツールたち: site2pdf, askrepo

地味LLMコマンドラインツールとは

ここでいうLLMツールとは、モデルのインプットとアウトプットを繋ぐ「グルーコード」として機能するプログラムのことです。
筆者が現在開発しているツールもLLMをベースにして、従来の作業をより簡便にすることを目的としています。
このツールは単独で完結する価値を持つというよりも、他のツールと組み合わせることでその真価を発揮します。
そのため、外見的には派手さがなく、地味な存在といえるでしょう(コマンドラインツールがそもそも地味の代名詞でありますが)。
例えば、アプリのソースコードを自動生成するような華やかなツールがある一方で、バックエンドで静かにデータを整形するだけの地味なライブラリも存在しているのです。

Gemini系モデルの良いところ

ツールの共通点としては意外とGemini系モデルを活用しています。

Geminiの特徴の一つは、その「トークン長」、つまりコンテキストウィンドウの広さにあります。
具体的には、Gemini 1.5 Flashでは100万トークン、Gemini 1.5 Proでは200万トークンのコンテキストウィンドウが標準装備されており、これは他のモデルと比較しても圧倒的です。
たとえば、GPT-4系のコンテキストウィンドウは12.8万トークン、Claudeは20万トークンであるのに対し、Geminiは数倍以上のトークン数を誇ります。
以前の記事では「GeminiはチャットAIとしてはイマイチ*1」という感想を述べましたが、ツールに組み込む用途ではその長大なコンテキストが有用です。

「対人苦手だけど情報処理能力高くてドキュメントワーク得意」みたいな人間の個性っぽくて面白い。

site2pdf

github.com

site2pdfはその名の通りウェブサイトをPDFに変換してダウンロードするコマンドラインツールです。
リンクをたどって複数のページを1つのPDFファイルにまとめることができるという、古のクローラー・ダウンローダーみたいなものです。
従来のクローラーとの違いはブラウザで見た時と同じように画像やスタイルなどの視覚情報もそのまま保存することです。
どうせAIがどんどん進化して中身を読めるようになるだろう、という見当でそうしました。

モチベーション

各種AI系の検索拡張生成(RAG)や質問応答(QA)サービスに、複数ページをまとめたPDFファイルはデータセットとして活用できます。
今までは「ウェブサイトをAIに食わせて何かやる」という場面ではpythonでクローラーを書いたり、URLをちまちまコピペしていましたが、世の中のサービスがインポート元としてPDFを受け入れるようになってきたのでPDFで用意すればいいんじゃないか? と発想しました。

ツール自体がGemini APIにアクセスしているわけではないですが、NotebookLMにPDFをインポートしたり、Gemini APIを使った処理を行うことができます。
そのNotebookLMをどう使っているのかについては以下の記事で書きました。

laiso.hatenablog.com

類似ツール

wkhtmltopdfがありますがリンクをたどらないし、そもそも更新が止まっています。

使い方

Node.jsがマシンにインストールされている必要があります。 ターミナルで次のコマンドを実行します

npx site2pdf-cli "https://www.typescriptlang.org/docs/handbook/" "https://www.typescriptlang.org/docs/handbook/2/"

out/ディレクトリにPDFファイルが保存されます。 以下引数の説明です。

npx site2pdf-cli <main_url> [url_pattern]
  • [url] : PDFに変換するWebサイトのメインURLです。
  • [url_pattern] : サブリンクをフィルタリングするためのオプションの正規表現です。 デフォルトでは、メインURLドメイン内のリンクのみが一致します。

注意点

数十ページを超えるような大規模なサイトをPDFに変換する場合、URLパターンを指定して変換するページを絞ることをお勧めします。 またデフォルトでCPU個数ぶん並列でダウンロードします。
アクセス方法はサイトの案内に従い自身で判断してください。ボロい図書館サイトは避ける、とか。  

実装方法

  1. puppeteer を使用して入力されたURLに移動する
  2. url_pattern に一致するすべてのサブリンクを見つける
  3. pdf-lib を使用して各サブリンクのPDFを生成する
  4. それらを単一のドキュメントにマージする
  5. URLに基づいて名前をつけて最終的なPDFファイルを保存する

TODO

  • [ ] robots.txtに準拠する
  • [ ] JavaScriptのレンダリングを待つ
  • [ ] 配布時のtsxの依存をなくす
  • [ ] ページのスクリーンショットを取得するバージョンを検証する

askrepo

github.com

askrepoはコードベースの理解を深めるための補助ツールです。
Gitで管理されたソースコードファイルから情報を抽出して、Gemini APIを使用して質問に対する回答をします。
コマンドラインからソースコードのパスと質問を受け取ります。
その後、ソースコードファイルの内容をGemini APIに送信し、APIの応答から回答を生成します。

モチベーション

OSSのソースコードを読む際に、不明な部分や理解できない部分があるときに、今まではチャットサービスにコピペして質問するか、チェックアウトしてCopilot Chatに聞いていました。
しかし、これらの断片的なソースコードはコンテキストが欠如しているため、質問に対する回答が不十分であることが多いです。
Copilot Chatも一見ローカルインデックスからいい感じに検索して回答を生成しているように見えますが、リポジトリが大きくなると実際には結構コンテキスト不足で幻覚を見ています。
これを解決するためには、検索一致部分を改善するか、もしくはコードベース全体を送信できる分だけ一気に送信することが必要であると思っていました。
その時にGeminiの200万トークンのコンテキストウィンドウのリリースを受けて、これを実現できると考えました。

面白い使い方としては「バグを探して」と頼むとコードレビューをしてくれました。
コードリーディングの目的で作ったけど、そっち方面でブランディングしていった方がいいかもしれません。

類似ツール

筆者を含め、幾らかの人達が類似のTIPSを公開していますが*2、全体エクスポートではなく質問毎に範囲指定するツールとして提供されているものは見つかりませんでした。

zenn.dev

使い方

Rustツールチェインのcargoがマシンにインストールされている必要があります。
なんでRustかというと、使いたかったからです。

それにGemini APIキーが必要です。 APIキーは、https://aistudio.google.com/app/apikeyから取得できます。
取得したAPIキーは、環境変数GOOGLE_API_KEYに設定します。

ターミナルで次のコマンドを実行します

cargo install askrepo
export GOOGLE_API_KEY="YOUR_API_KEY"
askrepo --prompt "What is the purpose of this code?" ../your-repo/src
  • [BASE_PATH] : ソースコードファイルを含むディレクトリを指定します。
  • [OPTIONS] : 以下のオプションを指定できます。
    • -p, --prompt : ソースコードに関する質問を指定します。 デフォルトは "Explain the code in the files provided" です。
    • -m, --model : 使用するGoogle AIモデルを指定します。 デフォルトは "gemini-1.5-flash" です。
    • -a, --api-key : Google APIキーを指定します。 環境変数GOOGLE_API_KEYで設定している場合は省略可能です。

注意点

ソースコードの取り扱いはご自身で判断してください。Gemini APIに送信してよいかどうか。銀行案件を自宅に持ち帰らない、とか。
またコストはGoogle CloudのGenerative Language APIのメトリクスで確認できます。

実装方法

  1. Gitリポジトリの特定のパスのファイル一覧を取得する
  2. .gitignoreを除外するため
  3. バイナリファイルを除外する
  4. {path}\t{content}\nのTSV形式にしてプロンプトに詰め込む
  5. Gemini APIにから回答を得る

TODO

  • [ ] バイナリで配布する
  • [ ] Geminiの制限を超えた時に分割して送信する
  • [ ] ストリーミングで出力する
  • [ ] セッションを作り継続的に会話できるようにする
  • [ ] .gitignoreのリスト除外できればいいのであってgitコマンドを呼び出す必要はないのでは????

コンテキストに収まらない量の時はときはGemini APIがRESOURCE_EXHAUSTE(429)エラーを返してきます。

おわりに

この記事では、著者が開発している地味なLLMコマンドラインツール「site2pdf」と「askrepo」について紹介しました。
Gemini系モデルの広いコンテキストウィンドウを活用して日々の作業を自動化することができました。

生成AI/LLMを使ったウェブサイト開発

週末にちょっとしたウェブサイトというかリンク集(?)を作った。 今回は生成AIツールをフル活用していつもより効率よく作業ができた。

生成AIツールについては日々、新しいものがヤバイすごいと宣伝されているけど、実際にどう使っているのかという情報が少ないと感じている。
なので具体的な使い方を書いてみることにした。

作ったもの

『最も重要な「最も重要なマンガ10選」10選』は「最も重要なマンガ10選」であげられている各作品を読み始めるためのリンク集です(なんのこっちゃ)。

manga-anond.lai.so

「最も重要なマンガ10選*1」は、はてな匿名ダイアリーのミームで、各投稿者が雑誌ごとに全期間を対象としておすすめ作品をあげて寸評していくというもの。
それをみて読者も感想や自分のおすすめをコメントするわいわいしたやりとりがある。
話題的に過去語りが多く、中年うけがいい。

私もいくつか知らない作品があって気になっていたのでリストを作って順番に読んでいた。
そのリストをサイトとして公開した。

このサイトはシンプルな一画面で、リストのタイトルとリンク、コメントを表示するだけのもの。
リストははてな匿名ダイアリーの記事をスクレイピングして取得し、AmazonのAPIを使ってリンク先を取得している。
リンクにアフィリエイトタグはつけていない。
というか便乗アフィサイトが出現したらいやだなぁと思ったので先に自分で作成した。 私は購入したものだけアフィリエイトタグをつけるポリシーをもっている。

開発の概要

生成AI/LLMツールを効果的に活用することで、ウェブサイト開発の効率を向上させることができた。
主なポイントは以下の通り:

  1. デザイン段階でのClaudeの活用:初期デザインの生成に Claude Artifacts を使用した
  2. コーディング作業Cursor を使用して、AIアシストによるコード修正や提案を活用した
  3. データ処理におけるLLMの活用:スクレイピングしたデータの構造化やAmazon商品データの選別にGPT-4 Turboを使用し、複雑なデータ処理を自動化した
  4. 動的・静的コードの使い分け:プロトタイピングの段階では動的なアプローチを、本番環境では静的生成(SSG)を採用し、開発の柔軟性とパフォーマンスの両立を図った
  5. 拡張性を考慮したアーキテクチャ設計:静的サイトをベースにしつつ、将来的な機能拡張に備えた構成を採用した
  6. 柔軟なデータベース設計:Firestoreを採用することで、LLMの出力結果を直接保存でき、将来的なデータ構造の変更にも対応しやすい設計とした

最終的なアーキテクチャ

カテゴリ 使用技術
デザインツール Claude Artifacts
Cursor
フロントエンド Next.js(Static Exports)
shadcn/ui
Tailwind
バックエンド
(サーバーレス)
Firebase Firestore
Vercel AI SDK
インフラ Cloudflare Workers(Static Assets)

UI開発に生成AIツールを使う

初期デザインの参考元

最初の発想時点でこれはカタログサイトになると思っていたので、似たようなマンガ系のサイトをいくつかチェックして、他にどのような構成やデザインが使われているかを確認した。
さらに、ECなどマンガ以外の類似サイトも調べて、インスピレーションを得るように努めた。
その結果、クックパッドの新デザインがTailwindを活用しており、自分の好みにぴったりで参考にしたいと感じた。
モバイルでクックパッドの画面をChromeのDevToolsで改変して機能を削減、スクリーンショットして、これを生成AIツールに渡すために保存しておいた。

デザインツールの選定と比較

ウェブサイトのコーディングを自動化する生成AIツールはいくつか選択肢がある。 今回はClaudeのArtifactsが作ったデザインをベースにした。

比較検討した主なツールと選定理由:

  1. Claude Artifacts(採用)

    • 特徴:生成物をプレビューする汎用的機能。プロンプトでshadcn/uiベースのコーディングを頼んだ
    • 選定理由:最初の生成物の品質が高かった
  2. ChatGPT Canvas(不採用)

    • 特徴:OpenAI「Artifactsっぽいやつを俺たちも提供するぞ」
    • 不採用理由:シンプルに成果物がしょぼく、自分自身でコーディングしたぐらいのレベルのものしか生成できなかった
  3. v0(不採用)

    • 特徴:Artifactsを今回のようなプロトタイプ生成に特化。コードをユーザーが変更して調整できる
    • 不採用理由:Claude Artifactsと大きな差異がなかった。モデルが同じ(claude系)であることが原因と考えられる
  4. Bolt(不採用)

    • 特徴:プロジェクト全体を生成
    • 不採用理由:部品的な取り込みが難しかった。モデルはclaude系

総じてUIのコーディングはClaudeのが得意、というのが分かった。
ツール上で細かい部分まで詰めたい場合はv0の方が良さそうだが、私は「あとはCursorさんとローカルでやりますんで」と切り上げたのでArtifactsで間に合った。
またプロジェクトの全体コードごと丸投げしたい場合はBoltもいい選択だと思う。コンポーネントのファイル分割を適切に行ってくれた。

Next.jsの利用

そして、Artifactsが生成するshadcn/uiのデザインを活用するために、Next.jsを選んだ。
shadcn/uiに加えてTailwindやradix-ui/も含まれており、これらをパツイチでビルドするためにNext.jsが最適だ(帰納的)。
ただし、この条件ならRemixを使用するのも選択肢の一つである。
Next.jsのが普及しているので構成トラブルが少ないと判断した。

Cursorを活用した開発

画面の更新作業にはCursorを使った。
Cursorの機能を活用することでNext.jsアプリのコード修正作業の効率が上がった。
デフォルトのモデル設定はClaude 3.5 Sonnetにしている。
Cursorでは、チャット上でソースコードの修正指示を出すと、diffを提案し、それをワンボタンでマージできる。
エディタとチャットサービス間をクリップボードで運搬するという人間が行うコピペプログラミングの本質的な作業をぐっと煮染めたようなUXである。
Next.jsのFast Refreshで、変更がすぐに反映されるのでフィードバックループも短縮される。
エディタがGitと連携しているので、万が一の際に気軽にロールバックできる点も安心だ。
また、Composerという実験的機能もあり、複数ファイルを横断して編集することもできるが、今回は1ファイルを中心に作業したため、活用しなかった。

データ整形にLLMを使う

スクレイピング

最初にはてな匿名ダイアリーの記事をLLMを活用してJSON形式に構造化し、データベースに保存するプロセスを構築した。
記事URLはせいぜい10個なので自分で探した。 URLを渡すと、記事のタイトル、本文、コメントを取得するスクリプトを作成した。
まず、各記事のURLから記事本文を取得し、そこからタイトルとコメントを抽出する。
次に、これらの要素をgpt-4-turbo(Vercel AI経由)を使ってJSONフォーマットに整形した。
最終的に、生成したJSONデータをFirestoreに保存する。 これを後工程で利用する。

作業の前にまずWebコンソールでこのプロンプトが機能するかを検証した。

データ抽出にはcheerioを使った。
はてな匿名ダイアリーは環境に優しいサービスなのでブラウザ上で電力を消費してビューの差分を書き換えたりしない。
そのようなサイトにはヘッドレスでブラウザと同じAPIが使えるcheerioがフィットする。

Amazonの商品データ取得

Amazonの商品データ取得には、PA API:Product Advertising API 5.0を使用した。
ASINが判明すると、その商品に対応するサムネイル画像とリンク先も確定できる。
まずデータベースに保存したタイトルを使ってPA APIで検索を行う。
その検索結果からシリーズの初巻を選ぶためにLLMに判断を任せた。
検索結果が必ずしも巻数順に並んでいるわけではないためルールベースで選ぶのは難しい。

これも実装前にまずWebコンソールで機能するかどうかを検証した。

LLMの選定

LLMにはgpt-4-turboを採用した。
Vercel AIのドキュメントに載っていたデフォルト設定*2をそのまま使用し、実際に試したところ精度に問題がなかったため、特に調整せずにそのまま取り入れた。
Vercel AIを利用しているため、精度やコストに不満があれば他社のモデルやローカルLLMに切り替えられる柔軟性もある。
APIが抽象化されているのでコードの変更もいらない。

以下にzodを使ってレスポンスをJSONに整形する方法が書かれている。

sdk.vercel.ai

コストについてはあまり気にしていなかったが、今回の作業全体で約10ドル程度に収まった。
単発の課金なので、許容範囲内だと感じた。

継続利用して料金高すぎと感じたらgpt-4-oやローカルLLMにスイッチする算段もあるので、gpt-4-turboな理由は最初の選択肢でしかない。
デフォルトがgpt-4-turboであるのはAI SDKの開発者の人によると意図はあるらしく、Toolsを使った多段の論理処理では安定する感覚があるらしい*3

動的なコードと静的なコードの使い分け

開発プロセスを効率化し、柔軟性を保ちながらも手早くサイトを立ち上げるために、動的なコードと静的なコードを戦略的に使い分けた。

TypeScriptを使わない範囲を定めた

あえてTypeScriptのコードを避ける範囲を設けることで、プロトタイピングの効率化を図った。
具体的には、データ処理のスクリプト部分などを対象とし、特にスクレイピングにおいては、最初はChromeのDevTools上で現在閲覧しているページに対して手動でスクレイピングを試した。

その結果、期待するデータが得られた段階で、最終的にNode.jsのコードに移行して自動化した。
スクリプトはJSにする、という方法はOSSプロジェクトでも自然に行われている。たとえばRemixもそうだ(remix/scripts at main · remix-run/remix)。

他にはNext.jsアプリ内のデータフェッチ関数の初期実装を進めるにあたり、まずNode.jsコンソール(REPL)でfirebase-adminを呼び出して試行錯誤し、どのコードを実行すればどのような結果が得られるかを確認した コードが複数行にわたるようになったら、.mjsファイルとしてbin/ディレクトリに配置し、組んだロジックが正常に機能するかを確認する。
この際、VSCodeではCtrl+Alt+Nで手軽に実行とデバッグができ、効率的に検証が行える。

その後、UIに渡すデータ構造が固まったら、型定義を作成し、lib/ディレクトリに移動して清書し、最終的にTypeScript化してアプリに組み込む準備を整える。
このように段階的に進めることで、いきなりpageコンポーネントと接続したコードに手をつけるよりも、データ処理の部分を確実に作り込むことができた。

静的サイト生成(SSG)の採用

この効率化の考え方をサイト設計にも拡大し、Static Exports(SSG:すべて静的なサイト生成)を採用する方針とした。
閲覧時にはすべてのコンテンツが事前に確定している状態となり、Cloudflare CDNにすべてのファイルを配置することで、アクセスの高速化を図る。
アーキテクチャが確定した段階でまずデプロイしてPageSpeed Insightsでパフォーマンスを測定し、深刻な結果が出ないことを確認した(Largest Contentful Paintは予測していた範囲だったので許容した)。
さらに、この静的サイトにはリンク集以外の機能を持たせる余地も残しており、必要に応じてNext.jsやサーバーサイドの機能を用いて動的な要素を追加できるように、デプロイ先をCloudflare Workersにした。
これは過去の開発の経験から「ペライチの静的サイトだと思っていたら、複数画面のSSR対応が必要になってアーキテクチャを作り直した」という事態を避けるためである。
このアプローチにより、シンプルながらも拡張性のあるサイト設計が実現できた。 コンテンツは静的に、アーキテクチャは動的にという考え方になる。

柔軟なデータベース設計

これは、データベースにFirestore(動的スキーマでNo SQL)を採用していることとも関係している。
一般的には、RDBを選び、JOINに耐えられるように前もって柔軟なテーブル設計を行い、free tierがあるPostgresSQLサービスなど*4を利用するのが個人開発者にとってのファーストチョイスとなりがちだ。
しかし、Firestoreを使うことで、スキーマを決める前に、先のLLMの生成結果をそのままキャッシュとして保存できる柔軟性が得られ、開発中のデータ構造の変更に対しても対応できる。
静的サイトにしたことで、ビルド時のみクエリを実行する形にして開発中は本番DBを管理画面からごちゃごちゃ操作してもデプロイしない限りはサイトに影響がない。
最初はいいけど運用に入ったら作業量のしわ寄せが来るんじゃないの? と思うだろうが、そこを静的サイトにして決定を遅延させている。
サーバーサイド処理をしたくなったタイミングでよいしょとデータをRDBに盛ってくる算段だ。

余談だが、Firestoreのドキュメント設計については1つのビューに必要なデータを1クエリで取得できるようにデータ構造を工夫するのがポイント。

まとめ

生成AI/LLMツールは開発プロセス全体を最適化し、短期間でサイトを立ち上げることができる強力な味方となる。
今回の例では通常の開発の1/2の時間でサイトを完成させることができた(当社比12時間→6時間レベル)。
私はフロントエンドは専門でないため、ClaudeやCursorが生成したコードを元に学習した。もしかしたらこれがAI世代のコード写経に変わるのかもしれない。


あと最後にマンガの話ですが、個人的最高おすすめ作品の『ザ・ワールド・イズ・マイン』は読んでください。

Anthropic Computer Useはどうやって実現されているのか?

答え:スクショからシェルのコマンドに渡す値を生成する

# 開発者が実装する!
await asyncio.create_subprocess_shell("xdotool mousedown 1 mousemove --sync 750 738 mouseup 1")

🤖「ヨシ!」

さらに詳しく

Anthropic Computer Useは、Claudeにコンピューター操作を指示するためのツールセットです。
この機能は既存のMessages APIを拡張する形で提供され、Computer tool、Text editor tool、Bash toolなどの関連ツールを含んでいます。
これらのツールにより、AIはデスクトップ環境の操作、ファイルの編集、シェルコマンドの実行など行うことが可能になります。
実装にあたっては、AnthropicがGitHub上で公開しているデモアプリの参照実装を利用できます。この実装はDocker上で動作し、StreamlitアプリケーションをユーザーインターフェースとしてClaudeとのインタラクションを可能にします。
APIの使用フローは、システムプロンプト、ツール定義、ユーザープロンプトの送信から始まります。Claudeはこれらの入力を基にツールの使用を判断し、実行指示を返します。開発者はこの指示に基づいてツールを実行するコードを実装し、その結果をAIに返します。このプロセスは、タスクが完了するまで繰り返されます。
処理の中核となるのは、ユーザーの指示に基づいてツールを実行し、結果を分析するループです。
Claudeはこのループを通じて、タスクの進捗を評価し、必要に応じて追加のアクションを実行します。例えば、ファイルの作成やテキストの書き込みといったタスクでは、複数のツールを組み合わせて使用し、各ステップの結果を確認しながら処理を進めます。

Anthropic Computer Use の概要

docs.anthropic.com

Anthropic Computer Use は、専用の API として提供されているわけではありません。
既存の Messages API に新しいツール1が追加される形で提供されます。
これらのツールは、computer-use-2024-10-22 という betaフラグを通じて有効にできます。

Messages API で使用可能なツール:

  • Computer tool (computer_20241022):
    • デスクトップ環境を操作するためのツールです。
    • マウスとキーボード操作をエミュレートし、スクリーンショットを取得できます。
    • アプリケーションの実行、テキスト編集、ファイル操作などを実行可能です。
    • 画面の解像度とディスプレイ番号を指定できます。
  • Text editor tool (text_editor_20241022):
    • ファイルの表示、作成、編集を行うためのツールです。
    • デスクトップ環境を必要とせずにファイル操作が可能です。
    • テキスト生成やコーディングなどのタスクに利用できます。
  • Bash tool (bash_20241022):
    • シェルコマンドを実行するためのツールです。
    • デスクトップ環境を必要とせずにコマンド操作が可能です。
    • ファイル一覧の取得、パッケージのインストール、コマンドの実行などが可能です。

これらのツールは Anthropic によって定義されていますが、ユーザーがツールの実行結果を評価する処理を実装して、結果をtool_results として返す必要があります。

処理の実行指示はAPIから送られてきますが、実行処理は開発者が実装します。

Messages APIとの統合:

  • 開発者は、API リクエストにツール定義を含めることで、Claude にコンピューター操作を指示できます。
  • Claude は、ツールの実行が必要と判断した場合、tool_use を含むレスポンスを返します。
  • 開発者は、tool_use レスポンスからツール名と入力を抽出し、対応するツールを実行します。
  • ツールの実行結果は、tool_result を含む新しいユーザーメッセージとして Claude に送信されます。

実装例と使用方法

Anthropic は、Computer Use API を使用したデモアプリの参照実装を GitHub で公開しています。

github.com

この参照実装は、Docker コンテナ内で動作し、Anthropic API(もしくはBedrock、Vertex) を通じて Claude 3.5 Sonnet モデルにアクセスします。

ユーザーインターフェイスとしてStreamlit アプリが含まれています。 これはチャット機能とコンテナ内のデスクトップをリアルタイムに表示する機能を提供します。

デモアプリが操作する対象のシステムはDocker コンテナ内で動作します。
コンテナ内のUbuntuデスクトップに必要な依存関係 (Ubuntu 22.04、xvfb、xterm、xdotool、scrot、imagemagick、sudo、mutter、x11vnc、Python 3.11.6 など) をセットアップします。
ここからPythonでMessages API を通じて Anthropic の Claude モデルと通信し、ツールを利用します。
起動方法については、Anthropic API を使用する場合は、ANTHROPIC_API_KEY 環境変数を設定して Docker コンテナを起動します。 Bedrock または Vertex を使用する場合は、それぞれの認証情報を設定する必要があります。

デモアプリの起動コマンド:

  export ANTHROPIC_API_KEY=%your_api_key%
  docker run \
    -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
    -v $HOME/.anthropic:/home/computeruse/.anthropic \
    -p 5900:5900 \
    -p 8501:8501 \
    -p 6080:6080 \
    -p 8080:8080 \
    -it ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest

http://localhost:8501 で Streamlitアプリ単体を。http://127.0.0.1:6080/vnc.html でVNCビュワーを。 http://localhost:8080 で Streamlit + VNCビュワーをiframeで統合した画面にアクセスできます。

VNC サーバーと NoVNC を使用した接続

デモアプリでは、コンテナ内でVNC サーバー (x11vnc) を起動し、ビュワーから NoVNC を使用して接続します。
NoVNC は、Web ブラウザ上で VNC 接続を可能にするクライアントです。
ユーザーはブラウザ上のVNCビュワーから Docker コンテナ内のデスクトップ環境にアクセスできます。
右上の「Toggle Screen Control (On)」ボタンを押すとデスクトップを操作できるようになります。

ツールでの自動実行がうまくいかない場合、デスクトップを手動で操作してデバッグすることができます。
先のとうり、 http://localhost:6080/vnc.html にアクセスすることで、単独のウィンドウで開くことができます。
また、任意の VNC クライアントからも直接 vnc://localhost:5900 で接続可能です。
このVNCビュワーについてはComputer Useに必須というわけではなく、主にデモ用+デバッグ用になります。
ユーザー向けのUIは主にStreamlitアプリ(http://localhost:8501)の方になるでしょう。

送受信フロー

送信

システムプロンプト:

Claude がツールを使用するための指示と、ツールの動作に必要なコンテキストを提供します。
ツール定義、ツール設定、ユーザー指定のシステムプロンプトから構築されます。
でもアプリでは、これはSYSTEM_PROMPTで定義されています。 このプロンプトはUbuntuの仮想環境でbashツールを使用してアプリケーションを操作し、GUIアプリケーションを起動するためのガイドラインを提供しています。

tools:

Computer toolなどの利用するツールの設定情報などをパラメータとして送信します。

ユーザープロンプト:

チャットで入力した指示と現在のスクリーンショットを送信します。

受信

Tool 実行指示の受信と実行 Claude は、ユーザーのプロンプトに基づいて、どのツールを使用するか、どのパラメータを指定するかを判断します。
tool_use という stop_reason と共に、ツール名、入力パラメータなどを含むレスポンスが返ってきます。
ユーザー (クライアントコード) は、tool_use レスポンスから以下の情報を抽出します。
name: 使用するツールの名前 id: このツール使用ブロックの一意の識別子 input: ツールに渡される入力 ユーザーは、対応するツールを実行します。

computer_20241022 ツールの例:

  • action: 実行するアクションの種類 (例: key - キーボードのキーを押す, type - テキストを入力する, mouse_move - マウスを移動する, left_click - 左クリックする, screenshot - スクリーンショットを撮る)
    • coordinate: マウス移動などのアクションの座標を指定するタプル (オプション)
    • text: 入力するテキスト (オプション)

コード例:

async def __call__(self, action: str, text: Optional[str] = None, coordinate: Optional[Tuple[int, int]] = None, **kwargs) -> ToolResult:
    if action == "key":
        # キーボード入力処理
    elif action == "type":
        # テキスト入力処理
    elif action == "mouse_move":
        # マウス移動処理
    elif action == "left_click":
        # 左クリック処理
    # ... その他のアクション

text_editor_20241022 ツールの例:

  • command: 実行するコマンド (例: view - ファイルを表示する, create - ファイルを作成する, str_replace - 文字列を置換する)
  • path: ファイルまたはディレクトリの絶対パス
  • file_text: 作成するファイルの内容 (オプション)
  • old_str: 置換する文字列 (オプション)
  • new_str: 新しい文字列 (オプション)

bash_20241022 ツールの例:

  • command: 実行する bash コマンド
  • restart: ツールを再起動するかどうか (オプション)

結果の保存

ツールの実行結果は、tool_resultを含む新しいユーザーメッセージとして Claude に送信されます。
tool_resultには、以下の情報が含まれます。
- tool_use_id: 結果が対応するツール使用リクエストの id - content: ツールの結果 (文字列, 画像, その他のコンテンツブロック) - is_error (オプション): ツールの実行がエラーになった場合は true に設定

スクリーンショットの base64:

computer_20241022 ツールの screenshot アクションでは、スクリーンショットが取得され、base64 エンコードされて tool_resultcontent に格納されます。
この base64 エンコードされた画像データは、sampling_loop メソッドを通じて Message API に送信されます。

コード例:

{
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
            "content": "15 degrees"
        }
    ]
}

処理とループ

デモアプリは、ユーザーの指示に基づいてツールを実行し、その結果を分析して、タスクが完了するまで処理をループします。 このループはユーザー入力なしでMessage APIへの送受信を繰り返します。

ループ内でのスクリーンショット取得

ComputerTool クラスの screenshot メソッドは、gnome-screenshot scrot コマンドを使用してスクリーンショットを取得し、base64 エンコードして ToolResult オブジェクトに格納します。
この base64 エンコードされた画像データは、sampling_loop メソッドを通じて Message API に送信されます。

タスクを完了したと判断する基準

Claude は、ツールの実行結果とユーザーの指示に基づいて、タスクが完了したかどうかを判断します。
例えば、ユーザーが「猫の画像をデスクトップに保存する」ように指示した場合、Claude は画像がデスクトップに保存されたことを確認してタスクを完了と判断します。

その後の処理

タスクが完了したら、Claude はユーザーに最終的な応答を返します。
応答には、タスクの実行結果や、追加の質問などが含まれる場合があります。

具体的な処理の流れの例

  1. ユーザーが「デスクトップに「test.txt」というファイルを作成し、「Hello, world!」というテキストを書き込んで保存してください。 」と指示します。
  2. Claude は、このタスクを完了するために、 bash_20241022 ツールと text_editor_20241022 ツールを使用する必要があると判断します。
  3. Claude は、bash_20241022 ツールを使用して、一度実行をしますがDesktopディレクトリが存在しないことがわかります。
  4. Claude は、bash_20241022 ツールを使用して、Desktop ディレクトリを作成します。
  5. Claude は、bash_20241022 ツールを使用して、「test.txt」という名前の新しいファイルを作成し、「Hello, world!」というテキストを書き込みます。
  6. Claude は、text_editor_20241022 ツールを使用して、ファイルの内容を確認します。
  7. Claude は、bash_20241022 ツールを使用して、ファイルの内容を確認します。
  8. Claude は、API 経由で、スクリーンショットと「タスクが完了しました。 」というメッセージをユーザーに返します。

Code Interpreter系との違い

OpenAIのCode Interpreterは自然言語のタスクについて解決するソースコードを生成して、それをプラットフォームの実行環境で実行します。
一方、Computer Useは開発者が自分で用意した実行環境のシステムを操作するためのアクション+パラメータのみを生成します。実行するコードは開発者自身が用意します。
「デスクトップを特定の順で操作する」などタスクの解決策が明確な場合においては、実行コードは開発者で用意して、可変になるパラメータのみ生成する方が柔軟で安定して運用という特性があります。
Code Interpreteについては「データを何らかの方法で分析する」などのHOWが明確でない場合に、コードで表現可能な範囲を動的に検証できて引き続き有用なので、使い分けすることができます。

現在の制限

悪用防止のためのガードが入っているためか、SNSへの自動投稿やECサイトでの商品購入は行ってくれませんでした。
またスクロールや小さい領域のクリックが必要な操作は期待どうり動かないことが多いです。
料金に関してはMessage APIに画像をbase64化したデータを添えるのでかさみがちです。
デモアプリを小一時間テストすると1〜数ドルレベルの課金が発生すると思います。

おわりに

LLMアプリ開発コミュニティでも、このような自律的AIでのPC操作を実現する試みは以前から行われていました。 AutoGPTに代表されるようなプロジェクトがその一例です。
プラットフォーム各社のAPIが画像を送信できるようになったことで、これらの試みは以前よりも実現可能性が高まっています。
そして今回のComputer Useのようなプラットフォーマー自身によってPC操作自動化のAPIが提供されたことは大きな変化です。
開発元によって最初からPC操作自動化を想定したモデルのチューニングが行われているということだからです。
このような事例は、アプリ開発の分野では「シャーロックされる(Sherlocking)2」として知られています。 のび太がひみつ道具で遊んでると横からジャイアンが入ってくるやつです。

デモ映えするのはComputer toolを使った派手なRPAエージェントかもしれませんが、個人的には実用性を考えるとText editor toolやBash toolの方が早く現実のニーズに応えられる可能性が高いと感じます。
たとえば、これらのツールを使えば、Node.js以外にも対応するDocker版の「volt.new 3」のようなシステムを作ることができるでしょう。

またComputer UseはLinuxやDockerに閉じた仕組みではないのでWindowsやmacOSの環境でも実現できます(今のところbashを中心に自動化しているのでWSLではない純粋なWindowsのサーバー環境では厳しいとは思いますが)。
その場合、システムプロンプトを「私の環境は(macOS/Windows)です。」と変更することになります。
Anthropicがいうようにサンドボックス環境でないと意図しないオペレーションが実行されるリスクが大きいため使うことはできなさそうなので、外部環境から切り離された実行専用のミニPCやクラウドのインスタンスを用意するというのも考えられます。


  1. Anthropic Tools Use Docs (Anthropic Tool Use Docs) を参照してください。
  2. The apps that Apple sherlocked at WWDC 2024 | TechCrunch
  3. Boltは内部的にはClaudeで独自XMLを生成していることが知られています→bolt.newのメッセージングプロトコルを調べた