自分専用のローカルAI秘書。自分の情報(プロフィール・予定・メモ・会話・書類の写真など)をベクトルDBに蓄積し、外部サービスにデータを渡さずプライベートな環境で エージェント × RAG × ローカルLLM を組み合わせて動かすプロジェクトです。iPhoneからも利用できます。
「自分のデータをクラウドに預けずに、自分専用のAIを持ちたい」という動機でスタートしました。Open WebUI をフロントに、自前の OpenAI 互換 API サーバー(FastAPI)を挟み、LangGraph のエージェントが「そのまま答えるか、道具を使うか」を判断して応答します。道具はローカルRAG検索(LlamaIndex + ChromaDB)、Googleカレンダー操作(MCP経由)、目覚まし設定など。
さらに毎晩のバッチが Slack のメモや AI との会話を Obsidian vault に回収・要約し、ノート同士を自動リンクして RAG に取り込みます。「日中に見聞きしたことが、翌朝には AI 自身の記憶になっている」 サイクルを回すことを目指しています。
Open WebUI からの質問を受け、エージェントが必要に応じて道具を呼び出してから回答します。
| 道具 / 仕組み | 内容 |
|---|---|
note_search |
LlamaIndex Retriever で ChromaDB を検索(ノート・画像の内容・予定の記録) |
calendar_* |
Googleカレンダーの予定の追加・確認・変更・削除。mcpo の openapi.json から起動時に自動生成するため、MCP側でツールを増やしてもコード修正不要 |
set_wake_time / get_wake_time |
「明日は7時に起きたい」で目覚ましを手動設定・確認。「やっぱりカレンダー通りで」で解除 |
| 会話履歴・日付認識 | 直近20メッセージの履歴と現在日時をシステムプロンプトで毎回付与(「明日」「来週」を正しく計算) |
| 天気の自動付与 | 現在地の天気を1日1回取得してプロンプトに差し込み |
| ツール偽装対策 | 小型モデルが道具を呼ばずに「追加しました」と嘘の完了報告をする問題をプロンプトで抑止 |
カレンダーの予定から起床時刻を計算し、iPhoneの時計アプリに毎晩自動セットします。
- 起床時刻 = その日最初の予定の 60分前(予定なしは8:30。8:30より遅くはしない)
- 会話で「明日は7時に起きたい」と言えば手動上書き(
data/wake_override.json、カレンダーより優先) - iPhoneショートカットが毎晩22:30に
GET /wake_timeを叩き、「AI目覚まし」アラームを削除→再作成 - Mac に届かない夜は前日の時刻のまま鳴る(繰り返しアラームを保険にした設計)
fail-fast のパイプラインで、1日の情報を vault → ChromaDB へ流し込みます。
| 順 | スクリプト | 内容 |
|---|---|---|
| 1 | memo_collect.py |
Slack #メモ の投稿を vault の inbox/ へ回収(処理済みは✅リアクションで管理) |
| 2 | chat_collect.py |
Open WebUI の会話をLLMが要約して vault へ(記録する価値があるかも LLM が判断) |
| 3 | obsidian_sync.py |
iCloud 上の Obsidian vault を data/obsidian/ に同期 |
| 4 | image_ingest.py |
スクショ・書類写真をビジョンAIで読み取り、内容ごとに自動グルーピングしてMD化 |
| 5 | ingest.py |
data/ 以下のMDを ChromaDB に取り込み(upsert対応) |
| 6 | auto_link.py |
埋め込みのコサイン類似度でノート同士を Obsidian の [[リンク]] で自動接続 |
| 7 | calendar_fetch.py |
Googleカレンダーの予定を ChromaDB に取り込み |
| スクリプト | 実行 | 内容 |
|---|---|---|
morning_briefing.py |
毎朝8:00 | 今日の予定と Anki 残数を #秘書室 に投稿 |
news_digest.py |
毎朝8:10 | AIニュース(RSS)をLLMで英語要約して #AIニュース に配信。日本語訳はスレッドに畳む学習動線 |
english_assistant.py |
毎分 | #英語 チャンネルの見張り番。投稿に翻訳・例文を自動返信し、📌を付けた単語は音声付きで Anki カード化(edge-tts) |
- バッチ失敗は どのステップで死んだかを Slack #エラー へ即通知(trap ERR + fail-fast)
- 成功しても異常に遅い場合(30分超)は通知(「7時間半かかっても無音」だった死角への対策)
- Ollama が落ちている夜は残りをスキップして正常終了(次回自然に回復)
flowchart TD
iPhone([iPhone / ブラウザ])
iPhone -->|Tailscale経由| WebUI
subgraph Docker["Docker (docker-compose)"]
WebUI["Open WebUI :8080"]
API["FastAPI server :8000<br/>(OpenAI互換API)"]
Agent["LangGraphエージェント<br/>(道具を使うか判断)"]
RAG["note_search<br/>(LlamaIndex Retriever)"]
Wake["set/get_wake_time"]
MCPO["mcpo :8600<br/>(MCP→REST変換・内部限定)"]
WebUI -->|質問| API --> Agent
Agent --> RAG
Agent --> Wake
Agent -->|calendar_*| MCPO
MCPO --> MCP["personal-mcp<br/>(別リポジトリ)"]
end
subgraph Host["Mac ホスト"]
Ollama["Ollama<br/>gemma4:e4b / nomic-embed-text"]
end
Chroma[("ChromaDB")]
GCal["Google Calendar API"]
RAG --> Chroma
Agent -.->|生成・埋め込み| Ollama
MCP --> GCal
API -->|SSEストリーミング| WebUI --> iPhone
flowchart LR
Slack["Slack #メモ"] -->|memo_collect| Vault["Obsidian vault<br/>(iCloud)"]
Chat["Open WebUIの会話"] -->|chat_collect<br/>LLM要約| Vault
Vault -->|obsidian_sync| Data["data/obsidian/"]
Images["書類写真・スクショ"] -->|image_ingest<br/>ビジョンAI| Data2["data/screenshots/MD/"]
Data -->|ingest| Chroma[("ChromaDB")]
Data2 -->|ingest| Chroma
Chroma -->|auto_link<br/>類似ノートを接続| Vault
Calendar["Googleカレンダー"] -->|calendar_fetch| Chroma
flowchart LR
GCal["Googleカレンダー"] --> WT["GET /wake_time<br/>最初の予定の60分前<br/>(予定なしは8:30)"]
Override["wake_override.json<br/>「明日は7時に起きたい」"] -->|カレンダーより優先| WT
WT -->|毎晩22:30 Tailscale経由| SC["iPhoneショートカット"]
SC --> Clock["時計アプリ<br/>「AI目覚まし」を削除→再作成"]
| カテゴリ | 使用技術 |
|---|---|
| フロントエンド | Open WebUI |
| APIサーバー | FastAPI(OpenAI互換エンドポイント) |
| エージェント | LangGraph / LangChain(ReActエージェント + StructuredTool) |
| RAGフレームワーク | LlamaIndex |
| ベクトルDB | ChromaDB |
| LLM実行環境 | Ollama(生成: gemma4:e4b / 埋め込み: nomic-embed-text) |
| ツール連携 | MCP(personal-mcp)+ mcpo(MCP→REST変換) |
| コンテナ管理 | Docker / docker-compose |
| リモートアクセス | Tailscale |
| 外部連携 | Google Calendar API / OpenWeatherMap / Slack API / Anki(AnkiConnect) / edge-tts / RSS |
| 自動化 | cron(Mac) / iPhoneショートカット・オートメーション |
| 言語 | Python |
personalAI/
├── src/
│ ├── server.py # OpenAI互換APIサーバー + LangGraphエージェント
│ ├── wake_time.py # 起床時刻の計算 + 手動オーバーライド
│ ├── weather_fetch.py # 天気情報の取得
│ └── query.py # CLIからRAGに質問するスクリプト
├── batch/
│ ├── ingest.py # data/ のMDをChromaDBに取り込み(upsert)
│ ├── obsidian_sync.py # iCloudのvaultを data/ に同期
│ ├── chat_collect.py # Open WebUIの会話をLLM要約してvaultへ
│ ├── memo_collect.py # Slack #メモ をvaultへ回収
│ ├── auto_link.py # ノート同士を意味の近さで自動[[リンク]]
│ ├── image_ingest.py # 画像をビジョンAIで読み取りMD化
│ ├── calendar_fetch.py # Googleカレンダーの予定を取り込み
│ ├── morning_briefing.py # 朝ブリーフィング(#秘書室)
│ ├── news_digest.py # AIニュース便(#AIニュース)
│ └── english_assistant.py # 英語学習アシスタント(#英語)
├── data/ # 個人データ(.gitignore対象)
├── secrets/ # 認証情報(.gitignore対象)
├── chroma_db/ # ベクトルDBの実体(.gitignore対象)
├── docker-compose.yml # server / open-webui / mcpo の3サービス
└── Dockerfile
-
Docker / docker-compose
-
Ollama(ホスト側で起動しておくこと)
-
必要なモデルを事前に取得:
ollama pull gemma4:e4b # 生成用LLM ollama pull nomic-embed-text # 埋め込み用モデル
-
カレンダー操作ツールを使う場合は、隣のディレクトリに personal-mcp リポジトリが必要(mcpo サービスがビルドに使用。公開ツールは
docker-compose.ymlのTOOLS環境変数で選択) -
secrets/以下に認証情報を配置(いずれも.gitignore済み):weather.json… OpenWeatherMap の APIキー(天気機能を使う場合)credentials.json/token.json… Google Calendar API の認証情報(カレンダー連携を使う場合)
git clone https://github.com/chris9609/personalAI.git
cd personalAI
docker-compose up起動後、ブラウザで http://localhost:8080(Open WebUI)にアクセスして利用します。Tailscale を設定すれば、同じURLにiPhoneなど外部端末からもアクセスできます。
| 時刻 | 内容 |
|---|---|
| 毎晩 5:00 | 夜間バッチ(memo_collect → chat_collect → obsidian_sync → image_ingest → ingest → auto_link → calendar_fetch) |
| 毎朝 8:00 | 朝ブリーフィング → #秘書室 |
| 毎朝 8:10 | AIニュース便 → #AIニュース |
| 毎分 | 英語学習アシスタント(#英語 の見張り番) |
| 毎晩 22:30 | iPhoneショートカットが目覚ましを自動セット(iPhone側オートメーション) |
※ Mac は電源接続が前提(バッテリー駆動だとスリープして cron が沈黙するため)。手動実行する場合:
# 例: data/ 配下のMarkdownを取り込み
.venv/bin/python -m batch.ingest
# 例: Googleカレンダーの予定を取り込み(初回はブラウザで認証)
.venv/bin/python -m batch.calendar_fetch- エージェントの道具を拡充(mcpo の
TOOLSに Anki・Slack を追加) - RAG検索の精度改善
- 経路検索(乗換案内)の道具化
- 取り込み処理の高速化
iPhoneからローカルLLMに相談できる「自分専用の秘書AI」を作ることがゴールです。プロフィールや予定、メモ、AIとの会話、書類の写真といった個人のコンテキストをローカルに蓄積し、クラウドにデータを渡さずに自分を理解したAIと暮らす環境を、ポートフォリオも兼ねて継続開発しています。