0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発】GCP × Slack × AI × Obsidianで作る「第二の脳」を作った話

Last updated at Posted at 2025-12-25

はじめに

こんにちは、ゆうやです。初めて投稿します。

僕は個人開発でAIシステムを作っています。

元々はLINEで応対させていたのですが、Obsidianに触れてから、Slackをメインインターフェースとして、毎朝の挨拶、日記の自動生成、知識検索、情報収集など、

生活のあらゆる場面をAIがサポートしてくれるシステムです。

この記事では、システムの全貌を技術的に詳しく解説します。

  • なぜ作ろうと思ったのか
  • どんな技術を使っているのか
  • どんな機能があるのか
  • インフラ構成はどうなっているのか

個人開発でここまで作れるんだ、という参考になれば嬉しいです。


スクリーンショット 2025-12-25 午後4.57.59.png

🎯 作ろうと思ったきっかけ

Obsidianで日記を書き始めた

2025年7月から、Obsidianで日記を書き始めました。

1日1ファイル、Markdownで記録していく。

最初は手動で書いていましたが、すぐに「めんどくさい」と思うようになりました。

  • 毎日ファイルを作成するのが面倒
  • フォーマットを統一するのが面倒
  • 後から見返すのが面倒

これ、自動化できないかな?

そう思ったのが、このシステムを作り始めたきっかけです。

Slackで完結させたかった

Slack使うのって、なんかかっこいいじゃないですか。(!)

「日記を書くためにObsidianを開く」という行為が、地味にハードルが高い。

でも、Slackで話しかけるだけで日記が自動生成されるなら、続けられると思いました。

AIを活用したかった

2023年にChatGPTに出会い、AIの可能性に衝撃を受けました。

「AIを使えば、もっと賢く、もっと効率的に日記が書ける」

  • 1日の会話を要約してくれる
  • 感情の変化を分析してくれる
  • 過去の日記から関連する情報を引っ張ってきてくれる

そんなシステムを作りたいと思いました。


🏗️ システムアーキテクチャ

全体構成図

┌─────────────────────────────────────────────────────────────────┐
│                         ユーザー (僕)                             │
│                              │                                  │
│                            Slack                                │
│                              │                                  │
│     ┌────────────────────────┴────────────────────────┐         │
│     │                                                  │        │
│     ▼                                                  ▼        │
│ ┌────────────────────┐               ┌───────────────────────┐  │
│ │ slack-event-handler│               │   Cloud Scheduler     │  │
│ │    (Python)        │               │   (定期実行)           │  │
│ │ ・RAG検索           │               └──────────┬────────────┘  │
│ │ ・AI対話            │                          │               │
│ │ ・レポート生成       │                          ▼               │
│ └────────┬───────────┘              ┌────────────────────────┐  │
│          │                          │   Go マイクロサービス群   │  │
│          │                          │ ・daily_checkin_go     │  │
│          │                          │ ・daily_reflection_go  │  │
│          │                          │ ・weekly_report_go     │  │
│          │                          │ ・process_rss_go       │  │
│          │                          │ ・cost_report_go       │  │
│          │                          │ ・on_this_day_go       │  │
│          │                          └──────────┬─────────────┘  │
│          │                                      │               │
│          ▼                                      ▼               │
│     ┌────────────────────────────────────────────────────────┐  │
│     │                   Gemini API                           │  │
│     │          (gemini-2.5-flash-lite)                       │  │
│     └────────────────────────────────────────────────────────┘  │
│                              │                                  │
│     ┌────────────────────────┴────────────────────────┐         │
│     ▼                                                  ▼        │
│ ┌────────────────────┐              ┌────────────────────────┐  │
│ │  Cloud Storage     │              │     Firestore          │  │
│ │  (GCS)             │              │  ・会話履歴              │  │
│ │  ・Markdown日記     │              │  ・RSS既読管理           │  │
│ │  ・画像             │              │  ・キャッシュ            │  │
│ │  ・支出CSV          │              └────────────────────────┘  │
│ └────────────────────┘                                          │
└─────────────────────────────────────────────────────────────────┘

データ同期フロー(Obsidian → GCS)

Obsidianで書いた日記が、GCSとFile Search Storeに同期されるまでの流れです。

┌─────────────────────────────────────────────────────────────────────────────┐
│                                                                             │
│  📱 ローカル(Mac / iPhone)                                                  │
│                                                                             │
│  ┌──────────────────────┐                                                   │
│  │      Obsidian        │                                                   │
│  │  (iCloud同期)         │                                                   │
│  │                      │                                                   │
│  │  ・日記を書く          │                                                   │
│  │  ・メモを作成          │                                                   │
│  │  ・既存ノートを編集     │                                                   │
│  └──────────┬───────────┘                                                   │
│             │                                                               │
│             │ iCloud Drive 自動同期                                          │
│             ▼                                                               │
│  ┌──────────────────────┐                                                   │
│  │   iCloud Drive       │                                                   │
│  │  (Mac ローカル)       │                                                   │
│  └──────────┬───────────┘                                                   │
│             │                                                               │
│             │ Git commit & push                                             │
│             ▼                                                               │
└─────────────────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                                                                             │
│  🐙 GitHub                                                                  │
│                                                                             │
│  ┌──────────────────────┐         ┌──────────────────────────────────────┐  │
│  │  hogehoge/Obsidian   │────────▶│      GitHub Actions                  │  │
│  │   (Private Repo)     │  push   │  ・sync-knowledge-base.yml           │  │
│  │                      │ trigger │  ・scheduled-jobs.yml                │  │
│  └──────────────────────┘         └──────────────────┬───────────────────┘  │
│                                                       │                     │
└───────────────────────────────────────────────────────┼─────────────────────┘
                                                        │
                                                        │ gsutil rsync
                                                        ▼
┌────────────────────────────────────────────────────────────────────────────┐
│                                                                            │
│  ☁️ Google Cloud Platform                                                  │
│                                                                            │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │                     Cloud Storage (GCS)                              │  │
│  │                  hogehoge-knowledge-base                             │  │
│  │                                                                      │  │
│  │  knowledge-base/                                                     │  │
│  │  ├── 02_note/02_doing/2025年12月/20251225_今日の出来事.md               │  │
│  │  ├── 04_bookmarks_x/...                                              │  │
│  │  ├── 07_rss/...                                                      │  │
│  │  └── ...                                                             │  │
│  └────────────────────────────────────┬─────────────────────────────────┘  │
│                                       │                                    │
│                                       │ Eventarc トリガー                   │
│                                       │ (ファイルアップロード時)               │
│                                       ▼                                    │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │                 import_to_file_search (Cloud Run)                    │  │
│  │                                                                      │  │
│  │  ・Markdownファイル検知                                                 │  │
│  │  ・ディレクトリに基づいてStore振り分け                                     │  │
│  │  ・REST APIでFile Search Storeにアップロード                            │  │
│  │  ・Firestoreにメタデータ保存                                            │  │
│  └────────────────────────────────────┬─────────────────────────────────┘  │
│                                       │                                    │
│                                       ▼                                    │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │              Gemini File Search Store                                │  │
│  │                                                                      │  │
│  │  ┌─────────────────────┐    ┌─────────────────────┐                  │  │
│  │  │   obsidian-vault    │    │   obsidian-vault2   │                  │  │
│  │  │                     │    │                     │                  │  │
│  │  │ ・日記               │    │ ・Xブックマーク       │                   │  │
│  │  │ ・メモ               │    │ ・YouTube           │                   │  │
│  │  │ ・思考               │    │ ・RSS               │                   │  │
│  │  │ ・X投稿              │    │                     │                   │ │
│  │  └─────────────────────┘    └─────────────────────┘                  │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                       │                                    │
│                                       │ RAG検索で参照                        │
│                                       ▼                                    │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │                 slack-event-handler (Cloud Run)                      │  │
│  │                                                                      │  │
│  │  Slackでメンションされたら、File Search Storeを検索して回答                │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

技術スタックまとめ

カテゴリ 技術 用途
メイン言語 Python 3.11 AI処理、複雑なロジック
サブ言語 Go 1.21 定期ジョブ、高速処理
AIモデル Gemini 2.5 Flash Lite テキスト生成、要約、画像解析
インフラ Google Cloud (GCP) フルサーバーレス構成
IaC Terraform インフラのコード管理
データベース Firestore 会話履歴、メタデータ
ストレージ Cloud Storage (GCS) Markdown、画像、CSV
メインUI Slack ユーザーインターフェース
知識ベース Obsidian Markdownベースのノート

🔧 コアサービス詳解

1. slack-event-handler (Python)

システムの心臓部です。

Slackからのイベント(メンション、メッセージ)を受け取り、適切な処理に振り分けます。

# 主なエンドポイント
POST /                      # Slackイベント受信
POST /process-diverse-news  # 多様なニュース配信
POST /spend/weekly          # 週次支出レポート
POST /spend/monthly         # 月次支出レポート
POST /insight/daily         # 日次洞察生成

主な機能:

  • RAG検索: 過去の日記やWikiを検索し、文脈を踏まえた回答
  • AI対話: Gemini APIを使った自然な会話
  • レポート生成: 日次・週次・月次・四半期レポート
  • 支出管理: CSVから予算残高を計算

ファイル構成:

slack_event_handler/
├── main.py               # エントリーポイント (382KB!)
├── rag_handlers.py       # RAG検索ロジック
├── report_generators.py  # レポート生成
├── conversation_handlers.py # 対話処理
├── budget_manager.py     # 予算管理
├── spend_analyzer.py     # 支出分析
├── insight_miner.py      # 洞察生成
├── slack_helpers.py      # Slack API操作
├── mcp_tools/            # MCPツール群
│   └── google_calendar/  # Googleカレンダー連携
└── utils/                # ユーティリティ
    ├── error_handler.py
    ├── logger.py
    └── cache.py

リソース設定:

# Terraform (cloud_run.tf)
resources {
  limits = {
    cpu    = "1000m"
    memory = "1Gi"
  }
  cpu_idle = true  # コスト最適化
}
timeout = "900s"   # 15分タイムアウト

2. daily_checkin_go (Go)

毎朝7:30に実行される、朝の挨拶ジョブです。

// 主な処理
1. 昨日の日記をGCSから取得
2. 天気情報を取得OpenWeatherMap API
3. Googleカレンダーから今日の予定を取得
4. 健康データ睡眠歩数を取得
5. Gemini APIで朝の挨拶メッセージを生成
6. Slackに送信

Slack通知例:

おはようございます、ゆうやさん!☀️

【今日の天気】
松江市:晴れ、最高気温 15℃

【今日の予定】
・10:00 定例ミーティング
・14:00 歯医者

【昨日の振り返り】
・睡眠時間: 7.5時間
・歩数: 6,234歩
・主な出来事: VBA開発が完了しました

今日も良い一日を!💪

3. daily_reflection_go (Go)

毎日22:00に実行される、日記自動生成ジョブです。

// 主な処理
1. Slack APIでその日の会話履歴を取得
2. 健康データを取得睡眠歩数心拍数
3. Gemini APIで1日の要約を生成
4. Markdownフォーマットで日記を作成
5. GCSに保存
6. GitHubにバックアップ同期

生成される日記の例:

---
created: 2025-12-25 00:00:00
updated: 2025-12-25 00:00:00
tags:
  - 仕事完了
  - 達成感
  - VBA
---

# 2025年12月25日 - VBA開発完了の日

## 📋 今日のサマリー

### 主な出来事
1. 開発が完了
2. 年末までの仕事が実質終了

### ヘルスケア記録
- 睡眠時間: 8.0時間
- 歩数: 3,467歩
- 平均心拍: 85 bpm

## 🌟 今日の気づき
...

4. weekly_report_go (Go)

毎週日曜11:00に実行される、週次レポート生成ジョブです。

// 主な処理
1. 過去7日分の日記をGCSから取得
2. RSS通知のサマリーを取得
3. 健康データの週間推移を集計
4. Gemini APIで週次レポートを生成
5. Slackに送信 + GCSに保存

週次・月次・四半期の3種類:

  • weekly: 毎週日曜11:00
  • monthly: 月末11:00
  • quarterly: 3/6/9/12月末11:00

5. process_rss_go (Go)

1日3回(8時、12時、17時)実行される、情報収集ジョブです。
もちろんQiita、Zennもチェックしています。
下記記事を参考にSlack版でも作りました。

// 主な処理
1. 登録済みRSSフィードをチェック
2. キーワードに関連する記事をチェック
3. Gemini APIが重要度判定
4. 一番上の重要度のものをSlackに通知
5. Firestoreに既読フラグを保存

購読しているフィード:

- https://zenn.dev/feed
- https://qiita.com/popular-items/feed.atom
- https://www.lifehacker.jp/feed/index.xml
- 技術ブログRSSアグリゲーター

キーワードフィルタ:

AWS, 生成AI, Python, Javascript, GAS, 
Docker, メンタル, Git, Cursor, Claude, Obsidian

6. cost_report_go (Go)

毎朝8:00に実行される、GCPコスト通知ジョブです。

// 主な処理
1. BigQueryの課金エクスポートテーブルにクエリ
2. 昨日のGCP利用料を集計
3. Slackに通知

通知例:

📊 GCPコストレポート(昨日分)

Cloud Run: ¥32
Cloud Storage: ¥8
Gemini API: ¥156
合計: ¥196

⏰ 定期ジョブスケジュール

Cloud Schedulerで管理している定期ジョブ一覧です。

時間 (JST) ジョブ名 内容
07:30 daily_checkin_go 朝の挨拶、天気、予定確認
08:00 process_rss_go 朝の技術ニュースチェック
08:00 on_this_day_go 1年前の今日の日記を通知
08:00 cost_report_go 前日のGCPコスト通知
08:00 diverse-news 多様なニュース配信
12:00 process_rss_go 昼の技術ニュースチェック
14:00 diverse-news 多様なニュース配信
17:00 process_rss_go 夕方の技術ニュースチェック
21:00 insight-daily 日次簡易洞察生成してSlackに送信
22:00 daily_reflection_go 1日のまとめ、日記生成
毎週金曜 22:30 generate_weekly_journal_audio (後述します)
日曜 01:00 spend-weekly 週次支出レポート
日曜 11:00 weekly_report_go 週次レビュー生成
月末 11:00 monthly_report_go 月次レビュー生成
四半期末 11:00 quarterly_report_go 四半期レビュー生成

📂 Terraform によるインフラ管理

IaC ディレクトリ構成

IaC/
├── cloud_run.tf      # Cloud Runサービス定義 (591行)
├── scheduler.tf      # Cloud Schedulerジョブ定義 (307行)
├── storage.tf        # GCSバケット定義
├── iam.tf            # IAM権限設定
├── apis.tf           # API有効化
├── variables.tf      # 変数定義
└── versions.tf       # プロバイダ設定

Cloud Run サービス例

# slack-event-handler (メインサービス)
resource "google_cloud_run_v2_service" "slack_event_handler_tokyo" {
  name     = "slack-event-handler"
  location = "asia-northeast1"
  ingress  = "INGRESS_TRAFFIC_ALL"

  template {
    containers {
      image = "gcr.io/${var.project_id}/slack-event-handler:latest"

      resources {
        limits = {
          cpu    = "1000m"
          memory = "1Gi"
        }
        cpu_idle = true  # 使用していない時はCPUを解放
      }

      # 環境変数 (抜粋)
      env {
        name  = "GCP_PROJECT"
        value = var.project_id
      }
      env {
        name  = "CHANNEL_2_AI_ASSISTANT_ID"
        value = "ABC"
      }
    }
    timeout = "900s"  # 15分
  }
}

Cloud Scheduler ジョブ例

# 定期ジョブ定義 (mapで管理)
locals {
  scheduler_jobs = {
    "daily-reflection-go" = {
      schedule       = "0 22 * * *"
      description    = "日記自動生成(毎日22時)"
      target_service = "daily-reflection"
      path           = "/"
    },
    "shinmizuki-weekly-report" = {
      schedule       = "0 11 * * 0"
      description    = "週次レビュー生成(毎週日曜11時)"
      target_service = "weekly-report"
      path           = "/"
    }
  }
}

resource "google_cloud_scheduler_job" "jobs_tokyo" {
  for_each    = local.scheduler_jobs
  name        = each.key
  schedule    = each.value.schedule
  time_zone   = "Asia/Tokyo"
  
  http_target {
    http_method = "POST"
    uri         = "${google_cloud_run_v2_service...uri}${each.value.path}"
  }
}

💾 データストレージ

Cloud Storage (GCS)

バケット: hogehoge-knowledge-base

hogehoge-knowledge-base/
├── 02_note/
│   ├── 02_doing/
│   │   └── 2025年12月/
│   │       ├── 20251225_今日の出来事.md
│   │       └── ...
│   └── 03_published/
├── 09_reviews/
│   ├── weekly/
│   ├── monthly/
│   └── quarterly/
├── 10_healthcare/
│   ├── sleep/
│   ├── steps/
│   └── heart_rate/
├── 11_spend/
│   ├── 2025_12_クレジットカード.csv
│   └── ...
└── integrations/
    └── google_calendar/
        └── token.json

Firestore

コレクション構成:

firestore/
├── conversations/          # 会話履歴
│   └── {user_id}/
│       └── {timestamp}/
├── rss_read_status/        # RSS既読管理
│   └── {feed_url_hash}/
└── cache/                  # 一時キャッシュ

🧠 AI処理の詳細

Gemini API の使い方

使用モデル: gemini-2.5-flash-lite

選定理由:

  • 高速: レスポンスが速い
  • 安価: コスト効率が良い
  • 十分な性能: 日記要約や会話には十分
# Pythonでの使用例 (slack_event_handler)
from google import genai

client = genai.Client()
response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents=[
        {"role": "user", "parts": [{"text": prompt}]}
    ],
    config={
        "temperature": 0.7,
        "max_output_tokens": 2048,
    }
)
// Goでの使用例 (daily_checkin_go)
import "github.com/google/generative-ai-go/genai"

client, _ := genai.NewClient(ctx, option.WithAPIKey(apiKey))
model := client.GenerativeModel("gemini-2.5-flash-lite")
resp, _ := model.GenerateContent(ctx, genai.Text(prompt))

RAG(検索拡張生成)

過去の日記や知識ベースを検索して、文脈を踏まえた回答を生成します。

# RAG検索の流れ
1. ユーザーの質問を受け取る
2. 質問をベクトル化
3. GCS内のMarkdownファイルを検索
4. 関連する文書を上位5件取得
5. 検索結果 + 質問をGeminiに送信
6. 文脈を踏まえた回答を生成

📚 Gemini File Search Store 自動インポート

概要

import_to_file_search は、Cloud StorageにアップロードされたMarkdownファイルを、自動的にFile Search StoreにインポートするCloud Functionです。

これにより、Obsidianに書いた日記やメモが、リアルタイムでRAG検索の対象になります。

アーキテクチャ

┌─────────────────────────────────────────────────────────────┐
│  Cloud Storage (yuyayoshiok-knowledge-base)                 │
│       │                                                     │
│       │ ファイルアップロードイベント                             │
│       ▼                                                     │
│  ┌────────────────────────────────────────┐                 │
│  │   import_to_file_search (Python)       │                 │
│  │   ・ファイル内容を取得                    │                 │
│  │   ・File Search Storeを選択             │                 │
│  │   ・REST APIでアップロード                │                │
│  │   ・Firestoreにメタデータ保存             │                │
│  └────────────────────────────────────────┘                │
│       │                                                    │
│       ▼                                                    │
│  ┌────────────────────────────────────────┐                │
│  │   Gemini File Search Store             │                │
│  │   ・obsidian-vault (日記、メモ)          │                │
│  │   ・obsidian-vault2 (ブックマーク、RSS)   │                │
│  └────────────────────────────────────────┘                │
└────────────────────────────────────────────────────────────┘

File Search Storeの振り分けロジック

ファイルパスに基づいて、適切なStoreに振り分けます。

def _select_store_by_path(file_path: str) -> str:
    """
    ファイルパスに基づいて適切なFile Search Storeを選択
    """
    # obsidian-vault2に追加するディレクトリ
    vault2_dirs = ["04_bookmarks_x", "06_youtube", "07_rss"]
    
    path_parts = file_path.split("/")
    if len(path_parts) >= 2:
        dir_name = path_parts[1]
        if dir_name in vault2_dirs:
            return "obsidian-vault2"
    
    # デフォルトはobsidian-vault(日記、メモ、思考など)
    return "obsidian-vault"
Store名 対象
obsidian-vault 日記、メモ、思考、X投稿
obsidian-vault2 Xブックマーク、YouTube、RSS

アップロード処理

Gemini File Search Store REST APIを使用してアップロードします。

def upload_to_file_search_store(
    api_key: str,
    store_name_full: str,
    display_name: str,
    content: str,
    mime_type: str = "text/markdown",
) -> dict:
    """ファイル内容をFile Search Storeにアップロード"""
    url = f"{UPLOAD_BASE_URL}/{store_name_full}:uploadToFileSearchStore"
    params = {"key": api_key, "uploadType": "multipart"}

    metadata = {
        "displayName": display_name,
        "mimeType": mime_type,
    }

    files = {
        "metadata": ("metadata", json.dumps(metadata), "application/json"),
        "file": (display_name, content.encode("utf-8"), mime_type),
    }

    resp = requests.post(url, params=params, files=files, timeout=300)
    resp.raise_for_status()
    
    # 非同期オペレーションの完了を待つ
    operation_name = resp.json().get("name")
    # ... ポーリング処理 ...

無料版 + 有料版の並行インポート

API制限を回避するため、無料版と有料版の両方のAPIキーでインポートします。

api_keys_to_use = [
    {"secret_id": "gemini-api-key", "type": "free"},
    {"secret_id": "gemini-api-key-paid", "type": "paid"}
]

for key_info in api_keys_to_use:
    api_key = access_secret(key_info["secret_id"], project_id)
    store_name_full = get_or_create_file_search_store(api_key, store_name)
    upload_to_file_search_store(api_key, store_name_full, ...)

Firestoreへのメタデータ保存

インポート結果はFirestoreに保存し、重複チェックやエラー追跡に活用します。

doc_data = {
    "file_path": file_path,
    "bucket_name": bucket_name,
    "file_size": blob.size,
    "content_hash": hashlib.sha256(content.encode()).hexdigest(),
    "uploaded_at": datetime.utcnow(),
    "store_name": store_name,
    "status": "completed",  # or "failed"
}

db.collection("rag_metadata").document(doc_id).set(doc_data, merge=True)

効果

  • リアルタイム同期: ファイルをアップロードするだけでRAG検索対象に
  • 自動振り分け: ディレクトリ構造に基づいて適切なStoreへ
  • 冗長性: 無料版 + 有料版の両方にインポート
  • 追跡可能: Firestoreでメタデータ管理

🎤Gemini TTS による音声生成

概要

週刊ジャーナルの音声版を、Gemini TTSで生成しています。

毎週金曜22:30に、1週間のSlack会話を分析して「編集長コラム」を生成し、音声ファイルをSlackに投稿します。

ユースケース

  1. 週刊ジャーナル音声版: 1週間の振り返りを音声で確認
  2. AI編集長のコラム朗読: 雑誌の巻頭コラム風のテキストを読み上げ
  3. MP3でSlack投稿: 音声ファイルをダウンロードして通勤中に聴ける

処理フロー

1. チャンネルから1週間分のメッセージを取得
2. Gemini APIで分析(頻出キーワード、感情傾向など)
3. AI編集長として巻頭コラムを生成
4. Gemini TTSで音声に変換(PCM → MP3)
5. SlackにMP3ファイルをアップロード

実装コード

def generate_weekly_journal_audio(journal_text: str, project_id: str) -> Optional[bytes]:
    """週刊ジャーナルの音声版を生成(Gemini TTS使用)"""
    
    # テキストからマークダウン記法を除去
    audio_text = journal_text
    audio_text = re.sub(r'^#+\s+.*$', '', audio_text, flags=re.MULTILINE)
    audio_text = re.sub(r'^---.*$', '', audio_text, flags=re.MULTILINE)
    audio_text = audio_text.strip()
    
    # Gemini TTS APIを使用
    from google.genai import types
    
    response = client.models.generate_content(
        model="gemini-2.5-flash-preview-tts",
        contents=audio_text,
        config=types.GenerateContentConfig(
            response_modalities=["AUDIO"],
            speech_config=types.SpeechConfig(
                voice_config=types.VoiceConfig(
                    prebuilt_voice_config=types.PrebuiltVoiceConfig(
                        voice_name='Kore',  # 日本語対応の音声
                    )
                )
            )
        )
    )
    
    # PCMデータを取得
    pcm_data = response.candidates[0].content.parts[0].inline_data.data
    
    # PCMをMP3に変換(pydub使用)
    from pydub import AudioSegment
    import io
    
    audio_segment = AudioSegment(
        pcm_data,
        frame_rate=24000,    # Gemini TTSは24kHz
        channels=1,          # モノラル
        sample_width=2       # 16bit
    )
    
    mp3_buffer = io.BytesIO()
    audio_segment.export(mp3_buffer, format="mp3")
    return mp3_buffer.getvalue()

Slackへのアップロード

生成したMP3ファイルは、Slack Files APIでアップロードします。

# Slackに音声ファイルをアップロード
if audio_content:
    files = {
        "file": ("weekly_journal.mp3", audio_content, "audio/mpeg")
    }
    response = requests.post(
        "https://slack.com/api/files.upload",
        headers={"Authorization": f"Bearer {slack_token}"},
        data={"channels": channel_id},
        files=files
    )
    print(f"✅ Audio version posted to #2_ai-assistant")

週刊ジャーナルの特徴

  • 2段階生成: まず分析、次にコラム執筆
  • ヘルスケア・天気連携: 睡眠、歩数、天気情報も参照

🔐 セキュリティ

Secret Manager

機密情報はすべてSecret Managerで管理しています。

secrets/
├── slack-bot-token        # Slack Bot Token
├── slack-app-token        # Slack App Token
├── gemini-api-key         # Gemini API Key
├── github-pat-token       # GitHub PAT
└── openweathermap-api-key # 天気API

IAM設計

# サービスアカウント
resource "google_service_account" "cloud_run_sa" {
  account_id   = "cloud-function-sa"
  display_name = "Cloud Run Service Account"
}

# 必要な権限のみ付与
# - GCS読み書き
# - Firestore読み書き
# - Secret Manager読み取り
# - BigQuery読み取り

💰 コスト

月間コスト概算

・Cloud Scheduler →20個くらいあるので1日数十円くらいかかっている
・Gemini →無料枠でレート制限になったら有料APIにフォールバックするようにしている
〜以下省略〜
ざっと月¥500〜1000円くらいで運用できています!

コスト最適化のポイント

  1. cpu_idle = true: 使用していない時はCPUを解放
  2. 冷却時間を短く: 定期ジョブは最小限のメモリで実行
  3. Gemini flash-lite: 安価なモデルを使用
  4. Firestore: 読み書きを最小限に

まとめ

システムの価値

  • 毎日の日記が自動生成される: 継続のハードルが劇的に下がった
  • 過去の自分を検索できる: RAGで「あの時どうしたっけ?」に即答
  • 情報収集が自動化: RSSで技術トレンドをキャッチアップ
  • 健康・支出を可視化: データドリブンな生活改善
  • ファイルを自動バックアップ: Github、Cloud Storageにもバックアップしています

技術的なポイント

  • Python + Go の使い分け: 適材適所で言語を選択
  • フルサーバーレス: 維持コストを最小化
  • Terraform管理: インフラをコードで管理
  • Gemini API: 安価で高速なAI処理

個人開発でここまでできる

クラウドサービスとAIの進化で、個人でも本格的なシステムが作れる時代になりました。

この記事が、同じように「自分だけのAIアシスタント」を作りたい人の参考になれば嬉しいです。

いいね、コメントお待ちしています!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?