👾

AI エージェントフレームワーク Semantic Kernel 超入門!!

に公開

Semantic Kernel(セマンティック カーネル) は Microsoft が提供するオープンソースの SDK で AI をアプリケーションに簡単に組み込むことができるツールです。AI エージェントも勿論作成できます。
本記事は Semantic Kernel(セマンティック カーネル) 超入門!簡単に概要と大まかな実装をご紹介します。

Semantic Kernel 何がすごいの?

単なる Copilot ではなく、もっとカスタマイズができる AI エージェントを会社、製品に導入したい!でもやり方が分からない、開発に時間をかけたくないけど信頼性の高いフレームワークを利用したい、、、時に活躍するのが Semantic Kernel(セマンティック カーネル) です。
Semantic Kernel により、AI エージェントを簡単に構築し、業務の簡易化、効率化に繋げることができます。

簡単に特徴を挙げると以下 3 つです:

  1. 大規模なエンタープライズ AI ソリューションに対応可能
    • C#、Python、Java の 3 つの言語をサポート(C#、Python がメイン)
    • テレメトリのサポート、フックやフィルターなどのセキュリティ強化機能あり
  2. ビジネスプロセスの自動化
    • 既存のコードや API をプラグインとして組み込み可能。この拡張性の高さにより AI の業務導入をより行いやすく
    • 実行すべきコードや API は AI が自律的に判断
  3. 未来志向の設計
    • 最新の AI モデルに簡単に接続でき、新しいモデルがリリースされた際にもコード全体を書き換えずにモデルの交換が可能

Semantic Kernel エンタープライズ対応の図

Semantic Kernel の仕組み

Semantic Kernel の主な構成要素は以下の 5 つです。

  1. カーネル
    「カーネル」は、Semantic Kernel で提供する機能の中核です。AI モデルやプラグイン、メモリー(ベクターストア) はカーネルに登録され、カーネルにより管理されます。
  2. AI サービスコネクタ
    AI モデルを呼び出すための抽象化レイヤーです。モデルごとに呼び出し方法が異なりますが、コネクタを挟むことで カーネルからシームレスにモデルの呼び出しが可能になっています。
  3. プラグイン
    AI モデルの能力を拡張する機能です。既存のコードや API をプラグインとしてカーネルに組み込むことで、AI がそれらを呼び出すことができます。
  4. メモリ(ベクターストア)コネクタ
    CosmosDB などのデータベースに AI モデルを接続させる機能です。メモリーをカーネルに登録することで、AI モデルにより必要に応じて自動的に呼び出しするように構成できます。この機能により容易に検索拡張生成(RAG)を実現できます。

カーネル

AI モデル、プラグイン、メモリーなど AI アプリケーションの実行に必要な機能がすべてカーネルで一元管理されることにより開発者にとってシンプルで分かりやすく監視もしやすい構成となっています。
大雑把に言ってしまえば、AI の呼び出しや関数の実行など Semantic Kernel での処理は Kernel を経由しているとイメージするとわかりやすいと思います。

参照: カーネルについて | Microsoft Learn
Kernel is at the center​

AI サービスコネクタ

OpenAI や Azure OpenAI, Anthropic, Gemini, Mistral AI などの主要な AI サービスはコネクタが用意されており、これを活用して簡単にモデルの呼び出しができます。
コネクタは、以下コードの一行目のようにインポート後、キーやモデルのデプロイ名など呼び出しに必要な情報を環境変数としてセットするか、もしくは引数としてコネクタに渡すことで初期化されます。
例えば以下コードでは環境変数としてキーやモデルのデプロイ名などの必要な情報を登録してあるためたった 5 行でカーネルにモデルの登録が完了します。

参照: チャット入力候補 | Microsoft Learn

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion

# You can do the following if you have set the necessary environment variables or created a .env file
chat_completion_service = AzureChatCompletion(service_id="my-service-id")

# Initialize the kernel
kernel = Kernel()

# Add the chat completion service created above to the kernel
kernel.add_service(chat_completion_service)

コネクタが用意されていないモデルは、Python であれば AIServiceClientBase クラスで独自に定義することも可能と思われますが、まずは既存のコネクタを使うことをお勧めします。

参考: AI サービスコネクタの一覧表

対応している AI サービスコネクタ
OpenAI HuggingFace
Azure OpenAI Mistral AI
Azure AI Inference Nvidia
Anthropic Ollama
Bedrock Onnx
Google AI
Vertex AI

プラグイン

ほとんどの最新の AI モデルでデフォルトとなっている関数呼び出し機能を利用を利用して、API の呼び出しやアプリケーションコードをプラグインとして Semantic Kernel で利用することができます。これにより AI 単体では実行が難しい、または不安定な処理を安定して実行できるようにし、AI の活用の幅や動作性を向上させることができます。

プラグインは、以下のような方法で定義できます。

参照: プラグインとは
プラグインの図

代表として Python コード(クラス) でプラグインを定義する例をご紹介します。
プラグインを Kernel に登録すると、AI が必要な時に自動的に呼び出します。関数がうまく AI から呼び出されない場合は、関数名や説明が不足しているか、AI にとってわかりづらいものになっている可能性があります。
AI は、関数の説明「Get the current location of the user」や 関数名、関数が返却する型とアノテーション(str 型、The current location) を参考にして関数の役割を理解し呼び出します。これらの値を詳細にしたり分かりやすいものを記載することで改善される場合があります。

from typing import Annotated

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, AzureChatPromptExecutionSettings
from semantic_kernel.core_plugins import TimePlugin
from semantic_kernel.functions import KernelArguments
from semantic_kernel.functions.kernel_function_decorator import kernel_function


class LocationPlugin:
    """A demo plugin for getting the location of a place."""

    # 関数で AI に実行させたい処理を記載
    @kernel_function(name="get_current_location", description="Get the current location of the user")
    def get_current_location(self) -> Annotated[str, "The current location"]:
        """Get the current location of the user."""
        return "NEW YORK, USA"

# カーネルを作成し、AI サービスとプラグインを追加
kernel = Kernel()
kernel.add_service(
    AzureChatCompletion(
        service_id="default",
    )
)
kernel.add_plugin(
    LocationPlugin(),
    plugin_name="LocationPlugin",
)
# デフォルトで用意されているプラグインを使用する方法もあります。こちらでは TimePlugin を使用
kernel.add_plugin(TimePlugin(), plugin_name="Time")

# 関数の実行設定を構成
# excluded_plugins に指定したプラグインは実行されません
req_settings = AzureChatPromptExecutionSettings(service_id="default", tool_choice="auto")
req_settings.function_choice_behavior = FunctionChoiceBehavior.Auto(filters={"excluded_plugins": ["Time"]})

# カーネルに送信する構成を準備
arguments = KernelArguments(settings=req_settings)

# カーネルからプロンプトを実行します。適宜必要な関数が実行されます
answer = await kernel.invoke_prompt("What is the current location?", arguments=arguments)
print(f"AI:> {answer}")
#print(answer.__str__) # answer.__str__ で実行結果の詳細が確認できます
answer = await kernel.invoke_prompt("What time is it?", arguments=arguments)
print(f"AI:> {answer}")
#print(answer.__str__) # answer.__str__ で実行結果の詳細が確認できます

メモリ(ベクターストア)コネクタ

Azure AI Search や CosmosDB、Postgress などのベクターストアに Semantic Kernel から接続できるようにする機能です。コネクタを使用して直接ベクターストアとやり取りすることもできますし、コネクタをプラグインとして実装し AI から活用できるようにする方法もあります。
ベクターストアとのやり取りは、SDK などを使用して独自実装することも可能ですが、コネクタを使うことで少ないコードで、安全に実現できます。

Semantic Kernel の実装を見ると、メモリ(ベクターストア)コネクタとして MemoryStore, Collection の二つのクラスが用意されています。それぞれのクラスでベクターストアとやり取りを行うことができます。本記事では Collection クラスを使った方法をご紹介します。

直接ベクターストアとやり取りする例

大まかに以下のような流れで構成します。

  1. Kernel を初期化し、Embedding など必要なサービスを追加します
  2. データモデルを定義します(Collectionクラスを使う場合)
  3. メモリ(ベクターストア)コネクタを作成します

実装例: https://github.com/microsoft/semantic-kernel/blob/ca382f926ab20e85f05538d5d0f919f77ce44fa8/python/samples/concepts/memory/simple_memory.py(一部抜粋)

# 1 ) Kernel を初期化し、Embedding など必要なサービスを追加します
kernel = Kernel()
embedder = AzureTextEmbedding(service_id="embedding")
kernel.add_service(embedder)

# 2 ) データモデルを定義します(Collectionクラスを使う場合)
@vectorstoremodel
@dataclass
class DataModel:
    vector: Annotated[
        list[float] | None,
        VectorStoreRecordVectorField(
            embedding_settings={"embedding": OpenAIEmbeddingPromptExecutionSettings()},
            index_kind=INDEX_KIND,
            dimensions=1536,
            distance_function=DISTANCE_FUNCTION,
            property_type="float",
        ),
    ] = None
    id: Annotated[str, VectorStoreRecordKeyField()] = field(default_factory=lambda: str(uuid4()))
    content: Annotated[
        str,
        VectorStoreRecordDataField(
            has_embedding=True,
            embedding_property_name="vector",
            property_type="str",
            is_full_text_searchable=True,
        ),
    ] = "content1"


# 3 ) メモリ(ベクターストア)コネクタを作成します
## semantic_kernel.connectors.memory から接続したいベクターストアをインポートします。以下例ではメモリ上に作成したベクターストアと接続しています
from semantic_kernel.connectors.memory.in_memory import InMemoryVectorCollection

async with InMemoryVectorCollection[str, DataModel](
    collection_name="test",
    data_model_type=DataModel,
) as record_collection:
    await record_collection.delete_collection()
    await record_collection.create_collection_if_not_exists()

    # 情報の追加
    records_with_embedding = await add_vector_to_records(kernel, records, data_model_type=DataModel)

    # 情報の検索
    search_results = await record_collection.vectorized_search(
            vector=(await embedder.generate_raw_embeddings([query]))[0],
            options=options,
        )

    # 情報の削除
    await record_collection.delete_collection()

プラグインとして活用する例(RAG)

流れは、直接ベクターストアとやり取りする例 と 3 まで同じです。ベクターストアを作成したら、プラグインとして Kernel に登録します。これで AI がこのプラグインを使用して回答ができます。

  1. AI サービスや Embedding など必要なサービスをカーネルに登録
  2. データモデルを定義します(Collectionクラスを使う場合)
  3. メモリ(ベクターストア)コネクタを作成します
  4. コネクタをプラグインとして Kernel に登録します

実装例: https://github.com/microsoft/semantic-kernel/blob/ca382f926ab20e85f05538d5d0f919f77ce44fa8/python/samples/concepts/memory/azure_ai_search_hotel_samples/step_2_use_as_a_plugin.py(一部抜粋)

# 1 ) AI サービスや Embedding など必要なサービスをカーネルに登録
kernel = Kernel()
service_id = "chat"
kernel.add_service(OpenAIChatCompletion(service_id=service_id))
embeddings = OpenAITextEmbedding(service_id="embedding", ai_model_id="text-embedding-3-small")
kernel.add_service(embeddings)

# 2 ) データモデルを定義します
@vectorstoremodel
class HotelSampleClass(BaseModel):
    ...

# 3 ) メモリ(ベクターストア)コネクタを作成します
collection = AzureAISearchCollection[str, HotelSampleClass](
    collection_name=COLLECTION_NAME, data_model_type=HotelSampleClass
)

# 4 ) コネクタをプラグインとして Kernel に登録します
text_search = collection.create_text_search_from_vector_text_search()
plugin = kernel.add_functions(
    plugin_name="azure_ai_search",
    functions=[
        text_search.create_search(
            # This second function is a more detailed one, that uses a `hotel_id` to get details about a hotel.
            # we set the top to 1, so that only 1 record is returned.
            function_name="get_details",
            description="Get details about a hotel, by ID, use the overview function to get the ID.",
            options=VectorSearchOptions(
                top=1,
            ),
            parameters=[
                KernelParameterMetadata(
                    name="hotel_id",
                    description="The hotel ID to get details for.",
                    type="str",
                    is_required=True,
                    type_object=str,
                ),
                KernelParameterMetadata(
                    name="hotel_name",
                    description="The name of the hotel.",
                    type="str",
                    type_object=str,
                    is_required=True,
                ),
            ],
        ),

Semantic Kernel のフレームワーク

Semantic Kernel には、2 つフレームワークが用意されています。本記事では多くのシナリオで採用されるエージェントフレームワークについてご紹介します。

  1. エージェントフレームワーク
    自律的または半自律的にタスクを実行するようなエージェントを作成し、特定のタスクやプロセスオーケストレーションを行う
  2. プロセスフレームワーク
    複雑なワークフローやビジネスプロセスの自動化を行う

Semantic Kernel Agent Framework(エージェントフレームワーク)

エージェントフレームワークを使用すると、Semantic Kernel を使って AI エージェントの作成が可能になり、エージェントパターンをアプリケーションに簡単に組み込むことができます。

AI エージェントのメリット

次のような主な利点があります。

  • 特定のタスクや分野に特化:
    コールセンターエージェントや、営業支援エージェント、特定の文書や業界調査エージェントなど、特定の業務やタスクに特化したさまざまな種類のエージェントを作成できます。目的が明確なエージェントはユーザーにとっても使いやすく、業務への導入を行いやすくします。
  • コラボレーション:
    複数のエージェント同士で協力してタスクを実行することができます。例えば、あるエージェントがデータ収集を処理し、別のエージェントがデータを分析し、さらに別のエージェントが結果を使用して意思決定を行うといった共同作業ができます。これによりエージェント単体では実行が難しい複雑なタスクへの適用を可能にします。
  • Human-in-the-loop:
    AI がまずは対応して、解決できなければ人間が対応するといった方法で、AI と人間が協力してタスクを行います。AI のみでは不安な作業もこれにより導入を行いやすく、また精度の安定を待たずに早期導入することもできます。
  • プロセス オーケストレーション:
    エージェントは、システム、ツール、API などさまざまなタスクの調整を行うことができ、従来のルールベースではシステム化が難しかったオーケストレーション プロセスに役立ちます。例えば、営業支援のエージェントでは、顧客行動分析、市場調査、ミーティングの調整といった複数のタスクを調整することによりさらに複雑な業務の自動化、効率化を推進することができます。

エージェントの作成と実行の流れ

ChatCompletionAgent など、Agent クラスを使用してエージェントを作成します。Semantic Kernel で使用可能なエージェントは、semantic_kernel.agents よりインポートできます。
エージェントの作成は以下 2 つの方法があり、どちらでも問題ありません。

# Define agent
agent = ChatCompletionAgent(...)
1. チャット完了サービスを直接提供する
from semantic_kernel.agents import ChatCompletionAgent

# Create the agent by directly providing the chat completion service
agent = ChatCompletionAgent(
    service=AzureChatCompletion(),  # your chat completion service instance
    name="<agent name>",
    instructions="<agent instructions>",
)
2. 最初にカーネルを作成し、そのカーネルにサービスを追加し、次にカーネルを提供します
# Define the kernel
kernel = Kernel()

# Add the chat completion service to the kernel
kernel.add_service(AzureChatCompletion())

# Create the agent using the kernel
agent = ChatCompletionAgent(
  kernel=kernel, 
  name="<agent name>", 
  instructions="<agent instructions>",
)

複数の AI サービスが Kernel に登録されている場合、AzureChatPromptExecutionSettings インスタンスを使用して サービスを選択することもできます。

from semantic_kernel.connectors.ai.open_ai import (
    AzureChatCompletion,
    AzureChatPromptExecutionSettings,
)

# Define the Kernel
kernel = Kernel()

# Add the AzureChatCompletion AI Service to the Kernel
kernel.add_service(AzureChatCompletion(service_id="service1"))
kernel.add_service(AzureChatCompletion(service_id="service2"))

settings = AzureChatPromptExecutionSettings(service_id="service2")

# Create the agent
agent = ChatCompletionAgent(
  kernel=kernel, 
  name="<agent name>", 
  instructions="<agent instructions>",
  arguments=KernelArguments(settings=settings)
)

エージェントと会話するには複数方法があります。最も簡単な方法は、get_response メソッドを呼び出す方法です。エージェントで呼び出し館の会話履歴を保持する場合は、ChatHistoryAgentThread を使用します。

# Define agent
agent = ChatCompletionAgent(...)

# Generate the agent response
response = await agent.get_response(messages="user input")
# response is an `AgentResponseItem[ChatMessageContent]` object

# Generate another response, continuing the conversation thread from the first response.
response2 = await agent.get_response(messages="user input", thread=response.thread)

実装例

以下実装例では、billing_agent と refund_agent という特定のタスクに特化したエージェントを triage_agent エージェントが取りまとめして調整します。

実装例: https://github.com/microsoft/semantic-kernel/blob/ca382f926ab20e85f05538d5d0f919f77ce44fa8/python/samples/concepts/agents/chat_completion_agent/chat_completion_agent_as_kernel_function.py(一部抜粋)

# Create and configure the kernel.
kernel = Kernel()

# The filter is used for demonstration purposes to show the function invocation.
kernel.add_filter("function_invocation", function_invocation_filter)

billing_agent = ChatCompletionAgent(
    service=AzureChatCompletion(),
    name="BillingAgent",
    instructions=(
        "You specialize in handling customer questions related to billing issues. "
        "This includes clarifying invoice charges, payment methods, billing cycles, "
        "explaining fees, addressing discrepancies in billed amounts, updating payment details, "
        "assisting with subscription changes, and resolving payment failures. "
        "Your goal is to clearly communicate and resolve issues specifically about payments and charges."
    ),
)
refund_agent = ChatCompletionAgent(
    service=AzureChatCompletion(),
    name="RefundAgent",
    instructions=(
        "You specialize in addressing customer inquiries regarding refunds. "
        "This includes evaluating eligibility for refunds, explaining refund policies, "
        "processing refund requests, providing status updates on refunds, handling complaints related to refunds, "
        "and guiding customers through the refund claim process. "
        "Your goal is to assist users clearly and empathetically to successfully resolve their refund-related concerns."
    ),
)

triage_agent = ChatCompletionAgent(
    service=AzureChatCompletion(),
    kernel=kernel,
    name="TriageAgent",
    instructions=(
        "Your role is to evaluate the user's request and forward it to the appropriate agent based on the nature of "
        "the query. Forward requests about charges, billing cycles, payment methods, fees, or payment issues to the "
        "BillingAgent. Forward requests concerning refunds, refund eligibility, refund policies, or the status of "
        "refunds to the RefundAgent. Your goal is accurate identification of the appropriate specialist to ensure the "
        "user receives targeted assistance."
    ),
    plugins=[billing_agent, refund_agent],
)

thread: ChatHistoryAgentThread = None


async def chat() -> bool:
    """
    Continuously prompt the user for input and show the assistant's response.
    Type 'exit' to exit.
    """
    try:
        user_input = input("User:> ")
    except (KeyboardInterrupt, EOFError):
        print("\n\nExiting chat...")
        return False

    if user_input.lower().strip() == "exit":
        print("\n\nExiting chat...")
        return False

    # ユーザーの入力を元に triage_agent から回答を取得します
    # triage_agent は必要に応じて billing_agent と refund_agent を実行し、情報を収集します
    response = await triage_agent.get_response(
        messages=user_input,
        thread=thread,
    )

    if response:
        print(f"Agent :> {response}")

    return True

async def main() -> None:
    print("Welcome to the chat bot!\n  Type 'exit' to exit.\n  Try to get some billing or refund help.")
    chatting = True
    while chatting:
        chatting = await chat()

Semantic Kernel の学び方

最後に、Semantic Kernel の学び方をご紹介します。
公式レポジトリにチュートリアルが公開されており、こちらをまず一通り実施することをお勧めします。次にソースコードで実装を確認する方法も理解が深まります。
公式ドキュメントは読みやすいですが、どうしても更新頻度がソースコード自体と比べると遅れてしまうので参考程度にご確認いただくのがいいかなと思います。
最後に、ほぼ毎週のようにリリースがある Semantic Kernel ですが、リリースノートに結構細かくアップデートが書かれています。参考になるコメントもあったりするのでこちらもぜひ参照ください。

まとめ

本記事では、Semantic Kernel の基本をざっくりおまとめしました。少しでもどなたかのお役に立てましたら嬉しいです。

では!

Microsoft (有志)

Discussion