蔵書データベースの運用整備と、.envの正本を1Passwordに置く判断

開発book-knowledge-base

朝いちばんの用件は取り込みだった。電子書籍リーダーで2冊を開いたまま、そのまま蔵書データベースに入れてほしい、と頼んだ。夕方に手を止めたとき、片付いていたのは本の取り込みではなく、認証情報の置き場所とリポジトリに溜まった5GBのほうだった。

開いたままの2冊を取り込む

戦略理論の1冊と、対人コミュニケーションの1冊。どちらもクラウドリーダー側で開けたので、拡張機能から1ページずつ撮っていくルートで進めさせた。

綴じ方向は実測で確かめさせた。左矢印の aria-label が「次のページ」になっていたので、右綴じで確定した。1冊目は173ページある。撮影は60秒で13ページのペースで進んだ。GPUに載せたOCRは4分ほどの見込みで、173枚のマークダウンになった。図は42枚あった。R2に上げ、表紙の画像URLを取って蔵書側のライブラリと書誌情報に紐づけた。全文検索も通り、143チャンクが引ける状態になった。目次を頼りに構造を組み直してもらうと、62チャンクに畳まれた。

2冊目の検索テストで「共感」が0件を返した。原因は取り込みではなく検索側にあった。全文検索が3文字単位で索引を張っているので、2文字の語はそもそも当たらない。3文字以上で引き直したら普通に出てきた。

ブラウザで表示を見たら、本文に「く」と「>」が延々と混ざっていた。1冊目だけで165行ずつ、合わせて330行。除去させると本文が連続した。索引はトリガーで自動同期される作りだったので、そのまま追随した。

devサーバーを立て、3ペイン構成が崩れていないこと、図が配信されて実際に描画されることまで画面で確かめた。取り込みが通ったという報告と、画面に出ているかどうかは別の話である。

クローンすれば別のPCでも開くのか

ここで気になったことを聞いた。レプリカはクラウドと同期している。ということは、このリポジトリを別のPCでクローンすれば、本棚はそのまま開くのではないか。

調べさせると、図の配信はR2経由に完全移行していて、ローカルのデータディレクトリを一切見ない実装になっていた。本文も図もクラウド側にある。それでも答えは「クローンだけでは動かない」だった。認証情報ファイルを1つだけ、手で持っていく必要がある。

認証情報の正本をどこに置くか

そこで思いついたのが、その1ファイルをローカルから消して1Passwordに預ける形だった。作業を実行するときは1Password経由で参照すればいいし、別のPCでもアプリを入れてログインすれば同じように使える。バックアップとしても筋がいい。

ただ、実行のたびに1Passwordから注入する形は現実的でなかった。理由は2つある。Windowsでは注入コマンドを走らせるたびに承認ダイアログが出て、1回あたり14秒ほど待たされる。もう1つは参照箇所の多さで、このファイルを見にいくコードは蔵書リポジトリだけで93、Git_repo 全体では243に及んでいた。全部を注入経由へ書き換える改修は割に合わない。

ローカルに置くか、実行のたびに取りにいくか。その二択ではない形に落ち着いた。正本は1Passwordに置き、ローカルのファイルは「そこから書き出した使い捨ての派生物」として扱う。書き出しは環境を立ち上げるときの1回で済み、コードは1行も変えなくていい。ローカルのファイルが消えても事故ではなく、取り直せばいい状態になる。

この方針と、別のPCで立ち上げるときの手順をルールとしてドキュメントにまとめさせた。設定を同期しているprivateリポジトリへpushする前にコミットの学習ゲートが出たが、今日はスキップして通した。

353バイトの正体

ドキュメントを書かせている途中で、サイズが噛み合わなくなった。1Password側に入っているファイルは353バイト、ローカルは4,826バイトある。7%しかない。「このまま書き出すとローカルが壊れます」という報告が上がってきた。

1Passwordの画面を見て、すぐ違うと分かった。353バイトは別のリポジトリのもので、4,826バイトのほうは別のアイテムとしてとっくに登録されている。一覧を見ずに1件だけを見て判断したための誤診だった。しかも登録済みのほうは最終更新が7/29で、ローカルの7/16より新しい。

では353バイトは何なのか。どのコードから読まれているのかを調べ直させたところ、どこからも読まれていない残骸だった。日付は4月末。共有用のリポジトリを切り出す前の名残である。切り出した痕跡は相対パスにも残っていて、DB接続もアップロード処理も設定ファイルも、そろってリポジトリの外へ出るパスを書いていた。もともと1つのリポジトリでやっていたものを、他からも参照するようになって外へ出した、という記憶と符合する。

残骸と分かった以上は消してもよかったが、置いておくことにした。コピーがあって困ることはない。

書き出しが本当に通るかは、中身を開かずサイズだけ比較して検証してもらった。4,826バイトで完全に一致した。承認ダイアログも出なかった。

出力先については指摘を1つ入れた。方針そのものは全リポジトリに効くのでユーザーレベルに置いた、という判断は分かる。ただそれだと、このリポジトリを別のPCで立ち上げる手順がリポジトリ側から抜け落ちる。プロジェクト側のドキュメントにも追記させ、単体のクローンで完結するようにした。ついでに未コミットの87件を選別させ、慣例に合うものだけを39ファイル、3つのコミットに分けてpushした。

裁断してスキャンしたPDFを流し込む

昼から、スキャンしたPDFのリネームに移った。6件のうち3件は重複している。中身を突き合わせてもらうと、28ページ版が完全版で、52ページ版は同じ内容に白紙の裏面が24ページ付いているだけだった。64ページ版の3件は、濃度がわずかに違うだけの同一物だった。1件だけリネームが busy で弾かれたのは、PDFビューアがそのファイルを開いていたからである。

途中で新しいスキャンが1本増えた。226ページの本を撮り直したものである。並べて比べると、先に撮ったほうは2値化で文字が潰れており、あとから撮ったほうが正常なグレースケールだった。新しいほうを採用し、3冊にリネームして、残った重複分はごみ箱へ送った。

そのまま3冊をOCRに流した。合計318ページを、1ページあたり2秒ほどで処理していく。取り込んで再構造化まで通し、219チャンクあった1冊は34セクションに畳んだ。夕方にもう1本スキャンが出てきたので、こちらは単体でリネームだけ回した。

表示を見ると、節の頭に「B」「2」といった柱の断片が残っている。同時に、一覧の「整」バッジが付いていないことにも気づいた。調べさせたら、これは図表クリーンアップ工程の完了フラグだった。取り込みパイプラインの真ん中の工程を、丸ごと飛ばしていたわけである。356冊中11冊しか付いていない。

全冊にかけると数時間規模で費用も変わると言うので、範囲を今回の3冊に限って回した。図版285枚をラベル付きのコンタクトシートにまとめ、並列で評価させると182枚が装飾だった。装飾はタグごと落とし、情報を持つ図は残す。表紙と扉の画像は、すでに整えた本でも残している例があったのでそのままにした。朝の2冊にも同じ処理をかけた。片方は章扉や人物のシルエット、矢印など13枚が装飾で、そのうえグラフの軸ラベルが本文に散らばっていた。凡例と年号まで含めて除去対象を組み直させてから本番に当てた。

書棚の一覧を開くと、15ページ目が1ページ目に飛ばされた。評価の付いていない本を既定のフィルタで落とす仕様のためで、条件を変えて確かめ直したら3冊とも正しく出ていた。作業に使った可視化用のJPGは、既存の本でも消すのが慣行だったので同じように削除した。3冊で448MBあった。ディレクトリごと消そうとして再帰削除のガードに止められ、非再帰で消し直した。

リポジトリが5GBあった

一段落したところで、変換時の残骸が溜まっていたことを思い出した。データディレクトリの中はほとんどログだろうから全部消していいか、と聞いた。そうではなかった。

約5.2GBのうち、ほぼ全部は捨ててよかった。ただし3つだけ、消すと画面が壊れるものが紛れていた。しかも全部 .gitignore 済みで、gitには1ファイルも入っていない。消したら戻らない。聞いてよかった。

内訳を出させて意外だったのは、マークダウンの軽さだった。実バイトで323MBしかない。ところがディスク占有は671MBある。96,241個の小さいファイルなので、NTFSのクラスタ余りが倍近くまで膨らませていた。重かったのは電子書籍のキャプチャ3.5GB、講義動画の書き起こしとその音声900MB、奥付画像450MBのほうである。

キャプチャと奥付画像は自分で消した。消している最中も測らせていたので、「57→40→5と減っています」という報告が返ってきた。

これでデータディレクトリは約450MBまで落ちた。ところがその外に、まだ1.5GBがあった。6月の作業用にコピーしたPDFが10本で、1本445MBのものまで残っている。ディレクトリごと消すことにして、削除は自分の手でやった。3つとも .gitignore に足し、以後は追跡から外れるようにした。

もう1冊をスキルにする前に

最後にもう1冊、朝取り込んだ対人コミュニケーションの本の中身をDBから取り出させた。スキルにしてほしい、と言いかけて途中で引っかかった。これは人間が人に対して使う技術である。だとするとインストール先はAIではなく自分の側で、必要なのはスキルよりも「身についているか確かめるテスト」ではないか。

読ませたところ、31項目からなる実践書だった。そのうち3分の1ほどは、AIが私に質問するときの規範としてそのまま効くという整理が返ってきた。それなら両方作ればいい。AI側の対話規範を1本、自分が受ける場面判定のテストを1本、続けて書かせた。テストは31項目に1問ずつで31問になった。

テストの器は、コミット前の学習ゲートに使っているクイズの仕組みを流用できた。もともとgitの差分に紐づくゲートだが、出題側が振り返り用のソース指定を受け付けるので、書籍由来の設問も同じ履歴DBに乗せられる。間隔反復にそのまま流せる。

作ったところで手が止まった。出題はまだ一度も叩いていない。今日はここまでにして、テストを受けるのは翌日に回した。8/1を期日にしたタスクとして登録し、閉じた。

学んだこと

  • 「クローンすれば動く」かどうかは、データがどこにあるかではなく、認証情報がどこから読まれているかで決まる。図もDBもクラウドに移し終えていたのに、ファイル1つで足止めされた。
  • 認証情報の正本を移すとき、実行のたびに注入する形は参照箇所の数で詰む。書き出しは1回、以降はファイルから読む形なら、コードを1行も変えずに正本だけ動かせる。
  • サイズが噛み合わないときは、まず「同じ名前の別物ではないか」を疑う。一覧を見ずに1件だけを見て判断すると誤診する。
  • 「誰が読んでいるか」を調べ直すと残骸が見つかる。使われていないファイルは、たいてい構造が変わった時点の日付を持っている。
  • 「全部いらない」と思っているディレクトリにも、ランタイムが読むものは混ざる。git管理外なら、消したときに戻す手段がない。
  • パイプラインの工程を飛ばしていたことには、画面のバッジで気づけた。356冊中11冊という数字を見るかぎり、取りこぼしは今回だけではないのだろう。