LINE API 入口
Webhookで受ける。受信したテキストはあとからAPIで取り直せないため、受信時に署名を確認して保存する。
求職者管理の次設計 · 2026-07-15
LINE APIかTursoか、の二択ではありません。LINE APIは受信・送信の入口、SQLite系DBは会話と判断根拠の保管庫です。無料枠・Worker接続・削除運用まで比較した結果、この仕事の正本はD1に集約するのが安全です。Tursoは「何ができ、なぜ今は正本にしないか」までを併記します。
Webhookで受ける。受信したテキストはあとからAPIで取り直せないため、受信時に署名を確認して保存する。
既存の実行経路を活かす。WebhookとLINE Readerが既にD1を使っている。まずここに候補者との紐付け・事実・監査を揃える。
会話全文を置かない。次の行動、LINE対応要否、AI提案だけを見せ、会話文脈は必要な時だけ開く。
文脈をLINE APIに任せることはできません。APIはイベントを届ける経路であり、判断に使う履歴・状態・監査は自分たちのDBで持つ必要があります。
候補者LINEの規模では両方とも無料枠に収まりやすいです。差は「数字」より、Webhookの保存経路とデータを一つに保てるかです。
| 観点 | D1 | Turso |
|---|---|---|
| Worker接続 | 同一WorkerのDB binding | HTTPS+認証トークン |
| 無料読取 | 500万行/日 | 5億行/月 |
| 無料書込 | 10万行/日 | 1,000万行/月 |
| 無料容量 | 合計5GB | 合計5GB |
| 時点復元 | Free 7日 | Free 1日 |
| 端末SQLite同期 | 標準なし | 明示 push / pull |
Tursoを採用するなら「追加」ではなく「置換」です。先に正本を決めれば、AIがどの状態を信じるかも一つにできます。
既存のactive計画は、管理表を現在状態の正本、TursoをPIIなしの監査台帳と定めています。一方、古いSQLにはSupabase正本の記述があり、実稼働Webhook/LINE ReaderはCloudflare D1を読んでいます。Tursoへ候補者・LINE本文を置くなら、既存計画を更新し、D1/Supabaseからの一回限りの移行と切替を別計画にします。
LINEの一文は証拠です。AIはまず事実を抽出し、既存状態と照合して「変更案」を出すだけにします。承認済みの変更だけが管理表とKPIに届きます。
今の管理表のステータス・戦略/方針・次の行動・ニーズ・金額はそのまま仕事の中心に置きます。LINEの全文をセルに入れず、対応判断に必要な短い派生情報だけを横に見せます。
担当者はセルを探さず、「誰から何が来たか」を起点に candidate へ任せます。AIは状況を要約して、変更ではなく確認しやすい提案を返します。
Webhookを検証してD1へ一度だけ保存する。重複イベントはイベントIDで除外する。
LINE user ID → candidate_id → profile_url を基本キーにする。未連携・同姓同名・低信頼なら要確認キューへ送る。
例:日程候補、意思、質問、辞退の兆候。本文ではなく根拠のメッセージIDと信頼度を一緒に持つ。
既存のステータス、戦略、次の行動を読んで矛盾を出す。KPIになるイベントなら必要項目が揃っているかも確認する。
ここがL2ゲート。承認後にだけ、正本・管理表・KPI・実行ログを一つのイベントIDで更新し、再読込で確認する。
D1もTursoもSQLite系です。DB製品ではなく、候補者の現在値を一行に潰さず、会話とAI判断を別テーブルにすることが精度と削除しやすさを決めます。
candidates 内部candidate_id・profile_url・確定状態・状態version ├── line_identity_links LINE user ID・紐付け根拠・確信度・連携状態 ├── line_messages webhook_event_id・方向・本文・受信時刻・削除期限 ├── candidate_facts 抽出事実・根拠message_id・信頼度・抽出version ├── candidate_change_proposals 状態/KPIの提案・承認/却下・操作event_id ├── sheet_projection_outbox 承認済み管理表反映・再読込状態 └── candidate_lifecycle 最終確認・保存停止・削除予定・保持保留
氏名やスプシ行番号は候補者キーにしません。内部IDと正式 profile_url、LINE user ID、WebhookイベントIDを役割ごとに一意にします。
candidate_id は内部UUID、profile_url は候補者を一意に示す業務キー、LINE user ID はLINE側のキー。氏名・表示名は表示専用にし、紐付けが不確かなら要確認キューへ送る。
line_messages は webhook_event_id を一意にする。再配信でも二重に保存しない。候補者削除時は外部キーの ON DELETE 方針に従い、本文・LINE紐付け・未送信予約を同じ削除単位にする。
事実、提案、確定状態、管理表outboxを一つのトランザクションで更新する。途中で失敗すれば確定状態だけが先に変わらず、外部表は同じoutboxを再実行できる。
候補者名、LINE本文、profile_urlをGit管理のフォルダへ出しません。実データはD1だけ、repoには再現可能なコード・SQL・匿名テストだけを残します。
仕事/ ├── mcp/ │ ├── sql/ │ │ ├── d1-001-candidate-context.sql 追跡:テーブル・索引・制約だけ │ │ ├── d1-002-sheet-projection.sql 追跡:outbox・寿命管理 │ │ └── d1-003-d1-migration.sql 追跡:一回限りの移行手順 │ ├── line-webhook/ │ │ ├── src/index.ts LINE署名確認・受信入口 │ │ ├── src/d1-writer.ts 会話の書込み専用 │ │ ├── src/lifecycle.ts 保存停止・猶予・削除の規則 │ │ └── scripts/snapshot-management-sheet.mjs 管理表の存在確認 │ └── line-reader/ │ ├── src/index.ts MCPの読み取り入口 │ └── src/d1-reader.ts 最小権限の読取り専用 ├── scripts/candidate/ │ ├── record_event.py 既存:PIIを拒否する操作ログ │ ├── tests/ 追跡:匿名fixtureのみ │ └── output/logs/ 非追跡:PIIなしの実行結果 ├── loops/line-data-lifecycle/ │ └── loop.md 発火・dry-run・停止・rollbackの正本 └── areas/整備/.../plans/ 正本の計画。実データは置かない
この案では、D1をLINE文脈の正本から外し、Turso CloudのSQLite互換DBへ直接書きます。Cloudflare Workerからはリモート接続用のSDKを使い、個人情報を持つローカルSQLiteファイルは作りません。
Tursoのローカル同期を候補者管理の仕組みにしません。同期対象を混ぜず、入口ごとに一方向の流れを固定します。
Webhookの署名確認後、webhook_event_id の一意制約で重複を防ぎ、受信本文・時刻・LINE user IDをTursoへ保存する。これは同期ではなく、原本の受入れである。
AIが抽出した事実と変更案を保存し、人がL2承認した時だけ候補者状態を確定する。同じトランザクションで管理表投影用のoutboxを作る。
承認済みoutboxだけを管理表の対応行へ反映し、読み直して完了にする。スプシのセルをTursoへ無制限に書き戻さない。
初回移行と、管理対象に存在するかの毎日スナップショットだけを入力にする。候補者の正式キーは行番号・氏名ではなく profile_url にする。
管理表にいない候補者のLINE本文を持ち続けないことには賛成です。ただし、行の誤削除・並べ替え・一時移動まで消さないよう、新規保存の停止と既存データの破棄を分けます。
候補者の状態をAIが判断する処理と、LINEデータを整理する処理は別責務です。後者はAI runnerではなく、一定のルールだけを実行するrepo-localのscript loopにします。
管理表をライブに読み、正式 profile_url の集合を作る。固定の行・列・氏名で判定しない。空の取得、見出し異常、前回比の大幅減少はfail closedで、DBに一切書かない。
LINE本文の新規保存と保留中の送信を停止する。既存本文は削除保留へ移し、担当者が誤って行を消しても猶予期間中なら同じ profile_url で復帰できる。
LINE本文、LINE user IDとの紐付け、未送信予約を消す。実行ログには人数・日時・理由だけを残し、氏名・profile_url・本文を残さない。保持義務がある候補者は retention_hold で除外する。
今のWebhookは毎分の定期実行で、allowlistにいない候補者のLINE本文を削除します。管理表同期が固定範囲・名前中心のため、このままでは誤削除を戻せません。
新しい同期がdry-runで十分に一致するまで、既存の即時削除を広げない。まず削除候補の件数だけを出し、人が確認してから「保存停止」「猶予」「完全削除」の順に切り替える。launchdではなく既存のCloudflare定期実行を使う場合も、発火変更と削除ポリシーは明示承認が必要です。
一度に正本移行をしないことが、今の管理表運用を止めずに精度を上げる鍵です。
D1・Supabase・管理表のどれが実際に更新されているかを読み取りだけで照合する。期間中は管理表を業務上の正本として維持する。
候補者状態はまだ自動変更しない。連携済み率、要確認件数、提案と人の判断の差を測る。
candidate のL2承認から、正本・管理表・KPI・ログを一つの操作IDで反映して再読込確認する。十分な照合後にだけ、DB正本化を判断する。
ここを決めれば、実装計画を安全に確定できます。いまはまだDB migrationやスプシ列追加をしません。
推奨は、実稼働中のD1へ統合すること。Supabaseの古い設計は監査後に統合または停止対象として扱い、Tursoは正本にしない。既存 mcp/sql にはSupabase用のSQLも混ざるため、D1へ流用せずSQLite互換の新migrationを別にする。
管理表から消えた後の猶予日数、最長保存、削除保留の条件を先に決める。既存Webhookには3か月で削除する処理があるため、業務要件に合うかを確認する。
当面は状態・KPI反映をL2承認のままにする。未読表示や要確認キューまでを自動化し、送信・状態変更・KPIは担当者が確定する。