factstore について
factstoreは、企業システムや文書に由来する追跡可能な記録を必要とするAIエージェント、開発者、チーム向けのPostgresベースのファクトストアです。属性を事前登録し、根拠付きで記録して変更不能な履歴を保つため、特定時点で把握していた内容を照会できます。
主な機能
- 01登録済みの型付き属性
- 02根拠にひも付いたファクト取引
- 03指定時点読み取り対応の不変履歴
- 04MCPサーバーとPython SDK
- 05カタログ作成と文書取り込み用スキル
活用シーン
- 01請求書やメールをファクトとして取り込む
- 02業務システムのエクスポートをカタログ化
- 03日付を指定してファクトと履歴を照会
MCP 対応
MCPサーバーはstdio経由でfactstoreの操作をツールとして公開し、認証情報は環境変数から取得します。MCPクライアントがファクトにアクセスでき、削除ツールは別の認証情報がある場合にのみ利用できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx factstoreFACTSTORE_DSNシークレットThe credential `factstore actor STORE NAME` prints: a Postgres connection string for one actor of one store.FACTSTORE_EXCISE_DSNシークレットAn excision credential (`factstore actor --excise`). Only then is the excise tool listed.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
factstore
Postgres 上の AI エージェント向けファクトストアです。エージェントは、企業のシステムやドキュメントに記載された内容をファクトとして記録します。ファクトは、登録済み属性の値であり、誰が書き込んだか、どのドキュメントに由来するかを記したトランザクションに保存されます。上書きはされないため、どの日付時点で何を把握していたかをストアから確認できます。エージェントは MCP サーバー経由で、スクリプトは Python SDK 経由で利用します。
エージェントが実行する手順であるスキルも付属しています。1つはエクスポートデータから企業のシステムをインデックス化し、もう1つはドキュメントやメールを読み取ってファクトにします。設計ではこの方式を採用した理由を、評価ではテスト対象を説明しています。MIT ライセンスです。
インストール
Postgres 16 以降、Python 3.12 以降、および uvx 用の uv が必要です。
1. Postgres。 自分のものを使うか、起動してください。factstore init にはスーパーユーザーが必要です。
docker run -d --name factstore-pg -e POSTGRES_PASSWORD=postgres -p 5432:5432 --shm-size=1g postgres:17
2. ストアと、エージェント用の認証情報。
pip install factstore
export FACTSTORE_ADMIN_DSN=postgresql://postgres:postgres@localhost:5432/postgres
factstore init acme # a store, with factstore-core
factstore install acme factstore-skills # the vocabulary the skills use
factstore actor acme "ingest agent" # prints the actor's ID and its credential
3. エージェントを接続します。 Claude Code では、プラグインが MCP サーバーとスキルを導入し、認証情報を尋ねます。
/plugin marketplace add Victor-EU/factstore
/plugin install factstore@factstore
その他の MCP クライアントでは、認証情報を環境変数に設定してサーバーを実行します。
{"mcpServers": {"factstore": {"command": "uvx", "args": ["factstore", "mcp"],
"env": {"FACTSTORE_DSN": "<credential>"}}}}
factstore skills DIR は、スキルを .claude/skills などのエージェントのスキルディレクトリにコピーします。カタログ作成スキルと取り込みスキルは SDK を使って一括書き込みも行うため、エージェントのシェルに pip install factstore が必要です。
4. 質問します。 「./invoices の請求書をストアに読み込んで」は factstore-ingest を使います。「Shopify と QuickBooks のエクスポートをインデックス化して」は factstore-catalogue を使います。「ストアにはどんな種類のものが入っている?」は factstore-ontology を使います。
リリースに含まれるもの
factstore install STORE NAME は、名前を指定してパッケージをインストールし、その依存パッケージもインストールします。
| パッケージ | 内容 |
|---|---|
factstore-core | エビデンス、ドキュメント、確信度、通貨、統合、ドメイン時間です。init がインストールします。 |
factstore-skills | カタログ作成、オントロジー、取り込みのスキルと、オントロジースキルが形状に名前を付ける際に使う語彙です。 |
factstore-ecom-ops | 中国で製造しオンラインで販売するブランドの供給側を扱います。サプライヤー、SKU、発注書、検査、出荷、通関申告が含まれます。付属のスキルはサプライヤーの PDF、チャット、出荷メールを読み取ります。 |
factstore-ecom-index | カタログ作成スキル用に、Shopify、Amazon、3PL、QuickBooks が管理するデータの ID と結合キーを扱います。 |
個人データ。 サーバーのルールにより、氏名、住所、電話番号はストアに保存されず、ドキュメント内に留まります。属性にこれらを保持させたい企業は、core/personal で許可します。
開発
git clone https://github.com/Victor-EU/factstore && cd factstore
docker compose up -d # Postgres 17 on port 54329
cd factstore
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
テストでは、テストごとにストアを作成し、削除します。ソースチェックアウトからは、factstore install にパッケージのディレクトリ(../packages/ecom-ops など)を指定することもできます。packages/ では、パッケージ形式とパッケージについて説明しています。パッケージの新しいバージョンは、追加する内容を登録します。また、カーネルで許可されている2種類の変更である、属性を多値にする変更と ID にする変更を行うこともできます。factstore-skills/ にスキルがあります。
import factstore
store = factstore.connect("<credential from above>")
store.register_attribute({"ident": "shopify/order_id", "type": "string", "cardinality": "one",
"unique": "identity", "doc": "Shopify's ID for an order."})
store.transact([{"e": ["shopify/order_id", "1234"], "a": "shopify/order_id", "v": "1234"}])
store.query('select e, v from "shopify/order_id"').rows
FACTSTORE_DSN が設定されていれば、同じ呼び出しをモジュールレベルでも実行できます:factstore.transact([...])、factstore.query(...)。
MCP サーバー
factstore mcp または factstore-mcp は、stdio 経由で呼び出しをツールとして提供します。認証情報は環境変数(FACTSTORE_DSN)から取得され、ツール呼び出しから渡されることはありません。FACTSTORE_EXCISE_DSN に消去用認証情報も設定されている場合に限り、excise が一覧に表示されます。tools.py のツール説明は、モデルが読むドキュメントです。
レイアウト
| ファイル | 内容 |
|---|---|
schema.sql | Postgres で不変条件を強制するテーブル、ロール、トリガー |
kernel.py | 書き込みロック、スキーマの読み込み、採番、ログと派生テーブルへの変更の適用 |
store.py | 呼び出し:transact、query、stats、register_attribute、search_attributes、excise |
read.py | 安全に読み取りを実行する処理:読み取り専用、ロールバック、単一ステートメント、タイムアウト、行数上限 |
stats.py | 属性の使用状況、シグネチャ、参照の接続性 |
server.py | MCP サーバー |
admin.py、cli.py | ストア、アクター、認証情報の作成、パッケージのインストール |
packages.py | パッケージマニフェスト、リリースに含まれるパッケージ、インストール順序 |
tools.py | MCP ツールの説明と入力スキーマ:モデル向けのドキュメント |
fs.py | カーネル独自の fs/ 属性 |
tests/reference.py | 参照リーダー:ログを単純に畳み込む処理。テストのオラクルとして使用 |
不変条件の強制方法
不変条件を強制するのは Python コードだけではなく、Postgres です。
- 不変性。 書き込み側が
factテーブルに対して持つ権限はINSERTとSELECTのみで、それ以外の権限はありません。トリガーは、fs_excise()外でのUPDATE、TRUNCATE、およびあらゆるDELETEを、所有者からの操作であっても拒否します。 - 書き込み者をカーネルが決定します。 認証情報は Postgres のログインです。トリガーは各トランザクションのアクターを
session_userから、時刻をシステムクロックから設定し、クライアントが送信した値は上書きします。カーネル独自の関数だけがfs/actor、fs/at、fs/excised_*のファクトを書き込めます。 - 登録済みの型付き属性。 すべてのファクトには登録済みの属性を指定する必要があり(外部キーで強制)、トリガーが値とその属性の型が一致することを確認します。別のトリガーは、属性の変更が許可されていない方向(型の変更、多対一への変更、一意性の削除)に進むのを防ぎます。
- コミット順序。 各書き込みはストアごとに1つのアドバイザリロックを取得し、そのロック内でトランザクション ID を割り当てます。トリガーも同じロックを取得し、最新の ID より後でない ID を拒否します。
読み取り
query はビューに対して1つの SQL ステートメントを実行します(設計上の未解決事項1。 スパイクで決定):
- 各属性は、その属性名を付けたビューです。スキーマ
currentの"po/status"(e, v, tx)と、ログ全体を対象とするhistory."po/status"(e, v, tx, op)があります。 - 登録時にトリガーがビューを作成します。
- as-of 読み取りでは
factstore.as_ofを設定し、すべてのビューがその値を尊重します。
モデルが SQL を記述するため、read.py は別の接続で実行します。
- 常にロールバックされる
READ ONLYトランザクション内で実行するため、SET ROLEを含め、ステートメントによる変更はすべて取り消されます。 - プリペアドステートメントとして実行するため、Postgres が2つ目のステートメントを拒否します。
- 10秒のタイムアウトと1,000行の上限を設けます。
アドバイザリロックはロールバック後も保持されるため、各読み取りの後に解放されます。クエリが書き込みロックを保持することはありません。
stats はエンティティをシグネチャ、つまりそれぞれが持つ属性の正確な集合ごとにグループ化し、シグネチャ間の参照数を数えます。これはオントロジースキルが必要とする形式での共起です。ある種類のものは通常、1つのシグネチャを持つか、オプション属性だけが異なる少数のシグネチャを持ちます。
実装時に行った決定
- カーディナリティ 1 は書き込み時に適用します。 新しい値をアサートすると、古い値の明示的な撤回がログに書き込まれます。その後の現在の状態は、属性のカーディナリティが当時どう設定されていたかにかかわらず、アサーションと撤回を単純に畳み込んで求められます。
- 冗長な書き込みは破棄します。 エンティティがすでに持っている値のアサートや、持っていない値の撤回は、変更なしとして報告され、ログには記録されません。何も変更しない呼び出しではトランザクションが書き込まれないため、取り込みを再実行してもログは変わりません。
- ルックアップによる作成はアサーションがある場合に限ります。 ルックアップで何も見つからず、呼び出しが何かをアサートする場合は、エンティティが作成されます。ルックアップが撤回のみに使われる場合、呼び出しはエラーになります。
- 文字列の等価性にはハッシュインデックスを使います。 文字列値の B ツリーは先頭 200 文字にインデックスを付けるため、長い値でも挿入が失敗することはありません。等価性の判定では値全体に対するハッシュインデックスを使うため、どのビューでも
v = 'x'を利用できます。 - カーネルのテストは何も入っていないストアで実行します。
initは factstore-core をインストールしますが、カーネルのテストではcore=Falseを指定します。カーネルは名前を認識しないため、そのテストが core に依存すべきではありません。各パッケージにはそれぞれ独自のテストがあります。 - 近似一致のしきい値。 これらはサンプル属性を使った初期調整値で、
store.pyの先頭に設定されています。tests/test_register.pyには、しきい値の検証に使ったケースが記録されています。M5 の「登録済み属性」指標を使って、しきい値を調整します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。