2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OKFに軽量オントロジーを足したら、コンテキストの量が45%減った

2
Last updated at Posted at 2026-07-05

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_ofjoins_withderived_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 TableData 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_ofinstance_of は、雑に is-a とまとめずに分けています。

たとえば「GA4 events table は BigQuery Table の一種」と、「このファイルは特定datasetの一部」は意味が違います。

前者は分類の関係です。後者は所属の関係です。

データカタログでは、特に joins_withderived_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.gclid
  • ADS_CLICKS.gclid
  • events_ または GA_EVENTS

LLMに採点させると評価自体が揺れるため、ここでは単純なキーワードチェックにしています。

Read file count

Claude Codeが Read したMarkdownファイル数です。

同じ正しさで答えられるなら、読むファイルは少ない方がよいです。

Read context tokens

Claude Codeが読んだファイル本文の概算トークン数です。

ここでは厳密なモデル別tokenizerではなく、ローカルの簡易カウントを使っています。絶対値そのものよりも、baseline_workspacegraph_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_workspacegraph_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エージェントのコンテキスト選択には十分に効く可能性があります

  1. frontmatterとは、Markdownファイルの先頭に --- で囲んで書くメタデータ領域のことです。たとえば titletagstype のような情報をYAMLで書きます。本文とは別に、ツールが機械的に読み取りやすい情報を置く場所だと考えると分かりやすいです。

  2. 評価対象が変わらないように、元リポジトリの特定commitから取得しています。取得元とcommitはデモディレクトリの UPSTREAM.md に記録しています。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?