はじめに
出版記念の特典を、公式LINEのキーワード応答で配布している。「魔法のランプ」「3つの願い」のような合言葉を送ると、対応するPDFが届く仕組みだ。
今回、新しく6つの特典と6つのキーワードを追加した。ところがテストしてみると、新しいキーワードを送っても、どれも同じ汎用メッセージが返ってくる。既存の2つのキーワードは正常に動くのに、新しい6つだけが全滅していた。
この記事は、その原因を突き止めて直すまでの記録。
最初にやったこと(うまくいかなかった)
まず疑ったのは、キーワードの登録ミスや、D1への書き込み漏れだった。
-
wrangler tailでログを見た → メッセージは確かに届いている -
messages_logテーブルを直接見た → 「マッチしなかった」ことしか分からない - 登録した内容をSELECTし直した → キーワードも本文も正しく入っている
データは正しい。ロジックのどこかが正しく動いていない、というところまでは分かったが、それ以上は推測の域を出なかった。
原因を突き止めた方法
このLINE自動応答システムはオープンソース(line-harness-oss)で、マッチングロジックそのものがGitHub上に公開されている。ログを眺めて推測するのをやめて、リポジトリをクローンし、実際にキーワードをマッチさせている関数を直接読んだ。
行き着いたのはこの1行だった。
SELECT * FROM auto_replies
WHERE is_active = 1
ORDER BY created_at ASC
ルールは作成日時の古い順に評価され、最初にマッチしたルールで確定する。ここに優先順位の実体があった。
そして、「どのキーワードにも該当しなかった場合」の汎用メッセージを返すフォールバック行が、実はkeyword = ''+部分一致という、かなり緩い条件で登録されていた。このフォールバック行の作成日時が、新しく追加した6つのキーワードより古かった。
つまり、新しいキーワードでメッセージを送っても、ORDER BY created_at ASCで先に評価される緩いフォールバック行がそこで先にマッチしてしまい、後から作った本来のルールにはたどり着けていなかった。
直し方
フォールバック行の作成日時を、未来の日付に書き換えた。
UPDATE auto_replies
SET created_at = '9999-12-31T23:59:59.999'
WHERE keyword = '' AND response_type = 'contains';
これで、フォールバックは常に最後に評価されるようになり、新しいキーワードを含め、以後追加するどのルールも正しくマッチするようになった。
この話の教訓
「ルールの優先順位を、作成日時という副産物に暗黙的に依存させる」設計は、後から誰かがルールを1つ追加した瞬間に、静かに壊れる。今回のように、既存の2つは動いていて新しい6つだけ死んでいる、という中途半端な壊れ方をするので、余計に発見しづらい。優先順位が本当に重要な意味を持つなら、priorityのような明示的なフィールドを持つべきだった。
もう一つ、今回の原因特定について。ログを眺めて「たぶんこれだろう」で片付けず、実際のマッチングロジックのソースコードを読んで確定させた。地味だが、これが一番再現性の高いデバッグ手法だった。AIエージェントに調査させる場合も、この「推測で終わらせず、一次情報(ソースコード)に当たって確定させる」姿勢は、Harness/Loop Engineeringの本で繰り返し書いている考え方そのものだと、また実感した。
おまけ:ついでにキーワード体験も見直した
このバグを直したついでに、受け取り方の設計も変えた。以前は「8個のキーワードを、登録前に全部知っておく」設計だったが、これは登録前に不要な認知負荷を課していた。
今は、友だち追加した瞬間に1つ目が自動配信され、受け取るたびに「次はこの言葉」と案内される、01→02→…→08の一本道のチェーンになっている。バグを直すことと、体験を見直すことは、同じ日にまとめてやる方が効率がいい。