0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

思いつきで置いた `.friend/` が、実はAI時代の `.git/` だった。

0
Posted at

ファイルシステム接地型AI文脈管理についての小さな設計論

1. はじまりは、ただの不満だった

最初に言っておくと、これは「自作AIインタフェースすごいでしょう」という話ではない。

むしろ逆である。
最初は、ただ作業ディレクトリの中に .friend/ という隠しディレクトリを置いただけだった。

ところが、ChatGPTのWeb UI変更に少し困ってみると、その何気ない置き方が、AIとの協働文脈をプロジェクトに接地させるための、かなり本質的な設計だったのではないかと思えてきた。

要するに、こういう話である。

思いつきで置いた .friend/ が、実はAI時代の .git/ だった。

話の始まりは、ただの雑談だった。

「ChatGPTのWeb UI、最近ちょっと変わったよね」

以前は、プロジェクトがツリーのように見えていた。
視線を移動しながら、過去のスレッドを探すことができた。
正確な名前を覚えていなくても、一覧を眺めていれば、

ああ、これだった。

と見つけられた。

ところが、新しいUIでは、まずプロジェクトを探し、クリックし、さらにスレッド一覧に入る。
プロジェクトの並び順もよく分からない。変更日も直感と合わない。
検索すれば出てくるのに、一覧には見当たらないものもある。

検索は強い。
しかし、検索には前提がある。

探したいものを、言葉で思い出せていること。

実際の作業では、そうはいかない。

「あの話、どこだったっけ」
「たしか、このへんで議論した」
「名前は忘れたけれど、見れば分かる」

これは検索ではなく、再認識の問題である。

remember → search

ではなく、

browse → recognize → recover

が必要になる。

そして、この小さなUI上の不満から、意外な話が始まった。

2. ところで、friend interfaceとは何か

ここで少しだけ、friend interface について説明しておく。

friend interfaceについては、Qiitaの別記事群、タグまたはキーワードとしては MakingOfFriendInterface あたりで、実装も含めて触れている。
興味のある方はそちらも眺めてほしいのだが、この記事では実装の詳細には立ち入らない。

friend interface は、私が自分用に作っている、少しおふざけの名前を持つ対話型生成AIインタフェースである。
ざっくり言えば、Emacs上で動く、ローカル作業ディレクトリ密着型のAI相棒である。

ChatGPTのように、Web UI上でプロジェクトやスレッドを選ぶのではなく、今開いている作業ディレクトリを基準にして、AIとの対話文脈を復元する。

たとえば、あるプロジェクトには次のような .friend/ ディレクトリを置く。

project/
  ├─ src/
  ├─ docs/
  ├─ tests/
  └─ .friend/
       ├─ ai-interaction.log
       ├─ ai-interaction.memory.md
       ├─ ai-interaction.summary.md
       └─ ai-interaction.state.json

ここには、そのプロジェクトに関するAIとの対話ログ、要約、継続的な設計メモ、状態情報などを置く。

一方で、ユーザー全体に共通する方針や知識は、ホームディレクトリ側の ~/.friend/ に置く。

~/.friend/
  ├─ foundation.md
  └─ repository/
       ├─ index.md
       ├─ ooc/
       ├─ wt/
       └─ ot/

つまり、friend interface では、AIとの文脈を大きく二層に分けている。

~/.friend/          = ユーザー横断の方針・知識
project/.friend/   = プロジェクト固有の対話文脈

この仕組みは、最初から大げさな理論に基づいて作ったわけではない。
Emacs、Eclipse、Unixのディレクトリ構造、Javaのパッケージ、.git/.vscode/ のようなプロジェクトローカルなメタ情報ディレクトリ。
そういった過去の経験から、「まあ、ここに置くのが自然だよね」という感覚で始めたものだった。

ところが、ChatGPTのWeb UI変更に少し困ってみると、この .friend/ 方式が、思いのほか強い設計だったことに気づいた。

なお、friend interface 自体の詳細は、ここでは扱わない。
この記事では、friend の実装紹介ではなく、.friend/ という置き方が持っていた設計上の意味に注目する。

3. friendでは、なぜ同じ問題が起きづらいのか

friendでは、プロジェクト管理の一次情報はWeb UIではない。
前節で触れたように、プロジェクトはファイルシステム上のディレクトリであり、AI文脈はその直下の .friend/ に置かれる。

workspace/
  ├─ morph-friend/
  ├─ ot/
  ├─ pms/
  └─ n88c/

各プロジェクトには、それぞれ .friend/ がある。

morph-friend/
  ├─ src/
  ├─ include/
  ├─ docs/
  ├─ Makefile
  └─ .friend/
       ├─ ai-interaction.log
       ├─ ai-interaction.memory.md
       ├─ ai-interaction.summary.md
       └─ ai-interaction.state.json

friendにおいて、作業文脈はこう解決される。

function resolve_friend_context(current_directory):
    dir = current_directory

    while dir is not filesystem_root:
        if exists(dir + "/.friend"):
            return dir + "/.friend"

        dir = parent(dir)

    return user_home + "/.friend"

つまり、現在の作業ディレクトリから上に向かって探索し、最も近い .friend/ を有効な文脈とする。

これは、特別に難しい仕組みではない。
むしろ、Unix的にはかなり自然である。

作業場所が、文脈を決める。

Web UIのプロジェクト一覧がどう変わっても、friendではまず作業ディレクトリに移動すればよい。

cd ~/workspace/devel/morph-friend

この時点で、少なくとも「どのプロジェクトか」は決まっている。
プロジェクト文脈は、UI上の一覧ではなく、ファイルシステム上の場所に結びついているからである。

4. ファイルシステムは、名前空間である

ここで見方を変える。

ファイルシステムは、単なる保存場所ではない。
それは、階層的な名前空間である。

filesystem = namespace
directory  = context boundary
path       = name within context
.friend/  = AI context attached to that boundary

この見方を採用すると、.friend/ の意味がかなり明確になる。

project-root/
  ├─ source/
  ├─ docs/
  ├─ tests/
  └─ .friend/

これは、

project-root = プロジェクト文脈の名前空間
.friend/    = その文脈に付随するAIメタ情報

である。

Web UI上の抽象的な「プロジェクト」ではなく、実際の作業対象が存在する場所に、AIとの協働文脈を置く。

一文で言えば、こうなる。

AI文脈はチャットUIの中ではなく、作業対象の隣に置く。

この考え方は、Javaのパッケージにも少し似ている。

src/
  └─ com/
      └─ example/
          └─ app/
              └─ Main.java

というディレクトリ構造が、

package com.example.app;

public class Main {
}

という名前空間に対応するように、friendでは、

/path/to/project/.friend/

が、そのプロジェクトのAI文脈を指す。

つまり、friend はAI対話文脈に、ファイルシステム上の住所を与えている。

5. .friend/.git/ なのか

ここで、あのキャッチコピーが出てくる。

思いつきで置いた .friend/ が、実はAI時代の .git/ だった。

もちろん、.friend/.git/ の代替ではない。
.git/ は成果物の差分履歴を管理する。
.friend/ はAIとの協働文脈を保持する。

.git     = 成果物の版管理
.friend  = 協働文脈の管理

しかし、構造はよく似ている。

project/
  ├─ source files
  └─ .git/

.git/ は、作業対象の隣に、その作業対象を復元・追跡するためのメタ情報を置く。

一方で、

project/
  ├─ source files
  ├─ docs
  └─ .friend/

.friend/ は、作業対象の隣に、その作業対象をAIと継続的に扱うためのメタ文脈を置く。

対応を表にすると、こうなる。

観点 .git/ .friend/
置き場所 project root直下 project root直下
対象 成果物の変更履歴 AI協働文脈
復元するもの ファイルの過去状態 作業時点の文脈
支える作業 開発履歴の追跡 AIとの継続作業
本質 成果物を履歴に接地する 文脈を作業場所に接地する

つまり、こう言える。

.git が成果物の履歴をプロジェクトに接地させたように、
.friend はAIとの協働文脈をプロジェクトに接地させる。

ここで言う「AI時代の .git/」とは、.git/ のような差分管理機構を作ったという意味ではない。
.git/ が成果物の履歴をプロジェクトに住まわせたように、.friend/ がAIとの協働文脈をプロジェクトに住まわせる、という意味である。

6. ただし、私はtar派である

ここで話が少しややこしくなる。
私は、実はGit派ではない。

これまで色々なバージョン管理ツールを使ってきたが、あまり良い思い出がない。
だから、明確な理論的理由というより、気持ちの問題として、昔ながらのtar派である。

しかし、これは .friend/ 戦略と矛盾しない。
むしろ、よく合っている。

.friend/ の本質は、Gitで差分管理することではない。
プロジェクト文脈を、プロジェクトと同じ名前空間に置くことである。

だから、保存方法はtarでもよい。

tar czf morph-friend-20260521-before-m6.tar.gz morph-friend/

これで、以下が丸ごと保存される。

source/
docs/
tests/
.friend/

つまり、

成果物の状態
設計文脈の状態
AIとの相談状況
次にやること

が、同じ時点のスナップショットとして固定される。

.friend/ にとって重要なのは、精密な差分履歴ではない。
むしろ、ある時点の文脈へ戻れることである。

.friend/ の差分を見たいのではない。
あの時点の friend に戻れればよい。

コードは差分で語れることがある。
しかし文脈は、しばしば時点として復元したい。

7. 1つのルート、1つの文脈

friendにおける基本原則は単純である。

1 project root = 1 project context

プロジェクトルートは、1つのプロジェクト文脈の根である。
そこに複数の独立プロジェクトを押し込めると、話が壊れる。

bad-root/
  ├─ .friend/
  ├─ compiler/
  ├─ gui-app/
  └─ docs-project/

この場合、.friend/ が何の文脈なのか分からない。

正しくは、こうである。

workspace/
  ├─ compiler/
  │   └─ .friend/
  ├─ gui-app/
  │   └─ .friend/
  └─ docs-project/
      └─ .friend/

複数プロジェクトを含む親ディレクトリは、プロジェクトルートではない。
それはワークスペースルートである。

workspace root = 複数プロジェクトを含みうる作業空間
project root   = 1つのプロジェクト文脈の根
friend root    = .friend/ を持つ文脈の根

ただし、入れ子のfriend rootはあり得る。

project/
  ├─ .friend/
  ├─ subproject1/
  │   └─ .friend/
  ├─ subproject2/
  │   └─ .friend/
  └─ subproject3/
      └─ .friend/

これは矛盾ではない。

project/             = 1 project context
project/subproject1/ = 1 project context
project/subproject2/ = 1 project context
project/subproject3/ = 1 project context

入れ子になった複数の project root があるだけである。

このとき、有効文脈は最近傍の .friend/ とする。

function active_context(path):
    return nearest_ancestor_containing(".friend/", from = path)

親文脈を子文脈に自動混入しない。
必要な場合だけ、明示的に参照する。

default = current context only

これは、文脈の単一責任原則である。

8. ~/.friend/project/.friend/

friendには、もう一つ重要な分離がある。

~/.friend/
project/.friend/

project/.friend/ は、そのプロジェクト固有の文脈である。

project/.friend/
  ├─ ai-interaction.log
  ├─ ai-interaction.memory.md
  ├─ ai-interaction.summary.md
  └─ ai-interaction.state.json

一方、~/.friend/ はユーザー横断の文脈である。

~/.friend/
  ├─ foundation.md
  └─ repository/
       ├─ index.md
       ├─ ooc/
       ├─ wt/
       └─ ot/

これは、次のような階層を作る。

global principle
  ↓
shared repository knowledge
  ↓
project-local memory
  ↓
current interaction summary
  ↓
current request

日本語で言えば、

普遍的な方針
再利用可能な知識
プロジェクト固有の判断
直近の作業文脈
今回の依頼

である。

この分離は、後から見ると非常に大きかった。

最初は、ただ自然な置き場所として、

~/.friend/
project/.friend/

を提案しただけだった。

しかし結果的には、ユーザー全体の知識とプロジェクト固有の文脈を分ける、かなり強い二層構造になっていた。

壮大な計画はなかった。
だが、過去のEclipse、Emacs、Unix、Java package、.git/.vscode/ などの経験が、水面下で設計感覚として働いていたのかもしれない。

9. なぜ商用AIでは同じ形になりづらいのか

ここで、素朴な疑問が出る。

もし .friend/ 型がそれほど自然なら、なぜ商用AIシステムでは全面的に採用されていないのか。

技術的な理由もある。
しかし、大きいのは、おそらくビジネス上・運用上の理由である。

商用AIサービスでは、文脈はサービス価値の中核である。

- 継続利用の理由
- パーソナライズの源泉
- 検索・推薦・自動化の材料
- チーム共有機能の基盤
- 課金プラン差別化の要素
- スイッチングコスト

ユーザー視点では、文脈は自分の資産である。

user:
  context is my working asset

一方、ベンダー視点では、文脈はサービス価値の中核である。

vendor:
  context is part of the product moat

この緊張がある。

friend型は、文脈の正本をローカルファイルシステムに置く。

cloud AI = 推論サービス
.friend/ = 文脈資産

これはユーザー主権・ローカルファーストには強い。
しかし、クラウドサービス主権とは緊張関係にある。

もちろん、クラウドを否定しているわけではない。
クラウドAIを推論サービスとして使うことと、プロジェクト文脈の正本をローカルに置くことは両立する。

問題は、文脈を誰が所有し、どこに正本を置くかである。

10. では、friend型は万人向けなのか

ここまで書くと、friend型はずいぶん良い仕組みに見える。

しかし、当然ながら万人向けではない。

最大の理由は、おそらくセキュリティでもビジネスモデルでもなく、もっと手前にある。

Unix的なファイルシステム文化に慣れていない人には、そもそも自然に見えない。

私にとっては、

project/
  ├─ src/
  ├─ docs/
  └─ .friend/

という構造はかなり自然である。

.git/.vscode/.emacs.d/~/.config/、Javaのパッケージディレクトリ、Eclipseのworkspace/project構造。
こうしたものを見慣れていると、「プロジェクトのメタ情報を隠しディレクトリに置く」という発想は、ほとんど説明不要に近い。

しかし、そうでない人にとっては違う。

なぜドットで始まるディレクトリがあるのか
なぜ見えない場所に大事な情報を置くのか
なぜChatGPTの画面ではなくファイルの中に文脈があるのか
なぜプロジェクトルートを意識しなければならないのか

という話になる。

つまり、friend型は、ローカルファーストで透明性の高い設計である一方、Unix的・IDE的・開発者的な前提知識にかなり支えられている。

ここは弱点である。

10.1 friendが万人に受け入れられるには

では、friend型がより広いユーザーに受け入れられるには、何が必要だろうか。

以下は、かなり独断と偏見である。

1. .friend/ を意識させないUI

まず、普通のユーザーに .friend/ を直接触らせてはいけない。

内部的にはファイルシステムに接地していてよい。
しかし、UI上では次のように見えた方がよい。

このプロジェクトのAI文脈
このプロジェクトの作業メモ
このプロジェクトの履歴
このプロジェクトの次作業

.friend/ は実装詳細であり、ユーザーには「プロジェクトに付属するAIノート」として見えるべきである。

2. project root を自動で見つける

開発者なら、project root という言葉で通じる。

しかし一般ユーザーには通じない。

したがって、friendが万人向けになるには、

このフォルダが作業場所ですね
この文書群を1つのプロジェクトとして扱いますね
このノート群にAI文脈を付けますね

という自動判定と確認UIが必要になる。

3. 見える索引が必要

.friend/ がファイルとして存在するだけでは足りない。

ユーザーは、過去の文脈を探したい。

そのためには、

最近の作業
未完了の論点
決定済みの設計判断
次にやること
関連する対話

を見える形で一覧できる必要がある。

つまり、friend型であっても、最終的にはUIが要る。
ただし、そのUIは文脈の正本ではなく、ローカル文脈を見やすくするビューであるべきだ。

4. セキュリティ境界を説明しなくても安全にする

.friend/ はプロジェクトの機密境界を継承する。
これは設計原則としてはよい。

しかし、万人向けにするには、ユーザーがそれを理解していなくても事故りづらい必要がある。

たとえば、

このプロジェクトはクラウド同期されています
.friend/ も同期対象です
このフォルダは共有されています
AI対話ログも共有される可能性があります

のような警告が必要になる。

さらに、公開・共有前には、

.friend/ に会話ログが含まれています
private memory が含まれています
state file が含まれています
公開してよいですか

のような確認があってもよい。

5. tar派にもGit派にも寄せない

friend型の本質は、Gitではない。
もちろんtarでもない。

本質は、

AI文脈を作業対象の隣に置くこと

である。

したがって、保存方法は選べた方がよい。

スナップショット保存
Git連携
zip出力
クラウドバックアップ
完全ローカル保存

どれでもよい。

ユーザーの作業文化に合わせられることが重要である。

6. 「チャット」ではなく「作業台帳」として見せる

万人向けにするなら、AIとの履歴を単なるチャットログとして見せない方がよい。

むしろ、

決定事項
未解決事項
次作業
変更理由
参照資料
作業メモ

として見せるべきである。

チャットは流れる。
文脈は残す。

この思想をUIとして表現できれば、friend型はかなり広いユーザーにも届くかもしれない。

10.2 つまり、friend型は玄人向けの構造を持つ

現時点のfriend型は、正直に言えば玄人向けである。

ファイルシステムを名前空間として見られる人、プロジェクトルートを意識できる人、隠しディレクトリに抵抗がない人、ローカルファイルの所有権と責任をある程度引き受けられる人には、とても強い。

一方で、すべてをWeb UI上で完結させたい人には、かなり重い。

したがって、friend型を万人向けにするには、内部は .friend/ のままでよいとしても、外側にはかなり丁寧なUIと安全装置が必要になる。

内部構造はUnix的に。
外側の体験は、非Unixユーザーにも分かるように。

これが、おそらくfriend型を広げるための条件である。

11. .friend/ は危険物なのか

.friend/ には濃い情報が入る。

- AIとの相談履歴
- 設計判断
- 次作業
- 失敗ログ
- 研究メモ
- 仮説
- 一時的な推測

だから、セキュリティには注意が必要である。

しかし、.friend/ だけが特別に危険というわけではない。

プロジェクトには、そもそも機密情報が含まれ得る。

project/
  ├─ src/
  ├─ docs/
  ├─ paper/
  ├─ notes/
  ├─ data/
  └─ .friend/

ソースコード、論文、研究ノート、データ、設計文書。
これらと .friend/ の機密度に、本当に大きな差があるのか。

多くの場合、答えは怪しい。

したがって、.friend/ だけを別扱いするより、プロジェクトルート全体を同じ機密境界で扱う方が自然である。

.friend/ は、その project root の機密境界を継承する。

高機密プロジェクトであれば、.friend/ だけではなく、プロジェクト全体を隔離すべきである。

- ネットワーク接続を排除する
- クラウドAIへ内容を送らない
- クラウド同期を使わない
- 外部へ送らない
- バックアップも管理された媒体に限定する

逆に、プロジェクト本体をprivate cloudやprivate backupに置いてよいなら、.friend/ だけを過剰に特別扱いする必要はない。

.friend/ は、プロジェクト外部の秘密箱ではない。
プロジェクト文脈に付随するメタ文脈である。

12. 小さな仕様としてまとめる

friend型の設計原則を、擬似仕様としてまとめるとこうなる。

type FriendContext = {
    root: Directory
    memory: MarkdownFile
    summary: MarkdownFile
    log: TextFile
    state: JsonFile
}

function is_friend_root(dir):
    return exists(dir / ".friend")

function resolve_friend_root(current_dir):
    dir = current_dir

    while dir != filesystem_root:
        if is_friend_root(dir):
            return dir

        dir = parent(dir)

    return null

function resolve_context(current_dir):
    root = resolve_friend_root(current_dir)

    if root != null:
        return FriendContext(root / ".friend")

    return GlobalFriendContext(home / ".friend")

セキュリティ境界はこう扱う。

function confidentiality_boundary(friend_context):
    return project_boundary(friend_context.root.parent)

function may_share(friend_context, target):
    project = friend_context.root.parent
    return may_share(project, target)

つまり、

.friend/ の共有可否は、原則として project root の共有可否に従う。

入れ子文脈はこう扱う。

function active_context(path):
    return nearest_friend_context(path)

function parent_context(path):
    active = active_context(path)
    return nearest_friend_context(parent(active.root.parent))

function effective_context(path, options):
    ctx = active_context(path)

    if options.include_parent_context:
        return merge(parent_context(path), ctx)

    return ctx

ただし、既定では親文脈を混ぜない。

default = current context only

13. Chatは流れる。文脈は残す。

AIチャットは、一見すると会話である。
しかし、開発、研究、文書作成に使い始めると、それは単なる会話ではなくなる。

そこには、

- 何を決めたか
- なぜそうしたか
- 何を試したか
- 何を捨てたか
- 次に何をするか

が含まれる。

それは、作業資産である。

従来のチャットUIは、会話をスレッドとして管理する。
しかし、長期作業において本質的な単位は、スレッドではない。

conversation thread = event
project context     = durable working memory

チャットはイベントである。
プロジェクト文脈は持続する。

だから、friendは文脈をスレッドではなく、作業ディレクトリに置く。

会話は流れる。
文脈は作業ディレクトリに残す。

14. おわりに

最初から壮大な計画があったわけではない。

.friend/~/.friend/ は、EclipseやEmacsやUnix的な実務感覚から、何となく自然な置き場所として出てきた。
プロジェクト固有の文脈はプロジェクト直下に。
ユーザー横断の文脈はホームディレクトリに。

ただ、それだけだった。

しかし後から見れば、それはかなり強い設計だった。

ファイルシステムを一次名前空間とする。
AI文脈を作業対象の隣に置く。
1つのルートは1つの文脈を表す。
ユーザー横断文脈とプロジェクト文脈を分ける。
文脈をUIではなく、ローカルに接地させる。

.friend/ は、最初から壮大な計画として設計されたものではなかった。
ただ、作業対象の隣にAI文脈を置くのが自然だと思っただけだった。

しかし、後から見れば、それはファイルシステムを名前空間とし、プロジェクトルートを文脈境界とし、AIとの協働履歴を作業場所に接地させる設計だった。

.git/ が、成果物の履歴をプロジェクトに住まわせたように。
.friend/ は、AIとの協働文脈をプロジェクトに住まわせる。

思いつきで置いた .friend/ が、実はAI時代の .git/ だった。

ただし、私はtar派である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?