DSLとは何か — プロンプトとDecision Trace Modelに「厳格性」を与える設計

生成AIの普及によって、システムの中心に「プロンプト」が入る場面が一気に増えてきました。
自然言語でAIに指示を与え、分類させ、要約させ、提案させる。こうした使い方は、すでに多くの現場で日常的なものになっています。

しかし、実務でAIを使い始めると、すぐにある壁にぶつかります。

同じような入力でも結果が揺れる。
なぜその判断になったのか説明しにくい。
どこまで自動で実行してよいのかが曖昧になる。
危険なケースで人に戻す条件が書かれていない。

つまり、AIは何かを「出力」することはできても、その出力をそのまま「意思決定」として扱うには不安定すぎるのです。

この問題は、モデルの性能だけでは解決しません。
なぜなら問題の本質は、AIの賢さではなく、判断の構造が外に書かれていないことにあるからです。

そこで重要になるのが、DSLです。

DSLとは何か

DSLとは、Domain Specific Language の略で、特定の領域に特化した言語を指します。
日本語でいえば、「ある目的のために設計された専用言語」です。

ここでいうDSLは、単なる記述フォーマットではありません。
特定の業務や意思決定領域において、判断・ルール・構造を明示的に記述するための言語です。

たとえば一般的なプログラミング言語である Python や JavaScript は、非常に幅広い処理を書ける汎用言語です。
一方で DSL は、自由度をあえて絞る代わりに、「何をどう判断するのか」を読みやすく、検証しやすく、再利用しやすい形で表現することに向いています。

違いを整理すると、次のようになります。

観点 汎用言語 DSL
目的 任意の処理を書く 特定ドメインの判断を表現する
可読性 技術者向けになりやすい 業務や設計の文脈で読みやすい
判断表現 実装の中に埋もれやすい 明示的に記述しやすい
再現性 実装者依存になりやすい 構造として統一しやすい

重要なのは、DSLは単に「書き方を変える」ものではなく、判断を暗黙知から明示知へ変えるための枠組みだという点です。

なぜ厳格性が必要なのか

AIやプロンプトを現場で使うときに、本当に必要なのは「それっぽい出力」ではありません。
必要なのは、何を根拠に、どう判断し、どこで止まり、誰に戻すのかが明確であることです。

ここでいう厳格性とは、単に細かく書くことではありません。
判断の前提、条件、例外、境界、責任の所在が、曖昧さを減らした形で構造化されていることを指します。

厳格性が必要になる理由は大きく4つあります。

1. 判断を再現可能にするため

同じ状況で、毎回違う判断が出てしまうシステムは、業務では使いにくいものになります。
もちろん現実は完全に同一ではありませんが、少なくとも「どういう条件ならこうなる」という骨格が必要です。

厳格性がないと、判断はモデルの気分や実装者の癖のように見えてしまいます。
それではシステムとして信用されません。

2. 説明可能にするため

AIを導入すると、必ず「なぜそうなったのか」という問いが発生します。
特に、却下、警告、エスカレーション、価格変更、特典付与、承認拒否のような判断では、この問いは避けられません。

説明には、単なる後付けの文章ではなく、事前に定義された判断構造が必要です。

3. 責任境界を明確にするため

AIが何かを提案したとしても、それをそのまま実行してよいのか、人の承認が必要なのか、ある条件では必ず止めるべきなのか。
この境界が曖昧だと、責任の所在も曖昧になります。

厳格性とは、責任の押し付けを防ぐための仕組みでもあります。

4. システムとして運用可能にするため

業務システムは、1回うまく動けばよいものではありません。
継続的に運用され、改善され、監査され、引き継がれる必要があります。

そのためには、判断を自然言語の雰囲気や担当者の経験に頼らず、検証可能な形で記述する必要があります。

プロンプトはなぜ厳格性を持ちにくいのか

プロンプトは強力です。
少ない実装で、高度な意味理解や生成を引き出せます。
しかし、プロンプトだけでシステム全体の判断を支えようとすると、限界が見えてきます。

その理由は、プロンプトが基本的に「自然言語による指示」であって、「仕様」ではないからです。

自然言語には柔軟さがあります。
だからこそAIとの対話には向いています。
しかしその柔軟さは、業務システムにとっては曖昧さにもなります。

たとえば次のような問題が起きます。

  • 同じ意味のつもりでも、解釈が揺れる
  • 条件の優先順位が明示されにくい
  • 例外処理が曖昧になりやすい
  • 停止条件や人への返却条件が埋もれやすい
  • ログとして残したときに構造化しづらい

つまり、プロンプトは「意味を引き出す」には向いていても、
判断を制度として固定することにはあまり向いていないのです。

ここで必要になるのが、プロンプトを否定することではありません。
むしろ逆です。
プロンプトの力を活かすために、その外側に厳格な構造が必要になります。
その役割を担うのがDSLです。

Decision Trace Model と DSL

この話をより明確にするのが Decision Trace Model です。

Decision Trace Model では、意思決定を次の流れで捉えます。

Event → Signal → Decision → Boundary → Human → Log

この構造の重要な点は、AIの出力をそのまま最終決定とみなさないことです。

  • Event は、現実世界で起きた事象や入力
  • Signal は、AIや分析モデルが生成した予測・分類・スコア
  • Decision は、そのSignalをどう解釈して何を行うかという判断
  • Boundary は、止める条件や人に戻す条件
  • Human は、責任を持つ主体
  • Log は、判断の履歴

ここで特に大事なのは、Signal と Decision は別物だということです。

AIは Signal を作ることができます。
たとえば「購入意欲が高い」「この問い合わせは高リスク」「この文書は規制該当可能性がある」といった予測や分類です。

しかし、そのSignalを受けて

  • 本当にクーポンを出すのか
  • 自動回答してよいのか
  • 必ず人に確認させるのか
  • 今回は何もしないのか

を決めるのは、Decision の層です。

そしてこの Decision を厳格に書くための道具が DSL です。

つまり DSL は、Decision Trace Model における Decision と Boundary を外在化するための記述手段 だと言えます。

DSLはどのように厳格性を実現するのか

DSLの本質は、判断を「読める形」「検証できる形」「実行できる形」に分解することです。
その代表的なポイントを順に見ていきます。

1. 条件を明示する

たとえば以下のような記述です。

decision:
 condition:
 - signal.intent == "purchase"
 - signal.score > 0.8
ここでは、何が判断条件なのかが明示されています。

「購入意図であり、かつスコアが0.8を超えている」という条件が、実装の奥ではなく仕様として表に出ています。

これにより、レビューや修正がしやすくなります。
また、「なぜその判断になったのか」を追いやすくなります。

2. 判断と実行を分離する

action:
 - send_coupon
 - log_decision
このように書けば、条件を満たしたあとに何を実行するのかを分離して記述できます。

判断のルールと、実際のアクションが分かれていると、
「判断は同じだがアクションだけ変える」
「アクションは同じだが条件を変更する」
といった運用がしやすくなります。

これは設計上かなり重要です。
判断ロジックと実行ロジックが混ざると、システムはすぐに読みにくくなります。

3. 停止条件を明示する

boundary:
 - signal.risk > 0.7: escalate_to_human

ここでは、リスクが高い場合は人に戻す、という境界条件が書かれています。

多くのAIシステムで本当に問題になるのは、「うまく答えること」よりも「止まるべきときに止まれるか」です。
Boundary をDSLとして書けることは、暴走防止、監査性、責任分離の面で非常に大きい意味を持ちます。

4. ログの対象を構造化する

log:
 - event_id
 - decision_reason
 - selected_action
これによって、何を記録すべきかが仕様の一部になります。

ログが単なるデバッグ情報ではなく、意思決定の証跡になります。

運用の現場では、「あとから説明できること」が非常に重要です。
DSLがログ項目まで含んでいれば、意思決定そのものが追跡可能になります。

オントロジーと組み合わせる意味

DSLだけでもかなり有効ですが、さらに重要なのがオントロジーとの組み合わせです。

オントロジーは、対象世界の概念や関係を定義する仕組みです。
簡単に言えば、「何をどういう意味で扱っているのか」を固定するための土台です。

たとえば、intent という項目があるとして、それが

  • purchase
  • browse
  • complaint
  • inquiry

のどれを意味するのかが定義されていなければ、DSLに書かれた条件も不安定になります。

たとえば次のような定義です。

{
 "intent": ["purchase", "browse", "exit"],
 "risk_level": ["low", "medium", "high"]
}
これにより、DSLが使う語彙の意味が揺れにくくなります。

つまり、オントロジーは「判断の前に意味を固定する」役割を持ちます。

流れとしては、

  • オントロジーが意味の範囲を定義し
  • DSLがその意味を使って判断条件を書く

という関係になります。

意味が曖昧なまま判断を厳格に書くことはできません。
そのため、DSLの厳格性はオントロジーによって支えられています。

BT(Behavior Tree)と組み合わせる意味

DSLが「何を条件として何を行うか」を記述するのに対し、Behavior Tree は「どの順序で評価し、どう分岐し、どこで失敗や成功を返すか」を表現するのに向いています。

Behavior Tree は、もともとゲームAIやロボティクスでよく使われてきた構造ですが、意思決定の制御にも非常に相性がよいです。

たとえば以下のような考え方です。

  • まず高リスクかどうか確認する
  • 高リスクなら人に戻す
  • そうでなければ通常の判断ルールを評価する
  • 条件を満たしたらクーポンを送る
  • どれも満たさなければログだけ残す

これをツリー構造として扱うことで、複雑な判断フローを整理しやすくなります。

概念的にはこうなります。

Selector
 ├── Condition: high_risk
 │ └── Action: escalate_to_human
 ├── Condition: purchase_and_high_score
 │ └── Action: send_coupon
 └── Action: log_only
このとき、
  • オントロジーは意味を定義する
  • DSLは条件やアクションを定義する
  • BTは評価順序と制御構造を定義する

という役割分担になります。

この3つが組み合わさることで、判断はかなり強固になります。

実装例

たとえば簡易的には、DSLで定義したルールをPython側で読み込んで評価することができます。

class DecisionEngine:
 def __init__(self, rules):
 self.rules = rules

 def evaluate(self, signal):
 for rule in self.rules:
 if all(cond(signal) for cond in rule["conditions"]):
 return rule["action"]
 return "no_action"


rules = [
 {
 "conditions": [
 lambda s: s["intent"] == "purchase",
 lambda s: s["score"] > 0.8
 ],
 "action": "send_coupon"
 }
]

engine = DecisionEngine(rules)

signal = {"intent": "purchase", "score": 0.9}
result = engine.evaluate(signal)
print(result)
この例は単純ですが、本質は見えています。

判断ロジックをコードの奥に埋めるのではなく、外から与えられるルールとして扱っている点です。

実際にはここに、

  • YAMLやJSONで書かれたDSLのパース
  • オントロジーによる語彙チェック
  • BTによる実行順序制御
  • 人へのエスカレーション
  • ログ保存

などが入ってきます。

そうすると、単なるAIアプリではなく、判断構造を持った意思決定システムになります。

DSLはすべて手で書くのか?

ここまで読むと、こう思うかもしれません。

「DSLって結局、全部人が手で書くのでは?」
「それって大変すぎないか?」

これは非常に自然な疑問です。
そして結論から言うと、

すべてを手で書く必要はないし、書くべきでもありません。

なぜ「全部手書き」ではないのか

DSLの目的は、「すべてを人が細かく記述すること」ではありません。
目的はあくまで、

判断の構造を外に出すこと

です。

つまり重要なのは、

  • どこを構造化すべきか
  • どこを可変にすべきか
  • どこをAIに任せるべきか

の設計です。

DSLとAIの役割分担

ここで重要なのが、DSLとプロンプト(LLM)の関係です。

DSLの役割

  • 判断ルールを定義する
  • 境界条件(止める条件)を定義する
  • 実行構造を固定する

「変えてはいけない部分」を担う

AI(プロンプト)の役割

  • 意味を解釈する
  • スコアや分類(Signal)を生成する
  • 曖昧な入力を構造化する

「変化する部分」を担う

この関係は、こう言い換えることもできます。

DSLは“骨格”、AIは“筋肉”

DSLはどのように生成・運用されるのか

実際のシステムでは、DSLはすべて手書きされるわけではなく、いくつかの方法で扱われます。

① 初期設計は人が書く(コアロジック)

最初の段階では、

  • 重要な判断条件
  • リスク境界
  • 人に戻す条件

といった「責任に関わる部分」は人が設計します。

これは仕様書に近い位置づけです。

② テンプレート化される

一度書かれたDSLは、次のように再利用されます。

  • 業務ごとのテンプレート
  • 業界ごとの標準ルール
  • 共通パターン(例:エスカレーション条件)

毎回ゼロから書くことはほとんどありません

③ AIがDSL生成を補助する

ここが重要な進化ポイントです。

AIを使って、

  • 自然言語 → DSLへの変換
  • 過去ログ → ルール抽出
  • 類似ケース → 条件候補生成

といったことが可能になります。

例:

「購入意欲が高く、かつ過去にクーポン反応が良いユーザーには割引を出す」

decision:
 condition:
 - signal.intent == "purchase"
 - signal.score > 0.8
 - user.coupon_response == "high"
DSLは“人が書くもの”から“人とAIで作るもの”へ変わる

④ 実行ログから改善される

DSLは固定ではありません。

  • 実行結果
  • KPI(CVR、ROIなど)
  • 人のフィードバック

をもとに、ルールは継続的に改善されます。

DSLは「運用しながら進化する設計資産」

なぜそれでもDSLが必要なのか

ここで改めて重要なポイントです。

「AIが全部やってくれるなら、DSLいらないのでは?」

という考えも出てきます。

しかし、ここに本質があります。

AIだけでは「責任ある判断」は作れない

AIはあくまで、

確率的にもっともらしい出力を出す存在

です。

一方で業務に必要なのは、

  • 明確な条件
  • 再現可能な判断
  • 説明可能なロジック
  • 責任の所在

です。

これらは「生成」ではなく「設計」の領域です。

DSLは「意思決定のインターフェース」

DSLは、単なる設定ファイルではありません。

人・AI・システムの間にある“判断のインターフェース”

です。

  • AIはSignalを出す
  • DSLが判断する
  • システムが実行する
  • 人が責任を持つ

この分離があるからこそ、システムは信頼できる形になります。

まとめ

DSLはすべて手で書くものではありません。

  • コアは人が設計する
  • テンプレートとして再利用される
  • AIが生成・補助する
  • 運用の中で改善される

そして何より重要なのは、

DSLは「書くこと」が目的ではなく、「判断を構造化すること」が目的である

という点です。

ユースケース

リテールのオファー最適化

AIが来店確率や価格感度をSignalとして出す。
しかし、実際にどの顧客にどの特典を出すかは、利益率、在庫、過去反応、配布制約などを踏まえて決める必要があります。

ここでDSLを使えば、

  • どの条件ならオファーを出すのか
  • どの条件なら出さないのか
  • どの条件なら人の確認が必要なのか

を明示的に書けます。

製造業のコンプライアンス判断

文書や設計変更に対してAIが規制該当可能性をSignalとして出しても、
それだけで承認・差し戻しを決めることはできません。

規制カテゴリ、リスクレベル、適用範囲、承認フローをDSLで書くことで、
AIは「検知」し、人間とルールが「判断」する構造にできます。

コールセンターや問い合わせ対応

AIが問い合わせ分類や応答候補を出しても、
高リスクの相談や曖昧なケースでは、人に戻す必要があります。

ここで Boundary をDSLとして定義しておけば、
「どこまで自動で答えるか」「どこからエスカレーションするか」が明確になります。

まとめ

DSLとは、特定の領域における判断・ルール・構造を明示的に記述するための言語です。
その役割は、単に書きやすくすることではありません。

暗黙だった判断を外に出し、再現可能にし、説明可能にし、責任境界を明確にすること。
そこにDSLの本質があります。

プロンプトは強力ですが、それだけでは仕様になりません。
AIの出力は有用ですが、それだけでは意思決定になりません。

Decision Trace Model の観点で見れば、

Event → Signal → Decision → Boundary → Human → Log

のうち、AIが担うのは主に Signal です。
Decision と Boundary を厳格に設計しなければ、システムは「出力するだけ」で終わってしまいます。

その空白を埋めるのが DSL です。
さらに、オントロジーが意味を支え、BTが実行構造を支えることで、
AIはようやく「判断を伴うシステム」として動き始めます。

つまりDSLは、プロンプトを否定するものではなく、
プロンプトを実務で使える形に変えるための土台なのです。

DSLの作成に関しては以下の記事も参照のこと。

タイトルとURLをコピーしました