はじめに
SoraSenseは、室内の温度・湿度を継続的に収集・保存し、Grafanaによる可視化と自然言語によるデータ照会を行うIoT環境モニタリングシステムです。
ソースコードと設計ドキュメントは、GitHubのSoraSenseリポジトリで公開しています。
センサー値を記録するだけでなく、次の一連の流れをひとつのシステムとして構築しました。
- M5StickC Plus2で温度・湿度を測定する
- FastAPIで測定データを安全に受け付ける
- PostgreSQLへ履歴を保存する
- 閾値とヒステリシスにより異常を検知する
- Grafanaで現在値・履歴・異常状態を確認する
- Geminiを利用し、保存済みデータについて日本語で質問する
本システムは、管理されたローカルネットワーク内での個人利用を想定しています。医療、生命維持、防災、公的な精度保証が必要な測定には使用しません。
目的
このアプリの目的は、蓄積した温度・湿度データを専門的な検索操作なしで活用できるようにすることです。そのために、利用者が入力した自然言語の質問を解釈し、必要なデータ照会を選択して、取得結果に基づく回答を生成するAIエージェントを実装しました。
たとえば「今日の平均温度は?」「昨日と今日の湿度を比較して」と質問すると、AIエージェントが質問の種類と対象期間を判定し、許可された参照専用Toolを通してPostgreSQLのデータを取得します。さらに、生成された回答の数値や期間をToolの実行結果と照合し、根拠を確認できた情報だけを利用者へ表示します。
これにより、自然言語処理の使いやすさと、測定データを扱うシステムに必要な正確性・安全性の両立を目指しました。
システム概要
全体構成は次のとおりです。
主要コンポーネントの責務を分離し、外部AIが停止しても測定・保存・Grafana表示は継続できる構成にしています。
| コンポーネント | 技術 | 主な役割 |
|---|---|---|
| センサーデバイス | M5StickC Plus2、ENV IV Unit、PlatformIO | 60秒周期の測定、検証、送信、再送 |
| Web/API | Python 3.12、FastAPI | 測定受付、認証、AI質問画面、ヘルスチェック |
| データベース | PostgreSQL 16、SQLAlchemy、Alembic | 測定値、アラート、AI利用履歴の保存 |
| 可視化 | Grafana | 現在値、履歴、集計、アラート、稼働状態の表示 |
| AI Agent | Gemini Developer API | 保存データの照会、集計、比較、傾向説明 |
仕組み
1. センサーデバイスによる測定と送信
M5StickC Plus2に接続したENV IV Unitから、温度と湿度を60秒周期で取得します。
デバイス側では、温度が-40.0〜85.0℃、湿度が0.0〜100.0%の範囲にある有限値かを確認します。無効な値は送信せず、次回の測定を継続します。
各測定にはUUID v4のmessage_idを付けます。同じ測定を再送するときはIDを変えないため、API側で重複を識別できます。通信失敗、タイムアウト、HTTP 429、5xxでは指数バックオフとジッターを使って再送します。待機中も計測ループを止めない非ブロッキングな設計です。
測定日時はNTP同期後のUTCで送信します。時刻を保証できない状態では、誤った日時のデータを保存しないよう送信を止め、同期を優先します。
2. FastAPIでの受付と冪等保存
デバイスは、Bearer APIキーと測定データをFastAPIへ送ります。APIは認証、Content-Type、本文サイズ、デバイスID、日時、値の範囲などを検証し、不正なデータを保存しません。
正常な測定は、測定履歴の保存とアラート状態の更新を同じトランザクション内で処理します。device_idとmessage_idの一意制約を最後の防御層にすることで、通信再送や同時送信が発生しても同じ測定を二重保存しません。
また、デバイス行をロックして同一デバイスの状態更新を直列化します。過去の測定が遅れて到着した場合は履歴として保存しますが、現在のアラート状態を巻き戻さないようにしています。
3. ヒステリシスを使った異常検知
温度・湿度には上限と下限を設定し、範囲を外れたときにHIGHまたはLOWのアラートを開始します。
単純な閾値判定だけでは、値が境界付近を上下するたびにアラートの発生と解消を繰り返します。SoraSenseではヒステリシスを設け、復帰判定に幅を持たせています。そのため、同じ異常が継続している間は未解消アラートを重複登録せず、正常範囲へ戻った後の再発だけを新しいアラートとして扱えます。
4. Grafanaによる可視化
PostgreSQLにはGrafana向けの参照専用VIEWを用意しています。Grafana用DBロールはVIEWへのSELECTだけが可能で、基底テーブルの更新や削除はできません。
ダッシュボードでは、主に次の情報を確認できます。
- 最新の温度・湿度と測定日時
- 温度・湿度の時系列と期間集計
- アラート履歴
- データ欠損区間
- デバイスの最終受信時刻と稼働状態
- AIの質問回数やトークン利用量
ダッシュボードとデータソースはProvisioningしているため、環境を再構築しても同じ表示を復元できます。
5. 根拠を検証するAI Agent
AI質問画面では、たとえば「今日の平均温度は?」「昨日と今日の湿度を比較して」のように、日本語で質問できます。
AI Agentへ任意のSQL実行権限は与えていません。質問を分類し、必要な期間をアプリ側で解決したうえで、次の参照専用Toolから該当する1種類だけを公開します。
- 最新値の取得
- 指定期間の統計取得
- 時系列データの取得
- 2期間の比較
- アラート履歴の取得
重要なのは、モデルが生成した回答をそのまま表示しないことです。アプリケーションは、回答の構造、対象期間、タイムゾーン、数値、単位、根拠パスをTool実行履歴と照合します。検証済みの値だけを画面へ表示し、モデルの回答を検証できない場合は、取得済みのTool結果から定型回答を再構築します。
データなしのNO_DATAと、一時的に取得できないUNAVAILABLEも区別します。AIが利用できない場合でも、データ収集・保存・Grafana表示には影響しません。
データが届いてから表示されるまでの流れ
ユースケース
室内環境を日常的に確認する
Grafanaを開き、現在の温度・湿度、直近の推移、センサーから最後にデータが届いた時刻を確認します。数値だけでなく時系列を見ることで、時間帯による変化も把握できます。
高温・低温や湿度異常を振り返る
設定した閾値を超えるとアラートが記録されます。いつ始まり、いつ解消したかを履歴で確認できるため、換気や空調の使い方を見直す材料になります。
自然言語で集計する
SQLやGrafanaのクエリを知らなくても、「昨日の最高温度と最低温度は?」「先週と今週の平均湿度を比較して」のように質問できます。回答には、システムが確定した対象期間と根拠となる測定値が添えられます。
デバイスやシステムの停止を見つける
最終受信時刻からデバイスの状態を確認し、データが途絶えていることを識別できます。FastAPIにはLiveチェックとReadyチェックがあり、アプリ自体の稼働とデータ保存先を含む受付準備状態を分けて確認できます。
設計で重視したこと
SoraSenseでは、機能を増やすこと以上に、データと回答を信用できる境界を明確にすることを重視しました。
- 測定値はデバイス側とAPI側の両方で検証する
- 再送を前提に、受付処理を冪等にする
- 測定保存とアラート更新を同一トランザクションにする
- GrafanaとAI Agentを参照専用にする
- AIの数値回答を、Toolの実行結果に照らして検証する
- AI障害をデータ収集経路から分離する
- Secretをコードやログへ出さない
- UTCで保存し、利用者向け表示と集計境界は
Asia/Tokyoに統一する
まとめ
SoraSenseでは、M5StickC Plus2による温湿度測定から、FastAPIとPostgreSQLによる安全な保存、Grafanaによる可視化、Geminiを使った自然言語照会までを一つの流れとして実装しました。
特に、通信再送を考慮した冪等性、ヒステリシスを使った異常管理、参照権限の分離、AI回答の根拠検証によって、IoTと生成AIを組み合わせる際に必要な信頼性を確保しています。
今後は、複数デバイス・複数拠点への対応、センサー種別の追加、外部通知、バックアップやCIなどの運用強化へ拡張できます。