OKF(Open Knowledge Format)は、知識を人間とAIエージェントの双方が扱いやすい形で表現するための、オープンでベンダーニュートラルなフォーマットです。
その具体的な表現として、Markdown本文とYAML frontmatterを持つファイル群を使います。1
そのミニマルさは強みです。人間がそのまま読めて、特定のベンダーや実行基盤に閉じません。
一方で、エージェントにとって少し惜しい点もあります。
OKFでは、概念どうしの関係を通常のMarkdownリンクで表せます。ただし、そのリンクが「joinできる」のか、「依存している」のか、「メトリクスの計算元」なのかは、周辺の自然言語を読まないと分かりません。
そこで、OKFのミニマルさを壊さずに、frontmatterへ relationships: を足す設計を試しました。
検証の主役は、固定の検索関数ではなくAIエージェントです。測りたいのは、グラフ検索アルゴリズムの性能ではなく、実際のエージェントが relationships: を手がかりに探索を変えるかどうかだからです。
同じOKFディレクトリを、relationships: なしの状態と、relationships: ありの状態で用意し、AIエージェントが実際にどのファイルを読み、どれくらいのコストで答えにたどり着くかを比較します。
検証テストを実施したところ、回答の正しさは同等のまま、読んだコンテキスト量は約45.4%、usage tokensは約17.1%、レイテンシーは**約16.0%**減りました。読むファイル数も、平均2.94件から2.37件に減っています。
OKFはすでに「グラフっぽい」
OKFの公式仕様では、OKFを「知識を表現するための、人間にもエージェントにも扱いやすいオープンなフォーマット」と位置づけています。
そのうえで、具体的なファイル表現として「MarkdownファイルとYAML frontmatterのディレクトリ」を採用しています。
重要なのは、OKFがオープンな知識フォーマットでありながら、意図的にミニマルに作られていることです。仕様は固定の分類体系や中央レジストリを求めません。frontmatterに独自の追加項目を持たせることもできます。
また、OKFは通常のMarkdownリンクで概念間の関係を表せます。公式READMEでも、OKFは単なるツリーではなく、リンクによってグラフ状の知識を表せると説明されています。
ただし、Markdownリンクだけでは「関係の種類」までは明示されません。
たとえば以下のリンクがあったとします。
Joined with [customers](/tables/customers.md) on `customer_id`.
人間が読めば、これはjoin関係だと分かります。
でも、機械的に見ると、分かるのは「このファイルから /tables/customers.md にリンクしている」ということだけです。
このリンクが単なる参考リンクなのか、join可能なテーブルなのか、メトリクス計算の依存先なのか。その違いは、リンクそのものには含まれていません。
ここに relationships: を足すと、関係の種類を明示できます。
ここで言うオントロジー
ここで扱うオントロジーは、OWLやRDFを全面導入するような重いものではありません。
Tom Gruberの定義に寄せるなら、オントロジーは領域の概念と、それらの関係を明示するための表現語彙です。
ここでは、もっと実務寄りに捉えます。
- 概念: 既存のOKFファイル
- 関係: OKFファイル同士のつながり
- 関係ラベル:
part_of、joins_with、derived_fromのようなつながりの名前 - 検索用インデックス:
relationships:を集めて作るJSONファイル - 本文: これまで通りMarkdownとして読める
つまり、OKFを別フォーマットに置き換えるのではありません。
OKFの上に、「このファイルは何と、どういう意味でつながっているのか」を薄く足します。
関係ラベルの語彙
relationships: では、関係の種類を predicate というキーで書きます。
predicate は日本語にすると「述語」ですが、ここでは「関係ラベル」と考えると分かりやすいです。
たとえば A joins_with B は、「AはBとjoinできる」という関係を表します。ここで joins_with が関係ラベルです。
この実験では、関係ラベルを次の8個に絞りました。
core:
- subclass_of
- instance_of
- part_of
- depends_on
- references
- same_as
data_catalog:
- joins_with
- derived_from
関係は source predicate target の形で読みます。
sourceは、relationships: を書いているファイルです。
たとえば event_count.md のfrontmatterに次のように書いた場合、
relationships:
- predicate: derived_from
target: /tables/events_.md
これは「event_count.md は /tables/events_.md から導出される」と読みます。
今回使った関係ラベルの定義は次の通りです。
| 関係ラベル | 定義 | OKFでの使いどころ |
|---|---|---|
subclass_of |
sourceがtargetのより具体的な種類であることを表す |
BigQuery Table が Data Asset の一種、のような分類関係 |
instance_of |
sourceがtargetで表される型・カテゴリの具体例であることを表す |
events_ テーブルが BigQuery Table という型の実体である、のような関係 |
part_of |
sourceがtargetの一部、配下、構成要素であることを表す | テーブルがデータセットに属する、フィールドがテーブルに属する |
depends_on |
sourceを理解・実行・計算するためにtargetが必要であることを表す | クエリが特定テーブルに依存する、手順が特定ポリシーに依存する |
references |
sourceがtargetを参照していることを表す。depends_on より弱い関係 |
補足資料、仕様書、外部ドキュメントへの参照 |
same_as |
sourceとtargetが同じ概念を指していることを表す | 別名、移行前後のID、重複している概念ファイルの対応 |
joins_with |
sourceがtargetとjoinできる、またはjoin仕様に接続していることを表す | テーブル同士のjoin、joinキー、join仕様ファイルへの接続 |
derived_from |
sourceがtargetから導出・計算・生成されることを表す | メトリクスがテーブルから計算される、集計テーブルが元テーブルから作られる |
全部を厳密なオントロジー語彙にしようとすると、設計が重くなります。OKFの良さは軽さなので、まずはAIエージェントの検索に効きやすい関係だけに絞っています。
subclass_of と instance_of は、雑に is-a とまとめずに分けています。
たとえば「GA4 events table は BigQuery Table の一種」と、「このファイルは特定datasetの一部」は意味が違います。
前者は分類の関係です。後者は所属の関係です。
データカタログでは、特に joins_with と derived_from が効きます。
frontmatterに直接書く
OKFファイルのfrontmatterに relationships: を追加します。
例:
relationships:
- predicate: part_of
target: /datasets/ga4_obfuscated_sample_ecommerce.md
- predicate: joins_with
target: /references/joins/events___ads_clickstats.md
via: collected_traffic_source.gclid
この例は、2つの関係を表しています。
1つ目は part_of です。
- predicate: part_of
target: /datasets/ga4_obfuscated_sample_ecommerce.md
これは、「このファイルは /datasets/ga4_obfuscated_sample_ecommerce.md で表されるデータセットの一部である」という意味です。
2つ目は joins_with です。
- predicate: joins_with
target: /references/joins/events___ads_clickstats.md
via: collected_traffic_source.gclid
これは、「このファイルは /references/joins/events___ads_clickstats.md に書かれたjoin仕様と関係があり、そのjoinには collected_traffic_source.gclid を使う」という意味です。
predicate: joins_with があると、AIエージェントは「これはjoinのために辿るべき関係だ」と判断しやすくなります。
OKFの仕様では、frontmatterに独自の追加項目を持たせることができます。そのため、relationships: はOKFのミニマルさと相性がいいです。
relationships: を知らないツールでも、Markdown本文はこれまで通り読めます。一方で、relationships: を理解するツールやエージェントは、関係ラベルを使って必要なファイルを選びやすくなります。
実装
検証に使ったコードとデータは、以下のリポジトリで公開しています。
検証用コードは次のような構成にしました。
okf-ontology-profile-demo/
bundles/
ga4/
stackoverflow/
crypto_bitcoin/
scripts/
build_graph.py
eval_claude_code_workspace.py
eval/
workspace_questions.yaml
derived/
claude_workspace_eval_results.json
claude_workspace_eval_results.csv
claude_workspace_eval_summary.json
使ったOKF bundle(OKFファイル群)は、GoogleCloudPlatform/knowledge-catalogで公開されている3つです。2
- GA4
- Stack Overflow
- Bitcoin
bundleのMarkdown本文は元リポジトリのまま使い、relationships: はこの検証のために付与しました。
python3 scripts/build_graph.py --all
python3 scripts/eval_claude_code_workspace.py --model sonnet --runs 5
build_graph.py は、各Markdownのfrontmatterを読み、relationships: を集めます。そして、検索で使いやすいように derived/<bundle>/okf.graph.json というJSONファイルを書き出します。
このJSONは、人間が直接編集するマスターではありません。OKFファイルから作る検索用インデックスです。
評価用スクリプトは eval_claude_code_workspace.py です。relationships: の有無だけが異なるOKFディレクトリを作り、AIエージェントが読むファイル、回答、usage tokens、レイテンシーを記録します。
評価設計: Claude Codeに実ファイルを探索させる
検証では、relationships: の有無だけが異なる2種類のOKFディレクトリを用意しました。
-
baseline_workspace: frontmatterからrelationships:だけを抜いたOKF -
graph_workspace:relationships:を残したOKF
Markdown本文、Markdownリンク、ファイル構成は同じです。比較したい差分は、frontmatterに書かれた関係情報だけです。
各質問に対して、AIエージェントには回答、根拠として使ったファイル、根拠の要約をJSONで返させます。
{
"answer": "...",
"cited_files": ["/tables/events_.md"],
"evidence": [
{
"file": "/tables/events_.md",
"fact": "..."
}
]
}
この出力と実行ログを使って、回答の正しさ、読まれたファイル数、読まれたコンテキスト量、usage tokens、レイテンシーを比較します。
評価指標
今回の主な評価指標は、次の5つです。
Answer check score
回答に必要な語句が含まれているかを、決定的なルールで採点します。
たとえば、「GA4 eventsとGoogle Ads click dataをどうjoinするか」という質問では、回答に次のような要素が入っているかを見ます。
collected_traffic_source.gclidADS_CLICKS.gclid-
events_またはGA_EVENTS
LLMに採点させると評価自体が揺れるため、ここでは単純なキーワードチェックにしています。
Read file count
Claude Codeが Read したMarkdownファイル数です。
同じ正しさで答えられるなら、読むファイルは少ない方がよいです。
Read context tokens
Claude Codeが読んだファイル本文の概算トークン数です。
ここでは厳密なモデル別tokenizerではなく、ローカルの簡易カウントを使っています。絶対値そのものよりも、baseline_workspace と graph_workspace の相対差を見るための値です。
Claude Code usage tokens
claude -p が返すusage tokensです。
これは、ファイル探索、ツール結果の読み込み、回答生成まで含んだ値です。
Latency
claude -p が開始してから結果を返すまでのend-to-end時間です。
ローカルファイル検索だけの時間ではありません。Claude Codeが判断し、ツールを呼び、回答するまでの時間です。
質問セット
質問は7問に絞りました。
ファイル名だけで当たりやすい質問ではなく、join、計算元、所属関係など、関係を辿る必要がある質問を中心にしています。
例:
- id: ga4_ads_join_key
bundle: ga4
question: How can GA4 events be joined to Google Ads click data? Include the join key.
gold_files:
- /tables/events_.md
- /references/joins/events___ads_clickstats.md
answer_checks:
- any:
- collected_traffic_source.gclid
- any:
- ADS_CLICKS.gclid
- ads_clicks.gclid
gold_files は、その質問の根拠として想定する正解ファイルです。後述するrecall系の補助指標の分母になります。
この質問では、単に関連ファイルを当てるだけでなく、join keyまで回答できるかを見ます。
実測結果
7問に対して baseline_workspace と graph_workspace を5回ずつ実行しました。
合計70回の claude -p 実行です。モデル指定は --model sonnet です。
| 条件 | 実行数 | Answer check score | Pass rate | Read files | Read context tokens | Tool calls | Claude Code usage tokens | Latency |
|---|---|---|---|---|---|---|---|---|
| baseline_workspace | 35 | 0.9943 | 0.9714 | 2.94 | 2082.9 | 5.43 | 40925.2 | 18008.9ms |
| graph_workspace | 35 | 0.9943 | 0.9714 | 2.37 | 1136.7 | 4.66 | 33922.0 | 15130.5ms |
relationships: を残した graph_workspace は、回答の正しさを同等に保ったまま、探索コストだけが下がりました。
- Answer check score / Pass rate: 変化なし(
0.9943/0.9714。取りこぼしは両条件とも同じ1問で1回ずつ) - Read files:
2.94 -> 2.37、約19.4%減 - Read context tokens:
2082.9 -> 1136.7、約45.4%減 - Tool calls:
5.43 -> 4.66、約14.2%減 - Claude Code usage tokens:
40925.2 -> 33922.0、約17.1%減 - Latency:
18008.9ms -> 15130.5ms、約16.0%減
ただし、一様に改善したわけではありません。同じ質問・同じrun indexでのペア比較では、読んだファイル数が増えたペアはゼロだった一方、usage tokensやレイテンシーはむしろ増えたペアもあり、平均の差は一部の設問が作っています。
差が出やすかったのは、joinや計算元の関係を辿る質問です。たとえば ga4_event_count_source では、frontmatterの derived_from から計算元をすぐ特定できるため、read context tokensが 3603.8 -> 1439.6 に減りました。逆に、答えに必要なテーブルが多く結局すべて読む質問では、差は出ませんでした。
注意点として、read recall(gold filesのうち実際に読まれた割合)は 0.924 -> 0.814 に下がっています。これは、join keyが明示されていればより少ないファイルで正答できるためで、悪化ではありません。エージェントの実利用に近い評価では、「関連ファイルを全部読んだか」ではなく、正答できたか・どれだけ少ない探索で到達できたかを見る必要があります。
今回の結果では、relationships: は単なる飾りではなく、Claude CodeがOKF内の関係を辿るためのショートカットとして働いていました。
まとめ
OKFの良さは、Markdownとして読めること、仕様が小さいことです。
その良さを保ったまま、AIエージェントにとって読みやすくするなら、まずは relationships: のような小さな拡張がちょうどよさそうです。
Markdownリンクは「このファイルはあのファイルと関係がある」ことを表せます。
relationships: は、そこから一歩進んで「どう関係しているのか」を表せます。
part_of なら所属関係、joins_with ならjoin関係、derived_from なら計算元の関係です。
この違いが分かるだけで、エージェントは読むべきファイルを選びやすくなります。
OKFを重いオントロジー形式に変える必要はありません。
まずはfrontmatterに relationships: を足す。
そのくらい薄い関係レイヤーでも、AIエージェントのコンテキスト選択には十分に効く可能性があります。