7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Autonomous AI Lakehouseのデータを Ontologyでつないで Private Agent Factoryから NL2SQL+RAG+Knowledge Graphしてみてみた

7
Last updated at Posted at 2026-10-06

詳細タイトル絵.jpg

前回の音楽フェス運営Agent に、機材・製造Lot・Stage・Task・Documentの意味と関係を加えてみました。今回は、一つの障害から、ほかに確認すべき機材と、その対応の根拠までたどるところを試します。まず、実際に確認できた結果から見ていきます。

■ Ontologyで、一つの障害から確認対象と対応の根拠をたどる

Waveform ArenaのNetwork Switchで起きた障害INC-2026-081では、公演3件に影響し、最大遅延は29分でした。障害状況を確認した後、次に知りたいのは、この一問です。

障害機材と同じ製造Lotの機材は、ほかのStageにもある? 開演前に、何を確認すればいい?

● Graph Studioで別Stageの2台へ至る経路を見る

障害機材EQ-NET-WA-01から、製造Lot NW-2603を介して、別Stageの機材2台へつながる経路を表示できました。玉には機材ID・Lot・Stage名を、矢印には関係名を表示しています。

52_VisualuzeGraph04-2.jpg

同じLotの別機材 配置Stage
EQ-NET-NG-01 Neon Groove Stage/STG-NG
EQ-NET-OM-01 Orbit Main Stage/STG-OM

同じLotに属することを根拠に、追加で確認する対象を見つけています。この関係だけで、別の機材にも故障が起きているとは判断しません。

別機材2台のLot関係と専用Taskは、本記事で追加する合成デモデータです。元Master由来の関係と区別し、出所を記録しています。

● PAFで数値・関係・文書の対応条件をまとめる

PAFの統合回答では、対象機材に加え、Databaseの数値、未完了Task、Service Bulletinの交換条件と運用手順書の手順を一つの回答で確認できました。見るポイントは、対象はどの機材か、どのTaskを優先するか、何の文書を根拠に対応するかです。

同じ会話でGraph・NL2SQL・RAGの単独質問を実行した後に得られた統合回答です。

24_NL2SQL+RAG+KnowledgeGraphで問い合わせ-3種類を組み合わせた質問01.jpg

確認対象 回答に含まれた内容
同一Lotの別機材 EQ-NET-NG-01/STG-NG、EQ-NET-OM-01/STG-OM
Databaseの事実 障害機材EQ-NET-WA-01、当日の日次最大温度91.755 ℃、最大Packet Loss 19.167 %、最低Fan 120 RPM、元データの期限超過保守1件、公演3件、最大遅延29分
未完了Task TASK-121:CRITICAL/OPEN/EQ-NET-OM-01、TASK-122:HIGH/OPEN/EQ-NET-NG-01
SBの交換条件 冷却ファン・制御基板交換、部品未着時の予備機への置換、60分負荷試験と確認基準
OPSの運用手順 常時監視、Critical時の5分以内の切替、切替後の動作確認、復旧後の隔離
Sources SB、OPS、IRの3つのASCII File Name
推奨 CRITICAL Taskを優先し、交換または予備機への置換と試験を進める提案

この回答では、SourcesにSB・OPS・IRが表示されています。IR本文の詳細、文書Version・URIの確認状況、Toolの実行記録については、後半の問い合わせ結果と検証表へ記載しています。新しい会話で複合質問1回だけを実行する場合の再現性は未確認です。

ここまでで示した成果は、確認対象の別機材が分かり、その機材への登録Taskと、対応条件の根拠までたどれたことです。今回、意味と関係をどのように定義し、実データへ対応付けたのかを、続けて説明します。

● 読みたいところから進める

本記事で使用するイベント、機器、障害、文書、Taskはすべて架空の合成データです。実在の人物、企業、製品、イベントとは関係ありません。同一Lotの別機材2件と専用Taskは、本記事で追加する合成デモデータです。

検証状況(2026-10-04更新):SQLとGraph APIの検査、PAFの単独質問・同じ会話での複合回答、Graph Studioの可視化を確認しました。実行条件・結果と未確認の範囲は、後半の「検証結果を記録して説明に使う」にまとめています。

■ この結果を支えるOntologyとは

Ontology(オントロジー)という言葉は知っていても、具体的に何を定義し、AIでどう使うのかとなると、少し分かりにくいところがあります。

「データの意味を定義する」「業務の知識をつなぐ」と説明されても、Databaseの設計や、データ同士の関連付けと何が違うのでしょうか。今回は、その意味を実際のデータと動く仕組みに対応付けながら整理してみます。

Ontologyは、ある業務に登場する概念、その意味、概念同士の関係などを、共通の定義として整理するものです。

例えば音楽フェスの運営なら、「機材」「製造Lot」「Stage」「Incident」「Task」「Document」といった概念が登場します。そのうえで、次のような関係を定義します。

  • 機材は、製造Lotに属する
  • 機材は、Stageに配置される
  • Taskは、機材を対象とし、Documentに記載された手順を使用する

ここで決めるのは、データをつなぐ列だけではありません。「同じ型番」と「同じ製造Lot」を区別すること、「未完了Task」をどの状態として扱うか、その文書をどの機材への対応根拠として使えるか、といった 業務上の意味 も整理します。こうした定義を共有しておくことで、人とシステムが同じ対象・同じ条件を使ってデータを扱いやすくなります。

そして、その定義を実際の機材IDや文書IDへ対応付け、具体的な関係としてつないだものが、今回作成するKnowledge Graphです。Ontologyで「何を意味し、どう関係するか」を決め、その関係をGraphでたどっていきます。

③ 意味を決める。実データをつなぐ。.jpg

● Ontology、Knowledge Graph、RAGの違い

・ 今回の用語を整理

用語 今回の意味
Domain Ontology 音楽フェス運営で共有する概念、関係、用語、制約の設計
Knowledge Graph Ontologyの設計に沿って実際のIDをNodeとEdgeで接続したデータ
SQL Property Graph 表やViewをVertex/EdgeとしてKnowledge Graph化するOracle AI Databaseの機能
Graph Pattern Matching GRAPH_TABLEで関係のPathを検索する処理
RAG 取得した情報を根拠にLLMが回答を生成する方式。本稿ではPDFの検索結果を利用
KG-informed RAG Graphが取得したDocument IDをRAGの検索質問へ渡す方式
Hard KG-guided RAG Graphが取得したDocument IDでVector検索対象をSQLレベルで制限する方式

PDFをVector Searchしているだけでは、Ontologyを実装したことにはなりません。

RAGは、質問に関連する情報を検索し、その情報を根拠にLLMが回答を生成する方式です。本稿ではPDFのVector検索を利用し、文書に書かれた根拠と回答を照合します。

一方、Domain Ontologyでは、例えば次の意味を定義します。

  • IncidentはEquipmentに影響する
  • EquipmentはStageに配置される
  • EquipmentはManufacturing Lotに属する
  • Incidentから改善Taskが作成される
  • TaskはEquipmentを対象とする
  • TaskはDocumentに記載された手順を使用する

・ 今回実装するOntologyの範囲

今回の構成は、次の2層に分けます。

軽量なDomain Ontology
  └─ 概念、関係、同義語、制約を定義

SQL Property Graph型Knowledge Graph
  └─ Ontology設計を表/View上の実データへマッピング

本記事は、明示的に登録したNodeとEdgeをGRAPH_TABLEで検索する実装です。RDF/OWLの公理に基づく推論は実行しません。表現形式と推論の違いは、後半の補足で整理します。

● Oracleで試す面白さ

Oracleの表やViewにあるデータを使いながら、SQLでは数値を集計し、SQL Property Graphでは関係をPathとして検索できます。さらにPAFから既存Select AI RAGを呼び出すことで、一つのIncidentから機材・配置・対応Task・根拠文書へと調査を広げていきます。

通常のJOINでも同じ関係を検索できます。そこで本記事では、JOINとGraphの対象IDが一致することも確かめます。Graphの関係名が業務の言葉に対応していると、Queryを読みながら「どこをたどってこの機材や文書へ到達したか」を追えるようになるのが、今回体験したいところです。

また、Taskを完了にしたり、文書との関係を外したりしたときに、返る情報がどう変わるかも確認します。自分でデータと定義を動かして、回答の根拠まで確かめられるハンズオンにします。

前回は、Lakehouse上の構造化・半構造・非構造ファイルをNL2SQL+RAGで分析しました。今回はその環境に意味と関係を追加し、数値・関係・文書の根拠を使った回答へつなげます。

ということで、今回はAutonomous AI LakehouseのデータをOntologyでつないで、Private Agent FactoryからNL2SQL+RAG+Knowledge Graphしてみてみます。

From One Outage to Clear Action.jpg

● 今回作成するもの

技術概要.jpg

② 数値・つながり・文書で、根拠をそろえる.png

今回のPAF Agentは、質問内容に応じて3種類の検索を使用します。

検索 主な役割 取得する情報
NL2SQL 表データの事実確認と集計 センサー値、保守状態、件数、日時
Knowledge Graph 関係と影響範囲の探索 Incident→Equipment→Lot→別Equipment→Stage、Incident→Task→Documentなどの経路
RAG 非構造文書の根拠取得 原因、閾値、交換条件、正式な対応手順

本記事のPAF 26.7構成では、Manager Agentという独立Nodeを使用しません。

本記事では、Agent Nodeへ各Toolを使って回答をまとめる役割を設定します。本編Flow名はLSF2026 ONTOLOGY IMPACT AGENTです。

■ 前回の環境へ追加して構築する

ここからは、冒頭の結果を自分の環境で再現するチュートリアルです。前回記事のAutonomous AI Lakehouse、Object Storage、External Table、Select AI RAG、PAFが構築済みであることを前提に進めます。

構築・検査の実行順序は、次の全体手順に沿って進めます。長い検査SQL、作成SQLの全文、生JSON、トラブル対応は折りたたみへ収めています。構築時は該当箇所を開いて実行し、本文に示した成功の目印を確認してください。

● 今回の追加構築の全体手順

・ 前回環境から進める実行順序

本記事は、前回の記事でAutonomous AI Lakehouse、Object Storage、External Table、Select AI RAG、PAFを構築し、SQLとRAGから回答を取得できる状態を前提に進めます。その環境へ、今回のOntologyとKnowledge Graphを追加します。

順序 行うこと 完了の目印
1 後述の質問表で、対象業務と答えたい問いを決める 期待する回答と「答えられない条件」が説明できる
2 概念・関係・語彙と元データの対応を決める 各関係に意味と根拠がある
3 前回構築したADB、Object Storage、PAFの接続と既存Objectを確認する 元表をSELECTでき、既存のSQLとRAGを利用できる
4 本稿の補足RelationとデモTaskを追加する 元CSVと合成補足Relationの出所を区別できる
5 Graph用Viewと整合性検査を実行する すべての整合性検査が0行を返す
6 Property GraphとGraph APIを作成する ObjectがVALIDで、期待ID・件数と一致する
7 通常のJOINとの比較と異常系テストを実行する 差分0件、未知IDは所定のエラーになる
8 PAFへGraph APIを接続して複合質問を実行する SQL結果、Graph Path、文書根拠を追跡できる
9(任意) PAFで言い換え・条件変更や比較Flowを追加検証する 回答の再現性と限界を評価し、実施した範囲を記録できる
10(任意) Graph Studioで既存Knowledge Graphを可視化する Graphの全体図と、Incidentから同じLotの別機材へ至る経路を確認できる

本編の実行範囲は手順8までです。Oracle側の整合性・API・条件変更テストとPAFからの問い合わせ結果を記録したら、後半は解説と任意の追加検証へ進みます。KG-informed RAGの説明、回答イメージ、設定済みガードレールの整理のために、追加のSQLやFlowを実行する必要はありません。

・ 前回構築した環境を確認する

前回作成したUser、External Table、Agent向けView、改善Task表、Select AI RAGを引き継ぎます。環境をまだ構築していない場合は、前回記事を完了してから本記事へ進んでください。

ここでは、使用する接続Userと既存Objectを確認します。Graph用の追加権限や今回用Objectは、後述の該当手順で設定します。

1) 既存の接続Userと役割を確認する

役割 本稿の名前 用途
管理者 ADMINなど 今回のGraph・Directoryなどに必要な追加権限の付与
データ・Graph所有者 ADB_USER External Table、View、補足Table、Graph、Packageの作成
Agent実行User LSF_AGENT 許可されたデータ参照とPackage実行
PAF Repository 製品管理用Schema 本稿の業務データ所有者・実行Userとしては使用しない

実環境で別のSchema名を使う場合は、GRANT、PAF Data Source、ProcedureのQualified Nameを同じ対応で置き換えます。

2) 既存Objectと接続情報を引き継ぐ

ADB_USERの既存External Table・Agent向けView・LSF_IMPROVEMENT_TASKSと、LSF_AGENTの接続をそのまま使用します。前回設定したResource Principal、Object StorageのNamespace・Bucket・Prefixも引き継ぎます。User、既存External Table、既存Task表の作成SQLは再実行しません。

後述の「事前準備」で今回用Repositoryを取得し、ローカルのsql/00_variables.sqlへ前回と同じ設定値を反映します。この設定ファイルは、Document Catalog作成前にADB_USERのSQLセッションで読み込みます。

Repositoryには、修正済みのdata/parquet/incidents/incidents.parquetと、内容をそろえた確認用data/csv/transactions/incidents.csvを同梱します。EXT_INCIDENTSが参照するのはParquetです。今回は完成済みファイルをダウンロードして同じObject名へ配置するため、CSVの手編集、CSVからParquetへの変換、pandasなどの追加インストールは不要です。

今回追加する補足Relation、Graph用View、Graph APIと3 Wrapperは、後述の手順で作成します。既存のSelect AI RAGも再利用し、新しいPAF Flowには読み取り用Toolを接続します。

3) DatabaseとPAFの疎通を個別に確認する

・ Login User確認

SQL> show user
USER is "ADB_USER"

・ EXT_INCIDENTSテーブルデータ確認

SQL
SELECT INCIDENT_ID, RELATED_EQUIPMENT_ID, DOCUMENT_ID
FROM EXT_INCIDENTS
WHERE INCIDENT_ID = 'INC-2026-081';
SQL実行結果
INCIDENT_ID     RELATED_EQUIPMENT_ID    DOCUMENT_ID
_______________ _______________________ ______________
INC-2026-081    EQ-NET-WA-01            IR-2026-081

EQ-NET-WA-01とIR-2026-081が返ることを確認します。PAFでは、SQLだけの質問とPDFだけの質問を先に試します。接続障害とOntology設計の問題を混同しないためです。

本稿のDatabase構文・Dictionary Viewは26aiのドキュメント、PAF画面は26.7を基準にしています。別Versionで試す場合は、そのVersionの機能・画面へ読み替えた箇所を検証記録へ残します。

・ この手順の再実行範囲

External Tableと補足Relation Tableの新規作成は初回用です。すでに存在する環境では、Table作成部分をそのまま再実行しません。

補足RelationのMERGE、デモTaskの追加Block、View・Graph・PackageのCREATE OR REPLACEは、検証専用環境での再実行を想定しています。Task追加Blockの再実行では、ONTOLOGY_DEMO_SEEDの専用TaskをOPENへ戻します。

通常の単体確認・比較・不正IDの異常系検証は読み取り処理です。後述の条件変更テストだけは合成データを一時更新し、同じBlock内でRollbackして復元します。DDLの暗黙Commitと区別するため、データ投入を完了・Commitしてから実行します。

● 事前準備

前回の記事の環境は構築済みとし、Autonomous AI Lakehouse、Object Storage、External Table、Select AI、RAG用PDF、PAFのDatabase Data Sourceを再利用します。ここからは、今回用の完成済みデータと追加SQLを取得します。

Private Agent Factoryは26.7へUpgrade済みの環境を使用します。

今回使用するテストデータは、前回と同じRepositoryから取得します。

今回の配布版は、Commit fd1144c85eb158eee355c399230fa19e1e8049ddです。修正済みParquet、データ投入SQL、Graph用View、SQL Property Graph、Graph API、分析用View、検証SQL、Graph Studio用Queryを収録しています。以降の取得手順ではこの版を使用し、実行順序と処理内容を本文で説明します。

RepositoryのOntology再現手順も参照できます。本記事では、そのうち前回環境がある場合の追加手順を使用します。本文掲載の実行ログは当時の検証記録として保持し、配布版のCommitと区別します。

git clone https://github.com/shirok-tech/lakehouse-sound-festival-2026-paf-demo.git lakehouse-sound-festival-ontology
cd lakehouse-sound-festival-ontology
git checkout fd1144c85eb158eee355c399230fa19e1e8049dd
git rev-parse HEAD
python3 scripts/validate_dataset.py
python3 scripts/validate_ontology.py

今回のCommitのZIPでも同じ版を取得できます。bd643e6は先行して公開したデータ準備版、5c18584は前回記事の版です。今回のGraph・API・分析用Viewまで含む配布ファイルを使うため、上記の固定Commitを取得します。

・ 完成済みデータで今回分を更新

データ準備では、Object StorageのIncidentファイルとDocument Catalogを更新し、Database内にOntology用の補足Relationを登録します。

配布物 今回の処理
修正済みincidents.parquet INC-2026-091の未解決の文書参照WX-2026-009をNULLへ修正する。Incident自体とその他の値は残す
同期済みincidents.csv Parquetと内容をそろえた確認用CSVを配置する
metadata/document_catalog.csv 本文の実行結果に合わせた日本語PDF名のCatalogを配置する。RAG用のASCII名とはDocument IDで対応付ける
sql/ontology/00_apply_demo_data.sql Lot補正2件、デモTask2件、Task・Lotと文書のRelationを登録し、Location Scopeを定義する

WX-2026-009は配布Document Catalogと確認した気象観測データに見つからないため、今回のデモではこの文書参照を外します。存在未確認の文書を追加する処理や、例外表で参照エラーを除外する処理は行いません。

取得したRepositoryのRoot(data、scripts、sqlが並ぶディレクトリ)で、前回と同じOCI CLI設定を使って次を実行します。環境変数はアップロードを実行する同じターミナルで設定します。未設定の場合はOCI_NAMESPACE: Set OCI_NAMESPACEで停止するため、前回と同じNamespace・Bucket・Regionを指定してください。

export OCI_NAMESPACE='自分のnamespace'
export OCI_BUCKET='lakehouse_sound_festival'
export OCI_REGION='ap-tokyo-1'
export OCI_OBJECT_PREFIX='lakehouse_sound_festival_2026'
bash scripts/upload_ontology_data.sh

このスクリプトは、次の3つのObjectへ完成済みファイルを上書きします。

lakehouse_sound_festival_2026/data/csv/transactions/incidents.csv
lakehouse_sound_festival_2026/data/parquet/incidents/incidents.parquet
lakehouse_sound_festival_2026/metadata/document_catalog.csv

Prefixを変更している場合はOCI_OBJECT_PREFIXに実際のPrefixを指定します。OCI Consoleを使用する場合も、ダウンロードしたファイルを上記の同じObject名へアップロードします。

Catalogの日本語PDF名とdocuments/pdf_ascii/のASCII名は、Repositoryのmetadata/document_pdf_mapping.csvでDocument IDごとに対応付けています。今回の3ファイルの更新にPDFの再取り込みは含まれず、PAFでは既存Select AI RAGを使用します。

Ownerで参照先と反映結果を確認します。

SELECT TABLE_NAME, LOCATION
FROM USER_EXTERNAL_LOCATIONS
WHERE TABLE_NAME IN ('EXT_INCIDENTS', 'EXT_DOCUMENT_CATALOG');

SELECT INCIDENT_ID, DOCUMENT_ID
FROM EXT_INCIDENTS
WHERE INCIDENT_ID = 'INC-2026-091';

INC-2026-091が1行返り、DOCUMENT_IDがNULLなら更新済みです。CSVだけを上書きしても、Parquetを参照するExternal Tableには反映されません。今回の実行でも、この更新を確認できました。

SQL実行結果
INCIDENT_ID     DOCUMENT_ID
_______________ ______________
INC-2026-091

追加SQLは、後述のDocument Catalogの準備が終わった時点で実行します。処理内容を確認できるよう、この記事にもTable定義と登録SQLを掲載しています。

本記事は、前回作成した次のObjectが存在することを前提にしています。

種別 Object
Incident EXT_INCIDENTS
Equipment EXT_EQUIPMENT_MASTER
Stage EXT_STAGES
Sensor EXT_EQUIPMENT_SENSOR_LOGS
Maintenance EXT_EQUIPMENT_MAINTENANCE
Agent向けView V_EQUIPMENT_HEALTH_SUMMARY、V_GATE_CONGESTION_ANALYSISなど
改善Task LSF_IMPROVEMENT_TASKS
Runtime User LSF_AGENT
RAG文書 documents/pdf_ascii/の10 PDF

・ 今回確かめる質問と期待結果

・ 今回のデモで確かめる質問

この問いを、対象のIncidentと必要な根拠が分かる質問へ具体化します。目指すのは、次の内容に、数値・関係・文書の根拠を揃えて回答するAgentです。

Waveform Arenaで発生したINC-2026-081について、
障害機材と同じManufacturing Lotの機材は、ほかのStageにも配置されていますか?

このIncidentへの対応として登録された未完了Taskとその対象機材、
Databaseで確認できる障害事実、関連Documentの交換条件と運用手順を示し、
開演前に優先すべき対応を根拠付きでまとめてください。

・ この一問から、どこまで分かるようにするか

一つの質問と、回答を支える根拠.jpg

本記事の合成デモRelationと専用Taskを追加した状態で、次の結果を確かめていきます。以下はチュートリアルで照合する期待結果です。

利用者が知りたいこと 確認する対象 主に使う仕組み
どの機材で障害が起きたか INC-2026-081とEQ-NET-WA-01 元データとGraph
同じLotの機材はほかにあるか NW-2603に属するEQ-NET-OM-01とEQ-NET-NG-01 Graphの関係探索
どこのStageを確認するか Orbit Main StageとNeon Groove Stage EquipmentとStageの関係
このIncidentへの未完了対応は何か 対象2台に登録する専用Task 2件とPriority Incident・Task・Equipmentの関係
障害時に何が起きていたか 温度、Packet Loss、Fan RPM、保守状況 NL2SQL
なぜその文書を使うか IR-2026-081、SB-NW-2603、OPS-NET-2.1へ至る経路と出所 Graph API
何を根拠に対応を提案するか 対象DocumentのVersion、該当箇所、交換条件・運用手順 RAGと根拠照合

例えば「Neon Groove Stageも確認してください」という回答が返ったときは、なぜそのStageが対象になったかを、障害機材→製造Lot→別機材→配置Stageの経路で確認します。冒頭には経路の実画面を、Graph Studioの補足章には表示手順とSQL結果を掲載しています。

次に、PAFの回答で対象機材の未完了Taskと、文書に書かれた交換条件・運用手順を確認します。「対象はこの2台」「理由は同じLot」「対応条件はこの文書」と、問いから根拠までを続けて見る構成です。

同じLotに属することは、同じ故障が発生している証明ではありません。本記事では、その区別も含めて、何が分かり、何を追加確認する必要があるかを回答させます。

このハンズオンで身につけること

● このハンズオンで身につけること

本記事では、音楽フェスの小さな業務を題材に、問いからOntologyを設計し、Oracle DatabaseとPrivate Agent Factoryで利用するまでを順に試します。

読み終えたときの到達点は、次の3つです。

  • 説明できる:Ontology、Knowledge Graph、NL2SQL、RAGがそれぞれ何を担当するか説明できる。
  • 再現できる:同じデータ・定義・検証条件で、関係の探索と文書根拠の取得を確認できる。
  • 応用できる:別の業務でも、問い・概念・関係・判断条件・評価方法を整理して検証を始められる。

重要なのは、Graphが描けたことだけで完了にしないことです。「この定義なら、この問いに、この根拠で答えられる」を確認します。

学ぶ内容 本記事で作成・確認するもの
何を答えたいか 質問と合格条件をまとめたCompetency Questions
何を同じ意味として扱うか 概念・関係・同義語・制約の定義
どのデータで裏付けるか 元表・ID・文書・出所の対応
Oracle上でどう使うか View、SQL Property Graph、read-only Graph API
正しく動いたとどう判断するか JOINとの結果比較、IDと文書の照合、異常系テスト
別の業務へどう展開するか 食品製造・候補者比較への設計の置き換え例

Ontology自体に業務上の意味を持たせるには、語彙や関係を定義する作業が必要です。PAFへPDFを登録するだけで、その会社の業務定義まで自動的に確定するわけではありません。

本稿では意味の設計を人が行い、その定義をOracle上で再利用する流れを学びます。文書から概念や関係をLLMで抽出し、人が確認する作成支援は、後半で拡張方法として扱います。

● 音楽フェス運営Ontologyを設計

Ontology の設計から検証まで.jpg

・ まず答えたい質問を決める

Ontologyを作る最初の作業は、業務で答えたい質問と対象範囲を決めることです。「あるだけ全部の表をGraphにする」前に、必要な意味と関係を整理します。

この質問群をCompetency Questionsと呼びます。Ontologyが質問に必要な概念・関係を備えているかを判断するために使います。Ontology Development 101

今回は、次の質問と合格条件を決めます。

ID 答えたい質問 合格条件 主な確認手段
CQ-01 INC-2026-081はどの機材に影響したか EQ-NET-WA-01と元Incidentの対応が一致 元表SELECTとAFFECTS
CQ-02 同じLotの別機材はどのStageにあるか 補足Relation反映後にEQ-NET-NG-01とEQ-NET-OM-01、各Stage、合成データの出所を返す Graph APIとJOIN比較
CQ-03 このIncidentに結び付く未完了Taskは何か デモTask 2件の実TASK_IDと対象機材が一致し、状態はOPENまたはIN_PROGRESS Task表とGraph API
CQ-04 なぜそのDocumentを使うのか IR-2026-081、SB-NW-2603、OPS-NET-2.1の関連理由を示す Document RelationとRAGの引用箇所
CQ-05 未登録Incidentでも回答してよいか 形式不正と未登録IDを区別し、架空の関係を補完しない API異常系テスト
CQ-06 同じLotなら、すでに同じ故障が発生したのか 同一Lotの関係だけでは故障を確定しない Agent回答の確認
CQ-07 「製造ロット」を「Lot」と言い換えても結果は同じか 同じ対象IDと根拠候補が選ばれる PAFでの言い換え比較

CQ-02の期待結果は、後述の合成Relationを追加した状態での値です。元Repositoryのままでは別機材0件になります。この前提を、データ追加前後の検証で確かめます。

今回のScopeはStage運営、現在の機材配置、登録済みIncidentとTaskです。将来の故障予測、配置履歴の復元、Gate運営、OWL推論はこのScopeに含めません。

・ 概念・実体・関係・判断を区別する

Ontologyの設計では、次の区別を先に共有しておくと進めやすくなります。

種類 音楽フェスでの例 本稿での扱い
概念/Class Equipment、Incident、Document 業務上の意味を定義し、Graph Labelへ対応付ける
実体/Instance EQ-NET-WA-01、INC-2026-081 IDを持つ各レコードをVertexへ対応付ける
属性/Property 型番、Status、Document Version 対象の値を保持する
関係/Relation EquipmentはLotに属する BELONGS_TO_LOTなどのEdgeで接続する
制約 Equipment IDは一意で、Edgeの接続先が存在する 制約・整合性検査で確認する
業務ルール 未完了TaskはOPENまたはIN_PROGRESS View・APIのSQL条件で実装する
推奨判断 どの機材を先に点検するか Priority、手順書、運用条件とともに説明する

ER図や外部キーはデータの構造を表現できます。Ontologyでは、その構造に加えて、「関係が業務上何を意味するか」「どの用語を同じ意味で使うか」「どの範囲で成立するか」を共有します。ER設計と重なる部分はありますが、表名をNode名へ置き換えるだけで意味の合意が済むわけではありません。

たとえば、CREATESは「そのIncidentへの対応として、このTaskが登録されている」という関係名です。Edgeを定義しただけで、DatabaseがTaskを自動作成するわけではありません。

・ 語彙・関係の意味を決める

1) 同義語を決める

このハンズオンでは「機材」と「Equipment」、「製造ロット」と「Manufacturing Lot」を同じ意味で使います。一方、「担当者」と「責任者」のように業務上の役割が異なる語は、安易に同義語へまとめません。

同義語と、概念の種類や個別の実体も分けます。

区別 例 設計で決めること
同じ概念の別名 Equipment/機材 同じ対象を指す用語として扱う
概念の種類 Network SwitchはEquipmentの一種 同義語へまとめず、種別やクラス階層として扱う
個別の実体 EQ-NET-WA-01 他の機材と区別できるIDを使う
実体間の関係 EQ-NET-WA-01がNW-2603に属する 個別の所属を裏付けるデータ・出所を保持する

例えば「音響機器」はEquipmentの一部を指し得るため、すべてのEquipmentの同義語にはしません。「会場」はStageだけでなくGateやフェス全体を指す場合があるため、このデモのStageと無条件に同一視しません。Questionの文脈から対象を確認し、必要な種別条件を加えるか、利用者へ範囲を確認します。

本稿では機材種別を既存のEQUIPMENT_CATEGORYで保持します。この例のためにNetwork Switch専用VertexやOWL推論を追加する必要はありません。

2) 関係の成立条件を決める

BELONGS_TO_LOTは機材と製造Lotの所属関係です。「同型番だから同じLot」「文書のシリアル範囲に入るから確実に対象」とは扱いません。今回の追加2件は検証用の合成関係として、元Masterと区別します。

3) 分からないことの扱いを決める

「GraphにDocumentがない」は「関連Documentが登録されていない」という意味です。「必要なDocumentが世界に存在しない」とは解釈しません。同様に、関係が見つからないことを安全性の証明には使いません。

4) ルールを実行する場所を決める

後述のYAMLは語彙設計の記録です。YAMLの制約文をDatabaseが自動実行するわけではありません。

定義した内容 実際に適用・確認する場所
IDの一意性・参照整合性 Table制約とGraph作成前の検証SQL
OPEN/IN_PROGRESSが未完了 GET_OPEN_TASKSのSQL条件
未登録Incidentを拒否 PackageのASSERT_INCIDENT_ID
Lot関係の出所を返す ViewのLOT_SOURCEとGraph APIのJSON
同一Lotから故障を断定しない Agent Instructionsと回答評価
Document候補外を根拠に使わない 標準構成ではPrompt上の方針。Hard Filterは別途SQLで実装

意味の定義、Databaseでの強制、Agentへの指示を分けることで、何が保証され、何を検証する必要があるか説明できます。

・ Nodeを定義

既存データには、すでに一意なIDが用意されています。

Node Key 例 主なProperty
Incident INCIDENT_ID INC-2026-081 Category、Severity、発生日時、Status
Equipment EQUIPMENT_ID EQ-NET-WA-01 Model、Serial、Criticality、Firmware
Stage STAGE_ID STG-WA Stage Name、Type、Capacity
Manufacturing Lot MANUFACTURING_LOT NW-2603 Lot ID
Task TASK_ID 環境ごとの自動採番 Status、Priority、Owner Team
Document DOCUMENT_ID SB-NW-2603 Type、Version、Effective Date、File Name

・ Edgeを定義

Source Edge Destination 意味
Incident AFFECTS Equipment Incidentが影響した機材
Equipment BELONGS_TO_LOT Manufacturing Lot Equipmentが属する製造Lot
Equipment DEPLOYED_AT Stage Equipmentの配置先
Incident DOCUMENTED_BY Document Incidentを記録した報告書
Incident CREATES Task Incidentから作成された改善Task
Task TARGETS Equipment Taskの対象機材
Task USES_PROCEDURE Document Taskで使用する手順・Service Bulletin
Manufacturing Lot GOVERNED_BY Document Lotへ適用されるService Bulletin/手順

・ Domain Vocabularyを定義

Domain Ontologyの概念・関係・同義語を、次のようなYAMLとして管理できます。

このYAML自体をOracle AI DatabaseがOWLとして推論するわけではありません。

PAFのAgent Instructions、Data Analysis AgentのDescription、Database ObjectのCommentへ同じ意味を反映するための語彙定義です。YAMLから各設定へ自動同期する処理は本稿には含めません。

synonymsには本稿で同じ概念として扱う語を記載します。query_terms_requiring_contextは、質問で現れたときに対象種別や範囲を確認する語です。後者は同義語一覧として一括置換しません。category_propertyとtype_propertyは既存データへの対応を記録する設計上の項目です。

ontology:
  name: LakehouseSoundFestivalOperations
  version: 1.1
  scope: Stage運営、現在の機材配置、登録済みIncidentとTask

classes:
  Incident:
    key: incident_id
    label_ja: インシデント
    definition: 運営上の事象としてID付きで登録された記録
    synonyms: [インシデント]
    query_terms_requiring_context: [障害, トラブル, 事象]

  Equipment:
    key: equipment_id
    label_ja: 機材
    definition: 個体ごとにIDを持つ運営用機材
    synonyms: [機材]
    category_property: EQUIPMENT_CATEGORY
    query_terms_requiring_context: [装置, デバイス, 音響機器]

  Stage:
    key: stage_id
    label_ja: ステージ
    definition: フェス内の個別公演区画としてStage Masterに登録された場所
    synonyms: [ステージ]
    query_terms_requiring_context: [会場, 配置先]
    excludes: [入場Gate, フェス会場全体]

  ManufacturingLot:
    key: manufacturing_lot
    label_ja: 製造ロット
    definition: 製造上の同一単位を識別するLot。型番やシリアル範囲とは区別する
    synonyms: [製造ロット, Manufacturing Lot, Lot]

  Task:
    key: task_id
    label_ja: 改善タスク
    definition: Incidentへの対応として登録された作業記録
    synonyms: [改善タスク, 対応タスク]
    query_terms_requiring_context: [点検タスク, Action]

  Document:
    key: document_id
    label_ja: 文書
    definition: IDとVersionを持ち、報告や手順の根拠となる文書
    synonyms: [文書]
    type_property: DOCUMENT_TYPE
    query_terms_requiring_context: [手順書, 報告書, Service Bulletin, マニュアル]

relationships:
  AFFECTS:
    source: Incident
    destination: Equipment
    label_ja: 影響する

  BELONGS_TO_LOT:
    source: Equipment
    destination: ManufacturingLot
    label_ja: 製造ロットに属する

  DEPLOYED_AT:
    source: Equipment
    destination: Stage
    label_ja: 現在配置されている

  CREATES:
    source: Incident
    destination: Task
    label_ja: 対応として登録されたTaskに結び付く

  TARGETS:
    source: Task
    destination: Equipment
    label_ja: 対象とする

  USES_PROCEDURE:
    source: Task
    destination: Document
    label_ja: 手順として使用する

  DOCUMENTED_BY:
    source: Incident
    destination: Document
    label_ja: 報告書に記録される

  GOVERNED_BY:
    source: ManufacturingLot
    destination: Document
    label_ja: 適用文書に結び付く

constraints:
  - 同じManufacturing Lotであることは、同一原因による故障を証明しない
  - Documentの根拠がない推奨を正式手順として扱わない
  - Graphに登録されていない関係を推測で補完しない
  - Query中の種類や範囲を表す語を、無条件に上位概念の同義語として扱わない

● 既存データを確認

・ NW-2603は1台だけ

公開Repositoryのequipment_master.csvを確認すると、NW-2603はEQ-NET-WA-01の1台だけです。

rg 'NW-2603' data/csv/master/equipment_master.csv
EQ-NET-WA-01,STG-WA,NETWORK_SWITCH,PulseGrid StageLink 48,
LSF26-00009,NW-2603,VND-002,3.4.1,2026-03-09,CRITICAL

この状態で同一LotをGraph検索しても、別StageのEquipmentは0件になります。

・ 既存TaskにはEquipment/Documentとの関係がない

前回作成したLSF_IMPROVEMENT_TASKSは、Incident ID、Task Title、Priority、Owner Teamなどを保持しています。

一方で、Taskが対象とするEQUIPMENT_IDと、使用すべきDOCUMENT_IDは保持していません。

そこで今回は、元CSVと既存Task表のSchema/既存行は変更せず、Task表へ出所付きの合成デモ行を追加します。Equipment→Lot、Task→Equipment、Task→Document、Lot→Documentの関係は、次の補足Relation Tableに保持します。

追加Object 役割
LSF_KG_LOT_PATCH 合成デモ用のEquipment→Lot補足Relationと出所を保持
LSF_KG_TASK_EQUIPMENT Task→Equipmentの関係を保持
LSF_KG_TASK_DOCUMENT Task→Documentの関係を保持
LSF_KG_LOT_DOCUMENT Lot→Documentの適用関係を保持
EXT_DOCUMENT_CATALOG Object Storage上のDocument Catalogを表として参照

元データを上書きせず、追加した関係のSOURCE_TYPEとSOURCE_REFERENCE_IDを残すため、どの情報を補足したか追跡できます。

● Knowledge Graph検証用データを追加

・ Oracleへ接続する前に期待する関係を確認

Graphで返るべき関係を、元CSVから独立に確認します。RepositoryのRoot Directoryで次を実行します。Python標準ライブラリだけで動作し、元CSVは更新しません。

CSVの期待する関係を確認する検査コード
python3 - <<'PY'
import csv
from pathlib import Path

root = Path('.')

def keyed(relative_path, key):
    with (root / relative_path).open(encoding='utf-8-sig', newline='') as f:
        rows = list(csv.DictReader(f))
    result = {r[key]: r for r in rows}
    assert len(result) == len(rows), f'duplicate key: {relative_path}'
    assert all(result), f'empty key: {relative_path}'
    return result

equipment = keyed('data/csv/master/equipment_master.csv', 'equipment_id')
incidents = keyed('data/csv/transactions/incidents.csv', 'incident_id')
stages = keyed('data/csv/master/stages.csv', 'stage_id')
documents = keyed('metadata/document_catalog.csv', 'document_id')

incident = incidents['INC-2026-081']
affected_id = incident['related_equipment_id']
assert affected_id == 'EQ-NET-WA-01'
assert incident['document_id'] == 'IR-2026-081'
assert incident['location_id'] == 'STG-WA'
assert equipment[affected_id]['manufacturing_lot'] == 'NW-2603'

base_lots = {k: v['manufacturing_lot'] for k, v in equipment.items()}

def peers(lots):
    return sorted(
        (k, equipment[k]['stage_id'])
        for k in equipment
        if k != affected_id
        and equipment[k]['stage_id'] in stages
        and lots[k] == lots[affected_id]
    )

assert peers(base_lots) == []
print('Original CSV same-lot peers: 0')

# Synthetic demo relations defined in this article; not vendor-confirmed facts.
demo_lots = dict(base_lots)
demo_lots.update({'EQ-NET-OM-01': 'NW-2603', 'EQ-NET-NG-01': 'NW-2603'})
expected = [('EQ-NET-NG-01', 'STG-NG'), ('EQ-NET-OM-01', 'STG-OM')]
assert peers(demo_lots) == expected
print('Synthetic demo peers:', peers(demo_lots))

# Catalog FILE_NAME uses Japanese names; RAG PDFs use the ASCII names below.
ascii_files = {
    'IR-2026-081': 'IR-2026-081_waveform_arena_delay_report.pdf',
    'OPS-NET-2.1': 'LSF2026_stage_audio_network_operations_v2.1.pdf',
    'SB-NW-2603': 'SB-NW-2603_network_switch_cooling_fan_bulletin.pdf',
}
assert all(d in documents for d in ascii_files)
for doc_id, file_name in ascii_files.items():
    pdf = root / 'documents/pdf_ascii' / file_name
    assert pdf.is_file(), f'missing PDF for {doc_id}: {pdf}'
print('Document catalog and PDF references: PASS')
print('CSV relation precheck: PASS')
PY

CatalogのFILE_NAMEには日本語名が含まれますが、RAGで使用するdocuments/pdf_ascii/にはASCII名のPDFを配置しています。そのため、上の検査ではDocument IDとASCIIファイル名を明示的に対応付けています。

正常時の出力は次のとおりです。従来のローカル検証ではPASSを記録していますが、ファイル名の対応を修正した上記コードは今回の原稿改訂では再実行していません。

Original CSV same-lot peers: 0
Synthetic demo peers: [('EQ-NET-NG-01', 'STG-NG'), ('EQ-NET-OM-01', 'STG-OM')]
Document catalog and PDF references: PASS
CSV relation precheck: PASS

この検査が確認するのは、元データのKey、Incidentの参照、合成Relationを重ねた場合の関係、CatalogとPDFファイルの対応です。OracleのGraph DDLやPAFの回答は、この後の手順で別に検証します。

・ 今回追加する関係

Service Bulletin SB-NW-2603には、対象の一部としてシリアル番号LSF26-00001からLSF26-00020が記載されています。

ただし、このシリアル範囲だけでは、個別のEquipmentがNW-2603に属することを確定できません。

そこで本記事では、別途照合済みという前提の合成デモ用Relationを2件追加します。実運用では出荷記録や対象シリアル一覧など、個別機材を特定できる根拠をSOURCE_REFERENCE_IDとして保持し、Service Bulletinだけを根拠にVERIFIEDとは判定しません。

Equipment ID Serial Number Stage 補正後Lot Relation Source
EQ-NET-WA-01 LSF26-00009 STG-WA NW-2603 元Master
EQ-NET-OM-01 LSF26-00001 STG-OM NW-2603 合成デモRelation
EQ-NET-NG-01 LSF26-00017 STG-NG NW-2603 合成デモRelation

EQ-NET-OM-01とEQ-NET-NG-01に、未完了の予防交換Taskも追加します。

Task Equipment ID Priority Status Document
環境ごとに自動採番 EQ-NET-OM-01 CRITICAL OPEN SB-NW-2603、OPS-NET-2.1
環境ごとに自動採番 EQ-NET-NG-01 HIGH OPEN SB-NW-2603、OPS-NET-2.1

ここで追加する2件は、本記事のGraph Traversalを確認するための合成データです。元RepositoryのCSVに存在する事実と混同しないように、Relation Sourceを保持します。

・ SQL Property Graph権限を付与

SQL Property Graphを作成するSchemaへ、必要な権限を付与します。

次はADMINなどの権限を持つUserで実行します。

GRANT CREATE PROPERTY GRAPH TO ADB_USER;

ADB_USERは環境のSchema名に置き換えます。

前回の手順で使用したCREATE TABLE、CREATE VIEW、CREATE PROCEDUREなどの権限も必要です。

・ Document CatalogをExternal Table化

前述のUpload Scriptで更新したmetadata/document_catalog.csvを、今回のDocument Vertexとして使用します。

ADB_USERで接続し、RepositoryのRootから次の設定ファイルを読み込みます。&CRED_NAMEと&OBJ_URIには前回と同じ値を設定してください。これはSQLセッションの変数設定であり、既存Objectの再作成は行いません。

@sql/00_variables.sql

EXT_DOCUMENT_CATALOGが未作成の場合は、同じセッションで次を実行します。作成済みの場合は、作成BlockをSkipして確認SELECTへ進みます。

BEGIN
  DBMS_CLOUD.CREATE_EXTERNAL_TABLE(
    table_name      => 'EXT_DOCUMENT_CATALOG',
    credential_name => '&CRED_NAME',
    file_uri_list   => '&OBJ_URI/metadata/document_catalog.csv',
    column_list     => 'DOCUMENT_ID VARCHAR2(40),
                        FILE_NAME VARCHAR2(255),
                        DOCUMENT_TYPE VARCHAR2(80),
                        VERSION VARCHAR2(20),
                        EFFECTIVE_DATE VARCHAR2(10),
                        PURPOSE VARCHAR2(500)',
    format          => '{"type":"csv","skipheaders":1,"delimiter":",","quote":"\"","rejectlimit":"unlimited"}'
  );
END;
/
SQL実行出力結果
PL/SQL procedure successfully completed.

すでにEXT_DOCUMENT_CATALOGを作成済みの場合、この手順はSkipします。

SELECT DOCUMENT_ID, FILE_NAME, VERSION
FROM EXT_DOCUMENT_CATALOG
ORDER BY DOCUMENT_ID;
SQL実行出力結果
DOCUMENT_ID          FILE_NAME                             VERSION
____________________ _____________________________________ __________
IR-2026-081          IR-2026-081_Waveform_Arena遅延報告.pdf    1.0
IR-2026-084          IR-2026-084_Gate_C混雑報告.pdf            1.0
MIN-MERCH-2026-04    LSF2026_物販売上在庫計画会議議事録.pdf             1.0
OPS-ACT-1.0          LSF2026_改善タスク登録手順.pdf                 1.0
OPS-GATE-1.2         LSF2026_入場ゲート障害対応手順_v1.2.pdf          1.2
OPS-GEN-1.3          LSF2026_運営統括マニュアル_v1.3.pdf            1.3
OPS-NET-2.1          LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf     2.1
SB-NW-2603           SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf      1.1
TKT-REFUND-2.0       LSF2026_チケット払戻ポリシー_v2.0.pdf           2.0
WX-PLAN-1.4          LSF2026_悪天候対応計画_v1.4.pdf              1.4

10 rows selected.

・ 補足Relation Tableを作成

ここまでの準備が終わったら、RepositoryのRootから起動したSQLcl/SQL*Plusで、Owner(記事ではADB_USER)として配布SQLを実行できます。

@sql/ontology/00_apply_demo_data.sql

これにより、以下の補足Table作成、Lot補正、デモTaskとRelationの登録、Location Scope Viewの作成、データ準備の検査をまとめて実行します。既存の同名Tableは再利用し、Tableがなければ作成します。再実行ではデモ専用Task2件をOPENへ戻します。

成功時は、SCOPE_UNKNOWN_OR_MISSING、EQUIPMENT_STAGE_SOURCE、INCIDENT_DOCUMENT_SOURCEがすべて0、Lot補正が2、デモTaskが2、Task–Equipmentが2、Task–Documentが4、Lot–Documentが2になります。後続の全Source/Edge検査も実行してください。

配布SQLを実行した場合、以下の「補足Relation Tableを作成」「Lot補正データを追加」「未完了Taskを追加」は処理内容の説明として読み、「Graph用Viewを作成」へ進みます。 手動で順に確認したい場合は、配布SQLの代わりに以下のSQLを使用します。

DDLの暗黙Commitとデータ登録BlockのCommitがあるため、配布SQL全体を1回のRollbackで取り消すことはできません。途中失敗時は原因を修正して再実行します。詳細は配布するdocs/ontology-data-setup.mdにも記載しています。

CREATE TABLE LSF_KG_LOT_PATCH (
  EQUIPMENT_ID       VARCHAR2(40)  PRIMARY KEY,
  MANUFACTURING_LOT  VARCHAR2(40)  NOT NULL,
  SOURCE_TYPE        VARCHAR2(30)  DEFAULT 'SYNTHETIC_DEMO' NOT NULL,
  SOURCE_REFERENCE_ID VARCHAR2(40) NOT NULL,
  ASSERTION_STATUS   VARCHAR2(20)  DEFAULT 'SYNTHETIC' NOT NULL,
  EFFECTIVE_DATE     DATE          NOT NULL,
  NOTE               VARCHAR2(500),
  CONSTRAINT CK_LSF_KG_LOT_PATCH_STATUS
    CHECK (ASSERTION_STATUS IN ('VERIFIED', 'PROVISIONAL', 'SYNTHETIC'))
);

CREATE TABLE LSF_KG_TASK_EQUIPMENT (
  TASK_ID       NUMBER       NOT NULL,
  EQUIPMENT_ID  VARCHAR2(40) NOT NULL,
  SOURCE_TYPE   VARCHAR2(30) DEFAULT 'SYNTHETIC_DEMO' NOT NULL,
  SOURCE_REFERENCE_ID VARCHAR2(40) NOT NULL,
  CONSTRAINT PK_LSF_KG_TASK_EQUIPMENT
    PRIMARY KEY (TASK_ID, EQUIPMENT_ID)
);

CREATE TABLE LSF_KG_TASK_DOCUMENT (
  TASK_ID      NUMBER       NOT NULL,
  DOCUMENT_ID  VARCHAR2(40) NOT NULL,
  SOURCE_TYPE  VARCHAR2(30) DEFAULT 'SYNTHETIC_DEMO' NOT NULL,
  SOURCE_REFERENCE_ID VARCHAR2(40) NOT NULL,
  CONSTRAINT PK_LSF_KG_TASK_DOCUMENT
    PRIMARY KEY (TASK_ID, DOCUMENT_ID)
);

CREATE TABLE LSF_KG_LOT_DOCUMENT (
  MANUFACTURING_LOT VARCHAR2(40) NOT NULL,
  DOCUMENT_ID       VARCHAR2(40) NOT NULL,
  RELATION_TYPE     VARCHAR2(40) DEFAULT 'APPLICABLE_GUIDANCE' NOT NULL,
  SOURCE_TYPE       VARCHAR2(30) DEFAULT 'SYNTHETIC_DEMO' NOT NULL,
  SOURCE_REFERENCE_ID VARCHAR2(40) NOT NULL,
  CONSTRAINT PK_LSF_KG_LOT_DOCUMENT
    PRIMARY KEY (MANUFACTURING_LOT, DOCUMENT_ID)
);

・ Lot補正データを追加

元のequipment_master.csvでは、NW-2603に属する機材は障害機材EQ-NET-WA-01の1台だけです。ここでは、別Stageの2台も同じLotに属するという関係をGraphで検証するため、EQ-NET-OM-01とEQ-NET-NG-01の補足RelationをLSF_KG_LOT_PATCHに登録します。元CSVやExternal Tableの値は書き換えません。

次のMERGEはEQUIPMENT_IDをキーに、各機材のRelationがすでにあれば更新し、なければ挿入します。再実行しても、この2台について同じ行を更新できます。両方の補足LotをNW-2603とし、SOURCE_TYPE=SYNTHETIC_DEMO、ASSERTION_STATUS=SYNTHETIC、SOURCE_REFERENCE_ID=BLOG-DEMO-LOT-001を保存して、元データ由来のLot所属と区別します。EFFECTIVE_DATEも、この合成Relationに設定するデモ用の日付です。

後続のV_KG_EQUIPMENTは、この表を機材MasterにLEFT JOINし、該当する合成Relationがある場合だけ、Graph探索用のLot値として補足Lotを優先します。その結果、障害機材→NW-2603→別Stageの2台という経路を検索できます。この2台のLot所属は検証用の設定であり、Service Bulletinのシリアル番号範囲から実証した事実ではありません。

SQL
MERGE INTO LSF_KG_LOT_PATCH T
USING (
  SELECT
    'EQ-NET-OM-01' AS EQUIPMENT_ID,
    'NW-2603'      AS MANUFACTURING_LOT,
    'SYNTHETIC_DEMO' AS SOURCE_TYPE,
    'BLOG-DEMO-LOT-001' AS SOURCE_REFERENCE_ID,
    'SYNTHETIC'    AS ASSERTION_STATUS,
    DATE '2026-07-12' AS EFFECTIVE_DATE,
    'Knowledge Graph検証用の合成Lot Relation' AS NOTE
  FROM DUAL
  UNION ALL
  SELECT
    'EQ-NET-NG-01',
    'NW-2603',
    'SYNTHETIC_DEMO',
    'BLOG-DEMO-LOT-001',
    'SYNTHETIC',
    DATE '2026-07-12',
    'Knowledge Graph検証用の合成Lot Relation'
  FROM DUAL
) S
ON (T.EQUIPMENT_ID = S.EQUIPMENT_ID)
WHEN MATCHED THEN
  UPDATE SET
    T.MANUFACTURING_LOT = S.MANUFACTURING_LOT,
    T.SOURCE_TYPE = S.SOURCE_TYPE,
    T.SOURCE_REFERENCE_ID = S.SOURCE_REFERENCE_ID,
    T.ASSERTION_STATUS = S.ASSERTION_STATUS,
    T.EFFECTIVE_DATE = S.EFFECTIVE_DATE,
    T.NOTE = S.NOTE
WHEN NOT MATCHED THEN
  INSERT (
    EQUIPMENT_ID, MANUFACTURING_LOT, SOURCE_TYPE, SOURCE_REFERENCE_ID,
    ASSERTION_STATUS, EFFECTIVE_DATE, NOTE
  )
  VALUES (
    S.EQUIPMENT_ID, S.MANUFACTURING_LOT, S.SOURCE_TYPE,
    S.SOURCE_REFERENCE_ID,
    S.ASSERTION_STATUS, S.EFFECTIVE_DATE, S.NOTE
  );
SQL実行結果
2 rows merged.

・ 未完了Taskを追加

前回作成したLSF_IMPROVEMENT_TASKS.TASK_IDはIdentity列です。固定IDを挿入すると既存Taskと衝突したり、Identity Sequenceの進行とずれたりするため、Task IDはDatabaseに自動採番させます。

INCIDENT_ID、TASK_TITLE、CREATED_BY_AGENTをデモ用の自然Keyとし、再実行時は同じデモ行だけを更新します。同じ自然Keyが複数存在する場合は、Relationの誤接続を避けるためエラーにします。

SQL
DECLARE
  L_TASK_OM_ID NUMBER;
  L_TASK_NG_ID NUMBER;

  PROCEDURE UPSERT_DEMO_TASK (
    P_TASK_TITLE       IN  VARCHAR2,
    P_TASK_DESCRIPTION IN  VARCHAR2,
    P_PRIORITY         IN  VARCHAR2,
    P_TASK_ID          OUT NUMBER
  ) AS
    L_COUNT PLS_INTEGER;
  BEGIN
    SELECT COUNT(*), MIN(TASK_ID)
      INTO L_COUNT, P_TASK_ID
      FROM LSF_IMPROVEMENT_TASKS
     WHERE INCIDENT_ID = 'INC-2026-081'
       AND TASK_TITLE = P_TASK_TITLE
       AND CREATED_BY_AGENT = 'ONTOLOGY_DEMO_SEED';

    IF L_COUNT > 1 THEN
      RAISE_APPLICATION_ERROR(-20021, 'Duplicate ontology demo task');
    ELSIF L_COUNT = 0 THEN
      INSERT INTO LSF_IMPROVEMENT_TASKS (
        INCIDENT_ID, TASK_TITLE, TASK_DESCRIPTION,
        PRIORITY, OWNER_TEAM_ID, CREATED_BY_AGENT, STATUS
      ) VALUES (
        'INC-2026-081', P_TASK_TITLE, P_TASK_DESCRIPTION,
        P_PRIORITY, 'TEAM-NOC', 'ONTOLOGY_DEMO_SEED', 'OPEN'
      )
      RETURNING TASK_ID INTO P_TASK_ID;
    ELSE
      UPDATE LSF_IMPROVEMENT_TASKS
         SET TASK_DESCRIPTION = P_TASK_DESCRIPTION,
             PRIORITY = P_PRIORITY,
             OWNER_TEAM_ID = 'TEAM-NOC',
             STATUS = 'OPEN'
       WHERE TASK_ID = P_TASK_ID;
    END IF;
  END UPSERT_DEMO_TASK;
BEGIN
  UPSERT_DEMO_TASK(
    P_TASK_TITLE       => 'Orbit Main StageのNW-2603予防交換',
    P_TASK_DESCRIPTION => 'EQ-NET-OM-01を開演前に隔離し、冷却ファン・制御基板を交換する。',
    P_PRIORITY         => 'CRITICAL',
    P_TASK_ID          => L_TASK_OM_ID
  );

  UPSERT_DEMO_TASK(
    P_TASK_TITLE       => 'Neon Groove StageのNW-2603予防交換',
    P_TASK_DESCRIPTION => 'EQ-NET-NG-01を開演前に隔離し、冷却ファン・制御基板を交換する。',
    P_PRIORITY         => 'HIGH',
    P_TASK_ID          => L_TASK_NG_ID
  );

  MERGE INTO LSF_KG_TASK_EQUIPMENT T
  USING (
    SELECT L_TASK_OM_ID AS TASK_ID,
           'EQ-NET-OM-01' AS EQUIPMENT_ID FROM DUAL
    UNION ALL
    SELECT L_TASK_NG_ID, 'EQ-NET-NG-01' FROM DUAL
  ) S
  ON (T.TASK_ID = S.TASK_ID AND T.EQUIPMENT_ID = S.EQUIPMENT_ID)
  WHEN MATCHED THEN
    UPDATE SET
      T.SOURCE_TYPE = 'SYNTHETIC_DEMO',
      T.SOURCE_REFERENCE_ID = 'BLOG-DEMO-TASK-001'
  WHEN NOT MATCHED THEN
    INSERT (
      TASK_ID, EQUIPMENT_ID, SOURCE_TYPE, SOURCE_REFERENCE_ID
    ) VALUES (
      S.TASK_ID, S.EQUIPMENT_ID,
      'SYNTHETIC_DEMO', 'BLOG-DEMO-TASK-001'
    );

  MERGE INTO LSF_KG_TASK_DOCUMENT T
  USING (
    SELECT L_TASK_OM_ID AS TASK_ID,
           'SB-NW-2603' AS DOCUMENT_ID FROM DUAL
    UNION ALL
    SELECT L_TASK_OM_ID, 'OPS-NET-2.1' FROM DUAL
    UNION ALL
    SELECT L_TASK_NG_ID, 'SB-NW-2603' FROM DUAL
    UNION ALL
    SELECT L_TASK_NG_ID, 'OPS-NET-2.1' FROM DUAL
  ) S
  ON (T.TASK_ID = S.TASK_ID AND T.DOCUMENT_ID = S.DOCUMENT_ID)
  WHEN MATCHED THEN
    UPDATE SET
      T.SOURCE_TYPE = 'SYNTHETIC_DEMO',
      T.SOURCE_REFERENCE_ID = 'BLOG-DEMO-TASK-001'
  WHEN NOT MATCHED THEN
    INSERT (
      TASK_ID, DOCUMENT_ID, SOURCE_TYPE, SOURCE_REFERENCE_ID
    ) VALUES (
      S.TASK_ID, S.DOCUMENT_ID,
      'SYNTHETIC_DEMO', 'BLOG-DEMO-TASK-001'
    );

  MERGE INTO LSF_KG_LOT_DOCUMENT T
  USING (
    SELECT
      'NW-2603' AS MANUFACTURING_LOT,
      'SB-NW-2603' AS DOCUMENT_ID,
      'SERVICE_BULLETIN' AS RELATION_TYPE
    FROM DUAL
    UNION ALL
    SELECT 'NW-2603', 'OPS-NET-2.1', 'OPERATING_PROCEDURE'
    FROM DUAL
  ) S
  ON (
    T.MANUFACTURING_LOT = S.MANUFACTURING_LOT
    AND T.DOCUMENT_ID = S.DOCUMENT_ID
  )
  WHEN MATCHED THEN
    UPDATE SET
      T.RELATION_TYPE = S.RELATION_TYPE,
      T.SOURCE_TYPE = 'SYNTHETIC_DEMO',
      T.SOURCE_REFERENCE_ID = 'BLOG-DEMO-DOC-001'
  WHEN NOT MATCHED THEN
    INSERT (
      MANUFACTURING_LOT, DOCUMENT_ID, RELATION_TYPE,
      SOURCE_TYPE, SOURCE_REFERENCE_ID
    ) VALUES (
      S.MANUFACTURING_LOT, S.DOCUMENT_ID, S.RELATION_TYPE,
      'SYNTHETIC_DEMO', 'BLOG-DEMO-DOC-001'
    );

  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    RAISE;
END;
/
SQL実行結果
PL/SQL procedure successfully completed.

Task IDは環境ごとに自動採番されます。以降のTASK_IDは、実行環境で返った値を使用します。

● Graph用Viewを作成

⑤ 長いSQLで、何を作っている?.jpg

SQL Property Graphは、既存の表、External Table、ViewをGraph Elementとして使用できます。

今回は元データの物理形式をGraph定義から分離し、Node用ViewとEdge用Viewを作成します。

・ Node用Viewを作成

CREATE OR REPLACE VIEW V_KG_INCIDENTS AS
SELECT
  CAST(I.INCIDENT_ID AS VARCHAR2(40)) AS INCIDENT_ID,
  I.FESTIVAL_DATE,
  CAST(I.LOCATION_ID AS VARCHAR2(40)) AS LOCATION_ID,
  I.CATEGORY,
  I.SEVERITY,
  I.START_TS,
  I.END_TS,
  I.TITLE,
  I.IMPACTED_PEOPLE,
  CAST(I.RELATED_EQUIPMENT_ID AS VARCHAR2(40))
    AS RELATED_EQUIPMENT_ID,
  CAST(I.STATUS AS VARCHAR2(40)) AS STATUS,
  CAST(I.DOCUMENT_ID AS VARCHAR2(40)) AS DOCUMENT_ID,
  I.SUMMARY
FROM EXT_INCIDENTS I
JOIN EXT_STAGES S
  ON S.STAGE_ID = I.LOCATION_ID;

CREATE OR REPLACE VIEW V_KG_EQUIPMENT AS
SELECT
  CAST(E.EQUIPMENT_ID AS VARCHAR2(40)) AS EQUIPMENT_ID,
  CAST(E.STAGE_ID AS VARCHAR2(40)) AS STAGE_ID,
  E.EQUIPMENT_CATEGORY,
  E.MODEL_NAME,
  E.SERIAL_NUMBER,
  CAST(
    COALESCE(P.MANUFACTURING_LOT, E.MANUFACTURING_LOT)
    AS VARCHAR2(40)
  ) AS MANUFACTURING_LOT,
  E.VENDOR_ID,
  E.FIRMWARE_VERSION,
  E.INSTALLED_DATE,
  E.CRITICALITY,
  CASE
    WHEN P.EQUIPMENT_ID IS NOT NULL THEN P.SOURCE_TYPE
    ELSE 'MASTER_CSV'
  END AS LOT_SOURCE,
  CASE
    WHEN P.EQUIPMENT_ID IS NOT NULL THEN P.ASSERTION_STATUS
    ELSE 'SOURCE_RECORD'
  END AS LOT_ASSERTION_STATUS,
  CAST(
    CASE WHEN P.EQUIPMENT_ID IS NOT NULL THEN P.SOURCE_REFERENCE_ID
         ELSE 'equipment_master.csv#' || E.EQUIPMENT_ID END
    AS VARCHAR2(200)
  ) AS LOT_SOURCE_REFERENCE_ID
FROM EXT_EQUIPMENT_MASTER E
JOIN EXT_STAGES S
  ON S.STAGE_ID = E.STAGE_ID
LEFT JOIN LSF_KG_LOT_PATCH P
  ON P.EQUIPMENT_ID = E.EQUIPMENT_ID
 AND P.ASSERTION_STATUS = 'SYNTHETIC';

CREATE OR REPLACE VIEW V_KG_STAGES AS
SELECT
  CAST(STAGE_ID AS VARCHAR2(40)) AS STAGE_ID,
  STAGE_NAME,
  STAGE_TYPE,
  CAPACITY,
  LOCATION_ZONE,
  NETWORK_ZONE,
  WEATHER_EXPOSED_FLAG
FROM EXT_STAGES;

CREATE OR REPLACE VIEW V_KG_LOTS AS
SELECT DISTINCT
  MANUFACTURING_LOT
FROM V_KG_EQUIPMENT
WHERE MANUFACTURING_LOT IS NOT NULL;

CREATE OR REPLACE VIEW V_KG_TASKS AS
SELECT
  T.TASK_ID,
  'TASK-' || TO_CHAR(T.TASK_ID) AS TASK_CODE,
  CAST(T.INCIDENT_ID AS VARCHAR2(40)) AS INCIDENT_ID,
  T.TASK_TITLE,
  T.TASK_DESCRIPTION,
  T.PRIORITY,
  T.OWNER_TEAM_ID,
  T.CREATED_BY_AGENT,
  T.CREATED_AT,
  CAST(T.STATUS AS VARCHAR2(40)) AS STATUS
FROM LSF_IMPROVEMENT_TASKS T
JOIN V_KG_INCIDENTS I
  ON I.INCIDENT_ID = T.INCIDENT_ID;

CREATE OR REPLACE VIEW V_KG_DOCUMENTS AS
SELECT
  CAST(DOCUMENT_ID AS VARCHAR2(40)) AS DOCUMENT_ID,
  FILE_NAME,
  DOCUMENT_TYPE,
  VERSION,
  EFFECTIVE_DATE,
  PURPOSE
FROM EXT_DOCUMENT_CATALOG;

元データのincidents.csvとequipment_master.csv、およびそれらから生成したExternal TableにはGate系のデータも含まれますが、今回のOntologyはStage運営にScopeを限定します。そのため、V_KG_INCIDENTSとV_KG_EQUIPMENTはEXT_STAGESに存在するSTAGE_IDとInner Joinしています。Gate分析まで拡張する場合は、Gate VertexとDEPLOYED_AT_GATE、OCCURRED_AT_GATE Relationを別途追加します。

External Tableの自動推論列とDatabase Tableでは、同じIDでもVARCHAR2の長さが異なる場合があります。後述のDISALLOW MIXED PROPERTY TYPESを維持できるよう、INCIDENT_ID、EQUIPMENT_ID、STAGE_ID、DOCUMENT_ID、STATUSなどの共有PropertyはView側で型と長さを明示的に揃えています。

・ Edge用Viewを作成

Edgeにも一意なKeyを持たせます。

CREATE OR REPLACE VIEW V_KG_INCIDENT_AFFECTS AS
SELECT
  I.INCIDENT_ID || '|AFFECTS|' || I.RELATED_EQUIPMENT_ID AS EDGE_ID,
  I.INCIDENT_ID,
  I.RELATED_EQUIPMENT_ID AS EQUIPMENT_ID
FROM V_KG_INCIDENTS I
JOIN V_KG_EQUIPMENT E
  ON E.EQUIPMENT_ID = I.RELATED_EQUIPMENT_ID;

CREATE OR REPLACE VIEW V_KG_EQUIPMENT_LOT AS
SELECT
  E.EQUIPMENT_ID || '|LOT|' || E.MANUFACTURING_LOT AS EDGE_ID,
  E.EQUIPMENT_ID,
  E.MANUFACTURING_LOT,
  E.LOT_SOURCE,
  E.LOT_ASSERTION_STATUS,
  E.LOT_SOURCE_REFERENCE_ID
FROM V_KG_EQUIPMENT E
WHERE E.MANUFACTURING_LOT IS NOT NULL;

CREATE OR REPLACE VIEW V_KG_EQUIPMENT_STAGE AS
SELECT
  E.EQUIPMENT_ID || '|STAGE|' || E.STAGE_ID AS EDGE_ID,
  E.EQUIPMENT_ID,
  E.STAGE_ID
FROM V_KG_EQUIPMENT E
JOIN V_KG_STAGES S
  ON S.STAGE_ID = E.STAGE_ID;

CREATE OR REPLACE VIEW V_KG_INCIDENT_DOCUMENT AS
SELECT
  I.INCIDENT_ID || '|DOC|' || I.DOCUMENT_ID AS EDGE_ID,
  I.INCIDENT_ID,
  I.DOCUMENT_ID
FROM V_KG_INCIDENTS I
JOIN V_KG_DOCUMENTS D
  ON D.DOCUMENT_ID = I.DOCUMENT_ID;

CREATE OR REPLACE VIEW V_KG_INCIDENT_TASK AS
SELECT
  T.INCIDENT_ID || '|TASK|' || TO_CHAR(T.TASK_ID) AS EDGE_ID,
  T.INCIDENT_ID,
  T.TASK_ID
FROM V_KG_TASKS T
JOIN V_KG_INCIDENTS I
  ON I.INCIDENT_ID = T.INCIDENT_ID;

CREATE OR REPLACE VIEW V_KG_TASK_EQUIPMENT AS
SELECT
  TO_CHAR(M.TASK_ID) || '|TARGETS|' || M.EQUIPMENT_ID AS EDGE_ID,
  M.TASK_ID,
  M.EQUIPMENT_ID,
  M.SOURCE_TYPE,
  M.SOURCE_REFERENCE_ID
FROM LSF_KG_TASK_EQUIPMENT M
JOIN V_KG_TASKS T
  ON T.TASK_ID = M.TASK_ID
JOIN V_KG_EQUIPMENT E
  ON E.EQUIPMENT_ID = M.EQUIPMENT_ID;

CREATE OR REPLACE VIEW V_KG_TASK_DOCUMENT AS
SELECT
  TO_CHAR(M.TASK_ID) || '|USES|' || M.DOCUMENT_ID AS EDGE_ID,
  M.TASK_ID,
  M.DOCUMENT_ID,
  M.SOURCE_TYPE,
  M.SOURCE_REFERENCE_ID
FROM LSF_KG_TASK_DOCUMENT M
JOIN V_KG_TASKS T
  ON T.TASK_ID = M.TASK_ID
JOIN V_KG_DOCUMENTS D
  ON D.DOCUMENT_ID = M.DOCUMENT_ID;

CREATE OR REPLACE VIEW V_KG_LOT_DOCUMENT AS
SELECT
  M.MANUFACTURING_LOT || '|DOC|' || M.DOCUMENT_ID AS EDGE_ID,
  M.MANUFACTURING_LOT,
  M.DOCUMENT_ID,
  M.RELATION_TYPE,
  M.SOURCE_TYPE,
  M.SOURCE_REFERENCE_ID
FROM LSF_KG_LOT_DOCUMENT M
JOIN V_KG_LOTS L
  ON L.MANUFACTURING_LOT = M.MANUFACTURING_LOT
JOIN V_KG_DOCUMENTS D
  ON D.DOCUMENT_ID = M.DOCUMENT_ID;

・ Semantic LayerとしてCommentを追加

Data Analysis Agentや運用担当者が列の意味を理解しやすいように、Commentも追加します。

COMMENT ON TABLE V_KG_EQUIPMENT IS
  '元Equipment Masterへ出所付きの合成デモLot Relationを重ねたKnowledge Graph用View';

COMMENT ON COLUMN V_KG_EQUIPMENT.MANUFACTURING_LOT IS
  'Graph探索で使用するCanonical Manufacturing Lot';

COMMENT ON COLUMN V_KG_EQUIPMENT.LOT_SOURCE IS
  'Lot関係の出所。MASTER_CSVまたはSYNTHETIC_DEMO';

COMMENT ON TABLE V_KG_TASKS IS
  'Incidentから作成された改善Task。OPENとIN_PROGRESSを未完了として扱う';

● SQL Property Graphを作成

・ TRUSTED MODEを使用する理由

今回のVertex/EdgeにはViewとExternal Tableを使用しています。

ViewやExternal TableにはGraphが利用できるPK/FK制約がないため、明示的なKeyと参照列を指定し、TRUSTED MODEを使用します。

TRUSTED MODEは整合性確認を省略してよい、という意味ではありません。

Keyの一意性とEdgeの参照整合性は、Application側で保証する必要があります。

実際の実行順序は「View作成 → 次の整合性検査 → CREATE PROPERTY GRAPH」とします。

まず、6 Vertexと8 EdgeのKeyが非NULLかつ一意であることを検査します。

この節の合格条件: Key・Endpoint・Sourceの3検査がすべてno rows selected。Scope監査のOUT_OF_SCOPE一覧は、エラー検査と区別します。

Keyの整合性検査SQL:非NULL・一意性を確認
SQL
WITH KEY_ERRORS (ELEMENT_KIND, ELEMENT_NAME, ERROR_COUNT) AS (
  SELECT 'VERTEX', 'INCIDENTS', COUNT(*) FROM (
    SELECT INCIDENT_ID FROM V_KG_INCIDENTS
    GROUP BY INCIDENT_ID
    HAVING INCIDENT_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'VERTEX', 'EQUIPMENTS', COUNT(*) FROM (
    SELECT EQUIPMENT_ID FROM V_KG_EQUIPMENT
    GROUP BY EQUIPMENT_ID
    HAVING EQUIPMENT_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'VERTEX', 'STAGES', COUNT(*) FROM (
    SELECT STAGE_ID FROM V_KG_STAGES
    GROUP BY STAGE_ID
    HAVING STAGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'VERTEX', 'LOTS', COUNT(*) FROM (
    SELECT MANUFACTURING_LOT FROM V_KG_LOTS
    GROUP BY MANUFACTURING_LOT
    HAVING MANUFACTURING_LOT IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'VERTEX', 'TASKS', COUNT(*) FROM (
    SELECT TASK_ID FROM V_KG_TASKS
    GROUP BY TASK_ID
    HAVING TASK_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'VERTEX', 'DOCUMENTS', COUNT(*) FROM (
    SELECT DOCUMENT_ID FROM V_KG_DOCUMENTS
    GROUP BY DOCUMENT_ID
    HAVING DOCUMENT_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'INCIDENT_AFFECTS', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_INCIDENT_AFFECTS
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'EQUIPMENT_LOT', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_EQUIPMENT_LOT
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'EQUIPMENT_STAGE', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_EQUIPMENT_STAGE
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'INCIDENT_DOCUMENT', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_INCIDENT_DOCUMENT
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'INCIDENT_TASK', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_INCIDENT_TASK
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'TASK_EQUIPMENT', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_TASK_EQUIPMENT
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'TASK_DOCUMENT', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_TASK_DOCUMENT
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
  UNION ALL
  SELECT 'EDGE', 'LOT_DOCUMENT', COUNT(*) FROM (
    SELECT EDGE_ID FROM V_KG_LOT_DOCUMENT
    GROUP BY EDGE_ID HAVING EDGE_ID IS NULL OR COUNT(*) > 1
  )
)
SELECT ELEMENT_KIND, ELEMENT_NAME, ERROR_COUNT
FROM KEY_ERRORS
WHERE ERROR_COUNT > 0
ORDER BY ELEMENT_KIND, ELEMENT_NAME;
SQL実行結果
no rows selected

次に、8 EdgeすべてのSourceとDestinationが存在することを検査します。

Endpointの整合性検査SQL:参照先の存在を確認
SQL
WITH ENDPOINT_ERRORS (EDGE_NAME, ERROR_COUNT) AS (
  SELECT 'INCIDENT_AFFECTS', COUNT(*)
  FROM V_KG_INCIDENT_AFFECTS E
  LEFT JOIN V_KG_INCIDENTS S ON S.INCIDENT_ID = E.INCIDENT_ID
  LEFT JOIN V_KG_EQUIPMENT D ON D.EQUIPMENT_ID = E.EQUIPMENT_ID
  WHERE S.INCIDENT_ID IS NULL OR D.EQUIPMENT_ID IS NULL
  UNION ALL
  SELECT 'EQUIPMENT_LOT', COUNT(*)
  FROM V_KG_EQUIPMENT_LOT E
  LEFT JOIN V_KG_EQUIPMENT S ON S.EQUIPMENT_ID = E.EQUIPMENT_ID
  LEFT JOIN V_KG_LOTS D
    ON D.MANUFACTURING_LOT = E.MANUFACTURING_LOT
  WHERE S.EQUIPMENT_ID IS NULL OR D.MANUFACTURING_LOT IS NULL
  UNION ALL
  SELECT 'EQUIPMENT_STAGE', COUNT(*)
  FROM V_KG_EQUIPMENT_STAGE E
  LEFT JOIN V_KG_EQUIPMENT S ON S.EQUIPMENT_ID = E.EQUIPMENT_ID
  LEFT JOIN V_KG_STAGES D ON D.STAGE_ID = E.STAGE_ID
  WHERE S.EQUIPMENT_ID IS NULL OR D.STAGE_ID IS NULL
  UNION ALL
  SELECT 'INCIDENT_DOCUMENT', COUNT(*)
  FROM V_KG_INCIDENT_DOCUMENT E
  LEFT JOIN V_KG_INCIDENTS S ON S.INCIDENT_ID = E.INCIDENT_ID
  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = E.DOCUMENT_ID
  WHERE S.INCIDENT_ID IS NULL OR D.DOCUMENT_ID IS NULL
  UNION ALL
  SELECT 'INCIDENT_TASK', COUNT(*)
  FROM V_KG_INCIDENT_TASK E
  LEFT JOIN V_KG_INCIDENTS S ON S.INCIDENT_ID = E.INCIDENT_ID
  LEFT JOIN V_KG_TASKS D ON D.TASK_ID = E.TASK_ID
  WHERE S.INCIDENT_ID IS NULL OR D.TASK_ID IS NULL
  UNION ALL
  SELECT 'TASK_EQUIPMENT', COUNT(*)
  FROM V_KG_TASK_EQUIPMENT E
  LEFT JOIN V_KG_TASKS S ON S.TASK_ID = E.TASK_ID
  LEFT JOIN V_KG_EQUIPMENT D ON D.EQUIPMENT_ID = E.EQUIPMENT_ID
  WHERE S.TASK_ID IS NULL OR D.EQUIPMENT_ID IS NULL
  UNION ALL
  SELECT 'TASK_DOCUMENT', COUNT(*)
  FROM V_KG_TASK_DOCUMENT E
  LEFT JOIN V_KG_TASKS S ON S.TASK_ID = E.TASK_ID
  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = E.DOCUMENT_ID
  WHERE S.TASK_ID IS NULL OR D.DOCUMENT_ID IS NULL
  UNION ALL
  SELECT 'LOT_DOCUMENT', COUNT(*)
  FROM V_KG_LOT_DOCUMENT E
  LEFT JOIN V_KG_LOTS S
    ON S.MANUFACTURING_LOT = E.MANUFACTURING_LOT
  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = E.DOCUMENT_ID
  WHERE S.MANUFACTURING_LOT IS NULL OR D.DOCUMENT_ID IS NULL
)
SELECT EDGE_NAME, ERROR_COUNT
FROM ENDPOINT_ERRORS
WHERE ERROR_COUNT > 0
ORDER BY EDGE_NAME;
SQL実行結果
no rows selected

Edge ViewはInner Joinで未解決Endpointを除外するため、Viewの出力だけを検査しても、結合前に消えた誤参照を検出できません。ここでは元TableからScopeと参照を確認します。

この固定デモでは、EXT_STAGESに存在するLocationをIN_SCOPEとします。Stage以外では、前回作成したV_GATE_CONGESTION_ANALYSISで確認できるGateに加えて、Equipment MasterにあるGATE-Sと、IncidentデータにあるMERCH-CENTRAL、MERCH-NORTHを確認済みのLocationとしてOUT_OF_SCOPEに定義します。どの一覧にもない値とNULLを、都合よくScope外扱いにはしません。

V_GATE_CONGESTION_ANALYSISは入場ログに現れたGateだけを保持するため、ログに現れない正規GateのLocation Masterにはなりません。そのため、この固定デモで確認した対象外Locationを明示的に補います。実運用では、Gateや物販エリアを含む正式なLocation MasterからScopeを作成します。

V_GATE_CONGESTION_ANALYSISが存在しない場合は、接続先Schemaと前回記事の構築状況を確認してください。本記事では、このViewを含む前回環境が構築済みであることを前提に進めます。次のViewは検査専用であり、PAFやGraphへは公開しません。

・ V_KG_SOURCE_SCOPE_AUDIT VIEW作成

SQL
CREATE OR REPLACE VIEW V_KG_LOCATION_SCOPE AS
SELECT CAST(STAGE_ID AS VARCHAR2(40)) AS LOCATION_ID,
       CAST('IN_SCOPE' AS VARCHAR2(20)) AS SCOPE_STATUS
FROM EXT_STAGES
UNION ALL
SELECT X.LOCATION_ID,
       CAST('OUT_OF_SCOPE' AS VARCHAR2(20))
FROM (
  SELECT DISTINCT CAST(G.GATE_ID AS VARCHAR2(40)) AS LOCATION_ID
  FROM V_GATE_CONGESTION_ANALYSIS G
  WHERE G.GATE_ID IS NOT NULL
  UNION
  SELECT CAST('GATE-S' AS VARCHAR2(40)) FROM DUAL
  UNION
  SELECT CAST('MERCH-CENTRAL' AS VARCHAR2(40)) FROM DUAL
  UNION
  SELECT CAST('MERCH-NORTH' AS VARCHAR2(40)) FROM DUAL
) X
WHERE X.LOCATION_ID IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM EXT_STAGES S WHERE S.STAGE_ID = X.LOCATION_ID
  );

CREATE OR REPLACE VIEW V_KG_SOURCE_SCOPE_AUDIT AS
SELECT 'INCIDENT' AS ENTITY_TYPE,
       CAST(I.INCIDENT_ID AS VARCHAR2(40)) AS ENTITY_ID,
       CAST(I.LOCATION_ID AS VARCHAR2(40)) AS LOCATION_ID,
       COALESCE(S.SCOPE_STATUS, 'UNKNOWN_OR_MISSING') AS SCOPE_STATUS
FROM EXT_INCIDENTS I
LEFT JOIN V_KG_LOCATION_SCOPE S ON S.LOCATION_ID = I.LOCATION_ID
UNION ALL
SELECT 'EQUIPMENT', CAST(E.EQUIPMENT_ID AS VARCHAR2(40)),
       CAST(E.STAGE_ID AS VARCHAR2(40)),
       COALESCE(S.SCOPE_STATUS, 'UNKNOWN_OR_MISSING')
FROM EXT_EQUIPMENT_MASTER E
LEFT JOIN V_KG_LOCATION_SCOPE S ON S.LOCATION_ID = E.STAGE_ID
UNION ALL
SELECT 'TASK', CAST(TO_CHAR(T.TASK_ID) AS VARCHAR2(40)),
       CAST(I.LOCATION_ID AS VARCHAR2(40)),
       CASE WHEN I.INCIDENT_ID IS NULL THEN 'UNKNOWN_OR_MISSING'
            ELSE COALESCE(S.SCOPE_STATUS, 'UNKNOWN_OR_MISSING') END
FROM LSF_IMPROVEMENT_TASKS T
LEFT JOIN EXT_INCIDENTS I ON I.INCIDENT_ID = T.INCIDENT_ID
LEFT JOIN V_KG_LOCATION_SCOPE S ON S.LOCATION_ID = I.LOCATION_ID;

・ 作成したV_KG_SOURCE_SCOPE_AUDIT VIEW確認

SQL
SELECT ENTITY_TYPE, ENTITY_ID, LOCATION_ID, SCOPE_STATUS
FROM V_KG_SOURCE_SCOPE_AUDIT
WHERE SCOPE_STATUS <> 'IN_SCOPE'
ORDER BY SCOPE_STATUS, ENTITY_TYPE, ENTITY_ID;
実行ログの全文(確認結果)
SQL実行結果
ENTITY_TYPE    ENTITY_ID        LOCATION_ID      SCOPE_STATUS
______________ ________________ ________________ _______________
EQUIPMENT      SCAN-A-01        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-02        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-03        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-04        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-05        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-06        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-07        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-08        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-09        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-10        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-11        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-12        GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-A-BK1       GATE-A           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-01        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-02        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-03        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-04        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-05        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-06        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-07        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-08        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-09        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-10        GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-B-BK1       GATE-B           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-01        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-02        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-03        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-04        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-05        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-06        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-07        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-08        GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-C-BK1       GATE-C           OUT_OF_SCOPE
EQUIPMENT      SCAN-S-01        GATE-S           OUT_OF_SCOPE
EQUIPMENT      SCAN-S-02        GATE-S           OUT_OF_SCOPE
EQUIPMENT      SCAN-S-03        GATE-S           OUT_OF_SCOPE
EQUIPMENT      SCAN-S-BK1       GATE-S           OUT_OF_SCOPE
EQUIPMENT      SCAN-V-01        GATE-V           OUT_OF_SCOPE
EQUIPMENT      SCAN-V-02        GATE-V           OUT_OF_SCOPE
EQUIPMENT      SCAN-V-03        GATE-V           OUT_OF_SCOPE
EQUIPMENT      SCAN-V-04        GATE-V           OUT_OF_SCOPE
EQUIPMENT      SCAN-V-BK1       GATE-V           OUT_OF_SCOPE
INCIDENT       INC-2026-073     MERCH-CENTRAL    OUT_OF_SCOPE
INCIDENT       INC-2026-084     GATE-C           OUT_OF_SCOPE
INCIDENT       INC-2026-M003    MERCH-NORTH      OUT_OF_SCOPE
INCIDENT       INC-2026-M004    GATE-B           OUT_OF_SCOPE
INCIDENT       INC-2026-M005    MERCH-NORTH      OUT_OF_SCOPE
INCIDENT       INC-2026-M006    GATE-B           OUT_OF_SCOPE
INCIDENT       INC-2026-M008    GATE-A           OUT_OF_SCOPE
INCIDENT       INC-2026-M011    MERCH-NORTH      OUT_OF_SCOPE
INCIDENT       INC-2026-M013    GATE-B           OUT_OF_SCOPE
INCIDENT       INC-2026-M014    GATE-B           OUT_OF_SCOPE
INCIDENT       INC-2026-M015    MERCH-NORTH      OUT_OF_SCOPE
INCIDENT       INC-2026-M016    GATE-A           OUT_OF_SCOPE
INCIDENT       INC-2026-M017    GATE-A           OUT_OF_SCOPE

55 rows selected.

この一覧にGateや物販エリアのOUT_OF_SCOPEがあることは正常です。UNKNOWN_OR_MISSINGがある場合は元データやScopeを確認します。配置不明、存在しないID、正規だが未登録のLocationを、Graphの結果0件として見過ごさないためです。

続いて、Scope内の関係と、今回追加した補足Relationの参照先を検査します。補足Relationは今回のStage Scope向けに追加したものなので、Scope外へつながる行もエラーとして扱います。

Sourceの整合性検査SQL:元データとScopeを確認
SQL
WITH SOURCE_ERRORS (RELATION_NAME, ERROR_COUNT) AS (
  SELECT 'SCOPE_UNKNOWN_OR_MISSING', COUNT(*)
  FROM V_KG_SOURCE_SCOPE_AUDIT
  WHERE SCOPE_STATUS = 'UNKNOWN_OR_MISSING'
  UNION ALL
  SELECT 'INCIDENT_AFFECTS_SOURCE', COUNT(*)
  FROM EXT_INCIDENTS I
  JOIN EXT_STAGES S ON S.STAGE_ID = I.LOCATION_ID
  LEFT JOIN V_KG_EQUIPMENT E
    ON E.EQUIPMENT_ID = I.RELATED_EQUIPMENT_ID
  WHERE I.RELATED_EQUIPMENT_ID IS NOT NULL
    AND E.EQUIPMENT_ID IS NULL
  UNION ALL
  SELECT 'EQUIPMENT_LOT_SOURCE', COUNT(*)
  FROM EXT_EQUIPMENT_MASTER E
  JOIN EXT_STAGES S ON S.STAGE_ID = E.STAGE_ID
  LEFT JOIN LSF_KG_LOT_PATCH P
    ON P.EQUIPMENT_ID = E.EQUIPMENT_ID
   AND P.ASSERTION_STATUS = 'SYNTHETIC'
  WHERE COALESCE(P.MANUFACTURING_LOT, E.MANUFACTURING_LOT) IS NULL
  UNION ALL
  SELECT 'EQUIPMENT_STAGE_SOURCE', COUNT(*)
  FROM EXT_EQUIPMENT_MASTER E
  LEFT JOIN V_KG_LOCATION_SCOPE S ON S.LOCATION_ID = E.STAGE_ID
  WHERE S.LOCATION_ID IS NULL
  UNION ALL
  SELECT 'INCIDENT_DOCUMENT_SOURCE', COUNT(*)
  FROM EXT_INCIDENTS I
  JOIN EXT_STAGES S ON S.STAGE_ID = I.LOCATION_ID
  LEFT JOIN EXT_DOCUMENT_CATALOG D ON D.DOCUMENT_ID = I.DOCUMENT_ID
  WHERE I.DOCUMENT_ID IS NOT NULL
    AND D.DOCUMENT_ID IS NULL
  UNION ALL
  SELECT 'INCIDENT_TASK_SOURCE', COUNT(*)
  FROM LSF_IMPROVEMENT_TASKS T
  LEFT JOIN EXT_INCIDENTS I ON I.INCIDENT_ID = T.INCIDENT_ID
  WHERE I.INCIDENT_ID IS NULL
  UNION ALL
  SELECT 'LOT_PATCH_SOURCE', COUNT(*)
  FROM LSF_KG_LOT_PATCH P
  LEFT JOIN V_KG_EQUIPMENT E ON E.EQUIPMENT_ID = P.EQUIPMENT_ID
  WHERE E.EQUIPMENT_ID IS NULL
  UNION ALL
  SELECT 'TASK_EQUIPMENT_SOURCE', COUNT(*)
  FROM LSF_KG_TASK_EQUIPMENT M
  LEFT JOIN V_KG_TASKS T ON T.TASK_ID = M.TASK_ID
  LEFT JOIN V_KG_EQUIPMENT E ON E.EQUIPMENT_ID = M.EQUIPMENT_ID
  WHERE T.TASK_ID IS NULL OR E.EQUIPMENT_ID IS NULL
  UNION ALL
  SELECT 'TASK_DOCUMENT_SOURCE', COUNT(*)
  FROM LSF_KG_TASK_DOCUMENT M
  LEFT JOIN V_KG_TASKS T ON T.TASK_ID = M.TASK_ID
  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = M.DOCUMENT_ID
  WHERE T.TASK_ID IS NULL OR D.DOCUMENT_ID IS NULL
  UNION ALL
  SELECT 'LOT_DOCUMENT_SOURCE', COUNT(*)
  FROM LSF_KG_LOT_DOCUMENT M
  LEFT JOIN V_KG_LOTS L
    ON L.MANUFACTURING_LOT = M.MANUFACTURING_LOT
  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = M.DOCUMENT_ID
  WHERE L.MANUFACTURING_LOT IS NULL OR D.DOCUMENT_ID IS NULL
)
SELECT RELATION_NAME, ERROR_COUNT
FROM SOURCE_ERRORS
WHERE ERROR_COUNT > 0
ORDER BY RELATION_NAME;
SQL実行結果
no rows selected

Key、Endpoint、Sourceの3つのエラー検査がすべて0行を返すことを確認します。Scope監査のOUT_OF_SCOPE一覧は別扱いです。エラー検査が1行でも返った場合は、元データ・参照先・Scopeの定義を修正するまでGraphを作成しません。

エラーが出た場合は、次の調査手順で対象IDと参照先を確認します。

Source検査が0行にならない場合の調査・修正手順

Source検査が0行にならない場合

集計結果だけでは、正規のScope外データと誤参照を区別できません。まず次のSQLで対象IDを確認します。

SQL
-- UNKNOWN_OR_MISSINGの内訳
SELECT ENTITY_TYPE, ENTITY_ID, LOCATION_ID, SCOPE_STATUS
FROM V_KG_SOURCE_SCOPE_AUDIT
WHERE SCOPE_STATUS = 'UNKNOWN_OR_MISSING'
ORDER BY ENTITY_TYPE, ENTITY_ID;
SQL
-- Location Scopeに登録されていないEquipment
SELECT E.EQUIPMENT_ID,
       E.EQUIPMENT_CATEGORY,
       E.STAGE_ID AS LOCATION_ID
FROM EXT_EQUIPMENT_MASTER E
LEFT JOIN V_KG_LOCATION_SCOPE S
  ON S.LOCATION_ID = E.STAGE_ID
WHERE S.LOCATION_ID IS NULL
ORDER BY E.EQUIPMENT_ID;
SQL
-- Stage Scope内のIncidentが参照している未登録Document
SELECT I.INCIDENT_ID,
       I.LOCATION_ID,
       I.DOCUMENT_ID
FROM EXT_INCIDENTS I
JOIN EXT_STAGES S
  ON S.STAGE_ID = I.LOCATION_ID
LEFT JOIN EXT_DOCUMENT_CATALOG D
  ON D.DOCUMENT_ID = I.DOCUMENT_ID
WHERE I.DOCUMENT_ID IS NOT NULL
  AND D.DOCUMENT_ID IS NULL
ORDER BY I.INCIDENT_ID;

SCOPE_UNKNOWN_OR_MISSINGにはIncident、Equipment、Taskが含まれるため、EQUIPMENT_STAGE_SOURCEなどと同じ不整合を重複して数える場合があります。件数を足して別々のエラーと判断せず、上の一覧をID単位で確認します。

Location IDが正規のGateや物販エリアで、今回のStage運営Scopeから除外する対象なら、そのIDを正式なLocation Masterまたは元データと照合し、V_KG_LOCATION_SCOPEへOUT_OF_SCOPEとして追加します。V_GATE_CONGESTION_ANALYSISは入場ログに現れたGateだけを保持するため、ログに現れない正規Gateは自動では登録されません。Stage IDであればEXT_STAGESへの未登録、文字列の空白・大文字小文字、元データの誤記を修正します。NULLや存在しないIDを、確認せずOUT_OF_SCOPEへ変更しません。

今回の固定データでINC-2026-091/WX-2026-009が残る場合は、旧Parquetを参照しています。前述の完成済みファイルのUpload先とUSER_EXTERNAL_LOCATIONS.LOCATIONを確認し、同じObject名へ配置してから再検査します。修正済みの配布データでは、このIncidentのDOCUMENT_IDはNULLです。

それ以外の未登録Documentは、次のいずれかに整理します。

  • 実在する根拠文書なら、Document ID、Version、File Nameをdocument_catalog.csvへ登録し、対応するPDFを配置する。
  • Catalog上の別IDを指す誤記なら、Incident側のDOCUMENT_IDを正しいIDへ修正する。
  • そのIncidentに文書が存在しないなら、空文字ではなくNULLとして扱う。

修正後にV_KG_LOCATION_SCOPEとV_KG_SOURCE_SCOPE_AUDITを再作成し、Source検査を再実行します。3種類のエラー検査がすべて0行になってからProperty Graphを作成します。

・ CREATE PROPERTY GRAPHを実行

Node用ViewをVertex、Edge用ViewをEdgeとして定義します。

CREATE OR REPLACE PROPERTY GRAPH LSF_FESTIVAL_OPS_GRAPH
  VERTEX TABLES (
    V_KG_INCIDENTS AS INCIDENTS
      KEY (INCIDENT_ID)
      LABEL INCIDENT
      PROPERTIES (
        INCIDENT_ID, FESTIVAL_DATE, LOCATION_ID, CATEGORY,
        SEVERITY, START_TS, END_TS, TITLE, STATUS, DOCUMENT_ID
      ),

    V_KG_EQUIPMENT AS EQUIPMENTS
      KEY (EQUIPMENT_ID)
      LABEL EQUIPMENT
      PROPERTIES (
        EQUIPMENT_ID, STAGE_ID, EQUIPMENT_CATEGORY, MODEL_NAME,
        SERIAL_NUMBER, MANUFACTURING_LOT, FIRMWARE_VERSION,
        CRITICALITY, LOT_SOURCE, LOT_ASSERTION_STATUS,
        LOT_SOURCE_REFERENCE_ID
      ),

    V_KG_STAGES AS STAGES
      KEY (STAGE_ID)
      LABEL STAGE
      PROPERTIES (STAGE_ID, STAGE_NAME, STAGE_TYPE, CAPACITY),

    V_KG_LOTS AS LOTS
      KEY (MANUFACTURING_LOT)
      LABEL MANUFACTURING_LOT
      PROPERTIES (MANUFACTURING_LOT),

    V_KG_TASKS AS TASKS
      KEY (TASK_ID)
      LABEL TASK
      PROPERTIES (
        TASK_ID, TASK_CODE, INCIDENT_ID, TASK_TITLE,
        PRIORITY, OWNER_TEAM_ID, CREATED_BY_AGENT, CREATED_AT, STATUS
      ),

    V_KG_DOCUMENTS AS DOCUMENTS
      KEY (DOCUMENT_ID)
      LABEL DOCUMENT
      PROPERTIES (
        DOCUMENT_ID, FILE_NAME, DOCUMENT_TYPE,
        VERSION, EFFECTIVE_DATE, PURPOSE
      )
  )
  EDGE TABLES (
    V_KG_INCIDENT_AFFECTS AS INCIDENT_AFFECTS
      KEY (EDGE_ID)
      SOURCE KEY (INCIDENT_ID) REFERENCES INCIDENTS (INCIDENT_ID)
      DESTINATION KEY (EQUIPMENT_ID) REFERENCES EQUIPMENTS (EQUIPMENT_ID)
      LABEL AFFECTS NO PROPERTIES,

    V_KG_EQUIPMENT_LOT AS EQUIPMENT_LOT
      KEY (EDGE_ID)
      SOURCE KEY (EQUIPMENT_ID) REFERENCES EQUIPMENTS (EQUIPMENT_ID)
      DESTINATION KEY (MANUFACTURING_LOT)
        REFERENCES LOTS (MANUFACTURING_LOT)
      LABEL BELONGS_TO_LOT
      PROPERTIES (
        LOT_SOURCE, LOT_ASSERTION_STATUS, LOT_SOURCE_REFERENCE_ID
      ),

    V_KG_EQUIPMENT_STAGE AS EQUIPMENT_STAGE
      KEY (EDGE_ID)
      SOURCE KEY (EQUIPMENT_ID) REFERENCES EQUIPMENTS (EQUIPMENT_ID)
      DESTINATION KEY (STAGE_ID) REFERENCES STAGES (STAGE_ID)
      LABEL DEPLOYED_AT NO PROPERTIES,

    V_KG_INCIDENT_DOCUMENT AS INCIDENT_DOCUMENT
      KEY (EDGE_ID)
      SOURCE KEY (INCIDENT_ID) REFERENCES INCIDENTS (INCIDENT_ID)
      DESTINATION KEY (DOCUMENT_ID) REFERENCES DOCUMENTS (DOCUMENT_ID)
      LABEL DOCUMENTED_BY NO PROPERTIES,

    V_KG_INCIDENT_TASK AS INCIDENT_TASK
      KEY (EDGE_ID)
      SOURCE KEY (INCIDENT_ID) REFERENCES INCIDENTS (INCIDENT_ID)
      DESTINATION KEY (TASK_ID) REFERENCES TASKS (TASK_ID)
      LABEL CREATES NO PROPERTIES,

    V_KG_TASK_EQUIPMENT AS TASK_EQUIPMENT
      KEY (EDGE_ID)
      SOURCE KEY (TASK_ID) REFERENCES TASKS (TASK_ID)
      DESTINATION KEY (EQUIPMENT_ID) REFERENCES EQUIPMENTS (EQUIPMENT_ID)
      LABEL TARGETS
      PROPERTIES (SOURCE_TYPE, SOURCE_REFERENCE_ID),

    V_KG_TASK_DOCUMENT AS TASK_DOCUMENT
      KEY (EDGE_ID)
      SOURCE KEY (TASK_ID) REFERENCES TASKS (TASK_ID)
      DESTINATION KEY (DOCUMENT_ID) REFERENCES DOCUMENTS (DOCUMENT_ID)
      LABEL USES_PROCEDURE
      PROPERTIES (SOURCE_TYPE, SOURCE_REFERENCE_ID),

    V_KG_LOT_DOCUMENT AS LOT_DOCUMENT
      KEY (EDGE_ID)
      SOURCE KEY (MANUFACTURING_LOT)
        REFERENCES LOTS (MANUFACTURING_LOT)
      DESTINATION KEY (DOCUMENT_ID) REFERENCES DOCUMENTS (DOCUMENT_ID)
      LABEL GOVERNED_BY
      PROPERTIES (RELATION_TYPE, SOURCE_TYPE, SOURCE_REFERENCE_ID)
  )
  OPTIONS (TRUSTED MODE, DISALLOW MIXED PROPERTY TYPES);
SQL実行結果
Property GRAPH created.

SQL Property Graphは元データを別のGraph StoreへCopyするのではなく、Underlying Table/ViewとのMapping Metadataを保持します。

そのため、元データや補足Relationが更新されると、Graph Queryも現在のデータを参照します。

・ Graph Metadataを確認

SQL
SELECT GRAPH_NAME
FROM USER_PROPERTY_GRAPHS
WHERE GRAPH_NAME = 'LSF_FESTIVAL_OPS_GRAPH';
SQL実行結果
GRAPH_NAME
_________________________
LSF_FESTIVAL_OPS_GRAPH
SQL
SELECT GRAPH_NAME, ELEMENT_NAME, ELEMENT_KIND, OBJECT_NAME
FROM USER_PG_ELEMENTS
WHERE GRAPH_NAME = 'LSF_FESTIVAL_OPS_GRAPH'
ORDER BY ELEMENT_KIND, ELEMENT_NAME;
SQL実行結果
GRAPH_NAME                ELEMENT_NAME         ELEMENT_KIND    OBJECT_NAME
_________________________ ____________________ _______________ _________________________
LSF_FESTIVAL_OPS_GRAPH    EQUIPMENT_LOT        EDGE            V_KG_EQUIPMENT_LOT
LSF_FESTIVAL_OPS_GRAPH    EQUIPMENT_STAGE      EDGE            V_KG_EQUIPMENT_STAGE
LSF_FESTIVAL_OPS_GRAPH    INCIDENT_AFFECTS     EDGE            V_KG_INCIDENT_AFFECTS
LSF_FESTIVAL_OPS_GRAPH    INCIDENT_DOCUMENT    EDGE            V_KG_INCIDENT_DOCUMENT
LSF_FESTIVAL_OPS_GRAPH    INCIDENT_TASK        EDGE            V_KG_INCIDENT_TASK
LSF_FESTIVAL_OPS_GRAPH    LOT_DOCUMENT         EDGE            V_KG_LOT_DOCUMENT
LSF_FESTIVAL_OPS_GRAPH    TASK_DOCUMENT        EDGE            V_KG_TASK_DOCUMENT
LSF_FESTIVAL_OPS_GRAPH    TASK_EQUIPMENT       EDGE            V_KG_TASK_EQUIPMENT
LSF_FESTIVAL_OPS_GRAPH    DOCUMENTS            VERTEX          V_KG_DOCUMENTS
LSF_FESTIVAL_OPS_GRAPH    EQUIPMENTS           VERTEX          V_KG_EQUIPMENT
LSF_FESTIVAL_OPS_GRAPH    INCIDENTS            VERTEX          V_KG_INCIDENTS
LSF_FESTIVAL_OPS_GRAPH    LOTS                 VERTEX          V_KG_LOTS
LSF_FESTIVAL_OPS_GRAPH    STAGES               VERTEX          V_KG_STAGES
LSF_FESTIVAL_OPS_GRAPH    TASKS                VERTEX          V_KG_TASKS

14 rows selected.

Graph作成者自身には、Graph ObjectのREAD権限を別途付与する必要はありません。後で他のUserからGraphを直接参照させる場合は、対象User GRAPH_READERが存在する場合に限り、次のObject権限を任意で付与します。

GRANT READ ON PROPERTY GRAPH LSF_FESTIVAL_OPS_GRAPH TO GRAPH_READER;

本記事のPAF実行経路はAUTHID DEFINER Packageのため、LSF_AGENTへGraphの直接READ権限は付与しません。

● GRAPH_TABLEで影響範囲を検索

④ 同じLotをたどると、調査が広がる.jpg

・ Incidentから同一Lotの別Equipmentを検索

INC-2026-081から、障害Equipment、Manufacturing Lot、同一Lotの別Equipment、その配置先Stageをたどります。

1) 変数設定

SQL
VAR P_INCIDENT_ID VARCHAR2(30)
EXEC :P_INCIDENT_ID := 'INC-2026-081';
SQL実行結果
PL/SQL procedure successfully completed.

2) Incidentから同一Lotの別Equipmentを検索

SQL
SELECT *
FROM GRAPH_TABLE (
  LSF_FESTIVAL_OPS_GRAPH
  MATCH
    (I IS INCIDENT)
      -[A IS AFFECTS]->
    (E1 IS EQUIPMENT)
      -[B1 IS BELONGS_TO_LOT]->
    (L IS MANUFACTURING_LOT)
      <-[B2 IS BELONGS_TO_LOT]-
    (E2 IS EQUIPMENT)
      -[D IS DEPLOYED_AT]->
    (S IS STAGE)
  WHERE I.INCIDENT_ID = :P_INCIDENT_ID
    AND E1.EQUIPMENT_ID <> E2.EQUIPMENT_ID
  COLUMNS (
    I.INCIDENT_ID       AS INCIDENT_ID,
    E1.EQUIPMENT_ID     AS AFFECTED_EQUIPMENT_ID,
    L.MANUFACTURING_LOT AS MANUFACTURING_LOT,
    E2.EQUIPMENT_ID     AS RELATED_EQUIPMENT_ID,
    S.STAGE_ID          AS RELATED_STAGE_ID,
    S.STAGE_NAME        AS RELATED_STAGE_NAME,
    B2.LOT_SOURCE       AS LOT_SOURCE,
    B2.LOT_ASSERTION_STATUS AS LOT_ASSERTION_STATUS,
    B2.LOT_SOURCE_REFERENCE_ID AS LOT_SOURCE_REFERENCE_ID,
    B1.LOT_SOURCE AS AFFECTED_LOT_SOURCE,
    B1.LOT_ASSERTION_STATUS AS AFFECTED_LOT_ASSERTION_STATUS,
    B1.LOT_SOURCE_REFERENCE_ID AS AFFECTED_LOT_SOURCE_REFERENCE_ID
  )
)
ORDER BY RELATED_STAGE_ID, RELATED_EQUIPMENT_ID;
SQL実行結果
INCIDENT_ID     AFFECTED_EQUIPMENT_ID    MANUFACTURING_LOT    RELATED_EQUIPMENT_ID    RELATED_STAGE_ID    RELATED_STAGE_NAME    LOT_SOURCE        LOT_ASSERTION_STATUS    LOT_SOURCE_REFERENCE_ID    AFFECTED_LOT_SOURCE    AFFECTED_LOT_ASSERTION_STATUS    AFFECTED_LOT_SOURCE_REFERENCE_ID
_______________ ________________________ ____________________ _______________________ ___________________ _____________________ _________________ _______________________ __________________________ ______________________ ________________________________ ____________________________________
INC-2026-081    EQ-NET-WA-01             NW-2603              EQ-NET-NG-01            STG-NG              Neon Groove Stage     SYNTHETIC_DEMO    SYNTHETIC               BLOG-DEMO-LOT-001          MASTER_CSV             SOURCE_RECORD                    equipment_master.csv#EQ-NET-WA-01
INC-2026-081    EQ-NET-WA-01             NW-2603              EQ-NET-OM-01            STG-OM              Orbit Main Stage      SYNTHETIC_DEMO    SYNTHETIC               BLOG-DEMO-LOT-001          MASTER_CSV             SOURCE_RECORD                    equipment_master.csv#EQ-NET-WA-01

上記の合成デモRelationを反映した場合の期待結果は次の2件です。

Incident 障害Equipment Lot 同一Lot Equipment 配置Stage Source Assertion
INC-2026-081 EQ-NET-WA-01 NW-2603 EQ-NET-NG-01 STG-NG / Neon Groove Stage SYNTHETIC_DEMO SYNTHETIC
INC-2026-081 EQ-NET-WA-01 NW-2603 EQ-NET-OM-01 STG-OM / Orbit Main Stage SYNTHETIC_DEMO SYNTHETIC

通常のSQLでもJOINできますが、Graph Queryでは「どのRelationを、どの順番でたどったか」がPatternとして明示されています。

・ 未完了Taskを検索

GET_OPEN_TASKSは、CREATESでIncidentに明示的に関連付けられ、TARGETSでEquipmentを指定した未完了Taskを返します。同一LotであることからTaskを推測するAPIではありません。

SQL
SELECT *
FROM GRAPH_TABLE (
  LSF_FESTIVAL_OPS_GRAPH
  MATCH
    (I IS INCIDENT)
      -[C IS CREATES]->
    (T IS TASK)
      -[X IS TARGETS]->
    (E IS EQUIPMENT)
      -[D IS DEPLOYED_AT]->
    (S IS STAGE)
  WHERE I.INCIDENT_ID = 'INC-2026-081'
    AND T.STATUS IN ('OPEN', 'IN_PROGRESS')
  COLUMNS (
    I.INCIDENT_ID   AS INCIDENT_ID,
    T.TASK_ID       AS TASK_ID,
    T.TASK_CODE     AS TASK_CODE,
    T.PRIORITY      AS PRIORITY,
    T.STATUS        AS TASK_STATUS,
    E.EQUIPMENT_ID  AS EQUIPMENT_ID,
    S.STAGE_ID      AS STAGE_ID,
    S.STAGE_NAME    AS STAGE_NAME,
    T.CREATED_BY_AGENT AS CREATED_BY_AGENT,
    X.SOURCE_TYPE AS SOURCE_TYPE,
    X.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID
  )
)
ORDER BY
  CASE PRIORITY
    WHEN 'CRITICAL' THEN 1
    WHEN 'HIGH'     THEN 2
    WHEN 'MEDIUM'   THEN 3
    WHEN 'LOW'      THEN 4
    ELSE 5
  END,
  TASK_ID;
SQL実行結果
INCIDENT_ID        TASK_ID TASK_CODE    PRIORITY    TASK_STATUS    EQUIPMENT_ID    STAGE_ID    STAGE_NAME           CREATED_BY_AGENT      SOURCE_TYPE       SOURCE_REFERENCE_ID
_______________ __________ ____________ ___________ ______________ _______________ ___________ ____________________ _____________________ _________________ ______________________
INC-2026-081           121 TASK-121     CRITICAL    OPEN           EQ-NET-OM-01    STG-OM      Orbit Main Stage     ONTOLOGY_DEMO_SEED    SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
INC-2026-081           122 TASK-122     HIGH        OPEN           EQ-NET-NG-01    STG-NG      Neon Groove Stage    ONTOLOGY_DEMO_SEED    SYNTHETIC_DEMO    BLOG-DEMO-TASK-001

次の出力結果になればOK

Task Priority Status Equipment Stage
TASK-<自動採番ID> CRITICAL OPEN EQ-NET-OM-01 Orbit Main Stage
TASK-<自動採番ID> HIGH OPEN EQ-NET-NG-01 Neon Groove Stage



・ 関連Documentを検索

Incident Report、Lotへ適用されるDocument、Taskで使用するDocumentの3つのPathを検索します。

WITH RELATED_DOCUMENTS AS (
  SELECT *
  FROM GRAPH_TABLE (
    LSF_FESTIVAL_OPS_GRAPH
    MATCH
      (I IS INCIDENT)
        -[R IS DOCUMENTED_BY]->
      (D IS DOCUMENT)
    WHERE I.INCIDENT_ID = 'INC-2026-081'
    COLUMNS (
      D.DOCUMENT_ID AS DOCUMENT_ID,
      D.FILE_NAME AS FILE_NAME,
      D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
      D.VERSION AS DOCUMENT_VERSION,
      D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
      I.INCIDENT_ID AS INCIDENT_ID,
      CAST(NULL AS VARCHAR2(40)) AS EQUIPMENT_ID,
      CAST(NULL AS VARCHAR2(40)) AS MANUFACTURING_LOT,
      CAST(NULL AS NUMBER) AS TASK_ID,
      CAST(NULL AS VARCHAR2(200)) AS TASK_CREATED_BY_AGENT,
      'INCIDENT_REPORT' AS RELATION_TYPE,
      'DOCUMENTED_BY' AS PATH,
      'INCIDENT_CSV' AS SOURCE_TYPE,
      'incidents.csv#' || I.INCIDENT_ID AS SOURCE_REFERENCE_ID,
      CAST(NULL AS VARCHAR2(30)) AS LOT_SOURCE,
      CAST(NULL AS VARCHAR2(200)) AS LOT_SOURCE_REFERENCE_ID
    )
  )
  UNION ALL
  SELECT *
  FROM GRAPH_TABLE (
    LSF_FESTIVAL_OPS_GRAPH
    MATCH
      (I IS INCIDENT)
        -[A IS AFFECTS]->
      (E IS EQUIPMENT)
        -[B IS BELONGS_TO_LOT]->
      (L IS MANUFACTURING_LOT)
        -[G IS GOVERNED_BY]->
      (D IS DOCUMENT)
    WHERE I.INCIDENT_ID = 'INC-2026-081'
    COLUMNS (
      D.DOCUMENT_ID AS DOCUMENT_ID,
      D.FILE_NAME AS FILE_NAME,
      D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
      D.VERSION AS DOCUMENT_VERSION,
      D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
      I.INCIDENT_ID AS INCIDENT_ID,
      E.EQUIPMENT_ID AS EQUIPMENT_ID,
      L.MANUFACTURING_LOT AS MANUFACTURING_LOT,
      CAST(NULL AS NUMBER) AS TASK_ID,
      CAST(NULL AS VARCHAR2(200)) AS TASK_CREATED_BY_AGENT,
      G.RELATION_TYPE AS RELATION_TYPE,
      'AFFECTS>BELONGS_TO_LOT>GOVERNED_BY' AS PATH,
      G.SOURCE_TYPE AS SOURCE_TYPE,
      G.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID,
      B.LOT_SOURCE AS LOT_SOURCE,
      B.LOT_SOURCE_REFERENCE_ID AS LOT_SOURCE_REFERENCE_ID
    )
  )
  UNION ALL
  SELECT *
  FROM GRAPH_TABLE (
    LSF_FESTIVAL_OPS_GRAPH
    MATCH
      (I IS INCIDENT)
        -[C IS CREATES]->
      (T IS TASK)
        -[U IS USES_PROCEDURE]->
      (D IS DOCUMENT)
    WHERE I.INCIDENT_ID = 'INC-2026-081'
      AND T.STATUS IN ('OPEN', 'IN_PROGRESS')
    COLUMNS (
      D.DOCUMENT_ID AS DOCUMENT_ID,
      D.FILE_NAME AS FILE_NAME,
      D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
      D.VERSION AS DOCUMENT_VERSION,
      D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
      I.INCIDENT_ID AS INCIDENT_ID,
      CAST(NULL AS VARCHAR2(40)) AS EQUIPMENT_ID,
      CAST(NULL AS VARCHAR2(40)) AS MANUFACTURING_LOT,
      T.TASK_ID AS TASK_ID,
      T.CREATED_BY_AGENT AS TASK_CREATED_BY_AGENT,
      'TASK_PROCEDURE' AS RELATION_TYPE,
      'CREATES>USES_PROCEDURE' AS PATH,
      U.SOURCE_TYPE AS SOURCE_TYPE,
      U.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID,
      CAST(NULL AS VARCHAR2(30)) AS LOT_SOURCE,
      CAST(NULL AS VARCHAR2(200)) AS LOT_SOURCE_REFERENCE_ID
    )
  )
)
SELECT *
FROM RELATED_DOCUMENTS
ORDER BY DOCUMENT_ID, RELATION_TYPE, TASK_ID;
SQL
DOCUMENT_ID    FILE_NAME                             DOCUMENT_TYPE       DOCUMENT_VERSION    EFFECTIVE_DATE    INCIDENT_ID     EQUIPMENT_ID    MANUFACTURING_LOT       TASK_ID TASK_CREATED_BY_AGENT    RELATION_TYPE          PATH                                  SOURCE_TYPE       SOURCE_REFERENCE_ID           LOT_SOURCE    LOT_SOURCE_REFERENCE_ID
______________ _____________________________________ ___________________ ___________________ _________________ _______________ _______________ ____________________ __________ ________________________ ______________________ _____________________________________ _________________ _____________________________ _____________ ____________________________________
IR-2026-081    IR-2026-081_Waveform_Arena遅延報告.pdf    Incident Report     1.0                 2026-08-10        INC-2026-081                                                                             INCIDENT_REPORT        DOCUMENTED_BY                         INCIDENT_CSV      incidents.csv#INC-2026-081
OPS-NET-2.1    LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf     Runbook             2.1                 2026-07-20        INC-2026-081    EQ-NET-WA-01    NW-2603                                                  OPERATING_PROCEDURE    AFFECTS>BELONGS_TO_LOT>GOVERNED_BY    SYNTHETIC_DEMO    BLOG-DEMO-DOC-001             MASTER_CSV    equipment_master.csv#EQ-NET-WA-01
OPS-NET-2.1    LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf     Runbook             2.1                 2026-07-20        INC-2026-081                                                121 ONTOLOGY_DEMO_SEED       TASK_PROCEDURE         CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
OPS-NET-2.1    LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf     Runbook             2.1                 2026-07-20        INC-2026-081                                                122 ONTOLOGY_DEMO_SEED       TASK_PROCEDURE         CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
SB-NW-2603     SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf      Service Bulletin    1.1                 2026-07-12        INC-2026-081    EQ-NET-WA-01    NW-2603                                                  SERVICE_BULLETIN       AFFECTS>BELONGS_TO_LOT>GOVERNED_BY    SYNTHETIC_DEMO    BLOG-DEMO-DOC-001             MASTER_CSV    equipment_master.csv#EQ-NET-WA-01
SB-NW-2603     SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf      Service Bulletin    1.1                 2026-07-12        INC-2026-081                                                121 ONTOLOGY_DEMO_SEED       TASK_PROCEDURE         CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
SB-NW-2603     SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf      Service Bulletin    1.1                 2026-07-12        INC-2026-081                                                122 ONTOLOGY_DEMO_SEED       TASK_PROCEDURE         CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001

7 rows selected.

このQueryは、同じDocumentへ別のTaskから到達した経路も残すためUNION ALLを使用し、最後にDISTINCTでまとめません。初期デモ状態では、Incident Reportの1経路、Lot経由の2経路、2 Taskそれぞれからの2経路、計7経路・3 Documentが返る想定です。

PATHは関係名の順序、各ID列はその経路の実体です。経由しない概念の列はNULLです。例えばCREATES>USES_PROCEDUREにはEquipmentが含まれないので、対象機材は同じTASK_IDを持つGET_OPEN_TASKSの結果で照合します。文書検索の候補だけを作る段階でDOCUMENT_IDとDOCUMENT_VERSIONを重複排除し、説明用の7経路は別に残します。

SOURCE_TYPEとSOURCE_REFERENCE_IDは文書へ到達する最後のRelationの出所です。Lot経由ではさらに、機材のLot所属を裏付けるLOT_SOURCEとLOT_SOURCE_REFERENCE_IDも返します。incidents.csv#<ID>とequipment_master.csv#<ID>は、本記事が元CSVのレコードを示すために生成する参照表記です。PDFのページ番号や外部URLではありません。

主要なDocumentは次です。

Document ID 種別 Relation 用途
IR-2026-081 Incident Report INCIDENT_REPORT 発生事実、直接原因、再発防止
SB-NW-2603 Service Bulletin SERVICE_BULLETIN 対象Lotの既知問題、交換条件
OPS-NET-2.1 Runbook OPERATING_PROCEDURE 監視閾値、フェイルオーバー手順

● PAF用のGraph APIを作成

本稿では探索経路を固定したread-only Procedureを作成し、後続の単体検証を通した3 ProcedureをPAFへ公開します。

入力はIncident ID、出力はJSON形式のCLOBです。

・ Graph API所有者へDirectory権限を直接付与

今回のExternal Tableは、DEFAULT DIRECTORYにDATA_PUMP_DIRを使用しています。これはDatabase内のDirectory Objectです。Parquetの配置先は引き続きObject Storageであり、Macのディレクトリを指定するものではありません。

Graph APIはAUTHID DEFINERで作成するため、参照先Objectの権限を所有者ADB_USERへ直接付与します。ユーザーがロール経由で持つ権限だけでは、Package内のSQLを実行できない場合があります。Definer’s Rightsの権限

1) ADMINでDirectoryと直接付与された権限を確認

同じDatabaseへADMINで接続します。

SQL
SHOW USER

SELECT DIRECTORY_NAME
FROM DBA_DIRECTORIES
WHERE DIRECTORY_NAME = 'DATA_PUMP_DIR';

SELECT GRANTEE, PRIVILEGE
FROM DBA_TAB_PRIVS
WHERE TABLE_NAME = 'DATA_PUMP_DIR'
  AND TYPE = 'DIRECTORY'
  AND GRANTEE = 'ADB_USER'
ORDER BY PRIVILEGE;
検証時のORA-06564と直接権限の確認結果

検証時はDirectoryが存在していましたが、ADB_USERへの直接付与はno rows selectedでした。Packageはコンパイルできても、API実行時のIncident存在確認で次のエラーになりました。

権限付与前のエラー
ORA-06564: Object DATA_PUMP_DIR does not exist or is not accessible to the user.

2) ADMINでREAD・WRITEを直接付与

DATA_PUMP_DIRが存在し、必要な直接権限が不足している場合に実行します。

SQL
GRANT READ, WRITE ON DIRECTORY DATA_PUMP_DIR TO ADB_USER;
SQL実行結果
Grant succeeded.

確認SQLを再実行すると、次の2行が返ります。

SQL実行結果
GRANTEE     PRIVILEGE
___________ ____________
ADB_USER    READ
ADB_USER    WRITE

今回の修正では、Directory権限はGraph API所有者のADB_USERに付与しました。Object Storage上のファイルやGraphの再作成は不要でした。DirectoryへのGRANT例

3) ADB_USERへ接続し直す

以降のPackage作成とOwnerでのAPI確認は、ADB_USERで実行します。

SQL
SHOW USER
SQL実行結果
USER is "ADB_USER"

・ Package Specificationを作成

SQL
CREATE OR REPLACE PACKAGE LSF_KG_API AUTHID DEFINER AS
  PROCEDURE GET_INCIDENT_IMPACT (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  );

  PROCEDURE GET_OPEN_TASKS (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  );

  PROCEDURE GET_RELATED_DOCUMENTS (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  );
END LSF_KG_API;
/
SQL実行結果
Package LSF_KG_API compiled

・ Package Bodyを作成

以下のPackage Bodyは修正済みです。Incident IDはGRAPH_TABLEの外側で絞り込みます。

検証時のORA-49028と修正理由

PL/SQL引数P_INCIDENT_IDをGRAPH_TABLE内部のWHEREから参照すると、検証環境では次のコンパイルエラーになりました。

修正前のエラー
ORA-49028: The PL/SQL variable "P_INCIDENT_ID" is referenced inside a GRAPH_TABLE operator.

以下は修正済みのSQLです。COLUMNSでIncident IDを取り出し、GRAPH_TABLEの外側でWHERE G.INCIDENT_ID = P_INCIDENT_IDと絞り込みます。Document検索は3経路をUNION ALLした結果の外側で同じ条件を適用します。機材IDの不一致条件とTaskの状態条件はGraph内部に残します。

これにより、静的SQLと入力値の検証を維持したまま、3つのProcedureをコンパイル・実行できます。先のSQLcl/SQL*Plusで使用した:P_INCIDENT_IDというバインド変数と、PackageのPL/SQL引数は区別します。

Package Bodyの作成SQL全文
CREATE OR REPLACE PACKAGE BODY LSF_KG_API AS
  PROCEDURE ASSERT_INCIDENT_ID (P_INCIDENT_ID IN VARCHAR2) AS
    L_COUNT PLS_INTEGER;
  BEGIN
    IF P_INCIDENT_ID IS NULL
       OR NOT REGEXP_LIKE(P_INCIDENT_ID, '^INC-[0-9A-Z-]{1,24}$') THEN
      RAISE_APPLICATION_ERROR(-20011, 'Invalid Incident ID');
    END IF;
    SELECT COUNT(*)
      INTO L_COUNT
      FROM V_KG_INCIDENTS
     WHERE INCIDENT_ID = P_INCIDENT_ID;
    IF L_COUNT = 0 THEN
      RAISE_APPLICATION_ERROR(-20012, 'Incident ID was not found');
    ELSIF L_COUNT > 1 THEN
      RAISE_APPLICATION_ERROR(-20013, 'Duplicate Incident ID');
    END IF;
  END ASSERT_INCIDENT_ID;
  PROCEDURE GET_INCIDENT_IMPACT (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  ) AS
  BEGIN
    ASSERT_INCIDENT_ID(P_INCIDENT_ID);
    SELECT JSON_ARRAYAGG(
             JSON_OBJECT(
               'incident_id'          VALUE INCIDENT_ID,
               'affected_equipment_id' VALUE AFFECTED_EQUIPMENT_ID,
               'manufacturing_lot'     VALUE MANUFACTURING_LOT,
               'related_equipment_id'  VALUE RELATED_EQUIPMENT_ID,
               'related_stage_id'      VALUE RELATED_STAGE_ID,
               'related_stage_name'    VALUE RELATED_STAGE_NAME,
               'lot_source'            VALUE LOT_SOURCE,
               'lot_assertion_status'  VALUE LOT_ASSERTION_STATUS,
               'lot_source_reference_id' VALUE LOT_SOURCE_REFERENCE_ID,
               'affected_lot_source' VALUE AFFECTED_LOT_SOURCE,
               'affected_lot_assertion_status' VALUE AFFECTED_LOT_ASSERTION_STATUS,
               'affected_lot_source_reference_id' VALUE AFFECTED_LOT_SOURCE_REFERENCE_ID
             )
             ORDER BY RELATED_STAGE_ID, RELATED_EQUIPMENT_ID
             RETURNING CLOB
           )
      INTO P_RESULT
      FROM GRAPH_TABLE (
        LSF_FESTIVAL_OPS_GRAPH
        MATCH
          (I IS INCIDENT)
            -[A IS AFFECTS]->
          (E1 IS EQUIPMENT)
            -[B1 IS BELONGS_TO_LOT]->
          (L IS MANUFACTURING_LOT)
            <-[B2 IS BELONGS_TO_LOT]-
          (E2 IS EQUIPMENT)
            -[D IS DEPLOYED_AT]->
          (S IS STAGE)
        WHERE E1.EQUIPMENT_ID <> E2.EQUIPMENT_ID
        COLUMNS (
          I.INCIDENT_ID       AS INCIDENT_ID,
          E1.EQUIPMENT_ID     AS AFFECTED_EQUIPMENT_ID,
          L.MANUFACTURING_LOT AS MANUFACTURING_LOT,
          E2.EQUIPMENT_ID     AS RELATED_EQUIPMENT_ID,
          S.STAGE_ID          AS RELATED_STAGE_ID,
          S.STAGE_NAME        AS RELATED_STAGE_NAME,
          B2.LOT_SOURCE       AS LOT_SOURCE,
          B2.LOT_ASSERTION_STATUS AS LOT_ASSERTION_STATUS,
          B2.LOT_SOURCE_REFERENCE_ID AS LOT_SOURCE_REFERENCE_ID,
          B1.LOT_SOURCE AS AFFECTED_LOT_SOURCE,
          B1.LOT_ASSERTION_STATUS AS AFFECTED_LOT_ASSERTION_STATUS,
          B1.LOT_SOURCE_REFERENCE_ID AS AFFECTED_LOT_SOURCE_REFERENCE_ID
        )
      ) G
     WHERE G.INCIDENT_ID = P_INCIDENT_ID;
    IF P_RESULT IS NULL THEN
      P_RESULT := TO_CLOB('[]');
    END IF;
  END GET_INCIDENT_IMPACT;
  PROCEDURE GET_OPEN_TASKS (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  ) AS
  BEGIN
    ASSERT_INCIDENT_ID(P_INCIDENT_ID);
    SELECT JSON_ARRAYAGG(
             JSON_OBJECT(
               'incident_id' VALUE INCIDENT_ID,
               'task_id'     VALUE TASK_ID,
               'task_code'   VALUE TASK_CODE,
               'priority'    VALUE PRIORITY,
               'status'      VALUE TASK_STATUS,
               'equipment_id' VALUE EQUIPMENT_ID,
               'stage_id'     VALUE STAGE_ID,
               'stage_name'   VALUE STAGE_NAME,
               'created_by_agent' VALUE CREATED_BY_AGENT,
               'source_type' VALUE SOURCE_TYPE,
               'source_reference_id' VALUE SOURCE_REFERENCE_ID
             )
             ORDER BY
               CASE PRIORITY
                 WHEN 'CRITICAL' THEN 1
                 WHEN 'HIGH'     THEN 2
                 WHEN 'MEDIUM'   THEN 3
                 WHEN 'LOW'      THEN 4
                 ELSE 5
               END,
               TASK_ID
             RETURNING CLOB
           )
      INTO P_RESULT
      FROM GRAPH_TABLE (
        LSF_FESTIVAL_OPS_GRAPH
        MATCH
          (I IS INCIDENT)
            -[C IS CREATES]->
          (T IS TASK)
            -[X IS TARGETS]->
          (E IS EQUIPMENT)
            -[D IS DEPLOYED_AT]->
          (S IS STAGE)
        WHERE T.STATUS IN ('OPEN', 'IN_PROGRESS')
        COLUMNS (
          I.INCIDENT_ID  AS INCIDENT_ID,
          T.TASK_ID      AS TASK_ID,
          T.TASK_CODE    AS TASK_CODE,
          T.PRIORITY     AS PRIORITY,
          T.STATUS       AS TASK_STATUS,
          E.EQUIPMENT_ID AS EQUIPMENT_ID,
          S.STAGE_ID     AS STAGE_ID,
          S.STAGE_NAME   AS STAGE_NAME,
          T.CREATED_BY_AGENT AS CREATED_BY_AGENT,
          X.SOURCE_TYPE AS SOURCE_TYPE,
          X.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID
        )
      ) G
     WHERE G.INCIDENT_ID = P_INCIDENT_ID;
    IF P_RESULT IS NULL THEN
      P_RESULT := TO_CLOB('[]');
    END IF;
  END GET_OPEN_TASKS;
  PROCEDURE GET_RELATED_DOCUMENTS (
    P_INCIDENT_ID IN  VARCHAR2,
    P_RESULT      OUT CLOB
  ) AS
  BEGIN
    ASSERT_INCIDENT_ID(P_INCIDENT_ID);
    SELECT JSON_ARRAYAGG(
             JSON_OBJECT(
               'document_id' VALUE DOCUMENT_ID,
               'file_name' VALUE FILE_NAME,
               'document_type' VALUE DOCUMENT_TYPE,
               'document_version' VALUE DOCUMENT_VERSION,
               'effective_date' VALUE EFFECTIVE_DATE,
               'incident_id' VALUE INCIDENT_ID,
               'equipment_id' VALUE EQUIPMENT_ID,
               'manufacturing_lot' VALUE MANUFACTURING_LOT,
               'task_id' VALUE TASK_ID,
               'task_created_by_agent' VALUE TASK_CREATED_BY_AGENT,
               'relation_type' VALUE RELATION_TYPE,
               'path' VALUE PATH,
               'source_type' VALUE SOURCE_TYPE,
               'source_reference_id' VALUE SOURCE_REFERENCE_ID,
               'lot_source' VALUE LOT_SOURCE,
               'lot_source_reference_id' VALUE LOT_SOURCE_REFERENCE_ID
               NULL ON NULL
               RETURNING CLOB
             )
             ORDER BY DOCUMENT_ID, RELATION_TYPE, TASK_ID
             RETURNING CLOB
           )
      INTO P_RESULT
      FROM (
        SELECT *
        FROM GRAPH_TABLE (
          LSF_FESTIVAL_OPS_GRAPH
          MATCH
            (I IS INCIDENT)
              -[R IS DOCUMENTED_BY]->
            (D IS DOCUMENT)
          COLUMNS (
            D.DOCUMENT_ID AS DOCUMENT_ID,
            D.FILE_NAME AS FILE_NAME,
            D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
            D.VERSION AS DOCUMENT_VERSION,
            D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
            I.INCIDENT_ID AS INCIDENT_ID,
            CAST(NULL AS VARCHAR2(40)) AS EQUIPMENT_ID,
            CAST(NULL AS VARCHAR2(40)) AS MANUFACTURING_LOT,
            CAST(NULL AS NUMBER) AS TASK_ID,
            CAST(NULL AS VARCHAR2(200)) AS TASK_CREATED_BY_AGENT,
            'INCIDENT_REPORT' AS RELATION_TYPE,
            'DOCUMENTED_BY' AS PATH,
            'INCIDENT_CSV' AS SOURCE_TYPE,
            'incidents.csv#' || I.INCIDENT_ID AS SOURCE_REFERENCE_ID,
            CAST(NULL AS VARCHAR2(30)) AS LOT_SOURCE,
            CAST(NULL AS VARCHAR2(200)) AS LOT_SOURCE_REFERENCE_ID
          )
        )
        UNION ALL
        SELECT *
        FROM GRAPH_TABLE (
          LSF_FESTIVAL_OPS_GRAPH
          MATCH
            (I IS INCIDENT)
              -[A IS AFFECTS]->
            (E IS EQUIPMENT)
              -[B IS BELONGS_TO_LOT]->
            (L IS MANUFACTURING_LOT)
              -[G IS GOVERNED_BY]->
            (D IS DOCUMENT)
          COLUMNS (
            D.DOCUMENT_ID AS DOCUMENT_ID,
            D.FILE_NAME AS FILE_NAME,
            D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
            D.VERSION AS DOCUMENT_VERSION,
            D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
            I.INCIDENT_ID AS INCIDENT_ID,
            E.EQUIPMENT_ID AS EQUIPMENT_ID,
            L.MANUFACTURING_LOT AS MANUFACTURING_LOT,
            CAST(NULL AS NUMBER) AS TASK_ID,
            CAST(NULL AS VARCHAR2(200)) AS TASK_CREATED_BY_AGENT,
            G.RELATION_TYPE AS RELATION_TYPE,
            'AFFECTS>BELONGS_TO_LOT>GOVERNED_BY' AS PATH,
            G.SOURCE_TYPE AS SOURCE_TYPE,
            G.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID,
            B.LOT_SOURCE AS LOT_SOURCE,
            B.LOT_SOURCE_REFERENCE_ID AS LOT_SOURCE_REFERENCE_ID
          )
        )
        UNION ALL
        SELECT *
        FROM GRAPH_TABLE (
          LSF_FESTIVAL_OPS_GRAPH
          MATCH
            (I IS INCIDENT)
              -[C IS CREATES]->
            (T IS TASK)
              -[U IS USES_PROCEDURE]->
            (D IS DOCUMENT)
          WHERE T.STATUS IN ('OPEN', 'IN_PROGRESS')
          COLUMNS (
            D.DOCUMENT_ID AS DOCUMENT_ID,
            D.FILE_NAME AS FILE_NAME,
            D.DOCUMENT_TYPE AS DOCUMENT_TYPE,
            D.VERSION AS DOCUMENT_VERSION,
            D.EFFECTIVE_DATE AS EFFECTIVE_DATE,
            I.INCIDENT_ID AS INCIDENT_ID,
            CAST(NULL AS VARCHAR2(40)) AS EQUIPMENT_ID,
            CAST(NULL AS VARCHAR2(40)) AS MANUFACTURING_LOT,
            T.TASK_ID AS TASK_ID,
            T.CREATED_BY_AGENT AS TASK_CREATED_BY_AGENT,
            'TASK_PROCEDURE' AS RELATION_TYPE,
            'CREATES>USES_PROCEDURE' AS PATH,
            U.SOURCE_TYPE AS SOURCE_TYPE,
            U.SOURCE_REFERENCE_ID AS SOURCE_REFERENCE_ID,
            CAST(NULL AS VARCHAR2(30)) AS LOT_SOURCE,
            CAST(NULL AS VARCHAR2(200)) AS LOT_SOURCE_REFERENCE_ID
          )
        )
      ) G
     WHERE G.INCIDENT_ID = P_INCIDENT_ID;

    IF P_RESULT IS NULL THEN
      P_RESULT := TO_CLOB('[]');
    END IF;
  END GET_RELATED_DOCUMENTS;
END LSF_KG_API;
/
SQL実行結果
Package Body LSF_KG_API compiled

・ Ownerでコンパイル状態とAPIの基本動作を確認

1) Packageの状態を確認

ADB_USERで実行します。

SQL
SHOW ERRORS PACKAGE BODY LSF_KG_API

SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM USER_OBJECTS
WHERE OBJECT_NAME = 'LSF_KG_API'
ORDER BY OBJECT_TYPE;
SQL実行結果
No errors.

OBJECT_NAME    OBJECT_TYPE     STATUS
______________ _______________ _________
LSF_KG_API     PACKAGE         VALID
LSF_KG_API     PACKAGE BODY    VALID

2) 3つのAPIのJSONと件数を確認

各APIを実行し、返却CLOBをJSON_ARRAY_Tで解析して配列の要素数を表示します。

SQL
SET SERVEROUTPUT ON
DECLARE
  L_RESULT CLOB;
  L_ROWS JSON_ARRAY_T;
BEGIN
  LSF_KG_API.GET_INCIDENT_IMPACT('INC-2026-081', L_RESULT);
  L_ROWS := JSON_ARRAY_T.PARSE(L_RESULT);
  DBMS_OUTPUT.PUT_LINE('IMPACT_COUNT=' || L_ROWS.GET_SIZE || ' (expected 2)');
  LSF_KG_API.GET_OPEN_TASKS('INC-2026-081', L_RESULT);
  L_ROWS := JSON_ARRAY_T.PARSE(L_RESULT);
  DBMS_OUTPUT.PUT_LINE('OPEN_TASK_COUNT=' || L_ROWS.GET_SIZE || ' (expected 2)');
  LSF_KG_API.GET_RELATED_DOCUMENTS('INC-2026-081', L_RESULT);
  L_ROWS := JSON_ARRAY_T.PARSE(L_RESULT);
  DBMS_OUTPUT.PUT_LINE('DOCUMENT_PATH_COUNT=' || L_ROWS.GET_SIZE || ' (expected 7)');
END;
/
SQL実行結果
IMPACT_COUNT=2 (expected 2)
OPEN_TASK_COUNT=2 (expected 2)
DOCUMENT_PATH_COUNT=7 (expected 7)

PL/SQL procedure successfully completed.

2026-10-03の実行では、3 APIすべてでJSON配列の解析に成功し、期待する件数と一致しました。Documentの7件は文書へ到達する経路の件数です。同じDocumentへ複数の経路で到達するため、7種類の文書という意味ではありません。

この確認はOwnerでの基本動作確認です。後述のPostconditionでは、Distinct Document IDや経路別の内容も検査します。続いてRuntime Userの権限設定・実行確認へ進みます。

・ Runtime Userへ権限を付与

LSF_KG_APIのPublic Specificationは、上記の3つのread-only Procedureだけです。PAFからは、Owner Schema名を含むQualified Nameでこの3 Procedureだけを実行します。

Runtime UserへProperty Graph自体の自由な参照権限は付与しません。

1) ADB_USERログイン確認

SQL
SQL> show user
USER is "ADB_USER"

2) 権限付与

GRANT EXECUTE ON LSF_KG_API TO LSF_AGENT;

GRANT SELECT ON V_KG_INCIDENTS TO LSF_AGENT;
GRANT SELECT ON V_KG_EQUIPMENT TO LSF_AGENT;
GRANT SELECT ON V_KG_TASKS TO LSF_AGENT;
GRANT SELECT ON EXT_EQUIPMENT_MAINTENANCE TO LSF_AGENT;

PackageはAUTHID DEFINERのため、Graph検索はOwner Schemaの権限で実行されます。

PAFのPackage Procedure対応と、接続Userから別SchemaのRoutineが画面上で発見できることは別に確認します。前回記事では、LSF_AGENTからOwner SchemaのProcedureがRoutine一覧に表示されず、実行User側のWrapperで接続した例があります。

まずLSF_AGENT接続で次を実行し、Database権限とJSON出力を確認します。次にPAFのRoutine一覧で3 Procedureを選択できるか確認します。

3) LSF_AGENTログイン確認

SQL
SQL> show user
USER is "LSF_AGENT"

4) 変数設定

SQL
VAR KG_RESULT CLOB
EXEC ADB_USER.LSF_KG_API.GET_INCIDENT_IMPACT('INC-2026-081', :KG_RESULT);
SQL実行結果
PL/SQL procedure successfully completed.

5) Database権限とJSON出力を確認

SQL
PRINT KG_RESULT
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_RESULT
--------------------------------------------------------------------------------
[{"incident_id":"INC-2026-081","affected_equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","related_equipment_id":"EQ-NET-NG-01","related_stage_id":"STG-NG","related_stage_name":"Neon Groove Stage","lot_source":"SYNTHETIC_DEMO","lot_assertion_status":"SYNTHETIC","lot_source_reference_id":"BLOG-DEMO-LOT-001","affected_lot_source":"MASTER_CSV","affected_lot_assertion_status":"SOURCE_RECORD","affected_lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"},{"incident_id":"INC-2026-081","affected_equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","related_equipment_id":"EQ-NET-OM-01","related_stage_id":"STG-OM","related_stage_name":"Orbit Main Stage","lot_source":"SYNTHETIC_DEMO","lot_assertion_status":"SYNTHETIC","lot_source_reference_id":"BLOG-DEMO-LOT-001","affected_lot_source":"MASTER_CSV","affected_lot_assertion_status":"SOURCE_RECORD","affected_lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"}]

一覧に見える場合は、Owner側の3 Procedureを直接選択します。見えない場合は、次のread-only Wrapperを追加します。これはPAFでの発見経路を揃えるための分岐で、既存のTask書き込みWrapperは今回の許可Routineへ追加しません。

管理者がLSF_AGENTへCREATE PROCEDUREを一時付与します。もともと付与済みかを管理者側で記録し、今回追加した場合だけ最後に取り消します。

SQL
-- ADMINなど、権限を管理するUserで確認する。
SELECT GRANTEE, PRIVILEGE
FROM DBA_SYS_PRIVS
WHERE GRANTEE = 'LSF_AGENT'
  AND PRIVILEGE = 'CREATE PROCEDURE';
SQL実行結果
no rows selected
SQL
-- 未付与の場合だけ実行する。
GRANT CREATE PROCEDURE TO LSF_AGENT;
SQL実行結果
Grant succeeded.

LSF_AGENTで接続し、3 Procedureを作成します。ADB_USERは実際のOwner名へ置き換えます。

1) 3 Procedure作成

CREATE OR REPLACE PROCEDURE LSF_GET_INCIDENT_IMPACT (
  P_INCIDENT_ID IN VARCHAR2,
  P_RESULT OUT CLOB
) AUTHID DEFINER AS
BEGIN
  ADB_USER.LSF_KG_API.GET_INCIDENT_IMPACT(P_INCIDENT_ID, P_RESULT);
END;
/

CREATE OR REPLACE PROCEDURE LSF_GET_OPEN_TASKS (
  P_INCIDENT_ID IN VARCHAR2,
  P_RESULT OUT CLOB
) AUTHID DEFINER AS
BEGIN
  ADB_USER.LSF_KG_API.GET_OPEN_TASKS(P_INCIDENT_ID, P_RESULT);
END;
/

CREATE OR REPLACE PROCEDURE LSF_GET_RELATED_DOCUMENTS (
  P_INCIDENT_ID IN VARCHAR2,
  P_RESULT OUT CLOB
) AUTHID DEFINER AS
BEGIN
  ADB_USER.LSF_KG_API.GET_RELATED_DOCUMENTS(P_INCIDENT_ID, P_RESULT);
END;
/

2) 3 Procedure作成確認

SQL
SELECT OBJECT_NAME, STATUS
FROM USER_OBJECTS
WHERE OBJECT_NAME IN (
  'LSF_GET_INCIDENT_IMPACT', 'LSF_GET_OPEN_TASKS', 'LSF_GET_RELATED_DOCUMENTS'
)
ORDER BY OBJECT_NAME;
SQL実行結果
OBJECT_NAME                  STATUS
____________________________ _________
LSF_GET_INCIDENT_IMPACT      VALID
LSF_GET_OPEN_TASKS           VALID
LSF_GET_RELATED_DOCUMENTS    VALID

3) KG_RESULT Procedure作成確認

SQL
VAR KG_RESULT CLOB
EXEC LSF_GET_INCIDENT_IMPACT('INC-2026-081', :KG_RESULT);
SQL実行結果
PL/SQL procedure successfully completed.
SQL
PRINT KG_RESULT
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_RESULT
--------------------------------------------------------------------------------
[{"incident_id":"INC-2026-081","affected_equipment_id":"EQ-NET-WA-01","manufactu

4) KG_RESULT Procedure作成確認

SQL
EXEC LSF_GET_OPEN_TASKS('INC-2026-081', :KG_RESULT);
SQL実行結果
PL/SQL procedure successfully completed.
SQL
PRINT KG_RESULT
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_RESULT
--------------------------------------------------------------------------------
[{"incident_id":"INC-2026-081","task_id":121,"task_code":"TASK-121","priority":"

5) KG_RESULT Procedure作成確認

SQL
EXEC LSF_GET_RELATED_DOCUMENTS('INC-2026-081', :KG_RESULT);
SQL実行結果
PL/SQL procedure successfully completed.
SQL
PRINT KG_RESULT
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_RESULT
--------------------------------------------------------------------------------
[{"document_id":"IR-2026-081","file_name":"IR-2026-081_Waveform_Arena遅延報告.pdf","

3件がVALIDであることと、OwnerのAPIと同じ結果になることを確認します。今回一時付与した場合だけ、管理者で次を実行します。EXECUTE ON ADB_USER.LSF_KG_APIはWrapper実行に必要なので保持します。

REVOKE CREATE PROCEDURE FROM LSF_AGENT;

PAF側でDatabase Routineを再取得し、Wrapperを選択します。以降のAgent InstructionsのRoutine名も同じ対応に置き換えます。

Owner側の直接呼出 Wrapperを使う場合
ADB_USER.LSF_KG_API.GET_INCIDENT_IMPACT LSF_AGENT.LSF_GET_INCIDENT_IMPACT
ADB_USER.LSF_KG_API.GET_OPEN_TASKS LSF_AGENT.LSF_GET_OPEN_TASKS
ADB_USER.LSF_KG_API.GET_RELATED_DOCUMENTS LSF_AGENT.LSF_GET_RELATED_DOCUMENTS

今回のPAF実機では、LSF2026 AGENT RUNTIME(LSF_AGENT)接続で上記3 Wrapperを選択しました。後続のGraph単独質問と複合質問で、同一Lotの別機材、未完了Task、関連Documentが回答に含まれることを確認しています。

● Graph APIを単体確認

PAFへ接続する前に、Databaseで3 Procedureを単体確認します。ここからはOwner Schema(ADB_USER)へ接続し直して実行します。

確認する結果: 同一Lotの別機材2件、未完了Task 2件、文書への経路7件(文書は3種類)。JSONの全文は折りたたみへ収め、後続の表とPostconditionでID・経路・出所を照合します。

SQL
SET SERVEROUTPUT ON

DECLARE
  L_RESULT CLOB;
BEGIN
  LSF_KG_API.GET_INCIDENT_IMPACT(
    P_INCIDENT_ID => 'INC-2026-081',
    P_RESULT      => L_RESULT
  );
  DBMS_OUTPUT.PUT_LINE(DBMS_LOB.SUBSTR(L_RESULT, 32767, 1));
END;
/
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
[{"incident_id":"INC-2026-081","affected_equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","related_equipment_id":"EQ-NET-NG-01","related_stage_id":"STG-NG","related_stage_name":"Neon Groove Stage","lot_source":"SYNTHETIC_DEMO","lot_assertion_status":"SYNTHETIC","lot_source_reference_id":"BLOG-DEMO-LOT-001","affected_lot_source":"MASTER_CSV","affected_lot_assertion_status":"SOURCE_RECORD","affected_lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"},{"incident_id":"INC-2026-081","affected_equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","related_equipment_id":"EQ-NET-OM-01","related_stage_id":"STG-OM","related_stage_name":"Orbit Main Stage","lot_source":"SYNTHETIC_DEMO","lot_assertion_status":"SYNTHETIC","lot_source_reference_id":"BLOG-DEMO-LOT-001","affected_lot_source":"MASTER_CSV","affected_lot_assertion_status":"SOURCE_RECORD","affected_lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"}]


PL/SQL procedure successfully completed.

上記の合成デモデータを反映した場合の期待出力例は次です。

JSON出力の全文(対象ID・経路・出所の詳細)
[
  {
    "incident_id": "INC-2026-081",
    "affected_equipment_id": "EQ-NET-WA-01",
    "manufacturing_lot": "NW-2603",
    "related_equipment_id": "EQ-NET-NG-01",
    "related_stage_id": "STG-NG",
    "related_stage_name": "Neon Groove Stage",
    "lot_source": "SYNTHETIC_DEMO",
    "lot_assertion_status": "SYNTHETIC",
    "lot_source_reference_id": "BLOG-DEMO-LOT-001",
    "affected_lot_source": "MASTER_CSV",
    "affected_lot_assertion_status": "SOURCE_RECORD",
    "affected_lot_source_reference_id": "equipment_master.csv#EQ-NET-WA-01"
  },
  {
    "incident_id": "INC-2026-081",
    "affected_equipment_id": "EQ-NET-WA-01",
    "manufacturing_lot": "NW-2603",
    "related_equipment_id": "EQ-NET-OM-01",
    "related_stage_id": "STG-OM",
    "related_stage_name": "Orbit Main Stage",
    "lot_source": "SYNTHETIC_DEMO",
    "lot_assertion_status": "SYNTHETIC",
    "lot_source_reference_id": "BLOG-DEMO-LOT-001",
    "affected_lot_source": "MASTER_CSV",
    "affected_lot_assertion_status": "SOURCE_RECORD",
    "affected_lot_source_reference_id": "equipment_master.csv#EQ-NET-WA-01"
  }
]

続いてTaskとDocumentのJSONを確認します。以下もOwner Schemaで実行します。

SQL
SET LONG 100000
SET LONGCHUNKSIZE 100000
VAR KG_TASKS CLOB
VAR KG_DOCUMENTS CLOB
EXEC LSF_KG_API.GET_OPEN_TASKS('INC-2026-081', :KG_TASKS);
SQL実行結果
PL/SQL procedure successfully completed.
SQL実行
PRINT KG_TASKS
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_TASKS
--------------------------------------------------------------------------------
[{"incident_id":"INC-2026-081","task_id":121,"task_code":"TASK-121","priority":"CRITICAL","status":"OPEN","equipment_id":"EQ-NET-OM-01","stage_id":"STG-OM","stage_name":"Orbit Main Stage","created_by_agent":"ONTOLOGY_DEMO_SEED","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001"},{"incident_id":"INC-2026-081","task_id":122,"task_code":"TASK-122","priority":"HIGH","status":"OPEN","equipment_id":"EQ-NET-NG-01","stage_id":"STG-NG","stage_name":"Neon Groove Stage","created_by_agent":"ONTOLOGY_DEMO_SEED","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001"}]
SQL実行
EXEC LSF_KG_API.GET_RELATED_DOCUMENTS('INC-2026-081', :KG_DOCUMENTS);
SQL実行結果
PL/SQL procedure successfully completed.
SQL実行
PRINT KG_DOCUMENTS
JSON出力の全文(対象ID・経路・出所の詳細)
SQL実行結果
KG_DOCUMENTS
--------------------------------------------------------------------------------
[{"document_id":"IR-2026-081","file_name":"IR-2026-081_Waveform_Arena遅延報告.pdf","document_type":"Incident Report","document_version":"1.0","effective_date":"2026-08-10","incident_id":"INC-2026-081","equipment_id":null,"manufacturing_lot":null,"task_id":null,"task_created_by_agent":null,"relation_type":"INCIDENT_REPORT","path":"DOCUMENTED_BY","source_type":"INCIDENT_CSV","source_reference_id":"incidents.csv#INC-2026-081","lot_source":null,"lot_source_reference_id":null},{"document_id":"OPS-NET-2.1","file_name":"LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf","document_type":"Runbook","document_version":"2.1","effective_date":"2026-07-20","incident_id":"INC-2026-081","equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","task_id":null,"task_created_by_agent":null,"relation_type":"OPERATING_PROCEDURE","path":"AFFECTS>BELONGS_TO_LOT>GOVERNED_BY","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-DOC-001","lot_source":"MASTER_CSV","lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"},{"document_id":"OPS-NET-2.1","file_name":"LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf","document_type":"Runbook","document_version":"2.1","effective_date":"2026-07-20","incident_id":"INC-2026-081","equipment_id":null,"manufacturing_lot":null,"task_id":121,"task_created_by_agent":"ONTOLOGY_DEMO_SEED","relation_type":"TASK_PROCEDURE","path":"CREATES>USES_PROCEDURE","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001","lot_source":null,"lot_source_reference_id":null},{"document_id":"OPS-NET-2.1","file_name":"LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf","document_type":"Runbook","document_version":"2.1","effective_date":"2026-07-20","incident_id":"INC-2026-081","equipment_id":null,"manufacturing_lot":null,"task_id":122,"task_created_by_agent":"ONTOLOGY_DEMO_SEED","relation_type":"TASK_PROCEDURE","path":"CREATES>USES_PROCEDURE","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001","lot_source":null,"lot_source_reference_id":null},{"document_id":"SB-NW-2603","file_name":"SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf","document_type":"Service Bulletin","document_version":"1.1","effective_date":"2026-07-12","incident_id":"INC-2026-081","equipment_id":"EQ-NET-WA-01","manufacturing_lot":"NW-2603","task_id":null,"task_created_by_agent":null,"relation_type":"SERVICE_BULLETIN","path":"AFFECTS>BELONGS_TO_LOT>GOVERNED_BY","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-DOC-001","lot_source":"MASTER_CSV","lot_source_reference_id":"equipment_master.csv#EQ-NET-WA-01"},{"document_id":"SB-NW-2603","file_name":"SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf","document_type":"Service Bulletin","document_version":"1.1","effective_date":"2026-07-12","incident_id":"INC-2026-081","equipment_id":null,"manufacturing_lot":null,"task_id":121,"task_created_by_agent":"ONTOLOGY_DEMO_SEED","relation_type":"TASK_PROCEDURE","path":"CREATES>USES_PROCEDURE","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001","lot_source":null,"lot_source_reference_id":null},{"document_id":"SB-NW-2603","file_name":"SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf","document_type":"Service Bulletin","document_version":"1.1","effective_date":"2026-07-12","incident_id":"INC-2026-081","equipment_id":null,"manufacturing_lot":null,"task_id":122,"task_created_by_agent":"ONTOLOGY_DEMO_SEED","relation_type":"TASK_PROCEDURE","path":"CREATES>USES_PROCEDURE","source_type":"SYNTHETIC_DEMO","source_reference_id":"BLOG-DEMO-TASK-001","lot_source":null,"lot_source_reference_id":null}]

SQL
SELECT DOCUMENT_ID, DOCUMENT_VERSION, EFFECTIVE_DATE,
       TASK_ID, EQUIPMENT_ID, MANUFACTURING_LOT,
       PATH_NAME, SOURCE_TYPE, SOURCE_REFERENCE_ID
FROM JSON_TABLE(
  :KG_DOCUMENTS, '$[*]' COLUMNS (
    DOCUMENT_ID VARCHAR2(40) PATH '$.document_id',
    DOCUMENT_VERSION VARCHAR2(20) PATH '$.document_version',
    EFFECTIVE_DATE VARCHAR2(10) PATH '$.effective_date',
    TASK_ID NUMBER PATH '$.task_id',
    EQUIPMENT_ID VARCHAR2(40) PATH '$.equipment_id',
    MANUFACTURING_LOT VARCHAR2(40) PATH '$.manufacturing_lot',
    PATH_NAME VARCHAR2(100) PATH '$.path',
    SOURCE_TYPE VARCHAR2(30) PATH '$.source_type',
    SOURCE_REFERENCE_ID VARCHAR2(200) PATH '$.source_reference_id'
  )
)
ORDER BY DOCUMENT_ID, PATH_NAME, TASK_ID;
SQL実行結果
DOCUMENT_ID    DOCUMENT_VERSION    EFFECTIVE_DATE       TASK_ID EQUIPMENT_ID    MANUFACTURING_LOT    PATH_NAME                             SOURCE_TYPE       SOURCE_REFERENCE_ID
______________ ___________________ _________________ __________ _______________ ____________________ _____________________________________ _________________ _____________________________
IR-2026-081    1.0                 2026-08-10                                                        DOCUMENTED_BY                         INCIDENT_CSV      incidents.csv#INC-2026-081
OPS-NET-2.1    2.1                 2026-07-20                   EQ-NET-WA-01    NW-2603              AFFECTS>BELONGS_TO_LOT>GOVERNED_BY    SYNTHETIC_DEMO    BLOG-DEMO-DOC-001
OPS-NET-2.1    2.1                 2026-07-20               121                                      CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
OPS-NET-2.1    2.1                 2026-07-20               122                                      CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
SB-NW-2603     1.1                 2026-07-12                   EQ-NET-WA-01    NW-2603              AFFECTS>BELONGS_TO_LOT>GOVERNED_BY    SYNTHETIC_DEMO    BLOG-DEMO-DOC-001
SB-NW-2603     1.1                 2026-07-12               121                                      CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001
SB-NW-2603     1.1                 2026-07-12               122                                      CREATES>USES_PROCEDURE                SYNTHETIC_DEMO    BLOG-DEMO-TASK-001

7 rows selected.

文書結果は7行の想定です。同じSB-NW-2603でも、Lotからの経路1行と各Taskからの経路2行を確認します。VersionとEffective DateはCatalogの値をそのまま返します。文書を選んだ理由は、PATHの関係名と経由IDを使い、Relationの出所と文書本文の引用箇所を区別して説明します。

Graph APIが空配列[]を返す場合、PAF側で推測によってRelationを補完してはいけません。

・ Postconditionをまとめて検査

次のBlockで、GraphとPackageがVALID、影響Equipmentが2件、未完了Taskが2件、Document経路が7件、Distinct Document IDが3件であることを検査します。JSON_ARRAY_T.PARSEにより、API出力が正当なJSON配列であることも同時に確認できます。

Postcondition検査SQL:件数・ID・経路を照合
SQL
DECLARE
  L_IMPACT       CLOB;
  L_TASKS        CLOB;
  L_DOCUMENTS    CLOB;
  L_INVALID      PLS_INTEGER;
  L_GRAPH_COUNT  PLS_INTEGER;
  L_PACKAGE_COUNT PLS_INTEGER;
  L_DISTINCT_DOC PLS_INTEGER;
  L_BAD_PATHS    PLS_INTEGER;
  L_IMPACT_JSON  JSON_ARRAY_T;
  L_TASKS_JSON   JSON_ARRAY_T;
  L_DOCS_JSON    JSON_ARRAY_T;
BEGIN
  SELECT COUNT(*)
    INTO L_INVALID
    FROM USER_OBJECTS
   WHERE OBJECT_NAME IN ('LSF_FESTIVAL_OPS_GRAPH', 'LSF_KG_API')
     AND STATUS <> 'VALID';

  IF L_INVALID > 0 THEN
    RAISE_APPLICATION_ERROR(-20031, 'Invalid graph or package object');
  END IF;

  SELECT COUNT(*)
    INTO L_GRAPH_COUNT
    FROM USER_PROPERTY_GRAPHS
   WHERE GRAPH_NAME = 'LSF_FESTIVAL_OPS_GRAPH';

  IF L_GRAPH_COUNT <> 1 THEN
    RAISE_APPLICATION_ERROR(-20032, 'Property graph was not found');
  END IF;

  SELECT COUNT(*) INTO L_PACKAGE_COUNT
  FROM USER_OBJECTS
  WHERE OBJECT_NAME = 'LSF_KG_API'
    AND OBJECT_TYPE IN ('PACKAGE', 'PACKAGE BODY')
    AND STATUS = 'VALID';
  IF L_PACKAGE_COUNT <> 2 THEN
    RAISE_APPLICATION_ERROR(-20038, 'Valid package and body were not found');
  END IF;

  LSF_KG_API.GET_INCIDENT_IMPACT('INC-2026-081', L_IMPACT);
  LSF_KG_API.GET_OPEN_TASKS('INC-2026-081', L_TASKS);
  LSF_KG_API.GET_RELATED_DOCUMENTS('INC-2026-081', L_DOCUMENTS);

  L_IMPACT_JSON := JSON_ARRAY_T.PARSE(L_IMPACT);
  L_TASKS_JSON  := JSON_ARRAY_T.PARSE(L_TASKS);
  L_DOCS_JSON   := JSON_ARRAY_T.PARSE(L_DOCUMENTS);

  IF L_IMPACT_JSON.GET_SIZE <> 2 THEN
    RAISE_APPLICATION_ERROR(-20033, 'Unexpected impact row count');
  END IF;

  IF L_TASKS_JSON.GET_SIZE <> 2 THEN
    RAISE_APPLICATION_ERROR(-20034, 'Unexpected open task row count');
  END IF;

  IF L_DOCS_JSON.GET_SIZE <> 7 THEN
    RAISE_APPLICATION_ERROR(-20035, 'Unexpected document relation count');
  END IF;

  SELECT COUNT(DISTINCT DOCUMENT_ID)
    INTO L_DISTINCT_DOC
    FROM JSON_TABLE(
      L_DOCUMENTS,
      '$[*]' COLUMNS (
        DOCUMENT_ID VARCHAR2(40) PATH '$.document_id'
      )
    )
   WHERE DOCUMENT_ID IN ('IR-2026-081', 'SB-NW-2603', 'OPS-NET-2.1');

  IF L_DISTINCT_DOC <> 3 THEN
    RAISE_APPLICATION_ERROR(-20036, 'Expected document IDs were not found');
  END IF;

  SELECT COUNT(*)
    INTO L_BAD_PATHS
    FROM JSON_TABLE(
      L_DOCUMENTS,
      '$[*]' COLUMNS (
        DOCUMENT_VERSION VARCHAR2(20) PATH '$.document_version',
        INCIDENT_ID VARCHAR2(40) PATH '$.incident_id',
        EQUIPMENT_ID VARCHAR2(40) PATH '$.equipment_id',
        MANUFACTURING_LOT VARCHAR2(40) PATH '$.manufacturing_lot',
        TASK_ID NUMBER PATH '$.task_id',
        PATH_NAME VARCHAR2(100) PATH '$.path',
        SOURCE_TYPE VARCHAR2(30) PATH '$.source_type',
        SOURCE_REFERENCE_ID VARCHAR2(200) PATH '$.source_reference_id'
      )
    )
   WHERE DOCUMENT_VERSION IS NULL
      OR INCIDENT_ID IS NULL OR INCIDENT_ID <> 'INC-2026-081'
      OR SOURCE_TYPE IS NULL OR SOURCE_REFERENCE_ID IS NULL
      OR PATH_NAME IS NULL
      OR (PATH_NAME = 'CREATES>USES_PROCEDURE' AND TASK_ID IS NULL)
      OR (PATH_NAME = 'AFFECTS>BELONGS_TO_LOT>GOVERNED_BY'
          AND (EQUIPMENT_ID IS NULL OR MANUFACTURING_LOT IS NULL));

  IF L_BAD_PATHS > 0 THEN
    RAISE_APPLICATION_ERROR(-20037, 'Missing document path or provenance');
  END IF;

  DBMS_OUTPUT.PUT_LINE('Knowledge Graph postconditions: PASS');
END;
/
SQL実行結果
Knowledge Graph postconditions: PASS


PL/SQL procedure successfully completed.

合成デモデータのPL/SQL Blockをもう1度実行し、このPostconditionが再度PASSになることも確認します。

● PAF接続前にGraph APIを検証

・ 接続前にGraphと通常のJOINを比較する

同じ意味の問い合わせを、通常のSQL JOINとGRAPH_TABLEで実行します。これはGraphの方が必ず速い、またはJOINでは答えられないと証明するテストではありません。定義したRelationが意図したデータへ接続されているかを確認するテストです。

Owner Schemaで次を実行します。差分が0行なら、双方の結果集合は一致しています。直前のPostconditionで2件と確認したうえで比較するため、両方空のまま成功と扱うことも避けられます。

JOINとGraphの差分検査SQL
WITH SQL_RESULT AS (
  SELECT DISTINCT
    I.INCIDENT_ID,
    E1.EQUIPMENT_ID AS AFFECTED_EQUIPMENT_ID,
    E1.MANUFACTURING_LOT,
    E2.EQUIPMENT_ID AS RELATED_EQUIPMENT_ID,
    S.STAGE_ID
  FROM V_KG_INCIDENTS I
  JOIN V_KG_EQUIPMENT E1
    ON E1.EQUIPMENT_ID = I.RELATED_EQUIPMENT_ID
  JOIN V_KG_EQUIPMENT E2
    ON E2.MANUFACTURING_LOT = E1.MANUFACTURING_LOT
   AND E2.EQUIPMENT_ID <> E1.EQUIPMENT_ID
  JOIN V_KG_STAGES S
    ON S.STAGE_ID = E2.STAGE_ID
  WHERE I.INCIDENT_ID = 'INC-2026-081'
), GRAPH_RESULT AS (
  SELECT DISTINCT
    INCIDENT_ID,
    AFFECTED_EQUIPMENT_ID,
    MANUFACTURING_LOT,
    RELATED_EQUIPMENT_ID,
    STAGE_ID
  FROM GRAPH_TABLE (
    LSF_FESTIVAL_OPS_GRAPH
    MATCH
      (I IS INCIDENT)
        -[A IS AFFECTS]->(E1 IS EQUIPMENT)
        -[B1 IS BELONGS_TO_LOT]->(L IS MANUFACTURING_LOT)
        <-[B2 IS BELONGS_TO_LOT]-(E2 IS EQUIPMENT)
        -[D IS DEPLOYED_AT]->(S IS STAGE)
    WHERE I.INCIDENT_ID = 'INC-2026-081'
      AND E1.EQUIPMENT_ID <> E2.EQUIPMENT_ID
    COLUMNS (
      I.INCIDENT_ID AS INCIDENT_ID,
      E1.EQUIPMENT_ID AS AFFECTED_EQUIPMENT_ID,
      L.MANUFACTURING_LOT AS MANUFACTURING_LOT,
      E2.EQUIPMENT_ID AS RELATED_EQUIPMENT_ID,
      S.STAGE_ID AS STAGE_ID
    )
  )
)
SELECT 'SQL_ONLY' AS DIFFERENCE, D.*
FROM (
  SELECT * FROM SQL_RESULT
  MINUS
  SELECT * FROM GRAPH_RESULT
) D
UNION ALL
SELECT 'GRAPH_ONLY' AS DIFFERENCE, D.*
FROM (
  SELECT * FROM GRAPH_RESULT
  MINUS
  SELECT * FROM SQL_RESULT
) D;
SQL実行結果
no rows selected

この比較は同じ公開Viewを参照するため、元データの真実性や語彙の妥当性まで保証しません。CSVの独立確認、業務定義の確認と組み合わせます。GRAPH_TABLE Operator

・ 不正IDと未知IDで異常系を確認する

「存在するIncidentに関連機材がない」と「Incident自体が存在しない」は異なります。Packageは前者を空配列、後者をエラーとして扱います。

次の読み取りテストでは、不正な形式は-20011、形式が正しくても未登録のIDは-20012になることを確認します。別データへ応用する場合、未知IDにはそのデータに存在しない値を使います。

SET SERVEROUTPUT ON

DECLARE
  PROCEDURE EXPECT_ERROR (
    P_ID IN VARCHAR2,
    P_EXPECTED_CODE IN NUMBER
  ) AS
    L_RESULT CLOB;
  BEGIN
    BEGIN
      LSF_KG_API.GET_INCIDENT_IMPACT(P_ID, L_RESULT);
    EXCEPTION
      WHEN OTHERS THEN
        IF SQLCODE = P_EXPECTED_CODE THEN
          DBMS_OUTPUT.PUT_LINE(P_ID || ': expected error PASS');
          RETURN;
        ELSE
          RAISE;
        END IF;
    END;
    RAISE_APPLICATION_ERROR(-20041, 'Expected API error was not raised');
  END EXPECT_ERROR;
BEGIN
  EXPECT_ERROR('INVALID-ID', -20011);
  EXPECT_ERROR('INC-UNKNOWN-999', -20012);
END;
/
SQL実行結果
INVALID-ID: expected error PASS
INC-UNKNOWN-999: expected error PASS


PL/SQL procedure successfully completed.

この時点で期待件数・ID・独立比較・異常系を確認してから、PAFへ接続します。以降、同じ問題がPAFだけで発生した場合は、Tool選択・引数・権限・回答統合の順に調べられます。

・ 条件を変え、APIの回答と復元を確認する

最初の回答が合っているだけでなく、TaskやRelationを変更したときに回答も追従するかを確認します。ここでは合成デモ行だけを変更し、同じSQLセッションからAPIを呼び出します。各ケースの最後にROLLBACK TOで変更前の値を復元し、初期状態のAPI結果を再検査します。

ケース 未完了Task 文書への経路 文書種類 SB-NW-2603への経路
初期状態 2 7 3 3
Orbit Main StageのTaskを1件完了 1 5 3 2
LotからService BulletinへのRelationを1件削除 2 6 3 2
Service BulletinへのLot 1件+Task 2件のRelationを削除 2 4 2 0

LotのRelationを1件削除しても、同じ文書へTaskから到達できるため、文書自体は候補に残ります。 最後のケースで、Service Bulletinへの全3経路を外したときに初めて候補から消えます。この差を確認すると、GraphのRelationと文書検索の候補がどうつながるかを体験できます。影響候補のEquipmentは、全ケースで2件のままです。

1) Taskの完了値を確認する

固定CommitをCloneしたRepositoryで、まずTask Tableの定義と更新処理を確認します。

コマンド
rg -n -i 'status|check|complete|done|close' sql/06_create_action_procedure.sql

rgがない環境では、同じ確認を次で実行できます。

コマンド
grep -nEi 'status|check|complete|done|close' sql/06_create_action_procedure.sql
コマンド実行結果
shirok@bacbook lakehouse-sound-festival-2026-paf-demo % grep -nEi 'status|check|complete|done|close' sql/06_create_action_procedure.sql
    7:  PRIORITY            VARCHAR2(10) CHECK (PRIORITY IN ('LOW','MEDIUM','HIGH','CRITICAL')),
    11:  STATUS              VARCHAR2(20) DEFAULT 'OPEN'

次に、前回作成したLSF_IMPROVEMENT_TASKS.STATUSのCHECK制約と現在の値を確認します。既存の業務環境へ適用する場合は、Task更新処理で定義された完了値を使用します。以下の固定デモ環境に完了値の定義がない場合は、この条件変更テストで使う値を明示して進めます。

・ USER_CONSTRAINTS確認

SQL
SELECT CONSTRAINT_NAME, SEARCH_CONDITION
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'LSF_IMPROVEMENT_TASKS'
  AND CONSTRAINT_TYPE = 'C';
SQL実行結果
CONSTRAINT_NAME    SEARCH_CONDITION
__________________ _________________________________________________
SYS_C0036864       "TASK_ID" IS NOT NULL
SYS_C0036865       "INCIDENT_ID" IS NOT NULL
SYS_C0036866       "TASK_TITLE" IS NOT NULL
SYS_C0036867       PRIORITY IN ('LOW','MEDIUM','HIGH','CRITICAL')

・ LSF_IMPROVEMENT_TASKS確認

SQL
SELECT STATUS, COUNT(*) AS TASK_COUNT
FROM LSF_IMPROVEMENT_TASKS
GROUP BY STATUS
ORDER BY STATUS;
SQL実行結果
STATUS       TASK_COUNT
_________ _____________
OPEN                  6

掲載結果では、STATUSを制限するCHECK制約はなく、現在の6件はすべてOPENです。この結果だけでは既存アプリケーションの完了値は分からないため、本記事の合成Taskを一時的に変更するテストでは、テスト用の完了値をCLOSEDと定めます。これは既存アプリケーションの状態定義を変更するものではありません。テスト後はRollbackで元の値に戻します。

SQL*Plus/SQLclでは次のDEFINEをそのまま使用します。SQL Developerではスクリプトとして実行し、DEFINEを使用しないClientでは、Block内のC_COMPLETED_STATUSへCLOSEDを直接設定します。既存環境に完了値の定義がある場合は、その値に置き換えてください。

2) 同じSQLセッションで3ケースを実行する

ここまでのデータ投入をCommit済みで、初期PostconditionがPASSになる検証用Owner Schemaで実行します。Block中にCOMMITやDDLを追加しないでください。各ケースの変更とAPI呼出を同じBlockで行うため、Clientが文ごとにAuto-commitする設定でも途中の変更を確定せずに復元します。

条件変更3ケースとRollback復元の検査SQL
SQL
SET SERVEROUTPUT ON
DEFINE KG_COMPLETED_STATUS = 'CLOSED'

DECLARE
  C_INCIDENT_ID CONSTANT VARCHAR2(40) := 'INC-2026-081';
  C_COMPLETED_STATUS CONSTANT VARCHAR2(40) := '&KG_COMPLETED_STATUS';
  L_TARGET_TASK_ID NUMBER;
  L_ORIGINAL_STATUS LSF_IMPROVEMENT_TASKS.STATUS%TYPE;
  L_RESTORED_STATUS LSF_IMPROVEMENT_TASKS.STATUS%TYPE;

  PROCEDURE REQUIRE_EQ(
    P_ACTUAL NUMBER, P_EXPECTED NUMBER, P_LABEL VARCHAR2
  ) AS
  BEGIN
    IF P_ACTUAL IS NULL OR P_ACTUAL <> P_EXPECTED THEN
      RAISE_APPLICATION_ERROR(-20061,
        P_LABEL || ': expected=' || P_EXPECTED ||
        ', actual=' || NVL(TO_CHAR(P_ACTUAL), 'NULL'));
    END IF;
  END;

  PROCEDURE CHECK_STATE(
    P_LABEL VARCHAR2,
    P_TASK_COUNT NUMBER, P_PATH_COUNT NUMBER,
    P_DOC_COUNT NUMBER, P_SB_PATH_COUNT NUMBER,
    P_LOT_PATH_COUNT NUMBER, P_TASK_PATH_COUNT NUMBER,
    P_TARGET_OPEN NUMBER
  ) AS
    L_IMPACT CLOB;
    L_TASKS CLOB;
    L_DOCS CLOB;
    L_JSON JSON_ARRAY_T;
    L_DISTINCT_DOCS NUMBER;
    L_SB_PATHS NUMBER;
    L_INCIDENT_PATHS NUMBER;
    L_LOT_PATHS NUMBER;
    L_TASK_PATHS NUMBER;
    L_UNEXPECTED_DOCS NUMBER;
    L_TARGET_OPEN NUMBER;
  BEGIN
    LSF_KG_API.GET_INCIDENT_IMPACT(C_INCIDENT_ID, L_IMPACT);
    LSF_KG_API.GET_OPEN_TASKS(C_INCIDENT_ID, L_TASKS);
    LSF_KG_API.GET_RELATED_DOCUMENTS(C_INCIDENT_ID, L_DOCS);

    L_JSON := JSON_ARRAY_T.PARSE(L_IMPACT);
    REQUIRE_EQ(L_JSON.GET_SIZE, 2, P_LABEL || '/equipment');
    L_JSON := JSON_ARRAY_T.PARSE(L_TASKS);
    REQUIRE_EQ(L_JSON.GET_SIZE, P_TASK_COUNT, P_LABEL || '/tasks');
    L_JSON := JSON_ARRAY_T.PARSE(L_DOCS);
    REQUIRE_EQ(L_JSON.GET_SIZE, P_PATH_COUNT, P_LABEL || '/paths');

    SELECT COUNT(*) INTO L_TARGET_OPEN
    FROM JSON_TABLE(L_TASKS, '$[*]' COLUMNS (
      TASK_ID NUMBER PATH '$.task_id'
    ))
    WHERE TASK_ID = L_TARGET_TASK_ID;
    REQUIRE_EQ(L_TARGET_OPEN, P_TARGET_OPEN, P_LABEL || '/target task');

    SELECT COUNT(DISTINCT DOCUMENT_ID),
           COUNT(CASE WHEN DOCUMENT_ID = 'SB-NW-2603' THEN 1 END),
           COUNT(CASE WHEN PATH_NAME = 'DOCUMENTED_BY' THEN 1 END),
           COUNT(CASE WHEN PATH_NAME =
             'AFFECTS>BELONGS_TO_LOT>GOVERNED_BY' THEN 1 END),
           COUNT(CASE WHEN PATH_NAME = 'CREATES>USES_PROCEDURE'
             THEN 1 END),
           COUNT(CASE WHEN DOCUMENT_ID IS NULL OR DOCUMENT_ID NOT IN
             ('IR-2026-081', 'SB-NW-2603', 'OPS-NET-2.1') THEN 1 END)
      INTO L_DISTINCT_DOCS, L_SB_PATHS, L_INCIDENT_PATHS,
           L_LOT_PATHS, L_TASK_PATHS, L_UNEXPECTED_DOCS
      FROM JSON_TABLE(L_DOCS, '$[*]' COLUMNS (
        DOCUMENT_ID VARCHAR2(40) PATH '$.document_id',
        PATH_NAME VARCHAR2(100) PATH '$.path'
      ));

    REQUIRE_EQ(L_DISTINCT_DOCS, P_DOC_COUNT, P_LABEL || '/document IDs');
    REQUIRE_EQ(L_SB_PATHS, P_SB_PATH_COUNT, P_LABEL || '/SB paths');
    REQUIRE_EQ(L_INCIDENT_PATHS, 1, P_LABEL || '/incident paths');
    REQUIRE_EQ(L_LOT_PATHS, P_LOT_PATH_COUNT, P_LABEL || '/lot paths');
    REQUIRE_EQ(L_TASK_PATHS, P_TASK_PATH_COUNT, P_LABEL || '/task paths');
    REQUIRE_EQ(L_UNEXPECTED_DOCS, 0, P_LABEL || '/unexpected documents');
    DBMS_OUTPUT.PUT_LINE(P_LABEL || ': PASS');
  END;
BEGIN
  SAVEPOINT KG_CONDITION_TEST;

  IF C_COMPLETED_STATUS IS NULL OR
     NOT REGEXP_LIKE(C_COMPLETED_STATUS, '^[A-Z][A-Z0-9_]{0,39}$') OR
     C_COMPLETED_STATUS IN ('OPEN', 'IN_PROGRESS') THEN
    RAISE_APPLICATION_ERROR(-20062, 'Set a valid completed status for this test');
  END IF;

  -- IDは固定せず、この記事で作った合成Taskを特定する。
  SELECT TASK_ID, STATUS
    INTO L_TARGET_TASK_ID, L_ORIGINAL_STATUS
    FROM LSF_IMPROVEMENT_TASKS
   WHERE INCIDENT_ID = C_INCIDENT_ID
     AND TASK_TITLE = 'Orbit Main StageのNW-2603予防交換'
     AND CREATED_BY_AGENT = 'ONTOLOGY_DEMO_SEED';

  CHECK_STATE('BASELINE', 2, 7, 3, 3, 2, 4, 1);

  -- ケース1: 合成Taskを1件だけ完了する。
  UPDATE LSF_IMPROVEMENT_TASKS
     SET STATUS = C_COMPLETED_STATUS
   WHERE TASK_ID = L_TARGET_TASK_ID
     AND INCIDENT_ID = C_INCIDENT_ID
     AND CREATED_BY_AGENT = 'ONTOLOGY_DEMO_SEED'
     AND STATUS = L_ORIGINAL_STATUS;
  REQUIRE_EQ(SQL%ROWCOUNT, 1, 'CASE1/update rows');
  CHECK_STATE('CASE1 TASK COMPLETED', 1, 5, 3, 2, 2, 2, 0);

  ROLLBACK TO KG_CONDITION_TEST;
  SELECT STATUS INTO L_RESTORED_STATUS
  FROM LSF_IMPROVEMENT_TASKS WHERE TASK_ID = L_TARGET_TASK_ID;
  IF L_RESTORED_STATUS IS NULL OR L_RESTORED_STATUS <> L_ORIGINAL_STATUS THEN
    RAISE_APPLICATION_ERROR(-20063, 'Task status was not restored');
  END IF;
  CHECK_STATE('RESTORED AFTER CASE1', 2, 7, 3, 3, 2, 4, 1);

  -- ケース2: Lot -> Service Bulletinの合成Relationだけを削除する。
  DELETE FROM LSF_KG_LOT_DOCUMENT
   WHERE MANUFACTURING_LOT = 'NW-2603'
     AND DOCUMENT_ID = 'SB-NW-2603'
     AND SOURCE_TYPE = 'SYNTHETIC_DEMO'
     AND SOURCE_REFERENCE_ID = 'BLOG-DEMO-DOC-001';
  REQUIRE_EQ(SQL%ROWCOUNT, 1, 'CASE2/delete lot rows');
  CHECK_STATE('CASE2 LOT PATH REMOVED', 2, 6, 3, 2, 1, 4, 1);

  ROLLBACK TO KG_CONDITION_TEST;
  CHECK_STATE('RESTORED AFTER CASE2', 2, 7, 3, 3, 2, 4, 1);

  -- ケース3: Service BulletinへのLot 1件とTask 2件を削除する。
  DELETE FROM LSF_KG_LOT_DOCUMENT
   WHERE MANUFACTURING_LOT = 'NW-2603'
     AND DOCUMENT_ID = 'SB-NW-2603'
     AND SOURCE_TYPE = 'SYNTHETIC_DEMO'
     AND SOURCE_REFERENCE_ID = 'BLOG-DEMO-DOC-001';
  REQUIRE_EQ(SQL%ROWCOUNT, 1, 'CASE3/delete lot rows');

  DELETE FROM LSF_KG_TASK_DOCUMENT D
   WHERE D.DOCUMENT_ID = 'SB-NW-2603'
     AND D.SOURCE_TYPE = 'SYNTHETIC_DEMO'
     AND D.SOURCE_REFERENCE_ID = 'BLOG-DEMO-TASK-001'
     AND EXISTS (
       SELECT 1 FROM LSF_IMPROVEMENT_TASKS T
       WHERE T.TASK_ID = D.TASK_ID
         AND T.INCIDENT_ID = C_INCIDENT_ID
         AND T.CREATED_BY_AGENT = 'ONTOLOGY_DEMO_SEED'
         AND T.TASK_TITLE IN (
           'Orbit Main StageのNW-2603予防交換',
           'Neon Groove StageのNW-2603予防交換'
         )
     );
  REQUIRE_EQ(SQL%ROWCOUNT, 2, 'CASE3/delete task rows');
  CHECK_STATE('CASE3 ALL SB PATHS REMOVED', 2, 4, 2, 0, 1, 2, 1);

  ROLLBACK TO KG_CONDITION_TEST;
  CHECK_STATE('RESTORED AFTER CASE3', 2, 7, 3, 3, 2, 4, 1);
  DBMS_OUTPUT.PUT_LINE('Condition-change tests: PASS; all changes rolled back');
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK TO KG_CONDITION_TEST;
    RAISE;
END;
/

次は以前の実行時に記録した3ケースと復元の結果です。このログには使用した完了値自体は出力されていません。今回の原稿では、再現用の値を上記のCLOSEDに明示しました。

SQL実行結果
BASELINE: PASS
CASE1 TASK COMPLETED: PASS
RESTORED AFTER CASE1: PASS
CASE2 LOT PATH REMOVED: PASS
RESTORED AFTER CASE2: PASS
CASE3 ALL SB PATHS REMOVED: PASS
RESTORED AFTER CASE3: PASS
Condition-change tests: PASS; all changes rolled back


PL/SQL procedure successfully completed.

このBlockは成功時も例外時も、テスト内の更新・削除を戻します。削除したRelationをINSERTし直さないので、SourceやReference IDを含む元の行をそのまま復元できます。SAVEPOINTより前の作業はCommitもRollbackもしません。

3) PAFで試す範囲を分ける

このSQLは、同一セッションのGraph APIに対する条件変更テストです。未Commitの変更は、別接続のPAFからは見えません。上記Block実行中にPAFへ質問しても、変更後の挙動を検証したことにはなりません。

PAFの最終回答まで比較するときは、検証専用のDatabase Cloneなど、元データへ影響しない環境を用意します。対象行の全列を主キー付きで退避してから1ケースだけ変更してCommitし、新しいPAF会話で同じ質問を実行します。その後、退避した全列から正確に復元してCommitし、read-onlyのPostconditionで初期状態を確認してから次のケースへ進みます。Taskを無条件にOPENへ戻す手順では、元がIN_PROGRESSだった場合に元どおりになりません。

本文で実装する自動テストの範囲は、上記の同一セッションSQL検証までです。PAFの条件変更テストは、Cloneの取得・復元方法を決めてから行い、実施したケースだけを検証結果へ記録します。

● Private Agent Factoryを構成

Database側の検証が完了したら、分析用ViewとPAFのToolを設定します。既存Select AI RAGを再利用し、Graph API・NL2SQL・RAGを本編Flowへ接続します。

・ 分析用Viewを作成

1) ADB_USERで実行

SQL> show user
USER is "ADB_USER"

2) 配布SQLで分析用Viewを作成する

作成SQLはRepositoryのsql/ontology/16_create_v_kg_incident_facts.sqlに収録しています。以下はそのSQL本体です。取得済みのファイルを使用できるため、転記や別ファイルへの保存は不要です。

分析用Viewの作成SQL全文(配布ファイルに収録)
sql/ontology/16_create_v_kg_incident_facts.sql
-- Run as ADB_USER in SQLcl / SQL*Plus.
-- Requires the previous article views and the ontology V_KG_* views.
-- PAF Data Analysis Agent selects ADB_USER.V_KG_INCIDENT_FACTS only.
-- Source definitions were checked against the published article.
-- This script has not been executed against your Oracle Database here.
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET SERVEROUTPUT ON

BEGIN
  IF USER <> 'ADB_USER' THEN
    RAISE_APPLICATION_ERROR(-20070, 'Connect as ADB_USER to create the facts view');
  END IF;
END;
/

CREATE OR REPLACE VIEW V_KG_INCIDENT_FACTS AS
WITH SENSOR_DAY AS (
  SELECT FESTIVAL_DATE, STAGE_ID, EQUIPMENT_ID,
         MAX(CASE WHEN METRIC_NAME = 'temperature_c'
                  THEN MAX_METRIC_VALUE END) AS DAILY_MAX_TEMPERATURE_C,
         MAX(CASE WHEN METRIC_NAME = 'packet_loss_pct'
                  THEN MAX_METRIC_VALUE END) AS DAILY_MAX_PACKET_LOSS_PCT,
         MIN(CASE WHEN METRIC_NAME = 'fan_rpm'
                  THEN MIN_METRIC_VALUE END) AS DAILY_MIN_FAN_RPM
  FROM V_EQUIPMENT_HEALTH_SUMMARY
  GROUP BY FESTIVAL_DATE, STAGE_ID, EQUIPMENT_ID
), MAINTENANCE_SUMMARY AS (
  SELECT EQUIPMENT_ID,
         COUNT(*) AS MAINTENANCE_RECORD_COUNT,
         SUM(CASE WHEN STATUS = 'OVERDUE' THEN 1 ELSE 0 END)
           AS OVERDUE_MAINTENANCE_COUNT,
         MIN(CASE WHEN STATUS = 'OVERDUE' THEN SCHEDULED_DATE END)
           AS EARLIEST_OVERDUE_SCHEDULED_DATE
  FROM EXT_EQUIPMENT_MAINTENANCE
  GROUP BY EQUIPMENT_ID
), DELAY_SUMMARY AS (
  SELECT INCIDENT_ID, COUNT(*) AS AFFECTED_PERFORMANCE_COUNT,
         MAX(DELAY_MINUTES) AS MAX_DELAY_MINUTES
  FROM V_STAGE_DELAY_ANALYSIS
  WHERE INCIDENT_ID IS NOT NULL
  GROUP BY INCIDENT_ID
)
SELECT I.INCIDENT_ID, I.FESTIVAL_DATE,
       I.LOCATION_ID AS INCIDENT_LOCATION_ID,
       I.CATEGORY AS INCIDENT_CATEGORY,
       I.SEVERITY AS INCIDENT_SEVERITY,
       I.START_TS AS INCIDENT_START_TS,
       I.END_TS AS INCIDENT_END_TS,
       I.TITLE AS INCIDENT_TITLE,
       I.STATUS AS INCIDENT_STATUS,
       I.DOCUMENT_ID AS INCIDENT_DOCUMENT_ID,
       I.RELATED_EQUIPMENT_ID AS AFFECTED_EQUIPMENT_ID,
       E.STAGE_ID AS EQUIPMENT_STAGE_ID,
       S.STAGE_NAME AS EQUIPMENT_STAGE_NAME,
       E.MANUFACTURING_LOT,
       E.LOT_SOURCE, E.LOT_ASSERTION_STATUS, E.LOT_SOURCE_REFERENCE_ID,
       H.DAILY_MAX_TEMPERATURE_C,
       H.DAILY_MAX_PACKET_LOSS_PCT,
       H.DAILY_MIN_FAN_RPM,
       M.MAINTENANCE_RECORD_COUNT,
       M.OVERDUE_MAINTENANCE_COUNT,
       M.EARLIEST_OVERDUE_SCHEDULED_DATE,
       D.AFFECTED_PERFORMANCE_COUNT,
       D.MAX_DELAY_MINUTES
FROM V_KG_INCIDENTS I
LEFT JOIN V_KG_EQUIPMENT E ON E.EQUIPMENT_ID = I.RELATED_EQUIPMENT_ID
LEFT JOIN V_KG_STAGES S ON S.STAGE_ID = E.STAGE_ID
LEFT JOIN SENSOR_DAY H
  ON H.FESTIVAL_DATE = I.FESTIVAL_DATE
 AND H.STAGE_ID = E.STAGE_ID
 AND H.EQUIPMENT_ID = E.EQUIPMENT_ID
LEFT JOIN MAINTENANCE_SUMMARY M ON M.EQUIPMENT_ID = E.EQUIPMENT_ID
LEFT JOIN DELAY_SUMMARY D ON D.INCIDENT_ID = I.INCIDENT_ID;

COMMENT ON TABLE V_KG_INCIDENT_FACTS IS
  'Incident単位の分析View。センサーはIncident当日の機材日次集計。保守は元データでOVERDUEの件数と最も早い予定日。Task・関連文書の経路はGraph APIで取得する。';
COMMENT ON COLUMN V_KG_INCIDENT_FACTS.DAILY_MAX_TEMPERATURE_C IS
  'Incident当日の機材の日次最大温度。障害発生時間帯だけの値ではない';
COMMENT ON COLUMN V_KG_INCIDENT_FACTS.DAILY_MAX_PACKET_LOSS_PCT IS
  'Incident当日の機材の日次最大パケットロス率(%)';
COMMENT ON COLUMN V_KG_INCIDENT_FACTS.DAILY_MIN_FAN_RPM IS
  'Incident当日の機材の日次最低ファン回転数(RPM)';
COMMENT ON COLUMN V_KG_INCIDENT_FACTS.EARLIEST_OVERDUE_SCHEDULED_DATE IS
  '元データのSTATUS=OVERDUEの保守予定日の最小値。Incident時点の保守状態を履歴から再構成したものではない';
COMMENT ON COLUMN V_KG_INCIDENT_FACTS.AFFECTED_PERFORMANCE_COUNT IS
  'V_STAGE_DELAY_ANALYSISでIncident IDが一致する公演行の件数';

GRANT SELECT ON V_KG_INCIDENT_FACTS TO LSF_AGENT;

-- Must return no rows: aggregating each source before joining prevents fan-out.
SELECT INCIDENT_ID, COUNT(*) AS ROW_COUNT
FROM V_KG_INCIDENT_FACTS
GROUP BY INCIDENT_ID
HAVING COUNT(*) <> 1;

-- Expected for INC-2026-081: 1 row, EQ-NET-WA-01,
-- daily sensor values 91.755 / 19.167 / 120,
-- earliest overdue scheduled date 2026-07-25,
-- affected performance count 3, maximum delay 29.
SELECT INCIDENT_ID, FESTIVAL_DATE, AFFECTED_EQUIPMENT_ID,
       DAILY_MAX_TEMPERATURE_C, DAILY_MAX_PACKET_LOSS_PCT,
       DAILY_MIN_FAN_RPM, MAINTENANCE_RECORD_COUNT,
       OVERDUE_MAINTENANCE_COUNT, EARLIEST_OVERDUE_SCHEDULED_DATE,
       AFFECTED_PERFORMANCE_COUNT, MAX_DELAY_MINUTES
FROM V_KG_INCIDENT_FACTS
WHERE INCIDENT_ID = 'INC-2026-081';

WHENEVER SQLERROR CONTINUE NONE

・ SQL実行

RepositoryのRootで起動したSQLcl/SQL*Plusから、ADB_USERで実行します。

@sql/ontology/16_create_v_kg_incident_facts.sql

3) V_KG_INCIDENT_FACTS作成確認
最後の確認SELECTで、INC-2026-081について次の値が返ることを確認してください。

障害機材             EQ-NET-WA-01
最大温度             91.755
最大Packet Loss      19.167
最低Fan RPM          120
期限超過の保守予定日  2026-07-25
関連公演数           3
最大遅延             29
実行ログの全文(確認結果)
SQL実行結果
PL/SQL procedure successfully completed.


View V_KG_INCIDENT_FACTS created.


Comment created.


Comment created.


Comment created.


Comment created.


Comment created.


Comment created.


Grant succeeded.


no rows selected

INCIDENT_ID     FESTIVAL_DATE    AFFECTED_EQUIPMENT_ID       DAILY_MAX_TEMPERATURE_C    DAILY_MAX_PACKET_LOSS_PCT    DAILY_MIN_FAN_RPM    MAINTENANCE_RECORD_COUNT    OVERDUE_MAINTENANCE_COUNT EARLIEST_OVERDUE_SCHEDULED_DATE       AFFECTED_PERFORMANCE_COUNT    MAX_DELAY_MINUTES
_______________ ________________ ________________________ __________________________ ____________________________ ____________________ ___________________________ ____________________________ __________________________________ _____________________________ ____________________
INC-2026-081    2026-08-08       EQ-NET-WA-01                                 91.755                       19.167                  120                           1                            1 2026-07-25                                                     3                   29
### ・ PAF構築へ進む

本記事ではPrivate Agent Factory 26.7のNode名と画面を基準にします。

・ Data Analysis Agentを作成

Data Analysis Agentは、構造化Databaseデータを自然言語から分析し、必要なSQLを生成して結果を説明します。

1) Data Analysis Agentsを開く

PAF左側MenuからData Analysis Agentsを開きます。
11_Data Analysis Agent作成01.jpg

2) Database Data Sourceを選択

前回作成したLSF2026 AGENT RUNTIME(LSF_AGENTで接続)を選択します。
11_Data Analysis Agent作成02.jpg

3) 参照Objectを限定
Views / Tables * (select one)で、ADB_USER.V_KG_INCIDENT_FACTSを1つ選択します。Incident単位で障害機材、センサー集計、保守、公演への影響を確認するためのViewです。同一Lotの別機材・Task・文書への関係は、後述のGraph APIに担当させます。
11_Data Analysis Agent作成03.jpg

4) NameとDescriptionを設定

Name:
LSF Structured Data Analyst

Description:
Lakehouse Sound Festival 2026のIncident、障害機材、当日のセンサー集計、期限超過保守、公演数・遅延をV_KG_INCIDENT_FACTSで分析する。

11_Data Analysis Agent作成05.jpg

Descriptionには上記の説明を1行で入力します。Help description (optional)は空欄にしました。文字列のNULLを入力する必要はありません。

5) Publish実行

作成後にPublishし、Agent BuilderからToolとして選択できる状態にします。

初回PublishのLLMエラーと再試行の記録

今回の初回PublishではDescription生成に関するLLMエラーが出ました。Prompt Labで選択モデルが実際に生成できることを確認し、Publishを再実行したところ成功しました。この結果だけでは、初回エラーの原因までは確定していません。

11_Data Analysis Agent作成06.jpg

6) Publish中〜
11_Data Analysis Agent作成07.jpg

7) Publish完了
11_Data Analysis Agent作成08.jpg

8) データ取得を確認
次の質問でデータ取得を確認

INC-2026-081について、障害機材ID、当日の最高温度、
最大パケット損失率、最低ファン回転数、期限超過保守件数、
影響を受けた公演数、最大遅延時間を表示してください。

前に実行したSQLの結果と、次の値が一致すれば、このケースの分析動作も確認できたことになります。

項目 確認済みの値
障害機材ID EQ-NET-WA-01
最高温度 91.755 ℃
最大パケット損失率 19.167 %
最低ファン回転数 120 RPM
期限超過保守件数 1
影響公演数 3
最大遅延時間 29分

11_Data Analysis Agent作成09.jpg

SQLタブで生成されたSQLが V_KG_INCIDENT_FACTS を参照しているか確認
11_Data Analysis Agent作成10.jpg

生成SQL確認
11_Data Analysis Agent作成11.jpg

・ 既存のSelect AI RAGを確認して再利用

本編では、前回の記事で構築したSelect AI RAGをそのまま使用します。PAFのFile Source/OCI Object Storage SourceへのPDF追加、Knowledge Agentの作成、Vector Indexの再作成は行いません。今回追加したGraphのDocument Catalogと、既存RAGのPDFをDocument ID・Version・File Nameで照合してつなぎます。

再利用するもの 設定値・役割
DatabaseのOwner ADB_USER
PAFのDatabase接続 LSF2026 SELECT AI OWNER(ADB_USERで接続)
RAG Profile LSF2026_RAG_PROFILE_PAF
Vector Index LSF2026_DOC_VECTOR
Chunk Table ADB_USER.LSF2026_DOC_VECTOR$VECTAB
PDFの保管元 前回Object Storageへ配置したdocuments/pdf_ascii/の10 PDF
本編のPAF Tool Select AI Bridge:LSF2026_RAG_PROFILE_PAF/Narrate

検索するChunk・Embeddingは、既存Select AI RAGを作成したAutonomous AI DatabaseのADB_USER Schemaにあります。PAFのRepositoryへ文書を新たに取り込む構成ではありません。Data Analysis AgentとGraph APIはLSF_AGENT接続、RAG Profileの呼出はADB_USER接続と、用途ごとに接続を分けます。

特に今回使用するDocumentは次の3つです。VersionはGraph APIとPDF本文を照合して確認します。

Document ID File
IR-2026-081 IR-2026-081_waveform_arena_delay_report.pdf
SB-NW-2603 SB-NW-2603_network_switch_cooling_fan_bulletin.pdf
OPS-NET-2.1 LSF2026_stage_audio_network_operations_v2.1.pdf

以下の確認を実行し、Profile/Vector Indexの状態、Chunk数、PAFからの回答を記録しました。Catalogの日本語File NameとRAGのASCII File Nameは異なるため、Document IDを介した対応表で照合します。

1) ADB_USERでProfileとVector Indexを確認する

SQLcl/SQL*Plusで、前回と同じAutonomous AI DatabaseへADB_USERとして接続します。USER_から始まる管理Viewは、接続中のUserが所有するObjectを表示するため、LSF_AGENTではなくOwnerで確認します。

SHOW USER
SET LONG 1000000
SET LONGCHUNKSIZE 32767
SET PAGESIZE 1000
SET LINESIZE 250
SPOOL lsf_ontology_rag_check.log

SELECT USER AS CONNECTED_SCHEMA, SYSTIMESTAMP AS CHECKED_AT
FROM DUAL;

SELECT PROFILE_NAME, STATUS
FROM USER_CLOUD_AI_PROFILES
WHERE PROFILE_NAME = 'LSF2026_RAG_PROFILE_PAF';

SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
FROM USER_CLOUD_AI_PROFILE_ATTRIBUTES
WHERE PROFILE_NAME = 'LSF2026_RAG_PROFILE_PAF'
  AND ATTRIBUTE_NAME IN (
    'provider', 'credential_name', 'model', 'embedding_model',
    'region', 'vector_index_name'
  )
ORDER BY ATTRIBUTE_NAME;

SELECT INDEX_NAME, STATUS
FROM USER_CLOUD_VECTOR_INDEXES
WHERE INDEX_NAME = 'LSF2026_DOC_VECTOR';

SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
FROM USER_CLOUD_VECTOR_INDEX_ATTRIBUTES
WHERE INDEX_NAME = 'LSF2026_DOC_VECTOR'
  AND ATTRIBUTE_NAME IN (
    'profile_name', 'pipeline_name', 'vector_table_name',
    'location', 'match_limit', 'chunk_size', 'chunk_overlap'
  )
ORDER BY ATTRIBUTE_NAME;
実行ログの全文(確認結果)
SQL実行結果
SQL> SELECT USER AS CONNECTED_SCHEMA, SYSTIMESTAMP AS CHECKED_AT
  2* FROM DUAL;

CONNECTED_SCHEMA    CHECKED_AT
___________________ ______________________________________
ADB_USER            04-OCT-26 03.18.14.514717000 AM GMT

SQL> SELECT PROFILE_NAME, STATUS
  2  FROM USER_CLOUD_AI_PROFILES
  3* WHERE PROFILE_NAME = 'LSF2026_RAG_PROFILE_PAF';

PROFILE_NAME               STATUS
__________________________ __________
LSF2026_RAG_PROFILE_PAF    ENABLED

SQL> SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
  2  FROM USER_CLOUD_AI_PROFILE_ATTRIBUTES
  3  WHERE PROFILE_NAME = 'LSF2026_RAG_PROFILE_PAF'
  4    AND ATTRIBUTE_NAME IN (
  5      'provider', 'credential_name', 'model', 'embedding_model',
  6      'region', 'vector_index_name'
  7    )
  8* ORDER BY ATTRIBUTE_NAME;

ATTRIBUTE_NAME       ATTRIBUTE_VALUE
____________________ ___________________________
credential_name      OCI$RESOURCE_PRINCIPAL
embedding_model      cohere.embed-v4.0
model                cohere.command-a-03-2025
provider             oci
region               ap-osaka-1
vector_index_name    LSF2026_DOC_VECTOR

6 rows selected.

SQL> SELECT INDEX_NAME, STATUS
  2  FROM USER_CLOUD_VECTOR_INDEXES
  3* WHERE INDEX_NAME = 'LSF2026_DOC_VECTOR';

INDEX_NAME            STATUS
_____________________ __________
LSF2026_DOC_VECTOR    ENABLED

SQL> SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
  2  FROM USER_CLOUD_VECTOR_INDEX_ATTRIBUTES
  3  WHERE INDEX_NAME = 'LSF2026_DOC_VECTOR'
  4    AND ATTRIBUTE_NAME IN (
  5      'profile_name', 'pipeline_name', 'vector_table_name',
  6      'location', 'match_limit', 'chunk_size', 'chunk_overlap'
  7    )
  8* ORDER BY ATTRIBUTE_NAME;

ATTRIBUTE_NAME    ATTRIBUTE_VALUE
_________________ _______________________________________________________________________________________________________________________________________________________
chunk_overlap     140
chunk_size        1000
location          https://objectstorage.ap-tokyo-1.oraclecloud.com/n/idqcucnenh88/b/lakehouse_sound_festival/o/lakehouse_sound_festival_2026/documents/pdf_ascii/*.pdf
match_limit       6
pipeline_name     LSF2026_DOC_VECTOR$VECPIPELINE
profile_name      LSF2026_RAG_PROFILE_PAF

6 rows selected.

ProfileとVector Indexがそれぞれ1行でENABLED、Profileのvector_index_nameがLSF2026_DOC_VECTOR、Indexのprofile_nameがLSF2026_RAG_PROFILE_PAFであることを確認します。locationは前回のPDF配置先を参照します。

ProfileのCredentialは、前回有効化したADBのOCI$RESOURCE_PRINCIPALを再利用します。PAFのLLM Connectionで使うInstance Principalとは別の認証設定です。どちらか一方の成功だけで、両方が正常とは判断しません。

2) Chunk数と対象3 PDFの取り込みを確認する

SELECT COUNT(*) AS CHUNK_COUNT
FROM LSF2026_DOC_VECTOR$VECTAB;

SELECT JSON_VALUE(ATTRIBUTES, '$.object_name'
                  RETURNING VARCHAR2(4000)) AS SOURCE_OBJECT,
       COUNT(*) AS CHUNK_COUNT
FROM LSF2026_DOC_VECTOR$VECTAB
GROUP BY JSON_VALUE(ATTRIBUTES, '$.object_name'
                    RETURNING VARCHAR2(4000))
ORDER BY SOURCE_OBJECT;

WITH EXPECTED_FILES (DOCUMENT_ID, FILE_NAME) AS (
  SELECT 'IR-2026-081',
         'IR-2026-081_waveform_arena_delay_report.pdf' FROM DUAL
  UNION ALL
  SELECT 'SB-NW-2603',
         'SB-NW-2603_network_switch_cooling_fan_bulletin.pdf' FROM DUAL
  UNION ALL
  SELECT 'OPS-NET-2.1',
         'LSF2026_stage_audio_network_operations_v2.1.pdf' FROM DUAL
), CHUNKS AS (
  SELECT REGEXP_SUBSTR(
           JSON_VALUE(ATTRIBUTES, '$.object_name'
                      RETURNING VARCHAR2(4000)), '[^/]+$'
         ) AS FILE_NAME,
         COUNT(*) AS CHUNK_COUNT
  FROM LSF2026_DOC_VECTOR$VECTAB
  GROUP BY REGEXP_SUBSTR(
             JSON_VALUE(ATTRIBUTES, '$.object_name'
                        RETURNING VARCHAR2(4000)), '[^/]+$'
           )
)
SELECT F.DOCUMENT_ID,
       D.VERSION AS CATALOG_VERSION,
       F.FILE_NAME,
       D.FILE_NAME AS CATALOG_FILE_NAME,
       NVL(C.CHUNK_COUNT, 0) AS CHUNK_COUNT
FROM EXPECTED_FILES F
LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = F.DOCUMENT_ID
LEFT JOIN CHUNKS C ON C.FILE_NAME = F.FILE_NAME
ORDER BY F.DOCUMENT_ID;
実行ログの全文(確認結果)
SQL実行結果
SQL> SELECT COUNT(*) AS CHUNK_COUNT
  2* FROM LSF2026_DOC_VECTOR$VECTAB;

   CHUNK_COUNT
______________
            22

SQL> SELECT JSON_VALUE(ATTRIBUTES, '$.object_name'
  2                    RETURNING VARCHAR2(4000)) AS SOURCE_OBJECT,
  3         COUNT(*) AS CHUNK_COUNT
  4  FROM LSF2026_DOC_VECTOR$VECTAB
  5  GROUP BY JSON_VALUE(ATTRIBUTES, '$.object_name'
  6                      RETURNING VARCHAR2(4000))
  7* ORDER BY SOURCE_OBJECT;

SOURCE_OBJECT                                             CHUNK_COUNT
______________________________________________________ ______________
IR-2026-081_waveform_arena_delay_report.pdf                         2
IR-2026-084_gate_c_congestion_report.pdf                            2
LSF2026_admission_gate_incident_response_v1.2.pdf                   2
LSF2026_improvement_task_registration_procedure.pdf                 2
LSF2026_merchandise_inventory_planning_minutes.pdf                  2
LSF2026_operations_management_manual_v1.3.pdf                       3
LSF2026_severe_weather_contingency_plan_v1.4.pdf                    2
LSF2026_stage_audio_network_operations_v2.1.pdf                     3
LSF2026_ticket_refund_policy_v2.0.pdf                               2
SB-NW-2603_network_switch_cooling_fan_bulletin.pdf                  2

10 rows selected.

SQL> WITH EXPECTED_FILES (DOCUMENT_ID, FILE_NAME) AS (
  2    SELECT 'IR-2026-081',
  3           'IR-2026-081_waveform_arena_delay_report.pdf' FROM DUAL
  4    UNION ALL
  5    SELECT 'SB-NW-2603',
  6           'SB-NW-2603_network_switch_cooling_fan_bulletin.pdf' FROM DUAL
  7    UNION ALL
  8    SELECT 'OPS-NET-2.1',
  9           'LSF2026_stage_audio_network_operations_v2.1.pdf' FROM DUAL
 10  ), CHUNKS AS (
 11    SELECT REGEXP_SUBSTR(
 12             JSON_VALUE(ATTRIBUTES, '$.object_name'
 13                        RETURNING VARCHAR2(4000)), '[^/]+$'
 14           ) AS FILE_NAME,
 15           COUNT(*) AS CHUNK_COUNT
 16    FROM LSF2026_DOC_VECTOR$VECTAB
 17    GROUP BY REGEXP_SUBSTR(
 18               JSON_VALUE(ATTRIBUTES, '$.object_name'
 19                          RETURNING VARCHAR2(4000)), '[^/]+$'
 20             )
 21  )
 22  SELECT F.DOCUMENT_ID,
 23         D.VERSION AS CATALOG_VERSION,
 24         F.FILE_NAME,
 25         D.FILE_NAME AS CATALOG_FILE_NAME,
 26         NVL(C.CHUNK_COUNT, 0) AS CHUNK_COUNT
 27  FROM EXPECTED_FILES F
 28  LEFT JOIN V_KG_DOCUMENTS D ON D.DOCUMENT_ID = F.DOCUMENT_ID
 29  LEFT JOIN CHUNKS C ON C.FILE_NAME = F.FILE_NAME
 30* ORDER BY F.DOCUMENT_ID;

DOCUMENT_ID    CATALOG_VERSION    FILE_NAME                                             CATALOG_FILE_NAME                        CHUNK_COUNT
______________ __________________ _____________________________________________________ _____________________________________ ______________
IR-2026-081    1.0                IR-2026-081_waveform_arena_delay_report.pdf           IR-2026-081_Waveform_Arena遅延報告.pdf                 2
OPS-NET-2.1    2.1                LSF2026_stage_audio_network_operations_v2.1.pdf       LSF2026_ステージ音響ネットワーク運用手順_v2.1.pdf                  3
SB-NW-2603     1.1                SB-NW-2603_network_switch_cooling_fan_bulletin.pdf    SB-NW-2603_ネットワークスイッチ冷却ファン通知.pdf                   2

対象3行のCatalog情報が存在し、Document IDに対応するASCII File Nameで各`CHUNK_COUNT`が1以上になることを確認します。今回、Catalogの日本語File NameとRAGのASCII File Nameは文字列として一致していません。上記の対応表で同じDocumentに対応付けます。`ATTRIBUTES`の`object_name`は出所Metadataです。フルPathでもFile Nameでも照合できるよう、最後の`/`以降を使用しています。

今回の実測値も10 PDF・合計22 Chunkでした。対象文書はIRが2、SBが2、OPSが3 Chunkです。Chunk数だけで判定せず、次の検索結果の内容・Version・Sourcesも確認します。

ProfileやIndexが見つからない場合は、接続先DatabaseとUserを確認します。対象PDFのChunkが0件なら、Indexのlocation、Object StorageのPDF、前回のPipelineの取込Logを確認します。既存Indexを削除して作り直す前に、前回記事のVector Index確認手順へ戻ります。

3) Databaseから既存RAGを単体テストする

Profile名とnarrateを明示して、対象PDFごとに検索します。chatではなく、既存Vector Indexを使うRAGの回答を確認します。

SELECT DBMS_CLOUD_AI.GENERATE(
  prompt => q'~Document ID IR-2026-081、
IR-2026-081_waveform_arena_delay_report.pdfに記載された、
INC-2026-081の障害機材と障害状況を説明してください。
使用した文書のFile Name、Sources、本文で確認できるVersionと
該当箇所を示し、取得できない情報は未確認としてください。~',
  profile_name => 'LSF2026_RAG_PROFILE_PAF',
  action => 'narrate'
) AS RESPONSE FROM DUAL;

SELECT DBMS_CLOUD_AI.GENERATE(
  prompt => q'~Document ID SB-NW-2603、
SB-NW-2603_network_switch_cooling_fan_bulletin.pdfに記載された、
製造Lot NW-2603の交換条件、部品未着時の対応、交換後の確認基準を
説明してください。使用した文書のFile Name、Sources、本文で確認できる
Versionと該当箇所を示し、取得できない情報は未確認としてください。~',
  profile_name => 'LSF2026_RAG_PROFILE_PAF',
  action => 'narrate'
) AS RESPONSE FROM DUAL;

SELECT DBMS_CLOUD_AI.GENERATE(
  prompt => q'~Document ID OPS-NET-2.1、
LSF2026_stage_audio_network_operations_v2.1.pdfに記載された、
Critical条件での予備Switchへの切替手順と復旧後の隔離方針を
説明してください。使用した文書のFile Name、Sources、本文で確認できる
Versionと該当箇所を示し、取得できない情報は未確認としてください。~',
  profile_name => 'LSF2026_RAG_PROFILE_PAF',
  action => 'narrate'
) AS RESPONSE FROM DUAL;

SPOOL OFF

各回答のSourcesのURI/File Nameが対象PDFと一致するか、回答内容がPDF本文にあるか、VersionがCatalogと一致するかを確認します。PromptのFile Nameを回答が繰り返しただけでは引用元の確認になりません。Page/Sectionが返らなければ、元PDFを開いて照合し、取得できない位置情報は補いません。RAG回答の細部は生成ごとに変わるため、文章の完全一致ではなく出所と内容で判定します。

4) PAFのSelect AI Frameworkで既存Profileを確認する

PAFのSelect AI Frameworkで、DatabaseにLSF2026 SELECT AI OWNERを選択します。Profiles/Vector IndexesをRefreshし、次の設定が表示されることを確認します。
・ Profilesタブ: LSF2026_RAG_PROFILE_PAF
・ Vector indexesタブ: LSF2026_DOC_VECTOR
PAFのData SourcesへPDFを追加する操作は不要です。今回、両方のObjectを画面で確認しました。接続のPartial access (5/6)表示は前回確認済みの状態で、今回もRAG単体Flowの実際の生成結果で利用可否を確認しています。

・ Profilesタブ: LSF2026_RAG_PROFILE_PAF確認
13_AFのSelect AI Frameworkで既存Profileを確認02.jpg

・ Vector indexesタブ: LSF2026_DOC_VECTOR確認
13_AFのSelect AI Frameworkで既存Profileを確認03.jpg

5) PAFからのRAG単体Flowを確認する

前回のRAGテストFlowが残っていれば、それを開いて設定を確認します。新しく作る場合はFlow名をLSF2026 ONTOLOGY RAG CHECKとし、Chat Input、Select AI、Chat Outputを配置します。

Select AIの項目 設定値
Database LSF2026 SELECT AI OWNER
Profile LSF2026_RAG_PROFILE_PAF
AI Action Narrate
Prompt Chat InputのMessageを接続
Result Chat Outputへ接続

14_RAGテストFlow作成LSF2026 ONTOLOGY RAG CHECK01.jpg

SaveしてPlaygroundを開き、手順3の質問を1つずつ入力します。Database単体と同じPDFを根拠に回答できることを確認し、設定と回答のScreenshotを保存します。Database単体が成功してPAFだけ失敗する場合は、Owner接続・選択Profile・Action・Nodeの接続を確認します。

質問1. Incident Report:障害状況

質問
Document ID IR-2026-081、
IR-2026-081_waveform_arena_delay_report.pdfに記載された、
INC-2026-081の障害機材と障害状況を説明してください。
使用した文書のFile Name、Sources、本文で確認できるVersionと
該当箇所を示し、取得できない情報は未確認としてください。

15_PAFからのRAG単体Flowを確認01.jpg

質問2. Service Bulletin:交換条件

質問
Document ID SB-NW-2603、
SB-NW-2603_network_switch_cooling_fan_bulletin.pdfに記載された、
製造Lot NW-2603の交換条件、部品未着時の対応、交換後の確認基準を
説明してください。使用した文書のFile Name、Sources、本文で確認できる
Versionと該当箇所を示し、取得できない情報は未確認としてください。

15_PAFからのRAG単体Flowを確認02.jpg

質問3. 運用手順:切替と隔離

質問
Document ID OPS-NET-2.1、
LSF2026_stage_audio_network_operations_v2.1.pdfに記載された、
Critical条件での予備Switchへの切替手順と復旧後の隔離方針を
説明してください。使用した文書のFile Name、Sources、本文で確認できる
Versionと該当箇所を示し、取得できない情報は未確認としてください。

15_PAFからのRAG単体Flowを確認03.jpg

6) 本編用Select AI Bridgeを設定する

本編Flow LSF2026 ONTOLOGY IMPACT AGENTへ、RAGをToolとして接続します。前回のLSF2026 OPERATIONS AGENTを基にする場合も、今回用のFlowを分けて次の設定を確認します。

Select AI Bridgeの項目 設定値
Mode Profile
Database LSF2026 SELECT AI OWNER
Profile LSF2026_RAG_PROFILE_PAF
AI Action Narrate

Tool名と説明は選択したProfile情報に基づいて生成されます。手動のTool Name/Tool Description設定欄を探す必要はありません。RAGの使い方は後述のAgentのCustom instructionsへ記載します。FlowのEdit detailsにあるName/Descriptionは、Flow自体の説明です。

参考:Components in an Agent Builder — Select AI Bridge。

Select AI BridgeのTool出力をAgentのToolsへ接続します。Portの表記は実際のPAF画面に従い、Tool/Server toolとして表示されるTool側の出力を選びます。固定の質問をBridgeへ埋め込まず、AgentがGraph結果を含むPromptをToolの入力へ渡します。

・ Agent BuilderへNodeを追加

次のNodeを配置します。

Chat Input
Agent
Data Analysis Agent
Select AI Bridge(既存Select AI RAG)
Oracle PL/SQL Executor
Chat Output

AgentのLLMは動作確認済みConnection PAF_OCI_GENAI_GROK_43を選び、今回の設定ではTemperatureを0.01にしました。RAGの生成・Embedding ModelはADBの既存Profile設定を使用し、AgentのLLM設定とは区別します。

FlowのEdit detailsで次を設定します。今回の画面ではDescriptionも必須でした。

項目 設定値
Name LSF2026 ONTOLOGY IMPACT AGENT
Description Lakehouse Sound Festival 2026のIncidentを起点に、NL2SQLで障害の数値情報、Knowledge Graphで同一製造Lotの機材・配置Stage・未完了Task・関連文書を確認し、既存Select AI RAGの文書根拠と合わせて運営への影響と優先対応を分析するFlow。

設定途中の画像にあるFestival Impact Managerは変更前のFlow名です。以下のAgent Nodeが、各Toolを使って回答をまとめます。

接続は次です。Port名は適用中のPAF 26.7画面に表示される名称に従います。

Connection 用途
Chat InputのMessage → AgentのPrompt 利用者の質問を渡す
Data Analysis AgentのTool → AgentのTools NL2SQL Toolとして接続
Select AI BridgeのTool → AgentのTools 既存RAG Toolとして接続
Oracle PL/SQL ExecutorのTools → AgentのTools Graph API Toolとして接続
AgentのMessage → Chat OutputのMessage 統合した回答を出力

固定の業務指示はAgentのCustom instructionsへ記載します。Chat InputをCustom instructionsへ接続せず、質問を受け取るPromptへ接続します。

20_Flow作成完了01.jpg

・ Data Analysis Agentを設定

Select Data Analysis agent to useで、作成済みのLSF Structured Data Analystを選択します。Tool出力をAgentのToolsへ接続します。

20_DataAnalysysAgent設定01.jpg

・ Oracle PL/SQL Executorを設定

1) Database Data Sourceを選択

LSF2026 AGENT RUNTIME(LSF_AGENTで接続)を選択します。

2) 許可Routineを選択

今回、Runtime Userで確認したWrapperの3 Procedureだけを選択します。前回のTask登録・更新用Procedureは選択しません。

LSF_AGENT.LSF_GET_INCIDENT_IMPACT
LSF_AGENT.LSF_GET_OPEN_TASKS
LSF_AGENT.LSF_GET_RELATED_DOCUMENTS

Packageを直接公開する分岐を使用した場合は、前節の対応表にあるADB_USER.LSF_KG_API.GET_*の3 Procedureを選び、Instructions中のRoutine名も合わせます。Owner Schema名は実際の環境に従います。

3) Auto-commitをOFFにする

今回のGraph APIはread-onlyです。

Auto-commitはOFFにします。

PAF 26.7のOracle PL/SQL Executorは、選択したProcedure/FunctionをDatabase Metadataから検出し、構造化された引数で呼び出します。

LLMへ任意のfree-form PL/SQLを実行させる構成ではありません。

18_Oracle PL:SQL Executor設定02.jpg

・ Agent Instructionsを設定

以下をAgentのCustom instructionsへ設定します。今回使用したWrapper名と文書対応を含む内容です。ExecutorでPackageを直接選んだ場合は、Routine名を前節の対応表どおりに変更します。

「Toolの使い分け」「複合質問の実行順序」「文書根拠の確認」「文書IDとFile Nameの対応」「回答のガードレール」に整理しています。見出しの整理によってToolの実行順序が強制されるわけではなく、回答内容と取得結果の確認が必要です。

あなたはLakehouse Sound Festival 2026の運営影響分析Agentです。

■ Toolの使い分け
1. Incidentと障害機材、当日のセンサー集計、期限超過保守、公演数・遅延は
   LSF Structured Data AnalystでV_KG_INCIDENT_FACTSを確認する。
   このViewが返さないTaskのStatusや関係を、ここから推測しない。

2. Incident、Equipment、Manufacturing Lot、Stage、Task、Documentの関係は
   Oracle PL/SQL Executorの次のread-only Procedureで確認する。
   LSF_AGENT.LSF_GET_INCIDENT_IMPACT
   LSF_AGENT.LSF_GET_OPEN_TASKS
   LSF_AGENT.LSF_GET_RELATED_DOCUMENTS

3. 原因、閾値、交換条件、正式な手順は、
   LSF2026_RAG_PROFILE_PAFをNarrateで呼び出す
   Select AI BridgeのToolで確認する。


■ 複合質問の実行順序

1. Incident IDを確定する。

2. LSF Structured Data Analystを呼び出し、障害機材ID、
   最高温度、最大Packet Loss、最低Fan RPM、期限超過保守件数、
   影響公演数、最大遅延時間を取得する。

3. Oracle PL/SQL Executorの3 Procedureで、
   同一Lotの別機材・配置Stage、未完了Task・対象機材、
   関連Document・Version・到達経路・出所を取得する。

4. LSF2026_RAG_PROFILE_PAFをNarrateで呼び出す
   Select AI BridgeのToolを使用する。
   Graphで取得した各候補文書について、対応するASCII File Nameを
   質問に含め、本文の根拠とSourcesを取得する。

5. 各Toolの返却結果を確認してから、
   Databaseの数値、Graphの関係、文書本文の根拠、推奨を統合する。
   数値やSourcesを、過去の回答・対応表・推測から補わない。

6. 必要なToolが未実行なら、複合分析は未完了と明記する。
   実行エラーや取得失敗の場合は、そのToolと未確認項目を示す。

■ 文書根拠の確認
Graphを使用する複合質問では、Graphが返したDocumentだけを根拠候補とする。
検索PromptにAPIのdocument_id、document_version、File Nameを含める。
APIのpath、経由ID、source_type、source_reference_idは文書を選んだ理由に使う。
Task経由のDocumentはtask_idで未完了Taskのequipment_idと照合する。
Document候補の指示はSoftで、Vector検索のDatabase Filterではない。
RAG回答のSourcesを候補のFile Nameと照合し、候補外の文書を根拠として採用しない。
Promptの文書名が回答中にあるだけでは、Sourcesを確認したことにしない。
Catalogと本文のVersionが違う場合、適合する根拠として採用しない。
Document ID、Version、取得できたPage/SectionとSourcesを回答に示す。
Versionや引用位置を取得できなければ未確認とし、存在しない引用を作らない。
Graphの関連Documentが0件なら、全Document検索へ自動Fallbackせず、
「Graphに関連Documentが登録されていない」と回答する。
Graphの関係を問わないRAG単独質問は、指定文書または既存のFestival文書を検索する。

■ 文書IDとFile Nameの対応
文書の検索とSources照合には、次のDocument IDとASCII File Nameの対応を使う。
GraphのCatalogの日本語File Nameとは、単純な文字列一致で判定しない。

IR-2026-081 → IR-2026-081_waveform_arena_delay_report.pdf
SB-NW-2603 → SB-NW-2603_network_switch_cooling_fan_bulletin.pdf
OPS-NET-2.1 → LSF2026_stage_audio_network_operations_v2.1.pdf

VersionはGraph APIのdocument_versionと文書本文を照合する。

Graphが返した各候補文書について、次を回答に示す。
1. Document IDとVersion
2. 文書を選んだGraphの到達経路と出所参照ID
3. RAGで確認した本文の根拠と、取得できた該当箇所
4. SourcesのFile NameとURI

Graphの文書選定理由と、文書本文の記載を分けて説明する。
運用手順は、切替条件・切替期限・復旧後の扱いまで具体的に示す。
根拠を取得できない候補文書は、未確認として明記する。

■ 回答のガードレール
同じManufacturing Lotであることは、同一原因による故障の証明ではない。
Graph Pathは関係を示すものであり、因果推論ではない。
合成Relationはsource_type=SYNTHETIC_DEMOとsource_reference_idを示す。
機材の全Taskではなく、このIncidentに登録された未完了Taskであることを示す。
Toolが返していないID、数値、Relationを補完しない。
Tool失敗、候補文書の検索失敗、引用元の不一致は未確認として明記する。
文書根拠を確認できていない推奨を、確認済みの正式手順として述べない。
Taskの登録・更新などの書込みは行わない。
採点用の期待結果やvalidationファイルを検索根拠にしない。

19_Agent Instructions設定01.jpg

・ Flowを保存して単独質問を確認

1) 設定を保存する

編集ダイアログを開いている場合はFinish editingで閉じ、Agent BuilderのSaveを押してからPlaygroundを開きます。設定変更後の単独確認はNew chatで始めます。

本編Flowで数値が取得できなかったときのSave確認

今回、Data Analysis Agent単体では数値が返るのに、本編Flowでは「データを取得できない」と回答したことがありました。FlowのSave漏れを修正した後、同じ質問で7項目の数値を取得できました。単体が成功して本編だけ失敗する場合は、まず選択Agent・接続先・保存状態を確認します。

2) Graph単独質問

INC-2026-081と同じManufacturing Lotの別Equipmentを、
配置Stageと一緒に示してください。

回答では起点EQ-NET-WA-01を除いた次の2台と配置Stage、Lot Relationの出所を確認できました。

Equipment Stage ID Stage
EQ-NET-NG-01 STG-NG Neon Groove Stage
EQ-NET-OM-01 STG-OM Orbit Main Stage

LotはNW-2603、この2台の補足RelationはSYNTHETIC_DEMO/BLOG-DEMO-LOT-001です。同一Lotであることと、同じ障害が発生していることは区別します。

22_Graph単独質問01.jpg

3) NL2SQL単独質問

INC-2026-081について、障害機材ID、当日の最高温度、
最大パケット損失率、最低ファン回転数、期限超過保守件数、
影響を受けた公演数、最大遅延時間を表示してください。

Data Analysis Agent単体に続き、本編Flowでも先のSQLと同じ7項目を確認しました。

項目 今回の回答で確認した値
障害機材ID EQ-NET-WA-01
当日の最高温度 91.755 ℃
最大Packet Loss 19.167 %
最低Fan RPM 120 RPM
期限超過保守件数 1
影響公演数 3
最大遅延時間 29分

22_Graph単独質問02-2.jpg

4) RAG単独質問

Document ID SB-NW-2603に記載された、
製造Lot NW-2603の交換条件、部品未着時の対応、
交換後の確認基準を説明してください。

本編Flowの回答に、SB-NW-2603 Version 1.1(有効日2026-07-12)、冷却ファン・制御基板の交換、部品未着時の予備機への置換、60分の負荷試験が示されました。この単独回答では試験基準が温度70℃未満、Fan 2,300 RPM以上、Packet Loss 0.5%未満と説明され、SourcesにSB-NW-2603_network_switch_cooling_fan_bulletin.pdfのURIが表示されました。

22_Graph単独質問03.jpg

・ 複合質問と追加質問の実行結果

ここでは2026-10-04に確認した結果を記録します。今回、数値・Graphの関係を返した複合質問の後、同じ会話で文書本文の確認を追加しました。3種類の情報を2段階で確認した結果として扱います。

1) 複合質問で数値・関係・候補文書を確認する

Waveform Arenaで発生したINC-2026-081について、
障害機材と同じManufacturing Lotの機材がほかのStageにもあるか確認してください。

このIncidentへの対応として登録された未完了Taskと対象機材、
Databaseで確認できる障害事実、
関連Documentの交換条件と運用手順を示し、
各Stageの開演前に優先すべき対応を根拠付きでまとめてください。

質問15:42:56、回答15:43:54(JST)の画面では、前述の7項目の数値、同一Lotの別機材2台と配置Stage、次の未完了Taskが表示されました。

Task Priority Status 対象Equipment Stage
TASK-121 CRITICAL OPEN EQ-NET-OM-01 Orbit Main Stage
TASK-122 HIGH OPEN EQ-NET-NG-01 Neon Groove Stage

Taskコードは今回の環境の実行結果です。再現時は自動採番された自分の環境のIDを使用します。対象機材Relationの出所はSYNTHETIC_DEMO/BLOG-DEMO-TASK-001です。

関連DocumentとしてIR-2026-081 v1.0、SB-NW-2603 v1.1、OPS-NET-2.1 v2.1が表示されました。ただし、この回答には各PDFの具体的な交換条件・切替基準とSourcesが揃っていませんでした。候補文書の一覧だけでは本文の根拠まで確認したとは扱わず、同じ会話で次の質問を追加しました。

23_Graph複合質問06.jpg

2) 同じ会話で3文書の本文確認を追加する

次は今回実際に入力した追加質問です。別のIncidentで試す場合は、Graphで取得した候補文書のFile Nameと確認事項へ置き換えます。

関連文書の本文確認を実行してください。

LSF2026_RAG_PROFILE_PAFをNarrateで呼び出す
Select AI BridgeのToolを使用し、次の3文書を検索してください。

1. IR-2026-081_waveform_arena_delay_report.pdf
   障害状況と原因の記載

2. SB-NW-2603_network_switch_cooling_fan_bulletin.pdf
   交換条件、部品未着時の対応、交換後の確認基準

3. LSF2026_stage_audio_network_operations_v2.1.pdf
   切替条件、切替期限、復旧後の隔離手順

各文書について、実際のTool返却結果にある本文の根拠、
Version、SourcesのFile NameとURIを示してください。
Graphの文書一覧やFile Name対応表だけから本文を推測しないでください。
取得できなければ、実行エラーまたは取得できなかった項目を示してください。

質問15:47:59、回答15:48:33(JST)では、3文書について内容の説明、Version、SourcesのFile Name/URIが表示されました。以下は回答内容の要約で、PDF原文の逐語引用ではありません。

Document ID 回答のVersion 回答に含まれた文書内容の要約
IR-2026-081 1.0 障害機材のFan回転低下、温度・Packet Loss上昇、公演への影響、交換部品の到着遅延による保守未完了
SB-NW-2603 1.1 高負荷イベントでの冷却ファン・制御基板交換、部品未着時の予備機への置換、交換後の60分負荷試験
OPS-NET-2.1 2.1 Packet Loss・温度・Fan回転数などの切替条件、Critical時の5分以内の切替、復旧後も再利用せず隔離する方針

回答のSourcesには次のFile NameとObject StorageのURIが示されました。いずれもGraphの候補文書に対応するASCII版です。

Document ID SourcesのFile Name/URI
IR-2026-081 IR-2026-081_waveform_arena_delay_report.pdf
SB-NW-2603 SB-NW-2603_network_switch_cooling_fan_bulletin.pdf
OPS-NET-2.1 LSF2026_stage_audio_network_operations_v2.1.pdf

23_Graph複合質問07-1.jpg

3) 今回確認できた範囲を整理する

Databaseの数値、Graph上の機材・Task・候補文書、追加質問による3文書の内容・Version・Sourcesを確認できました。一方、複合質問1回ですべての根拠が揃うことは、この結果では確認していません。

ここで記録したのは質問と最終回答の画面です。回答中の「Tool実行成功」という記述だけで、Toolの呼出順・入力・生の返却値まで照合できたとは扱いません。文書のPage/SectionとPDF原文との逐語照合も、今回の画面記録では未確認です。

例えばSBのFan基準は、単独回答が「2,300 RPM以上」、追加回答の英語が「above 2,300 RPM」と異なっていました。境界値を正式な判断条件として転記する場合は、元PDFで不等号まで照合します。Databaseの精密値91.755℃・19.167%と、文書回答の丸めた値91.8℃・19.2%も区別して記録します。

この結果を踏まえ、次の「KG-informed RAGについて」で、Graphが文書を選ぶ役割とRAGが本文の根拠を取得する役割を整理します。

● KG-informed RAGについて

⑥ Graphで文書候補を選び、RAGで根拠を確認.jpg

この章は、ここまでに構築したGraphと既存RAGの連携方式を説明する章です。以下のPromptはToolへ渡す内容の例で、この章のための追加設定や再実行は不要です。

・ PAF標準Nodeで実装する方式

本編では、Graph APIが関連Documentを選び、そのDocument情報を含む質問をSelect AI BridgeのRAG Tool(LSF2026_RAG_PROFILE_PAF/Narrate)へ渡します。BridgeはLSF2026_RAG_PROFILE_PAFのNarrateで既存Vector Indexを検索します。

例えば、Graph APIが次の候補を返した場合、RAG Toolの入力を次のようにします。

INC-2026-081と同じManufacturing LotのEquipmentについて、
開演前に必要な交換条件とフェイルオーバー手順を説明してください。

Graphで確認した根拠候補:
- IR-2026-081 / v1.0 / IR-2026-081_waveform_arena_delay_report.pdf
- SB-NW-2603 / v1.1 / SB-NW-2603_network_switch_cooling_fan_bulletin.pdf
- OPS-NET-2.1 / v2.1 / LSF2026_stage_audio_network_operations_v2.1.pdf

上記文書の記載を確認し、使用したSources、File Name、本文のVersion、
取得できた該当箇所を示してください。取得できない根拠は未確認としてください。

上記Versionは今回のGraph結果とRAG回答に表示された値です。別のデータで実行する場合も、APIのdocument_versionと文書本文を照合します。7件の到達経路が同じ3文書へ到達していても、検索候補はDocument ID+Versionで重複排除し、経路は文書の選定理由として保持します。

・ Hard Filterではない点に注意

Profile方式のSelect AI Bridgeへ渡すのはPromptです。Document IDをPromptに書いても、既存Vector Indexの10 PDFから検索する範囲がSQLでその3文書だけへ制限されたことにはなりません。

本編で実装するのはKG-informed RAGです。Graphが文書候補を提示し、AgentがRAG回答のSourcesを候補と照合します。候補外のSources、Versionの不一致、根拠の取得失敗があれば、その部分は未確認として残します。今回の記録では、最終回答の文書内容・Version・Sourcesを確認しました。Tool入力・生の返却値の照合は追加の確認項目です。取得できた実行記録がある場合は、質問・回答と対応付けて保存します。

方式 Document制限 本記事での呼称
GraphのDocument ID・Version・File NameをRAGのPromptへ渡す Soft。検索後に引用元を照合する KG-informed RAG(本編)
Chunk検索時にSQLでDocument ID+VersionをJOINして制限する Hard。検索対象をDatabaseで強制する KG-guided RAG(拡張例)

● NL2SQL+RAG+Knowledge Graphで問い合わせ

冒頭の「ほかのStageも確認すべき?」という問いを、実際の回答で確かめます。まずGraphで別機材と配置Stageを見つけ、障害機材の数値、文書の対応条件を確認し、最後にTaskと根拠を含む複合回答を見ます。

ここでは、前述の確認後に同じ本編Flowで質問を再実行した結果を記録します。2026-10-04に、同じ会話でGraph、NL2SQL、RAGの単独質問から複合質問へ進みました。その後、NL2SQLの保守項目を具体化して再質問しています。新しい会話での独立した単発テストとは区別して記録します。

・ Graphだけの質問

INC-2026-081と同じManufacturing Lotの別Equipmentを、
配置Stageと一緒に示してください。

この質問に対応するToolはLSF_AGENT.LSF_GET_INCIDENT_IMPACTです。Packageを直接公開した場合は、対応するADB_USER.LSF_KG_API.GET_INCIDENT_IMPACTを確認します。

16:31:10(JST)の回答では、EQ-NET-NG-01/STG-NGとEQ-NET-OM-01/STG-OMを確認しました。起点EQ-NET-WA-01は別機材から除外され、Lot Relationの出所SYNTHETIC_DEMO/BLOG-DEMO-LOT-001も示されました。

この画面で見るところ: 障害機材から、別のStageにある2台へ確認対象が広がっています。機材ID・配置Stage・合成Relationの出所を確認します。同じLotに属することから、2台も故障しているとは判断しません。

24_NL2SQL+RAG+KnowledgeGraphで問い合わせ-Graphだけの質問01.jpg

・ NL2SQLだけの質問

INC-2026-081の障害機材について、
最大温度、最大Packet Loss、最低Fan RPM、
期限超過保守件数、最も早い期限超過保守予定日を示してください。

初回の「保守状態を示してください」という質問では「CLOSED」と回答されましたが、その値がどの列を表すかは回答画面だけでは確認できませんでした。そこで上記のように保守件数と予定日を指定して再質問しました。

16:46:33(JST)に質問し、16:47:00の回答で次の5項目を確認しました。いずれも先に実行したV_KG_INCIDENT_FACTSのSQL結果と一致しています。

項目 今回の回答で確認した値
最大温度 91.755 ℃
最大Packet Loss 19.167 %
最低Fan RPM 120 RPM
期限超過保守件数 1件
最も早い期限超過保守予定日 2026-07-25

参考として表示された影響公演数3・最大遅延29分も一致しています。起点の障害機材は前段で確認したEQ-NET-WA-01です。2026-08-08から予定日までの14日という差は日付から計算できますが、今回の回答画面に表示された値とは分けて扱います。

この画面で見るところ: 障害機材EQ-NET-WA-01の数値を確認します。この温度やPacket Lossは、別Stageの2台の計測値ではありません。Graphで見つけた確認対象と、実際に取得した障害事実を区別します。

24_NL2SQL+RAG+KnowledgeGraphで問い合わせ-NL2SQLだけの質問02.jpg

・ RAGだけの質問

Document ID SB-NW-2603に記載された、
NW-2603の交換条件と交換後の確認基準を説明してください。

16:35:38(JST)の回答では、次の交換条件・確認基準が説明されました。以下は回答内容の要約です。

  • 高負荷イベントで使用する前に冷却ファン・制御基板を交換
  • 交換部品が未着の場合は予備機へ置換
  • 運用中に1,000 RPM未満となった場合は5分以内にFailoverして隔離
  • 交換後の60分負荷試験で温度70 C未満、Fan 2,300 RPM以上、Packet Loss 0.5%未満を確認

SourcesにはSB-NW-2603_network_switch_cooling_fan_bulletin.pdfとObject StorageのURI、根拠箇所にはSection 4/5が表示されました。今回の画面にはVersionの明記がないため、前段で確認したv1.1と分けて記録します。Sectionの表示と原文の該当箇所との照合も区別します。

この画面で見るところ: 「交換を検討する」という提案に対して、交換対象の部品、部品未着時の対応、交換後の確認基準が示されています。回答に付いたPDFのSourcesを確認します。

24_NL2SQL+RAG+KnowledgeGraphで問い合わせ-RAGだけの質問01.jpg

・ 3種類を組み合わせた質問

Waveform Arenaで発生したINC-2026-081について、
障害機材と同じManufacturing Lotの機材がほかのStageにもあるか確認してください。

このIncidentへの対応として登録された未完了Task、Databaseで確認できる障害事実、
関連Documentの交換条件と運用手順を示し、
各Stageの開演前に優先すべき対応をまとめてください。

AgentのCustom instructionsには次の処理順を指定しています。これは意図した処理順で、今回の実測Tool Traceではありません。接続とInstructionsだけでは順序や必要なTool呼出は保証されないため、文書根拠が不足した場合は、前述の追加質問で取得結果を確認します。新しい会話で複合質問1回だけを実行した場合の動作は、別の確認項目として扱います。

16:38:15(JST)の複合回答では、前の単独質問に続く同じ会話の中で、次の情報が一つの回答にまとめられました。

回答に含まれた対象機材・数値・Task・手順・Sourcesは、冒頭の統合回答に表で整理しています。

初回の複合質問では不足していた交換条件・運用手順・Sourcesが、この回答には含まれています。ただし、IRはSources一覧にあり、障害報告本文の詳細はこの回答には展開されていません。VersionとURIは前段の追加質問で確認した情報として参照します。

画面の「全Tool実行済み」はAgentの回答文です。この画面から確認できるのは最終回答の内容で、各Toolの再呼出や過去の会話の利用状況までは確定しません。単独質問から続く会話で統合回答を確認した結果として掲載します。

この画面で見るところ: 別機材2台に対して、登録TaskのPriorityと文書の対応条件が一つの回答にそろっています。「どの機材を先に確認し、どの条件で対応するか」を、機材ID・Task・Sourcesで追います。

統合回答の実画面は、冒頭の「PAFで数値・関係・文書の対応条件をまとめる」へ掲載しています。

■ 実行結果と確認できた範囲

● 検証結果を記録して説明に使う

「動いた」と説明するときは、質問と最終回答だけでなく、その途中の根拠を残します。次の表は、2026-10-04の更新時点で本文のSQLログ、PAFの設定・回答画面、Graph Studioの可視化画面に基づく確認状況です。以降の検証が完了したら、未確認の欄へ結果を追記します。

検証 合格条件 原稿作成時点の状況
元データ 固定Commitのvalidate_dataset.pyが全項目OK 既存のローカル検証記録はValidation passed.。今回の改訂では再実行していない
CSV上の関係 元データの別機材0件、合成補足後2件、CatalogとPDF参照が一致 既存記録はCSV relation precheck: PASS。今回、Catalogの日本語名とPDFのASCII名の対応を修正した検査コードは未再実行
Graph整合性 Key・Endpoint・Sourceの各検査が0行 本文掲載の実行結果で、3つの検査がすべてno rows selected
Graph作成 Vertex定義6種類/Edge定義8種類を登録 作成成功とUSER_PG_ELEMENTSの14行を本文に記録
Graph Studio可視化 定義の全体図と、同じLotの別機材へ至る2経路を表示 Schema Visualizationで6種類/8種類を確認。Queryは2行、図は7 Vertex/6 Edge。Vertex・EdgeのCaption設定済み画面を掲載
Package/基本API確認 Package/BodyがVALID、3 APIの件数が2・2・7 ADB_USERで実行済み。JSON配列の解析と件数一致を確認
詳細Postcondition Graph状態、Distinct Document ID、経路の内容まで一致 本文の実行結果でKnowledge Graph postconditions: PASS
JOIN比較 差分0件、正常系の対象IDとも一致 本文掲載の差分SELECTがno rows selected
異常系 不正ID・未知IDが所定のエラー 本文の実行結果で2ケースがexpected error PASS
条件変更と復元 Task完了・Relation削除の3ケースとRollback後の初期値が一致 既存の実行記録で3ケースと各復元がPASS。再現手順にテスト用完了値CLOSEDを明記(この値での再実行は未実施)。PAF経由の変更テストは未実施
Runtime User 3 WrapperがVALID、Ownerと同じ結果 LSF_AGENTの作成・実行結果を本文に記録
Data Analysis Agent 選択ViewとSQLの数値が一致 Publish済み。V_KG_INCIDENT_FACTS参照と掲載の7項目を確認
既存Select AI RAGの再利用 Profile/Indexと対象PDF、PAFからの回答を確認 両ObjectがENABLED、10 PDF・22 Chunk、対象3文書が2・2・3 Chunk。PAF単体Flowで3質問を確認
本編Flowの単独質問 Graph、NL2SQL、RAGの結果が期待対象と一致 別機材2台、数値7項目、SBの回答とSourcesを確認。保守項目を明確にした再質問でも1件・2026-07-25と一致
PAFの複合質問+追加質問 数値・関係・候補文書と、各文書の内容・Version・Sourcesを確認 2段階で確認。TASK-121/122と3文書の結果を本文に記録
同じ会話での統合回答 単独質問に続く複合回答に数値・関係・手順・Sourcesが揃う 16:38の回答で確認。SB/OPSの手順と3文書のSources File Nameを掲載
新しい会話で複合質問1回 会話履歴なしで数値・関係・文書根拠・推奨が揃う 未確認。今回の統合回答は単独質問から続く同じ会話で実行
詳細な取得根拠 Tool入力・生の返却値、PDFの該当箇所と境界値を照合 最終回答とSBのSection 4/5の表示まで確認。Tool呼出順・生の返却値、原文の該当箇所・境界値との照合は未確認
Graph追加前後の比較Flow 同じ情報・同じ質問でA/Bを比較 後続の任意手順。未実施
Hard KG-guided RAG Document条件をSQLで強制し、対象外Chunkを返さない 拡張設計例。Chunk投入・Embedding生成・API化は未実装

1) 実行条件を記録する

Database Version、PAF Version、LLM・Embedding Model、Repository Commit、Ontologyの版、実行日時、対象Schemaの役割を記録します。接続情報や認証情報は結果に含めません。

記録は一つの検証IDで揃えると、後から同じ条件で試し直せます。次の表へ、確認できた範囲を記録します。未確認の欄は後続の検証で追記します。

記録項目 この検証で記録する内容 実測・確認結果
検証ID・実行日時 自分で付けた識別子とTime Zone付き日時 検証IDは未付与。2026-10-04 JST:初回複合回答15:43:54、追加回答15:48:33、単独質問後の統合回答16:38:15、NL2SQL再回答16:47:00
環境 Database Version、PAF Version、LLM・Embedding Model PAF 26.7。Agent接続PAF_OCI_GENAI_GROK_43/Temperature 0.01。RAG:cohere.command-a-03-2025、Embedding:cohere.embed-v4.0、ap-osaka-1。Databaseの詳細Versionは未記録
データと定義 検証時のCommit、Ontologyの版、合成Relationの投入状態 実測時のデータ準備版はCommit bd643e63126a9cf31ecc7f360d757e1fcaf46f45(今回の配布版とは区別)。Lot補正2件、Task2件、Task–Equipment 2件、Task–Document 4件、Lot–Document 2件を確認
OwnerでのAPI実行 3 APIのJSON、Postcondition、JOIN差分 ADB_USERのJSON配列・件数2・2・7、Postcondition PASS、JOIN差分0行を本文に記録
Runtime Userでの実行 使用Routine名、直接呼出/Wrapper、OUT CLOBの受取 LSF_AGENT.LSF_GET_*の3 WrapperがVALID。呼出とOUT CLOBを本文に記録
PAFの質問と回答 質問原文、選択Tool、入力・返却値、最終回答 本編Flow名と設定、初回の2段階確認、同じ会話での統合回答、NL2SQL再実行の画像と結果を掲載。Tool呼出順・生の返却値は未確認
文書の根拠 Document ID・Version・経路・出所ID・引用箇所 初回追加回答でIR v1.0、SB v1.1、OPS v2.1とSources URIを確認。再実行のSB回答にはSection 4/5を表示。Graphの経路・出所と本文を分け、原文との逐語照合は未確認として記録
条件変更 変更前/変更中/復元後のAPI結果と、PAFで別途確認した範囲 同一SQLセッションの3ケース・各Rollback復元がPASS。ログに完了値自体の記録はなく、再現手順ではCLOSEDを明記。PAFでの条件変更は未確認
Graph Studio ログインUser、Graph所有者、Driver、Query結果と図 掲載画面のUserはADMIN、Graph所有者はADB_USER、DriverはSQL。Schema定義6種類/8種類、Query 2行、図7 Vertex/6 Edgeを確認

ここまでに保存したSQLログと回答画像を検証記録へまとめます。記録を整理するために、成功済みの処理を再実行する必要はありません。

新しい検証を行う場合は、SQLcl/SQL*PlusでGraph APIの単体確認やPostconditionの実行前に次を設定すると、結果を一つのLogへ記録できます。

SET ECHO ON
SET SERVEROUTPUT ON SIZE UNLIMITED
SET LONG 1000000
SET LONGCHUNKSIZE 32767
SET LINESIZE 200
SPOOL lsf_ontology_api_check.log
SELECT USER AS CONNECTED_SCHEMA,
       SYSTIMESTAMP AS CHECKED_AT
FROM DUAL;

この状態で、本文にある3 APIの呼出、Postcondition、JOIN比較、異常系、条件変更の各Blockを実行し、完了後に記録を閉じます。元データ投入やDDLを、条件変更テストのSAVEPOINTとROLLBACKの間へ挟まないようにします。

SPOOL OFF

PAF側は同じ検証IDで、入力した質問、PlaygroundのTool実行記録、最終回答のScreenshotを対応付けます。SQL Logだけで、PAFやRAGまで検証できたとは扱いません。

2) 事実と根拠を同じIDで追う

CQ-02ではGraph APIのEquipment ID・Stage IDと両端のLot所属の出所・参照ID、CQ-03では実TASK_ID・作成者・対象機材Relationの出所、CQ-04ではDocument ID・Version・経由ID・Relationの出所と、引用したPageまたはSectionを保存します。RAGの文章が正しくても、別機種や別Versionの根拠なら一致としません。

3) 言い換えと不足情報を試す(任意の追加検証)

この追加検証は、PAFの回答の再現性や不足情報への対応を評価する場合に実施します。本稿のPAF実行記録には含めていません。

同じ問いを「製造ロット」「Manufacturing Lot」「Lot」で言い換え、毎回新しい会話で試します。最初の回答を次の試行へ引き継がず、どのToolがどの引数で呼ばれたかを記録します。

未知ID、Document候補0件、根拠の取得失敗などでは、未確認の内容を追加しないか確認します。既存Select AI RAG Toolへ渡したDocument候補はSoftな指示なので、最終回答の引用元を別途照合します。

4) 正解表をAgentへ渡さない

本記事の期待結果、Repositoryのvalidation/、採点用ファイルは評価者用です。既存RAGのPDF配置先や文書取込対象へ追加しません。Agentには元の業務データ・文書と定義を渡し、評価者が別に保持した正解と比較します。

● 多層ガードレールの設定を整理

ここでは、本編で設定した権限、公開Object、許可Routine、Custom instructionsを整理します。この章で新たに実行するSQLや追加Nodeはありません。

PAF Agent BuilderにGuardrailという独立Nodeがあるわけではありません。

今回は複数Layerで制限します。

Layer ガードレール
Database LSF_AGENTへ最小権限だけを付与
NL2SQL Data Analysis Agentで選択した表/Viewだけを公開
Graph Static SQL+Bind変数のPackageだけを許可
PL/SQL Oracle PL/SQL Executorで3 ProcedureだけをAllowlist
Transaction read-only Graph API、Auto-commit OFF
RAG Agent policy(Soft)としてGraphのDocument ID候補外を採用しない。Databaseレベルの強制ではない
Agent Tool失敗時や0件時に推測で補完しない
Output Incident/Equipment/Lot/Stage/Task/Document IDを明示

Graph APIには件数上限、入力形式の検証、監査Logも追加できます。

Productionでは、Relation追加時の承認、Data Lineage、Role分離、Graph変更履歴、Document Version管理も必要です。

● 今回のAI Readyデータについて

今回の構成では、ファイルをLakehouseへ置くだけでなく、次の状態まで整備しています。

  • Incident、Equipment、Stage、Task、Documentに一意なIDがある
  • Manufacturing LotをCanonicalなRelationとして扱える
  • 概念、Relation、同義語、制約をDomain Ontologyで定義している
  • 外部表の物理形式をGraph用Viewで吸収している
  • RelationへSourceを持たせ、元Masterと補足データを区別できる
  • Document CatalogとPDF内のDocument IDが一致している
  • Taskから対象Equipmentと使用Documentをたどれる
  • Agentへ公開するSQL ObjectとPL/SQL Routineを限定している
  • 回答に使用したIDとDocumentを追跡できる

この状態にすると、構造化、半構造、非構造データをAI Agentから横断しやすくなります。

AI Readyとは、単にVector化した状態ではなく、意味、関係、権限、品質、根拠、追跡可能性まで整備された状態と考えると分かりやすいです。

Agentの回答イメージ(実回答の逐語転載ではなく、説明用の構成例)

● Agentの回答イメージ

ここまでで本編の構築と問い合わせ確認は完了です。以下は回答の構成を説明するための例です。冒頭と問い合わせ章の実回答画面に対応する検証結果は、直前の表へ記録しています。

以下は、数値・関係・文書根拠を一つの回答へ揃える場合の構成例です。実際には、初回は複合質問と追加質問の2段階、その後は単独質問から続く会話で統合回答を確認しました。下記はその実回答の逐語転載ではなく、説明用に整理した例です。

■ Databaseで確認できた事実

Incident ID: INC-2026-081
発生Stage: STG-WA / Waveform Arena
障害Equipment: EQ-NET-WA-01
Manufacturing Lot: NW-2603

2026-08-08の最大温度は91.755 C、最大Packet Lossは19.167%、
最低Fan RPMは120でした。
冷却ファン交換は2026-07-25予定でしたが未完了で、14日超過していました。

■ Knowledge Graphで確認できた関係

INC-2026-081
 → AFFECTS
EQ-NET-WA-01
 → BELONGS_TO_LOT
NW-2603
 ← BELONGS_TO_LOT
EQ-NET-OM-01
 → DEPLOYED_AT
STG-OM / Orbit Main Stage

同じPathで、EQ-NET-NG-01がSTG-NG / Neon Groove Stageに
配置されていることも確認しました。

この2台のLot Relationは、本記事で追加した合成デモデータです。
LOT_SOURCE: SYNTHETIC_DEMO
LOT_ASSERTION_STATUS: SYNTHETIC
LOT_SOURCE_REFERENCE_ID: BLOG-DEMO-LOT-001
起点EQ-NET-WA-01のLot所属はMASTER_CSV、
出所はequipment_master.csv#EQ-NET-WA-01です。

■ このIncidentへの対応として登録された未完了Task

- TASK-<実行時の自動採番ID> / CRITICAL / EQ-NET-OM-01 / OPEN
- TASK-<実行時の自動採番ID> / HIGH / EQ-NET-NG-01 / OPEN

上記の2 TaskはCREATED_BY_AGENT=ONTOLOGY_DEMO_SEEDの合成デモ行です。
対象機材Relationの出所はSYNTHETIC_DEMO / BLOG-DEMO-TASK-001です。

■ Documentの選定理由と根拠

IR-2026-081はINC-2026-081のDOCUMENTED_BYから取得しました。
出所はINCIDENT_CSV / incidents.csv#INC-2026-081です。
SB-NW-2603とOPS-NET-2.1は、障害EquipmentのLot NW-2603から
GOVERNED_BYで到達します。出所はSYNTHETIC_DEMO / BLOG-DEMO-DOC-001です。
さらに、上記2 TaskそれぞれのUSES_PROCEDUREからも到達します。
Task経由の出所はSYNTHETIC_DEMO / BLOG-DEMO-TASK-001です。
これらは文書を選んだ関係の根拠で、本文の引用箇所は別に確認します。

- IR-2026-081 v1.0
  EQ-NET-WA-01のFan RPM低下、温度・Packet Loss上昇、
  保守未完了が記録されています。

- SB-NW-2603 v1.1
  NW-2603を高負荷イベントで使用する場合、使用前に
  冷却ファン・制御基板を交換する必要があります。
  部品未着の場合は予備機へ置換します。

- OPS-NET-2.1 v2.1
  Critical条件では5分以内に予備Switchへ切り替え、
  復旧後も対象機器を再利用せず隔離します。

■ 推奨対応

1. EQ-NET-OM-01とEQ-NET-NG-01を開演構成から一時的に外す。
2. EQ-NET-OM-01のCRITICAL Taskを最優先、EQ-NET-NG-01のHIGH Taskを次点として実施する。
3. 交換できない場合は予備機へ置換する。
4. 交換後に60分の負荷試験を行い、Documentの基準を確認する。

同じManufacturing Lotであることは、2台に同じ障害が発生している
ことの証明ではありません。
今回はService Bulletinの対象候補として、予防点検を優先する判断です。

回答ではDatabase上の事実、Graph上の関係とTask、Documentの記載、Agentによる推奨を区別します。上の例と実行結果は区別して残します。引用Page/SectionやToolの実行記録は、取得・照合できたものだけを記録します。

■ 補足:追加検証と拡張

ここからは、本編の完成に追加で試す場合の検証・拡張と、別業務への応用をまとめます。比較Flowや厳密なKG-guided RAGは未実施・未実装の範囲です。Graph Studioの操作手順には、今回確認済みの結果と追加確認を区別して記載します。

● Graph追加前後を同じ条件で比較する(任意・未実施)

以下のSQL、比較用Profile、2つのFlowは、追加の比較検証を行う場合に使用します。本編の完成には不要です。

前回のNL2SQL+RAGと、今回のGraph追加後へ同じ質問・同じデータ・同じ評価基準を適用します。補足RelationをGraph側だけに与えると、検索方式の差ではなく入力情報の差を比較してしまいます。比較用NL2SQLにも同じCanonical Viewと関係データを公開し、文書検索も同じ既存RAGに揃えます。この比較は本編の複合回答を確認した後の任意検証です。

1) 比較用にも同じ概念とRelationを公開する

前回の構成をそのまま使うと、新たに追加したLot、Task、DocumentのRelationをNL2SQL側が参照できない場合があります。そこで、今回Graphの元にした6つのNode用Viewと8つのEdge用Viewを、比較用Select AI SQL ProfileのObject Listへ公開します。新しい比較用データは作成せず、同じCanonical Viewを使用します。

Owner SchemaのADB_USERで次を実行します。既に付与した3 Viewも含めて列挙しています。同じ権限の再付与は、データを書き換えません。

GRANT SELECT ON V_KG_INCIDENTS TO LSF_AGENT;
GRANT SELECT ON V_KG_EQUIPMENT TO LSF_AGENT;
GRANT SELECT ON V_KG_STAGES TO LSF_AGENT;
GRANT SELECT ON V_KG_LOTS TO LSF_AGENT;
GRANT SELECT ON V_KG_TASKS TO LSF_AGENT;
GRANT SELECT ON V_KG_DOCUMENTS TO LSF_AGENT;

GRANT SELECT ON V_KG_INCIDENT_AFFECTS TO LSF_AGENT;
GRANT SELECT ON V_KG_EQUIPMENT_LOT TO LSF_AGENT;
GRANT SELECT ON V_KG_EQUIPMENT_STAGE TO LSF_AGENT;
GRANT SELECT ON V_KG_INCIDENT_DOCUMENT TO LSF_AGENT;
GRANT SELECT ON V_KG_INCIDENT_TASK TO LSF_AGENT;
GRANT SELECT ON V_KG_TASK_EQUIPMENT TO LSF_AGENT;
GRANT SELECT ON V_KG_TASK_DOCUMENT TO LSF_AGENT;
GRANT SELECT ON V_KG_LOT_DOCUMENT TO LSF_AGENT;

Graphの元Table、補足Relationの登録Table、Graphの直接READ権限を追加する必要はありません。今回の比較対象は、同じStage運営Scopeと同じCanonical Lotを参照します。

2) Relationの意味と結合する列をCommentに残す

Owner Schemaで次を実行します。元のCommentを補足し、NL2SQLでも関係名・両端のID・出所を確認できる状態にします。

COMMENT ON TABLE V_KG_INCIDENTS IS
  'Stage運営ScopeのIncident。キーINCIDENT_ID。GateのIncidentは対象外';
COMMENT ON TABLE V_KG_EQUIPMENT IS
  'Stage配置Equipment。キーEQUIPMENT_ID。MANUFACTURING_LOTは出所付き合成補足を反映したCanonical Lot';
COMMENT ON TABLE V_KG_STAGES IS
  'Stage。キーSTAGE_ID。Equipmentの配置先';
COMMENT ON TABLE V_KG_LOTS IS
  '製造Lot。キーMANUFACTURING_LOT。同じLotへの所属は故障や同一原因の証明ではない';
COMMENT ON TABLE V_KG_TASKS IS
  'Stage ScopeのIncidentに登録された改善Task。キーTASK_ID。STATUSがOPENまたはIN_PROGRESSなら未完了';
COMMENT ON TABLE V_KG_DOCUMENTS IS
  'Document Catalog。キーDOCUMENT_ID。VERSIONは文書の版。本文は別途既存Select AI RAGから取得する';

COMMENT ON TABLE V_KG_INCIDENT_AFFECTS IS
  'AFFECTS: Incident.INCIDENT_IDからEquipment.EQUIPMENT_IDへの関係。列INCIDENT_IDとEQUIPMENT_IDで結合';
COMMENT ON TABLE V_KG_EQUIPMENT_LOT IS
  'BELONGS_TO_LOT: Equipment.EQUIPMENT_IDからLot.MANUFACTURING_LOTへの所属。LOT_SOURCE等で出所を確認';
COMMENT ON TABLE V_KG_EQUIPMENT_STAGE IS
  'DEPLOYED_AT: Equipment.EQUIPMENT_IDからStage.STAGE_IDへの配置関係';
COMMENT ON TABLE V_KG_INCIDENT_DOCUMENT IS
  'DOCUMENTED_BY: Incident.INCIDENT_IDからDocument.DOCUMENT_IDへの直接の報告文書参照';
COMMENT ON TABLE V_KG_INCIDENT_TASK IS
  'CREATES: Incident.INCIDENT_IDからTask.TASK_IDへの登録関係。このIncidentに登録されたTaskのみを表す';
COMMENT ON TABLE V_KG_TASK_EQUIPMENT IS
  'TARGETS: Task.TASK_IDからEquipment.EQUIPMENT_IDへの対象関係。SOURCE_TYPEとSOURCE_REFERENCE_IDは出所';
COMMENT ON TABLE V_KG_TASK_DOCUMENT IS
  'USES_PROCEDURE: Task.TASK_IDからDocument.DOCUMENT_IDへの使用文書関係。SOURCE_TYPEとSOURCE_REFERENCE_IDは出所';
COMMENT ON TABLE V_KG_LOT_DOCUMENT IS
  'GOVERNED_BY: Lot.MANUFACTURING_LOTからDocument.DOCUMENT_IDへの適用文書関係。RELATION_TYPEは文書との関係種別';

COMMENT ON COLUMN V_KG_EQUIPMENT_LOT.LOT_SOURCE IS
  'Lot所属の出所。MASTER_CSVは元Master、SYNTHETIC_DEMOは合成デモRelation';
COMMENT ON COLUMN V_KG_EQUIPMENT_LOT.LOT_ASSERTION_STATUS IS
  'SOURCE_RECORDは元データ、SYNTHETICは合成データ。推論による故障判定ではない';
COMMENT ON COLUMN V_KG_EQUIPMENT_LOT.LOT_SOURCE_REFERENCE_ID IS
  'Lot所属の出所を追跡する参照ID。値を回答へ残す';
COMMENT ON COLUMN V_KG_LOT_DOCUMENT.RELATION_TYPE IS
  'Lotと文書の関係種別。SERVICE_BULLETINまたはOPERATING_PROCEDUREなど';
COMMENT ON COLUMN V_KG_DOCUMENTS.VERSION IS
  'Documentの版。DOCUMENT_IDと合わせて既存Select AI RAGの引用元と照合する';

ViewにはGraph定義で指定したKeyやEndpointがDatabaseのPK/FK制約として自動追加されるわけではありません。比較用Profileではcomments: trueを指定し、関係の定義をManagerのInstructionsにも明記します。

3) 比較用Select AI SQL ProfileとToolを作成する

今回のData Analysis Agent画面はViews / Tables (select one)で、複数Objectを選べません。本編のLSF Structured Data AnalystはV_KG_INCIDENT_FACTSを1つ選んだまま両構成で共通に使い、RelationのJOIN用には別のSelect AI SQL Profileを用意します。

既存LSF2026_SQL_PROFILE_PAFのObject Listは今回の14 Viewを含まないため、そのままでは今回の比較に使えません。ADB_USERで次を実行し、動作確認済みのOCI設定を読み取って新しい比較専用Profileを作成します。既存SQL/RAG Profileは変更しません。

SET SERVEROUTPUT ON
DECLARE
  L_ATTRS   JSON_OBJECT_T := JSON_OBJECT_T();
  L_OBJECTS JSON_ARRAY_T;
  L_COUNT   PLS_INTEGER;
BEGIN
  SELECT COUNT(*) INTO L_COUNT
  FROM USER_CLOUD_AI_PROFILES
  WHERE PROFILE_NAME = 'LSF2026_KG_COMPARE_SQL';
  IF L_COUNT > 0 THEN
    RAISE_APPLICATION_ERROR(-20001,
      'LSF2026_KG_COMPARE_SQL already exists; review its attributes before reuse');
  END IF;

  FOR R IN (
    SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
    FROM USER_CLOUD_AI_PROFILE_ATTRIBUTES
    WHERE PROFILE_NAME = 'LSF2026_SQL_PROFILE_PAF'
      AND ATTRIBUTE_NAME IN (
        'provider', 'credential_name', 'oci_compartment_id', 'region', 'model'
      )
  ) LOOP
    L_ATTRS.PUT(R.ATTRIBUTE_NAME, R.ATTRIBUTE_VALUE);
  END LOOP;

  IF L_ATTRS.GET_SIZE <> 5 OR L_ATTRS.GET_STRING('provider') <> 'oci' THEN
    RAISE_APPLICATION_ERROR(-20002,
      'Check the five OCI attributes of LSF2026_SQL_PROFILE_PAF');
  END IF;

  L_OBJECTS := JSON_ARRAY_T.PARSE(q'~[
    {"owner":"ADB_USER","name":"V_KG_INCIDENTS"},
    {"owner":"ADB_USER","name":"V_KG_EQUIPMENT"},
    {"owner":"ADB_USER","name":"V_KG_STAGES"},
    {"owner":"ADB_USER","name":"V_KG_LOTS"},
    {"owner":"ADB_USER","name":"V_KG_TASKS"},
    {"owner":"ADB_USER","name":"V_KG_DOCUMENTS"},
    {"owner":"ADB_USER","name":"V_KG_INCIDENT_AFFECTS"},
    {"owner":"ADB_USER","name":"V_KG_EQUIPMENT_LOT"},
    {"owner":"ADB_USER","name":"V_KG_EQUIPMENT_STAGE"},
    {"owner":"ADB_USER","name":"V_KG_INCIDENT_DOCUMENT"},
    {"owner":"ADB_USER","name":"V_KG_INCIDENT_TASK"},
    {"owner":"ADB_USER","name":"V_KG_TASK_EQUIPMENT"},
    {"owner":"ADB_USER","name":"V_KG_TASK_DOCUMENT"},
    {"owner":"ADB_USER","name":"V_KG_LOT_DOCUMENT"}
  ]~');
  L_ATTRS.PUT('object_list', L_OBJECTS);
  L_ATTRS.PUT('object_list_mode', 'all');
  L_ATTRS.PUT('enforce_object_list', TRUE);
  L_ATTRS.PUT('comments', TRUE);
  L_ATTRS.PUT('temperature', 0);

  DBMS_CLOUD_AI.CREATE_PROFILE(
    profile_name => 'LSF2026_KG_COMPARE_SQL',
    attributes => L_ATTRS.TO_CLOB,
    status => 'enabled',
    description => 'Canonical relation SQL comparison for LSF2026'
  );
  DBMS_OUTPUT.PUT_LINE('LSF2026_KG_COMPARE_SQL created');
END;
/

SELECT PROFILE_NAME, STATUS
FROM USER_CLOUD_AI_PROFILES
WHERE PROFILE_NAME = 'LSF2026_KG_COMPARE_SQL';

SELECT ATTRIBUTE_NAME, ATTRIBUTE_VALUE
FROM USER_CLOUD_AI_PROFILE_ATTRIBUTES
WHERE PROFILE_NAME = 'LSF2026_KG_COMPARE_SQL'
  AND ATTRIBUTE_NAME IN ('object_list', 'object_list_mode',
                        'enforce_object_list', 'comments')
ORDER BY ATTRIBUTE_NAME;

これは前回記事のOCI Profile構成を前提にした作成例です。再実行で既存Profileを削除・上書きしないよう、同名Profileがあれば停止します。Profileを作成しただけでは、比較を実行したことにはなりません。

PAFのSelect AI FrameworkでOwner接続をRefreshし、ProfileのProvider・Model・Credentialと14 ViewのObject Listを確認します。Select AIの単体FlowでこのProfileを選択し、最初はShow SQLで「INC-2026-081の障害機材と同一Manufacturing Lotの別機材を、配置Stageとともに示す」SQLを確認します。正しいView・結合列・起点除外を確認したらRun SQLで実行し、前述の固定JOINの結果と照合します。

比較Flowには、もう1つのSelect AI Bridgeを追加します。

項目 設定値
Mode Profile
Database LSF2026 SELECT AI OWNER
Profile LSF2026_KG_COMPARE_SQL
AI Action Run SQL

この比較用Bridgeも、Tool名と説明はProfile情報から生成されます。以下の「Relation SQL Tool」は説明上の呼称です。Agent InstructionsではProfile名とActionを指定して識別します。

本編と同じ業務定義を、比較用ManagerのInstructionsへ加えます。採点用の正解IDや期待件数はToolへ登録しません。

Manufacturing Lot、Lot、製造ロットはMANUFACTURING_LOTを表す。
同一Lotの別Equipmentは起点Incidentの障害Equipmentを除外する。
未完了Taskは起点Incidentに登録され、STATUSがOPENまたはIN_PROGRESSのTaskとする。

V_KG_INCIDENT_AFFECTS: AFFECTS、INCIDENT_IDとEQUIPMENT_ID。
V_KG_EQUIPMENT_LOT: BELONGS_TO_LOT、EQUIPMENT_IDとMANUFACTURING_LOT。
V_KG_EQUIPMENT_STAGE: DEPLOYED_AT、EQUIPMENT_IDとSTAGE_ID。
V_KG_INCIDENT_DOCUMENT: DOCUMENTED_BY、INCIDENT_IDとDOCUMENT_ID。
V_KG_INCIDENT_TASK: CREATES、INCIDENT_IDとTASK_ID。
V_KG_TASK_EQUIPMENT: TARGETS、TASK_IDとEQUIPMENT_ID。
V_KG_TASK_DOCUMENT: USES_PROCEDURE、TASK_IDとDOCUMENT_ID。
V_KG_LOT_DOCUMENT: GOVERNED_BY、MANUFACTURING_LOTとDOCUMENT_ID。

Document候補はIncident直接、障害EquipmentのLot経由、未完了Task経由の3経路で取得する。
DOCUMENT_ID、VERSION、FILE_NAME、関係名の順序、経由ID、出所を返す。
Task経由の文書はTaskの対象機材と照合する。複数の到達経路を残す。
ID・出所・未登録のRelationを推測で補わず、同じLotから故障や原因を断定しない。

4) 同じFlowから比較用の2構成を作る

作成済みのLSF2026 ONTOLOGY IMPACT AGENTを基に、次の2つの検証用Flowを用意します。両方に同じLSF Structured Data Analystと同じSelect AI BridgeのRAG Tool(LSF2026_RAG_PROFILE_PAF/Narrate)を接続します。Relation SQL ToolはAだけ、Graph APIのExecutorはBだけへ接続します。

設定 A: 同じRelationを参照するNL2SQL+RAG B: 固定Graph API+NL2SQL+RAG
関係の取得 Canonical Relation SQL ToolがSQLを生成 3つのGraph APIを実行
数値・状態の確認 同じLSF Structured Data Analyst 同じLSF Structured Data Analyst
Oracle PL/SQL Executor 接続しない 既存の3 Procedureだけを接続
文書検索 SQL結果のDocument ID・Versionを同じ既存Select AI RAG Toolへ渡す API結果のDocument ID・Versionを同じ既存Select AI RAG Toolへ渡す
文書候補外の扱い 共通のSoftな候補制限 共通のSoftな候補制限

共通の数値分析はLSF_AGENT接続、共通のRAGはADB_USER接続です。AのRelation SQL ToolはProfileを所有するADB_USER接続、BのGraph APIはLSF_AGENT接続なので、Database Account自体の権限が同一の比較ではありません。AではProfileのObject Listを14 Viewへ限定し、Graph APIのExecutorを接続しません。実行Traceで使用ObjectとToolを確認します。先ほどのLSF_AGENTへのView GrantはSQLでの確認にも使用できますが、Ownerで実行する比較Profileのために必要なGrantではありません。

AのAgent Instructionsでは、既存の3つのGraph API呼出を、次の指示へ置き換えます。Toolの選択以外の業務定義、回答形式、文書候補外・0件・Tool失敗時の扱いはBと揃えます。

Incident、Equipment、Manufacturing Lot、Stage、Task、Documentの
関係にはLSF2026_KG_COMPARE_SQLをRun SQLで呼び出すSelect AI BridgeのToolを使用する。
起点Incident IDを確定し、同一Lotの別Equipmentと配置Stage、
このIncidentに登録された未完了Taskと対象Equipment、
3経路の関連DocumentをSQLで取得する。
SQLが返した対象ID、Document ID・Version、到達経路、出所を使用する。
Document ID+Versionを検索候補として重複排除し、同じ既存Select AI RAG Toolへ渡す。
説明用の到達経路は保持する。
関連Documentが0件なら、全Document検索へ自動Fallbackせず、
「明示的な関連Documentが登録されていない」と回答する。

この2構成の差には、Graphの検索表現に加え、問い合わせを事前に固定した効果も含まれます。「Graphというデータ表現だけの効果」として解釈しません。

5) 同じ入力で実行し、途中結果まで比較する

中心の一問、言い換え、Task完了などの条件変更を、両構成で同じ順に試します。各試行は新しい会話で開始し、ManagerのLLM・設定値、共通のRAG Profile・Vector Index、Embedding Model、検索件数、元データと補足Relationの状態を揃えます。片方を実行している間にTaskやRelationを変更せず、次の条件へ進む前に両方を実行します。

最終回答に加え、生成SQL/API名と引数、Equipment・Stage・TaskのID、Document ID・Version、経由ID・出所、RAGの引用箇所を保存します。文章の言い回しではなく、まずこれらの一致と不足を比較します。Document候補はID+Versionの集合、説明用経路は経由IDを含む行として別々に照合します。

Graphの検索表現自体を比べたい場合は、前述の「接続前にGraphと通常のJOINを比較する」を使い、固定したJOIN Queryと固定したGRAPH_TABLE Queryの結果を比較します。この節で掲載した単体比較の対象は、同一Lotの別機材と配置Stageです。Task・Documentまで含む固定JOIN APIとのAgent比較を行う場合は、同じ引数・返却項目・エラー動作のRoutineを別途用意してから評価します。本記事では、その追加APIを実装・計測済みとは扱いません。

評価するもの 記録方法
事実の正しさ 対象ID、数値、Statusの一致・不一致
関係の説明 Incidentから対象機材・Documentまでの経路を説明できるか
文書の根拠 引用した内容が、そのDocumentの該当箇所に存在するか
条件変更への追従 完了Taskを除外するなど、定義した条件が回答へ反映されるか
不明への対応 未取得・未登録を正しく示し、確定事項へ変えないか
運用の手間 質問追加時のSQL/Prompt修正、Relation追加・訂正に必要な作業
実行負荷 同じ試行条件での応答時間とTool呼出回数。件数と試行回数も併記

この比較を実行するまでは、「Graphにより精度が何%向上した」「必ず高速になった」とは記載しません。明示的なRelation名とIDを使って説明できることと、実測した回答品質・性能は別に記録します。

● 機能と表現の違いを整理

同じRelationをSQLへ公開した場合、通常のJOINでも対象機材や関連Documentを選び、その経路を返せます。今回の比較では、扱える情報と問い合わせの作り方を分けて整理します。

項目 同じRelationを参照するNL2SQL+RAG 固定Graph API+NL2SQL+RAG
Sensor集計・Databaseの状態確認 NL2SQLで確認 NL2SQLで確認
同一Lotの別機材とStage Relation ViewをJOIN Graph Patternで関係を探索
起点Incidentの未完了Task Incident–Task–EquipmentをJOINし、Statusで絞る CREATES→TARGETSを探索し、同じStatusで絞る
関連Documentの選定 同じ3経路をJOINし、候補ID・Versionを取得 同じ3経路をGraph APIで取得
Documentを選んだ理由 SQLの返却項目に関係名・経由ID・出所を含める APIの返却項目に関係名・経由ID・出所を含める
問い合わせの作成 質問ごとにLLMがSQLを生成 開発者が事前定義した3つのAPIを選択
問いの変更 生成SQLの妥当性を評価する 固定経路で答えられる範囲を確認し、必要に応じAPIを拡張する
PDF本文の根拠 同じ既存Select AI RAG Toolで取得して照合 同じ既存Select AI RAG Toolで取得して照合

GraphではRelationの順序をPatternとして記述し、その検索を業務APIとして公開できます。JOINでも同じ関係を検索できるため、「何が可能か」に加えて、業務の関係を記述・説明・変更しやすいかを実際に確認します。回答品質、性能、修正の手間は、この比較を実行した結果として記録します。

● 補足:厳密なKG-guided RAGへ拡張

この節は本編の完了に必須ではない、未実装の拡張設計例です。Vector検索対象をDocument ID+Versionへ確実に限定する場合は、独自Chunk Tableを作成します。Select AIが自動生成したLSF2026_DOC_VECTOR$VECTABに、以下の独自Columnがあるとは仮定しません。既存RAGの再利用だけでは、このHard Filterは実装されません。

少なくとも、次のMetadataを保持します。

Column 内容
CHUNK_ID Chunkの一意ID
DOCUMENT_ID Knowledge Graphと共通のDocument ID
DOCUMENT_VERSION GraphのDocument Version
PAGE_NO Page番号
SECTION_PATH 章・節
CHUNK_TEXT Chunk本文
EMBEDDING ChunkのVector

Graphが返したDocument IDとChunk TableをJOINしてからVector Distanceを計算します。次は、GOVERNED_BYでLotに適用されるDocumentだけを対象にした最小例です。

WITH RELATED_DOCS AS (
  SELECT DISTINCT
    DOCUMENT_ID,
    DOCUMENT_VERSION
  FROM GRAPH_TABLE (
    LSF_FESTIVAL_OPS_GRAPH
    MATCH
      (I IS INCIDENT)
        -[A IS AFFECTS]->
      (E IS EQUIPMENT)
        -[B IS BELONGS_TO_LOT]->
      (L IS MANUFACTURING_LOT)
        -[G IS GOVERNED_BY]->
      (D IS DOCUMENT)
    WHERE I.INCIDENT_ID = :P_INCIDENT_ID
    COLUMNS (
      D.DOCUMENT_ID AS DOCUMENT_ID,
      D.VERSION     AS DOCUMENT_VERSION
    )
  )
)
SELECT
  C.DOCUMENT_ID,
  C.DOCUMENT_VERSION,
  C.PAGE_NO,
  C.SECTION_PATH,
  C.CHUNK_TEXT,
  VECTOR_DISTANCE(C.EMBEDDING, :P_QUERY_VECTOR, COSINE) AS DISTANCE
FROM LSF_DOCUMENT_CHUNKS C
JOIN RELATED_DOCS R
  ON R.DOCUMENT_ID = C.DOCUMENT_ID
 AND R.DOCUMENT_VERSION = C.DOCUMENT_VERSION
ORDER BY DISTANCE
FETCH EXACT FIRST 8 ROWS ONLY;

このSQLでは、LLMがDocument IDを変更する余地がなく、Lotに明示的に関連付けられたDocument ChunkだけをVector検索できます。

Incident ReportとTask専用Procedureまで含む完全なAllowlistにする場合は、前節のGET_RELATED_DOCUMENTSと同じく、DOCUMENTED_BY、GOVERNED_BY、CREATES → USES_PROCEDUREの3 PathをUNION ALLし、重複を除いたDOCUMENT_ID/VERSIONをRELATED_DOCSにします。最小例の結果を、Incidentの全関連Documentとは扱いません。

実装する場合は、次を1つの許可済みPL/SQL Functionへまとめます。

LSF_KG_API.GET_KG_GUIDED_EVIDENCE
  1. GRAPH_TABLEの3 PathをUNIONし、関連Document IDを取得
  2. Document ID+VersionでChunkをHard Filter
  3. 同じEmbedding Modelで質問をVector化
  4. VECTOR_DISTANCEでTop-Kを取得
  5. Graph Path、Page、Section、Chunk、ScoreをJSONで返す

既存のSelect AI BridgeのRAG Toolは、一般的なマニュアル質問や従来RAGとの比較に使用できます。独自Chunk Table、Embedding生成、追加APIの実装は、この拡張を選ぶ場合の別作業です。

● 補足:RDF/OWLと今回の実装の違い

本編では軽量Domain OntologyをSQL Property Graphへマッピングしました。ここでは、RDF/OWLによる意味表現・推論との違いを整理します。以下の推論機能を本編で実行したという意味ではありません。

RDF:主語・述語・目的語で表す Graph.jpg
OWL :公理と推論.jpg

CREATE PROPERTY GRAPHを実行しただけで、RDF/OWLのOntology推論が実装されるわけではありません。

本稿のSQL Property Graph構成は、RDF/OWLの形式意味論に基づくクラス階層やowl:sameAsなどの推論を行いません。URI文字列をProperty GraphのIDや属性として保持することと、RDFの識別モデルやOWLの公理を使って推論することは別です。

本記事で実行するのは、明示的に登録したNodeとEdgeに対するGraph Pattern Matchingです。

より正確には、次の実装です。

音楽フェス運営の軽量Domain Ontologyを設計し、その概念設計をSQL Property Graph型Knowledge Graphへマッピングする

RDF/OWLとOracleがサポートするOWL Rulebaseによる推論は、次回以降の拡張対象とします。

Oracle AI Databaseでは、RDF/OWLに基づくOntologyの格納・問合せと、対応するOWL Rulebaseを使った推論をすでに利用できます。本記事はSQL Property Graphで明示的な関係を検索する検証に範囲を絞っているため、RDF Graphの作成やOWL推論は実行しません。クラス階層や推論で導かれる関係まで試す手順は、別の検証記事で扱います。

SQL JOIN • Property Graph • RDF/OWLの使い分け.jpg

● 補足:Oracleの機能と今回の実装の位置付け

OracleのGraphやAIの機能が進化していく中で、今回のハンズオンはどの部分を実践するのでしょうか。

今回は、業務の問いから意味と関係を決め、SQL Property Graphを作成し、決めた探索をAPIとしてPAFから呼び出すところを一通り試します。このとき作る問いと期待結果は、Graphの作成支援や自然言語によるGraph検索を試すときにも、正しく動いたかを確認する基準になります。

・ 現在の機能と、支援する工程

2026年10月2日時点で公開されている公式情報をもとに、今回の構成に関係する機能を整理します。

機能・構成 主に担当する工程 本記事との関係
SQL Property Graph+固定Graph API 定義した関係を探索し、必要なID・経路・出所を返す 今回作成する本編の構成
Select AI for Property Graphs 自然言語の質問から、Graphを検索するSQLを生成する 作成したGraphを使う、次の比較対象
Graph StudioのAIによるGraphモデル作成支援 選択した表を分析し、Graph定義を提案する 本稿で決めた概念・関係と、提案された定義を比較できる
RDF/OWLと対応する推論機能 概念階層やプロパティの意味を形式化し、対応する規則で推論する 形式的な意味表現・推論へ拡張する場合の選択肢

1) Select AI for Property Graphs:質問からGraph問い合わせを作る

2026年3月10日のリリースノートでは、Select AIがSQL Property Graph向けのNL2SQLに対応したことが公開されています。AI Profileの対象にGraphを指定すると、Graph定義の情報を使い、GRAPH_TABLEによる問い合わせを生成します。

本稿の固定APIでは、開発者が「Incidentから機材・Lotをたどる」といった探索をSQLで定義します。Select AIでは、その問い合わせの作成をLLMが担当します。同じ質問とデータで、生成された経路・条件・返却IDが一致するかを比較できます。

利用時には、SQL Property Graph用のAI Profileを、表/View向けのProfileとは別に用意します。現在の公式仕様では、Graphと表・Viewなどの別種のObjectを同じProfileに混在させるとエラーになります。また、この機能の対象はSQL Property Graphであり、PGQL Property Graphは対象ではありません。

したがって、既存の表向けProfileへ今回のGraphを追加するだけで、すべての問い合わせが切り替わる構成ではありません。本稿のPAF Flowは固定APIを使って完成させ、Select AIによるGraph問い合わせとPAFへの接続は、別の検証項目として扱います。

2) Graph StudioのAIによるGraphモデル作成支援:表から定義を提案する

2026年3月23日のOracle公式記事では、Graph Studioで選択した表から、生成AIがGraph定義を提案する機能が紹介されています。提案を確認・修正してGraph作成へ進めます。

本稿で人が整理する「何をNodeにし、何を関係にするか」という作業を支援する入口になります。提案を評価するときは、例えば次を確認します。

  • EquipmentとManufacturing Lotが、別の概念として表現されているか。
  • 「同じ型番」と「同じ製造Lot」が区別されているか。
  • Incidentへの対応Taskと、機材に登録されたすべてのTaskが混同されていないか。
  • Documentへの関係に、本文書を適用する根拠があるか。

列名やデータのつながりからGraphを提案できることと、その関係が業務上正しいことは、それぞれ確認します。今回の概念表・関係表・Competency Questionsが、その確認に使えます。

3) RDF/OWL:概念の意味と、そこから導けることを扱う

RDF/OWLを使う構成では、例えば「Network SwitchはEquipmentの一種」のような概念階層や、関係の特性を形式化します。Oracle AI Databaseは、対応する語彙・Rulebaseの範囲で推論機能を提供しています。利用する公理がどのRulebaseで対応しているかは、公式仕様と照合します。

今回のSQL Property Graphは、登録された関係をたどって対象を探します。CREATE PROPERTY GRAPHだけで、OWLのクラス階層や公理に基づく推論が有効になるわけではありません。「登録された関係をたどること」と「定義した意味から新しい関係を論理的に導くこと」を区別します。

・ 今回、固定Graph APIから試す理由

今回の狙いは、一つの問いを、意味の定義からPAFの回答まで追跡できるようにすることです。そのため、入力・探索条件・返却内容をSQLで明示する固定APIを使います。

確認したいこと 本稿で確かめる方法
どの範囲を調べたか 起点Incidentと、対象となるRelationを確認する
同じ条件で同じ対象を返すか Graph Queryと通常のJOINで、返却IDの差分を比較する
なぜこの文書を選んだか Document ID・Versionと、途中のID・Relation・出所を確認する
条件の変更が回答へ反映されるか Taskの完了やDocument Relationの変更後に再実行する
未確認のことを言い切っていないか 0件・不明ID・Tool失敗時の挙動を確認する

探索を固定しても、PAFのTool選択や回答文の生成にはLLMが関わります。API単体の結果と、PAFがその結果をどう使ったかを分けて記録します。

こうして一問を再現できると、次にSelect AIへ同じ質問を渡したときも、生成SQLの結果を比較する基準があります。Graphモデル作成支援を試すときも、同じCompetency Questionsに答えられるかを確認できます。

・ 新機能を試すときも、この一問から広げる

今回のハンズオンを終えたら、次の順に広げられます。以下は拡張時の検証方針で、本稿で実行済みの手順ではありません。

次に試すこと 再利用するもの 新たに確認すること
Select AIでGraphを自然言語検索する 今回のSQL Property Graph、質問、期待ID 生成SQLの経路・条件・結果、質問の言い換えへの応答
AIの提案でGraphモデルを作る 元データ、概念・関係の定義、Competency Questions 提案されたNode・Edgeの業務上の意味、必要な修正
RDF/OWLの推論を追加する 業務の問い、用語定義、元データのIDと出所 URIと公理へのマッピング、Rulebase、期待される推論結果

RDF/OWLへ進む場合には、表現形式と推論のための定義を追加します。SQL Property Graphをそのまま使うだけで推論へ切り替わる、という意味ではありません。

Oracle AI World 2026は2026年10月25〜28日の開催予定です。本稿の情報確認日は10月2日のため、ここでは公開済みの機能を扱っています。今後発表される機能も、「意味を定義する」「データから関係を作る」「問い合わせる」「根拠を示す」のどの工程を支援するかに注目すると、今回の構成へどう取り入れるかを考えやすくなります。

まずは、自分で決めた意味と関係を使って、一つのIncidentから別Stageの機材、対応Task、根拠文書までつながるところを試してみます。新しい支援機能を使うときにも、ここで確認した一問が出発点になります。

● 補足:この手順を別の業務へ応用する

⑦ この考え方、自分の業務にも使える?.jpg

ここからは、架空の食品会社を例にした応用の設計案です。食品会社のPoCを実行した結果ではありません。音楽フェスのSQLをそのまま流用するのではなく、「問い→概念・関係→根拠→検索→評価」という設計手順を再利用します。

・ 品質問題の影響範囲を調べる

音楽フェスで確認した「同じLotの別Equipmentはどこにあるか」は、食品会社では「同じ製造Lotの製品はどこへ出荷されたか」という問いへ応用できます。

設計項目 音楽フェスの例 食品会社の設計例
起点 Incident 品質問い合わせ
追跡する対象 Equipment 製造Lotとその出荷明細
所在の確認 EquipmentのStage配置 出荷先と出荷日
対応 点検Task 調査Task
根拠 Service Bulletin、点検手順 検査記録、品質手順書
結論の限界 同じLotでも故障とは断定しない 同じLotでも全品不良とは断定しない

ここでは、商品マスター上の「商品」と、実際に製造した「製造Lot」を別の概念にします。同じ商品でもLotが違い、一つの出荷明細に複数Lotが含まれるなら、その内訳を表すRelationも必要です。

名前が似た列をJOINする前に、何を追跡したいのか、どの単位のIDなのかを確認することがOntology設計の入口になります。

・ 後継者候補を根拠付きで提案する

「誰が適任か」を検証可能な問いにするには、まず「どの役割の後継者か」「何をもって適任とするか」を決めます。以下は候補比較を支援する設計であり、人事判断を自動確定するものではありません。

1) 役割と評価条件を先に決める

例えば「食品事業責任者の後継者候補を比較する」と対象を限定し、必要な職務経験、必須条件、望ましい条件、評価基準日を定義します。重み付けを使う場合は、その値と理由も合意します。

2) 人物と評価を分けてモデル化する

概念 保持する内容 関係の例
Person 候補者ID 職務経験を持つ
Role 後継者を必要とする役割 要件を持つ
Requirement 必須・望ましい条件、判定方法 役割に適用される
Experience 職務、実績、期間 人物に属し、根拠文書で裏付けられる
Evidence 根拠Document ID、Version、該当箇所 経験・実績を裏付ける
Assessment 要件ごとの判定、基準Version、評価日 人物と要件を結び付ける

最初から「Aさんは最適な後継者」というRelationを正解として登録すると、評価したい判断を入力データに埋め込んでしまいます。事実としての職務経験と、評価条件に基づく判定を分けます。

3) 満たす・満たさない・不明を区別する

経歴に記載がない場合は「不明」であり、「経験がない」とは限りません。候補ごとに、満たす条件、満たさない条件、追加確認が必要な条件と、その根拠を出します。

ランキングを付ける場合も、それは明示した評価基準による計算結果です。OntologyやGraphが人物の適任性を自動的に保証するわけではありません。

4) 正解と評価条件を用意する

合成データで「全必須条件を満たす候補」「必須条件を満たさない候補」「根拠が不足する候補」を用意します。期待する判定と根拠IDを別の評価用ファイルへ記録し、Agentの検索対象には含めません。

職務経験や評価基準を変更したとき、候補比較の結果と説明が意図どおり変わるかまで検証します。最終的な選任は権限を持つ担当者が行います。

・ 売上低迷の要因と打ち手を整理する

「売上低迷の原因は何か」も、売上の定義、比較期間、対象商品、販売チャネルを決めることで検証しやすくなります。

検索・設計 担当する確認 出力の位置付け
NL2SQL 商品・チャネル別の売上、数量、単価の変化 集計で確認した事実
Knowledge Graph 商品、製造Lot、在庫、出荷、販促、取引先の関係 調査対象のつながり
RAG 営業報告、欠品報告、販促計画の記載 文書に書かれた根拠
評価ルール 比較期間、売上計上基準、異常判定条件 合意した判断基準
Agentの提案 追加調査や対応案 根拠付きの仮説・提案

例えば「売上減少額の大きいチャネルと商品を特定し、同じ期間の欠品記録を示す」なら、集計値、関係、根拠文書の正解を用意できます。

一方、「欠品と売上減少が同時に発生した」だけでは、原因の断定にはなりません。季節性、価格変更、販促、販売日数などの別要因も確認し、数量・単価による売上差の分解と因果関係の説明を区別します。

● 補足:アカウントチームが説明・PoCで使う手順

・ 一つの問いから、関係と対応の根拠を見せる

説明の最初は、冒頭と同じ「Waveform Arenaで障害が起きた。ほかのStageも確認すべき?」という問いを示します。その後、次の順に画面を見せると、Ontology、Graph、NL2SQL、RAGの役割を具体例で説明できます。

これは構築後に成果を紹介するための表示順です。冒頭には同じLotの別機材へ至る2経路の実画面を、末尾の補足章にはGraph Studioの全体図と操作手順を掲載しています。保存済みの図とPAFの回答を並べて説明できます。自分の環境で表示する場合は、補足章の手順を実施します。

順序 見せる画面・結果 指し示す箇所と説明
1 NL2SQLの回答画面 「Waveform Arenaの障害機材で、温度・Packet Loss・Fan RPMを確認できました。では、ほかに確認すべき機材はあるでしょうか?」と問いを置く
2 Graph StudioのIncident周辺図、またはGraphの回答画面 EQ-NET-WA-01からNW-2603を介し、EQ-NET-OM-01/STG-OMとEQ-NET-NG-01/STG-NGへたどる。「同じLotという関係から、この2台を確認対象にできます」と説明する
3 複合回答の未完了Taskと文書条件 今回のTASK-121/CRITICAL、TASK-122/HIGHと、それぞれの対象機材を示す。続けてSBの交換条件とOPSの運用手順を示し、「登録Taskの優先度と文書条件を根拠に、開演前の対応を検討できます」と説明する
4 SQL結果・Graph経路・PDFのSources 回答の機材ID、数値、Task、文書IDを元の結果と照合する。Graphで文書を選んだ理由と、PDFに書かれた対応条件を対応付けて見せる

Taskコードは今回の実行環境での値です。別環境では、実際に採番されたTaskを示します。別機材2台のLot関係と専用Taskは合成デモデータであることも、対象を示す際に説明します。

最後に、最初の問いへ戻ります。**「確認すべき別機材が分かり、その機材への登録Taskと、対応条件の根拠までたどれた」**ことが、本デモで示す成果です。ほかの機材の故障や安全性を確定するには、それぞれのSensorや点検結果が必要です。

本編で確認した複合回答は、単独質問から続く同じ会話で得たものです。紹介時にもこの実行条件を添えます。新しい会話から実演する場合は事前に確認し、結果は新しい検証として記録します。保存済みの回答画面を説明に使う場合は、その旨を示します。

Graph追加前後の精度比較は、前述の「Graph追加前後を同じ条件で比較する」の条件をそろえて実施した場合に評価します。今回の説明では、実際に確認した対象ID・経路・Task・文書根拠を示します。意味の定義、実装、評価を対応付けることで、次のお客様の問いへ置き換える土台になります。

・ 顧客の問いを一枚の設計表へ落とす

PoCの入口では、まず一問について以下を埋めます。大きな全社Ontologyを最初から完成させようとせず、評価できる範囲を決めます。

決める項目 本記事の記入例 顧客業務で確認すること
答えたい質問 Incidentと同じLotの別機材、Task、Documentを示す 誰が、何の判断に使う回答か
対象範囲・時点 Stage機材、現在の配置 組織・期間・業務範囲をどこまで含めるか
用語の定義 同じLot、未完了Task、関連Document 部門間で意味が違う言葉はないか
IDとRelation Equipment ID、Lot ID、Task対象 IDが一致するか、対応表が必要か
根拠と出所 元Master、合成Relation、Document Version 正式な出所・承認者・有効期間は何か
合格条件 別機材2件、Task2件、文書3種類 期待するID・数値・根拠を誰が確認するか
不足情報 不明や対象外を明示 答えを保留すべき条件は何か
比較条件 同じデータによるJOIN・Graph比較 比較先と質問・情報・評価基準がそろうか
許可するAction 参照と対応提案 提案までか、承認後の実行までか

次に、業務の意味を決める担当者、データを確認する担当者、Oracle・PAFを実装する担当者、結果を評価する担当者を決めます。アカウントチームは、この表と検証結果を使って範囲・進捗・顧客確認事項を管理できます。

・ LLMでOntology作成を補助する場合

以下は今後の拡張手順です。本記事には、文書からOntologyを自動生成するFlowは実装していません。

1) 対象文書と質問を限定する

業務用語集、データ定義、手順書などから、対象の質問に必要な範囲を選びます。

2) 候補を根拠付きで抽出する

LLMには概念名、定義、同義語、関係の向き、関係の意味、根拠Document ID・Version・該当箇所、未確定事項を候補として出力させます。原文の事実とLLMが提案した定義を別の欄にします。

3) 人が意味とIDを確認する

同じ名前で別物を指していないか、別名が同じ実体を指していないか、時点や部署によって定義が変わらないかを確認します。採用した定義にはVersionと確認者を記録します。

4) 候補を採用・修正するところまで確認する

文書からの抽出を試す際は、抽出した候補と人が確認した結果を並べます。次は、本稿で扱うService Bulletinのシリアル範囲を題材にしたレビューの設計例です。LLMで実行した結果や、出荷記録で照合した結果ではありません。

候補の内容 確認すること 扱い
Bulletinに対象シリアル範囲が記載されている 文書ID・Version・該当箇所と原文が一致するか 原文で確認できた記載として保持する
範囲内の機材はすべて同じ製造Lotに属する 個体ごとのLot所属を、どの記録で確認できるか 範囲の記載だけで所属Relationを確定しない
機材IDとLotを出荷記録で照合する必要がある 必要な記録が利用できるか、誰が確認するか 未確定事項と次の確認作業を残す

実際のPoCでは、元の文、LLMの候補、確認者の判断、採用後のRelationと出所IDを一組で記録します。本稿の合成Relationを、文書から抽出して検証できた事実へ読み替えないようにします。

5) Oracleへマッピングして質問で評価する

採用した定義を表・View・Relation・Graphへ対応付け、Competency Questionの期待結果で検証します。質問に答えられない場合は、データ不足、定義不足、検索処理の不具合を分けて修正します。

この流れなら、LLMを候補作成に使いつつ、採用した意味と根拠を説明できます。自動抽出の正しさを評価する際も、Graph検索や回答生成とは別に評価します。

・ 自律的なActionへ進める場合

意味を共有するOntology、根拠を検索する処理、実行権限を持つ業務処理は別々に設計します。この記事の到達点は、参照と対応提案です。

次の段階では「根拠付き提案→承認後の実行→条件を限定した自動実行」の順に、対象Actionごとに検証範囲を広げます。実行条件、権限、重複実行の防止、結果確認、取消可能な操作の扱いを定義し、検索精度とは別の合格条件を設けます。

Ontologyを作成できたことと、AIへ業務判断・実行を委任できることを分けて説明すると、PoCの成果と次に検証する課題が明確になります。

● 実装時の注意点

・ YAMLとDatabase Commentの適用範囲

本記事のYAMLは設計内容を共有するための語彙定義です。OracleへのOntology自動Importファイルでも、OWL推論用の公理定義でもありません。

Database CommentやAgent Instructionsで意味を伝えることと、Database制約やSQL条件で動作を強制することを区別します。正式なクラス階層・論理推論を検証する場合は、必要な公理と期待される推論結果を別に定義してRDF/OWLを検討します。

・ 同じLotは因果関係ではない

同じManufacturing Lotの機材が見つかっても、同じ原因で故障しているとは限りません。

Knowledge Graphは調査と予防点検の対象を絞り込むために使用し、原因の断定にはSensor、点検結果、Incident Reportが必要です。

・ Document IDだけでなくVersionも管理

Documentが改訂される場合、DOCUMENT_IDだけではどの内容を根拠にしたか確定できません。

本稿のDocument VertexはCatalogのVERSIONとEFFECTIVE_DATEを保持し、APIはdocument_versionとeffective_dateとして返します。現在は1 Document IDにつき1 Versionというモデルであり、日付の値を返すだけで過去時点への適用判定をしているわけではありません。

Productionで複数Versionを併存させる場合は、DocumentのKeyとRelationの参照先をDocument ID+Versionへ変更し、Chunk側も同じ組に揃えます。承認状態、適用開始・終了日、質問の基準時点も条件に加えます。

・ Stage配置へ有効期間を持たせる

今回のデモはequipment_master.stage_idを現在の配置先として使用します。

実運用では機材が移動するため、DEPLOYED_AT EdgeへVALID_FROMとVALID_TOを持たせ、Incident発生時点の配置を検索する設計が適しています。

・ Property Graphの再作成

Underlying Viewの列やGraph定義を変更した場合は、CREATE OR REPLACE PROPERTY GRAPHで再定義します。

依存Objectの変更でGraphがInvalidになった場合は、定義とデータ整合性を確認した上で再作成またはCompileします。

・ PAFのLLMを実Flowで確認

LLM Managementの接続Testが成功しても、Tool Calling、Structured Output、長いContextが対象Flowで正しく動作する保証にはなりません。

Playgroundで、Tool選択、引数、0件、失敗、Timeout、Allowlist外Documentの各Caseを確認します。

● 補足:Graph StudioでKnowledge Graphを可視化

冒頭の「ほかのStageも確認すべき?」という問いに対して、Agentが別機材2台を挙げた理由を図で確認します。INC-2026-081から障害Equipment、Lot、別Equipment、配置Stageへ至るつながりを、Graph Studioで表示してみます。

この図をPAFの回答と並べると、図で確認対象をたどり、回答で未完了Taskと文書の対応条件を確認するという見せ方ができます。操作はGraph全体の構造、Incident周辺の実データの順に進めます。成果を説明するときは、先にIncident周辺の具体例を示し、その後に全体の設計を紹介してもよいと思います。

Graph StudioはAutonomous AI Database Serverlessに用意されたブラウザのツールです。この補足では、既存のADB_USER.LSF_FESTIVAL_OPS_GRAPHをSQLで参照するため、MacへのGraph Server/Clientのインストールは不要です。

検証状況(2026-10-04):Graph StudioでSchema VisualizationとQuery Visualizationを実施しました。Vertex定義6種類・Edge定義8種類を確認し、INC-2026-081から同じLotの別機材へ至るSQL結果2行を、7 Vertex・6 Edgeの図で表示しました。以下に実行結果とCaption設定後の画面を掲載します。Graph StudioでのProperty詳細の照合は、後述の追加確認手順です。画面名は利用環境のVersionによって異なる場合があります。

・ 今回可視化するもの

本記事では、業務の意味を軽量なDomain Ontologyとして整理し、実データとの関係をSQL Property Graphへ対応付けています。Graph Studioでは、そのGraphの構造と実データのつながりを、それぞれ表示します。

表示する図 確認する内容 Blogで伝えること
Schema Visualization Incident、Equipment、Stage、Manufacturing Lot、Task、Documentの6種類のVertexと8種類のEdge どんな概念と関係でデータをつないだか
Query Visualization INC-2026-081から同じLotの別Equipmentと配置Stageへ至る経路 Agentが挙げた対象を、どの関係から見つけたか

RDF/OWLのクラスやプロパティを表示するOntologyのデモとは、表現形式が異なります。ここでは本記事のSQL Property Graphを表示し、業務上の意味は前述の「音楽フェス運営Ontologyを設計」と対応付けて説明します。

参考:Graph Studio FAQ

・ Graph Studioへ接続

1) Graph所有者の権限を確認する

掲載画面でのGraph StudioのログインUserはADMINで、参照したGraphはADB_USER.LSF_FESTIVAL_OPS_GRAPHです。GraphとUnderlying Viewの所有者はADB_USERです。

所有者で再現する場合は、まずSQL DeveloperなどでADB_USERへ接続し、Graph Studioへのログインに必要なRoleを確認します。

SELECT GRANTED_ROLE
FROM USER_ROLE_PRIVS
WHERE GRANTED_ROLE = 'GRAPH_DEVELOPER';

直接付与されたRoleを確認するSQLです。GRAPH_DEVELOPERが返る場合は次へ進みます。未付与の場合は、ADMINなどRoleを付与できる管理者で、次を実行します。

GRANT GRAPH_DEVELOPER TO ADB_USER;

その後、ADB_USERへ接続し直します。本編のAgent実行UserであるLSF_AGENTは、引き続き許可されたWrapper経由でGraphを利用します。可視化のためのRoleは、作業する所有者へ付与します。

GRANT後の Select結果
GRANTED_ROLE
__________________
GRAPH_DEVELOPER

2) OCI ConsoleからGraph Studioを開く

  1. 今回のデータを配置したAutonomous AI Databaseの詳細画面を開きます。

  2. Tool configurationを開きます。

    51_GraphStudio01.jpg

  3. Graph StudioのPublic access URLをブラウザで開きます。

  4. ログインUserを確認します。所有者で再現する場合はADB_USERでログインします。SSOなどで別Userの画面が開くことがあります。今回の掲載画面ではADMINで開き、ADB_USERのGraphを参照しています。

    51_GraphStudio03.jpg

Database ActionsのDevelopment → Graph Studioから開くこともできます。

Graph Studioの計算環境にはECPU使用量に応じた利用料があります。利用時のCompute設定は、Autonomous AI DatabaseのTool configurationで確認します。

参考:OCI Consoleからのアクセス、Database Actionsからのアクセス

・ Graph全体の構造を表示

1) SQL Property Graphを選ぶ

Graph StudioのメニューからGraph Explorerを開き、左側のツリーを次の順に展開します。

  1. SQL Property Graphs
  2. ADB_USER
  3. LSF_FESTIVAL_OPS_GRAPH

51_GraphStudio04.jpg

Query Editor上部のDriverはSQLを選択します。本章はDatabase上のGraphへSQLを実行する経路です。

2) Schema Visualizationを表示する

LSF_FESTIVAL_OPS_GRAPHを右クリックし、Schema Visualizationを選択します。

51_GraphStudio05.jpg

この図で、Vertex定義6種類・Edge定義8種類を確認しました。Graphの定義を表す全体図であり、実データのIncident件数やEquipment台数を表す図ではありません。

51_GraphStudio07.jpg

本記事で定義した、次の8種類の関係と照合します。

始点のLabel EdgeのLabel 終点のLabel
INCIDENT AFFECTS EQUIPMENT
EQUIPMENT BELONGS_TO_LOT MANUFACTURING_LOT
EQUIPMENT DEPLOYED_AT STAGE
INCIDENT DOCUMENTED_BY DOCUMENT
INCIDENT CREATES TASK
TASK TARGETS EQUIPMENT
TASK USES_PROCEDURE DOCUMENT
MANUFACTURING_LOT GOVERNED_BY DOCUMENT

Graph ElementのAliasで表示される場合、INCIDENTS、EQUIPMENTS、STAGES、LOTS、TASKS、DOCUMENTSが、それぞれのVertexに対応します。

ここで全体図のスクリーンショットを保存します。続けてQueryを可視化すると、Schema VisualizationはQuery結果の表示へ切り替わります。

参考:Visualize the Schema of a SQL Property Graph

・ INC-2026-081から同じLotの別機材を表示

1) 可視化用のGraph Queryを入力する

同じGraphのQuery Editorへ、次のSQLを入力します。前述の「Incidentから同一Lotの別Equipmentを検索」と同じ検索条件で、表示用のVertex/Edge識別子を返します。

SELECT *
FROM GRAPH_TABLE (
  ADB_USER.LSF_FESTIVAL_OPS_GRAPH
  MATCH
    (I IS INCIDENT)
      -[A IS AFFECTS]->
    (E1 IS EQUIPMENT)
      -[B1 IS BELONGS_TO_LOT]->
    (L IS MANUFACTURING_LOT)
      <-[B2 IS BELONGS_TO_LOT]-
    (E2 IS EQUIPMENT)
      -[D IS DEPLOYED_AT]->
    (S IS STAGE)
  WHERE I.INCIDENT_ID = 'INC-2026-081'
    AND E1.EQUIPMENT_ID <> E2.EQUIPMENT_ID
  COLUMNS (
    VERTEX_ID(I)  AS INCIDENT_VERTEX,
    EDGE_ID(A)    AS AFFECTS_EDGE,
    VERTEX_ID(E1) AS AFFECTED_EQUIPMENT_VERTEX,
    EDGE_ID(B1)   AS AFFECTED_LOT_EDGE,
    VERTEX_ID(L)  AS LOT_VERTEX,
    EDGE_ID(B2)   AS RELATED_LOT_EDGE,
    VERTEX_ID(E2) AS RELATED_EQUIPMENT_VERTEX,
    EDGE_ID(D)    AS DEPLOYED_AT_EDGE,
    VERTEX_ID(S)  AS STAGE_VERTEX
  )
);
SQL実行結果
INCIDENT_VERTEX                                                                                                                         AFFECTS_EDGE                                                                                                                                                    AFFECTED_EQUIPMENT_VERTEX                                                                                                                 AFFECTED_LOT_EDGE                                                                                                                                   LOT_VERTEX                                                                                                                          RELATED_LOT_EDGE                                                                                                                                    RELATED_EQUIPMENT_VERTEX                                                                                                                  DEPLOYED_AT_EDGE                                                                                                                                       STAGE_VERTEX
_______________________________________________________________________________________________________________________________________ _______________________________________________________________________________________________________________________________________________________________ _________________________________________________________________________________________________________________________________________ ___________________________________________________________________________________________________________________________________________________ ___________________________________________________________________________________________________________________________________ ___________________________________________________________________________________________________________________________________________________ _________________________________________________________________________________________________________________________________________ ______________________________________________________________________________________________________________________________________________________ ___________________________________________________________________________________________________________________________
{"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"INCIDENTS","KEY_VALUE":{"INCIDENT_ID":"INC-2026-081"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"INCIDENT_AFFECTS","KEY_VALUE":{"EDGE_ID":"INC-2026-081|AFFECTS|EQ-NET-WA-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENTS","KEY_VALUE":{"EQUIPMENT_ID":"EQ-NET-WA-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_LOT","KEY_VALUE":{"EDGE_ID":"EQ-NET-WA-01|LOT|NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"LOTS","KEY_VALUE":{"MANUFACTURING_LOT":"NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_LOT","KEY_VALUE":{"EDGE_ID":"EQ-NET-OM-01|LOT|NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENTS","KEY_VALUE":{"EQUIPMENT_ID":"EQ-NET-OM-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_STAGE","KEY_VALUE":{"EDGE_ID":"EQ-NET-OM-01|STAGE|STG-OM"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"STAGES","KEY_VALUE":{"STAGE_ID":"STG-OM"}}
{"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"INCIDENTS","KEY_VALUE":{"INCIDENT_ID":"INC-2026-081"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"INCIDENT_AFFECTS","KEY_VALUE":{"EDGE_ID":"INC-2026-081|AFFECTS|EQ-NET-WA-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENTS","KEY_VALUE":{"EQUIPMENT_ID":"EQ-NET-WA-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_LOT","KEY_VALUE":{"EDGE_ID":"EQ-NET-WA-01|LOT|NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"LOTS","KEY_VALUE":{"MANUFACTURING_LOT":"NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_LOT","KEY_VALUE":{"EDGE_ID":"EQ-NET-NG-01|LOT|NW-2603"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENTS","KEY_VALUE":{"EQUIPMENT_ID":"EQ-NET-NG-01"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"EQUIPMENT_STAGE","KEY_VALUE":{"EDGE_ID":"EQ-NET-NG-01|STAGE|STG-NG"}}    {"GRAPH_OWNER":"ADB_USER","GRAPH_NAME":"LSF_FESTIVAL_OPS_GRAPH","ELEM_TABLE":"STAGES","KEY_VALUE":{"STAGE_ID":"STG-NG"}}

Graph Studioが図を描くには、VERTEX_ID()やEDGE_ID()で返されるGraph要素の識別子が必要です。EQUIPMENT_IDなどの文字列だけを返すSQLとは、出力する列が異なります。上の関数が返すJSON型の値は、そのまま返します。

また、今回のEdgeはKey列のEDGE_IDをPropertyとして公開していません。識別子の取得にはUnderlying Objectへの参照権限も関わります。掲載画面ではADMINで実行していますが、所有者で再現する場合はGraphとUnderlying Viewを所有するADB_USERを使用します。

参考:Query and Visualize SQL Property Graphs、Vertex and Edge Identifiers

2) Visualize Graphを実行する

DriverがSQLになっていることを確認し、Visualize Graphをクリックします。Run Statementは表形式で結果を確認するための操作です。

52_VisualuzeGraph02.jpg

3) VertexとEdgeのCaptionを設定する

Vertex(玉)の表示は、歯車のSettings → Styles → Create Vertex Style、または作成済みのVertex Styleの編集で設定します。LabelごとにCaptionを選択します。掲載画面では次のPropertyを使用しました。

玉の色・Label Captionに使うProperty 表示例
黄色/INCIDENT TITLE DJブース同期ネットワーク障害
水色/EQUIPMENT EQUIPMENT_ID EQ-NET-WA-01など
ピンク/MANUFACTURING_LOT MANUFACTURING_LOT NW-2603
緑/STAGE STAGE_NAME Orbit Main Stageなど

IncidentをIDで表示したい場合は、CaptionをINCIDENT_IDに変更できます。掲載画面では内容が分かるTITLEを表示し、IDはSQLの検索条件と実行結果で確認しています。

Edge(矢印)の表示は、Settings → Styles → Create Edge Style、または作成済みのEdge Styleの編集で設定します。Captionには**[labels]**を選ぶと、関係名を表示できます。

編集するEdge Style Captionの選択 矢印に表示される文字
AFFECTS [labels] AFFECTS
BELONGS_TO_LOT [labels] BELONGS_TO_LOT
DEPLOYED_AT [labels] DEPLOYED_AT

設定後は、機材ID・Lot・Stage名と、矢印の関係名を一緒に読めるようになりました。

Caption設定後の実画面は、冒頭の「Graph Studioで別Stageの2台へ至る経路を見る」に掲載しています。

合成デモRelationを反映した本記事のデータで、次の2経路が返ることを確認しました。

Incident 障害Equipment Lot 同じLotの別Equipment 配置Stage
INC-2026-081 EQ-NET-WA-01 NW-2603 EQ-NET-NG-01 STG-NG/Neon Groove Stage
INC-2026-081 EQ-NET-WA-01 NW-2603 EQ-NET-OM-01 STG-OM/Orbit Main Stage

SQL結果は2行で、共通するIncident、障害Equipment、Lotなどを共有した図の表示は7 of 7 vertices/6 of 6 edgesでした。つまり、7 Vertex・6 Edgeです。これはこのQueryが返す範囲の件数であり、Graph全体のデータ件数ではありません。

Lotを中心に2台へ分岐する形で見ると、「障害があった機材と製造Lotが同じため、ほかのStageの機材も確認対象にした」という関係を説明できます。

BELONGS_TO_LOTのEdgeの向きはEquipment → Lotです。QueryはLotから別Equipmentへ戻るときに<-[B2]-で逆向きにたどりますが、図の矢印が表す関係の向きは変わりません。

4) IDと関係の出所を確認する(追加確認)

ここからは、表示した図と本編のSQL結果を照合するための追加確認です。掲載画面ではCaptionと経路までを確認しており、以下のProperty詳細を開いた画面は記録していません。

VertexやEdgeを右クリックすると、Propertyを確認できます。各Vertexの種類に対応するID Property(IncidentならINCIDENT_ID、EquipmentならEQUIPMENT_ID、LotならMANUFACTURING_LOT、StageならSTAGE_ID)が、上の表と一致することを確認します。

同じLotの別Equipmentに結び付くBELONGS_TO_LOT Edgeでは、次のPropertyを確認します。

Property 別Equipment側の期待値
LOT_SOURCE SYNTHETIC_DEMO
LOT_ASSERTION_STATUS SYNTHETIC
LOT_SOURCE_REFERENCE_ID BLOG-DEMO-LOT-001

障害Equipment EQ-NET-WA-01側のLot関係は、元データ由来のMASTER_CSV/SOURCE_RECORDです。どの関係を補足データとして追加したかも、図とPropertyを対応付けて説明します。

この2経路とIDが読めるスクリーンショットを保存します。図の見た目だけで判断せず、Propertyの値と本編のSQL結果を照合します。

参考:Graph Explorerの画面とProperty表示、Query Visualization Settings

Graph Studioで表示できない場合の確認

・ 表示できない場合の確認

状況 確認すること
Graph Studioへログインできない 対象ADBのURL、ADB_USERの認証、GRAPH_DEVELOPERの付与を確認し、付与後は接続し直す
Graphが一覧にない 対象ADB・ログインUser・Schemaを確認する。ADB_USERのSQLセッションで下のMetadata確認を実行する
SQL Property GraphsやGraph Explorerがない DatabaseとGraph StudioのVersionを確認する。本章はSQL Property Graphを扱える26ai環境と、公式手順のGraph Explorerを前提とする
表は返るが図が出ない DriverがSQLか、Visualize Graphを選んだか、VERTEX_ID()/EDGE_ID()をそのまま返しているかを確認する
識別子取得時に権限エラーになる Graph所有者ADB_USERでログインしているか確認する。Graphだけの参照権限とUnderlying Objectへの参照権限を区別する
Queryが0行になる 本編の「Incidentから同一Lotの別Equipmentを検索」を同じUserで実行し、合成Relation投入後の2行が返るか確認する

Graphが一覧にない場合は、ADB_USERのSQLセッションで次を実行します。

SELECT GRAPH_NAME
FROM USER_PROPERTY_GRAPHS
WHERE GRAPH_NAME = 'LSF_FESTIVAL_OPS_GRAPH';

LSF_FESTIVAL_OPS_GRAPHが返ればGraphのMetadataは存在します。画面の接続先・User・表示内容を確認して切り分けます。

・ 可視化から読み取れること

Schema Visualizationでは、設計した概念と関係がGraphの定義に対応しているかを確認できます。Query Visualizationでは、今回のIncidentからどの機材へ到達したかを、実際のIDと経路で確認できます。

可視化を確認できたら、本編の複合回答に戻り、図に出ている2台と回答のTask対象機材が一致するかを見ます。さらに、Graph APIが候補に挙げた文書と、RAGが回答に付けたSourcesを照合します。図で関係を見せた後に、回答の根拠へ戻るところまでを一続きの説明にします。

同じLotにつながる図は、ほかの機材にも同じ障害が発生している証拠ではありません。Graphで調査対象を見つけた後、NL2SQLでSensorや保守の事実を、RAGで交換条件や運用手順を確認するという本編の役割分担につながります。

■ まとめ

● 一つのIncidentから、意味と根拠をたどる

今回の出発点は、音楽フェスで起きた一つの障害でした。「同じLotの機材はほかのStageにもあるか」「どのTaskが登録され、どの文書を使って対応するか」という問いから、必要な概念と関係を決めています。

その意味の設計を、元データのID、補足Relation、View、SQL Property Graph、Graph API、PAFへと対応付けました。本文の手順を通じて目指すのは、次のつながりを自分の環境で確かめられる状態です。

問いへの答え 確かめるもの ここで体験すること
何が起きたか Incident、センサー値、保守状態 SQLで事実を確認する
どこまで調べるか Equipment、Lot、Stageの経路 業務の関係をGraphでたどる
どの対応が登録されているか Incidentに結び付いた未完了Taskと対象機材 定義した条件が検索結果へ反映されることを確認する
なぜこの文書を使うか Documentへ至る経路、Version、出所ID 根拠の選定理由を追跡する
何を提案できるか 文書の該当箇所とDatabaseの事実 確認した情報と推奨を区別して説明する

イントロの「ほかのStageにも同じLotの機材がある? 開演前に何を確認する?」という問いから、別機材2台と配置Stage、登録済みの未完了Task、交換条件・運用手順の根拠までをたどりました。対象を挙げる理由と、対応を提案する理由を、データへ戻って説明できることが今回の成果です。

Graph Studioでも、6種類のVertex・8種類のEdgeからなる定義と、今回のIncidentから別機材2台へ至る2経路を可視化しました。Captionを設定することで、7 Vertex・6 Edgeの図から機材・Lot・配置Stageと関係名を読み取れるようになりました。

本文では、Oracle側の整合性・Graph API・条件変更テストに加え、PAF本編FlowのGraph/NL2SQL/RAG単独質問を確認しました。初回は複合質問と追加質問で3文書の内容・Version・Sourcesまで確認し、その後は単独質問に続く同じ会話で、数値・機材・Task・SB/OPSの手順・3文書のSourcesを含む統合回答を確認しました。保守項目を明確にしたNL2SQL再実行でも、期限超過保守1件・予定日2026-07-25がSQL結果と一致しました。

本編の成果は、この実行条件と回答画面に基づいて記録しています。新しい会話で複合質問1回だけを実行する場合の再現性、Tool入力・生の返却値の照合、PDF原文の該当箇所の確認、Graph追加前後の比較は今後の追加検証です。

● 「意味を決める」が、実装と回答につながる

Ontologyでは「機材とは何か」「同じ製造Lotとは何か」「未完了Taskをどう判定するか」「なぜそのDocumentが関係するか」を共有しました。その定義を、Databaseで実行する条件、Graphの関係、Agentへの指示、回答の評価へ対応付けています。

特に確かめたいのは、元データ、意味の定義、検索結果、最終回答のつながりです。Taskを完了にすれば未完了Taskの対象から外れること、関係を登録していない文書を登録済みの根拠として補わないこと、同じLotというだけで故障を断定しないことまで確認します。

OracleのSQL、Graph、文書検索を一つの業務の問いで使い分けると、それぞれの機能の役割を具体的に掴めます。返ってきた結果を、自分でSQLやGraphの経路に戻って確かめられることも、このハンズオンの楽しさです。

今回のPAF標準構成では、Graphが返したDocument候補をPromptで伝えるKG-informed RAGを使用します。候補への誘導と、SQLによる検索対象の強制は区別します。厳密な制限が必要な場合は、本文の拡張例のようにDocument ID+VersionでChunkを絞り込んでからVector検索する構成へ進めます。

● 同じ検証から、自分の業務へ広げる

音楽フェスの「同じLotの別機材はどこにあるか」は、食品会社なら「同じ製造Lotの製品はどこへ出荷されたか」という問いへ置き換えられます。対象が変わっても、問いを決め、概念と関係を整理し、IDと出所をつなぎ、根拠と合格条件を用意する進め方を再利用できます。

アカウントチームや業務担当者と話す際も、まずこの一問の結果を共有し、次に「お客様の業務では、何を同じものとみなすか」「何を根拠に判断するか」を設計表へ落とすと、次の検証が具体的になります。

さらに、Select AI for Property Graphsで自然言語からGraph Queryを生成する方法や、Graph StudioのAIによるモデル作成支援を試す場合も、今回のGraphとCompetency Questionsを比較の土台にできます。RDF/OWLへ進む場合は、必要なクラス階層や公理と、期待する推論結果を追加して検証します。

機能が増えたときにも、どの工程を支援し、同じ問いにどの根拠で答えられるかを確かめる。その土台になる検証を、この一枚に揃えていきます。

「こんなことはOracleでできる?」と聞かれたときに、同じデータと手順、実際の結果を示して「あるよ!」と返せるように。まずは音楽フェスの一問から、意味とデータと根拠がつながるところを、一緒に試してみていただければと思います。

■ 参考情報

■ 解説

Ontology、NL2SQL、RAG、Knowledge Grapなどバズワードたくさんあり身構えしますが、初心者でも分かりやすく解説しています。
セールストークにどーぞ

■ おまけ

おまけ2.jpg
今回の内容を、ずんだもん達に紹介してもらう漫画も作ってみました。 技術記事の補足として、少しでも楽しく読んでもらえたらうれしいです。

※ 本漫画は筆者による非公式の二次創作です。
※ 使用キャラクター:ずんだもん / 四国めたん / 春日部つむぎ
※ キャラクターの権利は各権利元に帰属します。
※ クレジット
 - ずんだもん / 四国めたん:東北ずん子・ずんだもんプロジェクト関連ガイドラインに基づいて利用
 - 春日部つむぎ:公式利用規約に基づいて利用

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?