0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLMに「数字」を作らせない。エンタープライズKPIチャットをRAGだけで作らない設計

0
Posted at

はじめに

社内データとLLMを組み合わせて、

「先月のA支店の売上は?」
「前年同月と比べてどう?」
「3拠点を比較して」

のような質問に自然言語で答えられる仕組みを作りたい。

こうした要求は、一見すると「社内データをRAGに入れてLLMに答えさせればよい」ように見えます。

しかし、実際に定量データを扱い始めると、一般的な「検索+LLM」だけではかなり厳しいことが分かってきます。

LLMは非常に優秀な言語理解・推論エンジンですが、

  • 似た指標を取り違える
  • 対象期間を補完する
  • 足りない条件を推測する
  • 取得した数字から別の数字を計算する
  • データがない場合にも、もっともらしい回答を生成する

といった挙動を完全には排除できません。

文章生成では便利なこの性質も、

30%を50%と答えたら困る

という世界では、そのまま使うわけにはいきません。

そこで私が採用した設計方針は非常に単純です。

LLMには考えさせる。だが、決めさせない。
文章は作らせる。だが、数字は作らせない。

本記事では、定量データを安全に扱うために、LLMを「権威」から降ろし、決定論的なプログラムを中心に据えたアーキテクチャについて紹介します。


1. RAGが悪いのではない。「何のAuthorityにするか」が問題

最初に明確にしておきたいのは、

「定量データにRAGは使えない」わけではありません。

問題なのは、RAGで取得した情報とLLMの推論結果を、そのまま**数値の最終Authority(権威)**にしてしまうことです。

定性的な用途

例えば、

  • 社内規程
  • マニュアル
  • 障害対応手順
  • 過去の問い合わせ
  • 技術文書
  • ノウハウ

などについて、

「このケースではどう対応するべきか?」

と質問する用途では、ベクトル検索+LLMは非常に強力です。

多少曖昧な表現で質問しても関連文書を探し、複数の文章をまとめ、人間が読みやすい形に整理できます。


定量的な用途

一方、

  • 売上
  • 利益率
  • CVR
  • 稼働率
  • 不良率
  • 全社KPI
  • 拠点別実績
  • 前年比較

などでは事情が変わります。

例えば、

「8月のA支店のCVRは?」

という質問に対して必要なのは、文章としての「それっぽさ」ではありません。

必要なのは、

指標        = CVR
対象        = A支店
期間        = 2026年8月
分子        = 正式に定義されたConversion数
分母        = 正式に定義されたVisit数
除外条件    = 会社が承認した条件
データソース = 承認済みデータセット

が毎回同じルールで解釈されることです。

したがって、私の設計では、

RAGはMeaningやEvidenceを探すためには使える。
しかし、数値を確定するAuthorityにはしない。

という境界を置いています。


2. 「KB+LLM」で数値を扱ったときに起きる問題

2.1 似た指標が増えるほど検索が難しくなる

例えば社内に、

売上
月次売上
累計売上
予算売上
前年差
前年同月売上
店舗売上
EC売上

が存在するとします。

人間から見れば明確に違う指標でも、Embedding上では非常に近い位置に配置されます。

この状態で、

「A店の今月の売上は?」

と検索すると、似た文書や似た数字が複数取得されます。

その後の最終判断をLLMに任せると、

検索
 ↓
候補1
候補2
候補3
 ↓
LLMが文脈から選択

という構造になります。

私はこれを内部的に**「意味空間の椅子取りゲーム」**と考えています。

データやKPIが増えるほど、この問題は悪化します。


2.2 LLMによる計算は「保証」が難しい

LLMは簡単な算術をかなり正確に処理できます。

したがって、

「LLMは計算ができない」

という表現は正確ではありません。

問題はそこではありません。

問題は、

同じ条件なら必ず同じ計算規則を適用する

という業務システムとしての保証を持たせにくいことです。

例えば、

CVR = 成約数 / 来店数

という指標でも、

  • キャンセルを含めるのか
  • NULLをどう扱うのか
  • 重複レコードを除外するのか
  • 期間境界は営業日か暦日か
  • 分母0を0%とするのかNULLとするのか

によって結果は変わります。

ここをLLMの推論に任せるべきではありません。


2.3 「分からない」の種類を区別しにくい

実際の業務では、

データが0件
データソースが存在しない
指標自体が未定義
対象店舗が不明
期間が曖昧
計算方法が未承認
システム障害

はすべて別の状態です。

ところがLLMだけに処理させると、これらをまとめて、

「おそらく○○です」

と補完してしまうことがあります。

エンタープライズ用途では、答えることよりも、

なぜ答えられないのかを正しく分類すること

の方が重要な場合があります。


2.4 会話履歴が「ゾンビ化」する

チャットシステムでは、過去の会話文脈も注意が必要です。

例えば、

User: A支店の8月は?
AI: ...

User: ではB支店は?

なら、8月を引き継ぐのは自然です。

しかし、

User: A支店の8月は?
AI: ...

User: C事業部の年間実績を教えて

という明示的な新しい条件が来たにもかかわらず、以前の「A支店」「8月」が一部残存すると危険です。

そのため、会話状態についても、

現在の明示的な指示 > 過去の会話状態

というルールをLLMではなくプログラム側で管理する必要があります。


3. 基本思想:「Proposal」と「Decision」を分離する

この設計の中心にある考え方は、

LLM Proposal != System Decision

です。

LLMは、

「この質問は、おそらくA支店・2026年8月・CVRについて聞いている」

という提案はできます。

しかし、その提案をそのままSQLへ渡してはいけません。

User
 ↓
LLM
 ↓
Proposal
 ↓
Deterministic Validator
 ↓
Decision

とします。

つまり、

LLMは候補を出す。
プログラムが採用するかどうかを決める。

この境界を設けるだけでも、システムの性質はかなり変わります。


4. 「真実」を1本のTrust Orderで考えない

当初私は、

DBの生データ
 >
プログラム
 >
LLM

のような単純なTrust Orderを考えていました。

しかし、これは少し不正確です。

なぜなら、

DBに数字が入っているだけでは、その数字の意味は決まらない

からです。

例えば、

value = 42

が正しくDBに保存されていても、

  • 何の42なのか
  • 単位は何か
  • どの粒度か
  • どの期間か
  • どの母集団か

が分からなければ、業務上の「真実」にはなりません。

そこで、Authorityを役割ごとに分離します。

Authority 役割
Meaning Authority そのKPIが何を意味するか
Capability Authority システムが安全に回答できるか
State Authority 今回の質問条件は何か
Execution Authority どの計算・データ取得方法を使うか
Data Authority 実際に取得されたデータ
Evidence Authority 最終的に回答根拠として確定したもの
Presentation Authority Evidenceをどう人間向けに表現するか

ポイントは、

LLMをこれらのAuthorityの中心に置かない

ことです。


5. 全体アーキテクチャ:8つの論理レイヤー

最終的には、質問を次のような流れで処理します。

User
  ↓
1. LLM Advisor
  ↓
2. Request Resolver / Orchestrator
  ↓
3. Semantic Catalog
  ↓
4. Capability Registry
  ↓
5. Execution Contract
  ↓
6. Typed Executor
  ↓
7. Canonical Evidence Package
  ↓
8. Answer Permission / Evidence-only Renderer
  ↓
User

順番に説明します。


5.1 LLM Advisor

ここではLLMを自然言語の通訳として利用します。

例えば、

「先月のA支店の新規獲得率どうだった?」

を、

{
  "metric_candidate": "customer_acquisition_rate",
  "entity_candidate": "branch_a",
  "period_candidate": "previous_month"
}

のような構造化された候補へ変換します。

重要なのは、これはまだProposalだということです。

LLMには、

  • SQL
  • テーブル名の決定
  • KPI計算式
  • WHERE条件
  • 集計方法

を決める権限を持たせません。


5.2 Request Resolver / Orchestrator

次に、LLMのProposalと現在の会話状態を検証し、

Resolved Request

を作ります。

例えば、

{
  "metric": "customer_acquisition_rate",
  "entity": "branch_a",
  "period": "2026-08"
}

です。

ここでは、

現在の明示指定
 >
今回安全に引き継げる文脈
 >
過去の文脈

という優先順位を決定論的に適用します。

曖昧なら勝手に補完せず、

CLARIFY

へ送ります。


5.3 Semantic Catalog

Semantic Catalogは会社における意味の正本です。

例えば、

customer_acquisition_rate

について、

正式名称
説明
単位
粒度
期間の意味
対象母集団
同義語
関連指標

などを管理します。

例えば、

metric_id: customer_acquisition_rate
display_name: 新規顧客獲得率
unit: percent
grain:
  - branch
  - month

のようなイメージです。

ここで重要なのは、

KPI名とSQLを直接結び付けない

ことです。

意味と実装を分離します。


5.4 Capability Registry

Semantic Catalogに指標が存在していても、

システムがその指標を安全に計算できる

とは限りません。

そこでCapability Registryを別に持ちます。

例えば、

DEFINED
SOURCE_EXISTS
SEMANTICS_CONFIRMED
EXECUTION_APPROVED
UNSUPPORTED

のように、

この質問にシステムとしてどこまで責任を持てるのか

を管理します。

これにより、

KPIの意味は知っている
しかし実績値を正確に算出する経路は未承認

という状態を表現できます。

これは非常に重要です。


5.5 Execution Contract

ここが定量システムの中核です。

Execution Contractには、

このKPIをどう計算するか

を機械可読な形で固定します。

概念的には、例えば次のようになります。

{
  "metric_id": "customer_acquisition_rate",
  "source_id": "monthly_branch_metrics",
  "grain": [
    "branch",
    "month"
  ],
  "formula": {
    "type": "ratio",
    "numerator": "new_customers",
    "denominator": "visitors"
  },
  "filters": [
    {
      "field": "record_status",
      "operator": "=",
      "value": "valid"
    }
  ],
  "zero_denominator_policy": "null"
}

ポイントは、

Formula
WHERE条件
JOIN条件
粒度
NULL処理
0除算処理

をLLMのPromptの中に置かないことです。

それらは、

  • Version管理
  • Review
  • Test
  • 承認

可能なExecution Contractとして管理します。

JSON/YAMLにするか、型付きPythonコードにするかは実装上の選択ですが、

LLMではなく、レビュー可能な正本がAuthorityである

ことが重要です。


5.6 Typed Executor

Execution Contractを実行するのがTyped Executorです。

一般的なText-to-SQLでは、

User
 ↓
LLM
 ↓
自由記述SQL
 ↓
DB

となります。

この設計では、

Resolved Request
        +
Execution Contract
        ↓
Typed Compiler
        ↓
Parameterized Query
        ↓
DB

とします。

例えば、

WHERE branch_id = :branch_id
  AND target_month = :target_month

のように、ユーザーから変化する部分は値のパラメータだけにします。

LLMが、

WHERE ...
GROUP BY ...
JOIN ...

を自由に発明する余地をできるだけ排除します。


5.7 Canonical Evidence Package

DBから結果が返ってきても、そのままLLMへ渡しません。

一度、回答に利用可能な事実をCanonical Evidence Packageとして固定します。

例えば、

{
  "authority": {
    "metric_id": "customer_acquisition_rate",
    "execution_contract": "customer_acquisition_rate_v3",
    "grain": "branch_month"
  },
  "request": {
    "branch": "branch_a",
    "period": "2026-08"
  },
  "evidence": {
    "actual": 31.4,
    "unit": "%",
    "row_count": 1
  },
  "provenance": {
    "source": "monthly_branch_metrics",
    "executor": "ratio_metric_executor"
  },
  "render_policy": {
    "allowed_claims": [
      "actual_value",
      "branch",
      "period",
      "unit"
    ]
  }
}

この瞬間、

何を回答してよいか

を確定します。

これがLLMとのTruth Boundaryになります。


5.8 Answer Permission / Evidence-only Renderer

最後にLLMをもう一度使います。

ただし、このLLMは極端に権限の弱いLLMです。

入力されたEvidence Packageを、

「A支店の2026年8月の新規顧客獲得率は31.4%です。」

という自然な文章へ変換するだけです。

禁止事項は明確です。

Evidenceにない数字を追加しない
勝手に平均との差を計算しない
前年比を計算しない
目標値を補完しない
原因を断定しない

つまりLLMはアナウンサーです。

分析官でも、DBAでも、会計士でもありません。


6. 「Rendererまで縛る」理由

SQLだけ安全にすれば十分だと思われるかもしれません。

しかし、実際にはそうではありません。

例えばDBから、

{
  "actual": 31.4
}

が返ってきたあと、LLMが、

「31.4%で、全社平均より5ポイント高い好調な結果です」

と回答したらどうでしょうか。

SQLは一切間違っていません。

それでも、

全社平均
5ポイント
好調

という3つの新しい主張が生成されています。

そのため、

ExecutorだけでなくRendererにも契約が必要

です。

Evidence Packageに、

"allowed_claims": [
  "actual_value",
  "entity_scope",
  "period_scope"
]

のような制約を持たせる理由がここにあります。


7. 「Deterministic」は「正しい」と同義ではない

ここはかなり重要です。

決定論的なシステムを作ったからといって、

必ず正しい数字が出る

わけではありません。

間違った計算式をExecution Contractに登録すれば、

毎回同じ間違った数字

を返すだけです。

Deterministicが保証するのは、

同じContractと同じ入力なら、同じルールで実行される

という再現性です。

したがって、本当に必要なのは、

Semantic Definition
+
Execution Contract
+
Test
+
Human Review
+
Data Provenance

です。

これはLLMの問題というより、従来のデータ基盤やBIでも同じ話です。

LLMを導入したからこそ、今まで暗黙だったKPI定義を機械可読なContractへ落とし込む必要が出てきた、とも言えます。


8. 目指すのは「回答率100%」ではない

この設計で重要視している指標の1つが、

何でも答えられること

ではありません。

むしろ、

答えられない質問に対して、正しく答えられないと言えること

です。

例えば、

ANSWER
CLARIFY
UNSUPPORTED
INSUFFICIENT_EVIDENCE
ERROR

のように、質問のDisposition(処置結果)を明確にします。

例えば、

「この指標の目標値は登録されていますが、実績値を安全に算出するExecution Contractは現在ありません。」

という回答は、一見すると能力不足です。

しかし業務システムとしては、

適当な数字を答える

よりはるかに価値があります。

私はこれを、

Answer CoverageではなくDisposition Correctnessを上げる

と考えています。

全社KPIを対象にするシステムでの「万能性」は、

全質問に数字を返すこと

ではなく、

どんな質問に対しても、ANSWER / CLARIFY / UNSUPPORTEDを正しく選べること

ではないでしょうか。


9. Agentic AIを否定しているわけではない

ここまで読むと、

「ではAgentic AIは使わないのか?」

と思うかもしれません。

そうではありません。

むしろ私は、

探索にはAgentic、確定にはDeterministic

という分離が重要だと考えています。

例えばWeb検索では、

検索
 ↓
証拠不足
 ↓
検索語を書き換える
 ↓
再検索
 ↓
十分なら回答

といったAgenticな動きが有効です。

これは最初から正解のデータソースが決まっていない探索問題だからです。

一方、

「8月のA支店の売上はいくらか」

は探索問題ではありません。

会社が正式な数字として扱うのであれば、

どのデータソース
どの定義
どの期間
どの粒度
どの集計方法

を事前に確定しておくべきです。

したがって、

Web Research
    ↓
Bounded Agentic Executor

KPI Execution
    ↓
Deterministic Typed Executor

のように、用途ごとに権限を分離します。

大事なのは、

Agentic AIをシステム全体の最高権限にしない

ことです。


10. 既存システムを一気に作り直さない

実際のエンタープライズシステムでは、

旧SQL
旧KPI
既存API
既存ダッシュボード
既存チャット

が既に大量に存在します。

そのため、私は一括置換よりも段階移行の方が安全だと考えています。

例えば、

既存経路
   │
   ├─────────────→ Production
   │
   └→ 新ExecutorをShadow実行
             ↓
         結果比較
             ↓
          Dry Run
             ↓
       Limited Runtime
             ↓
          Production

という進め方です。

特に数字を扱うシステムでは、

「新しい設計の方が綺麗だから置き換える」

ではなく、

同じ質問に同じ答えが返ることをEvidenceで確認してから権限を移す

方が安全です。


11. この設計のメリット

この構造にすると、LLMとデータ基盤のライフサイクルを分離できます。

LLMが高性能になれば、

曖昧な質問の理解
同義語理解
会話UX
自然な文章生成

は改善します。

しかし、

KPI定義
Execution Contract
Typed Executor
Evidence Package

はそのまま利用できます。

つまり、

LLMを交換しても「会社における数字の意味」を作り直す必要がない

という構造になります。

逆に、データソースが変更された場合も、Execution ContractやExecutor側を更新すればよく、LLMのPromptに業務ロジックを大量に埋め込む必要がありません。


12. このアーキテクチャで最も重要だったこと

開発を通じて最も重要だと感じたのは、

LLMをどう賢くするか

ではありませんでした。

重要だったのは、

LLMがどこまで間違えてよいかを、システム構造として定義すること

でした。

自然言語の解釈を間違えることはあります。

その場合はClarifyすればよい。

文章表現が多少不自然になることもあります。

それは改善できます。

しかし、

存在しない売上を作る
分母を勝手に変更する
期間を無言で補完する
別店舗の数字を混ぜる

ことは許容できません。

だからこそ、

LLM Proposal
      ↓
Deterministic Decision
      ↓
Typed Execution
      ↓
Canonical Evidence
      ↓
Evidence-only Generation

という境界を作りました。


まとめ

LLMは非常に優秀です。

だからこそ、何でも任せたくなります。

しかし、エンタープライズの定量データでは、

「できること」と「やらせてよいこと」は別

です。

RAGもAgentic AIもText-to-SQLも、それ自体が悪いわけではありません。

問題は、

どこにAuthorityを置くか

です。

私が採用した原則をまとめると、次のようになります。

LLMはProposalを作れる。
しかしDecisionは作れない。

LLMは質問を理解できる。
しかしKPI定義は変更できない。

LLMは検索を提案できる。
しかしExecution Contractは変更できない。

LLMは文章を生成できる。
しかしEvidenceにない数字は生成できない。

「LLMをもっと賢くすれば、いつか間違えなくなる」という前提でシステムを作るのではなく、

LLMが間違えても、会社の事実までは曲げられない

構造にする。

そして、Agenticな探索能力とDeterministicなAuthorityを分離する。

これが、長期間運用できるエンタープライズ向けデータチャットを作るうえで、一つの有効なアプローチだと考えています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?