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:
2026-08-20 14:22:59 +09:00
co-authored by Claude Opus 5
parent 8b2231ea97
commit be3af44891
13 changed files with 427 additions and 49 deletions
+10 -4
View File
@@ -116,17 +116,21 @@ async def resolve_tool_calls(
) -> tuple[list[dict], list[dict], bool]:
"""必要に応じてLLMにツールを呼ばせ、その結果を会話に組み込んだ上でメッセージ列を返す。
扱うツールは web_search と共有ノート(read_note / write_note)。ノート系は db が
渡されたときだけ提示する。モデルがtool callingに対応していない場合は何もせず
元のmessagesをそのまま返す。
扱うツールは web_search、共有ノート(read_note / write_note)、蓄積知識(read_knowledge)。
DBを見るものは db が渡されたときだけ提示する。モデルがtool callingに対応していない場合は
何もせず元のmessagesをそのまま返す。
戻り値: (拡張後のmessages, 実際に検索したクエリと結果のリスト, ノートを変更したか)
"""
# app.core.memory_kb.knowledge は app.core.llm を import しているため、
# モジュールレベルで読むと循環 import になる(_save_log と同じ理由で関数内 import する)。
from app.core.memory_kb.knowledge import KNOWLEDGE_TOOLS, execute_knowledge_tool
used_model = model or DEFAULT_MODEL
working_messages = list(messages)
searches: list[dict] = []
notes_changed = False
tools = [WEB_SEARCH_TOOL] + (NOTE_TOOLS if db is not None else [])
tools = [WEB_SEARCH_TOOL] + (NOTE_TOOLS + KNOWLEDGE_TOOLS if db is not None else [])
for _ in range(max_rounds):
try:
@@ -168,6 +172,8 @@ async def resolve_tool_calls(
if name in ("read_note", "write_note") and db is not None:
tool_text, changed = execute_note_tool(db, name, args)
notes_changed = notes_changed or changed
elif name == "read_knowledge" and db is not None:
tool_text = execute_knowledge_tool(db, name, args)
else:
query = args.get("query", "")
results = await search_web(query)