Commit Graph
5 Commits
Author SHA1 Message Date
colcaroliandClaude Opus 5 70735567f2 Thread Discord bot replies for a tidier channel
長い一般会話やリマインド確認のやり取りがチャンネルに直接積み上がって
埋もれるのを避けるため、全メンションへの返信をスレッドに集約する。

discord_bot._ensure_thread() を on_message の先頭、キーワード分岐より前に
挟み、以後の返信・タイピング表示・ReminderContext.channel_id をすべて
そこで決めた reply_channel 経由にする。

- 通常のチャンネルでのメンションは元メッセージからスレッドを新規作成する
  (名前は本文を90文字に切り詰め、自動アーカイブは1440分=1日。ブースト
  状況に関係なく常に選べる値を使う)
- 既にスレッド内でのメンションはそのスレッドを再利用し、二重に作らない
- DMや権限不足でスレッドを作れない場合は例外を握りつぶし、元のチャンネル
  へフォールバックする(Botの応答自体は止めない)
- message.reply() は常に元メッセージのチャンネル(スレッドの親)へ送って
  しまいスレッドには入らないため、_reply() でreply_channelがスレッドかどうか
  を見て送り先を明示的に使い分ける
- そのメンションから登録したリマインドの⏰通知も、登録元のスレッドに
  届くようにした(channel_idにスレッドのIDを渡すだけで済む。スレッドが
  自動アーカイブされていても送信時にDiscordが自動展開する)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 02:08:45 +09:00
colcaroliandClaude Opus 5 c6108a1b29 Route reminders by tool availability, not keyword matching
「7時にモーニングコールをお願い。」のような、REMINDER_KEYWORDS のどれとも
一致しない言い回しがリマインド機能を素通りし、ただの雑談として処理されて
いた(本番の api_logs で実例を確認)。「モーニングコール」「起こして」を
キーワードに追加しても、次の言い回しでまた同じことが起きるだけなので、
キーワード判定そのものをやめる。

discord_bot.on_message は雑談・天気のキーワード分岐はそのまま残し、それ以外の
全メンションで create_reminder / list_reminders / cancel_reminder の3ツールを
常に同席させる(reminders.build_tool_hint() が現在時刻の文脈を添える)。
使うかどうかはLLM自身の判断に委ねるため、キーワード一覧を事前に尽くす必要が
なくなる。本番のBedrock Nova 2 Liteで、実際に失敗していた文言が
create_reminder を正しく呼ぶことを確認済み。

reminders.handle_reminder_request() は resolve_reminder_tools(messages, ctx,
model) に一般化し、discord_bot 側で組み立てたmessages(人格プロンプト+天気
コンテキスト+ツール文脈)をそのまま受け取れるようにした。tool calling
非対応のモデルが設定されていた場合は、1ラウンド目の例外を検知してツール
なしの通常応答にフォールバックする。ツール呼び出しが一般会話の呼び出しに
統合された以上、それだけ別モデルを割り当てる余地がなくなったため
REMINDER_MODEL は廃止する。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 01:17:03 +09:00
colcaroliandClaude Opus 5 ae997914c2 Make Discord reminder failures diagnosable
リマインドが「⚠️ 応答の生成に失敗しました」だけを返して原因が追えなかった
ため、失敗経路を個別に捕まえてログにも残るようにする。

ロギング: uvicorn は自分のロガーしか設定しないため、アプリ側の
logging.getLogger(__name__) は logging.lastResort 送りになっていた。
WARNING 未満が全て消え、出力にレベルもタイムスタンプもロガー名も付かず、
logger.info("Reminder created: ...") すら残らない状態だった。
main.py で basicConfig を呼び、LOG_LEVEL(既定 INFO)で制御する。

reminders.py:
- choices[0].message の取り出しを個別に捕捉する。tool calling 非対応の
  エンドポイントは例外を投げずに choices が空の応答を返すことがあり、
  ここで落ちると on_message の汎用ハンドラに流れて原因が消える
- ツール実行を _run_tool() に切り出し、DBエラーを専用の文言で返す。
  併せてDBセッションをツール1回分だけ開くようにした。LLMとの往復のあいだ
  握り続けると SQLite のロックを長時間保持し、同時に走るAPIリクエストと
  競合して "database is locked" を起こしうるため
- tool_choice="auto" を渡すのをやめた。Bedrock はこの引数に非対応で、
  今まで通っていたのは app.core.llm が litellm.drop_params=True を
  立てている副作用に過ぎず、import 順が変われば壊れる。tools を渡した
  ときの既定が "auto" なので省いても挙動は変わらない
- どのモデルで何を処理したかを INFO で残す

CLAUDE.md には Lightsail への接続方法(作業ツリーにあった未コミット分)も
含む。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 00:08:44 +09:00
colcaroliandClaude Opus 5 c1aba42cf8 Schedule reminders by mentioning the Discord bot
メンション本文にリマインド系のキーワードが含まれていたとき、LLM に
create_reminder / list_reminders / cancel_reminder の3ツールを渡し、
自然言語から日時と用件を取り出して reminders テーブルに保存する。
system メッセージに現在時刻を入れてあるので『明日』『30分後』のような
相対表現はモデル側が絶対日時に直す。扱うのは一回きりの通知のみ。

配信は APScheduler の discord_reminders ジョブ(既定30秒間隔)が担い、
Webhook ではなく Bot クライアントから登録元チャンネルへ依頼者を
メンションして送るため、専用の Webhook URL 設定は要らない。

- 日時は他テーブルと同じく UTC で保存し、ユーザー・LLM とのやり取りは
  ローカル時刻で行って reminders.py の境界で変換する
- 過去・REMINDER_MAX_AHEAD_DAYS より先・解釈不能な日時はツール結果として
  エラー文をモデルに返し、聞き直させる
- 一覧・取り消しは依頼者の user_id で絞るため他人のリマインドには触れない
- 配信に5回失敗したら notified_at を立てて諦める(チャンネル削除などで
  ポーリングのたびにエラーログを出し続けるのを防ぐ)。カウンタは
  プロセス内のみなので、再起動すればまた試す

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 23:29:55 +09:00
colcaroli 347230e3c8 Reorganize core into subpackages, add local KB, ruff/pyright tooling
- Split backend/app/core into weather/, discord/, memory_kb/, search/
  subpackages by responsibility, keeping config.py and llm.py as the
  shared foundation; updated all import sites accordingly.
- Add local Markdown knowledge base support (local_knowledge.py,
  knowledge_base/fgo.md, LOCAL_KB_* settings) that embeds and injects
  relevant docs into the system prompt by topic similarity.
- Add ruff and pyright as backend dev dependencies with basic config
  in pyproject.toml for linting and type checking.
- Document local machine quirks in CLAUDE.md (Postgres on port 8080,
  SSLKEYLOGFILE breaking litellm/pyright via OPENSSL_Uplink crashes).
- Add backend/run_dev.ps1 dev launcher and docs/discord-bot.md.
2026-06-21 11:44:11 +09:00