
Kiro のワークフローで"自動車運転相棒AI"アプリを作ってみた(第2部:データで裏取りし、エッジで判定する)
こんにちは。クラウド事業統括本部の Yoshi です。普段は TAM(テクニカルアカウントマネージャー)として、お客様のAWS活用のご支援をしています。
Kiro IDE の ワークフロー機能 で、車載データと会話する相棒AI「AI Co-Driver」を個人開発で作ってみた実践記の第2部です。第1部では、いきなり作らず「不確実性の高い順」にマイルストーンを並べ、最大の山である実車データ取得(M1)を最初に突破しました。
今回は、その土台の上に 「いつ・何を言うべきか」を端末側で判定する仕組み を作ります。ポイントは、判定の基準を 主観ではなく実データで決めた ことです。
目次
- シリーズ構成
- おさらい — ハイブリッド構成「判定はエッジ、言い回しだけクラウド」
- M2:監視指標を揃える — 実機で「取れるか」を確かめる
- M2.5:走行ログを溜める — 「やってみる」の投資
- データが設計を正した — 机上の閾値は実測とズレていた
- もう一度データで殴る — 文脈を変えて再検証
- M3:エッジで決定論的に判定するトリガーエンジン
- 層分離の恩恵 — 実車なしでロジックを検証できる
- 業務感覚への接続 — 「データで基準を決める・安全の軸は人が握る」
- 次回予告
- まとめ
シリーズ構成
| 記事 | 位置づけ | 内容 |
|---|---|---|
| 機能紹介編(別記事・公開済み) | 土台 | Kiro のワークフロー機能は何ができるのか |
| 第1部(公開済み) | 実践記 | 全体像を描いてから動く(構想 → 実車にデータ取得) |
| 第2部(本記事) | 実践記 | データで裏取りし、エッジで判定する(PID拡充 → 走行ログ → トリガー判定) |
| 第3部(近日) | 実践記 | 言葉と声を与え、仕上げる(LLM連携 → 音声 → UI仕上げ、三部作の回収) |
- 機能紹介編: Kiro IDE のワークフロー機能を紹介 〜レシピとランタイムで"段取り"を任せる〜
- 第1部: 全体像を描いてから動く(構想 → 実車にデータ取得)
- 第3部: 言葉と声を与え、仕上げる
おさらい — ハイブリッド構成「判定はエッジ、言い回しだけクラウド」
第1部で触れた、この相棒AIのいちばん大事な設計判断をおさらいします。それは 「いつ・何を言うべきか」の判定は端末(エッジ)で完結させ、クラウドのLLMには「言い回しの生成」だけを任せる というハイブリッド構成です。

第2部でやるのは、この図の 左半分(エッジ側) です。LLM はまだ登場しません。「気づく」仕組みを、実データを根拠にして組み立てます。
M2:監視指標を揃える — 実機で「取れるか」を確かめる
M1 では水温と車速だけを通しました。M2 では、相棒AIが状況を語るのに必要な指標を揃えます。回転数、エンジン負荷、スロットル、吸気圧(MAP)、吸気温、大気圧。そして、スマホの加速度センサーから加速・減速・コーナリングのGも取り込みます。
ここでひとつ、やりたかった演出があります。ブースト圧です。私の車両はターボ車なので、「過給が立ち上がった」「ブースト最大」といった実況は相棒AIで実現したかった。ところが、標準のOBD-IIには「ブースト圧」そのものを返すPIDがありません。
そこで、こう設計しました。
ブースト圧(ゲージ) = 吸気圧(MAP) − 大気圧
正圧なら過給、負圧なら吸気負圧。これらを個別のPIDで取って引き算すれば、ブーストを算出できます。ただし「このPIDが自分の車で実際に取れるか」は、実機で確かめないと分かりません(第1部の油温のように、取れると思ったら取れた/取れないと思ったら取れた、は現場でしか決着しません)。
そこで、接続時に1回だけ 「この車で何が取れるか」を調べるスキャン を入れました。常時ポーリングには混ぜず(車のECUに負荷をかけないため)、接続直後に対応PIDの一覧だけ取得して記録します。
実機スキャンの結果、ブースト算出に必要なPIDはこうなりました。

| 試したPID | 内容 | 結果 |
|---|---|---|
01 0B |
吸気圧(MAP) | 取れた |
01 33 |
大気圧(Barometric) | 取れた |
01 70 |
ブースト圧制御(新しめの規格) | 取れなかった |
本命だと思っていた専用PID(01 70)は非対応でしたが、MAP と大気圧の両方が取れたので、引き算でブーストを正確に算出できることが確定しました(ADR-008/009)。この「現物で取得可否を確定させる」一手が、以降のトリガー設計と演出の土台になります。
M2.5:走行ログを溜める — 「やってみる」の投資
M2 で指標が揃ったところで、本来なら次は「どの値になったら喋らせるか(閾値)」を決める M3 に進みます。でも、ここで一歩寄り道をしました。
走行中の全データを、CSVに記録する機能 を先に作りました(M2.5)。理由はシンプルで、閾値を自分の頭の中(机上)で決めたくなかった からです。「水温が何℃を超えたら警告」なんて、実際の自分の車がどんな温度で走っているかを知らずに決めても、たぶん外すだろう。だったら先に実データを溜めて、それを見てから決めよう、という判断です。

走行ログを溜めて、PC側(手元のpandas)で分布やイベント候補を集計する——という小さなパイプラインを足しました。アプリ側のCSV書き出しと、PC側の分析スクリプトは、カラムの並びがズレると意味が壊れる ので、カラム定義を1つの「契約」として扱い、3か所(書き出し・分析スクリプト・README)で完全一致させました。
# CSVカラム契約(この並びを3か所で一致させる)
timestamp_ms, speed_kmh, rpm, coolant_c, oil_c, load_pct,
throttle_pct, map_kpa, boost_bar, intake_c, long_g, lat_g
データが設計を正した — 机上の閾値は実測とズレていた
実際に走ってログを溜め、分析してみて——これが面白いくらいに、机上の想定が実測とズレていました。
当初ざっくり考えていた閾値は「水温が105℃を超えたら警告」「エンジン負荷が80%を超え続けたら高負荷」といったものでした。ところが実データはこう語りました。
| 当初の想定(机上) | 実測で分かったこと | どう直したか |
|---|---|---|
| 水温 >105℃ で警告 | この車は走行中ふつうに103℃前後。105℃も日常域 | 警告を >108℃、危険を >112℃ に引き上げ |
| エンジン負荷 >80% 継続で高負荷 | 全開でも負荷は最大45%止まり(ターボ車では過小評価される) | 負荷単体の判定は不使用。ブースト×回転数で代替 |
| ブースト(感覚で設定) | 正圧が出ているのは走行時間のわずか数%、ピークは実測値で確定 | 立ち上がり/全開の 2段 を実測ピークから設定 |
一番驚いたのは水温です。「105℃=危険」という素朴な直感は、この車にとっては完全な誤りでした。105℃はこの車の"日常"です。もし机上の値のまま作っていたら、相棒AIは普通に走っているだけで「警告」を連発する、うるさいだけのアプリになっていました。データが設計を正してくれた 瞬間です。
負荷の件も象徴的でした。「負荷80%で高負荷」は、全開でも45%にしか届かないこの車では永遠に発火しない「死にトリガー」でした。ターボ車の計算上のエンジン負荷は実際の加速感より小さく出る、という特性が実データではっきり見えたので、負荷単体はやめて、ブーストと回転数の組み合わせで「全開で踏んでいる」を捉えることにしました。
もう一度データで殴る — 文脈を変えて再検証
ただ、1本のログで決めた閾値には不安が残ります。「たまたまその走り方だっただけでは?」という疑いです。そこで、わざと走り方を変えた追加ログ をもう3本録って、同じ閾値で殴り直しました。
- ワインディング(コーナーの多い道)
- 長時間の市街地(アイドリングを跨ぐ)
- 冷間始動(エンジンが冷えた状態から)
結果、閾値は どれも誤発火せず、変更不要 でした。むしろ、新しい発見がいくつも拾えました。
- 横G:最初のログでは薄かったコーナリングのGが、ワインディングで明確なピークを記録。「コーナリング検知」の閾値が妥当だと裏取りできました。
- 暖機の挙動:冷間始動のログで、油温は水温より遅れて温まることが実測で見えました(物理的に筋が通る)。これは第3部で触れる「暖機中の声かけ」の素材になります。
- 油温の一時欠損:長時間ログで油温が一時的に取れなくなる場面がありました。「値が取れないときに嘘の数字を出さない」フォールバックが要る、という設計上の宿題が見つかりました。
1本で決めた基準が、文脈の違う3本でも破綻しなかった。保守的なマージンを取ってデータドリブンで決めた設計が、追加データで確証に変わりました。
M3:エッジで決定論的に判定するトリガーエンジン
実データで基準が固まったので、いよいよ トリガーエンジン(M3)を作ります。これが「いつ・何を言うべきか」を端末側で判定する心臓部です。まだLLMは使いません。喋りもしません。「このタイミングで、この種類のイベントが発生した」というところまでを、決定論的に(=同じ入力なら必ず同じ結果になるように)判定してログに出します。
やっていることはシンプルです。
- 定期報告のタイマーで「状態レポート」のイベントを出す
- 水温・油温が閾値を超えたら「警告」「危険」のイベントを出す
- 加速・減速・コーナリングのGが閾値を超えたらイベントを出す
- 同じイベントが連発しないよう、クールダウン(待ち時間)を設ける
- モード(おだやか/スポーツ)で閾値のセットが切り替わる
ここで大事なのは、安全に関わる判定を全部ここ(エッジ・決定論的)に閉じ込めた ことです。「危険かどうか」をLLMに尋ねたりはしません。LLM は後(第3部)で、ここで確定した事実を自然な言葉にするだけに使います。確率的に揺れうるものに安全判断を任せない、というADR-004の思想を、実装で担保した形です。
ADR-004(抜粋): LLMは運転判断をしない
・安全に関わる判定(閾値超え検知)はエッジのルールで決定論的に行う
・LLMが使えないとき(圏外・エラー)も、定型文で機能を止めない
・ポーリングは厳選したPIDを安全なレートに抑え、ECUに負荷をかけない
層分離の恩恵 — 実車なしでロジックを検証できる
M3 を作るとき、この相棒AIの設計でいちばん効いた判断が姿を見せます。それは、第1部でも少し触れた 「コア層」と「アダプタ層」の分離(ADR-002)です。
- コア層:PIDの計算、閾値判定、モード切替、ブースト算出。これらは Android の部品を一切使わない純粋なロジック にしてあります。
- アダプタ層:Bluetooth通信、センサー読み取り、音声出力、LLM呼び出し。こちらは Android 固有です。
この分け方の何がうれしいか。コア層は実車もスマホも無しで、PCだけでテストできる のです。M2.5 で記録した走行ログをコア層に流し込めば、「このログを再生したとき、意図したタイミングで意図したイベントが出るか」を、車に乗らずに単体テストで確かめられます。
| 層 | 中身 | テスト方法 |
|---|---|---|
| コア層 | PID計算・閾値判定・モード・ブースト算出 | PC上の単体テスト(走行ログを再生) |
| アダプタ層 | Bluetooth・センサー・音声・LLM呼び出し | 実機テスト(最終ゲート) |
トリガーエンジンは判定ロジックの塊なので、ほぼコア層に収まります。おかげで M3 は、実車に乗らずとも大部分を検証できました。「どこを実車で確かめ、どこをPCで確かめるか」をあらかじめ設計で分けておいたことが、ここで効いてきます。
業務感覚への接続 — 「データで基準を決める・安全の軸は人が握る」
第2部でやったことを、普段の業務の言葉に置き換えると、こうなります。
- 思い込みでなく実データで基準を決めた。「105℃=危険」という直感を、1本の走行ログが正してくれた。机上の値のまま進めていたら、使いものにならないアプリになっていた。
- 一度決めた基準を、文脈を変えて殴り直した。1本で満足せず、ワインディング・市街地・冷間始動で再検証して、初めて「この基準で大丈夫」と言えた。
- 安全・判断の軸は自分(人)で握った。何を危険とみなすか、どう判定するかはエッジに決定論で閉じ込め、LLMには委ねなかった。
これも、日々の業務でやっていることとそのまま重なりました。お客様の環境でも、「たぶんここがボトルネックだろう」という当たりだけで動かず、メトリクスやログの実データで裏を取ってから手を打ちます。そして、可用性やセキュリティのような外せない軸は、便利なツールに丸投げせず自分で握る。この姿勢が、ワークフローでアプリを作るときにもそのまま効きました。手を動かすのはワークフロー、基準とデータで決める判断は人、という分担です。
次回予告
第3部では、ついに 「言葉と声を与え、仕上げる」 話をします。ここまで端末側で確定させた「事実」を、LLM(Gemini)に渡して自然な一言に変え(M4)、それを音声で鳴らして相棒AIとして完成させ(M5)、運転中に一瞥できるUIを実機で見ながら仕上げます。
そしてこの第3部が、三部作でいちばん正直に「うまくいかなかった話」を書く回になります。**「静的レビューはAPPROVED、でも実機ビルドは通らない」**この切り分けを、権限漏れ・型推論の循環・画面を貫くゲージの暴発といった実際のつまずきとともにお届けします。三部作全体の回収も第3部で行います。
| 記事 | 位置づけ | 内容 |
|---|---|---|
| 機能紹介編(公開済み) | 土台 | Kiro のワークフロー機能の紹介 |
| 第1部(公開済み) | 実践記 | 全体像を描いてから動く |
| 第2部(本記事) | 実践記 | データで裏取りし、エッジで判定する |
| 第3部(公開済み) | 実践記 | 言葉と声を与え、仕上げる(三部作の回収) |
まとめ
- 相棒AIの心臓部「いつ・何を言うか」の判定を、端末側(エッジ)で決定論的に 組んだ。LLM はまだ使わない。安全判断はエッジに閉じる(ADR-003/004)。
- 監視指標を揃え(M2)、標準PIDに無いブースト圧は MAP − 大気圧 で算出。取得可否は実機スキャンで確定した(ADR-008/009)。
- 閾値を机上で決めず、走行ログを溜めて実データで裏取りした(M2.5)。「105℃=危険」という机上の直感は誤りで、データが設計を正した。
- 1本で満足せず、文脈を変えた追加ログで再検証。閾値は誤発火せず、横G・暖機挙動・油温欠損という新しい素材まで拾えた。
- コア層とアダプタ層の分離(ADR-002)のおかげで、トリガー判定は実車なしでPC上の単体テストで検証できた。
- この進め方は、業務でやっている「実データで基準を決める・安全の軸は人が握る」とそのまま重なった。手を動かすのはワークフロー、データで決める判断は人。
この記事が誰かのお役に立てれば幸いです。以上 Yoshi でした。
参考
- Kiro IDE のワークフロー機能を紹介 〜レシピとランタイムで"段取り"を任せる〜(DevelopersIO) - 本シリーズの土台となる機能紹介編
- 第1部:全体像を描いてから動く(DevelopersIO) - このシリーズの第1部
- Kiro 公式サイト
- Kiro 公式ドキュメント
- 第1部: 全体像を描いてから動く(構想 → 実車にデータ取得)
- 第3部: 言葉と声を与え、仕上げる












