版数: 2.0  |  更新日: 2026-06-15  |  層: ④ 外部連携

外部連携仕様(山梨大 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)を埋め、合意内容を本ページに反映していきます。

「契約」は DB 用語ではありません。 ソフトウェア工学では 連携契約(Integration Contract)API 契約 と呼び、 「こちら側はこう渡す/向こう側はこう受け取る」という境界の約束事を指します。 DB のスキーマもその一部ですが、Storage のパス規則・削除ポリシー・認証方式も同じ「契約」に含まれます。

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 連携ガイド
データ参照(手動) 開発者が psqlfeedbacks を 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 日)
ポリシー次第
段階StorageDB(feedbacks)例(要合意)
Activestorage/{uuid}.jpg行あり、consumed_at NULL学習待ち
Consumed同上(またはそのまま)consumed_at 設定学習ジョブ ID と紐付け可
Archivedstorage/archive/{yyyy}/{uuid}.jpgarchived_at 設定監査・再学習用に短期保持
Deletedファイル削除行削除 or メタのみ残すアーカイブから 60 日後(たたき案)
60 日・180 日は仮置きです。競馬シーズン・再学習頻度・個人情報ポリシーに合わせて会議で決めてください。 削除ジョブは役場 PC 上の cron / タスクスケジューラ、または Worker 完了時の後処理など実装方針も要合意です。

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. 会議の進め方(推奨)

はい、本資料 + 質問表を持って調整会議を開くのがよい進め方です。

  1. 現状共有(10 分) — §1。Phase1 で何が動いていて、Worker がまだ FB を読んでいないこと
  2. 方式選択(20 分) — §3.2 の A/B/C と Q2・Q5
  3. データ条件・ライフサイクル(20 分) — §3.3 と Q4・Q6・Q9(60 日は仮のためその場で数字を決める)
  4. DB スコープ(15 分) — §2 と Q7。feedbacks 拡張だけ先に / Phase 2 は後回し
  5. 次のアクション(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/consumebody: {"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. 関連資料

§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_TOKENClassic PAT(repo スコープ)または Fine-grained(Contents + Actions)
GITHUB_REPOSITORYowner/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原因対処
403PAT スコープ不足repo 権限を付与
404リポジトリ名誤り / トークンにアクセス権なしGITHUB_REPOSITORY を確認
422workflow が default ブランチにないandroid-build.yml を push

例外時は DeveloperNotifierlevel="error" 通知を推奨します。

6. フォルダの持ち込み方

  • 推奨: 本リポジトリを clone し、学習 Worker から integrations/ を import
  • 代替: integrations/ フォルダだけを山梨大リポジトリにコピー(依存: httpx のみ)

7. 関連資料