Azure Functions では、Azure OpenAI 向けの拡張機能(Preview)が提供されています。
learn.microsoft.com
今回は、拡張機能の Assistant trigger を使って、Chat completion binding で構築したチャットアシスタントにカスタムスキルを追加してみました。
その他の拡張機能については、別記事を参照してください。
gooner.hateblo.jp
gooner.hateblo.jp
gooner.hateblo.jp
Assistant trigger とは
Function に Assistant trigger を定義することで、チャットアシスタントにカスタムスキルを提供できます。OpenAI の Function calling が内部的に使われており、呼び出す Function とそのタイミングを判定してくれます。
learn.microsoft.com
前準備
Azure Functions を Isolated worker model で作成し、NGet ライブラリをインストールします。インストールする前に、Visual Studio などのテンプレートで作成されたプロジェクトのライブラリ群を最新版に更新しておくことをお勧めします。
$ dotnet add package Microsoft.Azure.Functions.Worker.Extensions.OpenAI --version 0.16.0-alpha
local.settings.json において、Azure OpenAI Service の エンドポイントとキーを定義しておきます。
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", "AZURE_OPENAI_ENDPOINT": "https://xxx.openai.azure.com/", "AZURE_OPENAI_KEY": "xxx", "CHAT_MODEL_DEPLOYMENT_NAME": "gpt-4o" }
カスタムスキルを追加する
AssistantSkillTrigger
のバインディングを使って、チャットアシスタントによってトリガーできる2つの Function を作成します。
- Todo リストにタスクを追加する
- Todo リストからタスクを取得する
関数と引数の名前、AssistantSkillTrigger で定義する説明は、Function calling が呼び出す Function とそのタイミングを判断するための重要な情報となります。
public class AssistantSkills { private readonly ITodoManager _todoManager; private readonly ILogger<AssistantSkills> _logger; public AssistantSkills(ITodoManager todoManager, ILogger<AssistantSkills> logger) { _todoManager = todoManager; _logger = logger; } [Function(nameof(AddTodo))] public Task AddTodo([AssistantSkillTrigger("Create a new todo task")] string taskDescription) { if (string.IsNullOrEmpty(taskDescription)) { throw new ArgumentException("Task description cannot be empty"); } _logger.LogInformation("Adding todo: {task}", taskDescription); string todoId = Guid.NewGuid().ToString()[..6]; return _todoManager.AddTodoAsync(new TodoItem(todoId, taskDescription)); } [Function(nameof(GetTodos))] public Task<IReadOnlyList<TodoItem>> GetTodos( [AssistantSkillTrigger("Fetch the list of previously created todo tasks")] object inputIgnored) { _logger.LogInformation("Fetching list of todos"); return _todoManager.GetTodosAsync(); } }
Todo リストに追加するタスクの永続化は、データベースを使わずに、オンメモリで保持するようにしました。
public record TodoItem(string Id, string Task); public interface ITodoManager { Task AddTodoAsync(TodoItem todo); Task<IReadOnlyList<TodoItem>> GetTodosAsync(); } class InMemoryTodoManager : ITodoManager { readonly List<TodoItem> todos = new(); public Task AddTodoAsync(TodoItem todo) { this.todos.Add(todo); return Task.CompletedTask; } public Task<IReadOnlyList<TodoItem>> GetTodosAsync() { return Task.FromResult<IReadOnlyList<TodoItem>>(this.todos.ToImmutableList()); } }
最後に、Program.cs で TodoManager のインスタンスを追加しておきます。
var host = new HostBuilder() .ConfigureFunctionsWebApplication() .ConfigureServices(services => { services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); services.AddSingleton<ITodoManager, InMemoryTodoManager>(); }) .Build(); host.Run();
動作確認
VS Code 拡張機能の REST Client を使って、チャットアシスタントと会話してみます。
新しいアシスタントを作成する
@function_app_HostAddress = http://localhost:7074 ### Assistant create output binding PUT {{function_app_HostAddress}}/api/chat/1 HTTP/1.1 content-type: application/json
HTTP/1.1 201 Created Content-Type: application/json; charset=utf-8 Date: Sat, 07 Sep 2024 14:06:32 GMT Server: Kestrel Transfer-Encoding: chunked { "assistantId": "1" }
アシスタントにタスクの追加を依頼する
### Assistant trigger POST {{function_app_HostAddress}}/api/chat/1 HTTP/1.1 content-type: application/json { "userMessage": "出張の新幹線を予約するのを忘れないように" }
HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Sat, 07 Sep 2024 14:12:52 GMT Server: Kestrel Transfer-Encoding: chunked 「出張の新幹線を予約するのを忘れないように」というタスクを追加しました。
さらに、もう1つタスクを追加してみます。
### Assistant trigger POST {{function_app_HostAddress}}/api/chat/1 HTTP/1.1 content-type: application/json { "userMessage": "それと、ホテルの予約も" }
HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Sat, 07 Sep 2024 14:13:46 GMT Server: Kestrel Transfer-Encoding: chunked 「ホテルの予約をする」というタスクも追加しました。
アシスタントに今日のタスクを確認する
### Assistant trigger POST {{function_app_HostAddress}}/api/chat/1 HTTP/1.1 content-type: application/json { "userMessage": "今日は何をすればいい?" }
HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Sat, 07 Sep 2024 14:14:27 GMT Server: Kestrel Transfer-Encoding: chunked 今日のタスクは以下の通りです: 1. 出張の新幹線を予約するのを忘れないように 2. ホテルの予約をする 頑張ってください!
チャットアシスタントから今日のタスクが回答されたことが分かります。
まとめ
Azure Functions 拡張機能の Assistant trigger を使って、Chat completion binding で構築したチャットアシスタントにカスタムスキルを追加してみました。
AOAI の SDK を使った定型のコードが不要となり、Azure Functions のプログラミングモデルで Function calling を実装できて便利だと感じました。
今回のソースコードは、こちらのリポジトリで公開しています。
github.com