ホーム » 「Gemini」タグがついた投稿

タグアーカイブ: Gemini

システム

最近の投稿

アーカイブ

カテゴリー

バイブコーディングによる越前焼の試作システム

ジュニアドクターにて越前焼の試作システムを作りたい

ジュニアドクター(2025)にて指導している中学生の生徒さんから「越前焼の試作システム」を作りたいといった要望があり、Google Gemini を使ったバイブコーディングによりシステムを作ってもらった。

最初に基礎的なJavaScriptの説明を行った後、THREE.js だと3次元表示を簡単に作れる…といったことが分かったので、プロンプトに与える内容などの経過を見せながら、システムを作っていってもらった。

今回 改めて、バイブコーディングの説明のために、システム構築までのプロンプトを再現してみた。

THREE.js を用いた立体表示

VSCode に、Gemini Code Assist などをインストールした環境でプログラムを作成する環境を構築。

  • JavaScript と THREE.js を用いて、立方体を表示する index.html を作って。
    • 立方体を表示する index.html ファイルを作成しました。このファイルをウェブブラウザで開くと、緑色の立方体が回転しているのが確認できます。
  • 立方体の影が解るように、照明を追加して。
    • index.html ファイルが照明と影を含むように更新されました。以下の変更を加えました:
      1. レンダラー: 影マッピングを有効にし、より柔らかい影のために影マップタイプを THREE.PCFSoftShadowMap に設定しました。
      2. カメラ: 影がより良く見えるようにカメラの位置を調整しました。
      3. 立方体: マテリアルを THREE.MeshPhongMaterial に変更して光に反応するようにし、castShadow を有効にしました。
      4. 光源: 影を落とす THREE.DirectionalLight を追加し、その影のプロパティを設定しました。
      5. 平面: 立方体の下に影を受け取る水平な平面を追加しました。ウェブブラウザで index.html ファイルを開くと、照明と影のある立方体を確認できます。
    • https://tsaitoh.net/~t-saitoh/jd2025/step1/

マウス操作

  • 自動で立体が回転する処理を取り除いて、マウス操作で視点を変更できるようにして。
    • index.html を修正し、立体の自動回転を停止した上で、マウス操作(左クリックで回転、右クリックで平行移動、スクロールでズーム)による視点変更を可能にしました。主な変更点は以下の通りです:
      1. OrbitControls の導入: THREE.OrbitControls ライブラリを追加し、マウスでの直感的なカメラ操作を可能にしました。
      2. 自動回転の削除: animate 関数内にあった cube.rotation.x および y の加算処理を取り除きました。
      3. 慣性(Damping)の有効化: 操作に滑らかさを持たせるため、controls.enableDamping = true を設定しています。これで、マウスを使って自由に視点を動かせるようになっています。
    • https://tsaitoh.net/~t-saitoh/jd2025/step1-1/

越前焼のシステム

THREE.js を使ったシステムを簡単に作れることが確認できたので、越前焼の断面の座標をベースに回転体を表示するプログラムを作らせてみる。

  • JavaScript の THREE.js を用いて、回転体を表示するプログラムを作りたい。
    回転体の断面図の座標は、5点からなり、
    X0=(0,0),X1=(4,0),X2=(6,2),X3=(7.5,5),X4=(8,8)とする。X5=(4,-1)として、
    X0,X1,X2,X3,X4を直線で結んだ線と、X1,X5を直線で結んだ線で、Y軸を中心とした回転体を表示するプログラムを作成して。

  • 自動で立体が回転する処理を取り除いて、マウス操作で視点を変更できるようにして。
    • https://tsaitoh.net/~t-saitoh/jd2025/step2-1/
    • プロンプトに一度に作りたいものを指導しても、細々としたところで違ったものができる。
      X0~X4,X1,X5を結んだ直線と伝えたけど、X0~X5を結ぶ閉曲線で表示されている。

      • X0~X4を結ぶ直線とX1,X5を結ぶ直線で回転体を表示するように修正を指示。
  • 画面を2つに分けて、左側に3次元形状、右側に断面図を表示するようにして。
  • 断面図の点X1,X2,X3,X4,X5の座標をマウス操作で変更できるようにして。
  • X0,X1,X2,X3,X4の直線をスムージングして。

作りながら、最終的にはこのデータで 3D プリンタで出力させたいとのことで、厚さの変更や、3D形状データのダウンロード機能の追加などを指導する。

この後、指導した生徒さんが、お父さんの指導のもと、こちらから途中で色々なアドバイスも与えて、改良した最終形が以下の作品。

3Dプリンタで出力

プログラムの作成にあたり与えた座標が X0=(0,0),X1=(4,0),X2=(6,2),X3=(7.5,5),X4=(8,8),X5=(4,-1) だったけど、印刷したらすごく小さく3D印刷された。指の先にのるサイズ。このままだと単位が mm みたい。ということで、実際の印刷時には 7倍サイズに拡大して3D印刷している。

AI利用での様々な制限

ジュニアドクターでの指導にあたりコーディングには Gemini を使ってもらったけど、生徒さんのアカウントでは「13歳未満は Gemini は使えない」という制限があるため、指導に協力してもらったお父さんのアカウントを使いながらの作業となった。

今回、バイブコーディングの説明用に Gemini を使って途中経過のページを改めて作成したけど、Gemini も進化して Gemini3 とかが使えるけど、無課金状態なので「1日当たりの使用制限を越えた」とかで、途中で Gemini 2.5 Flash-lite などに切り替えたが、これでも上限越え。最後のスムージングなどMicrosoft Copilot 切り替えて作成している。Claude Code は、同じ Google アカウントに紐づいているからか、最初から課金してね…って言われたぞ。

Gemini for Home アップグレード

Gemini for Home の早期アップグレードが届いたことだし適用してみる。

完成度としては今ひとつという噂もあるけど。

2026-04-13追記

「OK google, テレビを消す!」と命令すると「テレビとチューナーどちらを消しますか?」と、チューナーがテレビの属性となっているので、余計な質問が増えた。連動設定してあるから、どっちを消してもちゃんと消えるけどさ。

copilot-cli と claude-code のインストール

gemini-cli を使うと、sudo を交えた検証などもしてくれて、便利に使っているけど、copilot-cli もあるみたい。

そういえば、claude-code CLI もインストールしてないな。

$ sudo npm install -g @github/copilot
$ sudo npm install -g @anthropic-ai/claude-code

$ sudo npm install -g @google/gemini-cli

Nest Mini も Gemini for Home に対応とな

Gemini for Home のニュースが出てて、スマートスピーカ―に自然に話しかけられて便利そうだけど、新しい Google のスマートスピーカ―買わないと…と思ってたけど、この記事だと、いまリビングで使っている Google Nest Mini でも Gemini for Home に対応してくれるとな。一応期待しておこう。

#現在、Google Nest Mini / 2Fリビング、Google Home Mini / 子供部屋, キッチンの2台

ノーコードで回転体の設計

お手伝いで、回転体を編集するプログラム作成。

gemini-cli を使って、ノーコードでどこまで作れるか試行錯誤。

事前に THREE.js を使って実験しながらだけど、こんなプログラムがソースコードを読んで編集といった作業なしで、作れた。すごいね。

無惨様、青の彼岸花を見つけました

{CAPTION}

{CAPTION}ウォーキング中にとった彼岸花を、Gemini で「赤を青に変えて」で青い彼岸花の完成。

Buffalo ルータの情報

Home Assistant を使っていたら、UPnP の機能でルータのパケット情報を取得しているらしい。ではどうやって取得するのか?

SSDPでUPnPデバイスを探す

Gemini に「UPnPの機能で Buffalo のルータの通信速度などの情報を調べるには?」と聞いたら、「SSDP(Simple Service Discovery Protocol)を使用してネットワーク上のUPnP対応デバイスを検索します。これは、マルチキャストアドレス239.255.255.250のUDPポート1900にM-SEARCHリクエストを送信することで行います。」とのアドバイスがもらえた。

「取得するプログラムを提案して」と聞いたら、以下の Python コードを教えてくれた。

import socket
import time

# SSDPマルチキャストアドレスとポート
SSDP_ADDR = '239.255.255.250'
SSDP_PORT = 1900

# M-SEARCHリクエストの作成
# ST: Service Type、ここではルーター(WANIPConnection)を指定
# MX: Maximum wait time in seconds
msearch_request = '\r\n'.join([
    'M-SEARCH * HTTP/1.1',
    'HOST: {}:{}'.format(SSDP_ADDR, SSDP_PORT),
    'MAN: "ssdp:discover"',
    'ST: urn:schemas-upnp-org:service:WANIPConnection:1',
    'MX: 3',
    '',
    ''
])

# ソケットの作成
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5)  # 応答を待機する時間(秒)

print("M-SEARCHリクエストを送信中...")

try:
    # リクエストの送信
    sock.sendto(msearch_request.encode('utf-8'), (SSDP_ADDR, SSDP_PORT))

    while True:
        try:
            # 応答の受信
            data, addr = sock.recvfrom(1024)
            print("-" * 20)
            print("デバイスを発見しました!")
            print("IPアドレス:", addr[0])
            print("ポート:", addr[1])
            print("\n応答内容:")
            print(data.decode('utf-8'))

        except socket.timeout:
            print("-" * 20)
            print("タイムアウトしました。すべての応答を受信したか、デバイスが見つかりませんでした。")
            break

except Exception as e:
    print("エラーが発生しました:", e)

finally:
    sock.close()

SSDPの返答から詳細取得

実行したら、以下の情報が得られた。これより、/getedesc.xml などで 49355 ポートに問い合わせすればいいみたい。

M-SEARCHリクエストを送信中...
--------------------
デバイスを発見しました!
IPアドレス: 192.168.xx.1
ポート: 1900

応答内容:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Tue, 09 Sep 2025 13:57:21 GMT
EXT:
LOCATION: http://192.168.xx.1:49355/gatedesc.xml
SERVER: Linux/3.14.77 UPnP/1.0 Intel_SDK_for_UPnP_devices/1.2
ST: urn:schemas-upnp-org:service:WANIPConnection:1
USN: (略)
  • http://192.168.xx.1:49355/gatedesc.xml
  • http://192.168.xx.1:49355/gateinfoSCPD.xml – 情報量少ない
  • http://192.168.xx.1:49355/gateicfgSCPD.xml
  • http://192.168.xx.1:49355/gateconnSCPD.xml

送受信パケット量を取得

いろいろと試す中、送受信パケットを取得する lua スクリプトを Gemini に作らせたら、下記のようになった。

-- socket.http ライブラリをロードします。
-- 'luasocket'パッケージが必要です。
local http = require("socket.http")
local ltn12 = require("ltn12")

-- UPnPサービス情報
local url = "http://192.168.xx.1:49355/upnp/control/WANCommonIFC1"

-- リクエスト情報を格納するテーブル
local requests = {
    {
        action = "GetTotalBytesReceived",
        tag = "<NewTotalBytesReceived>",
        label = "受信"
    },
    {
        action = "GetTotalBytesSent",
        tag = "<NewTotalBytesSent>",
        label = "送信"
    }
}

-- データを取得する関数
local function get_upnp_data(req_info)
    local soap_data = string.format([[<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:%s xmlns:u="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1">
    </u:%s>
  </s:Body>
</s:Envelope>]], req_info.action, req_info.action)

    local headers = {
        ["Content-Type"] = "text/xml; charset=\"utf-8\"",
        ["SOAPAction"] = '"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#' .. req_info.action .. '"'
    }

    print(req_info.label .. "データを取得中...")

    local response_body = {}
    local res, status_code = http.request{
        url = url,
        method = "POST",
        headers = headers,
        source = ltn12.source.string(soap_data),
        sink = ltn12.sink.table(response_body),
        timeout = 10
    }

    if status_code and (status_code == 200 or status_code == "200 OK") then
        local response_text = table.concat(response_body)

        local start_tag = req_info.tag
        local end_tag = string.gsub(start_tag, "<", "</")
        
        local _, _, bytes_string = string.find(response_text, start_tag .. "(.*)" .. end_tag)

        if bytes_string then
            local total_bytes = tonumber(bytes_string)
            local megabytes = total_bytes / 1024 / 1024
            
            print(string.format("合計%sバイト数: %d", req_info.label, total_bytes))
            print(string.format("合計%sメガバイト数: %.2f MB", req_info.label, megabytes))
        else
            print(req_info.label .. "データが見つかりませんでした。")
            print("応答内容:\n" .. response_text)
        end
    else
        print(req_info.label .. "データの取得に失敗しました。")
        print("ステータスコード: " .. (status_code or "不明"))
        print("応答内容:\n" .. table.concat(response_body))
    end
    print("----------------------------------------")
end

-- 各リクエストを実行
for _, req_info in ipairs(requests) do
    get_upnp_data(req_info)
end

SSDPとそのあとの処理をまとめた lua スクリプトは以下となった。

munin でのパケット流量観測に組み込んでみた。

Gemini中心のコーディング

でも、今回のプラグイン作成は、Gemini で作らせた。オレがやったのは生成されたプログラムをテストして、プログラム作ってとか、Lua で作ってとか、データ抽出では厳密なXML解析せず正規表現で作って…とか、munin のプラグインに直してとか、命令するばかりで、エディタで悩みながらコード書きはしなかった。エディタ使ったのは、コードの確認・コメント加筆・余計な動作検証の print を消す程度。

gemini-cli 便利

Gemini をコマンドラインから使えるツール gemini-cli が公開されている。

インストールは、参考ページの中から、グローバルインストールで行った。

$ sudo npm install -g @google/gemini-cli

((( 更新も同上コマンドで可能 )))

単純に、$ gemini で 以下のような画面が表示され、プロンプトを入力すればいい。

でも、本領発揮なのは、フォルダ配下を読み込んだうえで、アドバイスが得られること。

実際、お試しで Three.js を実験していたけど、import でトラブル発生。でも「index.html が動かない」と入力したら、index.html やら、そこから読み込まれる *.js なども自動的に読み込んだうえで、修正点を指摘してくれる。

さらに指摘した修正点を、適用するか聞いてくれるので、Yes を答えたら、勝手にソースコードも修正してくれる。

ページをアプリとしてインストール

Gemini のデスクトップアプリってないのか色々調べていたら、Gemini が「Webサイトをアプリとしてインストール」する技を紹介してくれた。今まで、デスクトップに URL を登録していたけど、Chromeで「メニュー・キャスト、保存、共有・ページをアプリとしてインストール」を使うと、スタートメニューに登録できたりして便利。しらんかった。

Gmail, Google ニュース, Gemini などに加え、Homebridge などをページとしてインストールしてみた。便利、便利、便利。

(追記) セッションが切れないから便利

「ページをアプリとしてインストール」で使っていると、ページのセッションが切れづらい。iCloud のメモをアプリ化したけど、セッションが切れないためいちいち Apple アカウントへのログインが発生しない。(発生しづらい)

Geminiに聞いてみたら、(本当かどうかはしらんけど)「PWAは、永続的なストレージを利用して、ユーザーのログイン情報(認証トークンなど)を保存することができます。これにより、PWAを閉じたり、PCを再起動したりしても、再度ログインせずにセッションが継続されることが多い」と教えてくれた。

Google 検索

My Google   Yahoo

Microsoft

ファンサイト

メタ情報