自作の WordPress プラグイン Rapls AI Chatbot 1.19.0 で、大きな文書を自動で分割して取り込む機能を入れました。ところが、1.19.0 より前に取り込みに失敗していた文書は、アップグレードしても失敗したままでした。そのうえ、今のコードを読み直すと、分割が効くのはファイルをアップロードしたときだけでした。
| 文書の入り方 | 1.19.0 以降、分割されるか |
|---|---|
| ファイルをアップロードする | 分割される |
| 管理画面の入力欄に直接書く | 分割されない |
| 既存のエントリを編集する | 分割されない |
| 設定のエクスポートファイルから読み込む | 分割されない |
| 1.19.0 より前に取り込んだ文書 | 何も起きない |
| 1.19.0 より前の文書の状態 | アップグレード後 |
|---|---|
| 埋め込みに成功していた | そのまま使える |
| 大きすぎて失敗していた | 未埋め込みのまま。再挑戦の対象からも外れている |
| 直す方法 | 削除して、ファイルとして入れ直す |
先に断っておくと、保存の形を変えたら既存のデータを移行する必要がある、というのは一般的な話です。ここで書くのは、それを入れなかった自分のプラグインで、何がどう残ったかと、入り口ごとに分割されるかどうかをコードで確かめた結果です。
8/31 夜 1.18.0:失敗を記録して、外すようにした
発端は、利用者の方からの報告でした。ナレッジベースに大きな文書を入れると、エラーも出さずに 0 件で終わる。
埋め込みのモデルには、1回に送れる長さの上限があります。text-embedding-3-small で約 8192 トークン、日本語でおよそ 3 万字です。これを超えた文書が、何も言わずに落ちていました。
1.18.0 では、落ちた理由を記録して一覧に出すようにしました。あわせて、大きすぎる(too_large)とテキストがない(no_text)の2つは、何度送っても結果が変わらないので、次の埋め込みの対象から外すようにしました。外さないと、埋め込み処理のループがその1件で止まらなくなるからです。
ここまでは「なぜ失敗したか分かる」まで
9/1 昼 1.19.0:取り込むときに分割する
1.19.0 で、大きな文書を取り込むときに分割するようにしました。1,500 文字を超える文書は、約 1,000 文字ずつに切って、別々の行として保存し、同じ group_id でまとめます。1つ1つの断片は上限よりずっと小さいので、大きすぎて落ちることは、作りの上で起きなくなります。
データベースには、列を3つ足しました。
ALTER TABLE ... ADD COLUMN group_id VARCHAR(32) DEFAULT NULL;
ALTER TABLE ... ADD COLUMN chunk_index INT UNSIGNED DEFAULT 0;
ALTER TABLE ... ADD COLUMN chunk_total INT UNSIGNED DEFAULT 1;
アップグレードの処理は、これだけです。列を足して、既定値を入れる。既存の行は、group_id が空の、分割されていない1件のまま残ります。
足すだけの移行は安全。そのかわり、何も直さない
9/10 既存の文書は、遡って分割されていなかった
1.19.0 を出したあと、報告してくれた方への返信を書くために、コードを確かめました。
分割しているのは、取り込みの処理の中だけでした。すでにテーブルにある行を、あとから読み直して分割する処理はありません。1.19.0 より前に落ちた大きな文書は、未埋め込みのまま残ります。
しかも、1.18.0 の仕組みがここで効いてきます。too_large が記録された行は、埋め込みの対象から外れています。アップグレードしても、誰も再挑戦しません。一覧には、失敗の理由が出たままです。
報告してくれた方には、1.19.0 で直ったことと、前に落ちた文書は一度削除して入れ直す必要があることを伝えました。取り込むときに分割され、断片ごとに埋め込まれます。正常に埋め込めていた文書は、触る必要がありません。
新しい機能が入っても、古いデータは古いまま
9/30 今のコードで、分割されない入り方を数えた
この記事を書くために、現在の 1.20.5 のコードを読み直しました。
文書が作られる入り口は4つありました。
- ファイルのアップロード:
import_from_file()からcreate_document()を呼ぶ。ここだけが分割する - 管理画面の入力欄:
create()を直接呼ぶ。分割しない - 既存エントリの編集:
update()を呼ぶ。分割しない - 設定のエクスポートファイルの読み込み:
create()を直接呼ぶ。分割しない
入力欄に 3 万字を超える文章を直接貼る人は多くないと思います。ただ、編集は別です。1.18.0 では、編集すると失敗の記録が消えて、次の埋め込みで再挑戦するようにしています。編集しても分割はされないので、大きな文書なら、同じ理由でまた落ちます。
設定の読み込みも同じです。1.19.0 より前に書き出した設定ファイルには、分割されていない大きな文書がそのまま入っています。それを別のサイトで読み込むと、分割されないまま登録されます。
入り口が4つあるのに、直したのは1つだった
いまのところ、大きな文書を正しく入れる方法は、ファイルとしてアップロードすることだけです。残りの3つの入り口にも create_document() を通すかどうかは、まだ決めていません。
ふだんはraplsworks.comで、WordPressプラグイン開発やClaude Codeまわりのことを書いています。
https://raplsworks.com/