技術約12分で読めます

かなにWeb調査の提案候補を付け、Twilogの近況からも関心を拾うようにした

いけさん目次

自作の会話窓口「かな」(Webブラウザから利用するチャットUI)に3DプリンターのX2Dを買う相談をしたら、その後の雑談でもX2Dの話が出続けた。
こちらが近況を尋ねるたびに、かなはセールを気にしているような返事をする。実際に価格を追跡しているわけではなく、前に話した題材を繰り返していた。

そこまで気になっているなら、価格が出た頃に確認して知らせてくれる方が便利だ。
ただ、会話に出たものを片っ端から調べ始められるのも困る。

会話から関心を拾う処理と、調査を始める処理を分けることにした。
入力欄の上に調査の候補ボタンを置き、押したときに検索や調査を実行する。あわせて、Twilog(Xの過去ログを記録するサービス)から自分の投稿を毎時取得し、候補の判別と普段の雑談で共用するようにした。

2026年11月13日に、Mac miniで常駐させているPi(ゲートウェイサーバー)とQwen(会話生成・ツール実行モデル)の窓口環境へ変更を加えた。
近況雑談と画像生成をつないだときの続きになる。

購入相談がかな自身の関心になっていた

直近の会話を読むこと自体は必要だが、そこに同じ題材が残ると、返事にも同じ題材が入りやすくなる。
かな自身が前に繰り返した発言まで、話題が続いている根拠として使われていた。

セールを待っているのは自分の方で、かな自身が買い物をするわけではない。
通常の会話と近況雑談の両方に、こちらの相談をかな自身の趣味や行動へ移さないプロンプト指示を入れた。時間が空いた後も、同じ場所で同じことをし続けているような返答を引き継がせない。

以前の相談ログはそのまま保存している。
こちらが続きを聞いたときには、その条件を使って答えられるようにしておく必要がある。話題を忘れさせるのではなく、今回の発言に必要な文脈として使う形にした。

キャラクターの近況と、ツールで確認した実際の情報を混同させないため、実際に価格やニュースを取得していないときは、見ている・調べ続けていると語らない指示も加えた。

関心が高そうでも、その場では調べない

以前試していた外部エージェント連携「Dots」から、GitHubの変更にある誤字を指摘する通知が来たことがあった。
その件は頼んでおらず、チャットでも話していなかったので、GitHub連携から拾ったものだと思う。

連携先から話題を拾う発想は参考になるものの、GitHubの細かなコード差分を常に見られても、日常の会話で欲しい提案にはなりにくい。
Twilogなら、自分が何を欲しがっているか、何に関心があるかが日々の投稿に自然と残る。そこで、かなとの会話とTwilogの投稿データを関心の候補判定に使うことにした。

バックエンドでは、会話の返答とは別に判別器を動かす。
Codex環境のLuna呼出し(GPT-5.3-Luna)を使い、本人の関心が高いか、まだ解決していないか、今確認する理由があるかを判定する。単語が出ただけの話や、他人の投稿の引用、すでに買ったものを毎回調べ直す候補にはしないように指示している。

判別器へ渡す会話履歴も、全体ではなく直近の範囲に絞った。
今回を含む直近最大8発言・16,000文字を渡し、既存候補の状態と根拠も参照させる。原文を途中で切って意味を変えず、文脈が足りなければ候補を出さない。自分の発言や投稿を根拠として残し、かなの過去の返事だけから関心を作らせないようにした。

判別器自身はツールを持たず、この段階では検索も始めない。
入力欄の上に、例えば「X2D Comboの現在のセール価格を調べる」といったボタンの候補を生成する段階にとどめている。

flowchart TD
    A["かなとの会話履歴"] --> C["背景で関心と未解決点を判別(Luna)"]
    B["Twilogの投稿データ"] --> C
    C --> D["入力欄の上に候補ボタンを表示"]
    D --> E["ユーザーが候補を押す"]
    E --> F["調査内容を保存して受付"]
    F --> G["QwenがWeb検索と本文取得を実行"]
    G --> H["調査結果を返答として表示"]

毎回「まだ気になってる?」「調べようか?」とチャットへ問いかける代わりに、必要なら押せる候補として置く。
調べる対象と内容がボタンの文面で決まっており、押した後に同じ確認を聞き直さない。

候補から実行する処理は、一回のWeb調査に限定した。
実行時に使えるツールも検索・本文取得・記憶の参照に絞り、ボタンを押した操作からファイル修正や公開、購入、予約といった作業まで勝手に進めないようにした。

候補が残り続けないようにする

入力欄の上に候補を置けても、興味のなくなった話が延々と残っていたら、雑談で同じ話題を繰り返されるのと同じ状態になってしまう。
候補ごとに「×」で消せるようにし、消した状態をサーバーのDB(SQLite)へ保存する。ブラウザを開き直したり、別のブラウザから接続したりしても同じ候補を出し直さない。

期限は作成から最長24時間にした。
最初の実装では48時間としていたが、基本は直近の話から作る候補なので24時間へ短縮した。表示を始める時刻を後ろへずらしても、作成からの期限は延びない。既存候補にもこの上限を適用する。

手動で消した候補と、期限が切れた候補は、同じ話題の再提案を7日間抑える。
かなが同じ題材を言い直しただけで新しい候補と判定され、期限が不必要に延び続けることも防いでいる。

その後の会話で購入済みや不要と分かった場合は、候補を終了させる。
新しい発言が届いてから判別が終わるまでは古い候補をいったん隠し、状況が変わった後に古い候補ボタンを押されないようにした。

押した候補の調査を一度だけ受け付ける

候補ボタンを押したときは、サーバー側に保存してある調査用のプロンプトを使う。
候補が有効か、削除済みでないか、別の接続からすでに受け付けていないかを確認し、通常のチャットと同じ保存処理で実行内容を記録する。

通信が切れて受付確認を取りこぼした場合も、同じ送信IDで再送する。
同じクリックを何度も新しい処理として登録せず、別のブラウザから同じ候補を押しても二重起動させない。プロンプトを変えて送られた場合も、保存した候補と一致しなければ受け付けない。

別の話を書きかけていた入力欄へ候補を押しても、その下書きを調査の文面で上書きしないようにした。
入力中のテキストや添付ファイルはそのまま残る。

Twilogは候補だけでなく、雑談にも使う

Twilogの取得は、関心の判別器から切り離した。
調査を提案する材料だけでなく、自分が最近何をしていたかを知る情報として、通常会話や自発的な雑談でも活用したかったからだ。

既存の認証を使って1時間ごとに最新1ページを取得し、直近24時間の自分の投稿を最大20件保存する。
投稿時刻と取得時刻、元投稿のURLを残す。他人の投稿やリポストは近況情報から除外し、画像の内容も解析対象には含めていない。

Qwenへ自動で渡すのは、そのうち最大6件・6,000文字の範囲だ。
今回の会話に自然に関係する場合だけ参考にする指示にし、毎回投稿を話題に引き戻したり、こちらの出来事をかな自身の体験として語ったりさせない。投稿中に命令文が含まれていても、作業指示や予約として実行する材料には使わない。

同じ保存済みの投稿を、調査候補を作る判別器も参照する。
候補機能をオフにしてもTwilogの更新や雑談での参照は継続できるよう、内部の処理を切り離している。

取得に失敗した直後に連続で取り直すことはせず、次の取得時刻まで待つ。
最後の成功から2時間を超えて更新できていないデータは、会話へ自動で渡さない。毎時取得は停止の指示があるまで継続する設定で有効にし、稼働中のサーバーで初回取得とHDDへの保存を確認した。

11月13日に入れた変更点

区分主な変更内容反映先
会話制御購入相談をかな自身の関心として繰り返さないプロンプト調整システム指示
候補判別会話履歴(直近8件)とTwilogから関心・未解決点を判別しボタン生成判別器(Luna)
調査実行ボタン押下でQwenによるWeb検索・本文取得を実行(1回限定)Qwenツール実行
状態管理24時間期限、手動削除、7日間再提案抑止、二重受付防止SQLite DB
近況連携Twilogを毎時取得し、直近24時間の投稿を会話と判別器で共用定期ジョブ・DB
運用上限判別を24時間に最大24回、未終了候補を最大5件に制限バックエンド設定

DBと常駐ファイルを退避してからMac miniの実環境へ反映し、既存の共有会話と設定を維持した状態で稼働させた。

手元で確認したところまで

判別と画面操作は、普段の共有会話とは分けたDBと合成会話で試した。
実際の判別器が、購入判断に必要な未確認事項から候補を作ること、購入済みなら候補を終了させること、他人の投稿の引用から候補を増やさないことを確認した。

ブラウザでは、削除後の再読込、二つのブラウザ接続への反映、期限切れによる非表示、下書きを保持したクリック受付を確認した。
候補を押した後のPiとQwenによるWeb検索、公開ページのHTTP 200での本文取得、返答の保存まで通している。取得に失敗した返答が出ただけで調査成功とせず、取得原本と保存記録でも確かめた。

Twilogは既存認証で実際のページを読み、共通の近況情報を保存できた。
保存した投稿をQwenが返答で参照する動作も試したが、会話としてどの程度自然か、どの投稿を拾うのが適切かといったバランス調整はまだこれからだ。

毎時更新は有効にして初回の保存を確認した段階だ。
長期間運用したときに候補の頻度が適切に保たれるかや、会話で不要と分かったあとの7日間抑止が意図どおり働くかは、今後の運用で見ていく。

2026年11月13日の追記

調査候補とTwilogの更新に加えて、同じ日にヘルスケアの記録と、スマートフォンから使う画面にも手を入れた。
機能を増やすだけでなく、毎日の報告を残す場所と、増えた機能へ移動する操作を整えた。

健康の報告を会話から記録する

かなに睡眠、体重、食事、活動、体調を報告すると、会話から健康記録を保存するようにした。
毎回「記録して」と指示する必要はなく、自分が実際に行った行動を対象にする。予定や仮定、他人の話は実績として記録しない。

保存先は、これまで会話や作業の記録を入れていたSQLiteのDBと同じだ。
元の発言を残し、その発言にひも付く健康記録と訂正履歴を持たせた。スマホから話しても、PCから話しても、同じサーバー側へ保存する。

ヘルスケアのページでは、期間と項目を選んでグラフを見たり、元の報告や訂正を確認したりできる。
CSVとJSONでも取り出せるようにした。食事は内容や分量を残すところから始めていて、栄養の精密な推定までは入れていない。

記録がない日を0として埋めると、報告しなかっただけなのに、何も食べていない、寝ていないというグラフになってしまう。
未報告と0は分け、日付が分からない記録を勝手に日別グラフへ置かないようにした。

睡眠は起床日で集計し、昼寝も分ける

睡眠は前の晩から翌朝へまたがることが多いが、夜中に寝れば同じ日付の中で終わる。
日付をまたいだかどうかで扱いを変えず、一回の睡眠に開始日時と終了日時を持たせ、日別の合計は起きた日へ入れることにした。

報告した睡眠日別の集計先時刻のグラフ
12日23時から13日7時まで13日の主睡眠12日の夜と13日の朝へ帯を分ける
13日2時から13日8時まで13日の主睡眠13日の中に帯を置く
13日14時から14時30分まで昼寝13日の昼寝主睡眠と色を分ける

「本格的にはまだ寝ていない」と「昼寝はした」も別に残せる。
昼寝を主睡眠の代わりにしたり、主睡眠が未報告だから0時間と決めたりしない。時間の分からない昼寝は、昼寝をした記録だけ残して、時間の合計へは足さない。

「6時間寝た」のように長さだけ分かる報告も、そのまま保存する。
開始・終了の時刻がなければ、グラフの帯を描くための時刻は作らない。就床と起床の時刻から計算した区間の長さも、機器で測定した実睡眠時間とは区別する。

記憶DBを深夜にフルコピーする

会話だけでなく健康記録も同じDBへ入れるので、DBを失うと困る範囲が広がった。
まずは毎日0時30分、日本時間で一回、外付けHDDの別フォルダーへDB全体をコピーする設定にした。日付ごとにフルコピーを残し、差分バックアップにはしない。

書込み中のDBはSQLiteのバックアップAPIでコピーし、一時ファイルの検査が通ってから日付付きの名前で確定する。
既存のかなの常駐処理に組み込み、新しい定期起動の仕組みは増やしていない。スリープや停止で時刻を過ぎた場合は、復帰してHDDを使えるときに当日分を一回作り、止まっていた日数分を一斉に実行しない。

今回の保存先は同じHDD内の別フォルダーとし、外付け媒体やNASへの転送は含めていない。
対象もSQLiteのDB本体で、添付画像や音声の原本ディレクトリまで一緒にコピーするものではない。

初回コピーの整合性と、同じ日に重複して作らない条件は確認した。
深夜の実時刻に定期処理が動くところは、まだその時刻を待って確認した段階ではない。

増えたリンクを共通のサイドバーへ移す

機能が増えるたびにチャット上部のリンクを増やしていたが、スマホでは場所を取り、移動先ごとにヘッダーの位置も違っていた。
ホーム画面からWebアプリ(PWA)として開くと、別タブへ移動した先にブラウザの戻るボタンがなく、チャットへ戻りづらい。

左端にアイコンを常時表示し、メニューボタンを押すと名前が広がるサイドバーへ変えた。
チャット、作業一覧、リマインダー・予約、ヘルスケア、ブログDEV、設定を同じ並びにして、アプリ内の移動は同じ画面で行う。移動先でもチャットのアイコンから戻れる。

ブログDEVも、単独のリンクから共通の枠内でプレビューを見る形へそろえた。
チャットで入力していた本文は、同じタブ内でページを移動して戻ったときに復元する。

ヘッダーをそろえた直後は、iPhoneの上下に余白を取りすぎていた。
実機のスクリーンショットと画面の寸法を見ながら、全ページを viewport-fit=auto にそろえ、OSが確保する領域(safe area)とアプリの余白を二重に取らないようにした。

上に足していた44pxを外し、下は一度8pxまで詰めたが、今度は下端が窮屈になった。
通常時の入力欄と設定アイコンの下には20pxの余裕を取り、画面キーボードが開いている間は8pxにした。

スマホの改行キーで送信しないようにする

スマートフォンから使う際、入力欄で改行できない問題もあった。
入力欄のEnterキーを送信に割り当てていたため、画面キーボードの改行キーでも本文を送ってしまっていた。

スマホでもPCでも、Enterキーは改行に統一した。
送信は矢印ボタンか、PCのCtrl/⌘+Enterで行う。画面キーボードにも改行キーとして指定し、日本語IMEの変換中やショートカットのキーリピートで誤送信しないようにした。

下が切れる問題は、下端の余白だけではなかった。
添付や入力が増えると、キーボード表示中の入力欄が枠からはみ出す場合もあったので、添付・受付状況・位置情報・調査候補をスクロールする範囲にまとめた。入力欄と送信ボタンはその外側に置き、本文が長いときの入力欄の高さも、見えている画面の高さに合わせて制限する。

修正後はPCとスマホ幅のブラウザに加えて、iOS 27.2のホーム画面Webアプリをシミュレーターで開いて確認した。
画面キーボードの改行キーでは送信されず、矢印ボタンで一件だけ保存されること、改行が保存後も残ることを確認した。添付画像8件と18行の本文を入れても、入力欄と送信ボタンがキーボードの上に隠れず表示できることを確認した。

保存の試験は普段の共有会話とは別のDBで行い、稼働中の画面では発言を送らずに改行と表示位置を確かめた。
シミュレーター上で確認した段階で、iPhone実機での最終表示はまだ未確認だ。