会社のハッカソンイベントに参加して作品を作ったので紹介します。
世界の拠点からエンジニアが集まったグローバルなイベントで、開発時間は 24 時間でした。

※イベント自体は 2024 年 9 月開催で、公開許可をいただいたのでブログに書いています。

作品

DEMO 動画 (一部編集済み)

AI が生活に欠かせなくなってきている昨今、普段使っている Mac の操作も AI にやってもらったら面白いのでは?と思い、macOS を自動操作する AI エージェント「あざらしすたんと」を作りました。詳しくは DEMO 動画をご覧ください。

古の時代に “お前を消す方法”で有名なイルカ がいました。現代の AI があれば、某イルカは真に便利なアシスタントになりそうですね。
今回はデザイナーのアイデアによりイルカではなくあざらしとして生まれ変わることになりました。名前は「らしたん」です。

技術

このアイデアで行くと決めた後、チームメンバーの AI エンジニアに「LangChain Agents」というものがあることを教えてもらい、それを使って実現することにしました。

らしたんを表示したいので SwiftUI で macOS アプリを作り、内部で LangChain Agents を利用して、AI を実行しました。

自分は macOS アプリと AI エージェントに自律的に行動してもらうための仕組み の部分を実装したので、そのあたりの技術について紹介します。

デスクトップマスコットの実装

古の時代にデスクトップマスコットと呼ばれるものがありました。ウィンドウ等を表示せずにデスクトップにキャラクターを表示 (常駐) させるアプリです。
これにはタイトルバーを消す実装やウィンドウの背景を透明にする実装、最前面に表示する実装などが必要になります。

SwiftUI では下記でタイトルを消すことはできます。

1
2
3
4
5
WindowGroup {
    ContentView()
}
.windowResizability(.contentSize)
.windowStyle(.hiddenTitleBar)

タイトルなし

ただ、タイトルバーの領域自体を消したり、閉じるボタンなどを消したりするのは Xcode 15 (開発時の環境) では不可能だったため、一部は AppKit を利用しました。

1
2
3
4
5
6
func configure(_ window: NSWindow) {
    window.standardWindowButton(.zoomButton)?.isHidden = true
    window.standardWindowButton(.closeButton)?.isHidden = true
    window.standardWindowButton(.miniaturizeButton)?.isHidden = true
    window.styleMask = [.borderless, .fullSizeContentView]
}

タイトルバーなし

背景色を消して透明なウィンドウを作成したり、最前面に表示するためには下記のようにします。

1
2
3
4
window.isOpaque = false
window.backgroundColor = .clear
window.level = .screenSaver
window.isMovableByWindowBackground = true

透明なウィンドウ

isMovableByWindowBackgroundtrueにすると、背景をドラッグしてウィンドウを移動できるようになります。

AI エージェントの実装

LangChainは特定の LLM に依存せず汎用的な AI 実装を実現したり、再利用可能な実装やプロンプトのベストプラクティスが詰まったフレームワークです。

今回はその中でも LangChain Agents という AI エージェントを実装するための機能を利用しました。

AI エージェントというのは一般的に、目的を達成するために必要な行動を AI 自身が考え、自律的に選択して行動するものを指します。
これによってユーザー視点ではシンプルな命令 (プロンプト) で複雑な目的を達成することができます。

macOS の自動操作

macOS はセキュリティ的な理由から制限が厳しいです。ハッカソンの時間内で実現できる方法を考えた結果、AppleScript を利用することにしました。AppleScript はシステム設定で許可をすれば、多くの操作を自動化できます。

動作ロジックとしては以下の通りです。

  1. ユーザーの命令からLLM で AppleScript のコードを生成
  2. 生成したコードに破壊的な動作が含まれるかを LLM で判定
    ・含まれる場合はユーザーに許可を求める
  3. AppleScript を実行
  4. もしエラーが起きた場合はエラーメッセージを LLM に伝え、AppleScript を再生成し、2 に戻る

LLM 自体は macOS を操作できませんが、AppleScript のコードは生成できるので、それを実行することで AI に macOS を操作させることができます。

ただ、ファイルをすべて削除するなど危険な行為をされては困るので、生成したコードに破壊的な動作が含まれるかを LLM で判定することで、最低限の安全性を確保しました。

また、AppleScript の生成は学習データセットが少ないのか、試してみると意外とうまくいかないことが多いので、システムプロンプトなどで実装方法のドキュメントなどを記載しておく必要があります。
それでもうまくいかないことがあるので、エラーメッセージを LLM に伝えて自己修復を繰り返すことで、安定性が大きく向上しました。

ごはん中のらしたん

ごはん中のらしたん

ツールの実装

LLM でできるのはテキストを生成することですが、AppleScript の例のように生成させたものを利用して何かを実行することで様々な事ができるようになります。

OpenAI でいうと「Tool calling (Function calling)」という機能があります。これは AI に呼び出してほしい関数のインターフェースなどのドキュメントを渡すと、AI が必要なときにその関数呼び出しに必要な情報を提供してくれるようになる機能です。

例えば、「hello と書かれたテキストファイルを作成して」という命令が来た時に createFile("file.txt", "hello") というような情報を返してくれるようにできます。
AppleScript の生成と実行も LangChain Agents の Tool の仕組みを使って実現しています。

1
2
3
4
5
6
7
8
9
@tool
def run_applescript(script: str) -> dict[str, Any]:
    """AppleScriptを実行
    <いろいろな説明を記述>
    """
    # LLMで破壊的な動作が含まれるか判定
    # ...
    # AppleScriptを実行
    return _run_applescript(script)

ハッカソンの終盤は時間の許す限り Tool の追加をしました。
例えば、画像を生成する機能らしたん自身のプロフィールに関する情報を取得する機能、必要な情報をランタイムでIn-Context Learning するための機能などを実装しました。

エビフライになるらしたん

らしたんはたまにエビフライになります(?)

感想

AI エージェントは面白い!

このハッカソンで初めて AI エージェントを実装しましたが、短時間の開発でもかなり色々なことができると実感して、AI 開発の視野がかなり広がりました。

最近は LangChain Agents の後続となる LangGraph を触っていますが、複数の AI エージェントを組み合わせることでチームを組んだ人間のように、大きなタスクを達成できたりと、とても面白いです。

日々進化している AI 技術をいろいろ触ってみて、面白いものが作れないか考えていきたいです。

今回の作品「あざらしすたんと」の作品情報はここでも見れます。よかったら見てください。