開発book-knowledge-base

やったこと

前日に計画書を書き上げた book-knowledge-base の奥付抽出パイプラインを、Phase 0 と Phase 1 まで走らせた。最初に haiku で10並列回したら9/10が誤読で全部やり直しになり、Opus に切り替えて末尾10ページに広げたら8/10が完璧、ISBN完全一致 7/7 まで一気に跳ねた。そのまま47件→337件にスケールしようとしたら大量エラーが出て、原因は Dropbox の「オンライン専用ファイル」だった。同期解除はサイズ的に諦めて、ローカル化済みの46件だけで Phase 2 を進め、ロングセラー検出レポートまで作って終業した。

Phase 0: スキーマだけは前日のうちに作ってあった

book_colophons テーブルを Turso 本番に流すスクリプトは前日仕込んであったので、uv run python scripts/apply_colophon_schema.py を1発叩いて終了。amazon_metadatafile_no で 1:1 紐付けされた状態が DB 側にできた。

Phase 1 v1: haiku で10並列、結果は精度1/10

計画書通り、末尾5ページを dpi=200 でPNG化して、Claude Code のサブエージェントを haiku で10並列起動した。10件返ってきた JSON を眺めた瞬間に「これは使えない」と分かった。

  • No338: 実体は「会社法 第2版(田中亘)」なのに "会計学講義" という別本のタイトルで返ってきた
  • No366: 「渋谷ではたらく社長の告白」が "渡谷ではなろく社長の古日" に化けた
  • No425: 「広重TOKYO」が "広辞TOKYO" に化けた
  • No582: 「ディズニーCEOが実践する10の原則」が "100の原則" で数字の桁を取り違えた

10件のうち1件しか正しい書名を拾えなかった。日本語奥付の小さい文字や漢字の細部が、haiku のVisionでは崩れる。書誌情報を DB に流し込む用途には精度が足りない。一旦結果を colophon_results.jsonl から退避して、モデルとパラメータを見直すことにした。

Phase 1 v2: Opus + 末尾10ページで再実行

計画書では「haiku で 10並列、追加API課金ゼロ」が売りだったが、精度が出ない以上、Opus に切り替えるしかない。Claude Code のサブエージェントは Opus でも10並列で走らせられる。末尾も5ページから10ページに広げて、巻末のページ構成のばらつきを吸収した。

10件で再実行したら、精度が逆転した。

  • success 8/10、no_colophon 2/10
  • ISBN を取得できた7冊は ISBN完全一致 7/7
  • No338 の「会社法 第2版(田中亘・東京大学出版会)」は、版・刷・初版発行日・著者まで全部正しく取れた
  • 数字の桁誤りも消えた(582、649 ともに完璧)

末尾10ページに広げたぶん画像コストは増えたが、1冊あたり Opus で約30k〜44kトークン、duration 20〜40秒。10並列で1バッチ約40秒。許容範囲だった。

副産物: 奥付抽出は書誌情報の正確性チェックにもなる

Opus が「実体PDFは『会社法 第2版(田中亘・東京大学出版会)』ISBN: 9784130323093」と返してきたので、Amazon側に登録してある書名を確認したら、そっちは「会社法〔第2版〕(伊藤靖史ほか・有斐閣ストゥディア)ISBN: 978-4641150843」だった。完全に別の本が同じ file_no で登録されていた

No416「財務3表一体理解法」も同じパターンで、Amazon側の登録は新書版なのに実体PDFは2007年初版だった。

奥付抽出は当初「Amazon メタデータを補完する」目的で組んだが、走らせてみたら 「Amazon メタデータの誤りを検出する」 という副作用が出てきた。蔵書管理としてはむしろこっちの価値が大きい。

47件 → 337件にスケールしようとして崩壊する

Phase 1 が安定したので、対象を ★3.5以上 × レビュー50件以上 = 347冊に広げて Phase 2 を走らせた。既処理10件を除いて 337冊。Opus で30バッチくらい回せば1時間以内で終わる見込みだった。

画像化スクリプトを叩いた瞬間、エラーが連続で吐き出された。

[ERR] No101: [Errno 22] Invalid argument
[ERR] No102: [Errno 22] Invalid argument
[ERR] No103: [Errno 22] Invalid argument
...

fitz.open()shutil.copyfile()Errno 22 で落ちる。337件のうち約290件が空ディレクトリのまま終わった。

原因: Dropbox Files On-Demand(オンライン専用ファイル)

Dropbox の「Files On-Demand」設定で、書籍PDFの大半が クラウドにのみ実体があり、ローカルにはプレースホルダしかない 状態になっていた。エクスプローラーでは ☁️ アイコンが付いていて、ダブルクリックすれば自動でダウンロードが走るが、Python の fitz.open() はその「仮想ファイル」を開けない。shutil.copyfile() で一時コピーを試みても同じく Errno 22

解決策は2つあった。

  1. Dropbox フォルダ全体を右クリック →「オフラインアクセスを許可」を選ぶ。957件、約60GBがダウンロードされる
  2. 必要なファイルだけ事前にダウンロードしておく

(1) は確実だが、60GB のダウンロードを開始してSSDの空きと回線を圧迫する。書籍PDF全件をローカルに常駐させる必要がそもそもない(普段はオンラインで開けば足りている)。(2) はバッチごとに attrib +P を叩く実装を組む必要があり、その実装と検証で半日溶ける気配がした。

判断に詰まって、結局**「今すでにローカル化されているぶんだけで進める」**に倒した。Phase 1 で処理済みの10件と、既にローカルにあった46件、合わせて56件。それ以上は明日以降に回す。

画像化スクリプトに細かい修正を3件入れた

Dropbox 問題の調査中に、ついでに以下の修正を入れた。後続で再実行するときの足元を固める意味で。

  • Windows cp932 のコンソール文字化け: PYTHONUTF8=1sys.stdout.reconfigure(encoding='utf-8') を入れて、書名の漢字がログで化けないようにした
  • Dropbox オンライン専用 fallback: ローカルにファイルがない場合は、shutil.copyfile() で一時コピーを試みて、それも失敗したらスキップしてエラーログに残すようにした(事故った時に何件落ちたかを後追いできる)
  • 末尾ページ数のパラメタライズ: --last-n 10 で末尾何ページを画像化するかを CLI 引数で渡せるようにした

ローカル化済み 46件 で Phase 2 を完走させる

ローカルにあった46件を Opus で抽出した。10並列 × 5バッチで約8分。結果は success 44件、no_colophon 2件。Phase 1 と合わせて56件が book_colophons に格納された。

これで Turso の DB を JOIN amazon_metadata USING (file_no) で叩くと、書名・初版発行日・累計刷数・ISBN・★評価・レビュー数が同じ行に並ぶようになった。次のステップとして「ロングセラーを検出する」が初めて意味を持つ。

ロングセラー検出レポートを作る

colophon-longseller-report.md に、56冊から拾えたロングセラーパターンを3軸でまとめた。

短期重版(初版から180日以内に第2刷以上)— 強いニーズシグナル

Noタイトル経過日数累計刷
678〈2時間で丸わかり〉インボイスと消費税の基本40日第3刷
763その節税が会社を殺す64日第4刷
416財務3表一体理解法67日第10刷

「インボイスと消費税の基本」は出版から1ヶ月強で第3刷。「その節税が会社を殺す」は2ヶ月で第4刷。実務書はテーマが「待たれていた」かどうかで重版速度が桁で変わる。財務3表は67日で第10刷、これは桁外れだった。

多刷ロングセラー(第5刷以上)— 安定した長期ニーズ

Noタイトル累計刷経過
493金持ち父さん貧乏父さん第92刷12年
394夜と霧第37刷51.6年
723具体と抽象第22刷8.3年
464法人税申告書のしくみとポイントがわかる本第15刷8.6年
845DIE WITH ZERO第10刷1.2年

「夜と霧」が新装版で第37刷、約50年走り続けている。「金持ち父さん」も12年で第92刷。実務書だと「法人税申告書のしくみとポイントがわかる本」が8.6年で第15刷で、税務系の実務書ロングセラーの代表。「DIE WITH ZERO」は出てから1年で10刷、ここ最近の最大ヒット級だと数字で見える。

コンテンツ化最有力候補

★4.0以上 × レビュー1000以上 × 第5刷以上で絞ると、6冊が浮上した。これが「自分が要約・図解化して出すコンテンツの優先順位」の元データになる。

明日に積み残し

  • Dropbox の「オフラインアクセスを許可」を 00連番フォルダに対して実行(60GB のダウンロード待ち)
  • 残り 291冊の画像化を再実行
  • 実務系 110冊(data/phase2_practical.csv)だけ先に Opus 抽出
  • 全件処理後、ISBN不一致を検出して amazon_metadata 修正候補を出力する

学び

  • Vision の精度はモデルで桁が変わる: haiku 1/10 → Opus 8/10 は誤差ではなく断絶。日本語の小さい文字(奥付)は Opus 一択
  • 末尾5ページは攻めすぎ: 10ページに広げただけで no_colophon の取りこぼしが激減した。本のページ構成(謝辞、参考文献、索引、奥付、著者プロフィール)は末尾に1〜10ページのバラつきがある
  • Dropbox Files On-Demand はバッチ処理と相性が悪い: 「ローカルにある前提」のスクリプトを書くと、290件が静かに空ディレクトリになって終わる。事前に hydrate 状態を確認するか、エラー時に明示的にスキップログを残す実装が必須
  • Amazon メタデータは信用しすぎない: 書名・著者が別本と入れ替わって登録されているケースが56件中2件あった。蔵書数 957件で換算すると30件以上は誤登録の可能性がある
  • 計画書通りに走るとは限らない: 前日の計画書では「haiku で追加課金ゼロ」を売りにしていたが、実走で精度が足りず、Opus に切り替えた。サブエージェント並列の枠内なので Claude Code サブスクリプション内で済んだのは救い

形容詞ではなく数字で示せる学びが多い1日だった。「精度が悪かった」ではなく「10件中9件が誤読で書名が別本に化けた」、「Dropbox で詰まった」ではなく「337件のうち290件が空ディレクトリになった」と書けるレベルで現象が見えた。