最近作ってる地味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系モデルの広いコンテキストウィンドウを活用して日々の作業を自動化することができました。