👌

「Prompty を読み込んで LangChain と Semantic Kernel で実行するのを試してみた」のコードを書きなおしてみた

2024/05/30に公開

1 つ前の記事で「Prompty を読み込んで LangChain と Semantic Kernel で実行するのを試してみた」という記事を書きました。

https://zenn.dev/microsoft/articles/lets-start-prompty

今回は、その時に書いた Prompty を読み込んで実行するコードをちょっと書きなおしてみました。

書きなおしたポイントは以下の通りです。

  1. その言語の命名規約の流儀に従う
    • 癖で Python の変数名を camelCase にしてしまっていたので snake_case に修正
  2. 非同期処理を使うように変更
  3. LangChain なんだから Chain を使うように変更
  4. ストリームを使うように変更
  5. なるべくシンプルに書く

Python 版 (LangChain)

書きなおす前は以下のようなコードでした。

from langchain_prompty import create_chat_prompt
from langchain_openai import AzureChatOpenAI
import os
from dotenv import load_dotenv
from pathlib import Path

load_dotenv()

# Prompty ファイルを読み込む
promptyFilePath = Path(__file__).parent / 'basic.prompty'
prompt = create_chat_prompt(promptyFilePath)
# question パラメーターに値を設定した状態のものを作成する
promptText = prompt.invoke(
    {
        'question': 'クラスの定義の仕方について教えてください。'
    }
)

# OpenAI にプロンプトを投げる
llm = AzureChatOpenAI(
    openai_api_version=os.getenv('AZURE_OPENAI_API_VERSION'),
    azure_deployment=os.getenv('AZURE_OPENAI_DEPLOYMENT_NAME'),
)
response = llm.invoke(promptText)
print(response.content)

書き換えた後のコードは以下のようになります。

import os
import asyncio
from dotenv import load_dotenv
from pathlib import Path
from langchain_prompty import create_chat_prompt
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import AzureChatOpenAI

load_dotenv()

# Prompty ファイルを読み込む
prompt = create_chat_prompt(Path(__file__).parent / 'basic.prompty')

# AOAI への接続用のクラスを作成
llm = AzureChatOpenAI(
    openai_api_version=os.getenv('AZURE_OPENAI_API_VERSION'),
    azure_deployment=os.getenv('AZURE_OPENAI_DEPLOYMENT_NAME'),
)

# 非同期用の関数を作成
async def get_response(chain):
    async for chunk in chain.astream({ 'question': 'クラスの定義の仕方について教えてください。' }):
        print(chunk, end='', flush=True)
    print()    

# チェーンを実行
chain = prompt | llm | StrOutputParser()
asyncio.run(get_response(chain))

C# 版 (Semantic Kernel)

C# 版も書き換えました。

書き換える前は以下のようなコードでした。

using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;

// 構成ファイルを読み込み
var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

// カーネルを作成
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        configuration["OpenAI:DeploymentName"]!,
        configuration["OpenAI:Endpoint"]!,
        new AzureCliCredential())
    .Build();

// Prompty ファイルを読み込んで KernelFunction を作成
#pragma warning disable SKEXP0040
var prompty = kernel.CreateFunctionFromPromptyFile("basic.prompty");
#pragma warning restore SKEXP0040

// 実行して結果を表示
var answer = await prompty.InvokeAsync<string>(kernel,
    new KernelArguments
    {
        ["question"] = "クラスの定義方法について教えてください。"
    });
Console.WriteLine(answer);

こっちは接続先情報を記載したファイルの appsettings.json を自前で読み込んだりしていたので Host.CreateApplicationBuilder を使って自動で読み込んでくれる機能を使うようにしました。

// Prompty が実験的機能なので警告を無効化
#pragma warning disable SKEXP0040
using Azure.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.SemanticKernel;

var builder = Host.CreateApplicationBuilder(args);

// Semantic Kernel のためのサービスを登録
builder.Services.AddAzureOpenAIChatCompletion(
    builder.Configuration["OpenAI:DeploymentName"]!,
    builder.Configuration["OpenAI:Endpoint"]!,
    new AzureCliCredential());
builder.Services.AddKernel();

var app = builder.Build();

// Kernel のインスタンスを取得して、Prompty のファイルを読み込んで関数を作成
var kernel = app.Services.GetRequiredService<Kernel>();
var prompty = kernel.CreateFunctionFromPrompty(await File.ReadAllTextAsync("basic.prompty"));
await foreach (var chunk in kernel.InvokeStreamingAsync<string>(
    prompty, 
    new() { ["question"] = "クラス定義方法について教えてください。" }))
{
    Console.Write(chunk);
}

Console.WriteLine();

まとめ

ということで、後でコード見直したら気になったので書き換えました。
どっちもスッキリしたと思います。

Python は素人なので、もうちょっといい書き方あるよとかあれば教えてください。

Microsoft (有志)

Discussion