カテゴリー: 開発ログ

  • E.D.E.N.開発ログ #3|Ollamaの3つのモデルを「人格」で切り替えたら、モデル選択画面を開かなくなった

    E.D.E.N.開発ログ #3|Ollamaの3つのモデルを「人格」で切り替えたら、モデル選択画面を開かなくなった

    E.D.E.N.という、わたし専用のAIアシスタントを作っています。従量課金のAPIを増やさず、手元のPCで動かすことを条件にしているプロジェクトです。

    今回は、E.D.E.N.に3つのモードを実装した話です。

    最初は演出のつもりでした。同じAIでも、呼び方と見た目が変わると気分が変わる。その程度の動機です。名前は、好きな作品に出てくる「知恵を貸してくれる存在」から借りています。JARVIS はアイアンマンから、RAPHAEL と CIEL は『転生したらスライムだった件』から取りました。あくまで自分用の呼び名で、作品のセリフや設定を再現しているわけではありません。

    作り終えてから気づいたのですが、これは人格の実装ではありませんでした。モデルサイズと、課金の境界を切り替えるスイッチになっていました。

    3つのモードが、実際には何を切り替えているか

    モード 役割 使うモデル 外部へ出る条件
    JARVIS(実行) 予定確認、家電操作、短い指示 Qwen 3.5 9B 自動判定では常にローカル。外部へ出したいときは接続先を手動で切り替える
    RAPHAEL(解析) 調べもの、整理、比較 Gemma 4 12B 通常会話はローカル。最新情報の調査時だけ契約内のAIへ昇格
    CIEL(対話) 考えがまとまっていない相談、企画 Gemma 4 26B 通常会話はローカル。複雑な依頼とWeb検索時だけ昇格

    いずれもOllamaで動かしています。昇格先は、すでに契約しているChatGPT(Codex)の枠内です。使った分だけ課金されるAPIは追加していません。

    上の表が、このシステムの設計そのものです。

    3つを分けた本当の理由は、タスクによって必要なモデルの大きさが違い、外部に出すべき境界も違うからでした。予定を読み上げるだけの用事に大きいモデルは要りません。逆に、考えを整理したい相談に小さいモデルを当てると、会話が浅くなって使わなくなります。

    設定画面でモデル名を選ばせる作りにすると、結局わたしは切り替えません。面倒だからです。モードに名前と声と色を与えたら、「ラファエル」と呼びかけるだけで、モデルと課金方針がまとめて切り替わるようになりました。

    演出は、無駄ではなかった

    E.D.E.N.のRAPHAELモード。左に3つのモード、中央に知性コア、右に対話パネルが並ぶ画面

    各モードには、声の高さと話す速さ、画面中央の「知性コア」の色と図形を別々に割り当てています。JARVIS は低い声で落ち着いた青、RAPHAEL は中高音で金、CIEL は高めでやわらかい紫です。

    これは趣味の部分ですが、実用上の効果が1つありました。今どのモードで動いているかを、画面を見なくても声で判別できることです。ローカルの小さいモデルと、契約内の大きいAIでは、返ってくる答えの質が違います。色と声が違えば、「今は軽い方で答えている」と分かった上で読めます。

    音声での切り替えにも対応しました。名前を呼びかける言い方を何通りか登録してあり、「〜モード」「〜に切り替えて」のような語尾の揺れも受け付けます。ここはAIの推測に任せず、決められた表記のパターンとして実装しています。開発ログ #2 で家電操作を固定ルールに寄せたのと同じ考え方です。

    名前を借りた作品のこと

    解析役と対話役の名前は、『転生したらスライムだった件』から取りました。どちらも作中で「考えを補助する存在」として登場する名前で、自分が作ろうとしていたものと役割の連想が近かったからです。実行役の JARVIS をアイアンマンから取ったのも同じ理由です。

    わたしはこの作品をラノベ・コミック・アニメのいずれも読んで(観て)います。わたしが読んだ・観た範囲では、ラノベは設定の説明が細かく、コミックは展開が追いやすく、アニメは映像と音の印象が強い、という感じ方でした。

    ※上記はAmazonアソシエイトのリンクです。紹介しているのは実際に読んだ・観たものだけで、作品の内容や画像は掲載していません(アフィリエイトについて)。

    従量課金を増やさないための線引き

    このプロジェクトの制約は「月額契約の中で完結させる。使った分だけ課金されるAPIは増やさない」です。3モードは、その制約を守るための実装でもあります。

    自動テストで確認している挙動を、いくつか挙げます。

    • CIEL に「少し相談したい」と話しかけた場合 → ローカルのまま処理する
    • RAPHAEL に「今日の最新ニュースを検索して」と頼んだ場合 → 契約内のAIへ昇格する
    • 手元の仕組みが既にニュースを取得できている場合 → 昇格せず、ローカルで答える

    JARVIS は自動判定では昇格しません。軽い用事を確実にローカルで済ませる枠として固定しています。

    3つ目が、この設計でいちばん効いています。「最新情報だから外部に出す」ではなく、「手元に答えが無いときだけ外部に出す」にすると、昇格の回数はかなり減ります。

    分かったこと

    人格を作ったつもりで、実際に作っていたのはルーターでした。

    AIの使い分けを「モデル名」や「エンジン設定」として持つと、切り替えるのが面倒になって、結局いちばん手近な1つしか使わなくなります。切り替えを名前のある人格という形で持たせると、日常の言葉のまま切り替えられます。中身は同じ条件分岐ですが、使われる頻度が変わりました。

    もしローカルLLMを複数入れたまま使い分けられていない方がいたら、モデル選択ではなく「呼び名」を作る方向を試してみてください。モデル選択画面を開く回数が減ります。

    使っている環境と機器

    この記録の時点で動かしている構成です。

    • ローカルLLM: Ollama(Qwen 3.5 9B / Gemma 4 12B / Gemma 4 26B を用途別に使用)
    • 昇格先: 既に契約しているAIサービスの枠内のみ。従量課金APIは追加していない
    • 音声: ローカルで動作する日本語音声合成。文単位で生成しながら読み上げ
    • 家電: Nature Remo 3(Remo-1W3)

    家電まわりで実測できたのは、温度・湿度・照度の3つの取得と、テレビ専用APIへ電源信号を1回送ってAPIに受け付けられたところまでです。汎用の赤外線信号ではテレビが反応せず、専用の経路を追加して直しました。赤外線は一方向のため、テレビ本体が実際に消えたかはE.D.E.N.側から確認できていません。消音・音量・チャンネル・入力切替はボタンを表示できた段階で、送信の実測はこれからです。経緯は 開発ログ #2 に書いています。

    スマートリモコン側は、センサー値が取れる機種でないと環境の記録ができないため、ここは機種差が出ます。

    ※上記はAmazonアソシエイトのリンクです。紹介しているのは実際に使っている機器だけで、実物の写真は掲載していません(アフィリエイトについて)。

    次にやること

    予定とタスクの連携を、個人情報を伏せた形で整理します。複数アカウントの予定を1画面へまとめる部分は動いていますが、記事にできる形にはまだなっていません。

    自分専用のAIに名前を付けている方は、何と呼んでいますか。使い分けの基準があるなら、コメントで教えていただけると参考になります。

    このシリーズの記事:

    設計をどう判断したかという話は、noteにも分けて書いています。

  • E.D.E.N.開発ログ #2|Nature Remoでテレビを消せなかった原因と修正

    E.D.E.N.開発ログ #2|Nature Remoでテレビを消せなかった原因と修正

    E.D.E.N.の対話画面。日時と利用者データを除いた収録用状態

    E.D.E.N.という、わたし専用のAIアシスタントを作っています。Project Artemisの中で、AIに実装と検証を任せ、人間は使って違和感を返す形で進めているプロジェクトです。

    前回、Nature Remoへ接続し、温度・湿度・照度を取得できるところまで進みました。しかし、ダッシュボードでテレビを見ると「専用操作は未対応」と表示され、電源を切れませんでした。声で「テレビを消して」と頼んでも操作されませんでした。

    接続できているのに、操作できない

    最初に疑ったのは、認証キーとNature Remo本体の接続です。しかしセンサー値は取得できていました。問題は接続全体ではなく、家電ごとの操作経路にあると切り分けました。

    E.D.E.N.が実装していたのは、Nature Remoへ登録した汎用赤外線信号を送る経路だけでした。一方、Nature Remo上でテレビとして登録された家電には、電源、消音、音量、チャンネル、入力切替などの専用ボタンがあります。テレビは、この専用経路を使う必要がありました。

    直した内容

    音声指示からNature Remoのテレビ専用APIへ送るまでの安全確認フロー

    修正は3点です。

    1. Nature Remoからテレビ専用ボタンの一覧を取得する
    2. ダッシュボードに電源、消音、音量、チャンネル、入力切替を表示する
    3. 「テレビを消して」のような明示的な言葉を、AIの推測ではなく決められた操作へ変換する

    家電IDや操作名を自由入力させず、Nature Remoから取得した家電とボタンの組み合わせだけを受け付けるようにしました。会話らしく見えても、実際の操作部分は決められた規則で動きます。

    再起動すると未接続へ戻る問題も見つかった

    修正後の確認中、別の問題も見つかりました。認証キーは端末の環境変数へ保存されていましたが、E.D.E.N.を再起動したとき、Node.jsのサーバーへ値が引き継がれていませんでした。

    起動スクリプトが保存済みの値を読み、サーバーへ渡す処理を追加しました。画面上で一度接続したあと、次回起動時にも自動接続される形です。

    検証結果

    • 構文検査に合格
    • 自動テスト33件に合格
    • 登録済みテレビから専用ボタン一覧を取得
    • 指定したテレビへ電源信号を1回送信し、APIの受付成功を確認

    赤外線は一方向です。APIが信号を受け付けても、テレビが実際に消えたかをE.D.E.N.から取得できるわけではありません。また、テレビの電源信号は「オン」と「オフ」が別ではなく、同じボタンで切り替わる方式です。画面にも実機確認が必要だと表示しました。

    使っている機器

    この記録で使っているのは Nature Remo 3(Remo-1W3)です。現時点でE.D.E.N.から取得できているのは温度・湿度・照度の3つです。送信を実測できたのはテレビの電源信号1回で、APIが受け付けたところまでです。消音・音量・チャンネル・入力切替はボタンを取得して画面に表示できた段階で、送信の実測はこれからです。

    動かなかったこと、確認できていないことも書いておきます。赤外線は一方向のため、テレビ本体が実際に消えたかどうかはE.D.E.N.側から確認できません。人感センサーは搭載されていますが今回の実装では使っていません。エアコンなど他の家電にもまだ対応していません。

    ※上記はAmazonアソシエイトのリンクです。紹介しているのは実際に使っている機器だけで、実物の写真は掲載していません(アフィリエイトについて)。

    今回分かったこと

    「APIにつながった」と「目的の操作ができた」は別でした。センサー値が取れていても、家電の種類によって操作方法が違います。

    もう一つは、会話で家電を動かす部分を生成AIへ直接任せなかったことです。言葉の受付は自然でも、物理操作は登録済みの対象と操作に絞りました。便利さより、意図しない家電を動かさないことを優先しています。

    次は、予定やタスクの連携部分を、個人情報を伏せて整理します。

    家電をAIにつないでいる方は、操作の範囲をどこまで自由にしていますか。「言えば何でもやってくれる」方向に寄せているか、E.D.E.N.のように登録済みの操作だけに絞っているか、コメントで教えていただけると参考になります。

    この修正で何を考えて設計を変えたのか、認証の分け方や許可リストの作り方まで含めた実装記録は、noteに分けて書いています。こちらは有料記事です。無料で読める範囲に、失敗の内容と原因、設計の原則を書いています。

    このシリーズの続き

    前回の記事: 開発ログ #1「個人用AIアシスタントを、課金APIに頼らず作ってみている話」

  • 個人用AIアシスタントを、課金APIに頼らず作ってみている話(開発ログ #1)

    普段のAI関連の作業のほとんどは、Claude CodeやChatGPTのようなクラウドサービス経由です。それとは別に、自分専用の、ローカルで動くAIアシスタントを作り始めました。この記事は、その実装の進み具合を残す記録です。

    なぜ作ろうと思ったか

    クラウドのAIサービスは便利ですが、使うたびに料金がかかります。日常的に話しかける相手として使うなら、従量課金に依存しない形にしたいと考えました。

    そこで、次の方針で作ることにしました。

    • 従量課金APIを追加しない。無料で使えるローカルのAIモデル(Ollama経由のQwenなど)を基本にする
    • 天気・為替・暗号資産の値など、外部データは無料のAPIから取得する
    • 音声でのやり取りに対応する(認識→文章生成→読み上げ)

    今の画面

    対話画面。JARVISに話しかける初期状態

    中央のコアが今の状態を表し、下の対話欄でやり取りします。モードは3つ用意していて、それぞれ役割・使うモデルを分けています(補佐役、分析役、対話役、という分担です)。ダッシュボード表示(天気・タスク・家電・予定を1画面にまとめる機能)も別タブで用意していますが、地域設定・認証キーがまだの項目があり、今回は割愛します。

    今できていること・できていないこと

    できていること

    • 対話画面と、状態をまとめて見るダッシュボードの2画面構成
    • 画面サイズが違っても崩れないレイアウト(実機で確認済み)
    • 声に反応する画面の動き(実際に声を出している間だけ表示が変化することを確認済み)
    • 天気・為替・暗号資産の値の取得
    • カレンダーの予定を読み取り専用で表示する経路(今後7日分。予定の作成・変更はしない設計)

    まだのこと

    • スマート家電(赤外線リモコン経由)の実機操作(認証キー未設定のため)
    • マイクでの聞き取りの実機確認
    • 複数画面を同時に開いたときの同期の実機確認

    この先やろうと思っていること(ラフな順番)

    実装ロードマップ。4つのステップを横並びで示す図

    1. 天気・カレンダーなど、外部データ連携を一通り実際に動く状態にする
    2. 音声まわり(聞き取り・読み上げ)を実機で確認する
    3. スマート家電の操作を、実際に1件動かしてみる
    4. 複数画面(メイン画面+別ウィンドウ)の同期を確認する

    大きな設計変更よりも、すでにある機能を「動くことを確認した」状態にすることを優先しています。作ったつもりで終わらせない、というのが今回のテーマです。

    追記(9/15): 家電連携が半分だけ進んだ

    Nature Remo(赤外線リモコン経由でエアコン等を操作できるサービス)のアクセストークン接続に成功しました。ただし、まだ「操作」までは届いていません。

    できたのはここまでです。

    • アクセストークンでの接続
    • 温度・湿度・照度のセンサー値の取得

    個別の家電を操作しようとすると、画面には「この家電の専用操作は未対応。Natureアプリをご利用ください」と表示されます。接続とセンサー取得はできても、信号を送って家電を動かす部分はまだ実装できていない、という状態です。

    あわせて、仕様として次の注記も見つけました。

    赤外線信号の送信受付は、実機の動作確認とは異なります。

    つまり、信号を送るAPIが「受け付けました」と返しても、それだけでは実際にエアコンが反応したかは分からない、ということです。ここは前回の記事で書いた「動いているのを確認した、とは別の話」と同じ壁だと感じています。

    個別家電の操作には、もう一段仕組みを足す必要がありそうです。次の更新で進捗を書きます。

    追記(9/16): 環境データの記録を、別の小さな仕組みに切り出した

    家電まわりの続きです。温度・湿度・照度のセンサー値を、1時間おきにスプレッドシートへ記録する仕組みを、本体とは別のGoogle Apps Scriptプロジェクトとして作り直しました。

    やったことは次の通りです。

    • センサー値の取得先(Nature Remo Cloud API)から、温度・湿度・照度を取得する処理を実装
    • 認証キーは、コードやシートに直接書かず、スクリプトのプロパティ機能に保存する形にした
    • 複数台の対応、センサーが無い機種でのエラー処理、記録上限(5万行)、最新値だけを見るシートを追加
    • 直近のデータをまとめて見られる簡易ダッシュボードの画面も実装(まだ外部には公開していない)
    • 本体側(対話アプリ)にも、照度と最終観測時刻を表示するようにし、家電のカードから記録シートを開けるようにした

    テストは33件中33件合格、コードの反映も完了しています。

    ただし、まだ「動いている」とは言えません。 認証キーの入力、Googleへの初回許可、定期実行の有効化が済んでいないため、実際のデータはまだ1件も記録されていません。これも、前回書いた「実装できた」と「動くのを確認した」の距離そのものです。

    このシリーズの続き

    注記

    この記事は E.D.E.N. 開発ログの1本目です。以降の進捗は、この記事への追記ではなく番号付きの新しい記事として公開しています。画面の中身(カレンダーの予定・地域設定など)は、公開前に個人情報が写り込んでいないか確認したうえで掲載しています。