タグ: gemini

  • Spec-kitの活用

    mcts-genとpy-chessboardjsにおけるSpec-kitの活用と開発ワークフロー改善の記録

    この記事では、私が最近行った2つのプロジェクトmcts-genpy-chessboardjsでの開発記録を基に、AIを活用したspec-kitツールの使い方、GitHub Actionsを用いたCI/CDワークフローの構築、そしてPyPIのTrusted Publisher設定について解説します。


    1. spec-kitのアップデートと古い設定ファイルの整理

    プロジェクトmcts-genakuroiwa/mcts-gen)において、spec-kitのバージョン更新を行いました。この際、古いファイルの削除に手間取りました。また、仮想環境管理ツールuvxを用いた初期インストール手順が、恒久的なツールの利点を享受できない原因となりました。

    🚨 古いバージョンからの移行時の注意点

    以前のバージョンでspecify initを実行していたため、新しいバージョンのspec-kitを導入する際、古いコマンド定義ファイルが残ってしまいました。

    現在のgemini-cliでspec-kitのコマンドは全て/speckit.から始まりますが、古いバージョンでは異なっていました。

    【新しいspec-kitのインストール】

    仮想環境をアクティベートした後、uv tool installで最新版を取得します。

    source .venv/bin/activate
    uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git

    【内部コマンドの更新】

    プロジェクトのルートで以下のコマンドを実行し、gemini-cliの内部コマンドを更新します。

    specify init --here

    これにより、新しいコマンドファイルが.gemini/commands/以下に作成されます。

    【新規ファイルの確認】

    git statusで確認すると、以下のような新しいファイルが追加されていることが分かります。

    ブランチ main
    追跡されていないファイル:
      (use "git add <file>..." to include in what will be committed)
        .gemini/commands/speckit.analyze.toml
        .gemini/commands/speckit.checklist.toml
        ...(中略)...
        .gemini/commands/speckit.tasks.toml
        .specify/

    .specify/ディレクトリにはmemory/, scripts/, templates/といったサブディレクトリが含まれます。

    🧹 古いファイルの削除手順

    新しいコマンドファイルと競合する古いファイルを特定し、手動で削除する必要がありました。

    1. 古いコマンド定義ファイルの削除

      cd .gemini/commands/
      rm plan.toml
      rm specify.toml
      rm tasks.toml
      cd -
    2. 古い.specify関連ファイルの削除

      rm -rf memory/
      rm -rf templates/
      rm -rf scripts/

    💡 ヒント: コマンド定義ファイルが管理対象外になるように、.gitignore.geminiを追加することも検討できます。ただし、その場合はgit statusには表示されなくなります。


    2. 既存プロジェクト(py-chessboardjs)でのspec-kit導入と運用

    既存のプロジェクトpy-chessboardjsakuroiwa/py-chessboardjs)でもspec-kitを導入しました。

    🛠️ 導入手順

    プロジェクトのルートディレクトリで、以下の手順を実行します。

    uv venv
    source .venv/bin/activate
    uv pip install -e . # プロジェクト依存パッケージのインストール
    uv tool install specify-cli --from git+https://github.com/github/spec-kit.git # spec-kitのインストール
    specify init --here # プロジェクト設定ファイルの作成
    specify --help # 動作確認

    spec-kitをgemini-cliで使用し始めると、プロジェクトルートに**GEMINI.md**というファイルが作成され、これがAIとの対話の出発点となります。

    🚀 spec-kitを利用した開発サイクル

    spec-kitのコアな使い方は、事前にGeminiにチャットで相談し、戦略を固めてからスラッシュコマンドを実行していく流れが最も効果的です。

    コマンド 目的
    /speckit.specify <文字列> 要件定義や仕様策定をAIに依頼し、**specs/**にMarkdownファイルを作成させる
    /speckit.plan 実装計画を立てる
    /speckit.tasks タスクリストを作成する
    /speckit.implement 実際にコードの実装を依頼する

    これらの作業を通して、specs/以下にMarkdownファイルが作成されます。原則として、specs/以下のサブディレクトリは作業ごとに作成されたブランチ名と同じになります。

    🔄 作業終了後のブランチマージ

    spec-kitがブランチを作成して作業を進めた後、作業をメインブランチに反映するには、以下の手順が必要です。

    git commit -m "修正内容のメッセージ" # 作業ブランチで必ずコミットする
    git checkout main
    git merge 001-fix-settings-load-error # 作業ブランチをmainにマージ
    git branch -d 001-fix-settings-load-error # 作業ブランチを削除

    3. GitHub ActionsとPyPI Trusted Publisherの設定

    パッケージの自動公開のために、GitHub Actionsのワークフローを.github/workflows/にYAMLファイルで記述します。このYAMLファイルはGeminiに依頼して作成してもらうことが可能ですが、動作確認が不可欠です。

    🚢 パッケージ公開ワークフロー

    py-chessboardjsのワークフロー例

    このYAMLファイルを使用して、PyPIのTrusted Publisherを登録します。登録時には、上記のYAMLファイル名を指定する必要があります。これにより、GitHub ActionsとPyPIが連携し、OIDC(OpenID Connect)を使って安全にパッケージを公開できます。

    🧪 テスト公開と本番公開

    タグ名によって、TestPyPIと本番PyPIへの公開を分けます。

    公開先 タグ名の条件 コマンド例
    TestPyPI -testを含むタグ名 git tag v0.0.3-test1
    本番PyPI -testを含まないタグ名 git tag v0.0.3

    タグを作成したら、git push origin <タグ名>でプッシュすることで、GitHub Actionsが起動します。

    📝 YAMLの構文チェック: Emacsではflycheck-yamllintをインストールすると、flycheck-modeでYAMLの構文エラーをリアルタイムに確認でき、大変便利です。


    4. AIとの協調作業における重要な教訓

    py-chessboardjsの開発中に遭遇した2つの不具合は、AIによる開発支援の限界と可能性を示す良い事例となりました。

    不具合1: ファイルパスの取得問題(C:\fakepath\

    現象: チェスエンジンのパス指定が機能しないエラーが発生し、出力にはFileNotFoundError: [Errno 2] そのようなファイルやディレクトリはありません: 'C:\\fakepath\\stockfish'とありました。

    解決: Google検索の結果、「fakepath」はHTMLのファイル入力フィールドがセキュリティ上の理由から実際のパスを隠蔽するブラウザの挙動であることが判明しました。この情報をGeminiに提供したところ、**「input type="file"をボタンに置き換え、pywebview.apiを介してPythonのネイティブファイルダイアログを呼び出す」**という正確な解決策が提示され、実装に成功しました。

    不具合2: キャスリング時のエラー(concurrent.futures.CancelledError

    現象: キャスリングを行うとエラーが発生しました。

    解決: ログからconcurrent.futures.CancelledErrorが見つかり、並列処理の不具合が疑われました。原因は、プロジェクトが使用しているchessboardjsの古いバージョンにあり、キャスリング時にFEN(盤面情報)が複数回更新され、チェスエンジンが処理しきれなかった可能性が高いと特定しました。

    以下はGeminiによる解説です:

    1. python-chessエンジン呼び出しの問題について

      根本的な問題は、GUIフレームワークであるpywebviewと、asyncioベースのエンジン通信を行うpython-chessライブラリとの間の並行処理の競合でした。

      • pywebviewのAPI呼び出しは同期かつ別スレッドで実行される: JavaScriptからpywebview.api.some_function()を呼び出すと、pywebviewは新しいPythonスレッドでsome_functionを実行し、その関数が戻るまでスレッドをブロックします。

      • python-chessのエンジン通信は非同期 (asyncio):
        chess.engine.popen_uci()関数やengine.play()メソッドは、Pythonのasyncioライブラリを使用して外部のチェスエンジンプロセス(Stockfishなど)と通信します。asyncioはイベントループ上でタスクを実行し、通常は単一のスレッドで行われます。

      • 競合 (レースコンディション):

        1. 以前のApi.uci_engine_move()は、await self.engine.play()を直接呼び出していました。このawait呼び出しは、エンジンが指し手を返すまでpywebviewのAPIスレッドをブロックしていました。
        2. このpywebviewのAPIスレッドがブロックされている間に、ユーザーが別のUI操作(例: 「リセット」ボタンや「戻る」ボタンのクリック)を行うと、その操作もpywebviewのAPI呼び出し(例: Api.on_closed())をトリガーしていました。
        3. 古い設計のApi.on_closed()メソッドはself.engine.quit()を呼び出し、エンジンプロセスを終了させていました。
        4. エンジンプロセスが終了すると、self.engine.play()が待機していたasyncioのFutureがキャンセルされ、concurrent.futures.CancelledErrorが発生しました。エンジンプロセスが予期せず終了した場合はchess.engine.EngineTerminatedErrorが発生しました。
        5. pywebviewのAPIスレッドはself.engine.play()の結果を待ってブロックされていたため、Futureがキャンセルされると、CancelledError(またはEngineTerminatedError)が伝播し、アプリケーションがクラッシュしたり、一貫性のない状態になったりしていました。
      • 解決策 (専用ワーカースレッド):

        • 私たちは、専用のthreading.Threadで実行されるEngineWorkerクラスを導入しました。
        • このEngineWorkerスレッドは独自のasyncioイベントループを持ちます。
        • pywebviewからApi.uci_engine_move()が呼び出されても、もはやブロックされません。代わりに、EngineWorkerが監視するqueue.Queueに「プレイ」タスクを投入します。
        • EngineWorkerはタスクを受け取り、自身の asyncioイベントループ上でawait self.engine.play()を実行します。指し手が準備できると、スレッドセーフなコールバック(_engine_move_callback)を使用して結果をApiクラスに返します。
        • Apiクラスはself.window.evaluate_js()を使用して、指し手をJavaScriptのフロントエンドにプッシュします。
        • なぜこれで解決したのか: pywebviewのAPIスレッドはasyncio操作によってブロックされなくなりました。asyncioイベントループは独自の安定したスレッドで継続的に実行されます。エンジンを終了させる可能性のあるUI操作は、EngineWorkerのキューに「終了」タスクを送信するようになり、ワーカースレッドはエンジンとそのループを安全にシャットダウンできます。計算中に「終了」が受信されても、ワーカースレッド内でasyncioタスクがキャンセルされるだけで、pywebviewのAPIスレッドには伝播せず、クラッシュを防ぎます。Engine move discardedというメッセージは、この正常な処理を示しています。
    2. specs/002-fix-castling-errorの削除について

      削除したのは良い判断でした。002-fix-castling-errorブランチとその関連する仕様ファイルは、バグ修正の失敗した試みを表していました。それらを残しておくと、誤った情報や置き換えられた情報でリポジトリが煩雑になります。

      フィーチャーの試みが失敗したり、新しいアプローチに置き換えられたりした場合は、古いブランチとその成果物をクリーンアップするのがベストプラクティスです。これにより、プロジェクトの履歴がクリーンに保たれ、成功した作業や関連する作業のみが反映されます。

      新しい003-fix-engine-concurrencyブランチとその仕様は、根本的な並行処理の問題を解決するための現在の成功したアプローチを正確に反映しています。

    💡 教訓: ユーザーの根気強さが成功の鍵

    これらの事例から学んだのは、Geminiでも不具合の特定は難しく、ユーザーとの共同作業であるということです。特に、エラー出力や挙動を注意深く観察し、根気強くAIにフィードバックすることが、適切な解決策を導き出すために不可欠となります。AIは強力な助けとなりますが、最終的なデバッグと検証は人間の役割です。

  • mcts-gen

    mcts-gen:AIエージェント統合型 次世代MCTSフレームワーク

    先日開発した mcts-gen は、標準的なUCT(Upper Confidence Bound 1 applied to Trees)アルゴリズムにAIエージェントを統合した、汎用的なMCTS(モンテカルロ木探索)フレームワークです。

    従来の chess-ant がGenetic Programming(GP)ベースであったのに対し、mcts-genは設計を根本から見直しました。Policy(方策)やValue(価値)の予測を外部AIエージェント(例えばgemini-cli)と連携させることで、探索効率と応用範囲を大幅に拡張しています。

    FastMCPによるAIエージェント連携

    このプロジェクトの核となるのは、FastMCP を介したAIエージェントとの通信です。mcts-genの探索エンジンは、MCP (Model Context Protocol) を通じて外部のAIエージェントと通信し、局面に応じたポリシー予測や価値評価を受け取ります。

    特に spec-kitgemini-cli を組み合わせ、AIをバックエンドとしたMCTSサーバーを生成・構築できる点が大きな特徴です。これにより、高速で柔軟な探索環境が実現します。

    未知領域探索への応用とPolicy Pruning

    MCPサーバーの導入により、このフレームワークは学習済みデータに基づく既存の知識空間だけでなく、未経験・未知の局面への探索も可能としました。

    AIエージェントによるPolicy Pruning(有望手の絞り込み)機能により、広大で非効率になりがちな探索空間の中から、効率的に有益な手だけを選び出して深く探索できます。これは、複雑な問題や新しいゲームへの応用において非常に強力なアドバンテージとなります。

    spec-kitワークフローの注意点(教訓)

    spec-kitのような高度な開発ツールを使用する際は、ワークフローの順守が不可欠です。

    spec-kitでは、/speckit.specify, /speckit.plan, /speckit.tasks といったスラッシュから始まるコマンドを実行すると、作業内容がMarkdownファイルに自動で記録され、新しいGitブランチも作成されます。その後、メインストリームへのマージ作業を行うのが基本の流れです。

    私の場合、コマンドを実行せずに直接GeminiにMarkdownファイルの更新を依頼したため、過去の作業記録が上書きされて失われてしまいました。これはGeminiの動作によるものではなく、規定の手順を踏まなかった私の操作ミスが原因です。ツールが意図するワークフローを守ることの重要性を改めて痛感しました。

    uvによるgemini-cliとFastMCP管理

    gemini-cliおよびFastMCPの公式推奨設定例 Gemini CLI 🤝 FastMCP では、uvコマンド による環境管理が原則とされています。

    uvはPythonパッケージの仮想環境管理ツールで、極めて高速かつクリーンな依存関係管理を可能にします。Rustで書かれており、従来のパッケージ管理ツールが抱えていたパフォーマンスの問題を解決します。

    Ubuntuでのuvインストールと使用法

    mcts-genプロジェクトでuvを利用するための基本的な手順は以下の通りです。

    pipxのインストール:

    sudo apt install pipx

    uvのインストール:

    pipx install uv

    基本的な利用方法。プロジェクトディレクトリで以下のコマンドを実行します:

    uv venv
    source .venv/bin/activate
    uv pip install mcts-gen[shogi]
    fastmcp install gemini-cli .venv/lib/python3.12/site-packages/mcts_gen/fastmcp_server.py:mcp

    コマンドを実行すると .gemini/settings.json が生成されます。
    因みに末尾の :mcpfastmcp_server.py にあるオブジェクト名です。オブジェクトが2つあるためか、自動でFastMCPサーバーのオブジェクトが認識されないので指定しなければなりません。run() メソッドで実行されます。
    仮想環境を終了するには deactivate コマンドを使います。

    マニュアルにある AGENTS.mdv0.0.2 で廃止しコード化しました。AIに指示を追加する場合のみcontextの設定を .gemini/settings.json に追記してください。


    mcts-genは、探索手法にAIの力を統合したいと考えるすべての開発者にとって、有力な選択肢となるはずです。詳細やセットアップ手順は、ぜひ公式ドキュメントも参考にしてください。

  • Gemini-JSCAD-Tool: GeminiとチャットしながらCAD開発する新しい体験

    概要

    gemini-jscad-tool は、Gemini CLI の拡張として開発されたツールで、JSCADを用いた3Dモデル生成を対話的に実現します。Geminiとチャットしながら、3Dモデルを作成し、リアルタイムにプレビュー&STLファイルに変換できます。

    特徴

    • 🔄 Geminiと自然言語でやり取りしながら設計できる
    • 🧊 JSCADモデルを作成し、STLへ変換
    • 🌐 ローカルのライブプレビューサーバーで即座にSTLの形状を確認可能
    • ⚙️ Gemini向け拡張機能の実装例としても活用可能

    インストール方法

    npm install -g gemini-jscad-tool

    インストール後、自動的に以下が行われます:

    • gemini-jscad-server コマンドがグローバルに配置される
    • gemini-extension.jsonGEMINI.md~/.gemini/extensions/jscad/ にコピーされる

    使用方法

    以下のように、Geminiとの会話ベースで操作を進めます。

    1. プレビューサーバーを起動

    あなた: Start the preview server.

    http://localhost:3000 にアクセスしてプレビュー。

    2. モデルを作成

    あなた: Create a 20mm cube.

    Geminiが自動的に .jscad ファイルを生成しSTLに変換後、プレビューサーバーに反映。

    3. サーバーを停止

    あなた: Stop the preview server.

    不要なプロセスの残留を防ぎます。

    STL変換について

    生成されたJSCADモデルは、ツール内部の処理を通じて STL形式に変換され、3Dプリント等にそのまま利用可能です。

    Gemini CLI拡張機能の開発に関して

    当パッケージは、Gemini CLI Extension 開発ガイドに則って作成されています。

    gemini-extension.json の基本構造とMCPサーバー登録

    拡張子 .json のこのファイルは、拡張(extension)ごとに最低1つ必要です。
    MCPサーバーの設定例は以下のようになります。
    因みにgemini-jscad-toolにはMCPサーバーは使用していません。後述の GEMINI.md で呼び出しを定義しています。

    マニュアルからの引用:

    {
      "name": "my-extension",
      "version": "1.0.0",
      "mcpServers": {
        "my-server": {
          "command": "node my-server.js"
        }
      },
      "contextFileName": "GEMINI.md",
      "excludeTools": ["run_shell_command"]
    }

    各項目の説明

    項目 必須 内容・役割
    name 拡張ディレクトリ名と同じにします。拡張の一意名識別子です。
    version 拡張のバージョン。任意の文字列(例 "1.0.0")。省略不可。
    mcpServers この拡張が使うMCPサーバーの "名前: 設定" マップ。各サーバーは必ずcommandプロパティが必要です。
    contextFileName 拡張についてAIへ与える文書ファイル名(通常はGEMINI.md)。省略可能。
    excludeTools Geminiに読み込ませたくないツール名列挙。(例 ["run_shell_command"]/["run_shell_command(rm -rf)"])省略可能。

    mcpServers の詳細

    MCP(Model Context Protocol) に準拠したサーバーの登録ができます。
    これにより、Geminiはサーバーを正しく起動・停止し、会話の中で状態管理することができます。

    mcpServers はオブジェクト形式で記述し、
    「サーバー識別名: 設定」 の形式でサーバーを複数登録できます。

    最小構成例:

    "mcpServers": {
      "my-server": {
        "command": "node my-server.js"
      }
    }
    • my-server:好きな識別名(ユニーク推奨)。
    • command:起動方法。実行ファイルやNodeスクリプト等を記述。

    コマンドにオプションや環境変数を指定したい場合

    因みに、 settings.jsonにもMCPサーバーを登録できます。
    非推奨パッケージになった @modelcontextprotocol/server-github
    設定例が書かれた記事も参照してください。

    マニュアルからの引用:

    "mcpServers": {
      "myPythonServer": {
        "command": "python",
        "args": ["mcp_server.py", "--port", "8080"],
        "cwd": "./mcp_tools/python",
        "timeout": 5000
      },
      "myNodeServer": {
        "command": "node",
        "args": ["mcp_server.js"],
        "cwd": "./mcp_tools/node"
      },
      "myDockerServer": {
        "command": "docker",
        "args": ["run", "-i", "--rm", "-e", "API_KEY", "ghcr.io/foo/bar"],
        "env": {
          "API_KEY": "$MY_API_TOKEN"
        }
      }
    }
    • args → コマンドライン引数
    • env → サーバープロセスの環境変数
    • cwd → サーバを起動する作業ディレクトリ
    • timeout → このMCPサーバーへのリクエストのタイムアウトをミリ秒単位で行います

    contextFileName について

    gemini-extension.json の説明に戻ります。
    この拡張のガイドやAI連携ガイダンスを含むMarkdownファイル名です。
    省略時は拡張ディレクトリ内の GEMINI.md が自動検出されます。

    excludeTools について

    Gemini CLIが利用可能なツールで、本拡張では“除外”したいものを配列で指定します。

    例:

    • すべてのシェルコマンド実行を除外
      "excludeTools": ["run_shell_command"]
    • 特定の危険コマンドのみ除外
      "excludeTools": ["run_shell_command(rm -rf)"]

    設定の優先順位と適用範囲

    • 個人設定(~/.gemini/extensions/)よりワークスペース直下(/.gemini/extensions/)の拡張が優先されます。
    • MCPサーバー設定はsettings.jsonの内容が同名の場合はそちらが上書きします。

    よくある誤り

    • mcpServersは必ずマッピング形式(オブジェクト)で書く。[マニュアル例と食い違う原因の多く]
    • サーバーごとに必ずcommandプロパティが必要

    GEMINI.md の役割

    GEMINI.md には、Geminiが拡張機能と対話する際のワークフローやコマンド定義がMarkdown形式で記述されています。例として下記のように構成します:

    # jscad Extension
    
    ## Commands
    
    ### Start Server
    User: Start the preview server.
    Gemini runs: gemini-jscad-server
    
    ### Create Model
    User: Create a 20mm cube.
    Gemini writes: cube({size: 20})
    
    ### Stop Server
    User: Stop the preview server.
    Gemini stops: jscad_preview

    これにより、Geminiは自動的に動作フローを把握し、ユーザーの自然言語要求をコード生成やファイル操作にマッピングします。

    コード・リソース

    おわりに

    gemini-jscad-tool は、AIとの自然な対話3D設計と即時プレビューの連携を実現した次世代のCLI拡張です。Gemini CLI拡張の実践例としても最適なので、カスタム開発の参考にもご活用ください。

    3D設計をもっと気軽に、もっと賢く―
    Geminiと一緒なら、それが可能です。