← 一覧に戻る

会話履歴 + フィードバック返信 + 認証コスト追跡

2026年3月15日 23:26 更新
MD から自動変換されたページです。内容について質問があれば右下の ? ボタンからどうぞ。

Context

前セッション(TDD 41〜47 + Codex 21ラウンド)で管理ダッシュボード・意見コマンド・マーケティングトラッカーは実装済み。 本セッションでは以下の 未実装3機能 + 1改修 を追加する:

  1. 会話履歴ログ — ユーザー/Botの全やり取りをD1に蓄積(90日保持)。意見の文脈理解に使う
  2. フィードバック返信 — ダッシュボードからLINE Pushで返信できるようにする
  3. 認証コール費用の追跡 — 現在は20%安全マージンで雑に吸収しているが、実発信を記録して正確にコスト表示する。オーナー/VIPの認証コールは devCost に計上
  4. プライバシーポリシー更新 — メッセージ用途拡大 + 会話履歴保存期間追加

DB スキーマ変更

新規: conversation_logs テーブル

CREATE TABLE conversation_logs (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  line_user_id TEXT NOT NULL,
  role TEXT NOT NULL,           -- 'user' or 'bot'
  content TEXT NOT NULL,
  created_at INTEGER DEFAULT (unixepoch())
);
CREATE INDEX idx_conversation_logs_user ON conversation_logs(line_user_id, created_at);

新規: verification_calls テーブル

認証コールの実発信を記録する。reminders テーブルに入らないため独自テーブルが必要。

CREATE TABLE verification_calls (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  line_user_id TEXT NOT NULL,
  created_at INTEGER DEFAULT (unixepoch())
);

変更: feedback テーブルに返信追跡カラム

ALTER TABLE feedback ADD COLUMN replied_at INTEGER;
ALTER TABLE feedback ADD COLUMN reply_content TEXT;

1. 会話履歴ログ

ヘルパー関数(src/index.ts に追加)

async function logConversation(db: D1Database, userId: string, role: "user" | "bot", content: string) {
  await db.prepare(
    "INSERT INTO conversation_logs (line_user_id, role, content) VALUES (?, ?, ?)"
  ).bind(userId, role, content).run();
}

replyAndLog / replyMessagesAndLog ラッパー

async function replyAndLog(token: string, accessToken: string, text: string, db: D1Database, userId: string) {
  await replyToLine(token, accessToken, text);
  await logConversation(db, userId, "bot", text);
}

async function replyMessagesAndLog(token: string, accessToken: string, messages: unknown[], db: D1Database, userId: string, logText: string) {
  await replyMessages(token, accessToken, messages);
  await logConversation(db, userId, "bot", logText);
}

挿入ポイント

90日クリーンアップ

handleScheduled(cron)の末尾に追加:

const ninetyDaysAgo = nowUnix - 90 * 86400;
await env.DB.prepare("DELETE FROM conversation_logs WHERE created_at < ?").bind(ninetyDaysAgo).run();

2. フィードバック返信

変更: GET /admin/feedback(会話履歴付き)

既存エンドポイント(L461-471)を拡張。各フィードバックの投稿前7日間の会話履歴(最大20件)を付与。 line_user_id はレスポンスに含めない(内部で reply に使う)。

{
  "items": [{
    "id": 1,
    "content": "朝5時にも使いたい",
    "created_at": 1710000000,
    "replied_at": null,
    "reply_content": null,
    "conversation": [
      { "role": "user", "content": "明日5時に起こして", "created_at": 1709999000 },
      { "role": "bot", "content": "5時00分に電話するね", "created_at": 1709999001 }
    ]
  }]
}

新規: POST /admin/feedback/:id/reply

LINE Push API でフィードバック投稿者に返信。

POST /admin/feedback/:id/reply
Body: { "message": "ありがとう!改善したよ!" }

実装:

  1. feedback から id で検索 → line_user_id 取得
  2. replied_at が既にあれば 409 Conflict
  3. LINE Push API (POST https://api.line.me/v2/bot/message/push) で送信
  4. replied_at + reply_content を更新

注意: Push は月200通制限(LINE無料プラン)。β規模では問題なし。

3. 認証コール費用の追跡

現状の問題

実装

発信時の記録(L862 の直後)

// 認証コール発信記録
await c.env.DB.prepare(
  "INSERT INTO verification_calls (line_user_id) VALUES (?)"
).bind(userId).run();

admin/stats の拡張

budget / devCost それぞれに verificationCalls + verificationCost を追加。

クエリ: verification_calls テーブルから今月分を集計(created_at >= monthStartUnix)。 オーナー/VIPの分は devCost 側に計上。

{
  "budget": {
    "completedCalls": 10,
    "pendingCalls": 2,
    "verificationCalls": 5,
    "trackedCost": 130,
    "verificationCost": 65,
    "projectedCost": 221,
    ...
  },
  "devCost": {
    "completedCalls": 3,
    "pendingCalls": 1,
    "verificationCalls": 2,
    "trackedCost": 39,
    "verificationCost": 26,
    "projectedCost": 78
  }
}

projectedCost = (completedCalls + pendingCalls + verificationCalls) × ¥13

ダッシュボード表示の変更

バジェットカードに認証コール数を表示。例:

完了10件 + 予約2件 + 認証5件
開発費(別枠): ¥78(完了3 + 予約1 + 認証2)

4. プライバシーポリシー更新

docs/privacy-policy.html + public/privacy-policy.html

第2条: (リマインダーの日時・内容の検出に使用)(サービス提供・改善に使用)

第7条: 保存期間テーブルに追加:

データの種類 保存期間
会話履歴データ(送受信メッセージ) 送受信日から90日間

TDD イテレーション

# 内容 テスト数
1 conversation_logs テーブル + logConversation / replyAndLog ヘルパー + 基本テスト +2
2 webhook の全 replyToLinereplyAndLog 置換 + ユーザーメッセージログ +2
3 verification_calls テーブル + 認証コール発信時の記録 +2
4 admin/stats に認証コスト追加(budget + devCost) +2
5 GET /admin/feedback 拡張(会話履歴 + replied_at/reply_content) +3
6 POST /admin/feedback/:id/reply(Push送信 + 二重送信防止) +3
7 ダッシュボード HTML 更新(フィードバック会話+返信 / バジェット認証コスト) 0
8 プライバシーポリシー更新 + 90日クリーンアップ 0

合計: +14テスト

変更ファイル一覧

ファイル 種別 内容
schema.sql 変更 conversation_logs + verification_calls テーブル追加、feedback に2カラム追加
src/index.ts 変更 会話ログ機能 + 認証コール記録 + admin/stats 拡張 + feedback 返信エンドポイント
src/index.test.ts 変更 +14テスト
public/admin/index.html 変更 フィードバック会話履歴+返信UI / バジェット認証コスト表示
docs/privacy-policy.html 変更 第2条・第7条の微修正
public/privacy-policy.html 変更 同上(Workers Static Assets 配信用コピー)

検証手順

  1. tsc --noEmit && pnpm test(全テスト通過)
  2. Codex レビューループ: ~/bin/codex-full review で P1/P2 指摘がなくなるまで修正→再レビューを繰り返す
  3. テスト通過 + Codex LGTM でコミット
📝 質問モード — テキストを選択してね
✓ 質問を送信しました