はじめに
AI エージェント向けに書き溜めていた markdown 群を、Google Cloud が公開している Open Knowledge Format(OKF)v0.2 に適合させました。対象は 35 本で、足したのは必須フィールドの type だけです。
エージェントが読み書きし続けるファイルに対して「何から作ったか」「まだ現役か」を frontmatter から答えられる状態にするためです。frontmatter は、markdown ファイルの先頭に --- で挟んで書く YAML のメタデータ領域を指します。
本記事のまとめ
- 適合条件は 3 つだけで、必須フィールドは
type1 つである。推奨フィールドを足さなくても適合する。 - 独自の frontmatter 規約を OKF のキーと並べたところ、対応先が無かったのは
typeだけだった。これは私がキーを並べて出した見立てである。 - バンドル相対のパスはバンドルルート基準である。どこをバンドルルートに置くかで、適合条件の及ぶ範囲も変わる。
- 適合を確かめる linter は、2026 年 10 月 11 日時点の v0.0.1 では何も検査しなかった。機械で確かめるなら自分で用意する前提になる。

Open Knowledge Formatとは|必須はtypeだけで適合条件は3つ
OKF は markdown と YAML frontmatter のディレクトリを知識の表現形式として定めた仕様で、必須フィールドは type 1 つだけです。
Google Cloud が 2026 年 6 月 13 日に発表した、特定のベンダーに依存しない形式です。実体はディレクトリで、ツリー全体を Knowledge Bundle、個々の .md を Concept と呼びます。Concept ID は .md を除いたパスそのものなので、識別子を別に振る必要はありません。
予約されたファイル名は 2 つです。
index.md— そのディレクトリの目次。log.md— 更新履歴。
この 2 つ以外の .md はすべて Concept として扱われます。
適合条件は次の 3 つだけです。
| 条件 | 内容 |
| 1 | 予約名以外のすべての .md が、解析できる YAML frontmatter を持つ |
| 2 | すべての frontmatter が、空でない type を持つ |
| 3 | 予約名のファイルがあるなら、仕様が定める構造に従う |
裏返すと、拒否してよい理由は極端に狭く定められています。仕様は、次の 5 つを理由にバンドルを拒否してはならないと明記しています。
- 任意の frontmatter フィールドが無いこと。
- 知らない
typeの値が来ること。 - 知らないキーが混ざっていること。
- ファイル間のリンクが切れていること。
index.mdが置かれていないこと。
v0.2 では、その Concept が何から作られたかを示す provenance(sources)、誰が書いて誰が確かめたかを示す trust(generated / verified)、まだ現役かを示す lifecycle(status / stale_after)が、仕様の一級の要素になりました。
仕様の全文は SPEC v0.2 にあります。発表時の狙いは Google Cloud のブログで読めます。

独自のfrontmatter規約と並べたら、欠けていたのはtypeだけだった
もともと持っていた 5 つのキーを OKF v0.2 のキーと並べたところ、対応先が無かったのは type だけでした。
変換前の frontmatter はこうなっていました。
---
status: active
updated_at: 2026-10-10
review_after: 2027-01-17
scope: infra
sources:
- issue-41
- issue-43
- issue-72
---OKF の語彙に寄せたあとが、こちらです。
---
type: Operational Guardrail
description: IAM 付与の具体則。Group 経由、ロールの選び方
status: stable
updated_at: 2026-10-10
stale_after: 2027-01-17T00:00:00Z
scope: infra
sources:
- resource: issue-41
- resource: issue-43
- resource: issue-72
---キーの対応は次のとおりです。
| 独自のキー | OKF v0.2 | 違い |
status | status | 名前は一致。値の語彙だけ違う |
updated_at | generated.at | 対応先はあるが変換しなかった(後述) |
review_after | stale_after | ほぼ同じ概念。絶対時刻が要る |
sources | sources | 名前は一致。形が違う |
scope | 追加キーとして許容 | そのまま残せる |
| — | type | 対応先が無かった唯一のキー |
scope は OKF に無いキーですが、仕様が producer 側の追加キーを明示的に認めているため、そのまま残せます。
対応先があるのに変換しなかったキーが 1 つだけあります。updated_at です。OKF の generated は「誰が書いたか」を必須に持ちますが、過去のファイルの書き手は分かりません。一律に埋めれば記録ではなく捏造になるので、独自キーのまま残しました。仕様は generated が無くても拒否してはならないと定めています。
「ほとんど一致していた」という感触は私のもので、事実は「対応表を作ったら 5 つのうち 4 つに行き先があった」というところまでです。

適合させる手順|typeの付与からindex.mdの書き換えまで
手順に入る前に、バンドルルートをどこに置くかを決めます。ここが後の作業範囲をすべて決めるためです。
適合条件 1 と 2 は「ツリー内のすべての .md」に及びます。リポジトリルートをバンドルルートにすると、対象外にしたいファイルまで type が必須になります。仕様はサブディレクトリを配布形態として認めているので、そこで範囲を絞れます。
もう 1 つ、/ 始まりのパスが指す先も変わります。/ はバンドルルート基準で、リポジトリルート基準ではありません。バンドルの外にあるファイルを指したいときは、相対パスを使います。

frontmatterをOKFの語彙に寄せる
frontmatter の中の作業は 5 つで、同じ場所をまとめて書き換えるので一度に済ませます。
typeを全ファイルに付ける。statusの値をdraft/stable/deprecatedの 3 語に寄せる。review_afterをstale_afterに変える。- 値がファイル名と同じだった
nameを消す。 - 全ファイルに
descriptionを付ける。
type の分類は 1 種類で揃えました。ドメインの軸は scope が持っているので、type もドメインで切ると同じ軸を 2 か所に持つことになります。これは私の判断で、仕様は type の値を中央登録しません。
仕様には「status が無ければ stable とみなす」と書かれているので、元の active 22 本は行を消しても結果は同じでした。読んで分かるほうがよいので stable と明示しています。
stale_after には絶対時刻が要るため、日付だけだった値に T00:00:00Z を足しました。
sourcesはマップ形式にする
sources の各エントリは、resource キーを持つマップでなければなりません。独自の規約では、ただの文字列のリストでした。
書き換えてみると、値は 3 つの形に分かれました。
sources:
- resource: issue-41
- resource: issue-211
title: CI 自動化を廃止し /context-update に一本化
- resource: ../../../scripts/context/1 つ目の issue-41 は追えるパスではありませんが、仕様は resource に「範囲を指す記述」を書くことも認めているので通ります。2 つ目は元が括弧で注記を付けた 1 本の文字列で、注記を title に分けました。3 つ目はバンドルの外なので相対パスです。
この変換には副産物がありました。文字列をパスに直したところ、リンク先が解決しない参照が出てきました。調べると、別のリポジトリの作業記録を指していたものでした。文字列のまま並んでいるあいだは、追える参照と追えない参照が同じ見た目をしていて、区別が付きませんでした。
index.mdはfrontmatterが禁止で、本文の構造まで決まっている
予約ファイルの index.md には frontmatter を置けません。例外は、バンドルルートの index.md にだけ書ける okf_version です。
本文の形も決まっていて、見出しごとにグループ化したリンクリストにします。各項目には、リンク先の description を添えることが推奨されています。
---
okf_version: "0.2"
---
# プロジェクト
* [project.md](project.md) - 目的・対象範囲・環境一覧
* [current/status.md](current/status.md) - 現在状態。自動生成・Git 管理外
# 実行ガードレール
* [knowledge/](knowledge/) - 編集中・実行直前に目に入らないと事故るもの手元の index.md は表形式で、一覧したファイルにリンクが張られていませんでした。リンクリストに書き換えて、適合条件の 3 つ目を満たしました。
適合を確かめるlinterは、v0.0.1では何も検査しなかった
適合を機械で確かめようと okf-lint を動かしましたが、2026 年 10 月 11 日時点の v0.0.1 は違反を 1 件も報告しませんでした。okf-lint は Google Cloud の公式ツールではなく、個人が公開している npm パッケージです。
$ npx okf-lint ./context
okf-lint
Linting ./context ...
$ echo $?
0バナーを 2 行出して、終了コード 0 で終わります。これだけでは「問題 0 件」とも「対象 0 件」とも読めるので、切り分けに 2 つ試しました。
- 出力形式に JSON を指定しても、JSON は出力されなかった。
- frontmatter が無いファイル、
typeが無いファイル、列挙にないstatusを混ぜたバンドルを検査させても、違反 0 件で終了コード 0 だった。
README には、複数のフラグ、1 problem (0 errors, 1 warning) という形の出力例、終了コード 0 / 1 / 2 の仕様まで書かれています。README の記述と実装が一致していません。リポジトリ名とスター数だけを見て CI に入れると、何も検査していないのに緑になります。
npm に公開されているのは v0.0.1 の 1 版だけで、公開日は 2026 年 7 月 4 日です。GitHub 側の最終更新も 2026 年 6 月で止まっています。Claude Code 向けのツールキットも別にありますが、個人のリポジトリでした。
入手先は GitHub のリポジトリと npm の配布ページです。保守の状況は読む時点で確かめてください。
結局、適合条件の 3 つを検査するスクリプトを自分で書いて確かめました。

自作チェッカーでつまずいた点|YAMLパーサが日時を変換する
自分で書いたチェッカーは、最初の実行で stale_after を全件 NG と報告しました。ファイル側は最初から正しく、誤っていたのはチェッカーのほうです。
原因は、YAML パーサが 2027-01-10T00:00:00Z を文字列ではなく datetime に自動変換することでした。文字列として正規表現を当てていたので、1 件も一致しません。
import yaml
v = yaml.safe_load("stale_after: 2027-01-10T00:00:00Z")["stale_after"]
print(type(v).__name__) # datetime
print(isinstance(v, str)) # False
print(str(v)) # 2027-01-10 00:00:00+00:00
print(v.tzinfo is None) # False文字列に戻すと区切りが T ではなく空白になり、末尾も Z ではなくなります。ISO 8601 の形を正規表現で確かめる書き方は、ここで崩れます。tzinfo が付いているかどうかで判定するように直して、違反 0 件になりました。
適合させる前と後で変わったこと
書式を仕様に寄せただけで、本文そのものは 1 文字も変えていません。それでも、数えられる差分が出ました。35 本のうち、目次 2 本と自動生成の 1 本を除いた 32 本が frontmatter を持つ concept です。
| 項目 | 前 | 後 |
type を持つ concept | 0 / 32 本 | 32 / 32 本 |
description を持つ concept | 0 / 32 本 | 32 / 32 本 |
値がファイル名と同じだった name | 12 本 | 0 本 |
| 目次から張られたリンク | 2 本 | 35 本(解決しないもの 0 本) |
| 親の目次が抱えていた表の行 | 40 行 | 0 行 |
いちばん効いたのは目次です。前は親が knowledge の一覧を 29 行の表で抱え、サブディレクトリ側にも別の一覧ファイルがありました。そちらは 30 本中 6 本しか載っておらず、どこからも参照されていません。予約名の index.md に転換したら、親は 1 行で指すだけになりました。

毎セッション自動で読み込む入口の index.md は、7,209 バイトから 2,945 バイトになりました。ただし一覧がサブの目次へ移っただけで、2 つを足すと前とほぼ同じです。type と description を足したぶん、ディレクトリ全体はむしろ 5% ほど増えました。トークンの消費量は測っていませんし、仕様もそこを目的にしていません。仕様が掲げているのは、信頼してよいかを frontmatter から判断できることと、別のツールや別の組織に渡しても、時間が経っても同じファイルがそのまま読めることです。
得られたものは、「適合した」という状態そのものより、変換の過程で独自規約の穴が見えたことでした。追えない参照が混ざっていたことも、日付が絶対時刻になっていなかったことも、キーを仕様に合わせようとしなければ気づけません。
一方で、期待していた効果のうち 1 つは得られませんでした。作業を始めた動機は「仕様に寄せれば外部のツールがそのまま使える」というものでしたが、少なくとも linter については当てになりませんでした。
ファイル間の相互リンクは 0 本のままです。書式ではなく中身の作業なので、別に切り出しました。
まとめ
独自の frontmatter 規約があるなら、OKF に寄せるために足すものは少なく済みます。
- 適合条件は 3 つだけで、必須フィールドは
type1 つである。拒否してよい理由も極端に狭い。 - 先に決めるのはバンドルルートの位置である。
/始まりのパスはバンドルルート基準で、適合条件の及ぶ範囲もここで決まる。 - 適合を機械で確かめる手段は、試した時点では揃っていなかった。自分で用意する前提で計画する。
まずは自分のリポジトリの frontmatter を OKF のキーと並べてみてください。足りないものがすぐに分かります。
なお、linter のバージョンと提供状況、仕様の版は変わります。実際に試すときは、最新を公式ドキュメントで確認してください。
