5.1さらうどん

@giginetの技術ブログ。ゲーム開発、iOS開発、その他いろいろ

AIコーディングツールのClineでGitHub Actionを書かせてみた

前回の記事

GitHub ActionをAIに書かせる

前回紹介したgithub-action-artifactbundleだが、コードのほとんどをClineを用いてAIに書かせている。この記事ではその過程について紹介したい。Actionの詳細については前回の記事をどうぞ。ちなみにこの記事は人間が書いている。

プロンプトエンジニアリングの到達点

ClineはVS CodeにAIエージェントを追加するエディタ拡張だ。プロンプトを与えるだけで、設計、実装、テストの実装、動作確認を一気通貫でやってくれる。

コードの理解と編集だけではなく、パーミッションを与えればコマンドラインの実行も可能。テストの実行をCLIから行い、出力を見て良い感じに修正を加えるという一連の流れを半自動化できる。プロンプトに応じて差分が来るので、Approveすると反映され、直してほしい場合はコメントする。良い感じに任せる場合は自動承認にもできる。

差分をポチポチマージするだけ

感覚としては、優秀なインターン生がステップ毎にPRを出してきてそれをレビューしてマージする感じ。

プロンプトとしては大体これぐらい具体的な指示を出していた。全体の設計や仕様はかなり固めた状態で指示することで、驚くほど意に添った実装をしてくれる。

フォーマットを突っ込むだけでなぜか実装された

特定ドメインに対しての類推能力も高い

今回実装したGitHub Actionはビルドシステムを扱うものでなかなか専門性の高い領域。そんな中でも、驚くほど高い洞察力を発揮していた。

特にギョッとしたのは、Swift Packageのビルドディレクトリの構造から、経験的にtriple*1を判定する処理だ。Clineがこのような実装を提案してきた。

めちゃくちゃ本質的な気づきとコメント

この実装に至るには多くのドメイン知識が必要になる。通常、Swift Package Managerによるビルドではビルド成果物は.build/arm64-apple-macosxなど、tripleを表すディレクトリ以下に出力される。一方でUniversal BinaryのビルドはXcodeビルドエンジンしかサポートされていため、必ず./build/apple以下に入る。

これはハックに近い経験則だ。当然、このような込み入った事情はプロンプトでは説明していないが、これを考慮したコードを出してきて驚いた。他の指示や全体の設計からこの結論に至ったのかもしれないが、大した洞察力だと思った。全体的にドメインに対しての理解力が異次元。

人間くさいテスト工程

今回のプロダクトは外部の入出力が多く、密結合だが、テストケースの実装の精度にも舌を巻いた。網羅性や、fixtureの策定と言った部分はほぼ完璧に近かった。

これぐらいの指示で複雑なE2Eテストも書いてくれる

まずテストケースを書き、実行、エラーを見て修正するというまさに試行錯誤の様子が見て取れた。一方で延々と同じような修正を繰り返して詰まってしまい、人間が出動する場面もしばしば。

特に今回はテストフレームワークJestの使い方という本質的ではない部分に弱く、ES ModuleとJSCommonモジュールの区別をせずにstubに失敗し続けていた。結局、人間が不慣れな領域ながら勉強して直させていただいた。ESMはAIにも早すぎる。

READMEや設定ファイルも自動生成

OSSを作る中で、この辺のドキュメンテーションは楽しくもあり、機能追加の度にドキュメントを追従するのは面倒。そんな中、READMEの自動生成は役だった。

意外と面倒で侮れない

設定ファイルなどのDSLはAI生成をするのに恰好の題材だと思う。毎回必要なときに調べて書いていたlinterの設定や、GitHub Actionのメタデータなどは「こういうことしたいから良い感じに頼む〜」と指示するだけで、大体意図通りの物が出てきた。

matrixの文法とか何回書いても覚えられない

AIバックエンドとモデル

最初は特に考えずにOpenAIのgpt4o、部分的にo1-miniを使っていた*2が、コンテキストの肥大化と共にみるみる金が溶けていってやめた 💸 最終的には1クリック50セントを超えることもあった。OpenAIはメチャクチャ高い。

結局、最終的にはClaude 3.5 Sonnetを使っていた。こちらはとてもリーズナブルで、高くても1クエリ10セント未満に収まる。質も全く問題なし。ただしAPI Limitが結構厳しく、課金によってTier3ぐらいまでレベリングしないとエラーが頻発してストレスフルだった。どう転んでもみなさまの人件費より安くつくので安心して課金しよう。

Claudeはチャットエージェントとして使うにはクセが強いが、APIバックエンドとして使うにはとてもお値打ちだ。

あくまで「優秀なインターン」

ここまで見てもらえたとおり、素晴らしい開発者体験ではあるが、まだまだ課題はある。

例えば、コードリーダビリティには大分難がある。AIは非常に高い読解力を持ち合わせているが、その基準で書かれたコードを人間が平易に理解するのは難しく、多重ループや冗長な処理、異様に長いメソッド、型の緩さなど、自動生成っぽさが目立つ。

基本的に途中はおまかせしていたので、把握し切れていないという理由もあるが、いざ自力で読んで手を加えようとするとなかなか厳しいコードだった。今回はプロンプトの段階から具体的な設計を与えていたが、0から考えてもらうとさらにこういった面が顕在化するのだろう。

ツール自体の細かな使い心地が悪い点もあり、まだまだ荒削りだ。例えばちょっとした命名の変更などに使うには大げさ。手でやった方が早い修正でも、コンテキストの途中で下手に手動での修正を加えると、後続の指示で先祖返りが発生することもある。

また、1ファイルずつ読み込んで修正するという性質から、一括置換やまとめてデバッグログを消してくれと言ったプロジェクト横断的な修正には向いていなさそうだった。

ウィンドウサイズの限界もあり、プロダクト完成までに1つのコンテキストを維持するのが困難という点も大きい。タスク毎に新たに状況を思い出してもらわなくてはならない。

とはいえ、この辺は時間が解決する問題だろう。

あとちょっとつれない!

早く仕事を奪ってほしい

破壊的イノベーションと断言できるほど強力なものが登場したなという印象。ドキュメンテーションやテストfixtureの準備というつまらない部分は任せ、本質的な設計や試行錯誤に時間を使えるのは素直に良いなと思った。

というか、ワンクリックで実装やテストが出てきて、目的が大体達成される楽さに脳がやられる。プロダクト1つ作りきった頃には、実装が面倒で、簡単な作業含めて、大部分をAIに丸投げするジャンキーと化してしまった。

しかし、全体的にお決まりの「AIに仕事を奪われる」状態にはならずに、まだまだ踏みとどまれると思っている。

結局、プロダクトの仕様決めや設計は熟達した経験が必要になるので、現状全部おまかせはなかなか難しいと感じる。また、UIを作るような仕事はあんまりしないので評価しづらいが、ユーザーフェイシングな部分に近いほど人手が必要になるところが増えていくだろう。

とはいえ、最近の急速な環境の変化を見ているに、1年後はどの水準まで到達しているか予想がつかない。引退の日も案外近いのかも。

*1:アーキテクチャやプラットフォームの組を表す文字列

*2:o3-miniはまだ使えていない

Swift PackageのArtifact Bundleを簡単に生成するGitHub ActionをAIで作った

github-action-artifactbundle

というGitHub Actionを作った。これでAppleの規定するバイナリ配布形式、Artifact Bundleに添ったアーカイブを簡単に作成できる。

Artifact Bundleは、本来、Package Pluginなどの仕組みでのexecutableの配布を想定したフォーマットだが、@mtj0928の開発したexecutable管理ツールnestもArtifact Bundleを想定しているため、この形式で配布しておくとユーザーの利便性が高い。

Artifact Bundleはアーキテクチャやプラットフォームごとのバイナリをzipに詰め、メタデータとしてJSONを添付するだけの簡易なフォーマットだ。一方で、簡単に作る方法がなく配布がとても面倒。このActionを使えば、Swift Packageからビルドしたバイナリを簡単に配布できる。

on:
  release:
    types: [published, edited]
name: Upload Artifact Bundle to Release
env:
  DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer'
jobs:
  release:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - name: Build Universal Binary
        run: swift build --disable-sandbox -c release --arch arm64 --arch x86_64
      - uses: giginet/github-action-artifactbundle@v2
        id: artifactbundle
        with:
          artifact_name: myexecutable
      - name: Upload Artifact Bundle to Release
        run: |
          BODY="${{ github.event.release.body }}"
          BUNDLE_PATH="${{ steps.artifactbundle.outputs.bundle_path }}"
          SHA256="${{ steps.artifactbundle.outputs.bundle_sha256 }}"
          TAG_NAME="${{ github.event.release.tag_name }}"
          gh release upload "${TAG_NAME}" "${BUNDLE_PATH}"
          NEW_BODY="$(printf "%s\n%s" "$BODY" "$SHA256")"
          gh release edit "${TAG_NAME}" --notes "${NEW_BODY}"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ビルドや配布の責務は持たないこととして、予め用意したバイナリを指定のフォーマットに圧縮するだけの設計にした。

Artifact Bundleの仕様上、バイナリであれば特にどの言語で書かれたものかは問わないが、このアクションでは割り切ってSwift Packageからのビルドにしか対応していない*1。Swift Packageのビルドディレクトリ構造から経験的にバイナリを探したり、アーキテクチャを特定している。

SHA256ハッシュの計算もできる。サンプルのようにReleaseトリガーや、ghと併用すれば、リリース作成時に簡単にアーカイブとハッシュ値を公開できる。

Static Linux SDKサポート

Swift 6.0から、macOSのみでLinux向けのSwiftバイナリをビルドできるようになった。このActionはこの仕組みによる成果物にも対応している。

Swift.org - Getting Started with the Static Linux SDK

macOS Runnerさえあれば、macOSとLinux両方に対応したバイナリ配布が容易にできる。これによって、macOSのいらないSwiftで書いたツールをLinuxで動かしたりもできる。

TypeScriptで書いた

GitHub Actionには、Dockerアクション、複合アクション、JavaScriptアクションの3つの形式があり、その気になればどの言語でもActionを実装できる。

とはいえ、今回のケースでは、まずmacOSでの実行を最優先に考えなくてはならない。残念ながらmacOS RunnerではDockerアクションは実行できないので、これで多くのインタプリタ言語が候補から消える。*2

もちろん、Swiftの採用は有力候補だったが、ビルドが必要な言語はビルド時間のオーバーヘッドが気になる。バイナリを配布すれば良いが、バイナリ配布のツールでバイナリ配布を考えなければいけないのは世話がない。

結果的にほぼJavaScriptアクションしか選択肢がなかったので、TypeScriptを選択した。

TypeScriptが書けないのでAIで書いた

TypeScriptはたまに必要があれば書いていて、過去にdenopsを使ったNeovimプラグインを書いたりしていたけど、あまり日常的に使っていないので不慣れ。node全くわからん。

というわけで、今回はほとんどをAIコーディング拡張のClineに任せて書いてみた。というか、どちらかというと因果関係が逆で、AIによるコーディングをちゃんとしてみたかったので、題材としてGitHub Actionを作ってみたというのが正しい。

結果的に、AIの頭の良さに感激した*3。今回のようなかなり尖ったドメインでも十二分にコーディングができることが実証できた。

今回の話はここからが見どころだけど、長くなるので次の記事で。

Artifact Bundleを配布しよう!!!

このツールで世の中のSwift製ツールのArtifact Bundle対応が進めば、全部nestでビルド済みバイナリとして入れられるようになって幸せ。どうぞご利用ください。

*1:action.ymlの仕様が貧弱で複雑な設定項目が難しかった

*2:下流ジョブにしたり、システムインタプリタで実行するなどやりようはあるけど・・・・・・

*3:頭の悪い感想しか出ない

macOSのβアップデートに失敗して吹き飛んだ話

まとめ

  • メインマシンをmacOS 15.3 Betaに軽い気持ちで上げたら文鎮化
  • データは全て吹き飛んだが、Time Machineがあって事なきを得た。マジでバックアップとろう
  • Apple Careも入ろう

macOSのβアップデートに失敗して吹き飛んだ話

メインマシンにmacOSのβ版を入れて運用していた。近年のmacOSは安定しているし、特にSequoiaにおいては、正式版リリース後もApple IntelligenceやImage Playground、ChatGPTサポート、Apple Vision Proのワイドディスプレイ化などは最新のマイナーバージョンにのみ提供されているため、βを使いたい理由も多かった。

僕の周りにはmacOS/iOSなどのAppleプラットフォーム開発者が多く、会話の中でわりとメインマシン*1にもβ版を入れている人が多かった。僕もまあ大丈夫やろ、と思って、ここ2〜3年はメインマシンにもβ版のmacOSを入れて使っていた。

5日ほど前にmacOS 15.3 Beta 2が配信された。すでに入っている15.3 β1からβ2への更新なので、特に疑問もなく空き時間にアップデートを選択した。しかし、次の日の朝になってもインジケーターがハングしていた。

どうにもならないので電源を切ってしまったところ、ヤバそうな画面が出て文鎮化。

DFUモードからの復活

Macのファームウェアを復活させる/復元する方法 - Apple サポート (日本)

この状態になると、2台のMacを使って、生きている方からファームウェアの修復パッチを流し込む「復活」を行わないとダメらしい。上記のガイドに従ったが、「復活」が上手く行く気配は無し。終わった 😇 この時点でデータロストを覚悟したが、家庭内で運用しているNASを調べてみたところ、年明け以降にバックアップが走っていることが判明。事なきを得た・・・・・・。

結局、自分ではどうにもならないので渋谷Apple StoreのGenius Barに行った。かなり込み入った状況らしくて、店舗のConfiguratorでも初期化が失敗し、このままだとロジックボード交換になるかもと言われた。入ってて良かったApple Care+*2

結果的に二の矢、三の矢を試し、2時間ほど格闘してもらい、ソフトウェアの対応だけで初期化できた。かなり重い症状を引いたっぽい・・・・・・。

β版を入れていたらサポート対象外になるかと思っていたが、かなり丁寧に対応してもらえて印象がとても良かった。Genius Barに行ったのはおそらく10年ぶりぐらいだけど、特にサービスの質は悪くなっていなかったし、都内でも予約がすぐ取れた。

Appleはオンラインでのやりとりは最悪なことが多いが、対面コミュニケーションはかなりちゃんと体験設計されていて、素直に凄いなと思った。

データの吹き飛ばしから、Apple Storeに端末を預けたり、Time Machineで復旧したりで3日ほどかかった。

Time Machineからの復元だけで12時間ぐらいかかってた

バックアップはマジで取ろう

βのバックアップからちゃんと復元できるかを心配したが、警告は出たが問題なく復元できた。多くのアクティブなデータやプロジェクトはiCloudやGitHubで管理しているため、データが吹き飛んで致命的になることは少なくなったが、検証用のコードなんかはいちいちリポジトリを作っていなかったりするので、維持されたのは助かった。 この機会に古いデータを保全するなど、データを整理するきっかけになった。

2年ほど前にTime Machine用のSSDをいちいち繋ぐのが面倒だなと、NASを導入しておいたのが今になって生きた。転ばぬ先の杖。

MacのTime Machineによるバックアップ!HDD、SSD、NASのおすすめポイント | アイ・オー・データ機器 I-O DATA

このI-O DATAの製品を使っているが、家のネットワークに繋いでおくだけで勝手にバックアップが取れるので、心配な方はすぐに導入した方が良い。

僕は20年以上Macを使っていて、今ではかなり古参だと自負しているが、ここまでの症状に当たるのは記憶になかった。慣れすぎているからこそ、β版をメインマシンに入れるのは危ないという当たり前の意識が欠如していた。言うまでもないことですが、β版はメインマシンに入れないでください

*1:iPhoneにも!

*2:筆者はM3 Max 128GBという80万ぐらいするマシンを使っている