Skip to content

Amazon Connect コスト集計自動化設計書

ドキュメント情報

項目内容
最終更新日2026-01-07
関連ドキュメントAmazon Connect コスト集計手順書

更新履歴

日付更新内容更新者
2026-01-07Step 5.6(カテゴリマスタ自動更新)とStep 5.7(CSV添付)を追加、Slack通知をPython化、事業単位サマリーCSV形式変更(小数点形式)SREチーム
2025-12-19Phase 2に逆算ロジックを追加(最大金額の事業単位を逆算して合計を厳密に一致)SREチーム
2025-12-13Phase 2を手動入力方式に変更、5ステップ計算ロジックを追加、total_cost_detailed_usdの説明を追加SREチーム
2025-12-11初版作成(Phase 1・Phase 2の基本設計)SREチーム

目次

  1. 概要
  2. 背景と目的
  3. アーキテクチャ設計
  4. 認証方式の選定
  5. ワークフロー設計
  6. Megazone手動オペレーション
  7. データフロー
  8. Claude AI活用設計
  9. S3データ構成
  10. インフラ構成
  11. 運用設計
  12. データ閲覧方法
  13. セキュリティ考慮事項
  14. 今後の展開
  15. トラブルシューティング

概要

Amazon Connectのコスト集計作業を半自動化し、GitHub Actionsワークフローとして実装する。Megazone Hyper BillingからのCSV/PDF取得は手動で行い、コスト按分計算を自動化する。集計結果はS3に保存し、CSV形式で出力する。

対象環境

リソース環境
AWSアカウント913831226605 (Amazon Connect環境)
Amazon ConnectインスタンスID: dbdef82b-a066-4ad3-a9d1-1da163ccc52c
GCPプロジェクトoc-monitoring-358909 (オンライン事業部)
BigQuery Datasetoc-monitoring-358909.aws_connect
BigQuery Tablecontact_trace_records
S3バケットamazon-connect-cost-analysis
IAM Roleaws-cost-monitoring-github-actions-role

背景と目的

背景

従来の手動運用

  • オンライン事業部で実施していたAmazon Connectのコスト集計作業をSREチームが引き継ぎ
  • 手動作業により2-3時間/月の工数が発生(運用手順書参照)
  • 課題:
    • スプレッドシートでの管理は拡張性・保守性に課題
    • ヒューマンエラーのリスク
    • 属人化(手順の複雑さ、暗黙知の存在)

Google Spreadsheet自動化の検討と断念

当初、Google Sheets APIを使用してスプレッドシートへの自動書き込みを検討しましたが、技術的制約により断念しました。

検討した認証方式と問題点:

  1. サービスアカウント + Workload Identity Federation

    • 問題点: サービスアカウントにはGoogle Driveのストレージ割り当てがなく、ファイルを所有できない
    • エラー: The user's Drive storage quota has been exceeded
    • 参考: Google Drive API - Handle Errors (Storage Limit)
  2. 共有ドライブ(Shared Drive)の利用

    • 問題点: Google Workspace管理者権限が必要
    • 制約: SREチームにGoogle Workspace管理権限がなく、管理が煩雑
  3. OAuth 2.0認証 + ユーザー権限

    • 問題点: 特定ユーザーに依存
    • リスク: ユーザーの異動・退職時にワークフロー停止
  4. Domain-Wide Delegation(ドメイン全体の委任)

    • 問題点: Google Workspace管理者権限が必要
    • 制約: SREチームにGoogle Workspace管理権限がなく、セキュリティリスクも高い

結論: Google Spreadsheetの利用を断念し、計算ロジックをシェルスクリプトとPythonの組み合わせで実装しS3に保存する方針に変更

Megazone Hyper Billing API提供なし

制約事項:

  • Megazone Hyper Billing APIの提供なし
    • Megazone社のHyper billingシステムにAPI経由のアクセス機能が存在しない
    • コスト情報のCSVとPDFは手動ダウンロードが必要

影響:

  • 完全自動化は不可能
  • 手動オペレーションを含む半自動化を採用

目的

なぜ半自動化するのか

ビジネス課題:

  • 代理店(Megazone)請求の手動運用
    • 毎月、Megazoneからの請求書とHyper billingデータを手動取得
    • バクラクでの支払依頼と事業部ごとのコスト按分を手作業で実施
    • 工数 + ヒューマンエラーリスク

現状の課題:

  • SREチームの手動作業による運用負荷の増加
  • 手動運用によるヒューマンエラーリスク
  • スプレッドシート管理による属人化(手順の複雑さ、暗黙知の存在)
  • 月次作業の実行忘れリスク

期待される状態:

  • Megazone請求データの手動取得は残すが、コスト按分計算を自動化
  • 計算ミスや入力ミスが発生しなくなる(品質向上)
  • 誰でも実行履歴を確認でき、問題調査が容易(透明性向上)
  • コードベースでの管理により、変更履歴が追跡可能(保守性向上)
  • 手動作業を最小限に抑えた半自動化運用を実現

達成すべきこと

  1. 自動化による工数削減: 月次2-3時間の手作業を半自動化(手動作業30分、自動処理90分)
  2. 品質向上: 計算ロジックの自動化によるミスの排除
  3. 属人化の解消: コード化による作業の標準化
  4. 監査性の向上: 実行履歴のログ化と追跡可能性の確保
  5. 保守性の向上: Google Workspace管理者権限に依存しないインフラ構成
  6. データ管理の改善: S3によるバージョン管理と長期保存
  7. 手動オペレーションの明文化: Megazone手動取得手順の標準化

アーキテクチャ設計

システム全体図

コンポーネント構成

1. GitHub Actions ワークフロー

  • ファイル: .github/workflows/amazon-connect@amazon-connect-cost-calculation.yml
  • トリガー:
    • 手動実行(workflow_dispatch)
    • スケジュール実行(cron: 月初2日 09:00 JST)

2. 処理スクリプト

  • BigQueryから対象月のコールトレースデータ取得
  • Amazon Connectからキュー一覧・電話番号一覧取得
  • 新規リソースのClaude AIカテゴリ判定(run-claudeアクション利用)
  • コスト按分計算とCSV生成
  • S3への結果アップロード

3. Terraform インフラ

  • AWS:
    • IAM Role(OIDC認証)
    • S3バケット
  • GCP:
    • Workload Identity Pool/Provider
    • Service Account
    • BigQuery Dataset/Table(コスト分析データ)

認証方式の選定

AWS認証: Workload Identity Federation (OIDC)

採用方式: GitHub Actions OIDC → AWS IAM Role

メリット:

  • ✅ キーレス認証(AWSアクセスキー不要)
  • ✅ 一時的な認証情報(セキュア)
  • ✅ GitHub Actionsとのネイティブ統合

実装:

実装例
yaml
- name: Configure AWS Credentials (OIDC)
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::913831226605:role/aws-cost-monitoring-github-actions-role
    aws-region: ap-northeast-1

必要な権限:

  • Amazon Connect読み取り
  • S3読み書き
  • Bedrock (Claude) 実行

GCP認証: Workload Identity Federation (OIDC)

採用方式: GitHub Actions OIDC → GCP Workload Identity Pool → Service Account

メリット:

  • ✅ キーレス認証(サービスアカウントキー不要)
  • ✅ 一時的な認証情報(セキュア)
  • ✅ GitHub Actionsとのネイティブ統合

実装:

実装例
yaml
- name: Authenticate to Google Cloud
  uses: google-github-actions/auth@v2
  with:
    workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
    service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}

必要な権限:

  • BigQuery Data Viewer(データセット読み取り)
  • BigQuery Job User(クエリ実行)

ワークフロー設計

概要

自動化を実現するため、ワークフローを2段階に分割します。

Phaseワークフロー名実行タイミング目的
Phase 1amazon-connect@amazon-connect-cost-calculation.yml毎月2日 09:00 JST(自動)AWS Cost Explorerベースの暫定按分計算
Phase 2amazon-connect@final-cost-allocation.yml手動実行のみMegazone実額ベースの最終按分計算

Phase 1とPhase 2の間に手動オペレーション(Megazone CSV/PDF取得、S3アップロード)が入ります。

Phase 1: AWS Cost Explorerベース暫定按分計算

ワークフロー: amazon-connect@amazon-connect-cost-calculation.yml

トリガー:

  • スケジュール実行: 毎月2日 09:00 JST (cron: '0 0 2 * *')
  • 手動実行: workflow_dispatch

目的:

  • AWS Cost Explorerデータを使った暫定按分計算
  • 次のアクション(Megazone手動取得)の通知

フロー図:

実行環境:

  • Runner: ubuntu-latest
  • タイムアウト: 30分
  • Python: 3.11(コスト計算ロジック)
  • 必要な権限: id-token: write(OIDC用)
  • 必要なツール: bq (BigQuery CLI), aws (AWS CLI), jq

主要ステップ:

  1. リポジトリチェックアウト
  2. AWS OIDC認証(Role: amazon-connect-monitoring-github-actions-role
  3. GCP Workload Identity認証
  4. 集計対象月の設定(前月: 2025-10-01 ~ 2025-10-31)
  5. BigQueryからコールトレースデータ取得
  6. Amazon Connect APIからキュー/電話番号マスタ取得
  7. Claude AIによる新規リソースのカテゴリ判定
  8. コスト按分計算(AWS Cost Explorerベース)
  9. 暫定結果をS3保存(processed/YYYY-MM/
  10. Step 5.6: カテゴリマスタ自動更新(新規リソース検出時のみ)
  11. Step 5.7: 事業単位サマリーCSVをS3からダウンロード(Slack添付用)
  12. Slack通知(暫定結果 + CSV添付 + 手順書リンク)

Slack通知内容:

✅ Amazon Connect コスト集計完了 (2025-10)

🏢 事業単位別集計
• 在宅事業 (B + 共通 + C): XXX件, XXX分, $XXX (XX.XX%)
• オンライン事業 (Online + メンタル): XX件, XXX分, $XXX (XX.XX%)
• 地域支援 (G): X件, XX分, $X (X.XX%)
• ファーマ (ファーマ): X件, XX分, $X (X.XX%)

📁 出力ファイル
• CSV: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv
• JSON: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.json
• サマリーCSV(添付): s3://amazon-connect-cost-analysis/processed/2025-10/business_unit_summary.csv

📎 添付ファイル
business_unit_summary_2025-10.csv(事業単位按分率サマリー)

⚠️ 分類誤りがある場合
利用がない事業部で計上されるなどの分類に誤りがある場合、以下のカテゴリファイルを修正しワークフローを再実行してください。
• s3://amazon-connect-cost-analysis/categories/queue_categories.json
• s3://amazon-connect-cost-analysis/categories/number_categories.json

📝 次のアクション: Megazone手動オペレーション
締切: 毎月5日まで
手順書: docs/sre/runbooks/megazone-bakuraku-manual-operation.md

Phase 2: Megazone実額ベース最終按分計算

ワークフロー: amazon-connect@final-cost-allocation.yml

トリガー:

  • 手動実行: workflow_dispatch
    • Megazone Hyper Billingから取得した実額データを手動入力

入力パラメータ(手動入力):

  • target_month: 対象月(YYYY-MM形式、例: 2025-11)
  • exchange_rate: 為替レート(円/ドル、例: 155.87)
  • aws_total_jpy: AWS総額(税込・円、例: 2457024)
  • total_call_cost_usd: 通話料総額(USD、Megazone実額、例: 18776.47)

目的:

  • Megazone実額データを使った最終按分計算
  • バクラク支払依頼用の按分結果出力
  • 手動計算(Excel/Google Spreadsheet)との完全一致(¥0差異)

背景と補足:

なぜ手動入力方式を採用したのか:

  1. セキュリティ上の理由

    • バクラク請求書PDFには機密情報(会社の財務情報、契約条件等)が含まれる
    • S3にアップロードすると、アクセス権限管理やログ監査の負担が増加
    • 手動入力方式であれば、必要最小限の数値のみを入力するため情報漏洩リスクが低い
  2. 運用の簡素化

    • PDFパースロジックの実装・保守コストを削減
    • PDFフォーマット変更時の対応が不要
    • Megazone CSVのS3アップロードも不要になり、手順が簡潔化
  3. Megazone API未提供

    • Megazone Hyper BillingにはAPI連携機能が存在しない
    • 完全自動化は技術的に不可能

フロー図:

実行環境:

  • Runner: ubuntu-latest
  • タイムアウト: 20分
  • Python: 3.11(計算ロジック)
  • 必要な権限: id-token: write(OIDC用)
  • 必要なツール: aws (AWS CLI), jq

主要ステップ:

  1. リポジトリチェックアウト
  2. AWS OIDC認証
  3. 環境変数から入力パラメータを取得
  4. Phase 1結果をS3から取得(processed/YYYY-MM/allocation_result.json
  5. 最終按分計算(5ステップロジック)
    • Step 1: 各事業単位の通話料(保持料金なし)を計算
    • Step 2: 全事業単位の通話料合計を計算
    • Step 3: 各事業単位の総コスト(保持料金込)を計算
    • Step 4: 全事業単位の総コスト合計を計算
    • Step 5: 各事業単位の円換算(按分比率方式)を計算
  6. 最終結果をS3保存(processed/YYYY-MM/final/
  7. Slack通知(最終按分結果JPY + バクラク期限リマインド)

Slack通知内容:

✅ Amazon Connect 最終コスト按分完了(2025年10月分)

【最終按分結果】
- 請求総額(税込): ¥2,457,024
- B事業部: ¥789,123
- C事業部: ¥1,476,890
- Online: ¥100,011
- 共通: ¥90,000
- FDT付調整: ¥1,000

⚠️ バクラク支払依頼期限: 第6営業日まで
📖 手順書: https://github.com/.../bakuraku-payment-request.md (TBD)

📊 結果CSV: s3://amazon-connect-cost-analysis/final/2025/10/final_allocation_202510.csv

エラーハンドリング

Phase 2で入力パラメータが不正な場合:

❌ Amazon Connect 最終按分エラー(2025年10月分)

エラー内容: 入力パラメータが不正です

対処方法:
1. Megazone Hyper Billingで通話料総額(USD)を確認
2. バクラク請求書PDFで為替レートとAWS総額(税込・円)を確認
3. 正しい値でPhase 2ワークフローを再実行

📖 手順書: https://github.com/.../megazone-bakuraku-manual-operation.md

Megazone手動オペレーション

概要

Megazone Hyper billingシステムはAPI連携が提供されていないため、以下の手動作業が必要です。

詳細な手順は Megazone手動オペレーション手順書(今後作成予定) を参照してください。

実施タイミング

項目詳細
開始毎月2日のPhase 1完了Slack通知受信後
期限なるべく早く(Phase 2手動実行のため)
注意Megazone請求書送付は毎月第3営業日が期限ですが、遅延することがあります

手順概要

詳細な手順は Megazone・バクラク手動オペレーション手順書(今後作成予定) を参照してください。

以下は概要のみ記載します。

1. Megazoneリンクへのログイン

2. Cost Pivotで通話料総額を確認

  • 保存済みフィルタ: amazon-connect-monthly-cost-division-filter を使用
  • 対象月のAmazon Connect関連コストを確認
  • 通話料総額(USD) をメモ(小数点第2位まで)
  • 例: 18776.47 USD

3. バクラクから請求書PDFダウンロード

  • Megazone請求書を検索・ダウンロード
  • PDFから以下の情報を抽出してメモ:
    • 為替レート(円/ドル) 例: 155.87
    • AWS総額(税込・円) 例: 2457024

4. Phase 2ワークフローを手動実行

  • GitHub Actions → amazon-connect@final-cost-allocation.yml
  • 「Run workflow」ボタンをクリック
  • 以下の4つの値を入力:
    • target_month: 対象月(例: 2025-11
    • exchange_rate: 為替レート(例: 155.87
    • aws_total_jpy: AWS総額・税込(例: 2457024
    • total_call_cost_usd: 通話料総額(例: 18776.47
  • 「Run workflow」を実行

注意: S3へのCSV/PDFアップロードは不要です(手動入力方式のため)

トラブルシューティング

問題原因対処
Megazone請求書が届かない送付遅延Megazone担当者に問い合わせ
Megazoneの通話料総額が不明アクセス権限不足SREチームに問い合わせて確認
Phase 2で入力パラメータエラー値の形式が不正数値形式を確認(例: 155.87, 2457024
Phase 2ワークフローが失敗Phase 1結果がS3にないPhase 1を先に実行してから再実行

アプリケーションロジック設計

概要

従来Google Spreadsheetで行っていた計算ロジックを実装します。

アーキテクチャパターン

パイプライン型処理: 各処理ステップを独立したスクリプトとして実装し、中間データをJSONファイルで連携

データフロー:

Input → Step1 → Step2 → Step3 → Step4 → Step5 → Output
(API)   (JSON)  (JSON)  (JSON)  (JSON)  (CSV)   (S3)

処理フロー全体図

Mermaid図

各ステップの詳細設計

Step 1: BigQueryデータ取得

要件:

  • 対象月のコールトレースデータをBigQueryから取得
  • JSON形式で出力し、Step 4の計算に使用

入力:

  • 環境変数: GCP_PROJECT_ID, TARGET_MONTH (YYYY-MM形式)
  • BigQuery: {project_id}.aws_connect.contact_trace_records

データソース:

  • プロジェクト: oc-monitoring-358909
  • データセット: aws_connect
  • テーブル: contact_trace_records

取得項目:

| 項目名 | データ型 | 説明 | 抽出元カラム | |---------|---------|------|--------|-------| | business_date | DATE | 通話日 | DATE(connectedtosystemtimestamp, 'Asia/Tokyo') | | queue_name | STRING | キュー名(NULLの場合あり) | JSON_EXTRACT_SCALAR(queue, '$.name') | | contact_time | FLOAT | 通話時間(分) | TIMESTAMP_DIFF(...) / 60 | | system_tel | STRING | システム電話番号 | JSON_EXTRACT_SCALAR(systemendpoint, '$.address') | | contact_type | STRING | 通話種別 | initiationmethod |

contact_type(initiationmethod)の種類:

  • INBOUND: 受信通話
  • OUTBOUND: 発信通話
  • QUEUE_TRANSFER: キュー転送
  • TRANSFER: 転送
  • EXTERNAL_OUTBOUND: 外部転送
  • API: 自動架電

処理ロジック:

  1. 環境変数から対象月とプロジェクトIDを取得
  2. bq queryコマンドで対象月のコールトレースをSELECT
    • 対象期間: 前月の1日00:00:00 ~ 最終日23:59:59(Asia/Tokyo)
    • フィルタ条件: system_tel IS NOT NULL(電話番号がNULLのレコードは除外)
    • queue_nameがNULLのレコードは含める(キュー未指定の通話も集計対象)
  3. 結果をJSON形式でdata/bigquery_raw_data.jsonに保存
  4. 取得件数を標準出力に表示

実装例 クエリの説明:

  • 対象期間: connectedtosystemtimestampが2025年10月1日~31日(Asia/Tokyo)
  • フィルタ: 電話番号がNULLのレコードは除外
  • queue_name: NULLの場合もそのまま取得(キュー未指定の通話も含む)
  • 通話時間計算: disconnecttimestampinitiationtimestampの差分を秒単位で計算し、60で割って分単位に変換
  • ソート: 日付、キュー名、電話番号、通話種別でソート 業務日(business_date)のタイムゾーン設定について:
  • DATE(connectedtosystemtimestamp, 'Asia/Riyadh') を使用
  • 理由: FastDoctorの業務日は午前6時で切り替わるため
    • 東京時間(Asia/Tokyo)とリヤド時間(Asia/Riyadh)は6時間の時差がある
    • 例: 東京時間 2025-07-01 03:00 → リヤド時間では前日 2025-06-30 の扱い
    • これにより、業務日ベースでの正確な集計が可能
  • 通話時間の計算は引き続き Asia/Tokyo を使用(実際の通話時間を正確に計測)

クエリ例: コールトレースデータ取得

sql
SELECT
  DATE(connectedtosystemtimestamp, 'Asia/Riyadh') AS business_date,
  JSON_EXTRACT_SCALAR(queue, '$.name') AS queue_name,
  -- CAST(JSON_EXTRACT_SCALAR(agent, '$.agentinteractionduration') AS INT64) AS interaction_duration,
  -- CAST(JSON_EXTRACT_SCALAR(agent, '$.agentinteractionduration') AS INT64) / 60 AS interaction_duration_minutes,

  -- contact_time 検証用
  TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'), DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60 AS contact_time,

  JSON_EXTRACT_SCALAR(systemendpoint, '$.address') AS system_tel,
  initiationmethod AS contact_type
FROM
  `oc-monitoring-358909.aws_connect.contact_trace_records`
WHERE
  -- initiationmethod = 'INBOUND'
  -- AND
  DATE(connectedtosystemtimestamp, 'Asia/Riyadh') BETWEEN '2025-07-01' AND '2025-07-31'
  AND DATETIME(connectedtosystemtimestamp, 'Asia/Tokyo') >= DATETIME(TIMESTAMP('2025-07-01 00:00:00'), 'Asia/Tokyo')
ORDER BY
  DATE(connectedtosystemtimestamp, 'Asia/Riyadh')

出力:

  • data/bigquery_raw_data.json: コールトレースデータ(キュー名、電話番号、通話タイプ別の通話時間と件数)

出力データ構造例:

JSON出力例
json
[
  {
    "queue_name": "(DP)テストクリニックA",
    "system_tel": "+815011111111",
    "total_interaction_duration_minutes_api": 0.0,
    "interaction_count_api": 0,
    "total_interaction_duration_minutes_external_outbound": 2.066666667,
    "interaction_count_external_outbound": 3,
    "total_interaction_duration_minutes_inbound": 2.966666667,
    "interaction_count_inbound": 5,
    "total_interaction_duration_minutes_outbound": 0.0,
    "interaction_count_outbound": 0,
    "total_interaction_duration_minutes_queue_transfer": 0.0,
    "interaction_count_queue_transfer": 0,
    "total_interaction_duration_minutes_transfer": 0.0,
    "interaction_count_transfer": 0
  },
  {
    "queue_name": "BasicQueue",
    "system_tel": "+815012345678",
    "total_interaction_duration_minutes_api": 5.5,
    "interaction_count_api": 12,
    "total_interaction_duration_minutes_external_outbound": 0.0,
    "interaction_count_external_outbound": 0,
    "total_interaction_duration_minutes_inbound": 125.8,
    "interaction_count_inbound": 87,
    "total_interaction_duration_minutes_outbound": 3.2,
    "interaction_count_outbound": 8,
    "total_interaction_duration_minutes_queue_transfer": 1.5,
    "interaction_count_queue_transfer": 2,
    "total_interaction_duration_minutes_transfer": 0.8,
    "interaction_count_transfer": 1
  }
]

注意事項:

  • BigQuery CLIが事前にセットアップされている必要がある
  • Workload Identity認証が完了している必要がある
  • クエリエラー時は非ゼロの終了コードを返すこと
  • 対象月の日付範囲は動的に生成すること
    • 開始: YYYY-MM-01 00:00:00 (月初日の00:00:00.000)
    • 終了: YYYY-MM-DD 23:59:59.999999 (月末日の23:59:59.999999)
    • 例: TARGET_MONTH=2025-102025-10-01 00:00:002025-10-31 23:59:59.999999
    • または >= '2025-10-01' AND < '2025-11-01' の範囲指定を推奨(境界値問題を回避)

Step 2: AWS Connectデータ取得

処理: Amazon Connectからキュー・電話番号取得

要件:

  • Amazon Connectからキュー一覧と電話番号一覧を取得
  • Step 3のカテゴリ判定とStep 4の計算に使用

入力:

  • 環境変数: CONNECT_INSTANCE_ID, AWS_ACCOUNT_ID
  • Amazon Connect: インスタンス dbdef82b-a066-4ad3-a9d1-1da163ccc52c

処理ロジック:

  1. aws connect list-queuesでSTANDARDキュー一覧を取得
  2. jqでJSON整形(queue_id, queue_name, queue_arn抽出)
  3. data/aws_queue_list.jsonに保存
  4. aws connect list-phone-numbers-v2で電話番号一覧を取得
  5. jqでJSON整形(phone_number, phone_number_type, phone_number_country_code抽出)
  6. data/aws_phone_numbers.jsonに保存
  7. 取得件数を標準出力に表示

出力:

  • data/aws_queue_list.json: キュー一覧(ID、名前、ARN)
  • data/aws_phone_numbers.json: 電話番号一覧(番号、タイプ、国コード)

注意事項:

  • AWS CLI認証が完了している必要がある
  • 削除済みキューは除外すること(QueueType == "STANDARD"のみ)
  • APIエラー時は非ゼロの終了コードを返すこと

Step 3: Claude AIカテゴリ判定

処理: 新規リソースのカテゴリ判定(run-claudeアクション利用)

要件:

  • キューと電話番号をカテゴリ(B / C / G / Online / 共通 等)に分類
  • 前月のカテゴリデータを継承し、新規リソースのみClaude AIで判定(コスト最適化)
  • 月次でカテゴリ履歴を保持(S3のprocessed/YYYY-MM/配下)

入力:

  • data/aws_queue_list.json(Step 2の出力)
  • data/aws_phone_numbers.json(Step 2の出力)
  • s3://amazon-connect-cost-analysis/categories/queue_categories.json(S3のカテゴリマスタ)
  • s3://amazon-connect-cost-analysis/categories/number_categories.json(S3のカテゴリマスタ)
  • .github/prompts/categorize_resources.md(カテゴリ判定ルール)

処理ロジック:

  1. S3からカテゴリマスタをダウンロード
    • s3://amazon-connect-cost-analysis/categories/queue_categories.json
    • s3://amazon-connect-cost-analysis/categories/number_categories.json
    • AWS CLIでaws s3 cpコマンドを使用
    • 常に最新版を参照(修正があればS3にアップロード後に反映)
  2. jqで新規リソースを抽出(既存カテゴリに含まれないもの)
    • 現在のキュー一覧とカテゴリマスタを比較
    • 現在の電話番号一覧とカテゴリマスタを比較
  3. 新規リソースリストを作成
    • new_resources.jsonとして保存(キュー名、説明、追加日、ARN等を記録)
  4. 新規リソースがある場合のみ:
    • 新規リソース情報をJSON化
    • .github/actions/run-claudeアクションでClaude AIに判定依頼
    • 判定結果をJSON形式で取得(category, confidence, reasoningを含む)
    • ai_categorization.jsonとして保存
    • 既存カテゴリと新規カテゴリをマージ(jq使用)
    • マージ時にcreated_atを現在日時、updated_atを現在日時に設定
  5. 新規リソースがない場合:
    • カテゴリマスタをそのまま使用
  6. 当月のカテゴリデータをdata/queue_categories.json, data/number_categories.jsonに保存(S3アップロード用)

出力:

  • data/queue_categories.json: キューカテゴリマッピング
    json
    {
      "BasicQueue": {
        "category": "共通",
        "description": "全事業部共通の基本キュー",
        "created_at": "2025-11-01",
        "updated_at": "2025-11-01"
      },
      "オンライン診療": {
        "category": "Online",
        "description": "オンドク問診サポート専用",
        "created_at": "2025-11-15",
        "updated_at": "2025-11-15"
      }
    }
  • data/number_categories.json: 電話番号カテゴリマッピング
    json
    {
      "+815012345678": {
        "category": "共通",
        "phone_type": "DID",
        "description": "全事業部共通の代表番号",
        "created_at": "2025-11-01",
        "updated_at": "2025-11-01"
      },
      "+818012099999": {
        "category": "C",
        "phone_type": "TOLL_FREE",
        "description": "toC往診専用フリーダイヤル",
        "created_at": "2025-11-01",
        "updated_at": "2025-11-01"
      }
    }
  • data/new_resources.json: 新規追加されたリソース一覧(レビュー用、新規リソースがある場合のみ)
  • data/ai_categorization.json: Claude AI判定結果と信頼度(レビュー用、新規リソースがある場合のみ)

注意事項:

  • 新規リソースが0件の場合はClaude AIを呼ばない(コスト削減)
  • カテゴリマスタはS3バケット(s3://amazon-connect-cost-analysis/categories/)からダウンロード
  • カテゴリマスタが修正されている場合、最新版が自動的に反映される
  • Claude AIの判定結果は必ずJSON形式でパースできること
  • カテゴリは事前定義された値のみ許可(B, C, G, Online, メンタル, ファーマ, デジタル往診, CS, マーケ, followup, シフト, 海外・旅行事業, 共通, C AND B, 未分類
  • プロンプトファイルにはカテゴリ定義とサンプルを含めること
  • カテゴリマスタが存在しない場合はエラーとして実行を中断すること
  • 「未分類」について:
    • 説明文がなく判定根拠が不十分な場合、Claude AIは「未分類」カテゴリを返す(confidence < 0.5)
    • 運用上、定期的に「未分類」リソースを確認し、説明文を追加するか手動でカテゴリを修正することを推奨

カテゴリ一覧と使用状況:

カテゴリマスタ管理

Phase 1(AWS Cost Explorerベース)で使用するカテゴリは以下の通りです。

カテゴリ状態説明備考
B✅ アクティブB事業部(在宅医療)在宅事業の主要カテゴリ
C✅ アクティブC事業部在宅事業の主要カテゴリ
G✅ アクティブtoG・地域支援・行政向けサービス地域支援事業
Online✅ アクティブオンライン診療・オンドクオンライン事業の主要カテゴリ
メンタル✅ アクティブメンタルヘルスサービスオンライン事業の一部
ファーマ✅ アクティブ薬局・調剤関連サービスファーマ事業
共通✅ アクティブ共通サービス・管理用途在宅事業の一部
未分類✅ アクティブ判定根拠不十分なリソース定期的にレビューが必要
デジタル往診⚪ 将来用デジタル往診サービス現在未使用
CS⚪ 将来用カスタマーサポート現在未使用
マーケ⚪ 将来用マーケティング用途現在未使用
followup⚪ 将来用フォローアップサービス現在未使用
シフト⚪ 将来用シフト管理関連現在未使用
海外・旅行事業⚪ 将来用海外・旅行関連サービス現在未使用
C AND B⚪ 将来用C事業部とB事業部のハイブリッド現在未使用

事業単位とカテゴリの対応(2025-12追加):

事業単位含まれるカテゴリPhase 1使用状況
在宅事業B + 共通 + C✅ 使用中
オンライン事業Online + メンタル✅ 使用中
地域支援G✅ 使用中
ファーマファーマ✅ 使用中

運用ルール:

  1. アクティブカテゴリの追加・変更はプロンプトファイル(.github/prompts/categorize_resources.md)を更新
  2. 新規カテゴリ追加時はpricing_config.yamlの更新不要(カテゴリ判定のみに使用)
  3. 将来用カテゴリは参考用として記載(Claude AIの判定対象には含まれるが、実運用では未使用)
  4. カテゴリマスタの更新履歴はS3バージョニングで管理

カテゴリマスタの配置場所:

  • 本番: s3://amazon-connect-cost-analysis/categories/
  • レビュー待機: s3://amazon-connect-cost-analysis/review/YYYY-MM/

Step 4: コスト按分計算

処理: リソース別コスト計算、カテゴリ別集計、按分比率計算、CSV生成

要件:

  • BigQuery詳細ログを(queue_name, system_tel, contact_type)の組み合わせで集計
  • FDフラグ(+81120で始まる電話番号)を考慮したINBOUND通話時間の分類
  • 按分用$コストをカテゴリ別に集計して按分比率を算出

入力:

  • data/bigquery_raw_data.json(Step 1の出力)
    • 詳細ログデータ
    • 各レコード: business_date, queue_name, contact_time, system_tel, contact_type
  • data/queue_categories.json(Step 3の出力)
  • data/number_categories.json(Step 3の出力)
  • .github/config/pricing_config.yaml(料金体系定義)

BigQueryデータ構造:

詳細ログ(抽出データ1相当):

カラム名データ型説明
business_dateDATE通話日
queue_nameSTRINGキュー名(NULLの場合あり)
contact_timeFLOAT通話時間(分)
system_telSTRINGシステム電話番号
contact_typeSTRING通話種別

contact_typeの種類:

  • INBOUND: 受信通話
  • OUTBOUND: 発信通話
  • QUEUE_TRANSFER: キュー転送
  • TRANSFER: 転送
  • EXTERNAL_OUTBOUND: 外部転送
  • API: 自動架電
pricing_config.yaml構造例
yaml
# Amazon Connect料金体系定義
version: "1.0"
last_updated: "2025-11"

# 基本単価
base_rates:
  system_usage_rate: 0.018  # システム利用料/分($)
  telephony_base_rate: 0.003  # テレフォニー基本単価/分($)

# contact_type別倍率(重み付け係数)
contact_type_multipliers:
  inbound: 1.0          # 通常INBOUND
  inbound_fd: 26.7      # フリーダイヤルINBOUND(+81120)
  outbound: 26.0
  queue_transfer: 26.0
  transfer: 26.0
  external_outbound: 26.0
  api: 26.0

# FDフラグ判定条件
fd_flag_detection:
  prefix: "+81120"      # この接頭辞で始まる電話番号をフリーダイヤルと判定
  prefix_length: 6      # 判定に使用する文字数

# 番号保持料
phone_holding_fees:
  did:
    per_day_usd: 0.10
    per_month_usd: 3.00
  toll_free:
    per_day_usd: 0.48
    per_month_usd: 14.40

処理ロジック:

  1. データ読み込み

    • BigQuery詳細ログ(各通話レコード)を読み込み
    • カテゴリマッピング(キュー、電話番号)を読み込み
    • 料金設定(単価、倍率)を読み込み
  2. (queue_name, system_tel)の組み合わせを抽出

    詳細ログから一意の(queue_name, system_tel)ペアのリストを作成

    : queue_nameがNULLまたは空文字列の場合も1つの組み合わせとして扱う

  3. FDフラグ判定

    各(queue_name, system_tel)ペアについて、FDフラグを判定:

    FDフラグ = IF(system_tel の先頭6文字 == "+81120", "FD", "")
  4. 通話時間の集計と分類

    各(queue_name, system_tel)ペアについて、以下の7つの通話時間を集計:

    ① Inbound(分)

    条件:
    - FDフラグ == "FD" の場合: 0(FD番号のINBOUNDは次のカラムに計上)
    - FDフラグ != "FD" の場合:
        queue_name が一致 AND
        system_tel が一致 AND
        contact_type == "INBOUND"
        の全レコードの contact_time の合計

    ② Inbound:FD(分)

    条件:
    - FDフラグ == "FD" の場合:
        queue_name が一致 AND
        system_tel が一致 AND
        contact_type == "INBOUND"
        の全レコードの contact_time の合計
    - FDフラグ != "FD" の場合: 0

    ③ Outbound(分)⑦ 自動架電(分): 同様に集計

    : queue_nameの一致条件

    • queue_nameがNULLまたは空文字列の場合は、ログのqueue_nameもNULLまたは空文字列と一致
    • queue_nameに値がある場合は、完全一致で比較
  5. カテゴリ紐付け

    各(queue_name, system_tel)ペアにカテゴリを紐付け:

    紐付け優先順位:

    1. queue_nameによるカテゴリ(queue_categoriesから取得)
    2. system_telによるカテゴリ(number_categoriesから取得)
    3. 上記いずれにも該当しない場合: 「未分類」

    確定カテゴリ(category_fix)の決定:

    IF queue_nameのカテゴリが存在し、かつ「未分類」でない場合:
        category_fix = queue_nameのカテゴリ
    ELSE IF system_telのカテゴリが存在し、かつ「未分類」でない場合:
        category_fix = system_telのカテゴリ
    ELSE:
        category_fix = "未分類"

    「未分類」の扱い:

    • Claude AIが説明文なし・判定根拠不十分で「未分類」と判定した場合、そのリソースはカテゴリ「未分類」として集計される
    • 運用上、定期的に「未分類」リソースを確認し、手動でカテゴリを修正することを推奨
  6. リソース別コスト計算

    各(queue_name, system_tel)ペアごとに以下を計算:

    6-1. 通話合計時間の算出

    通話合計(分) = Inbound(分) + Inbound:FD(分) + Outbound(分)
                 + キュー転送(分) + 転送(分) + 外部転送(分) + 自動架電(分)

    6-2. 音声サービス料金

    音声サービス料金($) = 通話合計(分) × $0.018

    6-3. テレフォニー料金- 重み付け計算

    重み付け通話時間 = (Inbound(分) × 1.0)
                     + (Inbound:FD(分) × 26.7)
                     + (Outbound(分) × 26.0)
                     + (キュー転送(分) × 26.0)
                     + (転送(分) × 26.0)
                     + (外部転送(分) × 26.0)
                     + (自動架電(分) × 26.0)
    
    テレフォニー料金($) = 重み付け通話時間 × $0.003

    6-4. 按分用$コスト

    按分用$コスト($) = 音声サービス料金 + テレフォニー料金
  7. カテゴリ別集計

    各カテゴリ(B, C, G, Online, メンタル, ファーマ, 共通、未分類等)ごとに以下を集計:

    • リソース数(キュー数、電話番号数)
    • 総通話時間(分)
    • 総按分用$コスト($)

    丸め処理(手動集計との一致を保証):

    カテゴリ総按分用$コスト($) = ROUND(Σ(各リソースの按分用$コスト), 整数)

    重要: カテゴリ別合計を整数に四捨五入することで、手動集計スプレッドシートと完全一致します。

  8. 事業単位別集計(2025-12追加)

    カテゴリ別集計結果を事業単位別に再集計します。

    事業単位の定義:

    事業単位含まれるカテゴリ説明
    在宅事業B + 共通 + C在宅医療関連サービス全体
    オンライン事業Online + メンタルオンライン診療とメンタルヘルスサービス
    地域支援GtoG・地域支援・行政向けサービス
    ファーマファーマ薬局・調剤関連サービス

    集計項目:

    • 含まれるカテゴリのリスト
    • 総リソース数(各カテゴリのリソース数の合計)
    • 総通話時間(各カテゴリの通話時間の合計)
    • 総按分用$コスト(各カテゴリの按分用$コストの合計)
    • 按分比率(事業単位の総コスト / 全体総コスト × 100)
  9. 按分比率計算

    カテゴリ別按分比率:

    カテゴリ按分比率(%) = (カテゴリ総按分用$コスト / 全体総按分用$コスト) × 100

    事業単位別按分比率:

    事業単位按分比率(%) = (事業単位総按分用$コスト / 全体総按分用$コスト) × 100
  10. CSV生成

出力(ローカル):

  • data/allocation_result.csv: 按分結果(37列、5行ヘッダー + 事業単位サマリーセクション)
  • data/allocation_result.json: 按分結果(JSON形式、カテゴリ別 + 事業単位別サマリー)
  • data/business_unit_summary.csv: Slack添付用事業単位サマリー(事業単位と按分率のみの2列CSV)

最終保存先(S3、Step 5でアップロード):

  • s3://amazon-connect-cost-analysis/processed/YYYY-MM/allocation_result.csv
  • s3://amazon-connect-cost-analysis/processed/YYYY-MM/allocation_result.json
  • s3://amazon-connect-cost-analysis/processed/YYYY-MM/business_unit_summary.csvSlack添付用

注意事項:

  • CSVフォーマットは既存の手動運用フォーマットと完全一致すること
  • 料金体系はpricing_config.yamlで管理
  • 浮動小数点演算の精度に注意(Decimal型推奨)

CSV結果ファイルフォーマット:

CSVファイルは、手動運用時に使用していたGoogle Spreadsheet「amazon connect_2025年10月版 - 202510_sample③詳細版※提出用.csv」と**同じフォーマット(37列、5行ヘッダー)**で出力されます。

フォーマット詳細

ヘッダー行(1-5行目):

  • 1行目: 集計期間、料金体系情報(INBOUND, OUTBOUND等の料金情報、保持料金情報)
  • 2行目: システム利用料、単価情報、保持料金詳細(DID/TOLL_FREE)
  • 3行目: 単価詳細(通話料金の単価/分)
  • 4行目: IMPORTRANGE参照式、倍率、集計金額
  • 5行目: カラム名

主要データカラム:

  1. No: 連番
  2. queue_name: キュー名
  3. system_tel: システム電話番号
  4. FDフラグ: フリーダイヤルフラグ(FD / 空白)
  5. category_queue: キューカテゴリ
  6. category_number: 番号カテゴリ
  7. category_fix: 確定カテゴリ(B / C / G / Online / 共通 等)
  8. Inbound(分): INBOUND通話時間(分)
  9. Inbound:FD(分): INBOUND(フリーダイヤル)通話時間(分)
  10. Outbound(分): OUTBOUND通話時間(分)
  11. キュー転送(分): QUEUE_TRANSFER通話時間(分)
  12. 転送(分): TRANSFER通話時間(分)
  13. 外部転送(分): EXTERNAL_OUTBOUND通話時間(分)
  14. 自動架電(分): API通話時間(分)
  15. 通話合計(分): 合計通話時間 16-37. その他集計列(コスト計算、按分用データ、カテゴリ別集計、PL反映金額等)

料金計算ルール(ヘッダーに記載):

  • INBOUND(通常): $0.018/分
  • INBOUND(フリーダイヤル): $0.003/分
  • OUTBOUND系: $0.078/分
  • システム利用料: $0.018/分(全通話タイプ共通)※pricing_config.yaml参照
  • DID保持料: $0.10/日 → $3.00/月
  • TOLL_FREE保持料: $0.48/日 → $14.40/月

事業単位別サマリーセクション(2025-12追加):

データ行の後に以下のセクションが追加されます:

[空行]
【事業単位別サマリー】
[空行]
事業単位 | カテゴリ | レコード数 | 合計時間(分) | 合計コスト($) | 按分率(%)
在宅事業 | B + 共通 + C | xxx | xxx.xx | xxx | xx.xx
オンライン事業 | Online + メンタル | xxx | xxx.xx | xxx | xx.xx
地域支援 | G | xxx | xxx.xx | xxx | xx.xx
ファーマ | ファーマ | xxx | xxx.xx | xxx | xx.xx
合計 | | xxxx | xxxx.xx | xxxx | 100.00

エンコーディング: UTF-8 with BOM(Excel対応)

参照元ファイル: amazon connect_2025年10月版 - 202510_sample③詳細版※提出用.csv


JSON結果ファイルフォーマット:

allocation_result.jsonには、カテゴリ別および事業単位別の集計結果が含まれます。

フォーマット詳細
json
{
  "target_month": "2025-11",
  "generated_at": "2025-12-04T00:19:00+09:00",
  "total_records": 1178,
  "total_cost_usd": 15253,
  "category_summary": {
    "B": {
      "record_count": 949,
      "total_duration_minutes": 193672.4,
      "total_cost_usd": 4603,
      "total_cost_detailed_usd": 4603.488,
      "allocation_percentage": 30.18
    },
    "C": {
      "record_count": 155,
      "total_duration_minutes": 34500.2,
      "total_cost_usd": 5402,
      "total_cost_detailed_usd": 5401.715,
      "allocation_percentage": 35.41
    },
    "G": {
      "record_count": 12,
      "total_duration_minutes": 1200.0,
      "total_cost_usd": 7,
      "total_cost_detailed_usd": 6.812,
      "allocation_percentage": 0.05
    },
    "Online": {
      "record_count": 45,
      "total_duration_minutes": 8500.5,
      "total_cost_usd": 4320,
      "total_cost_detailed_usd": 4320.338,
      "allocation_percentage": 28.32
    },
    "メンタル": {
      "record_count": 15,
      "total_duration_minutes": 2800.0,
      "total_cost_usd": 920,
      "total_cost_detailed_usd": 919.836,
      "allocation_percentage": 6.03
    },
    "ファーマ": {
      "record_count": 1,
      "total_duration_minutes": 25.0,
      "total_cost_usd": 0,
      "total_cost_detailed_usd": 0.123,
      "allocation_percentage": 0.00
    },
    "共通": {
      "record_count": 1,
      "total_duration_minutes": 50.0,
      "total_cost_usd": 0,
      "total_cost_detailed_usd": 0.259,
      "allocation_percentage": 0.00
    }
  },
  "business_unit_summary": {
    "在宅事業": {
      "categories": ["B", "共通", "C"],
      "record_count": 1105,
      "total_duration_minutes": 228222.6,
      "total_cost_usd": 10005,
      "allocation_percentage": 65.59
    },
    "オンライン事業": {
      "categories": ["Online", "メンタル"],
      "record_count": 60,
      "total_duration_minutes": 11300.5,
      "total_cost_usd": 5240,
      "allocation_percentage": 34.35
    },
    "地域支援": {
      "categories": ["G"],
      "record_count": 12,
      "total_duration_minutes": 1200.0,
      "total_cost_usd": 7,
      "allocation_percentage": 0.05
    },
    "ファーマ": {
      "categories": ["ファーマ"],
      "record_count": 1,
      "total_duration_minutes": 25.0,
      "total_cost_usd": 0,
      "allocation_percentage": 0.00
    }
  }
}

主要フィールド:

  • total_cost_usd: カテゴリ別合計を整数丸めした総コスト(表示用
  • category_summary: カテゴリ別の詳細集計
    • total_cost_usd: 整数丸め後のコスト(表示用、手動集計と一致)
    • total_cost_detailed_usd: 丸め前の詳細コスト(Phase 2計算用、完全精度)← 重要
    • allocation_percentage: 按分率(小数点第2位まで)
  • business_unit_summary: 事業単位別の集計(2025-12追加)
    • categories: 含まれるカテゴリのリスト
    • total_cost_usd: 事業単位の総コスト(表示用
    • total_cost_detailed_usd: 丸め前の詳細コスト(Phase 2計算用、完全精度)← 重要
    • allocation_percentage: 事業単位の按分率

重要: Phase 2で手動計算との完全一致(¥0差異)を実現するため、total_cost_detailed_usd(完全精度の小数点値)が必須です。total_cost_usdは表示用のみで、Phase 2の計算には使用しません。


Step 5: S3アップロード

処理: S3への結果ファイルアップロード

要件:

  • 全ての出力ファイルをS3にアップロード
  • ディレクトリ構造を維持
  • メタデータ付与

入力:

  • data/配下の全JSONおよびCSVファイル
  • 環境変数: S3_BUCKET, TARGET_MONTH

処理ロジック:

  1. Rawデータをアップロード(raw/YYYY-MM/
    • bigquery_raw_data.json
    • aws_queue_list.json
    • aws_phone_numbers.json
  2. 処理済みデータをアップロード(processed/YYYY-MM/
    • allocation_result.csv - 詳細按分結果(37列、全通話データ + 事業単位サマリー)
    • allocation_result.json - JSON形式の按分結果
    • business_unit_summary.csv - Slack添付用事業単位サマリー(事業単位と按分率のみ)
  3. レビュー用データをアップロード(review/YYYY-MM/
    • 新規リソースがある場合のみ:
      • new_resources.json: 新規追加されたキュー・電話番号
      • ai_categorization.json: Claude AI判定結果と信頼度

出力:

  • S3バケット: s3://amazon-connect-cost-analysis/
    • raw/YYYY-MM/*.json (再計算時に再利用)
    • processed/YYYY-MM/*.csv, *.json (最終結果)
    • review/YYYY-MM/*.json (新規リソースがある月のみ)

注意事項:

  • aws s3 cpコマンドを使用
  • アップロード失敗時は非ゼロの終了コードを返すこと
  • 本番のカテゴリマスタ(categories/)はStep 3で参照し、レビュー待機用(review/YYYY-MM/)はStep 5.5でアップロードする
  • S3バージョニングが有効なため、ファイルの全履歴が保持される
  • 新規リソースがない場合、review/ディレクトリは作成されない
  • raw/データは再計算モード時に再利用される

Step 5.5: レビュー待機ファイルのS3アップロード(条件付き)

処理: 新規リソースがある場合、レビュー待機用のカテゴリマスタをS3にアップロード

実行条件:

  • 新規リソースが1件以上ある場合のみ実行
  • 新規リソースがない場合はスキップ
  • 再計算モード時はスキップ

要件:

  • S3のカテゴリマスタを更新(レビュー待機用のデータをアップロード)
  • AI判定結果をSlack通知に含めることで人間がレビュー可能にする
  • 承認されれば手動でS3の本番カテゴリマスタを更新

入力:

  • data/queue_categories.json(Step 3でマージ済み、ローカル)
  • data/number_categories.json(Step 3でマージ済み、ローカル)
  • S3のreview/YYYY-MM/new_resources.json(新規リソース一覧)
  • S3のreview/YYYY-MM/ai_categorization.json(AI判定結果)
  • 環境変数: TARGET_MONTH, S3_BUCKET

処理ロジック:

  1. S3から中間ファイルをダウンロード(レビュー情報生成用)
    • aws s3 cp s3://${S3_BUCKET}/review/${TARGET_MONTH}/new_resources.json ./
    • aws s3 cp s3://${S3_BUCKET}/review/${TARGET_MONTH}/ai_categorization.json ./
    • これらは既にStep 5でS3にアップロード済み
  2. レビュー待機用のカテゴリマスタをS3にアップロード
    • data/queue_categories.jsons3://${S3_BUCKET}/review/${TARGET_MONTH}/queue_categories_pending.json
    • data/number_categories.jsons3://${S3_BUCKET}/review/${TARGET_MONTH}/number_categories_pending.json
    • これらはレビュー承認後に本番のcategories/フォルダに手動でコピー
  3. レビュー情報を生成
    • ai_categorization.jsonから信頼度別にリソースをグループ化
    • レビュー用のサマリー情報を作成(Slack通知で使用)

出力:

  • S3のreview/YYYY-MM/queue_categories_pending.json
  • S3のreview/YYYY-MM/number_categories_pending.json
  • レビュー情報(Slack通知用)

注意事項:

  • このステップはS3へのアップロードのみ実施(GitHubへのPR作成は廃止)
  • レビュー待機用ファイルはreview/フォルダに保存し、本番のcategories/とは分離
  • 人間がレビュー後、承認されれば手動でS3のcategories/フォルダにコピー
  • 承認されない場合、次回実行時も同じカテゴリマスタを使用(新規リソースとして再検出される)

Step 6: Slack通知

処理: 実行結果をSlackチャンネルに通知し、按分結果CSVを添付

実装: .github/scripts/amazon-connect/phase1_step6_notify_slack.py(Pythonスクリプト)

要件:

  • ワークフロー完了時に成功/失敗を通知
  • S3へのリンクと集計サマリーを含める
  • 按分結果CSVファイルをSlackスレッドに添付

CSV添付の仕組み:

  1. Step 5.7: S3からbusiness_unit_summary.csvをダウンロード(processed/${TARGET_MONTH}/business_unit_summary.csv
    • 添付CSVの内容: 事業単位と按分率のみのシンプルな2列CSV
  2. Step 6: Slack Bot Token APIのfiles.uploadV2を使用してCSVをアップロード
    • 3ステップAPI呼び出し: getUploadURLExternal → ファイルアップロード → completeUploadExternal
    • thread_tsパラメータでメイン通知にCSVを添付

Slack添付用CSV形式business_unit_summary.csv):

csv
事業単位,按分率
在宅事業,0.7019
オンライン事業,0.2980
地域支援,0.0001
ファーマ,0.0000

: 按分率は小数点形式(0.0000 〜 1.0000)で出力されます。スプレッドシートでの計算に使いやすい形式です。

入力:

  • 実行ステータス(成功/失敗/再計算)
  • 対象月(TARGET_MONTH
  • 実行時間(秒)
  • 新規キュー数
  • 新規電話番号数
  • S3バケットURL
  • PR URL(Step 5.5で作成された場合)
  • CSVファイルパス(Step 5.7でダウンロードされたローカルファイル)

通知内容:

実際のSlack通知内容は、本書の「ワークフロー設計 - Phase 1 - Slack通知内容」セクション(343-369行)を参照してください。

通知の特徴:

  • 事業単位別集計を表示
  • CSV添付(business_unit_summary.csv)
  • 分類誤りがある場合の対処方法を記載
  • 次のアクション(Megazone手動オペレーション)を通知
  • 新規リソース検出時も、カテゴリマスタは自動更新されます(Step 5.6)

出力:

  • Slackチャンネル: #squad-sre-noti-saas-status-dev
  • Webhook URL: SLACK_WEBHOOK_URL(GitHub Secrets)- 投稿メッセージ
  • Slack Bot Token: SLACK_BOT_TOKEN_COST_MANAGEMENT(GitHub Secrets、CSV添付用)
  • Slack Channel ID: SLACK_CHANNEL_ID_COST_MANAGEMENT(GitHub Secrets、CSV添付用)

Slack API使用:

  • Webhook API: メインメッセージの投稿(Blocks API形式)
  • Bot Token API: CSVファイルのアップロード(files.uploadV2、スレッド添付)

注意事項:

  • エラー時はメンションで担当者に通知
  • 重要: Slack通知には機密情報(電話番号、キュー名の詳細、医療機関名等)を含めないこと
    • 新規リソース数のみ通知(具体的なリソース名は通知しない)
  • CSVファイルにはキュー名・電話番号などの詳細情報が含まれるため、Slackチャンネルのアクセス権限管理を適切に行うこと
    • S3パスのみ通知(ファイル内容は通知しない)

データフロー

1. Amazon Connect → JSON

概要: Amazon Connect APIから、キュー一覧と電話番号一覧をJSON形式で取得します。これらのマスタデータは、通話レコードをカテゴリに分類する際の基準となります。キューには標準キュー(STANDARD)のみを対象とし、各キューの名前と説明文を取得します。

実装例
bash
# キューデータ
aws connect list-queues --instance-id {ID} \
  | jq -r '.QueueSummaryList[] | select(.QueueType == "STANDARD") | .Id' \
  | sort | uniq \
  | xargs -I {} aws connect describe-queue --instance-id {ID} --queue-id {} \
  | jq -r '[.Queue.Name, .Queue.Description // ""] | @csv'
  > queues.json

# 電話番号データ
aws connect list-phone-numbers-v2 --instance-id {ID} \
  | jq -c '.ListPhoneNumbersSummaryList[]' \
  > phone_numbers.json

2. BigQuery → 通話ログデータ

概要: Amazon Connectのコンタクトトレースレコード(CTR)をBigQueryから取得します。CTRには通話の開始・終了時刻、キュー名、電話番号、通話タイプ(INBOUND/OUTBOUND等)が記録されており、これをもとにコスト按分の基礎データを作成します。

データソース: oc-monitoring-358909.aws_connect.contact_trace_records

取得データの特徴:

  • キュー名×電話番号ごとに集計
  • 6種類の通話タイプ(initiationmethod)別に通話時間とコール数を算出
  • 対象期間は前月の1日〜最終日
  • システム電話番号(system_tel)がNULLのレコードは除外

接続シート4のクエリ(最も詳細なデータ):

実装例
sql
SELECT
  JSON_EXTRACT_SCALAR(queue, '$.name') AS queue_name,
  JSON_EXTRACT_SCALAR(systemendpoint, '$.address') AS system_tel,
  SUM(CASE WHEN initiationmethod = 'API' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_api,
  SUM(CASE WHEN initiationmethod = 'API' THEN 1 ELSE 0 END) AS interaction_count_api,
  SUM(CASE WHEN initiationmethod = 'EXTERNAL_OUTBOUND' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_external_outbound,
  SUM(CASE WHEN initiationmethod = 'EXTERNAL_OUTBOUND' THEN 1 ELSE 0 END) AS interaction_count_external_outbound,
  SUM(CASE WHEN initiationmethod = 'INBOUND' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_inbound,
  SUM(CASE WHEN initiationmethod = 'INBOUND' THEN 1 ELSE 0 END) AS interaction_count_inbound,
  SUM(CASE WHEN initiationmethod = 'OUTBOUND' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_outbound,
  SUM(CASE WHEN initiationmethod = 'OUTBOUND' THEN 1 ELSE 0 END) AS interaction_count_outbound,
  SUM(CASE WHEN initiationmethod = 'QUEUE_TRANSFER' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_queue_transfer,
  SUM(CASE WHEN initiationmethod = 'QUEUE_TRANSFER' THEN 1 ELSE 0 END) AS interaction_count_queue_transfer,
  SUM(CASE WHEN initiationmethod = 'TRANSFER' THEN
    TIMESTAMP_DIFF(DATETIME(disconnecttimestamp, 'Asia/Tokyo'),
                   DATETIME(initiationtimestamp, 'Asia/Tokyo'), SECOND) / 60
    ELSE 0 END) AS total_interaction_duration_minutes_transfer,
  SUM(CASE WHEN initiationmethod = 'TRANSFER' THEN 1 ELSE 0 END) AS interaction_count_transfer
FROM
  `oc-monitoring-358909.aws_connect.contact_trace_records`
WHERE
  DATE(connectedtosystemtimestamp, 'Asia/Tokyo') BETWEEN '2025-10-01' AND '2025-10-31'
  AND DATETIME(connectedtosystemtimestamp, 'Asia/Tokyo') >= DATETIME(TIMESTAMP('2025-10-01 00:00:00'), 'Asia/Tokyo')
  AND JSON_EXTRACT_SCALAR(systemendpoint, '$.address') IS NOT NULL
GROUP BY
  queue_name, system_tel
ORDER BY
  queue_name;

出力データ構造:

実装例
json
[
  {
    "queue_name": "(DP)テストクリニックA",
    "system_tel": "+815011111111",
    "total_interaction_duration_minutes_api": 0,
    "interaction_count_api": 0,
    "total_interaction_duration_minutes_external_outbound": 2.066666667,
    "interaction_count_external_outbound": 3,
    "total_interaction_duration_minutes_inbound": 2.966666667,
    "interaction_count_inbound": 5,
    "total_interaction_duration_minutes_outbound": 0,
    "interaction_count_outbound": 0,
    "total_interaction_duration_minutes_queue_transfer": 0,
    "interaction_count_queue_transfer": 0,
    "total_interaction_duration_minutes_transfer": 0,
    "interaction_count_transfer": 0
  }
]

3. コスト計算ロジック

概要: 取得した通話データと電話番号情報をもとに、カテゴリ別のコスト按分を計算します。通話料金は通話タイプ(INBOUND/OUTBOUND)と電話番号種別(フリーダイヤル/通常番号)によって異なる単価を適用し、番号保持料は各カテゴリの保有回線数比率で按分します。

3.1 料金体系

概要: Amazon Connect の料金は「通話料」「システム利用料」「番号保持料(DID料金)」の3種類で構成されます。通話料は通話タイプと電話番号の種類(フリーダイヤル/通常番号)によって単価が異なります。

通話料金単価:

実装例
python
CALL_RATES = {
    # INBOUND(受信)
    "INBOUND_FD": 0.003,         # フリーダイヤル(0120番号)
    "INBOUND_NORMAL": 0.018,     # 通常番号(050/03番号)

    # OUTBOUND・転送系(全て同一料金)
    "OUTBOUND": 0.078,
    "QUEUE_TRANSFER": 0.078,
    "TRANSFER": 0.078,
    "EXTERNAL_OUTBOUND": 0.078,
    "API": 0.078
}

# システム利用料(INBOUNDのみ)
SYSTEM_FEE = 0.018  # per minute

番号保持料(DID料金):

実装例
python
LINE_HOLDING_COST = {
    "DID": {
        "daily": 0.10,      # $0.10/日
        "monthly": 3.00     # $3.00/月(30日)
    },
    "TOLL_FREE": {
        "daily": 0.48,      # $0.48/日
        "monthly": 14.40    # $14.40/月(30日)
    }
}

# 保有回線数(2025年10月実績)
TOTAL_LINES = {
    "DID": 857,           # 050/03番号
    "TOLL_FREE": 169,     # 0120番号
    "TOTAL": 1026
}

# 月次番号保持料総額
MONTHLY_HOLDING_COST = {
    "DID": 2571.00,       # $3.00 × 857回線
    "TOLL_FREE": 2433.60, # $14.40 × 169回線
    "TOTAL": 5004.60      # 合計
}

3.2 フリーダイヤル判定

概要: 電話番号がフリーダイヤル(0120番号)か通常番号(050/03番号)かを判定します。この判定結果により、INBOUND通話の料金単価($0.003/分 vs $0.018/分)が決定されます。

判定ロジック:

  • 入力: 国際形式の電話番号(例: +81120xxxxxxx
  • 判定条件:
    • +81120で始まる → フリーダイヤル(0120番号)
    • +8112で始まる → フリーダイヤル(0120番号、短縮形)
    • ❌ それ以外 → 通常番号(050/03番号)
  • 戻り値: Boolean(True: フリーダイヤル、False: 通常番号)

3.3 カテゴリ分類ロジック

概要: 各通話レコードを事業部カテゴリ(B、C、Online等)に分類します。キューマスタとPhone番号マスタの2つのマスタデータを使用し、キューカテゴリを優先して判定します。これにより、同じ電話番号でも異なるキューに振り分けられた通話を適切な事業部に按分できます。

優先順位: category_queue > category_number

判定ロジック:

  1. キューカテゴリを優先
    • queue_master辞書からqueue_nameでカテゴリ検索
    • 見つかればそのカテゴリを返す
  2. 番号カテゴリで補完
    • キューカテゴリが見つからない場合
    • phone_master辞書からsystem_telでカテゴリ検索
    • 見つかればそのカテゴリを返す
  3. 未分類として扱う
    • どちらのマスタにも存在しない場合
    • "未分類"を返す

カテゴリ一覧:

B
共通
C
G
followup
マーケ
CS
Online
メンタル
シフト
海外・旅行事業
デジタル往診
C AND B
ファーマ

3.4 通話料計算

概要: BigQueryから取得した通話時間データをもとに、通話タイプ(INBOUND/OUTBOUND等)と電話番号種別(フリーダイヤル/通常)に応じた料金を計算します。INBOUNDのみシステム利用料が発生します。

計算ロジック:

  1. 通話時間取得

    • BigQueryデータから6種類のinitiationmethodごとの通話時間(分)を取得
    • INBOUND, OUTBOUND, QUEUE_TRANSFER, TRANSFER, EXTERNAL_OUTBOUND, API
  2. 通話料計算

    • INBOUND(フリーダイヤル): 通話時間 × $0.003/分
    • INBOUND(通常): 通話時間 × $0.018/分
    • OUTBOUND系: 通話時間 × $0.078/分
      • OUTBOUND, QUEUE_TRANSFER, TRANSFER, EXTERNAL_OUTBOUND, API
  3. システム利用料計算

    • INBOUNDのみ: 通話時間 × $0.018/分
    • OUTBOUND系はシステム利用料なし
  4. 小計計算

    • 全通話料 + システム利用料 = 小計
  5. 戻り値

    • 各通話タイプのコスト
    • システム利用料
    • 小計
    • 合計通話時間

3.5 番号保持料の按分

概要: 月次番号保持料総額(DID料金)を各カテゴリの保有回線数に応じて按分します。通話の有無に関わらず発生する固定費であるため、回線数比率で公平に配分します。

按分ロジック:

  1. 入力データ

    • カテゴリ別回線数統計(各カテゴリの保有回線数とフリーダイヤル数)
    • 月次番号保持料総額(月の保有回線数により増減)
      • DID料金: $0.10/日 × 30日 = $3.00/月/番号
      • TOLL_FREE料金: $0.48/日 × 30日 = $14.40/月/番号
      • 月次総額 = (DID回線数 × $3.00) + (TOLL_FREE回線数 × $14.40)
      • 例: DID 50回線 + TOLL_FREE 10回線 = ($3.00 × 50) + ($14.40 × 10) = $150.00 + $144.00 = $294.00
  2. 回線数比率の計算

    • 全カテゴリの合計回線数を算出
    • 各カテゴリの回線数 ÷ 全体回線数 = カテゴリ別比率
  3. 按分額の計算

    • 各カテゴリの按分額 = 総額 × カテゴリ別比率
    • 例: カテゴリBが100回線(全体の20%)→ $5,004.60 × 0.2 = $1,000.92
  4. 出力データ

    • カテゴリごとの按分額(例: {"B": 487.50, "C": 975.00, ...}

3.6 カテゴリ別集計

概要: BigQueryの通話データ、キューマスタ、電話番号マスタの3つのデータソースを結合し、カテゴリごとに通話料と番号保持料を合算して最終的なコスト按分結果を算出します。この処理により、事業部別にAmazon Connectの利用コストを可視化できます。

集計ロジック:

  1. データ結合

    • BigQueryコールトレースデータ × キューマスタ(queue_nameで結合)
    • 上記結果 × 電話番号マスタ(system_telで結合)
    • 結合方式: LEFT JOIN(全コールトレースを保持)
  2. カテゴリ決定

    • category_queue(キューカテゴリ)を優先
    • キューカテゴリが未設定の場合はcategory_number(番号カテゴリ)で補完
    • どちらも未設定の場合は未分類
  3. フリーダイヤル判定

    • system_telに対してフリーダイヤル判定を実施
    • 判定結果をis_fdフラグとして保持
  4. 通話料計算

    • 各行に対して通話料計算関数を適用
    • フリーダイヤルフラグを考慮した料金計算
    • 計算結果の小計をcall_costとして保持
  5. カテゴリ別グループ化

    • categoryでグループ化
    • 集計項目:
      • 通話料合計(call_costの合計)
      • 回線数(system_telのユニーク数)
      • 通話時間合計、コール数等
  6. 番号保持料按分

    • カテゴリごとの回線数統計を作成
    • 回線数比率で月次保持料総額を按分
    • 按分結果をholding_cost列として追加
  7. 総コスト計算

    • grand_total = call_cost(通話料) + holding_cost(保持料按分額)

出力データ:

  • カラム: [category, total_cost, total_minutes, call_count, holding_cost, grand_total]

3.7 事業単位別集計

概要: カテゴリ別集計(3.6)の結果を事業単位別に再集計します。複数のカテゴリを束ねて事業単位レベルでコストを可視化することで、経営判断に必要な粒度での分析を可能にします。

事業単位の定義:

事業単位含まれるカテゴリ説明
在宅事業B + 共通 + C在宅医療関連サービス全体
オンライン事業Online + メンタルオンライン診療とメンタルヘルスサービス
地域支援GtoG・地域支援・行政向けサービス
ファーマファーマ薬局・調剤関連サービス

集計ロジック:

  1. カテゴリ別集計結果の取得

    • 3.6で計算されたカテゴリ別コスト、通話時間、レコード数を使用
  2. 事業単位別グループ化

    • 上記の定義に基づき、カテゴリを事業単位にマッピング
    • 各事業単位内のカテゴリデータを合算
  3. 集計項目

    • レコード数合計
    • 通話時間合計(分)
    • コスト合計($)
    • 按分割合(%)
  4. 按分割合の計算

    • 按分割合(%) = (事業単位コスト / 全体総コスト) × 100
    • 小数第2位まで表示

出力形式:

CSV形式(5行のヘッダー後、事業単位サマリーセクション):

事業単位 | カテゴリ | レコード数 | 合計時間(分) | 合計コスト($) | 按分率(%)
在宅事業 | B + 共通 + C | xxx | xxx.xx | xxx | xx.xx
オンライン事業 | Online + メンタル | xxx | xxx.xx | xxx | xx.xx
地域支援 | G | xxx | xxx.xx | xxx | xx.xx
ファーマ | ファーマ | xxx | xxx.xx | xxx | xx.xx
合計 | | xxxx | xxxx.xx | xxxx | 100.00

JSON形式(business_unit_summaryセクション):

json
"business_unit_summary": {
  "在宅事業": {
    "categories": ["B", "共通", "C"],
    "record_count": 1105,
    "total_duration_minutes": 228222.0,
    "total_cost_rounded": 10005,
    "allocation_percentage": 65.59
  },
  "オンライン事業": {
    "categories": ["Online", "メンタル"],
    "record_count": 60,
    "total_duration_minutes": 11300.0,
    "total_cost_rounded": 5240,
    "allocation_percentage": 34.35
  }
}

実装参考: phase1_step4_calculate_costs.py:298-330


3.8 Phase 2: Megazone実額ベース最終按分計算

概要: Phase 1で計算した事業部別コスト(完全精度)を使用し、Megazoneからの実際の請求額を手動入力として受け取り、手動計算(Excel/Google Spreadsheet)と完全一致する5ステップロジックで最終按分金額を算出します。

前提条件:

  • Phase 1で計算済みの事業部別コスト(total_cost_detailed_usdで完全精度を保持)
  • Megazone Hyper Billingから取得した通話料総額(USD)
  • バクラク請求書PDFから取得した為替レートとAWS総額(税込・円)

入力パラメータ(手動入力):

python
target_month = "2025-11"           # 対象月
exchange_rate = 155.87             # 為替レート(円/ドル)
aws_total_jpy = 2457024            # AWS総額(税込・円)
total_call_cost_usd = 18776.47     # 通話料総額(USD、Megazone実額)

5ステップ計算ロジック(手動計算との完全一致版):

このロジックは手動計算のスプレッドシート構造を正確に再現しています。

ステップ変数名計算式
Step 1call_cost_usdsum(カテゴリ別のtotal_cost_detailed_usd)
Step 2total_call_cost_all_unitssum(全事業単位の通話料)
Step 3total_cost_with_line_fee通話料 + 回線保持料金総額 × (通話料 / 全事業単位の通話料合計)
Step 4total_cost_with_line_fee_all_unitssum(全事業単位の総コスト)
Step 5allocated_jpyround(総コスト / 全事業単位の総コスト合計 × 円換算総額)
Step 1: 各事業単位の通話料(保持料金なし)を計算

Phase 1で計算済みのカテゴリ別コスト(完全精度)を、事業単位ごとに合算します。

実装例
python
# Phase 1で計算済み(S3から取得)
# 重要: total_cost_detailed_usd(完全精度)を使用
category_summary = {
    "B": {"total_cost_detailed_usd": 4603.46905},
    "共通": {"total_cost_detailed_usd": 1245.17435},
    "C": {"total_cost_detailed_usd": 4157.19555},
    # ...
}

business_units = {
    "在宅事業": {
        "categories": ["B", "共通", "C"],
        # ...
    },
    # ...
}

# 各事業単位の通話料を計算(完全精度)
call_cost_usd_values = {}
for unit_name, unit_data in business_units.items():
    categories = unit_data['categories']
    call_cost_usd = sum(
        Decimal(str(category_summary[cat]['total_cost_detailed_usd']))
        for cat in categories
        if cat in category_summary
    )
    call_cost_usd_values[unit_name] = call_cost_usd

# 例:
# 在宅事業: 4603.46905 + 1245.17435 + 4157.19555 = 10005.83895 USD
Step 2: 全事業単位の通話料合計

全事業単位の通話料を合計します。

実装例
python
total_call_cost_all_units = sum(call_cost_usd_values.values())
# 例: 10005.83895 + 4246.5852 + 1.4 + 1.0 = 14254.824095 USD
Step 3: 各事業単位の総コスト(保持料金込)を計算

通話料に加えて、回線保持料金を通話料比率で按分して総コストを計算します。

計算式: 総コスト = 通話料 + 回線保持料金総額 × (通話料 / 全事業単位の通話料合計)

実装例
python
line_holding_fee_usd = 5004.60  # Phase 1から取得

total_cost_with_line_fee_values = {}
for unit_name, call_cost_usd in call_cost_usd_values.items():
    # 回線保持料金の按分
    unit_line_fee = line_holding_fee_usd * call_cost_usd / total_call_cost_all_units

    # 総コスト = 通話料 + 回線保持料金
    total_cost_with_line_fee = call_cost_usd + unit_line_fee
    total_cost_with_line_fee_values[unit_name] = total_cost_with_line_fee

# 例(在宅事業):
# unit_line_fee = 5004.60 × (10005.83895 / 14254.824095) = 3512.82...
# total_cost_with_line_fee = 10005.83895 + 3512.82... = 13518.66...
Step 4: 全事業単位の総コスト合計

全事業単位の総コストを合計します。

実装例
python
total_cost_with_line_fee_all_units = sum(total_cost_with_line_fee_values.values())
# 例: 13518.66... + 5995.39... + 1.97... + 1.40... = 19259.424095 USD
Step 5: 各事業単位の円換算(按分比率方式)

総コストの比率で円換算総額を按分します。

計算式: 円換算 = round(総コスト / 全事業単位の総コスト合計 × 円換算総額)

実装例
python
# 消費税率(10%)
tax_rate = Decimal('1.1')

# 円換算総額 = round(通話料総額(手動入力) × 為替レート × 1.1)
total_call_jpy_with_tax = (
    Decimal('18776.47') * Decimal('155.87') * tax_rate
).quantize(Decimal('1'), rounding=ROUND_HALF_UP)
# 例: 18776.47 × 155.87 × 1.1 = 3,219,632 円

# 各事業単位の円換算
for unit_name, total_cost_with_line_fee in total_cost_with_line_fee_values.items():
    # 按分比率を計算
    allocation_ratio = total_cost_with_line_fee / total_cost_with_line_fee_all_units

    # 円換算(四捨五入)
    allocated_jpy = (allocation_ratio * total_call_jpy_with_tax).quantize(
        Decimal('1'), rounding=ROUND_HALF_UP
    )

# 例(在宅事業):
# allocation_ratio = 13518.66... / 19259.424095 = 0.70185...
# allocated_jpy = round(0.70185... × 3,219,632) = 2,259,836 円
逆算による合計一致

各事業単位の円換算を独立して四捨五入すると、累積的な丸め誤差により合計値が目標値(total_call_jpy_with_tax)と一致しない場合があります(通常は±1〜2円程度)。

原因:

  • 各事業単位で独立して四捨五入を行うため、累積的な丸め誤差が発生

解決策:

  • N-1個の事業単位: 按分比率で計算して四捨五入
  • 最大金額の事業単位: 目標総額から他の事業単位の合計を引いた値を割り当て(逆算)
  • この方式により、合計が必ず厳密に一致し、かつ最大金額の事業単位で逆算するためパーセンテージ誤差が最小化される
実装例
python
# 最も金額の大きい事業単位を特定(最後に逆算する)
largest_unit = max(
    total_cost_with_line_fee_values.keys(),
    key=lambda unit: total_cost_with_line_fee_values[unit]
)

# N-1個の事業単位を按分計算
for unit_name, total_cost_with_line_fee in total_cost_with_line_fee_values.items():
    if unit_name == largest_unit:
        # 最大金額の事業単位はスキップ(後で逆算)
        continue

    # 按分比率を計算
    allocation_ratio = total_cost_with_line_fee / total_cost_with_line_fee_all_units
    allocated_jpy = (allocation_ratio * total_call_jpy_with_tax).quantize(
        Decimal('1'), rounding=ROUND_HALF_UP
    )

    final_allocations[unit_name]['allocated_jpy_with_tax'] = int(allocated_jpy)
    total_allocated_jpy += allocated_jpy

# 最大金額の事業単位を逆算(目標総額 - 他の事業単位の合計)
largest_unit_allocated_jpy = total_call_jpy_with_tax - total_allocated_jpy
final_allocations[largest_unit]['allocated_jpy_with_tax'] = int(largest_unit_allocated_jpy)
total_allocated_jpy += largest_unit_allocated_jpy

# 例: 在宅事業(最大金額)= ¥3,219,632 - (¥754,485 + ¥3 + ¥77) = ¥2,465,067
FDT付調整額の算出

目的: AWS総額と事業部別按分合計の差額を算出

計算:

python
# 全事業部按分合計
sum_allocated_jpy = sum(allocated_jpy for all units)

# FDT付調整額
fdt_adjustment_jpy = aws_total_jpy - sum_allocated_jpy
# 例: 2,457,024 - 2,259,836 - ... = XXX円

注意:

  • FDT付調整額は必ずしもプラスとは限らない
  • 最終的な合計がAWS総額と一致することを保証
出力CSV例
csv
category,call_cost_usd,line_cost_usd,total_usd,ratio,allocated_jpy
B,123.45,10.00,133.45,0.3113,764970
C,234.56,15.00,249.56,0.5821,1430134
Online,40.67,5.00,45.67,0.1065,261672
共通,20.00,3.00,23.00,0.0537,131999
FDT付調整,,,,,248

保存先: s3://amazon-connect-cost-analysis/final/YYYY-MM/final_allocation_YYYYMM.csv

メタデータ:

json
{
  "target_month": "2025-10",
  "exchange_rate": 151.50,
  "aws_total_jpy": 2234567,
  "total_jpy_with_tax": 2457024,
  "megazone_csv": "s3://.../megazone_cost_202510.csv",
  "megazone_pdf": "s3://.../megazone_invoice_202510.pdf",
  "phase1_result": "s3://.../processed/2025-10/preliminary_allocation.csv",
  "calculated_at": "2025-11-05T09:15:32Z"
}

4. Claude AI → カテゴリ判定

概要: Amazon Connectに新規追加されたキューや電話番号を、Claude AIを使用して自動的に事業部カテゴリ(B、C、Online等)に分類します。キュー名や説明文から用途を推測し、既存のカテゴリパターンと照合して最適なカテゴリを提案します。最終的な判定結果は人間がレビューして承認します。

入力:

実装例
json
{
  "new_queues": [
    {
      "name": "新規問い合わせキュー",
      "description": "一般的な問い合わせを受け付けるキュー"
    }
  ],
  "category_patterns": [
    {
      "category": "FDオンライン診療",
      "keywords": ["オンライン", "診療", "問診"],
      "examples": ["オンライン診療問い合わせ", "問診サポート"]
    }
  ]
}

出力:

実装例
json
{
  "categorized_queues": [
    {
      "name": "新規問い合わせキュー",
      "category": "FDオンライン診療",
      "confidence": 0.85,
      "reasoning": "説明文に「問い合わせ」が含まれ、オンライン診療関連と判断"
    }
  ]
}

5. S3保存

概要: 計算結果と元データをS3バケットに保存します。Rawデータ、処理済みデータ、カテゴリマスタ、レビュー用データの4種類に分類して保存し、新規リソースのレビューを容易にします。バージョニング機能により全履歴が自動保持され、CSV修正時の追跡も可能です。

バケット: amazon-connect-cost-analysis

保存処理:

  1. Rawデータ保存 (raw/YYYY-MM/)

    • bigquery_raw_data.json: BigQueryコールトレースデータ
    • aws_queue_list.json: Amazon Connectキュー一覧
    • aws_phone_numbers.json: Amazon Connect電話番号一覧
    • ContentType: application/json
    • メタデータ付与: target_month, data_type, execution_id
  2. 処理済みデータ保存 (processed/YYYY-MM/)

    • allocation_result.csv: コスト按分結果(37列、5行ヘッダー、詳細データ + 事業単位サマリー)
    • allocation_result.json: コスト按分結果(JSON形式、デバッグ用)
    • business_unit_summary.csv: Slack添付用事業単位サマリー(事業単位と按分率のみの2列CSV)
    • queue_categories.json: キューカテゴリマッピング
    • number_categories.json: 電話番号カテゴリマッピング

    : allocation_result.*business_unit_summary.csvは履歴保持用(タイムスタンプ付き)と最新版(固定名)の両方で保存されます。

  3. カテゴリマスタ保存 (categories/)

    • queue_categories.json: 最新のキューカテゴリ(次回実行で参照)
    • number_categories.json: 最新の電話番号カテゴリ(次回実行で参照)
  4. レビュー用データ保存 (review/YYYY-MM/)

    • new_resources.json: 新規追加されたキュー・電話番号のリスト
    • ai_categorization.json: Claude AIが判定したカテゴリと信頼度
      • 低信頼度(< 0.7)の項目は review_required: true フラグ付き
      • 人間がこのファイルを確認して不整合をチェック

アップロード仕様:

  • ✅ UTF-8エンコーディング(CSVはBOM付き)
  • ✅ S3メタデータ付与(対象月、データ種別、実行ID)
  • ✅ バージョニング有効(全履歴自動保持)
  • ✅ アップロード失敗時は非ゼロ終了コード

Claude AI活用設計

概要: 新規に追加されたAmazon Connectのキューや電話番号を、Claude AIが既存のカテゴリパターンと照合して自動分類します。これにより、手動でのカテゴリ設定作業を削減し、分類基準の一貫性を保つことができます。

カテゴリ判定ロジック

1. Claude AI プロンプト設計

概要: Claude AIに対して、カテゴリ分類のルールと期待する出力形式を指示するプロンプトです。キュー名・説明文から用途を推測し、カテゴリパターンと照合して最適なカテゴリを提案します。信頼度と判定理由も併せて出力することで、人間によるレビューを効率化します。

システムプロンプト:

実装例
あなたはFastDoctorのAmazon Connectのキュー・電話番号を適切な事業部カテゴリに分類する専門家です。

# 背景
FastDoctorは医療サービスを提供する企業で、複数の事業部がAmazon Connectの電話回線を共有しています。
コスト按分のため、各キューと電話番号を事業部カテゴリに分類する必要があります。

# カテゴリ一覧
以下のカテゴリから最適なものを選択してください:
- **B**: toB(医療機関向けサービス)
- **C**: toC往診(一般消費者向け往診サービス)
- **G**: toG(地域支援・行政向けサービス)
- **Online**: オンライン診療サービス(オンドク含む)
- **メンタル**: メンタルヘルスケアサービス
- **ファーマ**: 薬剤関連サービス
- **デジタル往診**: デジタル技術を活用した往診サービス
- **CS**: カスタマーサポート(一般問い合わせ)
- **マーケ**: マーケティング関連
- **followup**: フォローアップ・アフターケア
- **シフト**: シフト管理・調整
- **海外・旅行事業**: 海外・旅行関連サービス
- **共通**: 複数事業部で共有するリソース
- **C AND B**: toCとtoB両方で使用
- **未分類**: 上記のいずれにも該当しない

# 分類ルール
1. **キュー名・説明文から用途を推測**
   - キーワード(「オンライン」「診療」「往診」「問診」等)に注目
   - サービスの対象顧客(医療機関/一般消費者/行政)を考慮
2. **提供されたカテゴリパターンに基づいて分類**
   - category_patterns.jsonのキーワードと実例を参照
3. **優先順位**
   - 具体的な事業部名が含まれる場合は、その事業部を優先
   - 「オンライン」「診療」「問診」→ **Online**
   - 「往診」「訪問」「在宅」→ **B** または **C**
   - 「サポート」「問い合わせ」「ヘルプ」→ **CS**
4. **複数該当する場合**
   - 最も関連性の高いカテゴリを選択
   - 共有リソースと判断される場合は **共通**
5. **不明な場合**
   - **未分類** に分類し、confidence を低めに設定

# 出力形式
JSON形式で以下を返してください:

\`\`\`json
{
  "categorized_queues": [
    {
      "name": "キュー名または電話番号",
      "category": "カテゴリ名",
      "confidence": 0.85,
      "reasoning": "キュー名に「オンライン診療」が含まれ、説明文で問診サポートと記載されているため、Onlineカテゴリに分類"
    }
  ]
}
\`\`\`

# 注意事項
- **confidence**は0.0〜1.0の範囲で、判定の確信度を示す
  - 0.9以上: キーワードが明確に一致し、説明文でも裏付けられる
  - 0.7〜0.9: キーワードが一致するが、説明文が不明瞭
  - 0.5〜0.7: キーワードが部分的に一致、または推測による分類
  - 0.5未満: 不明確、人間のレビューが強く推奨される
- **reasoning**は日本語で、判定理由を具体的に記載(どのキーワードに基づいて判定したか等)
- 複数のキュー・電話番号を一度に判定する場合は、配列で返す

3. 人間によるレビュープロセス

概要: Claude AIによる自動判定結果は、必ず人間(SREチーム)がレビューして承認します。これにより、AIの誤判定を防ぎ、分類精度を維持します。レビューで発見された誤分類や新しいパターンは、カテゴリパターンファイルにフィードバックして継続的に改善します。

自動判定後の確認フロー:

  1. 完了後、Slack通知
  2. SREチームが判定結果をレビュー(S3のCSVを確認)
  3. 必要に応じてカテゴリを手動修正
  4. 修正内容をフィードバックとしてパターンファイルに反映

S3データ構成

概要: S3バケットは、データの種類(raw/processed/review)と対象月(YYYY-MM)で階層化されています。これにより、データの検索とアクセス管理を効率化し、新規リソースのレビューを容易にします。バージョニング機能により、CSV修正時も全履歴が保持されます。

バケット構造

実装例
s3://amazon-connect-cost-analysis/

├── categories/                    # カテゴリマスタ(機密情報)★変更点
│   ├── queue_categories.json          # キューカテゴリマッピング
│   └── number_categories.json         # 電話番号カテゴリマッピング

├── raw/                          # Phase 1: 生データ(タイムスタンプ付き)
│   └── YYYY-MM/                  # 例: 2025-11
│       ├── bigquery_raw_data_YYYYMMDD_HHMM.json     # BigQueryコールトレースデータ
│       ├── aws_queue_list_YYYYMMDD_HHMM.json        # Amazon Connectキュー一覧
│       └── aws_phone_numbers_YYYYMMDD_HHMM.json     # Amazon Connect電話番号一覧

├── processed/                     # Phase 1: 按分結果(タイムスタンプ付き + 固定名)
│   └── YYYY-MM/                   # 例: 2025-11
│       ├── allocation_result_YYYYMMDD_HHMM.json     # Phase 1按分結果JSON(履歴保持用)
│       ├── allocation_result.json                   # Phase 1按分結果JSON(最新版、固定名)
│       ├── allocation_result_YYYYMMDD_HHMM.csv      # Phase 1按分結果CSV(履歴保持用)
│       ├── allocation_result.csv                    # Phase 1按分結果CSV(最新版、固定名)
│       ├── business_unit_summary_YYYYMMDD_HHMM.csv  # 事業単位サマリー(履歴保持用)
│       ├── business_unit_summary.csv                # 事業単位サマリー(Slack添付用、最新版、固定名)
│       ├── queue_categories_YYYYMMDD_HHMM.json      # キューカテゴリ判定結果
│       └── number_categories_YYYYMMDD_HHMM.json     # 電話番号カテゴリ判定結果

├── final/                         # Phase 2: 最終按分結果(タイムスタンプ付き)
│   └── YYYY-MM/                   # 例: 2025-11
│       ├── final_allocation_YYYYMMDD_HHMM.csv       # 最終按分結果CSV(JPY・税込)
│       └── final_allocation_YYYYMMDD_HHMM.json      # 最終按分結果JSON

└── review/                        # レビュー用データ(新規リソース検出時のみ作成)
    └── YYYY-MM/                   # 例: 2025-11
        └── new_resources_detected_YYYYMMDD_HHMM.json  # 新規リソース検出情報

重要な変更: カテゴリマスタはS3のcategories/フォルダで管理します(GitHubリポジトリでの管理を廃止)

変更理由:

  • カテゴリマスタには機密情報(医療機関名、サービス名等)が含まれる
  • GitHubリポジトリでの管理はセキュリティリスクがある
  • S3のサーバー側暗号化(SSE-S3)により機密情報を保護
  • S3バージョニングにより変更履歴を完全に保持

バージョン復元:

  • Step 5.6の自動更新により誤ったカテゴリが設定された場合、S3バージョニングで前のバージョンに戻すことができます
  • 詳細な手順は カテゴリマスタバージョン復元手順書(今後作成予定) を参照してください

バケット命名規則:

  • production環境: amazon-connect-cost-analysis

パス命名規則:

  • 月次ディレクトリ: YYYY-MM形式(例: 2025-10
  • ハイフン区切りで統一

ファイル形式

raw/aws_queue_list.json

実装例
json
[
  {
    "queue_id": "a1b2c3d4-1234-5678-abcd-ef1234567890",
    "queue_name": "BasicQueue",
    "description": "基本キュー",
    "queue_arn": "arn:aws:connect:ap-northeast-1:900176301532:instance/.../queue/...",
    "status": "STANDARD"
  }
]

: 削除済みキューは自動的にスキップされ、JSONには含まれません

raw/aws_phone_numbers.json

実装例
json
[
  {
    "phone_number": "+815012345678",
    "phone_number_id": "12345678-abcd-...",
    "phone_number_type": "DID",
    "phone_number_country_code": "JP",
    "description": "メイン回線"
  },
  {
    "phone_number": "+818012099999",
    "phone_number_type": "TOLL_FREE",
    "phone_number_country_code": "JP",
    "description": "フリーダイヤル"
  }
]

raw/bigquery_raw_data.json

実装例
json
[
  {
    "business_date": "2025-10-01",
    "queue_name": "BasicQueue",
    "system_tel": "+815012345678",
    "contact_type": "INBOUND",
    "contact_time_minutes": 3.5
  }
]

processed/allocation_result.csv

5行ヘッダー + データ行、37列のCSV形式(詳細は別セクション参照)

processed/business_unit_summary.csv

目的: Slack添付用の事業単位サマリーCSV。事業単位と按分率のみのシンプルな2列形式。

実装例
csv
事業単位,按分率
在宅事業,0.7019
オンライン事業,0.2980
地域支援,0.0001
ファーマ,0.0000

ファイル形式:

  • エンコーディング: UTF-8
  • 列数: 2列
  • 行数: ヘッダー1行 + 事業単位4行(在宅事業、オンライン事業、地域支援、ファーマ)
  • 按分率: 小数点形式(0.0000 〜 1.0000、小数点第4位まで)
  • : スプレッドシートでの計算を考慮し、パーセントではなく小数点形式で出力

保存形式:

  • タイムスタンプ付き: business_unit_summary_YYYYMMDD_HHMM.csv(履歴保持用)
  • 固定名: business_unit_summary.csv(Slack添付用、最新版)

review/new_resources.json

目的: 前回実行時から新規追加されたキュー・電話番号のリスト。人間がレビューして不整合をチェックする。

実装例
json
{
  "target_month": "2025-10",
  "generated_at": "2025-11-01T00:05:23Z",
  "new_queues": [
    {
      "name": "新規オンライン診療キュー",
      "description": "オンドク問診サポート専用",
      "added_date": "2025-10-15",
      "arn": "arn:aws:connect:ap-northeast-1:123456789012:instance/xxx/queue/yyy"
    },
    {
      "name": "メンタルヘルス相談窓口",
      "description": "メンタルヘルスケア相談受付",
      "added_date": "2025-10-20",
      "arn": "arn:aws:connect:ap-northeast-1:123456789012:instance/xxx/queue/zzz"
    }
  ],
  "new_phone_numbers": [
    {
      "phone_number": "+815012345678",
      "type": "DID",
      "added_date": "2025-10-18",
      "arn": "arn:aws:connect:ap-northeast-1:123456789012:phone-number/xxx"
    }
  ],
  "summary": {
    "total_new_queues": 2,
    "total_new_phone_numbers": 1
  }
}

review/ai_categorization.json

目的: Claude AIが判定したカテゴリ。

実装例
json
{
  "target_month": "2025-10",
  "generated_at": "2025-11-01T00:05:45Z",
  "categorized_queues": [
    {
      "name": "新規オンライン診療キュー",
      "category": "Online",
      "confidence": 0.92,
      "reasoning": "キュー名に「オンライン診療」が含まれ、説明文で「オンドク問診サポート」と明記されているため、Onlineカテゴリに分類",
      "review_required": false
    },
    {
      "name": "メンタルヘルス相談窓口",
      "category": "メンタル",
      "confidence": 0.88,
      "reasoning": "キュー名・説明文に「メンタルヘルスケア」が含まれ、メンタルカテゴリに該当",
      "review_required": false
    }
  ],
  "categorized_phone_numbers": [
    {
      "phone_number": "+815012345678",
      "category": "未分類",
      "confidence": 0.45,
      "reasoning": "電話番号のみで用途が不明。キューとの紐付け情報がないため判定困難",
      "review_required": true
    }
  ],
  "summary": {
    "total_items": 3,
    "high_confidence_items": 2,
    "review_required_items": 1
  }
}

インフラ構成

概要: GitHub ActionsがAWSとGCPのリソースにアクセスするための認証・認可基盤です。Workload Identity Federation(OIDC)を使用することで、静的な認証情報(アクセスキー等)を使わずに安全にクラウドリソースへアクセスできます。

AWS構成

IAM Role(OIDC認証)

概要: GitHub ActionsワークフローがAWSリソース(Amazon Connect、S3、Bedrock)にアクセスするためのIAMロールです。OIDCトークンによる認証により、長期的なアクセスキーを管理する必要がなくなります。

ロール名: aws-cost-monitoring-github-actions-role

必要な権限:

  1. Amazon Connect読み取り権限
実装例
json
{
  "Sid": "AmazonConnectReadAccess",
  "Action": [
    "connect:ListQueues",
    "connect:DescribeQueue",
    "connect:BatchGetFlowAssociation",
    "connect:ListContactFlows",
    "connect:DescribeContactFlow",
    "connect:DescribeInstance"
  ],
  "Resource": [
    "arn:aws:connect:${aws_region}:${aws_account_id}:instance/${connect_instance_id}",
    "arn:aws:connect:${aws_region}:${aws_account_id}:instance/${connect_instance_id}/*"
  ]
}
  1. Amazon Connect電話番号読み取り権限
実装例
json
{
  "Sid": "AmazonConnectPhoneNumberReadAccess",
  "Action": ["connect:ListPhoneNumbersV2"],
  "Resource": "*"
}

: ListPhoneNumbersV2Resource: "*" が必要(AWS仕様)

  1. S3読み書き権限
実装例
json
{
  "Sid": "S3CostAnalysisBucketAccess",
  "Action": [
    "s3:PutObject",
    "s3:PutObjectAcl",
    "s3:GetObject",
    "s3:GetObjectVersion",
    "s3:ListBucket",
    "s3:ListBucketVersions"
  ],
  "Resource": [
    "arn:aws:s3:::amazon-connect-cost-analysis",
    "arn:aws:s3:::amazon-connect-cost-analysis/*"
  ]
}

: 本番環境では amazon-connect-cost-analysis を使用

  1. Bedrock (Claude AI) 実行権限
実装例
json
{
  "Sid": "BedrockInvokeInferenceProfile",
  "Action": [
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream"
  ],
  "Resource": "${bedrock_aip_arn}"
}
  1. Directory Service読み取り権限
実装例
json
{
  "Sid": "DirectoryServiceReadAccess",
  "Action": ["ds:DescribeDirectories"],
  "Resource": "*"
}

GCP構成

Workload Identity Pool

概要: GitHub ActionsがGCPリソース(BigQuery)にアクセスするためのWorkload Identity Federation設定です。GitHub OIDCトークンをGCPのサービスアカウントと紐付け、特定のリポジトリからのみアクセスを許可します。

リソース:

  • Workload Identity Pool: github-actions
  • Provider: github-actions-provider
  • Service Account: github-actions@${プロジェクト名}.iam.gserviceaccount.com

リポジトリ制限:

実装例
hcl
attribute_condition = "assertion.repository == 'fastdoctor-jp/terraform_for_aws'"

必要な権限:

  • roles/bigquery.dataViewer: BigQueryデータセット読み取り
  • roles/bigquery.jobUser: BigQueryジョブ実行

S3バケット(AWS)

概要: コスト按分計算結果と元データを保存するS3バケットです。バージョニングにより全履歴が保持され、ライフサイクルルールによりコスト効率的にデータを管理します。監査要件を満たすため、CloudTrailと連携して全アクセスログを記録します。

バケット名: amazon-connect-cost-analysis

基本設定:

  • ✅ バージョニング有効(全履歴保持)
  • ✅ サーバーサイド暗号化(AES256)
  • ✅ パブリックアクセスブロック有効
  • ✅ ライフサイクルルール設定

ライフサイクルルール:

対象保持期間アクション理由
review/1年間STANDARD人間がレビューする可能性あり
review/365日後STANDARD_IAに移行過去データ参照用
raw/1年間STANDARD再計算時に参照
raw/365日後STANDARD_IAに移行アーカイブ
processed/1年間STANDARD最終結果保持
processed/365日後STANDARD_IAに移行長期保存

アクセス制御:

  • 読み取り: SREチームロール
  • 書き込み: GitHub Actions OIDC Role、承認されたIAMロールのみ
  • 詳細は「セキュリティ考慮事項」セクション参照

タグ:

  • Name: cost-monitoring
  • ManagedBy: Terraform
  • Service: cost-monitoring

GCP構成

BigQuery

概要: 既存のBigQueryデータセット oc-monitoring-358909.aws_connect.contact_trace_records を使用します。Amazon Connectのコンタクトトレースレコード(CTR)が既に連携されているため、新たなデータパイプラインの構築は不要です。

GitHub Secrets

概要: GitHub Actionsワークフローが使用する機密情報をGitHub Secretsで管理します。これにより、認証情報をコードに含めずに安全に利用できます。

Environment: amazon-connect(GitHub Actions Environment Secretsを使用)

Secret名説明設定方法用途
CONNECT_INSTANCE_IDAmazon Connect インスタンスIDAWS ConsoleConnect API呼び出し
S3_BUCKETコスト集計データ保存用S3バケット名Terraform outputデータ保存・取得
GCP_PROJECT_IDGoogle Cloud プロジェクトIDGCP ConsoleBigQuery接続
GCP_WORKLOAD_IDENTITY_PROVIDERGCP Workload Identity ProviderTerraform outputGCP OIDC認証
GCP_SERVICE_ACCOUNTGCP Service AccountTerraform outputBigQuery実行権限
SLACK_WEBHOOK_URLSlack通知用Webhook URLSlack Appメッセージ投稿
SLACK_BOT_TOKEN_COST_MANAGEMENTSlack Bot User OAuth TokenSlack AppCSVファイル添付
SLACK_CHANNEL_ID_COST_MANAGEMENTSlack チャンネルID(#squad-sre-noti-saas-status-devSlack ConsoleCSVアップロード先

Slack App設定手順:

  1. Webhook URL取得: Incoming Webhooks機能を有効化してWebhook URLを生成
  2. Bot Token取得: OAuth & Permissions → Bot User OAuth Token(xoxb-で始まるトークン)
  3. Bot Scopes設定: 以下のスコープを付与
    • chat:write - メッセージ投稿
    • files:write - ファイルアップロード
  4. Channel ID取得: Slackチャンネルの詳細から取得(例: C01234ABCDE

運用設計

概要: コスト按分計算は2段階のワークフローで半自動化されます。Phase 1(毎月1日)で暫定按分を計算後、手動でMegazoneデータを取得、Phase 2(毎月5日)で最終按分を計算します。最終結果はバクラクで支払依頼を行います。

月次運用フロー

1日目: Phase 1自動実行

実行時刻: 09:00 JST(GitHub Actions自動実行)

処理内容:

  • AWS Cost Explorerベースの暫定按分計算
  • BigQueryからコールトレースデータ取得
  • Amazon Connect APIからキュー/電話番号マスタ取得
  • Claude AIによる新規リソースのカテゴリ判定
  • 暫定結果をS3保存(processed/YYYY-MM/

Slack通知内容:

✅ Amazon Connect コスト集計完了 (2025-10)

🏢 事業単位別集計
• 在宅事業 (B + 共通 + C): XXX件, XXX分, $XXX (XX.XX%)
• オンライン事業 (Online + メンタル): XX件, XXX分, $XXX (XX.XX%)
• 地域支援 (G): X件, XX分, $X (X.XX%)
• ファーマ (ファーマ): X件, XX分, $X (X.XX%)

📁 出力ファイル
• CSV: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv
• JSON: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.json
• サマリーCSV(添付): s3://amazon-connect-cost-analysis/processed/2025-10/business_unit_summary.csv

📎 添付ファイル
business_unit_summary_2025-10.csv(事業単位按分率サマリー)

⚠️ 分類誤りがある場合
利用がない事業部で計上されるなどの分類に誤りがある場合、以下のカテゴリファイルを修正しワークフローを再実行してください。
• s3://amazon-connect-cost-analysis/categories/queue_categories.json
• s3://amazon-connect-cost-analysis/categories/number_categories.json

📝 次のアクション: Megazone手動オペレーション
締切: 毎月5日まで
手順書: docs/sre/runbooks/megazone-bakuraku-manual-operation.md

2日〜5日: 手動作業(Megazone実額データ収集)

担当: SREチーム当番 or 経理担当者

作業内容:

  1. Megazone Hyper Billingにアクセス

  2. バクラクから請求書PDF取得

    • Megazone請求書PDFを取得
    • 為替レート(円/ドル) を確認してメモ
    • 例: 155.87 円/ドル
    • AWS総額(税込・円) を確認してメモ
    • 例: 2457024 円
    • 注意: 請求書送付は毎月第3営業日期限だが遅延が多い
  3. Phase 2ワークフローを手動実行

    • GitHub Actions → amazon-connect@final-cost-allocation.yml
    • 「Run workflow」ボタンをクリック
    • 以下の4つの値を入力:
      • target_month: 対象月(例: 2025-11
      • exchange_rate: 為替レート(例: 155.87
      • aws_total_jpy: AWS総額・税込(例: 2457024
      • total_call_cost_usd: 通話料総額(例: 18776.47
    • 「Run workflow」を実行

期限: なるべく早く(バクラク支払依頼期限: 第6営業日まで)

詳細手順: Megazone・バクラク手動オペレーション手順書(今後作成予定)

注意: S3へのCSV/PDFアップロードは不要です(手動入力方式のため)

手動作業完了後: Phase 2自動実行

実行方法: 前述の手動作業でワークフロー実行済み

入力パラメータ:

  • ワークフロー実行時に入力した4つの値を環境変数として使用

処理内容:

  • 環境変数から入力パラメータを取得
  • Phase 1結果をS3から取得(processed/YYYY-MM/allocation_result.json
  • 最終按分計算(5ステップロジック)
    • Step 1: 各事業単位の通話料(保持料金なし)を計算
    • Step 2: 全事業単位の通話料合計を計算
    • Step 3: 各事業単位の総コスト(保持料金込)を計算
    • Step 4: 全事業単位の総コスト合計を計算
    • Step 5: 各事業単位の円換算(按分比率方式)を計算
  • 最終結果をS3保存(processed/YYYY-MM/final/

Slack通知内容:

✅ Amazon Connect 最終コスト按分完了(2025年10月分)

【最終按分結果】
- 請求総額(税込): ¥2,457,024
- B事業部: ¥789,123
- C事業部: ¥1,476,890
- Online: ¥100,011
- 共通: ¥90,000
- FDT付調整: ¥1,000

⚠️ バクラク支払依頼期限: 第6営業日まで
📖 手順書: https://github.com/.../bakuraku-payment-request.md (TBD)

📊 結果CSV: s3://amazon-connect-cost-analysis/final/2025/10/final_allocation_202510.csv

〜第6営業日: バクラク手動作業

担当: 経理担当者 or SREチーム

作業内容:

  1. バクラクで支払依頼作成
  2. コメント記載: 「費用按分中」
  3. S3から最終按分CSV取得
  4. コメント更新: 各事業部の按分金額を記載
    【Amazon Connect 2025年10月分 費用按分】
    - B事業部: ¥789,123
    - C事業部: ¥1,476,890
    - Online: ¥100,011
    - 共通: ¥90,000
    - FDT付調整: ¥1,000
    - 合計: ¥2,457,024

期限: 毎月第6営業日

遅延時の対応: 経理に事前報告必須

詳細手順: Megazone・バクラク手動オペレーション手順書(今後作成予定)

実行スケジュール

スケジュール設定

Phase 1: 毎月2日 午前9時(JST)に自動実行

yaml
# Phase 1: amazon-connect@amazon-connect-cost-calculation.yml
on:
  schedule:
    - cron: '0 0 2 * *'  # 毎月2日 09:00 JST (UTC 00:00)
  workflow_dispatch:  # 手動実行も可能

# Phase 2: amazon-connect@final-cost-allocation.yml
on:
  workflow_dispatch:  # 手動実行のみ(Megazone作業完了後)

実行タイミングの設計根拠

1. なぜ毎月2日なのか?

Amazon Connect CTR(Contact Trace Records)の非同期反映特性に対応するため、毎月2日に設定しています。

CTRデータの特性:

  • 遅延反映: 通話終了後、BigQueryへの転送に数時間〜1日の遅延が発生の可能性あり
  • OUTBOUNDコールの複雑性: 特にアウトバウンドコールは処理が複雑で遅延しやすい
  • 月末データの反映時間: 月末最終日(例: 11/30)の通話が翌日以降に完全反映される可能性あり
  • データ整合性処理: 重複排除・データクレンジング・訂正処理の完了を待つ必要がある
2. データ検証結果に基づく判断

2025年11月分データで実際に観測されたデータ変化パターン例:

実行タイミングレコード数総コスト備考
12月1日114,648件$15,251一部CTRデータ未反映
12月2日(新版)114,662件$15,253遅延データ反映済み

重要な発見:

  • 月中データ(11/5, 11/17, 11/19, 11/27等)も後から追加・修正される
  • 単に月末だけでなく、月初〜月中のデータも継続的に更新される
  • システムメンテナンスやデータ品質向上処理により過去データが訂正される
3. 実行タイミング別の評価
実行日CTRデータ反映再計算リスクレポート速度総合評価
毎月1日❌ 不十分🔴 高✅ 最速非推奨
毎月2日✅ 十分🟢 低✅ 早い採用
4. 毎月2日実行のメリット(最終採用)
  1. データ安定性

    • CTR遅延データが十分に反映される(1日間の猶予で実績上問題なし)
    • データ整合性処理(重複排除・補完)が完了
    • 月中データの訂正も反映される
  2. 運用効率の向上

    • Megazone手動作業の開始を1日早められる
    • 月次締め作業全体を1日前倒しできる
    • 第6営業日のバクラク期限に余裕が生まれる
  3. 業務サイクルとの適合

    • 月初1営業日でデータ確定
    • 月初2日目にコスト計算実行
    • 結果レビューと承認のための時間的余裕が確保される
  4. コスト精度の向上

    • $1-2レベルの差異を回避(全体の0.01%程度)
    • 14-15レコード程度の漏れを防止
    • より正確な按分計算が可能
5. 運用上の推奨事項
  1. 自動実行の監視

    • GitHub Actions実行履歴の定期確認
    • 失敗時のSlack通知設定
  2. 不整合などの調査時の対応

    • 手動実行(workflow_dispatch)で任意のタイミングで実行可能
    • 月中のデータ確認やテストにも使用可能

モニタリング

1. 実行ログ

S3保存: s3://amazon-connect-cost-analysis/logs/YYYY/MM/DD/execution.json

実装例
json
{
  "execution_id": "run-12345",
  "execution_date": "2025-10-01T09:00:00Z",
  "status": "success",
  "target_month": "2025-10",
  "duration_seconds": 120,
  "error_message": null,
  "created_queues_count": 5,
  "updated_queues_count": 20,
  "claude_api_calls": 5,
  "s3_urls": {
    "monthly_report": "s3://amazon-connect-cost-analysis/reports/2025/10/monthly_report.csv"
  }
}

2. アラート

Slack通知設定:

  • 通知先チャンネル: #squad-sre-noti-saas-status-dev
  • 通知トリガー:
    • ワークフロー完了時(成功/失敗)

Slack通知例:

実装例
✅ Amazon Connect コスト集計完了 (2025-10)

🏢 事業単位別集計
• 在宅事業 (B + 共通 + C): 1105件, 228222分, $10005 (65.59%)
• オンライン事業 (Online + メンタル): 60件, 11300分, $5240 (34.35%)
• 地域支援 (G): 12件, 1200分, $7 (0.05%)
• ファーマ (ファーマ): 1件, 25分, $0 (0.00%)

📁 出力ファイル
• CSV: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv
• JSON: s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.json
• サマリーCSV(添付): s3://amazon-connect-cost-analysis/processed/2025-10/business_unit_summary.csv

📎 添付ファイル
business_unit_summary_2025-10.csv(事業単位按分率サマリー)

📝 次のアクション: Megazone手動オペレーション
締切: 毎月5日まで
手順書: docs/aws/operation/megazone-bakuraku-manual-operation.md

: 新規リソース検出時も、通知内容は同じです。カテゴリマスタはStep 5.6で自動的に更新されます。

エラーハンドリング方針

BigQueryタイムアウト時のリトライロジック

タイムアウト設定: 60秒

リトライ設定:

  • リトライ回数: 3回
  • リトライ間隔: Exponential Backoff(1秒 → 2秒 → 4秒)
  • 実装方法: Python tenacity ライブラリまたはシェルスクリプトでのリトライループ

失敗時アクション:

  • Slack通知(#squad-sre-noti-saas-status-dev
  • CloudWatch Logs記録
  • ワークフロー失敗ステータス
実装例
python
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=4)
)
def query_bigquery(query):
    # BigQueryクエリ実行
    pass

Amazon Connect API Throttling対策

Rate Limit: AWS公式制限

  • ListQueues: 2 TPS
  • DescribeQueue: 2 TPS
  • ListPhoneNumbers: 2 TPS

実装方針:

  • リクエスト間に0.5秒のsleep挿入
  • Throttling例外(TooManyRequestsException)発生時は5秒待機後リトライ(最大3回)

参考: Amazon Connect API throttling and service quotas

実装例
python
import time
import boto3
from botocore.exceptions import ClientError

def list_queues_with_throttling(connect_client, instance_id):
    max_attempts = 3
    for attempt in range(max_attempts):
        try:
            response = connect_client.list_queues(InstanceId=instance_id)
            time.sleep(0.5)  # Rate limit対策
            return response
        except ClientError as e:
            if e.response['Error']['Code'] == 'TooManyRequestsException':
                if attempt < max_attempts - 1:
                    time.sleep(5)  # 5秒待機
                    continue
                raise
            raise

PDF解析失敗時のフォールバック

エラー検出: 為替レートまたはAWS総額(JPY)の抽出失敗

フォールバック手順:

  1. Slack通知: PDF内容確認依頼
  2. ワークフロー一時停止: workflow_dispatchで再実行可能な状態
  3. 原因解析・修正: 原因解析とプログラムまたは資材を修正
  4. 再計算実行: 手動値での再計算実行

記録: 手動介入の記録をaudit/ディレクトリに保存

実装例
yaml
# .github/workflows/amazon-connect@final-cost-allocation.yml
on:
  workflow_dispatch:
    inputs:
      exchange_rate:
        description: '為替レート(手動入力時のみ)'
        required: false
        type: string
      aws_total_jpy:
        description: 'AWS総額JPY(手動入力時のみ)'
        required: false
        type: string

jobs:
  calculate:
    runs-on: ubuntu-latest
    steps:
      - name: Parse PDF or use manual input
        run: |
          if [ -n "${{ inputs.exchange_rate }}" ]; then
            echo "Using manual exchange rate: ${{ inputs.exchange_rate }}"
            EXCHANGE_RATE="${{ inputs.exchange_rate }}"
          else
            echo "Parsing PDF for exchange rate..."
            EXCHANGE_RATE=$(python parse_pdf.py)
          fi

GitHub Actionsリトライポリシー

実装例
yaml
- name: Get Amazon Connect Data
  uses: nick-fields/retry@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    retry_wait_seconds: 30
    command: |
      python .github/scripts/aggregate-connect-cost.py

CSV修正フロー

対象: 自動集計後に修正が必要な場合(カテゴリ誤判定、コスト調整等)

手順

1. S3から対象月のCSVをダウンロード

bash
aws s3 cp s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv \
  ./data/corrections/2025-10/allocation_result.csv

2. ローカルで修正

  • Excelで./data/corrections/2025-10/allocation_result.csvを編集
  • 修正理由をメモ(コミットメッセージに記載)

3. ブランチ作成してコミット

bash
git checkout -b fix/202510-cost-allocation-correction
git add data/corrections/2025-10/allocation_result.csv
git commit -m "fix: 2025年10月分のコスト按分を修正

修正理由:
- BasicQueueのカテゴリを「共通」→「Online」に変更
- PMからの指示により、オンライン事業部に按分

修正者: @oga
承認者: @pm-online"

4. PRを作成

bash
gh pr create \
  --title "fix: 2025年10月分のコスト按分を修正" \
  --body "## 修正内容
- BasicQueueのカテゴリを「共通」→「Online」に変更

## 修正理由
PMからの指示により、オンライン事業部に按分

## 影響範囲
- 影響行数: 12行
- コスト変動: +$87.84

## チェックリスト
- [x] CSV形式が正しい
- [x] PMの承認済み
- [x] 差分を確認済み" \
  --reviewer pm-online,sre-lead

5. レビュワーが差分確認・承認

  • GitHub上でCSVの差分を確認
  • 修正内容が妥当か確認
  • Approve後、PRをマージ

6. S3へアップロード(手動)例

bash
# PRマージ後、対象月のCSVをS3にアップロード
MONTH="2025-10"
FILE="data/corrections/${MONTH}/allocation_result.csv"
HASH=$(sha256sum "$FILE" | awk '{print $1}')

aws s3 cp "$FILE" "s3://amazon-connect-cost-analysis/processed/${MONTH}/allocation_result.csv" \
  --metadata "corrected_by=${USER},pr_number=<PR番号>,commit_sha=$(git rev-parse HEAD),sha256=${HASH}"

# 修正メタデータを記録
cat > correction_metadata.json <<EOF
{
  "corrected_by": "${USER}",
  "corrected_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
  "pr_number": "<PR番号>",
  "commit_sha": "$(git rev-parse HEAD)",
  "file": "${FILE}",
  "sha256": "${HASH}"
}
EOF

aws s3 cp correction_metadata.json \
  "s3://amazon-connect-cost-analysis/audit/${MONTH}/correction_$(date +%s).json"

: PRマージ後のGitHub ActionsによるS3自動アップロードは今後の展開(Phase 3)で実装予定


カテゴリ誤り修正と再計算フロー

概要: 月次処理後にカテゴリ誤りを発見した場合、カテゴリマスタを修正して該当月のコスト按分を再計算します。現在の実装では、GitHub Actionsの手動実行で全ステップを再実行しますが、修正済みカテゴリマスタが自動的に適用されるため、正しい結果が得られます。

前提条件

  • 月次処理が正常完了し、S3のraw/YYYY-MM/にデータが保存済み
  • カテゴリ誤りを発見(PRレビュー時またはS3レポート確認時)

シナリオ1: PR未マージ時の修正

状況: 月次処理完了後、自動作成されたPRをレビュー中にカテゴリ誤りを発見

手順:

  1. Slack通知でカテゴリ誤りを発見

    • Slackで通知されたレビュー情報を確認
    • AI判定結果を確認
    • 例: 「オンライン問診」が「C」と判定されているが、正しくは「Online」
    • S3のreview/YYYY-MM/queue_categories_pending.jsonを確認
  2. S3のレビュー待機ファイルをダウンロード

    bash
    # レビュー待機中のカテゴリマスタをダウンロード
    aws s3 cp s3://amazon-connect-cost-analysis/review/2025-10/queue_categories_pending.json ./ --region ap-northeast-1
  3. ローカルでファイルを編集

    • 誤ったカテゴリを修正:
      json
      // 修正前
      "オンライン問診": {
        "category": "C",
        "description": "オンライン診療専用",
        "created_at": "2025-10-15",
        "updated_at": "2025-10-15"
      }
      
      // 修正後
      "オンライン問診": {
        "category": "Online",
        "description": "オンライン診療専用",
        "created_at": "2025-10-15",
        "updated_at": "2025-11-05"  // 修正日時に更新
      }
  4. 修正済みファイルをS3の本番カテゴリマスタにアップロード

    bash
    # 修正済みファイルを本番の categories/ フォルダにアップロード
    aws s3 cp queue_categories_pending.json \
        s3://amazon-connect-cost-analysis/categories/queue_categories.json \
        --server-side-encryption AES256 \
        --region ap-northeast-1
    
    # アップロード確認
    aws s3 ls s3://amazon-connect-cost-analysis/categories/
  5. 再計算を手動実行

    • GitHub Actionsページを開く
    • Amazon Connect Cost Calculationワークフローを選択
    • Run workflowをクリック
    • パラメータ設定:
      • target_month: 2025-10
    • Run workflowを実行
  6. 実行される処理(現在の実装):

    • ✅ Step 1: BigQueryからCTRデータを再取得(同じ月なら同じデータが取得される)
    • ✅ Step 2: Amazon Connectからリソースメタデータを再取得
    • ✅ Step 3: 修正済みカテゴリマスタをS3から自動読み込み
      • カテゴリマスタの値が最優先で適用される
      • 既存リソースはマスタ値を使用、新規リソースのみClaude AI判定
    • ✅ Step 4: 修正済みカテゴリでコスト按分を再計算
    • ✅ Step 5: S3のprocessed/YYYY-MM/を更新(S3バージョニングで履歴保持)
    • ✅ Step 6: Slackに計算完了を通知

    : 現在の実装では全ステップを再実行しますが、BigQuery/Connect APIの再呼び出しコストは微々たるものです。将来的に高速化が必要な場合は、recalculate_onlyモードの実装を検討します(データ取得をスキップし、S3のrawデータを再利用)。

  7. 結果確認

    • Slackで再計算完了通知を確認
    • S3レポートをダウンロードして確認:
      bash
      aws s3 cp \
        s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv \
        ./allocation_result_fixed.csv

シナリオ2: レビュー後、数日経ってからカテゴリ誤りを発見

状況: 月次処理完了後、数日経ってからS3レポート確認時にカテゴリ誤りを発見

手順:

  1. S3の現在のカテゴリマスタをダウンロード

    bash
    # 現在の本番カテゴリマスタをダウンロード
    aws s3 cp s3://amazon-connect-cost-analysis/categories/queue_categories.json ./ --region ap-northeast-1
  2. ローカルでカテゴリマスタを修正

    • queue_categories.jsonを編集
    • 誤ったカテゴリを修正
    • updated_atを現在日時に更新
  3. 修正済みファイルをS3にアップロード

    bash
    # 修正済みファイルを本番の categories/ フォルダにアップロード
    aws s3 cp queue_categories.json \
        s3://amazon-connect-cost-analysis/categories/queue_categories.json \
        --server-side-encryption AES256 \
        --region ap-northeast-1
    
    # アップロード確認
    aws s3 ls s3://amazon-connect-cost-analysis/categories/
  4. 再計算を実行

    • 上記シナリオ1の手順5-7と同じ

シナリオ3: 複数月の一括修正

状況: 複数月にまたがるカテゴリ誤りを発見(例: 3ヶ月前から誤っていた)

手順:

  1. カテゴリマスタを修正・S3にアップロード(シナリオ2と同様)

  2. 影響を受ける月ごとに再計算

    • 古い月から順に手動実行:
      GitHub Actions → Run workflow
      - 2025-08: target_month=2025-08
      - 2025-09: target_month=2025-09
      - 2025-10: target_month=2025-10
    • 各実行で全ステップが実行され、修正済みカテゴリマスタが適用される
  3. 各月のS3レポートを確認

    bash
    for month in 2025-08 2025-09 2025-10; do
      aws s3 cp \
        s3://amazon-connect-cost-analysis/processed/${month}/allocation_result.csv \
        ./corrections/${month}_fixed.csv
    done

シナリオ4: AI判定プロンプトの修正

状況: AIによるカテゴリ判定が複数回にわたり誤っている、または新しいカテゴリパターンが追加されたためプロンプトの改善が必要

前提:

  • Claude AIプロンプトは.github/scripts/amazon-connect/phase1_step3_categorize_resources.shスクリプトファイル内に定義されている
  • プロンプト修正はGitHub PRレビュープロセスを経て反映される
  • プロンプト修正後は過去月の再計算は不要(次回の月次処理から新プロンプトが適用される)

手順:

  1. 誤判定のパターンを分析

    • S3のreview/YYYY-MM/ai_categorization.jsonを複数月分確認
    • 共通する誤判定パターンを抽出
    • 例:
      • 「オンライン問診」が常に「C」に誤分類される → 「Online」キーワードルールが不足
      • 「メンタル往診」が「C」に誤分類される → 「メンタル」優先度が低い
  2. プロンプト修正ブランチを作成

    bash
    TODAY=$(date +%Y-%m-%d)
    git checkout -b improve-ai-prompt-${TODAY}
    # 例: improve-ai-prompt-2025-11-18
  3. スクリプトファイルのプロンプトを修正

    • ファイル: .github/scripts/amazon-connect/phase1_step3_categorize_resources.sh
    • 修正箇所: スクリプト内のClaude AIプロンプト定義
    • 修正内容例:
      yaml
      # 修正前
      - 「オンライン」「診療」「問診」→ **Online**
      
      # 修正後
      - 「オンライン」「診療」「問診」「オンドク」→ **Online**(最優先)
      - ⚠️ 「往診」と併記されていても「オンライン」があればOnlineを優先
    • カテゴリ優先順位の明確化:
      yaml
      # 優先順位(高→低)
      1. 具体的なサービス名(「オンドク」「メンタルヘルス」等)
      2. 特定事業部キーワード(「Online」「メンタル」等)
      3. 一般的なキーワード(「往診」「サポート」等)
      4. 複数該当時は最も具体的なカテゴリを選択
  4. 信頼度ガイドラインの調整(必要に応じて)

    yaml
    # 修正前
    - 0.9以上: キーワードが明確に一致し、説明文でも裏付けられる
    
    # 修正後
    - 0.95以上: 固有のサービス名が含まれる(「オンドク」「ファストドクター往診」等)
    - 0.85〜0.95: カテゴリキーワードが明確に一致し、説明文でも裏付けられる
    - 0.7〜0.85: カテゴリキーワードが一致するが、説明文が不明瞭
    - 0.5〜0.7: キーワードが部分的に一致、または推測による分類
    - 0.5未満: 不明確、人間のレビューが強く推奨される
  5. PRを作成

    bash
    git add .github/scripts/amazon-connect/phase1_step3_categorize_resources.sh
    git commit -m "Improve AI categorization prompt (誤判定パターン修正: ${TODAY})"
    git push origin improve-ai-prompt-${TODAY}
    
    gh pr create \
      --title "AIカテゴリ判定プロンプト改善 (修正日: ${TODAY})" \
      --body "## 背景

複数月にわたりAIが特定のパターンを誤判定していることが判明したため、プロンプトを改善します。

誤判定パターン

  • 「オンライン問診」→ 誤: C / 正: Online(2025-08、2025-09、2025-10で発生)
  • 「メンタル往診」→ 誤: C / 正: メンタル(2025-09、2025-10で発生)

修正内容

カテゴリ優先順位の明確化

  • 「オンライン」キーワードを最優先に設定
  • 「メンタル」を「往診」より優先するよう調整

信頼度ガイドラインの細分化

  • 固有サービス名の信頼度を0.95以上に設定
  • 判定基準をより明確化

期待される効果

  • オンライン系サービスの誤判定率を削減
  • メンタル系サービスの誤判定率を削減
  • 信頼度の精度向上により、人間のレビュー負担を軽減

テスト計画

  • 次回月次処理(2025-12)で効果を検証
  • 過去の誤判定パターンが再発しないか確認"
    --reviewer sre-lead
  1. レビュー・承認・マージ

    • SREリードがプロンプト修正内容をレビュー
    • 期待される改善効果を確認
    • Approve & Merge
  2. 次回月次処理で効果を検証

    • 翌月の自動実行または手動実行で新プロンプトが使用される
    • review/YYYY-MM/ai_categorization.jsonで判定精度を確認
    • 信頼度と誤判定率の変化を評価
  3. 必要に応じて継続的に改善

    • 新しい誤判定パターンが発見された場合、再度プロンプト修正
    • カテゴリパターンの追加・変更時にプロンプトも更新

注意事項:

  • ⚠️ プロンプト修正は次回の月次処理から適用される(過去月には影響しない)
  • ⚠️ 過去月の誤分類はカテゴリマスタ修正+再計算で対応(シナリオ1-3参照)
  • ⚠️ プロンプト修正後は必ず実際の月次処理で検証すること
  • ⚠️ 大幅なプロンプト変更は、まずinfra-dev環境でテスト実行を推奨

高速再計算モード(将来対応)

現在の状況: 未実装

現在の実装では、カテゴリ修正後の再計算時も全ステップ(BigQuery/Connect API取得を含む)を実行します。将来的に高速化が必要になった場合、以下のrecalculate_onlyモードの実装を検討します。

将来実装予定のワークフロー入力パラメータ:

yaml
workflow_dispatch:
  inputs:
    target_month:
      description: '対象月 (YYYY-MM形式、例: 2025-10)'
      required: true
      type: string
    recalculate_only:
      description: 'カテゴリ修正後の再計算のみ実施(S3の既存データを再利用)'
      required: false
      type: boolean
      default: false

処理の違い(将来実装時):

処理ステップ現在の実装将来の再計算モード
Step 1: BigQueryデータ取得✅ 実行❌ スキップ
Step 2: Connectデータ取得✅ 実行❌ スキップ
Step 3: カテゴリ判定✅ 実行❌ スキップ
Step 3.5: S3データ読み込み❌ スキップ✅ 実行(raw/からロード)
Step 4: コスト按分計算✅ 実行✅ 実行(修正マスタ使用)
Step 5: S3保存✅ 実行✅ 実行(上書き)
Step 5.5: レビューファイルアップロード✅ 新規時のみ❌ スキップ
Step 6: Slack通知✅ 通常通知✅ 再計算通知

実装時のメリット:

  • ✅ データ取得が不要で高速(実行時間が1/3以下に短縮)
  • ✅ BigQuery/Connect APIを呼ばないためコスト削減
  • ✅ 何度でも再実行可能(冪等性)
  • ✅ S3バージョニングで履歴保持

現在の運用で十分な理由:

  • ✅ BigQuery無料枠が十分に大きい(月1TBまで無料)
  • ✅ Amazon Connect APIコストは微々たるもの(月数回の再計算なら数セント程度)
  • ✅ 全ステップを実行することで、データの整合性が保証される
  • ✅ 実装がシンプルで保守しやすい

実装判断基準: 以下の状況になった場合に実装を検討:

  • 月に10回以上の再計算が常態化
  • BigQuery無料枠を超過
  • 再計算の高速化要求(現在5-10分 → 1-2分への短縮)

追跡性の確保

修正内容は以下の仕組みで追跡可能:

  1. Git履歴: 修正内容・理由・承認者がコミットメッセージとPRに記録
  2. GitHub PR: 修正の差分、レビューコメント、承認履歴が残る
  3. S3メタデータ: 修正者、PR番号、コミットSHA、ファイルハッシュを記録
  4. S3バージョニング: 全バージョンの履歴を保持(上書き前のファイルも復元可能)
  5. CloudTrail: S3への操作ログ(誰が・いつ・何を)を記録(既存設定)
  6. 監査ログ: audit/YYYY-MM/correction_*.jsonに修正メタデータを保存

ディレクトリ構造例

terraform_for_aws/
├── data/
│   └── corrections/                    # 修正用CSV置き場(Git管理)
│       ├── 2025-10/
│       │   └── allocation_result.csv
│       ├── 2025-11/
│       │   └── allocation_result.csv
│       └── README.md                    # 修正手順書
└── docs/
    └── sre/
        └── runbooks/
            └── correct-amazon-connect-cost.md  # 詳細手順書

データ閲覧方法

1. S3コンソール直接アクセス

対象者: SREチーム

アクセス方法:

  1. AWS Console → S3 → amazon-connect-cost-analysis
  2. reports/YYYY/MM/ ディレクトリ配下のCSVをダウンロード

セキュリティ考慮事項

認証情報管理

AWS

  • ✅ OIDC認証(キーレス)
  • ✅ 一時的な認証情報
  • ✅ IAM Roleによる最小権限の原則

GCP

  • ✅ BigQuery読み取りのみ
  • ⚠️ サービスアカウントキーは使用しない

データアクセス制御

S3バケット

基本設定:

  • ✅ パブリックアクセスブロック有効
  • ✅ サーバーサイド暗号化(AES256)
  • ✅ バージョニング有効(全履歴保持)

アクセス制御(IAMポリシー):

書き込み権限をgithub actions, adminのIAMロールに限定

バージョニング設定:

全バージョンを保持し、上書き・削除操作を追跡可能に

バージョン確認・復元方法:

実装例
bash
# 全バージョン確認
aws s3api list-object-versions \
  --bucket amazon-connect-cost-analysis \
  --prefix processed/2025-10/allocation_result.csv

# 特定バージョンをダウンロード
aws s3api get-object \
  --bucket amazon-connect-cost-analysis \
  --key processed/2025-10/allocation_result.csv \
  --version-id <VERSION_ID> \
  old_version.csv

# 特定バージョンに復元(最新版として再アップロード)
aws s3 cp old_version.csv \
  s3://amazon-connect-cost-analysis/processed/2025-10/allocation_result.csv

CloudTrail(既存設定)

S3への全操作を記録(誰が・いつ・何を)

PRベースの承認フロー

CSV修正時の承認プロセス:

  1. ブランチ保護: mainブランチへの直接プッシュを禁止
  2. レビュー必須: 最低1名の承認が必要
  3. 差分確認: GitHub上でCSVの変更内容を可視化
  4. Git履歴: 修正理由・承認者がコミットメッセージとPRに記録

機密情報の取り扱い

スクリプト内での考慮:

実装例
python
# ❌ 悪い例
print(f"電話番号: {phone_number}")

# ✅ 良い例
print(f"電話番号: ***マスク済み***")

ログ出力時のマスキング:

  • 電話番号: 下4桁以外をマスク
  • 個人情報: 完全マスク

既知の課題と制約事項

1. 按分割合の計算における小数精度の保持(対応済み)

事象: 按分割合が手動計算と0.01%不一致(Online: 28.32% vs 28.33%、G: 0.05% vs 0.04%)

原因: 按分割合計算前にカテゴリ別コストを整数に丸めていた(例: $4,320.417 → $4,320で計算)

対応: 按分割合計算時は小数を保持、CSV/JSON出力時のみ整数に丸める設計に変更(phase1_step4_calculate_costs.py:567-587

結果: 按分割合が手動計算と完全一致(Online: 28.33%、G: 0.04%)


2. CTRデータの遅延反映(対応済み)

事象: CTRの非同期反映により実行タイミングで結果が変動(2025年11月分: 12月2日 114,648件/$15,251 → 12月3日 114,662件/$15,253)

原因: Amazon Connect CTRは非同期反映(最大1-2日遅延)、月中データも後日追加・修正される

対応: 実行スケジュールを毎月2日に設定

結果: CTR遅延データが十分に反映され、手動計算と同じデータバージョンを使用可能(実績上問題なし)


3. カテゴリ別コスト表示の丸め処理(対応済み)

事象: CSV/JSONに小数を出力すると手動集計の整数表示と見た目が不一致(例: $248.035 vs $248)

原因: 手動集計(Googleスプレッドシート)は内部で小数を保持するが表示形式で整数表示している

対応: 按分割合計算時は小数を保持、CSV/JSON出力時は整数に丸める(ROUND_HALF_UP)

結果: CSV/JSON出力値が手動集計の表示と完全一致、検証容易性と可読性が向上(最大±$0.49の丸め誤差は許容範囲)


4. 将来的な制約事項

4-1. カテゴリマスタの承認フロー

制約: 新規リソースのカテゴリ判定後、手動でS3の本番カテゴリマスタを更新する必要あり(Slack通知からワンクリック承認は未対応)

将来対応: Phase 5でSlack承認フローの自動化を検討(今後の展開 参照)

4-2. Phase 2(Megazone連携)の手動作業

制約: Megazone Hyper Billingからのデータ取得と最終按分計算は手動実行(毎月第3-5営業日、バクラク支払依頼作成が必要)

将来対応: Megazone APIが利用可能になった場合、Phase 2の完全自動化を検討


今後の展開

Phase 0: インフラ準備(事前準備)

目標: GitHub ActionsがAWS/GCPリソースにアクセスするための認証基盤構築

1. AWS IAMポリシーとロールの作成(Terraform)

  • 目的: GitHub ActionsがAWSリソースにアクセスするための認証基盤を構築
  • 対象リソース:
    • IAM Role: aws-cost-monitoring-github-actions-role
    • IAM Policy: Amazon Connect読み取り、S3読み書き、Bedrock実行、Directory Service読み取り
    • OIDC Provider: GitHub Actions用(token.actions.githubusercontent.com

2. GCP環境準備とWorkload Identity Poolの作成

ステップ1: オンライン事業部への権限設定依頼

  • 実施者: SREチーム
  • 依頼先: オンライン事業部PM
  • 依頼内容: GCPプロジェクト oc-monitoring-358909 に以下の権限を持つユーザー追加
    • プロジェクトIAM管理者(roles/resourcemanager.projectIamAdmin
    • サービスアカウント管理者(roles/iam.serviceAccountAdmin
    • Workload Identity Pool管理者(roles/iam.workloadIdentityPoolAdmin
    • BigQueryデータ閲覧者(roles/bigquery.dataViewer
  • 対象ユーザー: SREチームメンバー(Googleアカウント)

ステップ2: GCP Workload Identity Poolの作成

  • 実施者: SREチーム(権限取得後)
  • 目的: GitHub ActionsがGCPリソース(BigQuery)にアクセスするための認証基盤を構築
  • 対象リソース:
    • Workload Identity Pool: github-actions
    • Provider: github-actions-provider
    • Service Account: amazon-connect-monitoring@oc-monitoring-358909.iam.gserviceaccount.com
  • 設定内容:
    • Provider: GitHub OIDC認証設定
    • Attribute Mapping: リポジトリ制限(fastdoctor-jp/terraform_for_aws
    • Service Account IAM Binding: BigQuery読み取り権限付与
  • 注意事項:
    • オンライン事業部のGCPプロジェクト(oc-monitoring-358909)に作成
    • Terraformでの作成が望ましいがSRE管理外のプロジェクトであるため作成方法はオンライン事業部と相談する

3. S3バケットの作成(Terraform)

  • 目的: コスト按分結果と元データを保存するストレージを構築
  • 対象リソース:
    • S3 Bucket: amazon-connect-cost-analysis
    • バケットポリシー、バージョニング、暗号化、ライフサイクルルール
  • Terraformファイル配置先: fastdoctor-template/common/amazon-connect/s3/
  • 設定内容:
    • バージョニング有効化(全履歴保持)
    • サーバーサイド暗号化(AES256)
    • パブリックアクセスブロック
    • ライフサイクルルール:
      • categories/: 1年間STANDARD → 365日後STANDARD_IA(削除なし)★追加
      • processed/: 1年間STANDARD → 365日後STANDARD_IA(削除なし)
      • megazone/: 1年間STANDARD → 365日後STANDARD_IA(削除なし)
      • final/: 1年間STANDARD → 365日後STANDARD_IA(削除なし)
      • review/: 1年間STANDARD → 365日後STANDARD_IA(削除なし)

4. 初期カテゴリマスタの作成と配置(S3)

  • 目的: 既存のキュー・電話番号のカテゴリ分類データをS3バケットに配置
  • タイミング: 初回実行前の事前準備として実施
  • 配置場所: S3バケット(サーバーサイド暗号化による機密情報保護)
  • 対象ファイル:
    • s3://amazon-connect-cost-analysis/categories/queue_categories.json: キューのカテゴリマッピング
    • s3://amazon-connect-cost-analysis/categories/number_categories.json: 電話番号のカテゴリマッピング
  • セキュリティ設定:
    • サーバーサイド暗号化(SSE-S3)有効
    • バージョニング有効(変更履歴を完全保持)
    • パブリックアクセス禁止
キューカテゴリファイル(queue_categories.json)

必要なカラム:

カラム名データ型説明必須備考
queue_namestringキュー名Amazon Connectのキュー名と完全一致すること
categorystring事業部カテゴリB / C / G / Online / 共通 / メンタル / ファーマ / その他 等
descriptionstring説明推奨キューの用途や備考(人間のレビュー用)
created_atstring作成日推奨ISO 8601形式(例: 2025-11-01)
updated_atstring更新日推奨ISO 8601形式

ファイル形式例:

json
{
  "BasicQueue": {
    "category": "共通",
    "description": "全事業部共通の基本キュー",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  },
  "オンライン診療キュー": {
    "category": "Online",
    "description": "オンドク問診サポート専用",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  },
  "往診キュー": {
    "category": "C",
    "description": "toC往診サービス専用",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  },
  "医療機関向けキュー": {
    "category": "B",
    "description": "toB医療機関サポート",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  }
}
電話番号カテゴリファイル(number_categories.json)

必要なカラム:

カラム名データ型説明必須備考
phone_numberstring電話番号E.164形式(例: +815012345678)
categorystring事業部カテゴリB / C / G / Online / 共通 / メンタル / ファーマ / その他 等
phone_typestring電話番号タイプ推奨DID / TOLL_FREE(フリーダイヤル判定に使用)
descriptionstring説明推奨用途や備考(人間のレビュー用)
created_atstring作成日推奨ISO 8601形式
updated_atstring更新日推奨ISO 8601形式

ファイル形式例:

json
{
  "+815012345678": {
    "category": "共通",
    "phone_type": "DID",
    "description": "全事業部共通の代表番号",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  },
  "+818012099999": {
    "category": "C",
    "phone_type": "TOLL_FREE",
    "description": "toC往診専用フリーダイヤル",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  },
  "+818012088888": {
    "category": "Online",
    "phone_type": "TOLL_FREE",
    "description": "オンライン診療専用フリーダイヤル",
    "created_at": "2025-11-01",
    "updated_at": "2025-11-01"
  }
}
作成手順
  1. 既存CSVからカテゴリ情報を抽出

    • 参照ファイル: amazon connect_2025年10月版 - 202510_sample③詳細版※提出用.csv
    • 抽出カラム: queue_name, category_queue, system_tel, category_number, FDフラグ
  2. キューカテゴリファイルの作成

    • CSVのqueue_namecategory_queueの組み合わせをユニークに抽出
    • JSON形式に変換(キー: queue_name、値: オブジェクト)
    • description, created_at, updated_atを追加
  3. 電話番号カテゴリファイルの作成

    • CSVのsystem_telcategory_numberの組み合わせをユニークに抽出
    • FDフラグからphone_typeを判定(FD → TOLL_FREE、空白 → DID)
    • JSON形式に変換(キー: phone_number、値: オブジェクト)
    • description, created_at, updated_atを追加
  4. S3バケットへの配置

    bash
    # S3にアップロード(サーバーサイド暗号化を指定)
    aws s3 cp queue_categories.json \
        s3://amazon-connect-cost-analysis/categories/ \
        --server-side-encryption AES256 \
        --region ap-northeast-1
    
    aws s3 cp number_categories.json \
        s3://amazon-connect-cost-analysis/categories/ \
        --server-side-encryption AES256 \
        --region ap-northeast-1
    
    # アップロード確認
    aws s3 ls s3://amazon-connect-cost-analysis/categories/
配置先
  • s3://amazon-connect-cost-analysis/categories/queue_categories.json
  • s3://amazon-connect-cost-analysis/categories/number_categories.json

S3管理のメリット:

  • ✅ セキュリティ: サーバーサイド暗号化(SSE-S3)による機密情報保護
  • ✅ バージョン管理: S3バージョニングで全変更履歴を保持
  • ✅ アクセス制御: IAMロールによる厳密な権限管理
  • ✅ 機密性: GitHubリポジトリに機密情報(医療機関名等)を含めない
  • ✅ レビュープロセス: review/フォルダを経由した承認フロー
  • ✅ ロールバック容易: S3バージョニングで過去バージョンに復元可能

完了条件:

  • AWS IAMロール・ポリシーがTerraformで作成され、GitHub Actionsから利用可能
  • GCP Workload Identity PoolがTerraformで作成され、BigQueryアクセス可能
  • S3バケットがTerraformで作成され、データ保存可能(categories/フォルダ含む)
  • 初期カテゴリマスタがS3のcategories/フォルダに配置済み(SSE-S3暗号化有効)
  • 疎通試験成功(AWS CLI、bq queryコマンド実行成功)

注意事項:

  • カテゴリマスタはS3管理され、常に最新版を参照
  • 新規リソース追加時、レビュー待機用ファイルがS3のreview/フォルダにアップロードされる
  • 人間がレビュー・承認後、手動でS3のcategories/フォルダにコピーすることで、次回実行時に反映

Phase 1: データ収集・集計の半自動化

目標: 2段階ワークフローによる半自動化を実現

前提条件: Phase 0(インフラ準備)完了

実装タスク:

0. S3バケットライフサイクルルールの設定

  • TODO: 新規追加ディレクトリ(processed/, megazone/, final/, review/)のライフサイクルルールをTerraformで設定する

1. Phase 1処理スクリプトの実装

  • BigQueryデータ取得スクリプト
  • Amazon Connect API呼び出しスクリプト
  • Claude AIカテゴリ判定スクリプト(run-claudeアクション利用)
  • コスト按分計算スクリプト(AWS Cost Explorerベース)
  • S3アップロードスクリプト
  • Slack通知スクリプト(手順書リンク付き)

2. Phase 1 GitHub Actionsワークフローの作成

  • ワークフローファイル: .github/workflows/amazon-connect@amazon-connect-cost-calculation.yml
  • スケジュール設定: 毎月2日 09:00 JST(cron: 0 0 2 * *
  • 手動実行トリガー: workflow_dispatch
  • ステップ構成:
    1. AWS/GCP認証(OIDC: Workload Identity Federation)
    2. BigQueryデータ取得(.github/scripts/amazon-connect/phase1_step1_fetch_bigquery_data.sh
    3. Amazon Connectデータ取得(.github/scripts/amazon-connect/phase1_step2_fetch_aws_connect_data.sh
    4. Claude AIカテゴリ判定(.github/scripts/amazon-connect/phase1_step3_categorize_resources.sh
    5. コスト按分計算(.github/scripts/amazon-connect/phase1_step4_calculate_costs.py
    6. S3保存(processed/YYYY-MM/.github/scripts/amazon-connect/phase1_step5_upload_to_s3.sh
    7. レビュー待機ファイルのS3アップロード(新規リソース時のみ、.github/scripts/amazon-connect/phase1_step5_5_upload_review_files.sh
    8. CSVダウンロード(S3からbusiness_unit_summary.csvを取得、Slack添付用)
    9. Slack通知事業単位サマリーCSV添付付き.github/scripts/amazon-connect/phase1_step6_notify_slack.py

3. Phase 2処理スクリプトの実装

  • S3からMegazone CSV/PDF取得スクリプト
  • CSV解析スクリプト(ProductCode別unblendedCost合計)
  • PDF解析スクリプト(為替レート、AWS総額JPY抽出)
  • 最終按分計算スクリプト(Megazone実額ベース)
  • S3アップロードスクリプト
  • Slack通知スクリプト(最終結果 + バクラク期限リマインド)

4. Phase 2 GitHub Actionsワークフローの作成

  • ワークフローファイル: .github/workflows/amazon-connect@final-cost-allocation.yml
  • スケジュール設定: なし(手動実行のみ)
  • 手動実行トリガー: workflow_dispatch
  • ステップ構成:
    1. AWS OIDC認証
    2. S3からMegazone CSV/PDF取得
    3. 前提条件チェック(ファイル存在確認)
    4. CSV解析(ProductCode別unblendedCost合計算出)
    5. PDF解析(為替レート、AWS総額JPY抽出)
    6. Phase 1結果取得(S3から)
    7. 最終按分計算(Megazone実額ベース)
    8. S3保存(final/YYYY-MM/
    9. Slack通知(最終結果 + バクラク期限リマインド)

5. 監視・アラート設計

  • TODO: CloudWatch Logs Metric Filter/Alarm作成の検討
    • Phase 1/2失敗時のCloudWatch Alarm
    • ワークフロー実行失敗、BigQueryタイムアウト、PDF解析失敗
    • SNS経由でSlack通知
  • TODO: CloudWatch Dashboard作成(Terraform)
    • Phase 1/2実行時間推移(過去30日)
    • エラー率と種別分布
    • S3データサイズ推移

6. トラブルシューティングドキュメント(運用検証後に拡充)

  • 参照: 運用検証は SRE-1809 で実施
  • 方針: 運用検証期間中に発生したエラーや課題を記録し、トラブルシューティングガイドとして整備
  • 内容(検証後に追加予定):
    • Megazone請求書遅延時のエスカレーションフロー
    • よくあるエラーと解決方法(BigQueryタイムアウト、API Throttling、PDF解析失敗等)
    • 緊急連絡先とエスカレーション手順

完了条件:

  • Phase 1ワークフローが月次自動実行され、暫定結果がS3に保存される
  • Phase 2ワークフローが手動実行され、最終結果がS3に保存される
  • 手動オペレーション手順書が完備され、SREチームで運用可能
  • ワークフローとカテゴリ判定が3ヶ月で安定稼働
  • コスト集計の精度が手動運用と同等

運用検証TODO:

  • JIRAチケット: SRE-1809
  • 検証期間: Phase 1実装後、3ヶ月間
  • 検証項目:
    • 按分金額の正確性(手動運用との差異確認)
    • ワークフロー実行時間(Phase 1: 目標30分以内、Phase 2: 目標20分以内)
    • エラー発生率(目標: 95%以上の成功率)
    • 手動オペレーションの所要時間(目標: 15分以内)
    • Megazone請求書遅延の頻度と対応時間
  • 検証方法:
    • 毎月の実行ログをS3から取得し、スプレッドシートに記録
    • 按分結果を経理チームと照合
    • 問題発生時はインシデント記録を作成
  • 検証結果に基づく改善:
    • 実行時間が目標を超過する場合、処理の並列化を検討
    • エラー頻度が高い場合、リトライロジックの改善
    • 手動作業の負担が大きい場合、Phase 4(バクラク自動化)の優先度を上げる

Phase 2: 部署別コスト按分の自動化(将来対応)

目標: カテゴリ別コストを部署ごとに按分

実装内容:

  • カテゴリと部署の対応マッピング定義
  • 按分ルールの設定
  • 部署別コストレポートの自動生成
  • S3保存 (final/YYYY-MM/department_allocation.csv)

完了条件:

  • 部署別コスト按分が自動化
  • 按分ルール変更が設定ファイルで容易に可能

Phase 3: CSV修正の完全自動化(将来対応)

目標: レビュー待機ファイルの承認・反映・再実行を自動化

現状の課題:

  • レビュー待機用ファイル(review/YYYY-MM/queue_categories_pending.json)が作成される
  • 人間が手動でレビュー・承認し、手動でcategories/フォルダにコピーする
  • 手動で再実行ワークフローをトリガーする必要がある

実装内容:

  • GitHub Actionsワークフロー作成(approve-category-master.yml
  • Slack通知にApprove/Rejectボタンを追加(Slack Interactive Messages)
  • Approveボタンクリックで自動的に以下を実行:
    1. レビュー待機ファイルを本番categories/フォルダにコピー
    2. コスト再計算ワークフローを自動トリガー
    3. 完了通知をSlackに送信
  • メタデータ記録(承認者、承認日時、Version ID)

完了条件:

  • Slackからワンクリックで承認・反映・再実行が完了
  • 手動でS3コピーやワークフロー実行が不要
  • 承認履歴が自動記録される

Phase 4: バクラク自動化と通知強化

目標:

  • バクラクAPIによる支払依頼・コメント記載の自動化
  • 作業漏れ防止のための通知強化

実装内容:

  1. バクラクAPI統合
    • 支払依頼の自動作成
    • 費用按分結果の自動コメント記載
  2. Slackリマインド通知
    • 毎月第6営業日 08:00 JST
    • 対象: 経理チャンネル + SREチャンネル
    • 内容: バクラク支払依頼期限リマインド

完了条件:

  • バクラク手動作業がゼロになる(完全自動化)
  • 期限超過インシデントがゼロ
  • 経理チームからのフィードバックが良好

Phase 5: カテゴリマスタ承認フローの自動化(将来対応)

現状の課題:

  • 新規リソース検出時、レビュー待機ファイル(*_pending.json)を手動でS3の本番フォルダ(categories/)にコピーする必要がある
  • 手動コピー作業が発生し、承認フローが属人化するリスク

目標:

  • カテゴリマスタの承認フローをワークフロー化
  • Slack通知からワンクリックで承認可能にする

実装検討内容:

1. 承認ワークフローの自動化

以下の方式を検討:

方式A: Slack Interactive Components

  • Slack通知にボタンを追加(「承認」「却下」)
  • ボタンクリックでAWS Lambda経由でS3ファイルをコピー
  • メリット: Slack上で完結、直感的
  • デメリット: Lambda実装が必要、OIDC設定の追加

方式B: GitHub Actions workflow_dispatch + 手動承認

  • 専用の承認ワークフローを作成
  • GitHub UIから手動実行(approve_categories.yml
  • パラメータ: target_month, action: approve|reject
  • メリット: GitHub内で完結、監査ログ自動記録
  • デメリット: GitHub UIへのアクセスが必要

方式C: AWS Step Functions + Approval State

  • Step Functionsで承認待ちステートを定義
  • SNS通知 → 承認リンククリック → S3コピー実行
  • メリット: AWS標準機能、柔軟な承認フロー
  • デメリット: 複雑、コスト増

2. 承認プロセスの設計

承認ワークフロー例(方式B: GitHub Actions):

yaml
name: Approve Category Master
on:
  workflow_dispatch:
    inputs:
      target_month:
        description: '対象月 (YYYY-MM)'
        required: true
      action:
        description: '承認アクション'
        required: true
        type: choice
        options:
          - approve
          - reject

jobs:
  process-approval:
    runs-on: ubuntu-latest
    steps:
      - name: Approve categories
        if: inputs.action == 'approve'
        run: |
          aws s3 cp \
            s3://${S3_BUCKET}/review/${{ inputs.target_month }}/queue_categories_pending.json \
            s3://${S3_BUCKET}/categories/queue_categories.json

          aws s3 cp \
            s3://${S3_BUCKET}/review/${{ inputs.target_month }}/number_categories_pending.json \
            s3://${S3_BUCKET}/categories/number_categories.json

      - name: Notify approval
        run: |
          # Slack通知: 承認完了または却下

3. 運用フローの改善案

過去の手動運用(2026年1月まで):

1. ワークフロー実行(新規リソース検出)
2. Slack通知受信(レビュー待機中)
3. 手動でS3からダウンロード
4. 手動でレビュー・修正
5. 手動でS3にアップロード

現在の自動化された運用(2026年1月以降、Step 5.6実装済み):

1. ワークフロー実行(新規リソース検出)
2. Step 5.6で自動的にカテゴリマスタを更新
3. Slack通知受信(自動更新完了)
4. 分類に誤りがある場合のみ、S3でカテゴリファイルを修正しワークフロー再実行

4. 実装タスク(TODO)

Phase 1: 運用検証期間中(現在)

  • [ ] 運用検証の実施(3ヶ月程度)
    • 新規リソース検出時のカテゴリ判定精度の確認
    • 手動レビュー・修正作業の記録
    • 判定ミスのパターン分析とプロンプト改善

Phase 2: 運用検証完了後の自動化(2026年1月実装済み)

  • [x] カテゴリマスタ自動更新機能の実装(Step 5.6として実装済み)
    • 新規リソース検出時、自動的にcategories/を更新
    • S3バージョニングを活用したロールバック機能(復元手順書(今後作成予定))
    • 更新履歴の記録(メタデータにauto-updatedupdated-attarget-monthを記録)
  • [x] Slack通知の改善(実装済み)
    • 「手動コピー」指示を削除し、「自動更新完了」通知に変更
    • 分類誤りがある場合の対処方法を記載
    • CSV添付機能の追加(Step 5.7 + Step 6)

Phase 3: 承認フロー追加(今後の拡張)

  • [ ] 承認方式の選定(方式A/B/Cから選択)
  • [ ] 承認ワークフローの実装
  • [ ] Slack通知への承認ボタン追加(方式Aの場合)
  • [ ] AWS Lambda関数の実装(方式Aの場合)
  • [ ] 承認履歴の記録方法検討(CloudWatch Logs / DynamoDB)
  • [ ] 誤承認時のロールバック手順の整備
  • [ ] 運用テスト(3ヶ月間)
  • [ ] 運用手順書の更新

補足:

  • 2026年1月以降、カテゴリマスタは自動更新される(Step 5.6実装済み)
  • 分類に誤りがある場合は、S3でカテゴリファイルを修正しワークフロー再実行
  • S3バージョニングにより、誤った更新をロールバック可能(復元手順書(今後作成予定))

5. Phase 2完了条件(2026年1月達成済み)

  • [x] カテゴリマスタの自動更新機能(Step 5.6)
  • [x] 手動でのS3コピー作業が不要
  • [x] 更新履歴が記録され、監査可能(S3メタデータ)
  • [x] 誤更新時のロールバック手順が確立(復元手順書(今後作成予定))

6. Phase 3完了条件(未達成)

  • [ ] カテゴリマスタの承認がSlackまたはGitHub UIから実行可能
  • [ ] 承認履歴が記録され、監査可能
  • [ ] 誤承認時のロールバック手順が確立

5. 監査要件

懸念:

  • 誰が何を変更したかの追跡

対策:

  • GitHub Actionsの実行ログ保存
  • S3バージョニング有効化
  • CloudTrailによるログの取得


トラブルシューティング

過去月分の再計算(回線数の自動取得)

: 環境変数による回線数オーバーライド機能(did_count_override, toll_free_count_override)は、S3自動取得の検証後に削除予定です。検証完了後は、本セクションの内容も更新されます。

背景

過去月分のコスト計算を再実行する際、従来は現時点の回線数が自動取得されるため、過去月の実際の回線数と異なる問題がありました。

具体例(従来の問題):

  • 2025年8月時点の回線数: 946回線(DID: 777、TOLL_FREE: 169)
  • 2025年12月時点の回線数: 1,026回線(DID: 857、TOLL_FREE: 169)
  • 12月に8月分を再計算すると、誤って1,026回線で計算される → $240の差額

解決方法

現在の実装(2025年12月): 過去月分を再実行する場合、回線数は自動的にS3から取得されます。手動指定は通常不要です。

取得の優先順位:

  1. 環境変数(DID_COUNT_OVERRIDE, TOLL_FREE_COUNT_OVERRIDE)※検証後に削除予定
  2. S3の過去月データ(自動取得) ← 通常はこれを使用
  3. AWS API(現時点の回線数)← S3にデータがない場合のみ
Phase 1ワークフロー(過去月の再計算)

GitHub Actionsから手動実行:

  1. ワークフロー: Amazon Connect Cost Calculation
  2. パラメータ:
    • target_month: 対象月(例: 2025-08
    • did_count_override: DID回線数(通常は空欄)※過去分実行用
    • toll_free_count_override: TOLL_FREE回線数(通常は空欄)※過去分実行用

推奨実行方法(自動取得):

target_month: 2025-08
did_count_override: (空欄)
toll_free_count_override: (空欄)

動作:

  1. S3から過去月のデータを自動取得: processed/2025-08/allocation_result_*.json
  2. 過去月の回線数(DID: 777、TOLL_FREE: 169)を使用
  3. 回線保持料金: $4,764.60

手動指定が必要なケース(S3データが間違っている場合のみ、検証用):

: この手動オーバーライド機能は、S3自動取得の検証後に削除予定です。

target_month: 2025-08
did_count_override: 777
toll_free_count_override: 169
Phase 2ワークフロー(Phase 1の結果を使用)

Phase 1で正しい回線数を指定して実行した後、Phase 2を実行します。

GitHub Actionsから手動実行:

  1. ワークフロー: Amazon Connect Final Cost Allocation (Phase 2)
  2. パラメータ:
    • target_month: 対象月(例: 2025-08
    • exchange_rate: 為替レート(例: 154.38
    • aws_total_jpy: AWS総額(税込JPY、例: 12992093
    • total_call_cost_usd: 通話料総額(USD、例: 14422.50

実行例:

target_month: 2025-08
exchange_rate: 154.38
aws_total_jpy: 12992093
total_call_cost_usd: 14422.50

: Phase 2は、Phase 1で計算された回線保持料金(line_holding_fee_usd)をS3から自動的に読み込みます。

注意事項

✅ 回線数の自動取得(優先順位):

  1. 環境変数(手動オーバーライド) - S3データが間違っている場合のみ ※検証後に削除予定
  2. S3の過去月データ(自動) - processed/{target_month}/allocation_result_*.json
  3. AWS API(現時点の回線数) - S3にデータがない場合のフォールバック

⚠️ 回線数オーバーライドパラメータの使用目的(検証後に削除予定):

  • S3自動取得機能の検証用
  • S3データが間違っている場合の緊急修正用(将来的には不要)
  • 検証環境でのテスト実行

⚠️ 本番運用時の注意点:

  • 過去月分の再実行では、パラメータを空欄のまま実行(S3から自動取得)
  • 通常の月次集計では、パラメータを空欄のまま実行(現時点の回線数を取得)
  • Phase 1結果JSON(allocation_result_*.json)に回線数情報が保存される

回線数の確認方法

対象月の正しい回線数を確認する方法:

  1. Phase 1結果JSONから確認(最も確実、自動取得で使用):

    • S3バケット: amazon-connect-cost-analysis-production
    • パス: processed/YYYY-MM/allocation_result_YYYYMMDD_HHMM.json
    • フィールド:
      json
      {
        "did_count": 777,
        "toll_free_count": 169,
        "line_holding_fee_usd": 4764.6,
        ...
      }
    • : 過去月の再実行時、このファイルから回線数が自動取得されます
  2. 手動集計ファイルから確認:

    • Google Spreadsheetの保持料金セクション
    • DID回線数とTOLL_FREE回線数が記載されている
  3. Phase 1の過去実行ログから確認(GitHubActions):

    📞 現時点の回線数(AWS APIから取得):
       DID回線数: 777
       TOLL_FREE回線数: 169
       回線保持料金: $4764.6

    注意: GitHubActionsのログは90日で削除される可能性があります

  4. S3保存CSV(allocation_result.csv)のヘッダーから確認:

    • パス: processed/YYYY-MM/allocation_result.csv
    • ヘッダー行1: 保有回線数(946) のような表記がある

改版履歷

バージョン日付変更内容作成者担当チーム
1.02025-11-12初版作成大賀SRE
1.12025-11-26Megazone Hyper Billing半自動化対応(2段階ワークフロー、Phase 2追加)大賀SRE
1.22025-12-01Phase 5追加、Step 5.5を手動運用に変更大賀SRE
1.32025-12-04設計書の整合性更新(事業単位別集計追加、システム利用料金修正[$0.025→$0.018]、カテゴリ一覧明確化、丸め誤差仕様追加、既知の課題セクション追加)大賀SRE
1.42025-12-04按分割合計算の修正(step4_calculate_costs.py)、既知の課題セクションを簡潔化(事象・原因・対応・結果で記載)、按分割合が手動計算と完全一致大賀SRE
1.52025-12-17トラブルシューティングセクション追加(過去月分再計算時の回線数オーバーライド機能)、Phase 2の不要なオーバーライドパラメータを削除大賀SRE
1.62025-12-19Phase 2に逆算ロジックを追加(最大金額の事業単位を逆算して合計を厳密に一致)、BigQueryクエリにIS NOT NULL条件を追加(手動集計との完全一致のため)大賀SRE