0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI に効くスキーマの書き方 — PostgreSQL の COMMENT ON とビュー定義に「意味」を書く

0
Last updated at Posted at 2026-07-24

🔗 この記事は 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_valuevw_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.orgGitHub にまとまっている。手順はここでは繰り返さない — どうやるか はドキュメントが 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行足すところから始めてみてほしい。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?