🔥

Semantic Kernel Pythonでユーザーごとの記憶を持たせてみる

2023/04/22に公開

https://devblogs.microsoft.com/semantic-kernel/py/

MS版LangChainことSemantic KernelのPython版が出たので気になっていたSemantic Memory関連の動作を試した。

https://gist.github.com/laiso/15e3cc6b7214fc9783d2d85f40d89ade

セットアップ系

今のところ.envは必須らしい。

KV = {}  # DBのかわり


def init_kernel():
    k = sk.Kernel()

    api_key, org_id = sk.openai_settings_from_dot_env()
    k.config.add_chat_backend(
        "chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id)
    )
    k.config.add_embedding_backend(
        "ada", sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id)
    )
    k.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
    return k


def build_chat_function(k):
    prompt_config = sk.PromptTemplateConfig.from_completion_parameters(
        max_tokens=2000, temperature=0.7, top_p=0.8
    )

    prompt_template = sk.ChatPromptTemplate("""Chat:
        {{$chat_history}}
        User: {{$user_input}}
        ChatBot:""", k.prompt_template_engine, prompt_config)

    prompt_template.add_system_message("""
        あなたはChatBotです。Userの質問にフレンドリーに答えてください。答えは10単語程度にしてください。
        """)

    function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template)
    return k.register_semantic_function("ChatBot", "Chat", function_config)

呼び出し

chat関数にユーザーIDを渡して呼び出せるようにする。

kernel = init_kernel()
chat_function = build_chat_function(kernel)

async def chat(user_id, message):
	context_vars = KV.get(user_id, sk.ContextVariables(variables=dict(
	    user_input="",
	    chat_history="",
	)))
	context_vars["user_input"] = message
	talk = f'{user_id}: ' + context_vars["user_input"]
	print(talk)
	answer = await kernel.run_async(chat_function, input_vars=context_vars)
	print('ChatBot: ' + answer.result)
	await kernel.memory.save_information_async(user_id, talk, uuid.uuid4())
	context_vars["chat_history"] += f"\n{talk}\nChatBot:> {answer.result}\n"
	KV[user_id] = context_vars
	return "\n".join([talk, 'ChatBot: ' + answer.result])
await chat('user-1', '私は今日トマトのオムライスを食べました。')
await chat('user-2', '私は今日テニスをしました。')

await chat('user-1', '私は何を食べましたか?')
await chat('user-2', '私は何を食べましたか?')

ChatBotはuser-1さんとuser-2さんを区別していることが分かる。

output
user-1: 私は今日トマトのオムライスを食べました。
ChatBot: それは美味しそうですね。お腹いっぱいになりましたか?
user-2: 私は今日テニスをしました。
ChatBot: スポーツをするのはとても良い運動になりますね。楽しかったですか?
user-1: 私は何を食べましたか?
ChatBot: あなたは今日トマトのオムライスを食べたと言いました。
user-2: 私は何を食べましたか?
ChatBot: 申し訳ありません、あなたは何を食べたか教えてくれませんでした。今日何か食べましたか?

メモリは個別にキーを指定して中身をセマンチックに検索できる。

memories = await kernel.memory.search_async('user-1', 'Userの今日の活動')
print("(´ー`)...: " + memories[0].text)

memories = await kernel.memory.search_async('user-2', 'Userの今日の活動')
print("(´ー`)...: " + memories[0].text)
(´ー`)...: user-1: 私は今日トマトのオムライスを食べました。
(´ー`)...: user-2: 私は今日テニスをしました。

Discussion