train-delay-advisor の開発で、6月13日は1日で大きく進んだ日だった。データを集める仕組みを動かし始めたら、その日のうちにアプリの心臓部「言語化」まで完成してしまった。
Contents
やったこと
最初にやったのは .gitignore の確認だった。APIキー(.env)と収集中の生データ(data/)がGitHubに上がらないようになっているか。これを毎回最初に確認する習慣にしている。理由は単純で、一度でも公開リポジトリにAPIキーが上がると世界中から見える状態になり、最悪第三者に使われて課金されるからだ。
次に fetch_trains.py を作成し、都営線の全列車の在線位置を約90秒ごとに取得する仕組みをテスト実行。94本取得・遅延1本という結果で正常動作を確認した。90秒という間隔は、APIの更新間隔がそもそも約90秒だから。それより速く叩いても同じデータが返るだけで、無駄にAPIへ負荷をかけることになる。
収集スクリプトをGitHubに登録し、caffeinate -i でMacがスリープしても収集が止まらないようにして本格稼働を開始。遅延は毎日起きるわけではないので、収集は早く始めるほどデータが貯まって得になる。
その後 analyze_intervals.py で、朝ラッシュ(07:00〜09:30)の浅草線の駅ごとの間隔を実データから算出した。本線(浅草・新橋・大門など)は平均約3分間隔、支線(西馬込方面)は約5分間隔・本数も約半分。実際の運行形態をデータが正しく検出できていることが確認できた。
続けて「到着レート × 間隔」という単純な式で、駅ごとのホーム滞留人数を試算。主要駅の平常時と最大時を比べると、差が数字としてはっきり出た。
最後に、出た判断結果を自然な日本語の一文に直す「言語化」レイヤーを実装。ここはローカルLLMに任せつつ、検算フォールバックを入れて事実改変を防止する作りにした。「新橋は今、電車の間隔が9.0分(平常の2.0倍)で、1本待つと空いた電車が来ます」——この一文が、生データから検算付きで自動生成されるようになった。
気づいたこと
一番の収穫は「電車の間隔が空くと、ホームの待ち人数もその分だけ増える」という、当たり前に思えることが実データで確認できた点だ。これがアプリの判断根拠の土台になる。観測・物理モデル・判断・言語化の4レイヤーが、ターミナル上で実際につながって動いていることを確認できた1日だった。
追記:watch.pyの本当の役割に気づいた話
この日に作った監視用スクリプト watch.py(「1本待て」を記録する仕組み)について、数日後にあらためて考える機会があった。最初は「帰宅後の答え合わせ用」という位置づけだと思っていたが、それだけではないと気づいた。
本来の主目的は「見逃し防止」だ。「1本待て」が出るのは団子(電車の混み方の偏り)が起きた特定の数十秒〜数分間だけで、通勤中にスマホをずっと見続けることは不可能。だからこそ、自分の代わりに60秒ごとに見張ってくれる仕組みが必要だった。答え合わせは、その保険を活用した副産物にすぎない。
さらに、この「定期的に状態を確認し、条件に合えば記録する」という型は電車アプリ専用ではないとも気づいた。天翔ポータルのAI予測が「異常」を出した瞬間を記録する仕組みや、病院のログ監視で特定のエラーコードが出た瞬間を記録する仕組みにも、そのまま使える監視・検知系のテンプレートになっている。一度自分で書いたことがある型は、次は迷わず作れる——これが今回一番大きな気づきだった。
Python / ODPT / 物理モデル / ローカルLLM
このブログでは、ローカルLLM・Aider・個人開発の試行錯誤をそのまま記録しています。
