Adds a note_pages table backing a tabbed notes panel where the user and the model can both read and write. Only page titles (plus a short preview) are injected into the system prompt on every turn; full page content is kept out of context and fetched on demand via a read_note tool, with write_note letting the model append to, replace, or create a page. Generalizes llm.resolve_web_search into resolve_tool_calls so the tool-calling loop handles web_search and the new note tools through one path. The chat stream emits a notes_updated SSE event when the model edits a note so the frontend panel can refresh; local edits are tracked as dirty and never clobbered by that refresh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.3 KiB
6.3 KiB
アーキテクチャ概要
全体構成
フロントエンド (Vue 3 + TypeScript + Vite, :5173)
↕ REST + SSE (Vite dev proxy 経由)
バックエンド (FastAPI + Python, :8000)
↕ SQLAlchemy
SQLite (playchat.db)
↕ LiteLLM
OpenAI / Anthropic / Ollama など任意の LLM
バックエンド
ディレクトリ構成
backend/
├── app/
│ ├── main.py # FastAPI インスタンス、CORS、ルーター登録、DB初期化
│ ├── api/
│ │ ├── chat.py # チャット・セッション関連エンドポイント
│ │ ├── memory.py # 長期メモリ参照・削除
│ │ └── logs.py # API ログ参照・削除
│ ├── core/
│ │ ├── config.py # .env 読み込み・定数定義
│ │ ├── llm.py # LiteLLM ラッパー(ストリーミング・ログ記録)
│ │ └── memory.py # メモリ読み書きロジック(要約・コンテキスト生成)
│ └── db/
│ ├── database.py # SQLite 接続・セッションファクトリ・テーブル自動作成
│ └── models.py # SQLAlchemy モデル定義
└── pyproject.toml
DB テーブル
| テーブル | 主なカラム | 用途 |
|---|---|---|
sessions |
id (UUID), title, model, created_at, updated_at | チャットセッション |
messages |
id, session_id, role, content, created_at | 会話メッセージ(短期メモリ) |
memories |
id, session_id, summary, embedding_json, created_at | 要約による長期メモリ |
note_pages |
id, title, content, position, created_at, updated_at | ユーザーと AI が共同編集する共有ノートのページ |
api_logs |
id, session_id, model, request_json, response_text, error, latency_ms, created_at | LLM API 呼び出しログ |
メモリ機構(core/memory.py)
- 短期メモリ: セッション内の全
messagesをコンテキストとして送信 - 長期メモリ: メッセージ数が
MEMORY_SUMMARIZE_THRESHOLD(デフォルト 20)を超えると、古い半分を LLM で要約してmemoriesに保存しメッセージを削除 - コンテキスト合成:
get_context_messages()がmemoriesの要約をシステムメッセージとして先頭に付加し、残存メッセージと結合して返す
LLM 呼び出し(core/llm.py)
stream_chat()/complete()が LiteLLM を薄くラップ_log_contextで呼び出しを囲み、成功・失敗どちらもapi_logsテーブルに記録DEBUG=trueでlitellm._turn_on_debug()が有効になり詳細ログが出力される
共有ノート(core/notes.py / api/notes.py)
ユーザーと AI が共同編集するノート。note_pages の 1 行が UI の 1 タブに対応する。
get_notes_context()がタイトル一覧のみをシステムメッセージとして生成し、get_context_messages()が会話コンテキストに差し込む(本文は含めない)- 本文の読み書きはツール経由:
read_note(本文取得)/write_note(追記・置換・新規作成) llm.resolve_tool_calls()がweb_searchと同じループでこれらを解決する。ノート系ツールはdbを渡したときのみモデルに提示される- REST(
/api/notes)はフロントのノートパネル用。GET 一覧 / POST 作成 / PATCH 更新 / DELETE 削除
SSE ストリーミング(api/chat.py)
POST /chat/stream は StreamingResponse で以下のイベントを順番に送出する:
data: {"type":"session_id","value":"<uuid>"} # 新規セッション時のみ
data: {"type":"chunk","value":"テキスト断片"} # 0回以上
data: {"type":"notes_updated"} # AIが共有ノートを更新した時のみ
data: {"type":"done"} # 正常完了
data: {"type":"error","value":"エラー内容"} # 例外発生時
バッファリング防止のため Cache-Control: no-cache と X-Accel-Buffering: no を付与する。
フロントエンド
ディレクトリ構成
frontend/src/
├── api/
│ └── client.ts # fetch ラッパー・SSE クライアント・型定義
├── stores/
│ └── chat.ts # Pinia ストア(全アプリ状態)
├── components/
│ ├── SessionList.vue # セッション一覧サイドバー
│ ├── ChatWindow.vue # メッセージ表示エリア
│ ├── MessageInput.vue # 入力欄・送信
│ ├── ModelSelector.vue # LLM モデル切替セレクト
│ ├── MarkdownContent.vue # marked + DOMPurify によるレンダリング
│ ├── LogViewer.vue # API ログ閲覧パネル
│ └── ToastList.vue # エラートースト通知
└── App.vue # ルートレイアウト
状態管理(stores/chat.ts)
| 状態 | 型 | 説明 |
|---|---|---|
sessions |
SessionInfo[] |
セッション一覧 |
currentSessionId |
string | null |
選択中セッション |
messages |
Message[] |
表示中メッセージ |
isStreaming |
boolean |
SSE 受信中フラグ |
selectedModel |
string |
選択中 LLM モデル |
toasts |
Toast[] |
エラー通知キュー |
SSE 受信フロー
sendMessage()がユーザーメッセージと空のアシスタントメッセージをmessagesに追加streamChat()が fetch + ReadableStream で SSE を受信chunkイベント受信のたびにmessages.value[msgIdx].contentを更新(Vue reactive proxy 経由)doneでpendingフラグを解除error/ ネットワークエラーはerrorフラグを立て赤バブル表示 + トースト通知
Markdown レンダリング(MarkdownContent.vue)
- アシスタントメッセージのみ
markedで HTML に変換後DOMPurifyでサニタイズ - ストリーミング中(
pending=true)は未閉じのコードフェンス(```)を自動補完してから描画
Vite プロキシ設定
/chat、/memory、/logs、/health を http://localhost:8000 に転送。
/chat は SSE バッファリング無効化のため configure で x-accel-buffering: no を付与。