🛬

Semantic Kernel Getting Started Part2 Planner編

2023/09/24に公開

そもそもSemantic Kenrelって何なの?

前回から突然Semantic Kernelの使い方の記事を始めたが、そもそもSemantic Kernelが何なのかを知らない人が大半ではないかと思う。非常におおざっぱだがSemantic Kernel(以下SK)とはMicrosoftが提供?しているLangChainだと思えばよい。LangChainも知らない人はこの記事は読まないと思うが要はAIオーケストレーターSDKである

ではなぜAIオーケストレーターが必要なのかというと、ChatGPT先生はこう言っている

開発の簡素化
Semantic KernelのようなAIオーケストレーションSDKは、さまざまなAIサービスを統合するための統一されたインターフェースを提供します。これにより、開発者は使用したい各AIサービスのAPIを個別に学ぶ必要がなくなります。

再利用性とモジュール性
このようなSDKは、異なるプロジェクトで再利用できるプリビルトのプラグインやモジュールがよく含まれています。このモジュール性により、開発サイクルが速くなり、繰り返しの作業が減少します。

効率的なリソース利用
オーケストレーションSDKは、リソースの割り当てと利用をより効率的に管理でき、AIモデルとサービスが最適に使用されるようにします。

高度な機能
SDKを使用すると、APIを直接使用する場合には利用できないような計画機能やAIメモリなどの追加機能が提供されることが多いです。これらの機能は、より高度なAIアプリケーションを作成する上で重要です。

エンタープライズレベルのサポート
ビジネスにとって、エンタープライズアプリ開発者をサポートするように設計されたSDK(例:Semantic Kernel)を使用することで、AIの統合が堅牢で、スケーラブルで、維持可能であることが確保されます。

ワークフローの効率化
オーケストレーションSDKは、複雑なタスクやパイプラインを自動化することで、大規模にAIソリューションを展開する際のワークフローを効率化します。

要するに、AIオーケストレーションSDKは、AIを既存のアプリやサービスに統合するための構造化された効率的な方法を提供し、開発を加速させ、複雑性を減らし、より堅牢で強力なAI実装につながる可能性があります。

個人的にはSemantic KernelはMicrosoftが提供しておりエンタープライズで使いやすいのと、LangChainと比べてシンプルなのが気に入っている

notebookの続き

では引き続き公式レポジトリにあるnotebookをやっていく。前回は04まで動かしてみた

05-using-the-planner.ipynb

いよいよPlannerの登場である。Plannerとは公式ページによると

Plannerは、ユーザーからのリクエストを受け取り、そのリクエストを達成するためのプランを返す機能である。AIを使ってカーネルに登録されているプラグインをミックス・アンド・マッチさせることで、目標を達成する一連のステップに組み替えることができる。
これは強力なコンセプトで、開発者が思いつかないような使い方ができるアトミック関数を作ることができる。
例えば、タスクとカレンダーのイベントプラグインがあれば、プランナーはそれらを組み合わせて、「買い物に行ったら牛乳を買うようにリマインドする」とか、「明日母に電話するようにリマインドする」といったワークフローを、明示的にコードを書くことなく作ることができる。

などとある。要はChatGPTのプラグインのようなものだ。ChatGPTのプラグインもユーザーがどのプラグインを使うべきか指示をしなくても、AIが勝手に必要にプラグインを選んでタスクを実行してくれる。

そしてPlannerにはBasic Planner、Action Planner、Sequential Planner、Stepwise Plannerの4種類のPlannerがあり、だんだん高度になっていくようだ

import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, AzureChatCompletion

kernel = sk.Kernel()

useAzureOpenAI = True

# Configure AI backend used by the kernel
if useAzureOpenAI:

    deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
    kernel.add_chat_service("gpt-3.5", AzureChatCompletion(deployment, endpoint, api_key))
else:
    api_key, org_id = sk.openai_settings_from_dot_env()
    kernel.add_chat_service("gpt-3.5", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id))

これはいつものやつ

ask = """
Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French.
Convert the text to uppercase"""

askはSK用語の一つでユーザーからの要求や与えられたタスクである

from semantic_kernel.core_skills.text_skill import TextSkill

skills_directory = "../../samples/skills/"
summarize_skill = kernel.import_semantic_skill_from_directory(skills_directory, "SummarizeSkill")
writer_skill = kernel.import_semantic_skill_from_directory(skills_directory, "WriterSkill")
text_skill = kernel.import_skill(TextSkill(), "TextSkill")

ここではimportしたTextSkillとディレクトリにあるSummarizeSkill、WriterSkillを読み込んでSemanticスキルを作成している

Basic Planner

まずはBasic Plannerから見ていく

from semantic_kernel.planning.basic_planner import BasicPlanner
planner = BasicPlanner()

Basic Plannerのライブラリをインポートし、オブジェクトを作成

basic_plan = await planner.create_plan_async(ask, kernel)

askとkernelを引数としてPlanを作成

print(basic_plan.generated_plan)

作成されたPlanの中身をみてみると、

{
    "input": "Valentine's Day Date Ideas",
    "subtasks": [
	{"function": "WriterSkill.Brainstorm"},
	{"function": "WriterSkill.EmailTo"},
	{"function": "WriterSkill.Translate", "args": {"language": "French"}},
	{"function": "TextSkill.uppercase"}
    ]
}

Plannerがaskを受け取り、利用できるスキルを使ってPlanを作成している。WriterSkillとTextSkillを使い、SummarizeSkillは使わないという判断をしたようだ。しかしEmailToという不要なスキルもあったりするのはご愛敬か

sk_prompt = """
{{$input}}

Rewrite the above in the style of Shakespeare.
"""
shakespeareFunction = kernel.create_semantic_function(sk_prompt, "shakespeare", "ShakespeareSkill",
						      max_tokens=2000, temperature=0.8)

ここで何故かshakespeareFunctionというインラインスキルを定義

 ask = """
Tomorrow is Valentine's day. I need to come up with a few date ideas.
She likes Shakespeare so write using his style. She speaks French so write it in French.
Convert the text to uppercase."""

new_plan = await planner.create_plan_async(ask, kernel)

そしてaskをアップデートし、新しくプランを作成。再度プランを見てみると、

{
    "input": "Valentine's Day Date Ideas",
    "subtasks": [
	{"function": "WriterSkill.Brainstorm"},
	{"function": "ShakespeareSkill.shakespeare"},
	{"function": "WriterSkill.Translate", "args": {"language": "French"}},
	{"function": "TextSkill.uppercase"}
    ]
}

おお、作成したシェイクスピアスキルが採用されている!

さて、いよいよプランの実行である

results = await planner.execute_plan_async(new_plan, kernel)

実行に59秒もかかったが無事完了!どんな結果になるか楽しみだ

print(results)

ASSUREZ-VOUS DE N'UTILISER QUE LE FRANÇAIS.

1. UN DÎNER ROMANTIQUE AUX CHANDELLES DANS UNE TAVERNE CHIC,
OÙ LES FLAMMES DOUCES DANSENT SUR CHAQUE TABLE LÀ-BAS.
2. UN PIQUE-NIQUE DANS LE PARC, AVEC DES SANDWICHS EN FORME DE CŒUR,
ET DU VIN PÉTILLANT QUI BRILLE ALORS QUE CUPIDON DISTRIBUE.
3. UNE JOURNÉE AU SPA, OÙ LES COUPLES SERONT CHOYÉS, MAIN DANS LA MAIN,
AVEC DES HUILES ET DES BAUMES APAISANTS, PENDANT QUE LE FEU DE L'AMOUR S'ÉTEND.
4. UNE CROISIÈRE AU COUCHER DU SOLEIL, SUR UN NAVIRE ÉLÉGANT,
OÙ LES AMOUREUX NAVIGUENT, ÉCLAIRÉS PAR LA DOUCE LUEUR DE LA LUNE.
5. UNE RANDONNÉE JUSQU'À LA COLLINE LOINTAINE, OÙ SE TROUVE LE SOMMET,
ET LÀ, PARMI LES NUAGES, UN PIQUE-NIQUE ROMANTIQUE SE LÈVERA.
6. UNE SOIRÉE CONFORTABLE À LA MAISON, AVEC DU POPCORN FAIT MAISON,
ET SUR LE CANAPÉ, LA CHALEUR DE L'AMOUR NE S'ESTOMPERA JAMAIS.
7. UNE DÉGUSTATION DE VINS, À LA PORTE D'UN VIGNOBLE LOCAL,
OÙ LES AMOUREUX GOÛTENT DES NECTARS SUCRÉS ET EN REDEMANDENT.
8. UN COURS DE DANSE, OÙ LES COUPLES APPRENDRONT AVEC GRÂCE,
DE NOUVEAUX STYLES DE MOUVEMENT, POUR S'ÉTREINDRE DANS L'ÉTREINTE DE L'AMOUR.
9. UN VOL EN MONTGOLFIÈRE, OÙ NOUS PLANERONS HAUT DANS LES AIRS,
ET NOUS ÉMERVEILLERONS DES PAYSAGES QUE LES AILES DE L'AMOUR RESTAURENT.
10. UNE SOIRÉE JEUX À LA MAISON, AVEC DES JEUX DE SOCIÉTÉ POUR S'AMUSER,
PENDANT QUE LES COLLATIONS SATISFERONT ET QUE LE RIRE REMPLIRA LA SCÈNE.

うーん、フランス語なので何が書いてあるかわからないです

しかし、最初のプランは何のために作成したんだ。。

Sequential Planner

Sequential PlannerはXMLベースのステップバイステッププランナーだ。

from semantic_kernel.planning import SequentialPlanner
planner = SequentialPlanner(kernel)

sequential_plan = await planner.create_plan_async(goal=ask)

ふーん、BasicPlannerとは作り方が少し異なるようだ。しかし、実際に実行してみるとエラーになってしまう

PlanningException: (<ErrorCodes.CreatePlanError: 3>, ('Not possible to create plan for goal with available functions.\n', 
<以下略>

notebookのとおりにやっているのになぜだろうと思っていろいろ調べてみると、公式Discordにこんな投稿を見つけた

"Correct, for stepwise and sequential planners you really need to use gpt4. 3.5-turbo has never worked for me either, even when I did a self rolled ReAct agent. Gpt4 has a much higher level of coginitive reasoning IME."

はー、なんですと!gpt-3.5-turboだとダメだって?でもnotebookでロードしてるのは3.5ですよ?

と半信半疑でgpt-4でやってみたところ、なんとうまくいった。これはかなりのトラップだ。。

for step in sequential_plan._steps:
    print(step.description, ":", step._state.__dict__)

シーケンシャルプランの中身を見ると

Given a goal or topic description generate a list of ideas : {'variables': {'input': ''}}
Generic function, unknown purpose : {'variables': {'input': ''}}
Translate the input into a language of your choice : {'variables': {'input': ''}}
Convert a string to uppercase. : {'variables': {'input': ''}}

なんかできているので実行してみる

result = await sequential_plan.invoke_async()

print(result)

1. PLONGEZ-VOUS DANS UN BANQUET ROMANTIQUE, ASSEMBLÉ PAR VOS PROPRES MAINS, EMBELLI DE BOUGIES SCINTILLANTES ET DE LA DOUCE MÉLODIE DE VOS AIRS PRÉFÉRÉS.
2. BALADEZ-VOUS DANS UN PARC À PROXIMITÉ OU UN HAVRE HORTICOLE, ÉMERVEILLÉ PAR LES SPLENDEURS DE LA NATURE EN BONNE COMPAGNIE.
3. CONCOCTEZ UNE SOIRÉE REMPLIE DE CONTES ROMANTIQUES PROJETÉS SUR VOS MURS, ACCOMPAGNÉS DE VOS GRIGNOTAGES PRÉFÉRÉS.
4. PARTICIPEZ À UNE LEÇON CULINAIRE ENSEMBLE, APPRENANT LES SECRETS D'UNE NOUVELLE NOURRITURE.
5. PRÉPAREZ UNE SURPRISE SABBATIQUE À UNE CABINE ISOLÉE OU UN GRAND HÔTEL POUR LE WEEK-END.
6. VISITEZ UN VIGNOBLE OU UNE BRASSERIE LOCALE POUR UNE DÉGUSTATION DE VINS ET DE BIÈRES.
7. PROFITEZ D'UNE JOURNÉE AU CENTRE DE SANTÉ POUR UN PEU DE CHOUCHOUTAGE LUXUEUX ET DE TRANQUILLITÉ.
8. PARTICIPEZ À UN COURS DE DANSE ENSEMBLE, MAÎTRISANT DE NOUVEAUX PAS RYTHMIQUES.
9. VIVEZ UN PIQUE-NIQUE ROMANTIQUE DANS UN PARC LOCAL OU AU BORD DE LA MER.
10. PRÉPAREZ UNE SURPRISE AMOUREUSE IMPLIQUANT LE PASSE-TEMPS OU L'ENTREPRISE PRÉFÉRÉE DE VOTRE PARTENAIRE.

またフランス語なのでわからないが、うまくいったようだ。
しかし、これをBasicPlannerとどう使い分ければいいのだろうか

Action Planner

Action PlannerはFunctionのリストとゴールを受け取り、目的に合った一つのFunctionを返す

from semantic_kernel.planning import ActionPlanner
planner = ActionPlanner(kernel)	

from semantic_kernel.core_skills import FileIOSkill, MathSkill, TextSkill, TimeSkill
kernel.import_skill(MathSkill(), "math")
kernel.import_skill(FileIOSkill(), "fileIO")
kernel.import_skill(TimeSkill(), "time")
kernel.import_skill(TextSkill(), "text")

ask = "What is the sum of 110 and 990?"

plan = await planner.create_plan_async(goal=ask)

result = await plan.invoke_async()

print(result)

1100

あっさり終わったが、Planの中身は確認できないのかな?

Stepwise Planner

さていよいよ大トリのStepwisePlannerだ。これは、MRKL(Modular Reasoning, Knowledge and Language)の論文をベースにしており、ReACT(Reasoning and Acting in Language Models)にも似ている。核となるのは、ステップワイズ・プランナーによって、AIが「思考」と「観察」を形成し、それに基づいてユーザーの目標を達成するための行動を実行することである。これは、必要な機能がすべて完了し、最終的な出力が生成されるまで続けられる

以下のURLからBing Search用のAPIキーを作成し、.envファイルに追加しておく
https://www.microsoft.com/en-us/bing/apis/bing-web-search-api

from semantic_kernel.planning import StepwisePlanner
from semantic_kernel.planning.stepwise_planner.stepwise_planner_config import (
    StepwisePlannerConfig,
)

ライブラリのインポート

class WebSearchEngineSkill:
    """
    A search engine skill.
    """
    from semantic_kernel.orchestration.sk_context import SKContext
    from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter

    def __init__(self, connector) -> None:
	self._connector = connector

    @sk_function(
	description="Performs a web search for a given query", name="searchAsync"
    )
    @sk_function_context_parameter(
	name="query",
	description="The search query",
    )
    async def search_async(self, query: str, context: SKContext) -> str:
	query = query or context.variables.get("query")[1]
	result = await self._connector.search_async(query, num_results=5, offset=0)
	return str(result)

WebSearchEngineSkillというクラスを作成

from semantic_kernel.connectors.search_engine import BingConnector

BING_API_KEY = sk.bing_search_settings_from_dot_env()
connector = BingConnector(BING_API_KEY)
kernel.import_skill(WebSearchEngineSkill(connector), skill_name="WebSearch")

Bing Searchコネクタを作成し、WebSearchEngineSkillをインポートする

from semantic_kernel.core_skills.math_skill import MathSkill
from semantic_kernel.core_skills.time_skill import TimeSkill

kernel.import_skill(TimeSkill(), "time")
kernel.import_skill(MathSkill(), "math")

MathSkillとTimeSkillもインポート

planner = StepwisePlanner(
    kernel, StepwisePlannerConfig(max_iterations=10, min_iteration_time_ms=1000)
)

StepWisePlannerの作成

ask = """How many total championships combined do the top 5 teams in the NBA have?"""

plan = planner.create_plan(goal=ask)

Bingで最新の情報を得る必要のある、複雑なaskをやってみる

result = await plan.invoke_async()

プランの実行。3分20秒かかった

print(result)

Result not found, review _steps_taken to see what happened.
[
    {
	"thought": "To answer this question, I need to first identify the top 5 teams in the NBA. Then, I need to find out how many championships each of these teams has won. The total number of championships will be the sum of the championships won by each of these teams. I can use the WebSearch.searchAsync function to find this information. Let's start by identifying the top 5 teams in the NBA.",
	"action": "WebSearch.searchAsync",
	"action_variables": {
	    "query": "top 5 teams in the NBA"
	},
	"observation": "['Stats. Teams. More. Where do the Nets check in now? A look at where every team stands heading into the 2022-23 season.', 'LEADERS NFL View All CHI 0-1 TB 1-0 62.0 % TB -2.5 O/U 40.5 Sun, 10:00am PT FOX GB 1-0 1-0 62.7 % -3 O/U 40.5 Sun, 10:00am PT FOX IND 0-1 HOU 0-1 58.0 % HOU -1 O/U 39.5 Sun, 10:00am PT FOX...', 'NBA Team Standings & Stats | NBA.com NBA 2022-23 Regular Season Standings Season Group By Section No data available xClinched Playoff Berth oEliminated from Playoffs contention eClinched...', '2021-22 record: 5-5 Previous ranking: 10 After going 1-for-13 in a loss to the Trail Blazers in a game where the Lakers trailed by as many as 34 points, Russell Westbrook sounded undeterred.']",
	"final_answer": null,
	"original_response": "[THOUGHT]\nTo answer this question, I need to first identify the top 5 teams in the NBA. Then, I need to find out how many championships each of these teams has won. The total number of championships will be the sum of the championships won by each of these teams. I can use the WebSearch.searchAsync function to find this information. Let's start by identifying the top 5 teams in the NBA.\n[ACTION]\n{\n  \"action\": \"WebSearch.searchAsync\",\n  \"action_variables\": {\"query\": \"top 5 teams in the NBA\"}\n}"
    },
    {
	"thought": "The search results do not clearly list the top 5 teams in the NBA. I will need to refine my search query to get more specific results. Let's try searching for \"NBA team rankings 2022\".",
	"action": "WebSearch.searchAsync",
	"action_variables": {
	    "query": "NBA team rankings 2022"
	},
	"observation": "['Checkout the Team Ratings for the 2022-23 NBA Season and more on Basketball-Reference.com', 'Rankings & Projections Summary. Highlights from our predictive power ratings and simulations of currently scheduled games. NBA basketball picks, NBA power rankings, basketball odds, player stats, scores, teams, and schedules.', 'Our NBA insiders break down where every team stands after a wild free agency spree, plus which questions remain ahead of the 2022-23 season. Note: These rankings are based on where the...', 'NBA 2022-23 Regular Season Standings Season Group By Section No data available xClinched Playoff Berth oEliminated from Playoffs contention eClinched Eastern Conference wClinched Western...']",
	"final_answer": null,
	"original_response": "[THOUGHT]\nThe search results do not clearly list the top 5 teams in the NBA. I will need to refine my search query to get more specific results. Let's try searching for \"NBA team rankings 2022\".\n[ACTION]\n{\"action\": \"WebSearch.searchAsync\", \"action_variables\": {\"query\": \"NBA team rankings 2022\"}}"
    },
    {
	"thought": "The search results still do not clearly list the top 5 teams in the NBA. I will need to refine my search query again to get more specific results. Let's try searching for \"NBA team rankings 2022 list\".",
	"action": "WebSearch.searchAsync",
...
	"final_answer": null,
	"original_response": "[THOUGHT]\nThe search results still do not clearly list the top 5 teams in the NBA. I will need to refine my search query again to get more specific results. Let's try searching for \"NBA power rankings 2022 top 5 teams list\".\n[ACTION]\n{\"action\": \"WebSearch.searchAsync\", \"action_variables\": {\"query\": \"NBA power rankings 2022 top 5 teams list\"}}"
    }
]

[THOUGHT][ACTION][OBSERVATION]という順番でやっていくようだ。プラン自体はうまく動いているが結論にはたどりつかなかった様子

ちょっとaskの内容を変えてやってみる

ask = """AppleとMicrosoftの2022年の株価の最高値は?"""

というaskに対して

Appleの2022年の株価の最高値は$182.88、Microsoftの2022年の株価の最高値は$366.78でした。

という回答。stepを見てみると

Step: 0
Description: Execute a plan
Function: StepwisePlanner.ExecutePlan
  Output:
 This was my previous work (but they haven't seen any of it! They only see what I return as final answer):
  [THOUGHT]
  この問題を解決するためには、まずAppleとMicrosoftの2022年の株価の最高値を検索する必要があります。これを行うためには、WebSearch.searchAsync関数を使用します。まずはAppleから始めましょう。
  [ACTION]
  {"action": "WebSearch.searchAsync", "action_variables": {"query": "Apple 2022 stock price high"}}
  [OBSERVATION]
  ['The all-time high Apple stock closing price was 196.19 on July 31, 2023. The Apple 52-week high stock price is 198.23, which is 13.4% above the current share price. The Apple 52-week low stock price is 124.17, which is 29% .', "On the first day of trading in 2022, the Silicon Valley company's shares hit an intraday record high of $182.88, putting Apple's market value just above $3 trillion. The stock ended the...", 'Discover historical prices for AAPL stock on Yahoo Finance. View daily, weekly or monthly format back to when Apple Inc. stock was issued.', '50.77%. Get the latest Apple Inc (AAPL) real-time quote, historical performance, charts, and other financial information to help you make more informed trading and investment decisions.']
  [THOUGHT]
  Appleの2022年の株価の最高値は$182.88であることがわかりました。次に、Microsoftの2022年の株価の最高値を調べる必要があります。これもWebSearch.searchAsync関数を使用して検索します。
  [ACTION]
  {"action": "WebSearch.searchAsync", "action_variables": {"query": "Microsoft 2022 stock price high"}}
  [OBSERVATION]
  ['The all-time high Microsoft stock closing price was 358.73 on July 18, 2023. The Microsoft 52-week high stock price is 366.78, which is 15.7% above the current share price. The Microsoft 52-week low stock price is 213.4332.7% .', 'Discover historical prices for MSFT stock on Yahoo Finance. View daily, weekly or monthly format back to when Microsoft Corporation stock was issued.', 'The share price of Microsoft Inc. (MSFT) now. What analysts predict: $361.92. 52-week High/Low: $366.01 / $211.39. 50/200 Day Moving Average: $332.64 / $290.4. This figure corresponds to the Average Price over the previous 50/200 days. For Microsoft stocks, the 50-day moving average is the resistance level today.', 'Amount of cash a business has after it has met its financial obligations such as debt and outstanding payments. 16.90B. 34.16%. Get the latest Microsoft Corp (MSFT) real-time quote, historical ...']
  [THOUGHT]
  None

という感じで、最初のSTEPでAppleの株価を検索して調べ、次にMicrosoftの株価を検索して調べて賢く回答しているのがわかる。だからStepWiseなのか

05-using-the-planner.ipynbに関しては以上。続きはまた!

Discussion