以下の内容はhttps://techtekt.persol-career.co.jp/entry/tech/260224_01より取得しました。


AI エージェントに「記憶」を。Orphan ブランチ、Git worktree、DVC で構築する、思考とコードの完全同期フロー

AI エージェントが「忘れる」問題

最近、AI エージェントとペアプロをするのが当たり前になってきました。彼らは驚くほど優秀ですが、長時間一緒に作業していると、ふとした瞬間に「さっき話したこと、もう忘れたの?」と寂しくなることはありませんか?

特にデータサイエンスの実験では、この「文脈の欠如」が致命傷になります。本記事では、Git worktree という「少しマニアック」な機能を使って、AI に記憶と整理整頓された実験ノートを授ける方法を解説します。

Git コマンドのおさらい

本手法の根幹を支える Git コマンドを説明します。

  • git branch: 歴史の分岐点。コードの「試作」を本番から切り離します。
  • git switch: 世界線の移動。瞬時に異なるコード状態へ切り替えます。
  • git checkout: 万能コマンド。ブランチ切り替えもファイル復元もこれ一本でしたが、役割が多すぎるため現在は switch(切り替え)と restore(復元)に分化しました。
  • git worktree: 魔法の同期。一つのリポジトリから、特定のブランチ(ここでは notes)だけを別ディレクトリに「実体化」させます。これが「時空を超えた思考のアーカイブ」を実現する核となります。

キーコンセプト:二つのタイムライン

解決策の鍵は、Git のブランチ戦略とディレクトリ構造にあります。以下の二つの異なるタイムラインを管理します。

  1. コード履歴 (main, exp/* ブランチ)

    • 動作するソースコード、モデル定義、DVC ロックファイルを管理します。
    • 実験ごとに exp/YYYYMMDD_HHMMSS のようなブランチを切ります。
  2. 思考履歴 (notes ブランチ)

    • 実験計画書 (spec.md)、実行結果と考察 (result.md) を管理します。
    • Orphan Branch(孤立ブランチ) として作成し、メインのコード履歴とは一切混ざりません。

この二つを、git worktree 機能を使って同じディレクトリ階層にマウントします。これにより、AI エージェントは cd notes とするだけで、現在のコードブランチに関わらず、過去の全ての実験記録(思考履歴)にアクセスできるようになります。

再現性の完全性と時系列からの解放

このワークフローは、単にコードとノートを分けるだけでなく、科学的実験に不可欠な「再現性」と、探索的プロセスに必要な「柔軟性」を両立させます。

1. コミット ID による完全な再現

Note: DVC (Data Version Control) とは?

Git が「コード」の変更を記録するツールであるのに対し、DVC は「データ(巨大なファイル)」の変更を記録するツールです。 データの実体は S3 などのクラウドストレージに保存し、Git には「そのデータのハッシュ値(ID)」だけを dvc.lock ファイルとして記録します。 これにより、Git のコミット ID さえ確認できれば、その時点のコードだけでなく、その時点で使っていた学習データや生成されたモデルまで、完全に復元(dvc pull)することができます。

main ブランチにおける コミット ID (Git SHA) は、プロジェクトのあらゆる状態を再現するための唯一の真実として機能します。

  • コードの再現: Git がソースコードのバージョンを保証します。
  • 環境の再現: uv.lock が依存ライブラリのバージョンを厳密に固定します。uv を使うことで、誰がどこで、実行しても同じ Python 環境が再現されます。
  • データの再現: dvc.lock がデータセットやモデル、中間生成物のハッシュ値を記録します。dvc はこれを元に、S3 などのストレージから正確なバージョンのデータをプルします。

この仕組みにより、過去の任意のコミット ID をチェックアウトし、uv syncdvc pull を実行するだけで、当時の実験環境が完全に復元されます。

2. クリーンな実験実行と MLflow

Note: MLflow とは?

機械学習の実験管理ツールです。「いつ、誰が、どんなパラメータで実験し、どんな結果(精度)が出たか」を Web 画面で一覧表示できます。 本手法では、この MLflow の記録に「Git のコミット ID」を紐付けることで、実験結果から正確なコードとデータの状態を逆引きできるようにします。

再現性を担保するための基本的なステップを整理します。

  1. git commit: コードと環境の状態を固定。
  2. make repro (dvc repro): 固定されたコードで実験を実行。
  3. MLflow: 実験結果を MLflow に飛ばす。この際、現在の Git コミット IDが自動記録される。
  4. dvc.lock commit: 生成されたデータのハッシュ値を保存。

これにより、MLflow 上の「結果」から、Git コミット ID をたどり、その時使っていたデータ(DVC)と環境を再現するために必要な情報をほぼ漏れなくたどれるようになります。

3. 時空を超える思考履歴 (Orphan Branch)

一方で、思考履歴を管理する notes ブランチは、Orphan Branch(親を持たない孤立したブランチ) として存在します。これはコードのコミット(バージョン)に依存しません。

通常、Git で過去のコミットに戻ると、その当時のファイル状態に戻ってしまいます。しかし、git worktree でマウントされた notes ディレクトリは、コード側が過去 (switch --detach HEAD~10) に戻ろうが、現在 (switch main) に戻ろうが、常に「最新の思考履歴」を保持し続けます。

これにより、「過去の実験結果を参照しながら、新しい仮説を立てる」ことや、「複数の実験を行き来しながら、考察を一つのノートにまとめる」といった、時系列に縛られない柔軟な思考プロセスが可能になります。

Makefile による定型実行

この複雑なワークフローを人間(そして AI)が手動で行うのはミスの元です。そこで、セットアップから実験の開始・終了までを自動化する Makefile を作成しました。

Note: Makefile とは?

make コマンドが読む「レシピ集」です。 ターミナルで毎回

git switch -c exp/...
mkdir -p notes/...
uv run dvc repro
git add dvc.lock
git commit -m "..."

のような長いコマンド列を手で打つ代わりに、

make start
make repro
make finish

のような短いコマンドで同じ処理を実行できるようにする仕組みです。

  • プロジェクトのルートディレクトリに Makefile というファイル名で置く
  • make ターゲット名 で、そのターゲットに対応する一連のコマンドが実行される
    (この記事では setup, start, repro, finish がターゲット)

つまり「実験の標準手順書をコード化したもの」が Makefile だと考えると理解しやすいでしょう。

これをプロジェクトのルートに配置するだけで、フローが完成します。

Makefile

# 変数
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
EXP_ID_FROM_BRANCH := $(shell echo $(BRANCH) | sed -e 's/^exp\///')
IS_EXP_BRANCH := $(filter exp/%,$(BRANCH))
NOTES_DIR := notes
EXP_NOTES_PATH := $(NOTES_DIR)/$(EXP_ID_FROM_BRANCH)

.PHONY: setup start repro finish

help:
    @echo "AI-DS Workflow Commands:"
    @echo "  make setup          - 思考履歴用のOrphanブランチを作成しWorktree展開"
    @echo "  make start          - 実験ブランチ作成と計画書の初期化"
    @echo "  make repro          - テストとDVC実行 (※事前にコミットが必要)"
    @echo "  make finish         - 結果報告のコミットとメインへの統合"

# 1. セットアップ:Orphanブランチの作成とWorktreeマウント
setup:
    @if [ -d "$(NOTES_DIR)" ]; then echo "Error: $(NOTES_DIR) already exists."; exit 1; fi
    git switch --orphan notes
    git rm -rf . || true
    git commit --allow-empty -m "Initial commit for experiment notes"
    git switch main
    git worktree add $(NOTES_DIR) notes
    @echo "Setup complete. 'notes/' is now linked to 'notes' branch."

# 2. 開始:コードブランチ作成 + 思考ブランチへの計画書追加
start:
ifneq ($(IS_EXP_BRANCH),)
    @echo "Error: Cannot start experiment from an experiment branch."; exit 1
endif
    @$(eval NEW_EXP_ID := $(shell date +%Y%m%d_%H%M%S))
    @echo $(BRANCH) > $(NOTES_DIR)/$(NEW_EXP_ID)_source_branch
    git switch -c exp/$(NEW_EXP_ID)
    mkdir -p $(NOTES_DIR)/$(NEW_EXP_ID)
    @printf "### 実験計画: $(NEW_EXP_ID)\n- 仮説:\n- 手法:\n" > $(NOTES_DIR)/$(NEW_EXP_ID)/spec.md
    @mv $(NOTES_DIR)/$(NEW_EXP_ID)_source_branch $(NOTES_DIR)/$(NEW_EXP_ID)/source_branch.txt
    @# 思考履歴側でのコミット
    cd $(NOTES_DIR) && git add $(NEW_EXP_ID)/spec.md $(NEW_EXP_ID)/source_branch.txt && git commit -m "Plan: $(NEW_EXP_ID)"
    @echo "Created branch exp/$(NEW_EXP_ID) and notes/$(NEW_EXP_ID)/spec.md"

# 3. 実験実行:コード側のコミット + DVC実行
repro:
ifeq ($(IS_EXP_BRANCH),)
    @echo "Error: Not on an experiment branch."; exit 1
endif
    @if [ -n "$$(git status --porcelain | grep -v '^?? notes/')" ]; then \
        echo "Error: Uncommitted code changes found. Commit them first."; \
        exit 1; \
    fi
    uv run pytest src/tests/
    uv run dvc repro
    git add dvc.lock
    # .gitignore might be updated by dvc
    git add .gitignore || true
    git commit -m "[exp: $(EXP_ID_FROM_BRANCH)] dvc repro executed"

# 4. 終了:思考側のレポートコミット + コード側のマージ
finish:
ifeq ($(IS_EXP_BRANCH),)
    @echo "Error: Not on an experiment branch."; exit 1
endif
    @ls $(EXP_NOTES_PATH)/result.md > /dev/null 2>&1 || (echo "Error: result.md not found."; exit 1)
    @# 思考履歴側でのレポートコミット
    cd $(NOTES_DIR) && git add $(EXP_ID_FROM_BRANCH)/result.md && git commit -m "Result: $(EXP_ID_FROM_BRANCH)"
    @$(eval PARENT_BRANCH := $(shell cat $(EXP_NOTES_PATH)/source_branch.txt 2>/dev/null || echo main))
    git switch $(PARENT_BRANCH)
    git merge exp/$(EXP_ID_FROM_BRANCH)
    @echo "Merged experiment $(EXP_ID_FROM_BRANCH) into $(PARENT_BRANCH). Notes are safe in 'notes' branch."

Git worktree の魔法

この仕組みの肝は make setup にあります。

git switch --orphan notes
git rm -rf . || true
git commit --allow-empty -m "Initial commit..."
git switch main
git worktree add notes notes

通常、Git でブランチを切り替えるとディレクトリの中身が全て入れ替わります。しかし、git worktree add notes notes を実行すると、現在のディレクトリの中に notes/ というサブディレクトリができ、その中だけが notes ブランチの状態になります。

つまり、src/ などのコードを編集している main (または exp/*) ブランチにいながら、同時に notes/ 以下のファイル(別ブランチ)も参照・編集できるのです。これにより、AI は「ディレクトリ移動」というごく自然な操作で、コードと思考を行き来できるようになります。

Warning: 混ぜるな危険

もしあなたがプロジェクトのルートディレクトリで誤って git switch notes を実行してしまうと、大量のファイルが「削除された」あるいは「未追跡」の状態として表示され、混乱するでしょう。これは、notes ブランチが main とは全く異なる歴史(Orphan Branch)を持つためです。

この状態で誤って git merge 等をしてしまうと、コードの歴史と思考の歴史が混ざり合い、このワークフローの前提である「タイムラインの分離」が崩壊します。notes ブランチは常に git worktree 経由(cd notes)でのみ操作することを心がけてください。

AI への指示書

この環境を AI に正しく認識させるために、以下のプロンプト(指示書)を .ai-instructions.md としてプロジェクトルートに置いておきます。Cursor、Claude Code、Antigravity などの AI コーディングエージェントにルールとして設定することで、彼らは自律的に実験サイクルを回し始めます。

.ai-instructions.md

# AI DS Protocol
あなたは `ai-ds-worktree-flow` プロトコルに従うエージェントです。

## 基本動作
1. プロジェクト開始時に `make setup` が済んでいるか確認せよ。
2. 実験開始は `make start`。作成された `notes/[ID]/spec.md` を埋め、ユーザーの「承認」を待て。
3. コードを書き終えたら `git commit``notes/` 以外の変更)を行い、その後 `make repro` を実行せよ。
4. 結果が出たら `notes/[ID]/result.md` を書き、承認を得てから `make finish` を実行せよ。

## 注意点
- あなたが触るディレクトリは `src/`(コード)と `notes/`(記録)の 2 か所である。
- `notes/` 内の操作は `notes` ブランチに、それ以外は `exp/` ブランチに記録されることを理解せよ。

AI エージェントとの対話ログ(シミュレーション)

実際にこの環境で AI と協働すると、どのような体験になるのでしょうか。

: 「昨日の実験、学習率 0.001 だと全然収束しなかったよね。別の手段を考えて。」

AI: 「はい、notes/20260216_xxx/result.md を確認しました。前回は損失がプラトーに達していましたね。学習率ではなく、バッチサイズを 2 倍にする実験を提案します。新しい実験ブランチを作成するために、make start を実行しますか?」

: 「お願いします。」

(AI がコマンド実行: make start) (出力: Created branch exp/20260217_120000 and notes/20260217_120000/spec.md)

AI: 「実験ブランチを作成し、計画書に『バッチサイズ倍増』の仮説を記述しました。実装に移ります。」

(AI がコード修正... git commit ... make repro)

AI: 「実験完了です。損失が順調に減少し、収束しました。notes/20260217_120000/result.md に結果を記録しました。」

: 「よし、これでいこう。マージして。」

AI: 「make finish を実行します。」

(コードは main にマージされ、レポートは notes ブランチに保存される)


このように、AI はもはや単なる「コード生成ツール」ではなく、「実験のコンテキストを共有するパートナー」として振る舞うようになります。notes/ ディレクトリを見るだけで、過去にどんな実験をして、どんな結果だったのか(そしてそれはどのコードブランチでの出来事だったのか)を、AI 自身がいつでも振り返ることができるのです。

ぜひ、あなたのプロジェクトにも「AI のための記憶領域」を作ってみてください。

alt

浦山 昌生 Masao Urayama

データ・AIソリューション本部 AIプロダクト統括部 AIプロダクトソリューション部 シニアデータアナリスト

AI ベンダーでデータサイエンティスト兼PL(プロジェクトリーダー)として機械学習モデルの開発やデータ分析の受託業務に従事。それまでは、ネットワークエンジニア、情報セキュリティエンジニアとして顧客の課題解決に対応。2021年10月にパーソルキャリアに入社し、推薦モデルの開発、情報検索システムの開発等、先進的なデータの活用を実践。

※2026年2月現在の情報です。




以上の内容はhttps://techtekt.persol-career.co.jp/entry/tech/260224_01より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14