“通報アプリ × 軽いSNS × 地図 × ポイント × AI(AIは黒子)” 簡易実装イメージ

“通報アプリ × 軽いSNS × 地図 × ポイント × AI(AIは黒子)” 簡易実装イメージ(アイデアメモ パート2)
主役はデータ収集と利活用。AIは整理・匿名化・推計の黒子。ぼかしは顔/ナンバーではなく車体まるごと。生データは管理団体のみ。
本稿は「いますぐ作り始められる」ことを最優先に、Web+Webアプリ前提での最小構成、DB(マスタ/業務/監査)、API、非同期タスク、重い演算を切り離した“AI API/ワーカー”の形を一気に示します。難しいところは後回し、でも穴は開けない。直球でいきます。
1. 全体観(最小でも回る構成)
フロント
API
DB/PostGIS
S3(anon/raw)
Queue
AIワーカー
定点カメラ
[利用者PWA/ブラウザ] ─── HTTPS ───> [API Gateway/ALB → Web API(FastAPI)] │ │ │(事前署名URLで直UP) │SQL(PostGIS) ├──────────────> [S3 anon/] ├────> [Aurora PG + PostGIS] │ │ │ └────> [SQS] → [AIワーカー群(内部API)] │ ├─ anonymize_vehicle(車体ぼかし) │ ├─ classify(カテゴリ推定/NG検知) │ ├─ dedupe(重複クラスタ) │ └─ traffic_aggregate(利用率集計) [定点カメラ/エッジ] ──(匿名化/カウント済みメタ)──> [API /devices/{id}/events]
ポイント:アップロードは匿名化側 S3 anon/
をデフォ。S3 raw/
は原則使わず、管理団体の明示ワークフロー時のみ。重い処理はSQS/ワーカーへ。
2. 初期に用意するクラウド要素(MVP)
- 静的フロント: S3 + CDN(PWA、Map描画、オフライン対応)
- Web API: FastAPI(Fargate か Functions/Lambda)
- DB: Aurora PostgreSQL + PostGIS
- ストレージ: S3(
anon/
=ぼかし済み公開用、raw/
=管理団体限定) - キュー: SQS(ingest/anonymize/classify/dedupe/traffic_aggregate)
- 認証: Cognito/OIDC(LINEログイン併用可)
- 監視: CloudWatch + APM(例:OpenTelemetry)
3. データモデル(最小セット)
マスタ
organizations
:自治体/団体categories
:通報カテゴリ(10個程度に絞る)areas
:担当エリア(MultiPolygon)road_links
:道路リンク(LineString)
業務
reports
:通報(Point, status, attributes)report_media
:画像/動画(anon/
,raw/
鍵)report_status_history
:進捗履歴devices
:定点カメラ/エッジcamera_events
:カウント結果(車/人)traffic_agg_hourly
:道路利用率(リンク×時刻)
-- 位置はPostGIS。公開時は丸める(例:50m格子) CREATE TABLE core.reports( id UUID PRIMARY KEY, org_id UUID NOT NULL, user_id UUID NULL, category_id INT NOT NULL, title TEXT, body TEXT, status TEXT CHECK(status IN ('received','triaged','in_progress','resolved','rejected')) DEFAULT 'received', location geometry(Point,4326), location_precision_m INT DEFAULT 50, attributes JSONB DEFAULT '{}'::jsonb, dedupe_group_id UUID, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now() ); CREATE INDEX ON core.reports USING GIST(location);
4. API(公開REST)
メソッド | パス | 概要 |
---|---|---|
GET | /api/v1/master/categories | カテゴリ一覧 |
POST | /api/v1/reports | 通報作成(メタ登録→S3事前署名返却) |
POST | /api/v1/media/presign | S3事前署名の発行 |
POST | /api/v1/media/confirm | アップ完了通知→anonymize_vehicle投入 |
GET | /api/v1/reports?bbox&status | 地図用リスト(クラスタリング済み) |
PATCH | /api/v1/reports/{id}/status | 進捗変更(operator以上) |
POST | /api/v1/devices/{id}/events | 定点の集計メタ投入(匿名化済み計数) |
GET | /api/v1/traffic/links/{id}/hourly | 道路利用率(時系列) |
例:通報登録→アップロード
// 1) メタ登録 POST /api/v1/reports { "category_code": "road_damage", "title": "舗装の穴", "body": "夜間に危ない", "event_time": "2025-09-14T09:30:00+09:00", "location": {"lat": 35.68, "lng": 139.76, "precision_m": 50}, "media": [{"kind":"image","presign":true}] } // 2) レスポンス(S3 anon/ への事前署名) { "report_id": "d1c7-...", "uploads": [ {"media_id":"m-1","url":"https://s3/anon/...","fields":{"key":"anon/..","policy":".."}} ] } // 3) クライアントがS3へ直接PUTした後 POST /api/v1/media/confirm { "media_id": "m-1", "sha256":"abc...", "raw_requested": false }
5. “難しい演算部”はAPI/ワーカーに切り離す
原則: フロントとWeb APIは薄く。重い処理(画像処理・類似検索・時系列集計)はキュー経由でワーカーが実施。Web APIは結果だけを見る。
(A) 内部API(サービス間)
POST /_internal/anonymize/vehicle
:車体まるごとぼかしPOST /_internal/classify
:カテゴリ推定・NG判定POST /_internal/dedupe
:重複クラスタ更新POST /_internal/traffic/aggregate
:道路利用率集計
VPC内/認証必須。外部からアクセス不可。
(B) ワーカー(擬似コード/Python)
@app.task(name="anonymize_vehicle") def anonymize_vehicle(media_id: str): media = db.get_media(media_id) img = load_from_s3(media.s3_key) # ぼかしは車体全体 boxes = detect_vehicles(img) # car/bus/truck/bike... img2 = mosaic_outside_safe_margin(img, boxes) save_to_s3(img2, media.s3_key) # anon/ に上書き保存 enqueue("classify", {"report_id": media.report_id})
重複クラスタ(dedupe)イメージ
def recompute_cluster(report_id): r = db.reports[report_id] candidates = search_nearby( point=r.location, radius_m=120, time_window="±24h", text_embed=r.attributes['text_embed']) cluster_id = union_find_by_similarity(candidates + [r]) db.update(report_id, dedupe_group_id=cluster_id)
道路利用率(定点カメラ→リンク集計)
POST /api/v1/devices/{id}/events { "captured_at": "2025-09-14T00:00:00Z", "counters": {"vehicle": 86, "pedestrian": 240}, "link_id": 10234 } # 集計ジョブ(hourly) INSERT INTO traffic_agg_hourly(link_id,hour,vehicle_cnt,ped_cnt) VALUES(...) ON CONFLICT (...) DO UPDATE ...
6. フロント(PWA/Map)実装イメージ
画面
- ホーム:地図+進捗フィルタ(クラスタ表示)
- 新規通報:撮る→位置→送る(3タップ)
- 詳細:写真(ぼかし済み)、進捗、類似報告
- マイページ:ポイント、履歴
- 運用:重複束ね、優先度キュー、道路利用率グラフ
コード(超要点)
// 位置丸めはサーバ側。クライアントは細かいことしない。 async function createReport(meta){ const r = await fetch('/api/v1/reports',{method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(meta)}); const {uploads} = await r.json(); // presigned URLに直PUT await fetch(uploads[0].url,{method:'PUT',body: selectedFile}); await fetch('/api/v1/media/confirm',{method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({media_id: uploads[0].media_id, sha256: fileHash})}); }
地図は MapLibre GL 等。アクセシビリティのためボタンは大きく、音声入力も許容。オフライン時はService Workerで下書き保存→復帰時に送信。
7. セキュリティ/プライバシ運用の肝
- ぼかし原則: 顔/ナンバー単体ではなく車体まるごと。誤検知を避ける。
- rawの管理: 原則使わない。必要時のみ管理団体の承認付きで保管/閲覧。アクセスは監査ログ必須。
- 公開データ: 座標は丸め(例:50m)、期間限定、再識別チェック。
- RBAC: resident / moderator / operator / admin を分離。
- レート制御/WAF: 荒らし対策はフロントではなくゲートで止める。
8. 運用KPI(MVPで見る数字)
- 重複統合率(dedupe成功率)
- 匿名化成功率(車体ぼかし漏れ0を目標)
- 受付→現地確認リードタイム
- 道路リンク×時間のカバレッジ率
- 初回→翌月継続率(ポイント依存度は低く)
9. ディレクトリと設定例
repo/ ├─ frontend/ # Next.js PWA ├─ api/ # FastAPI (REST) │ ├─ routes/ # reports, media, devices... │ ├─ services/ # db, s3, presign, auth │ └─ schemas/ # pydantic ├─ workers/ # AIワーカー(anonymize/classify/dedupe/aggregate) │ └─ tasks/ ├─ infra/ # IaC(Terraform) / pipelines └─ docs/
# .env(例:API) DATABASE_URL=postgresql+psycopg2://... S3_BUCKET=city-app-bucket ANON_PREFIX=anon/ RAW_PREFIX=raw/ QUEUE_URL_ANONYMIZE=... JWT_AUDIENCE=...
10. フェーズ2で足すもの(必要になったら)
- GraphQL(AppSync等)でリアルタイム更新
- OpenSearchで全文/類似検索
- Step Functionsでワークフローの可視化(失敗時の再試行/DLQ)
- ダッシュボード(QuickSight/Looker Studio)
- Open311互換API公開、オープンデータ自動エクスポート
11. 用語説明集(自治体担当者向け・横文字を噛み砕く)
技術用語を「要はどういうこと?」に言い換えました。会議で出てきたら、この表をそのまま使ってOKです。
用語 | 意味(かんたん) | 役割・たとえ |
---|---|---|
Webアプリ / PWA | ブラウザで動くアプリ。ホーム画面に入れられ、電波が弱くても一部動く。 | 「インストールいらずのアプリ」。 |
CDN | 全国にある中継所から近場で配る仕組み。 | 配達拠点が多い宅配便。 |
S3 | 画像や動画を置く大きな倉庫。 | 「anon = ぼかし済み棚」「raw = 生データ棚」。 |
事前署名URL(Presigned URL) | 一時的に倉庫へ直接アップできる合鍵。 | 期限つきの搬入口パス。 |
API | アプリ同士の受け口(やり取りの決まり)。 | 役所の「窓口の様式」。 |
FastAPI / NestJS | APIを作るための道具。 | 窓口の仕切りや帳票を作るツール。 |
ALB / API Gateway | 外からの通信の玄関。 | 正面ロビーの受付。 |
ECS Fargate | サーバ管理なしでアプリを動かす台。 | 「レンタカー、燃料補給も自動」。 |
Lambda | 呼ばれたときだけ動く小プログラム。 | 必要な時だけ呼ぶタクシー。 |
Aurora PostgreSQL | 信頼性の高いデータベース。 | 台帳の本丸。 |
PostGIS | DBに地図の頭脳を足す。 | 点や道路線を扱える地図機能。 |
OpenSearch | 全文検索・似ている文章探し。 | 図書館の司書+レコメンド。 |
Redis | 超速い一時置き場。 | 受付の手元メモ。 |
SQS(キュー) | 順番待ちの箱。重い処理はここに並べる。 | 整理券発券機。 |
Step Functions | 処理の流れ図をそのまま機械に実行させる。 | 業務フローの標準化。 |
AppSync / WebSocket | 画面を自動更新する仕組み。 | 電光掲示板が勝手に更新。 |
Cognito / OIDC | ログインの仕組み。LINEログインも接続可。 | 入館証の発行・確認。 |
RBAC | 役割ごとに権限を分ける。 | 住民/職員/管理者で見える範囲が違う。 |
IAM | 誰が何をして良いかの許可証。 | 庁内の権限台帳。 |
KMS / Secrets Manager | 暗号鍵とパスワードの金庫。 | 鍵付きロッカー。 |
VPC / AZ | 自分専用ネットワーク/データセンターの区画。 | 専用フロア/別館。 |
VPC Endpoint | 倉庫(S3)へ裏口直結で安全に接続。 | 荷捌き場の専用通路。 |
CloudWatch / X-Ray / OpenTelemetry | 動作状況の見える化。 | 監視カメラ+動線の可視化。 |
MapLibre GL | 地図を画面に描く道具。 | 地図描画エンジン。 |
タイル / クラスタリング | 地図を小分けに配る/近いピンをまとめて表示。 | 分割配布/人数カウントの丸め表示。 |
bbox | 今見えている地図の四角い範囲。 | 地図の窓枠。 |
JSON/JSONB | 柔らかい形式の箱に情報を入れる。 | 可変の追記事項メモ。 |
GiST / GIN(索引) | 地理/文章検索を速くする仕掛け。 | しおり・目次。 |
geometry(Point/LineString) | 点/線として位置情報を保存。 | 地点/道路線。 |
位置丸め | 50mなど大まかな位置だけ公開。 | 自宅の真上にピンを立てない。 |
匿名化 | 個人が特定されないよう隠す。 | 画角・解像度・位置の配慮。 |
車体まるごとぼかし | 顔/ナンバー単体より安全側で隠す。 | 誤検知対策・再識別防止。 |
エッジ処理 | カメラ側で先に匿名化やカウントして送る。 | 現場で下ごしらえ。 |
データレイク / Glue / Athena / QuickSight | 倉庫に貯めたデータをSQLで分析・可視化。 | 統計室と壁一面のグラフ。 |
Open311 | 通報の標準仕様(カテゴリ/進捗)。 | 他システムと連携しやすい型。 |
Webhook | 外部システムへ自動でお知らせを送る。 | 連絡票の自動FAX。 |
DLQ | 失敗タスクの保管箱。 | 要再処理トレイ。 |
IQR | 外れ値(おかしな数)を見つける指標。 | 異常検知の物差し。 |
A/Bテスト | 2案を同時に試して良い方を選ぶ。 | 案内文AとBで比較。 |
Embedding(埋め込み) | 文章や画像を数列に変えて似ている度合いを見る。 | 似てる度=コサイン類似度。 |
重複検知 / クラスタ | 似た報告をまとめる。 | 同じ穴の報告を1束に。 |
マップマッチング / Link ID | カウントを道路の区間番号に結びつける。 | 集計のひと単位。 |
「この言葉が出たらこう理解」でOK(超要約)
- PWA=ブラウザで動くアプリ。
- API=システム間の窓口。
- PostGIS=地図に強いDB。
- キュー=順番待ち箱(重い処理を後で)。
- 匿名化/ぼかし=個人に結びつく情報を隠す。原則は車体まるごと。
- データレイク=とりあえず貯めて後から分析。
- Open311=通報の共通ルール。
まとめ:横文字は多いですが、要は「データを安全に集め、見える化し、根拠を持って手を打つ」ための道具立てです。
©株式会社ビー・ナレッジ・デザイン