開発book-knowledge-base

やったこと

/yomitoku-kindle スラッシュコマンドで Kindle 本 5冊を一気に取り込んだ。 撮影 → OCR → Turso DB 投入 → restructure(チャンクをセクションに統合)まで、本来直列だったパイプラインを途中で並列に組み替えながら回した。 14:13 に開始、15:18 に全工程が落ち着いて、約 1時間 5分で 5冊分の知識が book-knowledge-base DB に入った。

対象は 5冊:

  • 不動産投資の会計と税務(B0CR9K4L5M)
  • 【図解】消費税完全マスター
  • 地政学が最強の教養である
  • 世界史と地理
  • 世界史を大きく動かした植物

1冊目で詰まる: Kindle Cloud Reader が読めない本だった

1冊目の B0CR9K4L5M(不動産投資の会計と税務)を開きにいったら、Kindle Cloud Reader が「Kindle App is Required」と返してきた。 ブラウザでは読めない本だった。 kindle_library テーブルにはヒットしていたので「持っている本」のはずだったが、実際の Kindle ライブラリには無くて誤検知で残っていた可能性が高い。

この本だけスキップして 2冊目に進めた。 kindle_library の整合性確認は後日の課題として記事末尾の TODO に積んだ。

Kindle Cloud Reader の URL クセを発見

2冊目に切り替えるとき、最初は https://read.amazon.co.jp/?asin=XXXX だけで navigate していた。 すると Kindle CR がライブラリ画面にリダイレクトしてしまい、本が開かない。

ユーザーから「&ref_=kwl_kr_iv_rec_2 こんな感じみたいですけどね」と URL の実例をもらって、ref_ パラメータを足したら通った。

その場で .claude/commands/yomitoku-kindle.md の Step 4 を書き換え、navigate URL に &ref_=kwl_kr_iv_rec_2 を必ず付けるルールに固定した。 こういう「次に同じ罠にハマる未来の自分」向けのメモは、気づいた瞬間にスラッシュコマンド側に反映するのが結局いちばん早い。

拡張機能 content.js の一時的な文字化け

Chrome 拡張のフローティングパネルが書名を「文字化け」、現在位置を「位置: 不明」と返してきた。 content.js のセレクタが Kindle CR 側の DOM 変更を踏んだ気配がある。

ただ、撮影フローは内部的に ASIN だけで回るので、書名取得が壊れていても撮影自体は走る。 原因調査は別件として切り離し、撮影開始ボタンを押した。

通常モードの撮影は「Tabs cannot be edited」エラーが連鎖しそうな気配だったので、start-detached(別ウィンドウで開始)に切り替えて回避した。 ここもスラッシュコマンドの Step 5 を start-detached ベースに書き換えた。

3冊目に切り替えたら書名も位置も正常に取れていたので、文字化けは一時的な症状だった。 ただし「症状が出ても撮影は ASIN だけで進める」「start-detached を使う」というワークアラウンドは残しておいた方が安全なので、コマンド側のルールはそのまま据え置いた。

Step 2(拡張リロード)を「不要なら省略」に書き換え

毎回の Step 2 で chrome://extensions/ を開いて拡張をリロードしていたが、これが地味に効いてくる。 バッチで 5冊回すなら毎回やる必要はなくて、content.js を編集した直後・不具合時だけでいい。

これもスラッシュコマンドを書き換え、Step 2 を「デフォルト省略・拡張を編集した直後 / 不具合時のみ実行」に変えた。 バッチ実行時のリスク(Kindle CR タブ周りの状態が乱れる)も注記として残した。

OCR 待ち時間で次の本の撮影を始められると気づいた

2冊目の撮影が終わって OCR を回している間、何気なく「次の本も並行で撮影できますか」とユーザーから聞かれた。 撮影スクリプトは1回叩けばバックグラウンドで done.json の出現を監視するだけなので、サブエージェントを使わずに並列化できることに気づいた。

ここから一気に流れが変わった。

  • 撮影が回っている裏で OCR を回す
  • OCR が回っている裏で次の本の撮影を始める
  • DB 投入も並列で走らせる

直列だと「1冊あたり 15〜20 分 × 5冊 = 1.5〜2時間」と最初に見積もっていたが、ここで並列に組み替えてからは時計の進みより仕事の進みが速くなった。

4冊目で「Tabs cannot be edited」を踏んだ

並列化に乗ってきたところで、4冊目(B0CK3R2FJ1 世界史と地理)が 74/241 枚で Tabs cannot be edited エラーで止まった。

原因は「4冊目の撮影中に、5冊目の準備のため new_page を叩いた」こと。 Chrome 拡張側がタブの編集権を取り合って衝突したらしい。

ルールを単純化して「撮影中の本が完走するまで、次の本の new_page は仕込まない」に変えた。 4冊目のパネル状態を一度リロードでリセットし、撮影をリトライしたら 7分で 241/241 まで完走した。

restructure をサブエージェントで非同期並列化

/restructure-book は、撮影してきたチャンクを読み込んでセクション定義を組み直す重い処理で、1冊あたり 5〜9分かかる。 最初は直列で「DB 投入 → restructure → 次の本」と回そうとしていたが、これだと並列化のうまみが消える。

そこで restructure をサブエージェントに切り出して非同期に並列起動するように切り替えた。

2冊目 DB 投入完了 → restructure サブエージェント起動(非同期)
3冊目 撮影/OCR/DB 投入を進める
4冊目 DB 投入完了 → restructure サブエージェント起動(非同期)
...

メインのターンは撮影/OCR/DB 投入を進めながら、restructure は完了通知が届くまで放っておく構成にした。 スラッシュコマンドの Step 16 も「サブエージェントで非同期 restructure」に書き換えた。

結果、restructure の所要時間(5〜9分 × 4本 = 20〜36分相当)が、撮影/OCR の時間に重なって消えた。

結果

5冊分の chunk 数(restructure 後):

  • 2冊目 消費税: 111 → 89 chunks(9分16秒)
  • 3冊目 地政学: 131 → 31 chunks(5分12秒)
  • 4冊目 世界史と地理: 121 → 53 chunks(8分5秒)
  • 5冊目 植物世界史: 73 → 19 chunks(completion 通知より先に DB 上で commit 済みを確認した)

検索テストも通った。2冊目は「消費税」「仕入税額」「インボイス」「免税事業者」がそれぞれ 20件ずつヒット。 3冊目は「地政学」「ロシア」「海洋国家」がヒット。 「中国」は 2文字で FTS の trigram 制約に当たって 0件、これは別件の検索仕様の問題として認識した。

学び

  • 撮影スクリプトはバックグラウンド監視型なので並列化できる。OCR 待ち時間に次の本の撮影を仕込めるかどうかで、5冊取り込みの所要時間が倍違う
  • 「撮影中の本が完走するまで、次の本の new_page は仕込まない」。Chrome 拡張のタブ編集権の衝突は、ルールを単純化して回避するのが早い
  • 重い後処理はサブエージェントに切り出して非同期化する。restructure をメインターンに置くと、撮影/OCR の並列性が殺される
  • スラッシュコマンドはその場で書き換えるref_=kwl_kr_iv_rec_2 パラメータ、Step 2 の省略ルール、start-detached への切替、Step 16 のサブエージェント化と、気づいた罠は全部コマンド側に固定した

残った TODO

  • kindle_library の整合性確認: B0CR9K4L5M のように「テーブルにあるが実際は Kindle Cloud Reader で読めない本」を洗い出す
  • 拡張機能 content.js の書名取得が時々文字化けする件の調査
  • 2冊目 第8章 p.91-92 が OCR で欠落している件の確認
  • FTS の trigram 制約で 2文字キーワード(「中国」等)が 0件になる件の対処