コンテンツにスキップ

自作ナレッジ基盤の構築

Author:Kazukichi
  • 自分に纏わる情報を一箇所に集約し、横断的に活用するための個人用のナレッジ基盤を作っている
  • 過去に書いた記事や資料を検索し、稼働記録や給与明細のような数値データをダッシュボードで可視化し、新しい記事や作品を作るときの材料として引く、という3つの用途がある
  • 前身となる自作のライフログ管理・分析基盤を、AIエージェントが中心になって動く前提で作り直し、扱う対象も数値データから文章まで広げた
  • 2023年春頃から運用しており、データソースはSwitchBotの消費電力・気温・湿度 / ポモドーロタイマーの記録 / 給与明細のPDFの3種類だった
  • SQLAlchemyのモデルとAlembicのマイグレーションでスキーマを定義し、Pythonで書いたインポート用のクラスでパースしてSQLiteに入れていた
  • 可視化はDocker Composeで起動するMetabase上でSQLを書いて行っていた
  • 全てのデータソースを単一のスキーマに正規化してSQLiteへ入れる構造だったため、記事や議事録のような文章の置き場所がなかった
  • 文章はObsidianで別管理していたが、UIが個人的にしっくり来ず、特定のエディタに依存する形になっていた
  • データソースを1つ増やすだけでも、モデルの定義 / マイグレーションの生成 / インポート用クラスの追加という定型作業が必要で、拡張性に乏しかった
  • SwitchBotのデータは整備が大変な割に活用が難しく、大抵の場合はリアルタイムの確認だけで事足りたため廃止した
  • 可視化の定義がMetabase側のデータベースに閉じていて、Git管理の外に出てしまっていた
  • Docker Composeによる常時起動が必要な点も、個人用の基盤としては重かった
  • そもそもAIエージェントから使う想定がなく、蓄積したデータを読むにはSQLを書く必要があった
  • 過去の資産を横断的に活用できるようにしたかった
    • 検索して確認したり、抜き出して新しい資料として再構成したりする
    • 資料同士の関連付けから新しいアイデアを得る
    • データの蓄積から傾向を得て意思決定の材料にする
  • この関連付けから着想を得るという狙いは、メモ1枚の価値が他のメモとのリンクで決まるとするルーマンのZettelkastenの考え方に近い
  • 集約するだけで終わらせず、そこから知識や知恵を引き出したいという方向性は、データが情報・知識・知恵へと段を上がるDIKWモデルの語彙でも整理できる
  • 作品制作 / 日々の検証 / 手順書の作成において、文章の整理という非本質的な部分にかかる工数を削り、デリバリーを速くしたかった
  • これからのソフトウェアエンジニアリングは、人が1つずつ実装するのではなく、環境を整えてAIエージェントに任せる形へ移っていくと見ている
  • その環境を自分の手元に持つために、ハーネスエンジニアリングやループエンジニアリングの実験場にするという狙いもある
  • 価値の源泉は、自分が直接触れてきたもの・生み出してきたものの集約であることだと考えている
  • 他人のソースを勝手に拾い集めて育っていくwikiだとしたら、LLMの上にもう一段薄いレイヤーを重ねているだけになる
  • そうしたwikiの中身はLLM自身がRAG(Retrieval-Augmented Generation)なしでもある程度出せてしまうため、情報としての新規性がほぼゼロになる
  • 境界線はソースが他人かどうかより、そこに自分自身の判断・取捨選択が乗っているかどうかに置いている
    • 他人の記事でも、読んで自分の問題意識とどう繋がるかを見極めて残すなら、一次的な判断が乗った価値になる
    • 逆にLLMが自律的に拾ってくる部分が増えるほど、価値の薄い構造に近づいていく
  • knowledge/{source}/ は過去に書いた記事・資料をそのままマークダウン化して置く場所で、過去資産の原本という位置付けから原則として改変しない
    • マークダウンのファイルをGitリポジトリに直接置いているため、特定のエディタに依存しない
  • drafts/ は作成途中の記事を置く場所で、 .gitignore の対象にしている
    • 進捗度合いで sketch/ (草案) / wip/ (作成中) / ready/ (完成・未公開)に分け、公開後に knowledge/ へ移す
  • data/{source}.csv は knowledge/ 内の生データから抽出・集計した分析用データで、ダッシュボードへの描画対象となる
  • scripts/ には条件分岐として書き下せる処理をCLIスクリプトとして置き、 .claude/skills/ には書き下せない読解・視覚判断を要する定型作業を置く
  • dashboard/ は Streamlit で書いた可視化の置き場所になる
  • .github/ には日次のissueを作るGitHub Actionsのワークフローを置く
  • 定期的に更新されるソースは取得をスクリプト化し、更新頻度が低いものはAIエージェントにオンデマンドに変換してもらっている
  • 正規化をやめてソースごとの形のまま置いているため、ソースを足すときもスキーマの定義やマイグレーションは要らず、取得スクリプトの追加と、後述するskillへの1行追記で済む
  • 前身で扱っていたSwitchBotのセンサ系は、前述した理由から引き継いでいない
ソース 内容 取り込み方
mtg うゆぷんの月次報告 定期取得
takada-semi 高田ゼミの記事 定期取得
note note.comの記事 定期取得
assets うゆぷんのイベント資料 定期取得
github コントリビュート数 定期取得
youtube 主担当として制作した動画 定期取得
spotify 年間トップ曲 年1回の取得
qiita / hatena 技術記事 都度変換
slides / profile / misc 手元のスライドや職務経歴書等 都度変換
payslip 給与明細のPDFと作業ログ 都度変換
pomodoro ポモドーロの稼働記録 都度貼り付け
daily 日次のTODOと振り返り GitHub Actionsで自動生成
  • 記事・資料として置いているマークダウンファイルは、800件弱ある
  • data/ 配下のうち決定論的な抽出は、生成元1件につき1行として実行のたびに全件再生成するため、何度実行しても同じ結果になる
  • 読解を伴う抽出は既存ファイルに追記し、チェックポイントファイルで差分管理することで、読解のコストを払い直さずに済む
  • ロジックはCLIから単体実行できるスクリプトを正として整備し、どれも uv run scripts/*.py の形で叩けるようにしている
  • 名前で役割が分かるように、接頭辞を揃えている
接頭辞 役割
fetch_*.py 外部ソースからの取得
extract_*.py / normalize_*.py 取得したファイルから分析用データへの変換
convert_*.py / format_*.py / unlock_*.py マークダウン化や整形、ロック解除といった決定論的な後処理
append_*.py / add_*.py ポモドーロの記録やGitHub Projectへの追記
  • 外部APIを叩くものは差分実行にしており、 fetch_github_contributions.py は前回取得済みの最新日以降のみ、 fetch_spotify_genres.py は未取得のアーティストのみを取る
  • MarkItDown によるPDF/PPTX変換では文字化けや制御文字が決まった形で混ざるため、 text_cleanup.py で機械的に除去できる分だけを落としている
  • format_bullet_article.py だけは knowledge/ の既存資産ではなく drafts/ で執筆中の記事にかけるもので、太字の除去や全角記号の半角化のように判読が不要な整形を担う
  • skillは現在8つで、どういう言い回しの依頼で発火するかと、読むときの判断の基準を自然言語で書いている
skill 役割
route-knowledge-source 曖昧な依頼やファイル追加から、実行すべきスクリプトとその順序を判断する
sync-all-sources 定期取得のソースをまとめて更新し、ソースごとの扱いと順序を振り分ける
fix-markitdown-conversion PDFやPPTX由来のマークダウンの崩れを、元ファイルの見た目と比べて直す
bullet-style-article-proofreading 執筆中の箇条書き記事の構成や表記を校正する
revise-proofreading-tools 執筆で得た校正の知見を、スクリプトと校正skillに振り分けて反映する
extract-mtg-experiences 月次報告の自由記述から美術館 / ライブ / 映画 / 読書の体験を抽出する
add-project-task やりたいことをGitHub Projectへ追加し、重複の確認とカテゴリの判定を行う
record-pomodoro-sessions 貼り付けた時刻レンジをポモドーロの記録として追記する
  • fix-markitdown-conversion では、文字コードの誤マッピングと制御文字の除去を決定論的な処理としてスクリプトに置き、文がどこで分断されたかの判断は元ファイルを見ないと決まらないためskillに置いている
  • extract-mtg-experiences では、体験がツアー名や展覧会名といった固有名詞だけで書かれていて単純なキーワード一致では拾えないため、カテゴリの定義と判断に迷ったときの扱いをskillに書き下している
  • revise-proofreading-tools は校正の道具そのものを直すためのskillで、記事を1本書き終えるたびに、その執筆で実際に出た指摘を機械的に直せるものと読解が要るものに仕分けて、前者をスクリプトへ、後者を bullet-style-article-proofreading のルールへ反映している
    • ルールが単調に増え続けないよう、追加と同じ回のうちに既存ルールの圧縮まで行う
  • 毎朝5時にGitHub ActionsがTODOのテンプレートを daily ラベル付きのissueとして作成する
  • issueのコメントで /carryover とすると、前日のissueから、毎日のルーティンを除く未完了項目を引き継ぐ
  • 一日の中でTODOの消化状況や感情の動きをコメントに書き足していく
  • 一日の終わりに /lookback とすると、Claude Codeによる振り返りが投稿され、 knowledge/daily/{year}/{month}/{day}.md として保存されてissueがcloseされる
  • この日次ログは500件近く溜まってナレッジの中で最も件数の多いソースになっているが、事後的な活用はまだほとんどできていない
  • この仕組み自体は GitHub Actionsで人生のPDCAをぶん回す と Claude Code GitHub Actionsを振り返りに活用している で過去に詳しく紹介している
  • data/ と knowledge/ を入力に、ポモドーロ / ナレッジ量 / カルチャー / GitHub / Spotify / 給与の6タブを用意している
  • どのタブも年や月を軸にした時系列で並べ、時期ごとの偏りが見えるようにしている
  • 可視化の定義がPythonのコードとしてGit管理下に入ったのが、Metabaseからの一番の変化になっている
  • 常時起動のコンテナが不要になり、 uv run streamlit run dashboard/app.py だけで立ち上がるようになった
  • 検索はAIエージェントに knowledge/ 配下のマークダウンファイルをgrepさせるだけで、技術文書が中心で語彙も揺れにくいため今のところ足りている
  • ただし件数はベクトル検索が優位になるとされる数百本規模に達しており、インデックスの導入を再検討する段階に来ている
  • 意味検索を入れる場合は、マークダウンファイルを正としたまま SQLite FTS5 やベクトルDBのインデックスを別に生成する二段構えになる
  • 記事同士を明示的に繋ぐwikilinksを検索軸として持たせる案もあり、 ReMe のようにキーワード検索や埋め込みと組み合わせる構成を参考にしている
  • ブラウザのブックマークやChatGPTの会話ログのような、まだ手を付けていない個人データの取り込みも検討している
  • ダッシュボードは今のところローカルでしか動かしておらず、認証付きでStreamlit Community Cloudにデプロイしたい
  • 過去にこういう記事を書いたなと思い出したときに、すぐ引っ張ってこられるのがかなり便利になった
  • 記事を書くときの非本質的な部分も大きく減り、執筆の速度は目に見えて上がった
    • 以前は1つの記事を集中して書くという形だったが、複数の記事が同時に少しずつ自然にできていく形に変わった
  • 過去の実測値をグラフで見られることで、定量的な目標が立てやすく、モチベーションにもなっている
  • いかにして技術記事を書くか で書いた執筆の進め方も、この基盤の上でまた組み替わってきているので、この点に関しては別途、紹介したい