エンタープライズのバックエンドにLLMを組み込む3つのアプローチ:どれを選ぶべきか?
ERP、CRM、社内ヘルプデスクシステムにLLMを組み込む作業は、単にプロンプトを送信してテキストを受け取るだけでは済みません。実際の現場において、エンジニアは主に以下の3つの選択肢を比較検討することになります。
- API直接呼び出し(OpenAI SDK / REST):生のコードを書き、自前でJSONスキーマをパースし、Function Callingのループ処理を手作業で実装する。
- LangChainの採用:巨大なエコシステムと豊富な既存プラグインが魅力。その反面、過度な抽象化(abstraction)によって、システム肥大化時のデバッグやデータスキーマの制御が非常に困難になる。
- Microsoft Semantic Kernel(SK)の採用:DI(依存性の注入)、厳格な型安全性、明示的で透明性の高いプラグインオーケストレーションなど、エンタープライズアーキテクチャの思想に忠実なオープンソースフレームワーク。
3つの選択肢の実態比較
1. API直接呼び出し:立ち上がりは速いが、後から破綻しやすい
最大のメリットは軽量である点です。不要な依存関係を追加することなく、リクエストペイロードをバイト単位で完全にコントロールできます。
しかし、これがうまく機能するのはツールの数が少ない初期段階に限られます。ビジネスロジックの関数が20〜30個に達した途端、リトライ処理、レート制限、コンテキストメモリ、プロンプトのオーケストレーションといった無数の課題が一気に押し寄せ、コードベースは保守不能なスパゲッティコードへと陥りがちです。
2. LangChain:プロトタイピングには最適だが、本番運用には注意が必要
LangChainは、数日程度でプロトタイプを作成したり、基本的なRAGパイプラインを構築したりする用途には極めて優秀です。
しかし、24時間365日稼働するエンタープライズプロジェクトに持ち込む場合、頻繁なリリースに伴う破壊的変更(breaking changes)に悩まされることが少なくありません。さらに、内部で自動的に行われる暗黙的な推論機構のせいで、ISO準拠の監査ログ記録やセキュリティ監査時のトレーサビリティ確保に多くの課題が生じます。
3. Microsoft Semantic Kernel:エンタープライズ標準の堅牢性
Semantic Kernelは、明示的で予測可能、かつ厳格な制御を重視する伝統的なソフトウェア開発の思想に基づいています。Native Pluginsの仕組みにより、わずか数行のType Hints(型ヒント)を付与するだけで、通常のPython関数をAgentから安全に呼び出せるツールへと変換できます。
- メリット:PydanticおよびPython標準の型定義を最大限に活用でき、Semantic Functions(プロンプトテンプレート)とNative Functions(通常のPythonコード)をシームレスに併用可能。pytestによる単体テストが極めて容易で、マイクロサービスアーキテクチャとも親和性が高い。
- デメリット:Python SDKのドキュメント整備状況は、先行するC#/.NET版に比べて一歩遅れている。また、サードパーティ製プラグインのコミュニティ規模もLangChainほど大きくはない。
どのようなケースでSemantic Kernelを採用すべきか?
「明日までに上長へデモを見せなければならない」という状況であれば、LangChainを使うかAPIを直接叩くのが最適解です。
しかし、電子マネーの残高引き落とし、データベースの500行一括更新、顧客への注文確認メール送信など、基幹業務データに直接関わるAgentを構築する場合は、厳格なパラメータバリデーション機構を備えたSemantic Kernelのほうが遥かに安全な選択肢となります。
40以上の社内APIと連携する実際のERPプロジェクトにおいて、私たちのチームはAgentが文字列(string)と数値(int)のデータ型を誤って渡してしまう問題に直面しました。しかし、Semantic Kernelが型ヒントに基づくスキーマレイヤーで即座にエラーを検知してくれたおかげで、不正なクエリがデータベースに到達する前に確実にブロックできました。
Semantic KernelとNative Pluginの実装ステップ
ステップ1:環境構築
Semantic Kernelの実行にはPython 3.10以上が必要です。仮想環境を作成し、必要なライブラリをインストールします。
python3 -m venv venv
source venv/bin/activate
pip install semantic-kernel python-dotenv
ステップ2:ビジネスロジックを組み込んだNative Pluginの実装
Native Pluginの実態は通常のPythonクラスです。AIに実行権限を与えたい関数に対して、@kernel_functionデコレータを付与します。
特に重要な点として、Annotatedを使用してdocstringと型ヒントを極めて詳細に定義する必要があります。Semantic Kernelはこれらの説明文をそのまま解析し、LLMに渡すFunction Definitionを自動生成します。
from typing import Annotated
from semantic_kernel.functions import kernel_function
class InventoryPlugin:
"""社内在庫の照会および更新を行うプラグイン。"""
@kernel_function(
name="check_stock",
description="製品コード(SKU)の在庫数量を確認します。"
)
def check_stock(
self,
sku: Annotated[str, "照会する製品のSKUコード(例:SKU-101)"]
) -> str:
# 社内データベースへのクエリをシミュレート
mock_db = {
"SKU-101": 15,
"SKU-202": 0,
"SKU-303": 42
}
quantity = mock_db.get(sku.upper(), None)
if quantity is None:
return f"在庫システム内に製品コード {sku} が見つかりませんでした。"
return f"製品 {sku} の現在の在庫数は {quantity} 個です。"
@kernel_function(
name="create_restock_ticket",
description="在庫切れ製品の補充申請チケットを発行します。"
)
def create_restock_ticket(
self,
sku: Annotated[str, "補充が必要なSKUコード"],
quantity: Annotated[int, "補充申請する数量(例:50)"]
) -> str:
return f"補充チケット #TK-{sku}-99(数量: {quantity})の発行が完了しました。"
ステップ3:Kernelの初期化と自動関数呼び出し(Auto Invocation)の有効化
main.pyを作成します。この例ではOpenAIのGPT-4o-miniを使用していますが、コネクタを差し替えるだけでAzure OpenAIのエンドポイントへも同様に接続可能です。
import asyncio
import os
from dotenv import load_dotenv
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAIChatPromptExecutionSettings
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from inventory_plugin import InventoryPlugin
load_dotenv()
async def main():
# 1. オーケストレーションを司るKernelの初期化
kernel = Kernel()
# 2. LLMサービスの登録
chat_service = OpenAIChatCompletion(
service_id="enterprise_agent",
ai_model_id="gpt-4o-mini",
api_key=os.getenv("OPENAI_API_KEY")
)
kernel.add_service(chat_service)
# 3. Native PluginをKernelへロード
kernel.add_plugin(InventoryPlugin(), plugin_name="inventory")
# 4. 自動関数呼び出し(Auto Invocation)機能の有効化
execution_settings = OpenAIChatPromptExecutionSettings(
service_id="enterprise_agent",
temperature=0.2,
function_choice_behavior=FunctionChoiceBehavior.Auto(auto_invoke=True)
)
# 5. 2つの関数を連続して連携実行するプロンプトの処理
user_prompt = "製品SKU-202の在庫はまだありますか?もし在庫切れなら、50個の補充申請チケットを発行してください。"
print(f"User: {user_prompt}\n")
response = await kernel.invoke_prompt(
prompt=user_prompt,
settings=execution_settings
)
print(f"Agent:\n{response}")
if __name__ == "__main__":
asyncio.run(main())
本番環境(Production)導入における4つの実践プラクティス
- 型ヒント(Type Hints)の省略は厳禁:LLMがデータ型を正確に推測することは不可能です。
Annotated[型, "説明文"]を徹底し、AIによる勝手なパラメータ生成の原因となるAny型の使用は絶対に避けてください。 - FunctionInvocationFilterによるリスク制御:Agentに無制限のデータ更新権限を与えてはなりません。Semantic Kernelでは、関数呼び出しの前段にフィルタを挟むことで、ユーザー権限(RBAC)の検証や、送金・アカウント削除といったセンシティブな処理に対する人間の承認フロー(Human-in-the-loop)を強制できます。
- べき等性(Idempotency)の担保:ネットワークの瞬断やコンテキストの欠落により、LLMが同一ツールを2回リトライして実行してしまうケースがあります。状態を変更するすべての処理(POST/UPDATE)には必ず
idempotency_keyを実装し、注文などの二重生成を防止してください。 - Native Function内部で例外を完全にハンドリング:データベースのクエリエラーなどでプロセス全体をクラッシュさせてはいけません。処理ロジックを
try/exceptブロックで適切に囲み、「エラー:在庫サーバーに一時的に接続できません。しばらく経ってから再試行してください」といったわかりやすい文字列メッセージを返却します。AIはそのエラーメッセージを解釈し、エンドユーザーに対して適切なトーンで状況を説明してくれます。

