Add section grouping to shared note tabs
Notes can now be clustered into named sections in the tab bar: each tab gets a 🏷 button to assign it to a section, and clicking a section's label renames (or, given an empty name, ungroups) every page in it. note_pages gains a `section` column. Since the app has no migration framework, database.py adds the column to existing SQLite files at startup via PRAGMA table_info + ALTER TABLE. core/notes.set_section() keeps same-section pages contiguous by renumbering position whenever a page's section changes, so the tab bar and the AI-facing title list (now grouped under "## section" headings) can both treat "adjacent" as "same group" without a separate ordering field. Also fixes a bug in the tab rename input: ref="renameInput" sits inside a nested v-for, which Vue 3 binds as an array rather than a single element, so renameInput.value?.select() was throwing on every rename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,17 +59,34 @@ Copy-Item .env.example .env
|
||||
frontend (Vue3 + Pinia, :5173)
|
||||
↕ REST + SSE (Vite proxy)
|
||||
backend (FastAPI, :8000)
|
||||
├── app/api/ チャット・メモリ・ノート・ログ のルーター
|
||||
├── app/core/ LLM ラッパー・メモリロジック・共有ノート・設定
|
||||
└── app/db/ SQLAlchemy モデル・DB 接続
|
||||
├── app/api/ チャット・メモリ・ノート・ログ・設定 のルーター
|
||||
├── app/core/ LLM ラッパー・共有ノート・設定
|
||||
│ ├── memory_kb/ コンテキスト管理(要約・蓄積知識・ローカルKB・タグ付け)
|
||||
│ ├── search/ Tavily Web 検索
|
||||
│ └── discord/ Discord Bot・定期通知
|
||||
└── app/db/ SQLAlchemy モデル・DB 接続
|
||||
↕
|
||||
SQLite (playchat.db) + LiteLLM → 任意の LLM
|
||||
```
|
||||
|
||||
### メモリ機構
|
||||
### コンテキスト管理
|
||||
|
||||
- **短期**: セッション内の全メッセージをコンテキストに含める
|
||||
- **長期**: メッセージ数が `MEMORY_SUMMARIZE_THRESHOLD`(デフォルト 20)を超えると古い半分を LLM で要約 → `memories` テーブルに保存 → 次回以降のシステムプロンプトに注入
|
||||
LLM に渡すコンテキストは `app/core/memory_kb/memory.py` の `get_context_messages()` が毎リクエスト組み立てる。
|
||||
以下の順に system メッセージを積み、最後に会話履歴を連結する(詳細は `docs/architecture.md`)。
|
||||
|
||||
1. **蓄積知識** (`memory_kb/knowledge.py`): 過去の Web 検索で得た `knowledge_entries` の要約全文+出典。最新 `KNOWLEDGE_CONTEXT_LIMIT`(既定 15)件。セッションを横断する
|
||||
2. **共有ノート** (`core/notes.py`): タイトル一覧+本文先頭 40 文字のプレビューのみ
|
||||
3. **ローカル KB** (`memory_kb/local_knowledge.py`): `backend/knowledge_base/**.md` のうち、直近ユーザー発言と埋め込みコサイン類似度が高い上位 `LOCAL_KB_TOP_K`(既定 3)件かつ `LOCAL_KB_MIN_SCORE`(既定 0.75)以上を**全文**注入。プロンプト上は Web 検索結果より優先する扱い
|
||||
4. **長期メモリ**: `memories` の要約を全件連結。上限なし
|
||||
5. **短期メモリ**: セッション内の `messages` 全件(要約で削除された分を除く)
|
||||
|
||||
**要約**: `maybe_summarize()` がアシスタント応答の保存直後に走り、メッセージ数が `MEMORY_SUMMARIZE_THRESHOLD`(既定 20)以上になると古い半分を LLM で要約して `memories` に保存する。**要約した元メッセージは物理削除される**(復元不可)。
|
||||
|
||||
**遅延読み込み**: 重い情報は目次だけ注入し、`llm.resolve_tool_calls()` が本番生成の前にツール(`web_search` / `read_note` / `write_note`、最大 2 ラウンド)を解決する。Web 検索の結果は構造化要約にされ `knowledge_entries` と `knowledge_base/web_search/*.md` の両方に保存されるため、次回以降は層 1・層 3 として自動的に効いてくる。
|
||||
|
||||
**付随機能**: `memory_kb/tagging.py` が各メッセージ・要約にバックグラウンドでタグを 3 つ付ける(コンテキストには注入せず、Discord 雑談 Bot の話題選びに使う)。`memory_kb/word_memory.py` は Janome で抽出した名詞を `deque(maxlen=200)` にプロセス内保持する(DB 非永続。起動時に直近 200 件から再シード)。
|
||||
|
||||
**変更時の注意**: トークン数ベースの制御は一切なく、すべて件数・文字数ベース。またツールを使わない会話でも `resolve_tool_calls()` がフルコンテキストを 1 回投げるため、1 応答あたり LLM 呼び出しは最低 2 回になる。
|
||||
|
||||
### 共有ノート
|
||||
|
||||
@@ -80,6 +97,7 @@ SQLite (playchat.db) + LiteLLM → 任意の LLM
|
||||
- **ツール実行の流れ**: `llm.resolve_tool_calls()` が本番の応答生成前にツール呼び出しを解決する。`web_search` と共通の仕組みで、ノート系ツールは `db` が渡されたときだけモデルに提示される
|
||||
- **UI への反映**: AI がノートを書き換えた場合、SSE で `notes_updated` を送ってフロント側のノートパネルを再読み込みさせる
|
||||
- **編集の衝突回避**: フロントは未保存の編集があるページ(`dirtyIds`)をサーバーの値で上書きしない
|
||||
- **セクションによるグルーピング**: `note_pages.section` にセクション名を持たせ、同じセクションのページは `position` 上で常に連続させる(`core/notes.set_section()` が割り当て時に並べ替える)。タブバーは `position` 順に描画し、隣接して同じ `section` が続く区間を1グループとして表示する。`get_notes_context()` はセクションごとに `## セクション名` の見出しを付けてタイトル一覧を出す。セクション名の一括変更は `POST /api/notes/sections/rename`(`core/notes.rename_section()`)
|
||||
|
||||
### SSE ストリーミング
|
||||
|
||||
@@ -109,9 +127,17 @@ SQLite (playchat.db) + LiteLLM → 任意の LLM
|
||||
|
||||
| 変数 | デフォルト | 説明 |
|
||||
|---|---|---|
|
||||
| `DEFAULT_MODEL` | `gpt-5.4` | デフォルト LLM モデル |
|
||||
| `DEFAULT_MODEL` | `gpt-4o` | デフォルト LLM モデル(`.env.example` では `gpt-5.4`) |
|
||||
| `DATABASE_URL` | `sqlite:///./playchat.db` | DB 接続先 |
|
||||
| `MEMORY_SUMMARIZE_THRESHOLD` | `20` | 長期メモリ化するメッセージ数の閾値 |
|
||||
| `KNOWLEDGE_CONTEXT_LIMIT` | `15` | 蓄積知識をシステムプロンプトに注入する最大件数 |
|
||||
| `TAVILY_API_KEY` | — | Tavily Web 検索の API キー(未設定なら `web_search` は空を返す) |
|
||||
| `SEARCH_SUMMARY_MODEL` | — | Web 検索結果の要約に使うモデル(未設定なら `DEFAULT_MODEL`) |
|
||||
| `TAGGING_MODEL` | — | メッセージの自動タグ付けに使うモデル(未設定なら `DEFAULT_MODEL`) |
|
||||
| `LOCAL_KB_DIR` | `knowledge_base` | ローカル Markdown ナレッジベースのディレクトリ |
|
||||
| `LOCAL_KB_EMBEDDING_MODEL` | `text-embedding-3-small` | ローカル KB の埋め込みモデル(GUI 設定画面から実行時変更可) |
|
||||
| `LOCAL_KB_TOP_K` | `3` | ローカル KB から注入する最大ファイル数 |
|
||||
| `LOCAL_KB_MIN_SCORE` | `0.75` | ローカル KB を注入するコサイン類似度の下限 |
|
||||
| `CORS_ORIGINS` | `http://localhost:5173` | 許可する CORS オリジン |
|
||||
| `DEBUG` | `false` | LiteLLM デバッグログの有効化 |
|
||||
| `OPENAI_API_KEY` | — | OpenAI API キー |
|
||||
|
||||
Reference in New Issue
Block a user