AWS の Context Ontology Accelerator を試してみた
本文の状態
日本語全文を表示中
詳細モードで約33分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
ExaWizards Tech Blog
AWS が公開した OSS の Context Ontology Accelerator を試すことで、AI エージェントがデータベースの文脈を正しく理解し SQL を生成する仕組みとオントロジーの基礎概念が解説される。
AI深層分析を開く2026年9月4日 20:17
AI深層分析
キーポイント
COA の目的と機能
AWS が OSS として公開した Context Ontology Accelerator は、AI エージェントがデータベースの文脈(売上定義など)を誤解する問題をオントロジーで補うために設計されている。
オントロジーの技術的定義
記事ではオントロジーを「ドメイン概念とその関係を機械が読める形で記述したもの」と定義し、OWL による公理の役割や推論の仕組みを解説している。
関連用語と技術スタック
RDF、RDFS、OWL、SPARQL、SHACL といったオントロジー構築に不可欠な言語やモデルの定義と相互関係について整理が行われている。
オントロジーの定義と役割
オントロジーはドメイン内の概念や関係を機械が読める形で記述するもので、データベーススキーマのように違反データを弾くのではなく、書かれていない事実を推論するために機能する。
OWL の公理と開世界仮説
OWL はクラスやプロパティの関係を「公理」として宣言し、開世界仮説に基づいて未知の情報を偽ではなく不明として扱うため、推論を導くことが可能になる。
重要な引用
AI エージェントにデータベースを扱わせると、SQL は動くのに数字の意味が違う、という壊れ方をすることがあります
オントロジーの公理は、書かれていないことを導くために働きます
スキーマの制約は、違反するデータを弾くために働きます
オントロジーの公理は、書かれていないことを導くために働きます。
編集コメントを表示
編集コメント
AI エージェントの信頼性を高めるために、単なるスキーマ定義を超えた文脈理解の重要性が改めて浮き彫りになった記事である。オントロジーというやや難解な概念を、具体的な実装例を通じて解説している点に価値がある。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
先端技術開発グループ(WAND)の伊賀です。
AI エージェントにデータベースを扱わせると、SQL は動くのに数字の意味が違う、という壊れ方をすることがあります。**
例えば「売上」はどのカラムをどの条件で集計した値なのか、テーブル名と型だけで決めるのは難しそうです。
そこをオントロジーで補おうとしているのが、AWS が OSS として公開した Context Ontology Accelerator(以下 COA)です。
どのように動作するのかなど気になったので、AWS アカウントにデプロイし、データベースの接続から自然言語での問い合わせまで一通り試してみました。
この記事はその記録です。
COA の入門を兼ねるつもりで、オントロジーという言葉になじみがなくても読み通せるように、自分が引っかかった用語のおさらいから始めます。
オントロジーとは
この記事でいうオントロジー
オントロジー(ontology)は、もともと「存在論」を意味する哲学の用語のようです[[1]](#fn-0929-1)。
ここでは、あるドメインに登場する概念とその関係、そこで前提として置く決め事を機械が読める形で書いたもの、と捉えることにします[[2]](#fn-0929-2)。
オントロジーを機械が読める形で記述する言語として、代表的なのが OWL(Web Ontology Language)です。
OWL では、クラス、プロパティ、個体などがオントロジーの構成要素になります。
EC サイトを例にすると、次のようになります。
- クラス:ドメインに登場する概念(
Customer、Order、Product)
- 個体:ドメインに実際に存在する対象(特定の顧客や注文)
- データプロパティ:個体と文字列や数値を結ぶ属性(
Customer.email、Order.total_amount)
- オブジェクトプロパティ:個体どうしの関係(Customer が Order を発注する、Order が Product を含む)
これらの構成要素を使って「これを前提として認める」と宣言した文を、OWL では公理(axiom)として記述します。
数学の公理と同じで、証明されるものではなく推論の出発点です。
異なるのは、少数を厳選するのではなく、クラスの上下関係もプロパティの性質も個々の事実も、すべて公理として書き足していく点です。
例えば「すべての Order はちょうど1人の Customer に属する」は、Order クラスについて宣言した公理です。
データベースのスキーマにも見えますが、書いたものの役割が異なります。
スキーマの制約は、違反するデータを弾くために働きます。
オントロジーの公理は、書かれていないことを導くために働きます。
例えば「発注するのは Customer である」という公理のもとで「X が発注した」という事実を足すと、X は弾かれるのではなく、「X は Customer なのだろう」と推論されます。
このあと頻出する略語も、ここで一度整理しておきます。
このうち RDF だけはデータモデルそのもので、残りはいずれも RDF グラフを対象とする言語です。
- RDF(Resource Description Framework):主語、述語、目的語の三つ組でデータを表す抽象データモデル
- RDFS(RDF Schema):RDF にクラスとプロパティの階層を導入する拡張。
rdfs:subClassOfなどは OWL でもそのまま使われる
- OWL(Web Ontology Language):クラス、プロパティ、個体について、前提として宣言する公理を記述する言語。RDFS をさらに拡張して定義され、RDF グラフとして表現される
- SPARQL(SPARQL Protocol and RDF Query Language):RDF グラフへの問い合わせ言語
- SHACL(Shapes Constraint Language):RDF データが満たすべき条件を宣言して検証する制約言語。OWL の公理が推論を導くのに対し、SHACL は違反データを報告する
OWL は開世界仮説(open world assumption)に立ち、書かれていないことを「偽」ではなく「不明」として扱います。
公理が弾くためではなく導くために働くのは、この前提の上に立っているからです。
この性格は、後で COA が生成する公理を読むときに効いてきます。
金融業界の FIBO(Financial Industry Business Ontology)や汎用の Schema.org のように、業界単位で整備された既製のオントロジーもあります。
ナレッジグラフ**という語の定義は一つに定まっていませんが、実務では、オントロジーで定義した概念に沿った実データを、グラフとして問い合わせ可能な形で保持したものを指すことが多いようです。
AI エージェントとオントロジーの関係
オントロジーを RDF や OWL で記述する技術は、セマンティック Web の文脈で2000年代から研究されてきました。**
それが今、AI エージェントの文脈で持ち出されているのは、LLM の推測を、明示した定義で補えるからです。
LLM にデータベースを扱わせる場合、素朴にやるとスキーマ情報(テーブル名、カラム名、型)をプロンプトに渡して SQL を書かせることになります。
もちろん、意味を保持する手段はデータベース側にもあります。
CHECK 制約、外部キー、テーブルとカラムの COMMENT、業務的な定義を埋め込んだ VIEW、別管理のデータ辞書は、いずれも意味の担い手です。
問題になるのは、それらが整備されていないか、整備されていてもエージェントに渡されていない場合です。
このとき「アクティブ顧客とは直近90日以内に注文した顧客を指す」といった業務上の定義は、どこからも把握できません。
推測で書かれた SQL は、構文の上では正しいのに返ってくる数字が業務の定義とずれる、という冒頭の壊れ方をしがちです。
RAG で関連文書を渡すやり方もあります。
ただ、概念同士の関係や公理を、検索のたびにテキスト断片から組み立てるのは安定させにくそうです。
オントロジーは、その関係とルールをあらかじめ機械可読な形で定義しておくやり方です。
定義を先に整備して機械に渡す発想自体は、データカタログやナレッジグラフの構築として以前からあります。
そのうえで RDF や OWL のような標準を使用すると、FIBO や Schema.org のような既製のオントロジーと接続でき、問い合わせや物理スキーマへの対応付けにも既存の仕様をそのまま使えます。
Context Ontology Accelerator とは
COA は、AWS が Apache 2.0 ライセンスで公開している、セルフマネージド型の OSS です。
公式の説明を要約すると、自社データから人のレビューを挟んだ AI 支援でオントロジーを構築し、顧客所有のナレッジグラフとして保持し、MCP 経由で任意のエージェントに提供する「セマンティックコンテキストレイヤー」です。
セマンティックレイヤーはエージェントとデータベースの間にあるデータそのものではなくデータの意味(概念の定義、関係、業務指標)を定義した層のことです。
COA はマネージドサービスではないため、自分の AWS アカウントに AWS CDK でデプロイして使います。
オントロジーの格納には Amazon Neptune、ベクトル検索には Amazon OpenSearch Serverless、オントロジーの生成や回答合成には Amazon Bedrock(Claude)が使われます。
COA が「オントロジー」と呼んで Neptune に格納するのは、前の章で整理したクラスやプロパティと公理、つまり概念の定義の部分です。
顧客や注文といった行データはグラフには複製されません。
「顧客所有のナレッジグラフ」といっても、その中身は実データではなく定義の集まりで、実データへは問い合わせのたびに SQL でアクセスします。

データソース、オントロジー、メトリクスといったリソースは、名前空間(namespace)という単位で分けて管理します。
認可も名前空間を軸に設定します。
Scan、Model、Serve
COA には構築作業用の Web アプリが同梱されており、デプロイすると使えるようになります(画面はデプロイの節でお見せします)。
フェーズは Scan、Model、Serve の3つに分かれています。
Scan ではデータソースを取り込みます。
PostgreSQL、MySQL、Redshift、SQL Server といった JDBC データソースのほか、Glue Data Catalog や Amazon S3 上のドキュメント(PDF や Word 文書など)も接続できます。
接続すると COA がスキーマを自動発見し、LLM がテーブルやカラムの説明文、類義語、キー関係を推定して付与します。
COA は推定結果をそのまま採用せず、人間がレビューして承認したものだけを次のステージに進めます。
Model では、承認済みのデータソースから Bedrock 上の Claude がオントロジーの案を作ります。
COA での呼び名は ontology induction(Web アプリでは Induction)です。
生成された案を人間がレビューし、必要なら編集して受け入れると、Neptune 上のナレッジグラフに反映されます。
あわせて、SUM(orders.total_amount) のような業務指標を、SQL 式つきのガバナンス済みメトリクス**として定義しておけます。
Serve では、できあがったセマンティックコンテキストを使うことができます。**
Web アプリ内のチャット UI である Playground、REST API、MCP サーバーの3つの入口から、自然言語で問い合わせられます。
MCP サーバーは Amazon Bedrock AgentCore Runtime 上で動き、Claude や Amazon Q など任意の MCP クライアントから接続できます。
公開されるツールは、スキーマ把握用の list_metrics と describe_schema、実行用の query、translate_sparql、rag_retrieval、graph_traversal の6つです。
induction とグラウンディング
induction には、概念の重複を防ぐためのグラウンディングという仕組みが備わっています。
新しく生成されたクラスを、FIBO のような既製のオントロジーや、その名前空間で過去に受け入れたオントロジーの概念と突き合わせます。
既存概念の下位に位置づけられるものは rdfs:subClassOf で、同一や類似と判定されたものは、概念間の対応を表す SKOS(Simple Knowledge Organization System)の skos:exactMatch や skos:closeMatch で、既存概念に結びつけます。
この照合がないと、induction を繰り返すたびに似たクラスが増えていきます。
どこまで積極的にグラウンディングするかは groundingMode パラメータで制御でき、デフォルトの ENHANCED では名前の一致だけでなく、LLM が意味の一致まで検証します。
例えば Dublin Core の「Policy(方針)」と保険ドメインの「policy(保険証券)」のように、同名で意味の違う概念を誤って統合しないための判定です。
3層のクエリ解決
Serve ステージの問い合わせは、精度の高い経路から順に試す3層構造になっています。
この3層の分け方は標準規格ではなく、COA 自身の仕組みです。
| Tier | 解決方法 | 使われる場面 |
|---|---|---|
| 1 | メトリクス解決 | 質問が定義済みメトリクスにちょうど1件マッチしたとき、その SQL 式を実行 |
| 2 | 構造化クエリ | オントロジー経由の NL→SPARQL→SQL 変換、または直接の NL-to-SQL |
| 3 | ナレッジ検索 | ベクトル検索やグラフ探索など複数の検索で文脈を集め、Bedrock が回答を合成 |
定義済みの計算を優先し、なければ SQL を組み立て、最後に検索と回答合成へ回す順番です。
Tier 1 では、質問に対して複数のメトリクスがマッチした場合、いずれかを当て推量で選ぶのではなく、曖昧と判定して Tier 2 以降に進めます。
Tier 2 には2つの経路があります。
ひとつは VKG**(Virtual Knowledge Graph、仮想ナレッジグラフ)で、自然言語をいったん SPARQL に変換し、さらに SQL へ変換して実行します。
SPARQL が問い合わせる相手はオントロジー上の概念、SQL が問い合わせる相手は物理テーブルなので、この変換には「このクラスはこのテーブル、このプロパティはこのカラム、この関係はこの結合条件」という対応表が必要です。
その対応表を宣言する W3C 標準のマッピング言語が R2RML(RDB to RDF Mapping Language)で、COA ではオントロジーの生成時にあわせて作られます(後述)。
実データをグラフに複製せず、問い合わせのたびに SQL に変換するので「仮想」ナレッジグラフです。
もうひとつは直接の NL-to-SQL(Natural Language to SQL)で、オントロジーとスキーマの情報をベクトル検索で集め、それをコンテキストとして LLM に SQL を書かせます。
SPARQL を経由するかどうかが両者の違いです。
クエリのガバナンス
LLM が書いた SQL をそのまま実データベースに流して大丈夫なのかは、気になるところです。
COA では、すべてのクエリが Cedar(AWS のポリシー言語)による名前空間単位の認可とロールベースアクセス制御を通ります。
加えて、Tier 1 と Tier 2 で生成された SQL は、実行前に COA 自身が実装する SQL ファイアウォールの検査を受けます(SQL を実行しない Tier 3 にはこの検査はありません)。
検査は SQL 文字列ではなく構文解析後の AST(抽象構文木)に対して行われ、SELECT 文だけがユーザーごとのテーブル許可リストとカラム拒否リストの範囲内で実行されます。
ユーザーが解決できるメトリクスも、許可リストで制限できます。
認可の判定で「誰のアクセスか」として扱われるのは、エージェントではなく、それを操作している人間のユーザーだと説明されています。
エージェントは専用のクレデンシャルを持たず、OIDC の認可コードフローを通じて、指示した人間のユーザーの代理として振る舞う設計です。
今回試した範囲では、Playground、REST API、MCP のいずれの入口でも、トレースには操作したユーザーの ID が残っていました。
実際に動かす
今回の環境
検証した COA は v0.1.0 相当(正確には v0.1.0 の2コミット後の 9022deb、2026-07-31)です。
以下の既定値や挙動は、すべてこのコミットのものです。
その後 v0.2.0(2026-08-13)と v0.2.1(2026-08-20)がリリースされ、この記事で触れる挙動のいくつかには修正が入っています。
修正をコードで確認できたものは、該当箇所にその旨を注記します。
リージョンは us-east-1、所要時間は検証時に計測した値です。
データは EC サイトを模した4テーブルにわたるものであり、今回の検証のために一から作った架空のものです。
画面を順番に触るだけでなく、生成されたメタデータやオントロジー、トレースも取り出して、実データと突き合わせました。
デプロイ
デプロイは CDK ベースで、Makefile から実行できます。
git clone https://github.com/aws/context-ontology-accelerator.git
cd context-ontology-accelerator
git checkout 9022deb # 本記事で検証したコミット
make setup # Smithy コード生成 + 依存関係のインストール
make deploy-dev # dev 環境へのデプロイ
スタックは全部で16個あり、Neptune、OpenSearch Serverless、API Gateway、AgentCore Runtime などが含まれます。
公式の目安は、CDK と CloudFormation のプロビジョニングで約1.5時間です。
今回の検証では合計1時間24分かかりました。
検証したコミットでは、デプロイ中に2回つまずきました(バンドル処理が PATH 上の古い Python を拾って失敗する、既定で信頼する管理ロール名が実在せずスタックの作成が止まる、の2つです)。
どちらも v0.2.1 までに修正が入っているのをコードで確認できたので、この記事では詳細を省きます。
コストには注意が必要です。
公式ドキュメントのコスト目安は「Neptune + OpenSearch Serverless cost ~$930/mo when idle」で、この2つは使っていなくても課金されます。
また、デプロイと検証が済んでいるのは us-east-1 のみで、既定のモデル ID が米国のクロスリージョン推論プロファイルを使用しているため、米国外のリージョンで使うにはモデル ID の差し替えなどが必要になります。
検証が終わったら make destroy-dev で削除し、Neptune、OpenSearch Serverless、AgentCore Runtime などの課金対象が残っていないかを個別に確認します。
デプロイが終わると、Web アプリの URL が coa-dev-web スタックの出力に現れます。
ログインすると、サイドナビゲーションに Scan、Ontology、Serve の3ステージが並んでいます(Model ステージは、画面上では Ontology という表記です)。

4テーブルの EC データを用意する
使ったのは、この検証用にスクリプトで用意した、架空の EC サイトのデータベースです。
customers:顧客。都道府県と会員登録日を持つ
products:商品。家電、食品、書籍、ファッションの4カテゴリ
orders:注文。ステータス(completed / cancelled / pending)と合計金額total_amountを持つ
order_items:注文明細。orders と products への外部キーを持つ
シードデータは顧客60名、商品24点、直近1年の注文600件と明細1200行です。
外部キーは DDL で明示的に作成しました。
induction がクラス間の関係として拾うかを見たいためです。
シードは乱数を使わず剰余演算で生成し、基準日時も固定して何度流しても同じデータになるようにしました。
テーブルとカラムには COMMENT を付けませんでした。
COA のスキャンは LLM に説明文や類義語を推定させますが、COMMENT があるとその内容に強く引っ張られると考え、今回は外しました。
手がかりをテーブル名、カラム名、型、DDL に書いた制約、実データだけに限る形にしました。
都道府県別の注文件数は意図的に偏らせてあります。
トップ3を尋ねたときに同数の県が並び、正解が曖昧になるのを避けるためです。
注文が東京都150、神奈川県90、大阪府80と並ぶように配分し、顧客数にも東京都15人から兵庫県2人まで傾斜をつけました。
正解はあらかじめ SQL で確定させておきました。
| 質問 | 期待値 |
|---|---|
| 顧客は全部で何人いますか | 60 |
| 都道府県別の注文件数トップ3 | 東京都150 / 神奈川県90 / 大阪府80 |
| 完了注文の売上合計 | 41,990,100 |
データベースは、COA からアクセスできるよう、COA と同じ VPC のプライベートサブネットに置きました。
接続用には読み取り専用ユーザーを作り、認証情報を Secrets Manager に登録しています。
セキュリティグループやシークレットの命名規則など、接続まわりの細かな設定は本題から外れるので省きます。
データソースの接続とスキャン
Web アプリにログインして名前空間(今回は ec-demo)を作成し、Sources 画面から JDBC Database として接続情報とシークレットの ARN を登録します。
登録後は REGISTERED → SCANNING → ENRICHING → PENDING_REVIEW と自動で進みます。
今回の4テーブルでは1分ほど時間がかかりました。

PENDING_REVIEW になったら、LLM が作った説明文、類義語、キー推定をレビューします。
テーブル単位の説明はおおむね合っていました。
| テーブル | 生成された類義語 |
|---|---|
| customers | customer_master, parties, accounts |
| products | product_catalog, product_master, product_registry |
| orders | purchase orders, transactions, sales orders |
| order_items | order_lines, line_items, order_details |
prefecture には "Geographic prefecture or regional subdivision where the customer is located" が付き、日本固有の行政区分も正しく解釈されていました。
ここまでは順調です。
気になったのは、外部キーの推定と字面だけでは意味が決まらないカラムの説明でした。

外部キーの推定
外部キーは3つとも参照先まで正しく認識され、確信度はいずれも95%でした。
この確信度は COA 独自のスコアで、推定時に LLM 自身が命名の一致度や型の適合を根拠に申告した値です(確定制約として読み取れた場合は 1.0 が入ります)。
ただし今回の根拠は AI_INFERRED、つまり LLM の推定となっていました。
DDL に外部キーを書いたのに、確定情報として扱われていません。
COA には information_schema から実際の制約を読む実装があり、読めていれば根拠はDETERMINISTIC になるはずです。
調べてみると、この原因は PostgreSQL 側にありました。
information_schema のビューは照会するユーザーの権限で行がフィルタされ、今回 COA に渡した読み取り専用ユーザーからは、主キーも外部キーも見えません[[3]](#fn-0929-3)。
LLM による関係の推定は、確定制約の読み取りとは別に常に走り、確定情報がある外部キーは推定で上書きしない方式となっています。
今回は確定制約が1件も取れなかったため、LLM 推定の結果だけが残っていたということです。
今回は命名が素直だったため3つとも当たりましたが、実在のスキーマで同じように当たるとは限りません。
status の説明文は実データと合っているか
生成された説明はこうです。
Current state of the order in its lifecycle
(e.g., pending, confirmed, shipped, delivered, cancelled)
実データは completed / pending / cancelled の3値だけです。
confirmed、shipped、delivered は存在せず、逆に実在する completed は挙げられていません。
一般的な EC の注文ステータスとしてはもっともらしい並びですが、このデータベースの説明としては誤りです。
total_amount も "including all items and applicable charges" と説明されました。
実際には明細の合計だけで、送料や手数料の概念はありません。
字面から決められない部分は、データ固有の説明ではなく、もっともらしい一般論で埋まってしまいました。
レビュー画面は承認の手続きというより、こうした説明を直す場所なのだと思います。
なお、今回はこの2つの誤りには気付いたうえで、修正せずにそのまま承認しました。
誤った説明文が後段のオントロジーにどう伝播するかを見たかったためです。
induction を走らせる
Ontology → Induction 画面から Start induction を実行し、承認済みの JDBC ソースを選びます。
induction は非同期ジョブとして走ります。
4テーブルでは28秒で完了し、生成された提案を受け入れて Neptune へ反映するのにさらに34秒かかりました。
結果はクラスが4つ、クラス間の関係が3つ、データプロパティが19、公理が221でした。
テーブルがクラスに、カラムがデータプロパティに、外部キーがクラス間の関係(オブジェクトプロパティ)に対応する形で、スキャンで承認したメタデータが冒頭で整理した構成要素に対応しています。
この結果はExplorer の Classes タブで一覧表示できます。

オントロジーの本体は、Turtle 形式のファイル(.ttl。以下 TTL)として REST API のダウンロードエンドポイントから取得できます。
以下の抜粋もそこから引用しました。
外部キーは、rdfs:domain と rdfs:range を持つオブジェクトプロパティとして扱われていました。
ind:orders_customerId a owl:ObjectProperty ;
rdfs:label "customer_id" ;
scl:fkProvenance "AI_INFERRED" ;
rdfs:comment "Foreign key: orders.customer_id references customers.customer_id" ;
rdfs:domain ind:Orders ;
rdfs:range ind:Customers ;
skos:altLabel "cust_id" .
説明文は rdfs:comment に、スキャンで推定された類義語は skos:altLabel に入っています。
推定の由来(fkProvenance)のような COA 固有の情報には、標準のプロパティではなく、scl: から始まる COA 独自のプロパティが使われています。
あわせて、Tier 2 の VKG が使う R2RML マッピングも生成され、そこには結合条件が入っていました(列名は SQL 識別子として二重引用符つきで埋め込まれています)。
rr:joinCondition [ rr:child "\"customer_id\"" ; rr:parent "\"customer_id\"" ] ;
rr:parentTriplesMap ind:TriplesMap_Customers .
VKG が SPARQL を SQL へ変換できるのは、この対応表があるからのようです。
主キーから作られる公理
主キーは、次の2つの形で扱われていました。
ind:Orders a owl:Class ;
rdfs:subClassOf [ a owl:Restriction ;
owl:cardinality "1"^^xsd:nonNegativeInteger ;
owl:onProperty ind:orders_orderId ] ;
owl:hasKey ( ind:orders_orderId ) .
主キーのメタデータが、OWL 上で推論に使う公理へ変換されています。
owl:cardinality は「Orders の個体はちょうど1つの orders_orderId を持つ」、owl:hasKey は「orders_orderId の値が同じ Orders の個体は同一である」ととらえることができます。
データから拾った情報を公理と呼ぶことには引っかかるかもしれません。
ただ、OWL の公理は前提として宣言された文のことで、トップダウンの設計に由来するか、データからの抽出に由来するかは問いません。
COA では、スキーマから読み取った構造を LLM が公理の候補として提案し、人間がレビューで受け入れたものが前提に加わる、という分担になっています。
ただし、SQL の PRIMARY KEY や UNIQUE と同じ制約になったわけではありません。
先に触れたとおり、OWL は開世界仮説に立つ推論の体系で、公理は違反データを検出して弾く仕組みではなく、推論の前提となります。
owl:hasKey があっても、同じキー値を持つ行の挿入が拒否されるようにはなりません。
データを検査して違反行を弾きたいなら、SHACL のような別の仕組みが必要です。
値の集合と説明文のずれ
生成物で特に気になったのが status です。
ind:orders_status a owl:DatatypeProperty ;
scl:distinctValues "cancelled", "completed", "pending" ;
rdfs:comment "Current state of the order in its lifecycle
(e.g., pending, confirmed, shipped, delivered, cancelled)" .
scl:distinctValues は実データの値と一致しました。
これは許容値の制約ではなく、スキャン時点で値の種類が少ないと判定された文字列カラムについて、実際に観測された値を並べたものです。
値の種類が多いカラムには記録されませんし、スキャン後にデータへ新しい値が入っても自動では追従しません(再スキャンで取り直されます)。
rdfs:comment には、レビューで直さなかった誤った説明がそのまま残っています。
同じプロパティに、実際に観測された値の一覧と、存在しない値を挙げる自然言語の説明が並んでいることになります。
オントロジーに載った情報がすべて確定的とは限らず、自然言語の部分には LLM の推測が混ざることがわかります。
エージェントがどちらを読むかで振る舞いが変わる可能性もあるので、rdfs:commentはレビューで念入りに確認する必要があると感じました。
グラウンディングの照合結果
induction のレポートでは、23件の照合結果がすべて novel(新規)でした。
名前空間に既製オントロジーを読み込んでいなかったので、照合相手がいなかったためです。
rdfs:subClassOf や skos:exactMatch の新しい概念を既存概念に結びつける文を見たければ、COA が用意している基礎オントロジーのカタログから名前空間に読み込むか、同じ名前空間で2回目の induction を走らせる必要があります。
型のマッピング
total_amount は xsd:decimal になった一方で、integer の order_id と timestamp の ordered_at はどちらも xsd:string となっていました。
SPARQL で数値の比較や日付の範囲指定をするときに効いてきそうな差分です。
Playground で質問する
いよいよ Serve です。
Playground から自然言語で問い合わせます。
問い合わせたのは次の2つです。
- 顧客は全部で何人いますか?
- 都道府県別の注文件数トップ3を教えて
1つ目は単一テーブルの集計、2つ目は customers と orders の結合が必要な質問です。
どちらも期待値どおりの答えが返却されました。

2つ目の質問で生成された SQL は次のとおりです。
SELECT c.prefecture, COUNT(o.order_id) AS order_count
FROM orders o JOIN customers c ON o.customer_id = c.customer_id
GROUP BY c.prefecture ORDER BY order_count DESC LIMIT 3
結合条件も答えも正解でした。
ただ、この2問だけではオントロジーの効果までは分かりません。
どちらもテーブル名とカラム名が素直で、結合すべき外部キーが1組しかないシンプルな質問のためです。
「外部キーの推定が結合条件として有効に作用した」と言うには、オントロジーを渡さない場合と比べる対照実験が必要ですが、その比較は本記事では行っていません。
Rationale パネルの読み方
Playground には Rationale というパネルがあり、解決の過程が段階ごとに表示されます。

Tier 1 でメトリクスが見つからず Tier 2 へ進んだこと、Cedar と SQL ファイアウォールを通過したこと、各段階の所要時間をここで把握することができます。
Tier 1, Tier 2, Tier 3 の3層構造が実際に動く様子が確かめられます。
ただ、表示をそのまま鵜呑みにすると誤解してしまう点が3つありました。
Confidence: 90%のような確信度は、正答確率ではなく、SQL 生成時に LLM 自身が申告した数値です。
- 確信度に添えられた説明文は、リクエストごとに生成されたものではなく、解決経路ごとの定型文です
- 実行結果の行に並ぶテーブル名は、実行された SQL が参照したテーブルではなく、SQL を作るときに LLM へ渡した候補の一覧です
3つ目は、表示と実行内容が実際に食い違うことがあります。
1問目で実行された SQL が参照したのは customers だけなのに、画面は4テーブルすべてを挙げていました。
「どのテーブルから答えを取ったか」をこの画面で監査するのは、現状では難しそうです。
もうひとつ、Tier 2 には VKG 経由と直接の NL-to-SQL の2経路がありますが、今回の2問はどちらも直接の NL-to-SQL で解決されていました。
既定の戦略が NL-to-SQL を先に試す順序になっており、そこで解決できたためです。
オントロジーを参照した変換自体は REST API の translate エンドポイントで確認でき、生成されたオントロジーのクラスを参照する SPARQL が返ってきました。
メトリクスを定義して Tier 1 を通す
Tier 1 の挙動も確かめます。
完了した注文の売上合計を、ガバナンス済みメトリクス completed_revenue として定義しました。

式は SELECT 文で書く必要があります。
Tier 1 で解決したときも SQL ファイアウォールの検査を通るため、トップレベルが SELECT でなければ弾かれます。
類義語として、日本語で「確定売上」「完了売上」「売上合計」を登録しました。
「確定売上はいくらですか?」と聞くと、答えは 41,990,100 で正解でした。
ところが、解決されたのは Tier 2 でした。
Tier 1 のメトリクス照合は miss になっていました。
Tier 2 が生成した SQL が定義とほぼ同じだったため、答えだけが偶然一致していたのです。
英語で "What is the completed revenue?" と聞くと、Tier 1 で解決しました。
| 質問 | 解決 Tier | メトリクス照合 |
|---|---|---|
| 確定売上はいくらですか? | 2 | miss |
| What is the completed revenue? | 1 | 一致(確信度 1.0) |
| completed_revenue | 1 | 一致(確信度 1.0) |
照合の内訳を見ると、英語で一致した根拠は登録した類義語ではなくメトリクス名でした。
実装を確認すると、メトリクス名と類義語を単語境界つきの正規表現(\b)に変換して質問文と照合しています。
正規表現の上では日本語の文字も単語文字なので、「確定売上」の直後に助詞の「は」が続くと境界が成立せず一致しません。
前後が空白や括弧、句読点なら一致するので日本語が全てうまくいかないわけではありませんが、通常の質問文では語の直後に助詞が続くことが多いと思います。
日本語の類義語は、登録しても効きにくいというのが今回の検証結果です。
MCP から接続する
最後に、エージェントからの入口である MCP サーバーにも接続しました。
AgentCore Runtime のエンドポイントに対して Streamable HTTP で接続します。
query ツールは REST API と同じく Tier 1 で解決され、Cedar の認可と SQL ファイアウォールも同じように通過しました。
トレースには委譲されたユーザーの ID が記録されました。
MCP 経由の translate_sparql は、SPARQL が空で返ってしまい正常に動きませんでした。
同じ質問を REST API に投げると SPARQL が返却されたので、Serve 層は動いているようです。
実装を追うと、Context Manager が SPARQL を返すときのキー名と、MCP サーバーが読み取るキー名が食い違っており、この経路では常に既定値の空文字列が返却される状態でした。
なお、このキー不一致は v0.2.1 で修正されたようです。
おわりに
今回試した範囲では、COA が自動化しているのは、既存のデータからオントロジーのドラフトを作る工程まででした。
外部キーの推定も結合条件も正しく、Playground の2問も正解でした。
ドラフトを作る手間は確かに減ります。
ただ、そのドラフトは LLM が生成するものです。
実際、status の説明に挙がった存在しない値や、total_amount に手数料を含むという説明のように、もっともらしい推測が混ざっていました。
チャットの回答に混ざった誤りなら、その会話が終われば影響もそこで終わります。
オントロジーに書き込まれた推測は、再利用される定義として残ります。
しかも公理の形式で書かれている以上、推測であっても、読む人やエージェントには前提として認められた宣言に見えます。
したがって、生成された定義をそのまま前提として採用するわけにはいきません。
生成は下書きを用意する補助と割り切り、何を前提に加えるかは人間がレビューで承認して決める、という分担が必要だと実感しました。
承認しようとすると、status が取り得る値は何か、total_amount に手数料を含むのか、といった問いに答えることになります。
どちらも字面からは決まらない、業務の側で決めるべき定義です。
今回は COMMENT を意図的に外しましたが、LLM の推測が入り込む余地の広さは、こうした定義がどれだけ明文化されていないかの裏返しでもあります。
オントロジーの構築は、暗黙になっている業務の定義を整理する作業と表裏一体なのだと思います。
今回のデータは4テーブルと小さく、対照実験もしていないため、オントロジーによって AI エージェントの挙動がどう変わるかについては結論を出せていません。
参考リンク
脚注
- 「存在するとはどういうことか」を問う形而上学の一分野です。情報科学に輸入されてからは、「対象ドメインに何が存在するか」の明示的な記述、という意味で使われています。 ↩︎
- よく引用される定義として、Tom Gruber による「概念化の明示的な仕様(an explicit specification of a conceptualization)」があるようです。 ↩︎
pg_catalog.pg_constraintは権限に依存せず読めるため、同じ読み取り専用ユーザーで照会しても3件の外部キーが返ります。COAの実装を変えれば取得できる情報です。 ↩︎
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み