外部連携仕様(山梨大 Worker)
この文書について(標準インターフェース設計の構成)
| 目的 | 関係者間の責任分界とデータの渡し方を合意可能な形で定義する |
|---|---|
| 想定読者 | 山梨大(Worker・アプリ)、役場インフラ担当、プロジェクト調整役 |
| いつ読むか | 連携会議の前、結合試験の設計時、本番移行のレビュー時 |
一般的な連携ドキュメントの章立て:
| 標準の章 | 本資料 |
|---|---|
| 関係者・環境 | 環境の整理 |
| データモデル・契約 | ER 図、Worker フロー |
| 方式比較・仮定 | 先行構築の仮定(パターン A 正) |
| 会議・合意 | 質問表、会議の進め方 |
| 技術 IF(API / SDK) | §2 GHA dispatch モジュール |
山梨大 AI Worker との連携境界・データ取得方式・GHA 組み込みを 1 ページにまとめています。
§1 連携境界・FB データ
0. 環境の整理(2026-06-15)
| 環境 | 役割 | 備考 |
|---|---|---|
| 開発者 PC(検証用) | いまの Docker・Tunnel・実機検証 | 本番移行前の検証場所 |
| 役場 PC | 本番 — FB 受信・DB・Storage・Worker・アプリ | WBS 3.1.1 移設先。移設手順は Runbook §5 |
| 山梨大 | アプリ・Worker Docker を開発中 | 最終的に役場 PC へ移設 → 本番では Worker と DB が同一マシン(パターン A に適する) |
0.1 FB 件数と DB の要否
想定: 月 100 件以下。この規模ではファイル中心(DB レス)でも運用可能ですが、Phase1 では PostgreSQL + feedbacks が既に稼働しているため、当面は DB を維持しパターン A で進める方針です。役場 PC 移設時に DB レスへ簡略化するか再検討します。
山梨大(AI Worker・iPhone アプリ)と役場側(FB 受信インフラ)の役割分担を決めるためのたたき案です。 会議で質問表(§5)を埋め、合意内容を本ページに反映していきます。
1. 現状(Phase1 実装済み)
| 項目 | 現状 |
|---|---|
| FB 受信 | アプリ → Tunnel → FastAPI POST /api/v1/feedback → DB + Storage 保存 |
| DB(実装済み) | feedbacks のみ(id, image_path, inference_result, created_at) |
| 画像ファイル | data/storage/(Docker ボリューム)に {uuid}.jpg。DB の image_path はその絶対パス文字列 |
| AI Worker(本リポジトリのスタブ) | FB データは読まない。 models/base/stub.tflite をコピーして manifest.json を更新し、GHA を dispatch するだけ(Worker runbook) |
| 山梨大 Worker(本番想定) | 未接続。 データ取得方法は未合意。GHA dispatch 用モジュールのみ提供(GHA 連携ガイド) |
| データ参照(手動) | 開発者が psql で feedbacks を SELECT、エクスプローラで data/storage/ を確認 — 自動パイプラインは未実装 |
2. 想定 ER 図(たたき案)
実線枠 = 実装済み、点線 = 合意後に追加候補。
Phase 2 テーブル(training_jobs 等)は 参照 §2 DB の案のまま保留可能です。
erDiagram
feedbacks {
varchar id PK "UUID"
varchar image_path "Storage 上のパス"
text inference_result "端末推論 JSON"
timestamptz created_at "受信日時"
timestamptz consumed_at "提案: 学習取込済み"
timestamptz archived_at "提案: アーカイブ移動日"
}
training_jobs {
varchar id PK
varchar status "pending/running/completed/failed"
timestamptz started_at
timestamptz completed_at
int feedback_count
}
feedback_training {
varchar feedback_id FK
varchar training_job_id FK
}
feedbacks ||--o{ feedback_training : "used_in"
training_jobs ||--o{ feedback_training : "uses"
consumed_at / archived_at / feedback_training は提案列・提案テーブルです。
Worker が「DB から条件付きで一覧取得」するなら、最低限 consumed_at IS NULL のようなフラグが必要になります。
3. AI Worker の動き(想定フロー)
月次(または手動)トリガーで Worker が FB を学習に使う流れの一例です。数値(60 日等)は会議で決めます。
3.1 データ取得〜学習
sequenceDiagram
autonumber
participant Cron as スケジューラ / 手動
participant WK as AI Worker(山梨大)
participant DB as PostgreSQL
participant ST as Storage(data/storage)
participant GHA as GitHub Actions
Cron->>WK: 学習ジョブ開始
WK->>DB: SELECT 未使用 FB
例: consumed_at IS NULL
AND created_at >= 前回以降
DB-->>WK: id, image_path, inference_result
loop 各 FB 件
WK->>ST: image_path のファイルを読込
ST-->>WK: 画像バイト
end
WK->>WK: 学習実行
WK->>DB: training_jobs 登録・更新(任意)
WK->>DB: consumed_at = NOW() を更新
WK->>GHA: repository_dispatch(model-updated)
3.2 取得方式の 3 パターン(会議で 1 つ選択)
| 方式 | Worker の動き | メリット | デメリット |
|---|---|---|---|
| A. DB + Storage 直アクセス | PostgreSQL に接続して SELECT → image_path でファイル読込 |
シンプル、バッチ向き | DB 接続情報の共有・ネットワーク経路が必要 |
| B. エクスポート API | FastAPI に GET /api/v1/feedback/export(認証付き)で一覧+署名付き URL |
DB を Worker に開放しない | API 実装・ページング設計が必要 |
| C. ファイル共有 / 手動 | 管理者が ZIP や共有フォルダで渡す(現状に近い) | 最初の結合試験が早い | 本番運用には向かない |
3.3 画像ライフサイクル(たたき案 — 60 日削除)
画像は DB には入れず Storage 上のファイルとして保持します。 「DB の定石」としては、メタデータ(いつ・学習に使ったか・いつ消すか)を DB に持ち、実ファイルは Storage ジョブで削除するのが一般的です。
stateDiagram-v2
[*] --> Active: FB 受信(feedbacks 行 + storage ファイル)
Active --> Consumed: Worker が学習に取込
consumed_at 設定
Consumed --> Archived: ファイルを archive/ へ移動
archived_at 設定
Archived --> Deleted: 保持期限経過(例: 60 日)
Storage 削除 + DB 行削除 or 匿名化
Active --> Deleted: 未使用のまま TTL(例: 180 日)
ポリシー次第
| 段階 | Storage | DB(feedbacks) | 例(要合意) |
|---|---|---|---|
| Active | storage/{uuid}.jpg | 行あり、consumed_at NULL | 学習待ち |
| Consumed | 同上(またはそのまま) | consumed_at 設定 | 学習ジョブ ID と紐付け可 |
| Archived | storage/archive/{yyyy}/{uuid}.jpg | archived_at 設定 | 監査・再学習用に短期保持 |
| Deleted | ファイル削除 | 行削除 or メタのみ残す | アーカイブから 60 日後(たたき案) |
4. 全体配置(誰が何を触るか)
flowchart TB
subgraph Yamanashi["山梨大(開発・運用)"]
iOS[iPhone アプリ]
WK[AI Worker]
GHAiOS[iOS GHA 将来]
end
subgraph Yakuba["役場 PC + Cloudflare"]
App[Android アプリ]
API[FastAPI FB API]
DB[(PostgreSQL)]
ST[(Storage)]
Tunnel[cloudflared]
GHAand[Android GHA]
end
App -->|HTTPS Bearer| Tunnel --> API
iOS -->|HTTPS Bearer| Tunnel --> API
API --> DB
API --> ST
WK -.->|方式 A/B/C| DB
WK -.-> ST
WK -->|repository_dispatch| GHAand
WK --> GHAiOS
5. 会議用 質問表(山梨大 × 役場)
回答欄は会議後に記入してください。印刷または画面共有用です。
| # | 質問 | 現状の理解(役場側) | 山梨大の回答(記入) | 合意メモ |
|---|---|---|---|---|
| Q1 | 現状、学習用 FB データはどう取得していますか?(手動コピー / DB / その他) | 本インフラでは自動取得なし。スタブ Worker は FB を読まない。手動は psql + フォルダ確認のみ | ||
| Q2 | DB から取得する実装は可能ですか?(PostgreSQL 接続 or API 経由) | 技術的には可能。接続情報・ネットワーク・認証の設計が未決 | ||
| Q3 | Worker の実行場所は?(山梨大サーバー / 役場 PC / クラウド) | 未合意。dispatch だけなら山梨大側から GitHub API で可 | ||
| Q4 | 学習に使う FB の条件は?(期間・件数上限・ラベル等) | — | ||
| Q5 | データ取得方式は A / B / C(§3.2)のどれがよいか | A=直アクセス、B=API、C=手動 — 本番は A または B を推奨 | ||
| Q6 | 学習完了後、画像をいつ・誰がアーカイブ/削除するか | たたき案: 学習後 archive → 60 日後削除(§3.3) | ||
| Q7 | training_jobs 等 Phase 2 テーブルは Worker 側で必要か |
役場側は案のみ(001_schema.sql)。Worker 要件確定後に適用したい |
||
| Q8 | 学習完了 → GHA dispatch(Android / iOS)は現行 GitHubActionsClient で足りるか |
連携ガイド 参照。iOS workflow は将来 | ||
| Q9 | 個人情報・画像の保持期間に法的・運用上の上限はあるか | — | ||
| Q10 | 結合試験の第一歩 — 手動(C)で 1 回通してから A/B に移行してよいか | Phase1 スタブとの差を意識しつつ、段階導入を推奨 |
6. 会議の進め方(推奨)
はい、本資料 + 質問表を持って調整会議を開くのがよい進め方です。
- 現状共有(10 分) — §1。Phase1 で何が動いていて、Worker がまだ FB を読んでいないこと
- 方式選択(20 分) — §3.2 の A/B/C と Q2・Q5
- データ条件・ライフサイクル(20 分) — §3.3 と Q4・Q6・Q9(60 日は仮のためその場で数字を決める)
- DB スコープ(15 分) — §2 と Q7。
feedbacks拡張だけ先に / Phase 2 は後回し - 次のアクション(5 分) — 合意を本ページに追記 → WBS 3.1.3 / 3.2 のタスク化
参加者の目安: 山梨大(Worker・アプリ担当)、役場(インフラ・運用)、必要なら開発支援。 成果物: 質問表の「合意メモ」列が埋まった状態 + 本 HTML の版数更新。
7. 先行構築の仮定パターン(会議待ちせず進める)
質問がすべて埋まるまでステータスを止めないため、以下を仮定して構築を先行します。 会議(§6)で別方式に変わった場合は差分だけ吸収します。パターンごとの試作は Git ブランチで分岐可能(B は API 実装済み)。
| 項目 | 仮定(Working Assumption) | 実装状況 |
|---|---|---|
| 本番の正(main) | A. DB + Storage 直アクセス — 役場 PC 上の Worker が SQL + ファイル読込。数日おきの学習バッチ向き | Worker DB クライアント・バッチ骨格は次イテレーション |
| 未使用 FB の条件 | consumed_at IS NULL AND archived_at IS NULL、任意で日付範囲 |
列追加済み(002_feedback_lifecycle.sql) |
| バッチ(3 本) | ① 学習(Worker)② アーカイブ(consumed → archive/)③ 削除(60 日後・仮) |
未実装 |
| B. Export API(代替) | 遠隔 Worker 用の保険。Bearer HTTP で pending / image / consume | 実装済み(2026-06-15)。本番正は A |
| C. 手動 | 結合試験の第一歩として ZIP / フォルダ共有 | 手順のみ |
| DB レス(ファイルのみ) | 月 100 件以下なら成立。役場移設時に再検討 | 比較用に §0.1 参照 |
| Phase 2 テーブル | training_jobs 等は Worker 要件確定まで適用しない |
DDL 案のみ(参照 §2) |
7.1 パターン A — Worker(本番正)
-- 学習バッチ(例)
SELECT id, image_path, inference_result
FROM feedbacks
WHERE consumed_at IS NULL AND archived_at IS NULL
ORDER BY created_at;
-- 取込後
UPDATE feedbacks SET consumed_at = NOW() WHERE id IN (...);
画像は image_path から Storage を直接読みます。本番では Worker・DB・Storage が同一役場 PC 上に載る想定です。
7.2 パターン B — Export API(代替・実装済み)
いずれも Authorization: Bearer <FEEDBACK_API_KEY> 必須(SEC-01)。
| メソッド | パス | 用途 |
|---|---|---|
| GET | /api/v1/feedback/pending?limit=100&since=... | 未取込 FB 一覧(image_url 付き) |
| GET | /api/v1/feedback/{id}/image | 画像バイナリ |
| POST | /api/v1/feedback/consume | body: {"ids":["uuid",...]} → consumed_at 更新 |
# マイグレーション(既存 DB)
.\scripts\apply-feedback-lifecycle.ps1
# 疎通例(ローカル)
curl -H "Authorization: Bearer $KEY" "http://localhost:8000/api/v1/feedback/pending"
curl -H "Authorization: Bearer $KEY" -o sample.jpg "http://localhost:8000/api/v1/feedback/{id}/image"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"ids":["..."]}' "http://localhost:8000/api/v1/feedback/consume"
会議で A(DB 直) が正のまま確定した場合、B API は使わず SQL で足ります。 遠隔開発期のみ B を使う選択も可。
8. 関連資料
- DB スキーマ — 現行 DDL と Phase 2 案
- 山梨大 GHA 連携 — dispatch のみ確定済み
- 本番導入計画 — コンポーネント Step
- WBS 3.1.1〜3.2
- K-31 全体構成図
§2 GHA 連携ガイド
学習完了後に GitHub Actions(Android / 将来 iOS ビルド)を起動するための 共有 Python モジュールの使い方です。 山梨大側の AI Worker コードから数行で組み込めます。
1. 提供モジュール
| ファイル | クラス / 関数 | 用途 |
|---|---|---|
integrations/github_actions_client.py |
GitHubActionsClient |
学習完了 → repository_dispatch(event: model-updated) |
integrations/notifier.py |
DeveloperNotifier |
開発者へ Webhook 通知(任意) |
2. セットアップ
pip install -r integrations/requirements.txt
integrations/.env.example を .env にコピーし、PAT を設定します。
| 変数 | 説明 |
|---|---|
GITHUB_TOKEN | Classic PAT(repo スコープ)または Fine-grained(Contents + Actions) |
GITHUB_REPOSITORY | owner/repo(例: makoto55879/github-actions-test) |
NOTIFY_WEBHOOK_URL | 任意。Slack Incoming Webhook 等 |
NOTIFY_FORMAT | 任意。slack / discord / generic |
3. 最小コード例(学習完了時)
from integrations.github_actions_client import GitHubActionsClient
from integrations.notifier import DeveloperNotifier
import os
def on_training_complete(model_version: str, model_path: str) -> None:
"""
山梨大 AI Worker の学習完了ハンドラから呼び出す。
model_version: 例 "v3"
model_path: models/ からの相対パス 例 "versions/v3/horse_model.coreml"
"""
client = GitHubActionsClient(
token=os.environ["GITHUB_TOKEN"],
repository=os.environ["GITHUB_REPOSITORY"],
)
result = client.trigger_model_updated(
model_version=model_version,
model_path=model_path,
# iOS 用 workflow を分ける場合は extra_payload で拡張可能
)
DeveloperNotifier.from_env().send(
title="Training complete → GHA triggered",
message=f"{result.event_type} HTTP {result.status_code}",
level="info",
context={
"model_version": model_version,
"model_path": model_path,
},
)
4. 送信される payload
GitHub API POST /repos/{owner}/{repo}/dispatches に以下が送られます。
{
"event_type": "model-updated",
"client_payload": {
"model_version": "v3",
"model_path": "versions/v3/horse_model.tflite"
}
}
受信側ワークフロー: .github/workflows/android-build.yml(将来 ios-build.yml 追加可)。
成功時は HTTP 204(ボディなし)。
5. エラー時の扱い
| HTTP | 原因 | 対処 |
|---|---|---|
| 403 | PAT スコープ不足 | repo 権限を付与 |
| 404 | リポジトリ名誤り / トークンにアクセス権なし | GITHUB_REPOSITORY を確認 |
| 422 | workflow が default ブランチにない | android-build.yml を push |
例外時は DeveloperNotifier で level="error" 通知を推奨します。
6. フォルダの持ち込み方
- 推奨: 本リポジトリを clone し、学習 Worker から
integrations/を import - 代替:
integrations/フォルダだけを山梨大リポジトリにコピー(依存:httpxのみ)
7. 関連資料
- Phase1 Worker 手順 — スタブでの動作確認
- K-22 — PAT 発行場所(ユーザー Settings)
- DB スキーマ —
training_jobs/model_versions設計