Store timestamps as UTC-aware and slim the knowledge context to headlines
タイムゾーン: SQLite の DATETIME は tzinfo を保存できず、DateTime(timezone=True) を 指定してもバインド時に捨てられるため、API のレスポンスがオフセット無しの ISO 文字列になりフロントがローカル時刻として誤解していた。UtcDateTime (TypeDecorator) を追加し、書き込み時は naive な UTC に正規化、読み出し時は UTC の aware として返すようにする。格納フォーマットは変わらないので既存 データはそのまま正しく解釈でき、マイグレーションは不要。 コンテナは TZ 未設定で UTC だったため、天気通知の cron 時刻・深夜スキップ 判定・「明日」の日付計算が9時間ずれていた。TZ=Asia/Tokyo を指定し、slim イメージに無い tzdata を導入する。 蓄積知識: 層1が最新15件の要約全文を毎リクエスト無条件に注入しており、同じ内容が ローカルKB(層3)にも保存されているため重複していた。層1を「クエリ+ 一行要約」の目次に変え、本文は read_knowledge ツールで読み出す形にする。 一行要約は要約生成時の既存 LLM 呼び出しに相乗りするので呼び出し回数は 増えない。headline を持たない既存エントリは本文の先頭行から導出する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,7 +77,7 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
|
||||
|
||||
| # | 層 | 中身 | 実装 | 量の制御 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 蓄積知識 | 過去の Web 検索で得た `knowledge_entries` の**要約全文**+出典 URL | `knowledge.get_knowledge_context()` | 最新 `KNOWLEDGE_CONTEXT_LIMIT`(既定 15)件 |
|
||||
| 1 | 蓄積知識 | 過去の Web 検索で得た `knowledge_entries` の**見出し一覧のみ**(本文・出典は含めない) | `knowledge.get_knowledge_context()` | 最新 `KNOWLEDGE_CONTEXT_LIMIT`(既定 15)件 |
|
||||
| 2 | 共有ノート | ページの**タイトル一覧+本文先頭 40 文字**のみ | `notes.get_notes_context()` | 全ページ(本文は入れない) |
|
||||
| 3 | ローカル KB | `knowledge_base/**.md` から選ばれた**ファイル全文** | `local_knowledge.get_local_kb_context()` | 直近ユーザー発言との cos 類似度上位 `LOCAL_KB_TOP_K`(既定 3)件、かつ `LOCAL_KB_MIN_SCORE`(既定 0.75)以上 |
|
||||
| 4 | 長期メモリ | `memories` の要約を全件連結 | `memory.get_context_messages()` | 上限なし |
|
||||
@@ -100,6 +100,7 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
|
||||
`llm.resolve_tool_calls()` が本番のストリーミング生成の**前に**別途 LLM を呼んでツール呼び出しを解決する(最大 2 ラウンド)。
|
||||
|
||||
- `read_note` / `write_note` … 共有ノート本文の読み書き(`db` を渡したときだけモデルに提示)
|
||||
- `read_knowledge` … 層 1 の見出し一覧に出ている知識の本文を読む(同上)。層 3 として本文が既に注入されていれば呼ぶ必要はない
|
||||
- `web_search` … Tavily 検索。結果は `knowledge.summarize_results()` で構造化要約にされ、
|
||||
`knowledge_entries` と `knowledge_base/web_search/*.md` の両方に保存される。
|
||||
→ 次回以降の層 1・層 3 に自動的に効いてくる
|
||||
@@ -113,8 +114,8 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
|
||||
|
||||
#### 設計上の注意点
|
||||
|
||||
- トークン数ベースの制御は無く、すべて件数・文字数ベース。層 1(知識 15 件の要約全文)と層 3(Markdown 全文 ×3)は
|
||||
会話が短くてもコンテキストを大きく押し上げる
|
||||
- トークン数ベースの制御は無く、すべて件数・文字数ベース。層 3(Markdown 全文 ×3)は会話が短くても
|
||||
コンテキストを大きく押し上げる(層 1 は見出しのみなので件数を増やしても軽い)
|
||||
- ツールを使わない会話でも `resolve_tool_calls()` が必ず 1 回フルコンテキストを投げるため、**LLM 呼び出しは最低 2 回**になる
|
||||
- 層 4(長期メモリ)に上限が無く、長いセッションでは要約が積み上がる(要約の再要約はしない)
|
||||
- `get_local_kb_context()` が毎リクエストで `sync_local_kb()`(ディレクトリスキャン)と埋め込み API 呼び出しを 1 回行う
|
||||
@@ -122,7 +123,7 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
|
||||
### LLM 呼び出し(`core/llm.py`)
|
||||
|
||||
- `stream_chat()` / `complete()` / `embed()` が LiteLLM を薄くラップ
|
||||
- `resolve_tool_calls()` が `web_search` / `read_note` / `write_note` を解決し、結果を組み込んだメッセージ列を返す
|
||||
- `resolve_tool_calls()` が `web_search` / `read_note` / `write_note` / `read_knowledge` を解決し、結果を組み込んだメッセージ列を返す
|
||||
- `_log_context` で呼び出しを囲み、成功・失敗どちらも `api_logs` テーブルに記録(`embed()` は対象外)
|
||||
- `DEBUG=true` で `litellm._turn_on_debug()` が有効になり詳細ログが出力される
|
||||
|
||||
|
||||
Reference in New Issue
Block a user