🔗 この記事は https://blog.tak3.jp/ja/blog/writing-db-meaning-for-ai/ からの転載です(一次情報源)。
前回、Kozou の紹介記事でこう書いた。意味の置き場は PostgreSQL に最初からある、COMMENT ON とビュー定義だ、まず意味を DB に書け — と。生の DDL しか見ない AI エージェントは、列名と型を読むのはうまいが、どの列が罠で、どのビューが「正」かを知らない。だから、もっともらしく間違う。
では、その意味をどう書けば AI に効くのか。本稿はその実装編だ。答えは列名でも型でもなく、COMMENT ON とビュー定義に、決まった作法で書くことにある。題材には Kozou の quickstart に同梱されたデモスキーマ(小さなオンラインストア)を使う。デモの実物 COMMENT は英語で書かれているが、本稿は日本語で書いた作例で示す — 後で見るとおり、COMMENT の本文は日本語で書いてよいからだ(実物の英語版と、そのまま動く quickstart は kozou.org / GitHub にある)。ここで示す書き方は Kozou を導入する前から、素の PostgreSQL で実践できる。
意味は「行頭タグ」に書く
COMMENT ON の中に、Kozou が解釈する規約タグを行頭に書いていく。意味を書く中心になるのは3つ、@ai / @policy / @example だ。ほかに UI 向けの @widget、関数向けの @expose があるが、AI に意味を渡すという本稿の目的では扱わない。
| タグ | 用途 | 書ける対象 | 本稿で扱う |
|---|---|---|---|
@ai |
AI エージェントへの指示・背景(自由文) | テーブル / 列 / ビュー / 外部キー / 関数 | ◎ 中心 |
@policy |
ビジネスルールの記録(助言。強制はしない) | 同上 | ◎ 中心 |
@example |
ビューに実行可能な例クエリを添える | ビュー | ○ |
@widget |
管理 UI の入力コントロール指定 | 列 | 触れるだけ |
@expose |
関数を RPC アクションとして公開 | 関数 | 触れるだけ |
書き方には作法がある。地味だが、外すと効かない。
-
行頭に書く。 既知のタグ(
@ai、@policy、…)を行の途中に置くと、タグとして解釈されず、説明文の一部としてそのまま残る(Kozou は警告を出す)。 -
@ai/@policyは1タグ1行。 言いたいことが複数あるなら、@ai:の行を複数並べる。1つの注記が複数行にまたがってよいのは@exampleブロックだけだ。
そして — この記事の眼目でもあるが、本文は日本語でよい。 タグ名(@ai など)と、その直後の区切り : は半角だが、それ以降の本文は言語を選ばない。タグ名は ASCII の識別子として、区切りは半角コロンとして認識され、あとに続くテキストは(改行まで)そのまま verbatim に AI へ渡る。だから日本語で書いて、そのまま効く。
ひとつだけ、日本語話者がはまりやすい罠がある。区切りのコロンは半角 : でなければならない。IME が全角にした @ai:(全角コロン)は、タグとして認識されず、警告も出ないままただの説明文に落ちる(構造化されない)。行頭のタグ名と区切りだけ半角にすれば、本文中の全角コロンや句読点は問題ない。
@ai:レポートに使わない -- ✗ 全角「:」→ タグと認識されず、静かに説明文へ落ちる
@ai: レポートに使わない -- ✓ 半角「:」→ タグとして認識される
書くことは、そのまま検索性にもつながる。Kozou の search_schema は、オブジェクト名だけでなく COMMENT 本文と @ai / @policy の注記も検索対象にする(日本語の本文も、そのまま部分一致で検索できる)。注記に書いた言葉そのものが、AI が意味に辿り着くための手がかりになる。
以降の COMMENT 例は、Kozou デモスキーマの内容を日本語で書いた作例だ(テーブル名・列名などの識別子は実物のまま英語)。COMMENT 文字列の中の
''(連続するシングルクォート2つ)は、SQL 文字列内でのシングルクォートのエスケープ。横に長い行はそのまま1行で書いている(作法どおり、1つの@ai:注記は1行だ)。
@ai: — 罠を名指しし、正本へ誘導する
@ai は AI エージェント向けの自由文だ。効かせるコツは、「この列が何か」の説明で終わらせず、**「AI が何をすべきか、何をすべきでないか」**を、多くは命令形で書くこと。3つ見る。
まず、いちばん分かりやすい罠。使ってはいけない列だ。
COMMENT ON COLUMN orders.amount_total IS '非正規化された合計金額(非推奨)。
@ai: レポートに使わないこと。これはアプリが更新をやめた古いキャッシュで、明細と食い違い、テスト注文も含む。売上は vw_recognized_revenue から計算する。';
amount_total は「合計金額」というそれらしい名前と numeric(12,2) という型を持つ。売上を聞かれた AI がこれを SUM したくなるのは自然だ。だが実体は、アプリが更新をやめた古いキャッシュで、明細と食い違い、テスト注文も混ざっている。ここでの書き方の型は、「使うな」で終わらせず「代わりに vw_recognized_revenue を使え」という逃げ道を必ず添えること。罠を名指しするだけでは、AI は次にどこへ行けばいいか分からない。
次は、常に効く除外条件。
COMMENT ON COLUMN orders.is_test IS 'QA・負荷試験用の内部フラグ。
@ai: is_test = true は、売上・注文件数・ダッシュボードのすべてから常に除外すること。実顧客の注文ではない。';
型は、強い言葉(「常に」「決して」)で、適用範囲まで書くこと。「売上でも、件数でも、ダッシュボードでも除外せよ」と範囲を明示すると、AI はどの文脈でこのルールが効くのかを取り違えにくい。
3つめは、いちばん厄介な「静かに間違う」罠だ。
COMMENT ON COLUMN products.list_price IS '現在のカタログ価格。
@ai: この価格は時間とともに変わる。過去の注文の評価には絶対に使わないこと。各注文は注文時点の価格を order_items.unit_price に捕捉している。list_price を過去の注文に結合すると、売上を静かに誤って評価してしまう。';
list_price(現在のカタログ価格)で過去の注文を評価すると、JOIN は成立し、クエリはエラーも出さず、金額だけが静かに狂う。捕捉すべきは注文時点の価格 order_items.unit_price のほうだ。エラーにならず結果だけが誤る類の罠は、「なぜ誤るのか」まで書いておく。そうしないと、間違いに気づく手がかりが誰にも残らない。
こうして書いた @ai は、どう AI に届くのか。Kozou がスキーマを読むと、@ai 行は MCP(AI エージェントに外部ツールをつなぐ標準プロトコル)経由の応答で aiDescription として、その列のすぐ隣に載る。
{
"name": "amount_total",
"dataType": "numeric(12,2)",
"aiDescription": "レポートに使わないこと。これはアプリが更新をやめた古いキャッシュで、明細と食い違い、テスト注文も含む。売上は vw_recognized_revenue から計算する。"
}
書いた注意書きが、書いたとおりの言葉で、列の隣に届く。1つの列に複数の @ai: 行を並べていれば、それらは改行で連結され、1つの aiDescription にまとまる。
@policy: — 助言であって、強制ではない
@ai が「こう振る舞ってほしい」なら、@policy は「これは破ってはいけない規則だと記録しておきたい」ときに使う。2つ見る。
COMMENT ON TABLE orders IS '顧客の注文。
@ai: 注文が認識済み売上になるのは status = ''paid'' かつ is_test = false かつ deleted_at IS NULL、さらに顧客が論理削除されていないときだけ。各明細は order_items.unit_price(捕捉時の価格)で評価し、products.list_price は使わない。
@ai: vw_recognized_revenue ビューがこれらのルールをすべて適用済み。売上の質問はまずそこから始める。
@policy: ''refunded'' と ''chargeback'' は売上を取り消す。決して売上に数えない。';
COMMENT ON TABLE customers IS '注文する人々。
@ai: deleted_at IS NOT NULL の行は論理削除済み(監査・法的保持のために残している)。顧客向けのクエリ・件数・指標からは除外すること。
@policy: email は個人データとして扱う。集計や公開レポートで露出させないこと。';
ここに、紹介記事でも触れた一線がある。@policy は AI への助言であって、施行ではない。「email を集計や公開レポートに出すな」と @policy に書いても、それ自体がアクセスを止めるわけではない。実際の強制は、PostgreSQL 側の権限(GRANT)と行レベルセキュリティ(RLS)に残る。意味の供給と、権限の施行を混ぜない — これは Kozou の設計思想であると同時に、書き手の側でも意識しておく線引きだ。本当に見せたくないなら、コメントではなく権限で止める。
@policy は、@ai(改行連結された1つの文字列)とは別枠で、規則の配列として AI に渡る。「考え方の指針」と「破ってはいけない規則」を、別のものとして届けられる。書き分けの目安は、「AI にこう考えてほしい」=@ai、「これは規則だと記録したい」=@policy。ただし強制を担保するのはコメントではなく DB の権限、という一線は忘れない。
ビューを「名前の付いた概念」にする
ここまでは列やテーブルに注記を足す話だった。もう一段上の書き方がある。「正しいやり方」そのものを、ビューとして定義してしまうことだ。
「認識済み売上の正しい出し方」が人間の頭の中にあるなら、それをビューにすれば、名前の付いた、実行可能な概念になる。デモスキーマの vw_recognized_revenue はまさにそれだ。
CREATE VIEW vw_recognized_revenue AS
SELECT o.id AS order_id, o.customer_id, o.placed_at, o.channel,
sum(oi.quantity * oi.unit_price - oi.discount) AS net_revenue
FROM orders o
JOIN customers c ON c.id = o.customer_id AND c.deleted_at IS NULL
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status = 'paid' AND o.is_test = false AND o.deleted_at IS NULL
GROUP BY o.id, o.customer_id, o.placed_at, o.channel;
テスト注文の除外も、論理削除された注文・顧客の除外も、'paid' だけを数えることも、明細単価での評価も、すべて定義に織り込まれている。散らばっていたルールが、1つの実行可能な定義に集約される。
そのビューにも COMMENT ON を書く。
COMMENT ON VIEW vw_recognized_revenue IS '認識済み売上の正本。paid な注文1件につき1行。
@ai: これが売上の正本(source of truth)。テスト注文・論理削除された注文と顧客・paid 以外のステータスをすべて除外済みで、各明細は捕捉時の unit_price から discount を引いた額で評価している。売上・販売の質問はここから始める。orders.amount_total や products.list_price から再導出しないこと。
@example: 四半期ごとの売上。
SELECT date_trunc(''quarter'', placed_at) AS quarter,
sum(net_revenue) AS revenue
FROM vw_recognized_revenue
GROUP BY 1
ORDER BY 1;';
書き方の型はこうだ。ビューの @ai で **「これが正本だ、売上の質問はここから始めろ、生テーブルから再導出するな」**と宣言する。そして @example に、そのビューの代表的な使い方を添える。@example は、説明を1行書き、続くインデント行に SQL を書く — 本稿で唯一、複数行にまたがってよい注記だ。上のブロックで、@ai は1行、@example は複数行になっているのはそのためだ。
概念は積める。別のビューが、このビューを土台にできる。
COMMENT ON VIEW vw_customer_lifetime_value IS 'アクティブな顧客ごとの累計認識済み売上。
@ai: 顧客生涯価値は vw_recognized_revenue を再利用しているので、同じ認識ルールが自動的に適用される。論理削除された顧客は除外される。';
vw_customer_lifetime_value は vw_recognized_revenue を選択元にする。だから、そのビューのルール(テスト注文の除外・論理削除の考慮)は自動的に適用され、書き直す必要はない。ただし継承されるのは実行可能な定義(ルール)であって注記ではない — @ai は各ビューに自前で書く(現に vw_customer_lifetime_value も自前の @ai で「再利用している」と明示している)。概念の上に概念を積むと、一度書いたルールが下から効き続ける。注記だけは、各概念に書く。
Kozou はこうしたビューを「ドメイン概念」として扱う。AI から get_concept_context を呼ぶと、そのビューの @ai 注記・推奨クエリ元・関連テーブル・@example の例が、まとめて返る。AI はビジネスルールを生テーブルから組み立て直す代わりに、ルールを内包したビューへ向かえる。
そして、テーブルをまたぐとき。ON 条件そのものは、外部キーを見れば機械が導ける。だが 「なぜこの JOIN があるのか」 は、外部キー制約に人間が書く意味のほうだ。外部キー制約に COMMENT を書いておくと、Kozou はその文面を JOIN の「目的(purpose)」として、ON 条件と一緒に AI へ渡す。デモスキーマは外部キーにコメントを付けていないので、ここは書き方の作例を示す。
-- 作例(デモスキーマには未収録・書き方の例)
COMMENT ON CONSTRAINT orders_customer_id_fkey ON orders IS '各注文はちょうど1人の顧客に属する。
@ai: 売上を顧客に紐付けるときは、この外部キーを辿って JOIN する。email や名前で一致させないこと。';
「この関係は何のためにあるのか」+「どう結ぶべきか(曖昧な一致を避けよ)」を書いておけば、外部キーがあるのに email や名前で結ぼうとする AI に、正しい結び方を指し示せる。
注記は保守する — 陳腐化への注意
書いた意味には、賞味期限がある。@ai や @policy は一次資産だが、スキーマを変えたのに注記を更新しなければ、AI は古くなった主張を、確信を持って信じてしまう。「この列は使うな」と書いた列が実は現役に戻っていた、という食い違いは、生の DDL の食い違いより気づきにくい — 注記は自信たっぷりに間違いを語るからだ。
だから、マイグレーションで列やルールを変えたら、対応する @ai / @policy も同じ変更の中で直す。COMMENT の鮮度をテストで守る仕組みは基本的に無いので、最後の砦はレビューの目視になる。スキーマの diff を見るとき、コメントの diff も一緒に見る習慣をつけたい。
書いた意味は、そのまま AI に届く
意味を書くこと自体は、素の PostgreSQL で完結する。COMMENT ON とビュー定義は、スキーマと同じ場所に住み、スキーマと一緒にマイグレーションされる一次資産だ。Kozou が無くても、この習慣は単体で価値がある — 新しく入った人のオンボーディング、レビューでの認識合わせ、将来ツールを乗り換えるときの引き継ぎ。意味がスキーマに同梱されていれば、誰が読んでも同じ一次情報に当たれる。
その書かれた意味を、要約も言い換えもせず(verbatim)AI エージェントに配送するのが Kozou だ。タグの解釈も、search_schema での発見性も、aiDescription / policy への整形も、get_concept_context の組み立ても、Kozou 側の仕事になる。さっき書いた amount_total の @ai が、そのまま aiDescription として列の隣に届く様子は、すでに上で見たとおりだ。ひとつ安心なのは、**Kozou の MCP は既定では「読むだけ」**で、SQL を生成・実行・書き込みするツールは既定で存在しないこと。意味を渡すことと、DB を操作することは、はっきり分かれている。
セットアップやクライアント別の設定、そして実物(英語)のデモスキーマは kozou.org と GitHub にまとまっている。手順はここでは繰り返さない — どうやるか はドキュメントが source of truth だ。
なお、スキーマとコメントという1つのソースから、AI 向けの文脈だけでなく管理 UI・REST・ドキュメント・型まで導く、という Kozou のもう一つの側面(「1つの定義から、多くの忠実な形へ」)については、稿を改める。
まとめ
- 意味は列名や型ではなく、
COMMENT ONとビュー定義に書ける。作法は 行頭タグ・@ai/@policyは1タグ1行・要点が増えたら行を並べる。そして本文は日本語でよい(タグ名と区切り:だけ半角)。 -
@aiは罠を名指しして正本へ誘導する(「使うな」+「代わりに○○を使え」)。@policyは破ってはいけない規則の記録だが、あくまで助言であって、強制は DB の権限と RLS に残す。 - ビューは「名前の付いた概念」にできる。正本ビューに
@ai(これが正本だと宣言)と@example(代表クエリ)を添え、概念を合成すればルールは継承される。外部キーのコメントは JOIN の目的として渡る。 - 注記は書いて終わりではない。スキーマを変えたら同じ変更で注記も更新する。古い注記は、自信を持って AI を誤らせる。
- こうして書いた意味を、Kozou がそのまま AI に配送する。まず、意味を DB に書け。
リポジトリは github.com/kozou-dev/kozou(Apache-2.0)、ドキュメントは kozou.org。デモスキーマは quickstart に同梱されている。自分のスキーマに @ai: を1行足すところから始めてみてほしい。