ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Azure Functions の Azure OpenAI Extension を使ってチャットアシスタントを構築する

Azure Functions では、Azure OpenAI 向けの拡張機能(Preview)が提供されています。
learn.microsoft.com

今回は、拡張機能の Chat completion binding を使って、チャットアシスタントを構築してみました。
その他の拡張機能については、別記事を参照してください。
gooner.hateblo.jp
gooner.hateblo.jp
gooner.hateblo.jp

Chat completion binding とは

チャットアシスタント向けの3種類の binding が提供されています。

Assistant create output binding

Assistant create output binding を使うことで、指定したシステムプロンプトによる新しいアシスタントを作成できます。
learn.microsoft.com

Assistant post input binding

Assistant post input binding を使うことで、アシスタントにメッセージを送信するとともに、Table Storage にメッセージ内容を永続化します。
learn.microsoft.com

Assistant query input binding

Assistant query input binding を使うことで、アシスタントが永続化したチャット履歴を取得できます。
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"
  }

新しいアシスタントを作成する

AssistantCreateOutput のバインディングを使うことで、指定した ID とシステムプロンプトを持つチャットアシスタントを作成できます。作成した結果は、AssistantCreateRequest で受け取ることができます。

public class Chat
{
    private readonly ILogger<Chat> _logger;

    public Chat(ILogger<Chat> logger)
    {
        _logger = logger;
    }

    [Function(nameof(CreateAssistant))]
    public CreateAssistantOutput CreateAssistant(
        [HttpTrigger(AuthorizationLevel.Function, "put", Route = "chat/{assistantId}")] HttpRequest req,
        string assistantId)
    {
        _logger.LogInformation("Assistant create output binding function processed a request.");

        var responseJson = new { assistantId };
        var instructions = """
            You are an AI assistant that helps people find information.
            """;

        return new CreateAssistantOutput
        {
            HttpResponse = new OkObjectResult(responseJson) { StatusCode = 201 },
            AssistantCreateRequest = new AssistantCreateRequest(assistantId, instructions)
        };
    }

    public class CreateAssistantOutput
    {
        [AssistantCreateOutput()]
        public AssistantCreateRequest? AssistantCreateRequest { get; set; }

        [HttpResult]
        public IActionResult? HttpResponse { get; set; }
    }
}

アシスタントにメッセージを送信する

AssistantPostInput のバインディングを使うことで、チャットアシスタントにメッセージ(プロンプト)を送信できます。送信した結果は、AssistantState で受け取ることができます。

[Function(nameof(PostUserMessage))]
public IActionResult PostUserMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "chat/{assistantId}")] HttpRequestData req,
    string assistantId,
    [AssistantPostInput("{assistantId}", "{userMessage}", Model = "%CHAT_MODEL_DEPLOYMENT_NAME%")] AssistantState state)
{
    _logger.LogInformation("Assistant post input binding function processed a request.");

    return new OkObjectResult(state.RecentMessages.LastOrDefault()?.Content ?? "No response returned.");
}

チャット履歴を取得する

AssistantQueryInput のバインディングを使うことで、チャットアシスタントが永続化したチャット履歴を取得するクエリを送信できます。クエリ結果は、AssistantState で受け取ることができます。

[Function(nameof(GetChatState))]
public IActionResult GetChatState(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "chat/{assistantId}")] HttpRequestData req,
    string assistantId,
    [AssistantQueryInput("{assistantId}", TimestampUtc = "{timestampUtc}")] AssistantState state)
{
    _logger.LogInformation("Assistant query input binding function processed a request.");

    return new OkObjectResult(state);
}

動作確認

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 05:33:20 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "assistantId": "1"
}

アシスタントにメッセージを送信する

### Assistant post input binding
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 05:35:26 GMT
Server: Kestrel
Transfer-Encoding: chunked

京都には多くの美しい観光地があります。いくつかのお勧めを紹介します。

1. **清水寺(きよみずでら)**:
   - 世界遺産にも登録されている清水寺は、特に「清水の舞台」からの眺めが見どころです。春の桜や秋の紅葉の季節がお勧めです。

2. **金閣寺(きんかくじ)**:
   - その美しい金色の外観で有名な金閣寺。正式名は鹿苑寺(ろくおんじ)で、日本の歴史や文化を感じることができます。

3. **銀閣寺(ぎんかくじ)**:
   - 正式には慈照寺(じしょうじ)。庭園と一緒に楽しむ禅の雰囲気が魅力です。

4. **伏見稲荷大社(ふしみいなりたいしゃ)**:
   - 多数の鳥居が連なり、その美しい光景で知られる伏見稲荷。山全体に広がる参道を散策すると、日本の伝統文化に触れることができます。

5. **嵐山(あらしやま)**:
   - 自然豊かな嵐山エリアは、竹林の道や渡月橋(とげつきょう)など、美しい風景が楽しめます。また、トロッコ列車や保津川下りも人気です。

6. **二条城(にじょうじょう)**:
   - 世界遺産に登録されている歴史的な城で、美しい庭園や御殿内に描かれた障壁画が見所です。

7. **祇園(ぎおん)**:
   - 京都の伝統的な街並みを感じることができるエリア。舞妓さんや茶屋など、日本の古き良き風情が楽しめます。

8. **天龍寺(てんりゅうじ)**:
   - 世界遺産である天龍寺は、美しい庭園が特徴です。特に曹源池庭園(そうげんちていえん)は見逃せません。

これら以外にも、京都には多数の素晴らしい観光地やお寺があります。訪れる時期や興味に合わせて計画を立ててくださいね。

チャットアシスタントから京都のお勧めの観光地が回答されたことが分かります。


チャット履歴を取得する

### Assistant query input binding
GET {{function_app_HostAddress}}/api/chat/1?TimestampUtc=2024-09-06T00:00:00
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 07 Sep 2024 05:40:48 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "id": "1",
  "exists": true,
  "createdAt": "2024-09-07T05:33:21.9599132Z",
  "lastUpdatedAt": "2024-09-07T05:35:27.1306636Z",
  "totalMessages": 3,
  "totalTokens": 594,
  "recentMessages": [
    {
      "content": "You are an AI assistant that helps people find information.",
      "role": "system",
      "name": null
    },
    {
      "content": "京都のお勧めの観光地はどこですか?",
      "role": "user",
      "name": null
    },
    {
      "content": "京都には多くの美しい観光地があります。いくつかのお勧めを紹介します。\n\n1. **清水寺(きよみずでら)**:\n   - 世界遺産にも登録されている清水寺は、特に「清水の舞台」からの眺めが見どころです。春の桜や秋の紅葉の季節がお勧めです。\n\n2. **金閣寺(きんかくじ)**:\n   - その美しい金色の外観で有名な金閣寺。正式名は鹿苑寺(ろくおんじ)で、日本の歴史や文化を感じることができます。\n\n3. **銀閣寺(ぎんかくじ)**:\n   - 正式には慈照寺(じしょうじ)。庭園と一緒に楽しむ禅の雰囲気が魅力です。\n\n4. **伏見稲荷大社(ふしみいなりたいしゃ)**:\n   - 多数の鳥居が連なり、その美しい光景で知られる伏見稲荷。山全体に広がる参道を散策すると、日本の伝統文化に触れることができます。\n\n5. **嵐山(あらしやま)**:\n   - 自然豊かな嵐山エリアは、竹林の道や渡月橋(とげつきょう)など、美しい風景が楽しめます。また、トロッコ列車や保津川下りも人気です。\n\n6. **二条城(にじょうじょう)**:\n   - 世界遺産に登録されている歴史的な城で、美しい庭園や御殿内に描かれた障壁画が見所です。\n\n7. **祇園(ぎおん)**:\n   - 京都の伝統的な街並みを感じることができるエリア。舞妓さんや茶屋など、日本の古き良き風情が楽しめます。\n\n8. **天龍寺(てんりゅうじ)**:\n   - 世界遺産である天龍寺は、美しい庭園が特徴です。特に曹源池庭園(そうげんちていえん)は見逃せません。\n\nこれら以外にも、京都には多数の素晴らしい観光地やお寺があります。訪れる時期や興味に合わせて計画を立ててくださいね。",
      "role": "assistant",
      "name": null
    }
  ]
}

Table Storage の OpenAIChatState テーブルを確認すると、チャット履歴が永続化されています。


まとめ

Azure Functions 拡張機能の Chat completion binding を使って、チャットアシスタントを構築してみました。
AOAI の SDK を使った定型のコードが不要となり、シンプルな実装で Chat Completions API を呼び出すことができ、さらにチャット履歴も永続化できて便利だと感じました。

今回のソースコードは、こちらのリポジトリで公開しています。
github.com