🧙♂️
Semantic Kernelを使ってC#でAI (2)
ASP.Net CoreにAIを
前回の記事で、Semantic Kernelを使うと非常に簡単にAIを導入することができることを感じてもらえたと思います。
今回はASP.Net CoreからSemantic KernelでAIを使うようにしてみましょう。
プログラムを作成する
サンプルは、こちら。
プロジェクトを作成
プロジェクト作成
$ dotnet new webapp
$ dotnet add package Microsoft.SemanticKernel
$ dotnet add package Microsoft.SemanticKernel.Connectors.Ollama --prerelease
$ dotnet add package DotEnv.Core
$ dotnet add package Markdig
プログラム改変
メインプログラム
Program.cs
using DotEnv.Core;
using Microsoft.SemanticKernel;
new EnvLoader().Load();
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddMemoryCache();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
#pragma warning disable SKEXP0070 // 種類は、評価の目的でのみ提供されています。将来の更新で変更または削除されることがあります。続行するには、この診断を非表示にします。
builder.Services
.AddKernel()
.AddOllamaChatCompletion(Environment.GetEnvironmentVariable("OLLAMA_MODEL"), new Uri("http://localhost:11434"))
;
#pragma warning restore SKEXP0070 // 種類は、評価の目的でのみ提供されています。将来の更新で変更または削除されることがあります。続行するには、この診断を非表示にします。
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapStaticAssets();
app.MapRazorPages()
.WithStaticAssets();
app.Run();
アプリ起動部分。
セッション使ってメモリキャッシュにhistory入れておくので、それ関連を追加してあります。
builder.Services.AddMemoryCache();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
app.UseSession();
そして、Semantic Kernelを追加。
builder.Services
.AddKernel()
.AddOllamaChatCompletion(Environment.GetEnvironmentVariable("OLLAMA_MODEL"), new Uri("http://localhost:11434"))
;
AddKernel
でSemantic Kernelのカーネル部分を追加しています。
内部的には CreateBuilder
を呼んでそれを返す、位に短いものではあるコンビニエンス関数です。
メインビュー
Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<form method="post">
<label for="query">AIに質問 : </label>
<input type="text" name="queryText" />
<input type="submit" value="実行" />
</form>
<p>
@Html.Raw(Model.ResultText)
</p>
後述の IndexModel
に queryText
変数を使って入力を投げ、 ResultText
で返してくるようになっているRazor Pageです。
本筋ではないので、詳しい動きはRazor関連で調べてください。
メインモデル
Index.cshtml.cs
using Markdig;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Diagnostics;
namespace Sample02.Pages;
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IChatCompletionService _chatCompletion;
private readonly IMemoryCache _memoryCache;
public string ResultText { get; set; } = string.Empty;
public IndexModel(
IChatCompletionService chatCompletion,
IMemoryCache memoryCache,
ILogger<IndexModel> logger
)
{
_chatCompletion = chatCompletion;
_memoryCache = memoryCache;
_logger = logger;
}
public void OnGet()
{
}
const string HistoryIdKey = "history_id";
public async Task OnPost(string queryText)
{
var sessionId = HttpContext.Session.GetString(HistoryIdKey);
if (string.IsNullOrEmpty(sessionId))
{
sessionId = HttpContext.Session.Id;
}
HttpContext.Session.SetString(HistoryIdKey, sessionId);
ChatHistory history = null;
if (!_memoryCache.TryGetValue(sessionId, out history))
{
history = new ChatHistory();
}
if (history == null)
{
history = new ChatHistory();
}
history.AddUserMessage(queryText);
var res = await _chatCompletion.GetChatMessageContentAsync(history);
var ret = res.Content ?? string.Empty;
history.AddAssistantMessage(ret);
_memoryCache.Set(sessionId, history);
this.ResultText = Markdown.ToHtml(ret);
}
}
DIで最初に AddOllamaChatCompletion
を使って追加してある IChatCompletion
をコンストラクタで取得してきています。
DI楽ですね。
後はセッション機能使ってhistory引っ張ってきて、入力と戻り値を前回と同じくUserとAssistantに設定しています。
そのおかげで、入力していくとそれに合わせて返信を行ってくれる、というものになっています。
まとめ
ASP.Net Coreに変更しただけで、やっていることは前回と何も変わっていなかったりします。
なので、今回のキモはDIで使う AddKernel
だけですね。
久しぶりにRazor触って、どんなんだっけ?となったのは内緒です。
Discussion