9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Azure OpenAI開発】Static Web Apps × Azure Functionsを使って、WebアプリケーションにAzure OpenAIを組み込む

Last updated at Posted at 2023-05-14

完成品

動作イメージ

※質問に対する回答時間がやや長めです。

demo-auth-chat.gif

全体像

本環境で作成したリソースの構成は以下の通りです。
※ インターネット公開されているため、セキュリティ面の注意が必要です。

image.png

次回以降は以下の図のような、プライベートまたはアクセス可能なユーザを限定した環境を構築していこうかと思います。
Azureの専門ではないため、図は正確でない部分もあるかと思います。
あくまでイメージとして捉えていただければと思います。

image.png


Prerequisites

nvm install 16.19.0
nvm use 16.19.0
  • Python3+
python -V
Python 3.10.9 # 本環境はv3.10.9

1. 雛形プロジェクトを作成

①フロントエンド (Azure Static Web Apps)

本環境ではVue.jsを使用してフロントエンドを構築します。
vue-cliをインストールします。
本環境では、vue/cli v5.0.8を使用します。

# インストール
npm install -g @vue/cli
vue --version

雛形プロジェクトを作成します。
本環境ではvue2を使用します。

vue create dev-app  # dev-appは任意のプロジェクト名
# vue3 or vue2を対話型で聞かれる→vue2を選択

作成したプロジェクトの起動確認を行います。

cd dev-app && npm run serve

### 出力 ###
 DONE  Compiled successfully in 5932ms

  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.0.7:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

出力されたURLを開き、以下のような画面が開かれることを確認します。
URLはどちらでも構いません。

image.png

起動確認後buildしておきます。

npm run build

②バックエンド (Azure Functions)

環境構築

本環境では、Flaskを使用してバックエンドを構築します。

VSCodeの左側拡張機能からAzure Toolsを検索し、ダウンロードします。
拡張機能を使い、VSCode上からAzureにログインします。
ファンクションキーF1を押下し、Azure: Sign Inをクリックします。
Webページが開かれたら、お使いのAzureアカウントを選択してログインします。

image.png

Static Web Appsと連携するAzure Functionsを作ります。
再度ファンクションキーF1を押下し、Azure Static Web Apps: Create HTTP Functionをクリックします。

image.png

言語選択画面にてPythonを選択し、バージョン選択画面にてv.3.10.9を選択します。
公式ドキュメントに記載がある通り、Python3.10.xはプレビュー版でのみ提供されています。

image.png

image.png

任意の関数名を入力し、Enterを押下します。

image.png

ここまで以下のようなフォルダ構成になるかと思います。

  • apiフォルダ (Azure Functionsに該当します)
  • dev-appフォルダ (Azure Static Web Appsに該当します)

image.png

Flaskの環境を構築します。
ローカル開発用にpip install flaskを実行することに加え、
Azure Functionsにもflaskがインストールされるようにapi/requirements.txtにflaskを追記します。

pip install flask
api/requirements.txt
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues

azure-functions
flask # 追記

雛形となるAzure Functionsを作成

Flaskのソースコードを作成します。
api/flask_appフォルダを作成し、その配下にapp.pyを作成します。

image.png

from flask import Flask

app = Flask(__name__)

@app.route('/api/test', methods=['GET'])
def api_test():
    return 'This is Test'

if __name__ == '__main__':
    app.run()

Azure Fuctionsの設定変更

Azure FunctionsへのHTTPリクエストがFlask側に流れるように設定を変更します。

① Azure FunctionsへのリクエストをWSGI handler(flask)にリダイレクトする

  • api/HttpTrigger1/__init__.pyを以下のように編集します。
api/HttpTrigger1/__init__.py
import azure.functions as func
from flask_app.main import app

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    """
    Each request is redirected to the WSGI handler.
    """
    return func.WsgiMiddleware(app).handle(req, context)

②ルーティング規則を変更する

具体的には以下2つの変更を行います。

  • /apiのリクエストがAzure Functionsに流れるようにする。
  • /api/*のリクエストがWSGI handler=Flaskに流れるようにする

編集するファイルは以下の2つです。

  • api/host.json
  • api/HttpTrigger1/function.json
api/host.json
{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  },
  ### 追記 ###
  "extensions": {
    "http": {
        "routePrefix": "api"
    }
  }
  ############
}
api/HttpTrigger1/function.json
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "{*route}"       ← 追記
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

ローカル環境でAzure Functionsをテストします。
Azure Functions Core Toolsをインストールし、func startを実行します。

cd api
func start

### 出力 ###
Functions:

      HttpTrigger1: [GET,POST] http://localhost:7071/api/{*route}

For detailed output, run func with --verbose flag.
[2023-05-13T15:05:43.763Z] Worker process started and initialized.
[2023-05-13T15:05:47.523Z] Host lock lease acquired by instance ID '000000000000000000000000083FF14C'.

別ターミナルを開き、curlでGETリクエストを送ります。

curl http://localhost:7071/api/test

### 出力 ###
StatusCode        : 200
StatusDescription : OK
Content           : This is Test
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 12

リクエストが成功すればAzure Functionsの雛形は完成です。


2. CI/CD環境の構築

①GitHubへアップロード

ここまでの手順で作成したフォルダをGitHubへアップロードします。
私の環境では、apiフォルダとdev-appがアップロード対象となります。

image.png

②Azure Static Web Appsの作成

AzureポータルからStatic Web Appsを作成します。

image.png

主な設定項目は以下の通りです。

  • デプロイの詳細
    • ソース=GitHub
    • アカウント=GitHubアカウント名
  • 組織:GitHubアカウント名
  • リポジトリ:本環境のリポジトリ
  • 分岐:main
  • ビルドのプリセット:Vue.js
  • アプリの場所:dev-app (vueのプロジェクト名)
  • APIの場所:api (Azure Functionsフォルダ)
  • 出力先:dist

image.png

確認及び作成→作成とクリックし、リソースを作成します。
デプロイ画面に遷移します。成功したらリソースに移動をクリックします。

CI/CDプロセスのステータスを確認します。
Static Web Appsのリソース画面に移動した後、画面右側のGitHubアクションの実行をクリックします。

image.png

GitHubの画面に遷移します。
デプロイエラーが発生していなければデプロイが完了しています。

image.png

Static Web Appsの画面に戻り、画面右側に表示されているURLをクリックします。

image.png

1-①で作成した画面と同じものが表示されていればCI/CD環境の構築は完了です。



3. ユーザ認証を実装する (未完了)

認証方法は多数ありますが、今回は簡易的な認証を実装します。

  • Static Web Appsからアプリケーションへのアクセスを許可するユーザを招待
  • 招待リンクをユーザに送信し、ユーザ側で招待を

※他認証方法は本記事では取り扱いません。参考情報をいくつか紹介します。

①ユーザ招待リンクの生成

画面左側タブ:ロール管理をクリックします。

image.png

認証プロバイダーを選択し、必要な情報を入力します。
本環境では、GitHubアカウントを認証に使用します。

image.png

image.png

生成された招待リンクをコピーし、ユーザに送信します。

image.png

②ユーザ側で招待リンクを開き、アカウント連携を行う

①で生成したリンクを開き、招待を承認します。

image.png

Static Web Appsとのアカウント連携を承認します。

image.png

③Static Web Appsに認証機能を追加する

②で招待したユーザのロールを使用し、認証機能を実装します。
先ほど招待したユーザのロールを確認します。
Static Web Apps → ロール管理を開くと、以下のように先ほど招待したユーザのロールを確認できます。

image.png

①で設定したロール名invited_usersにて、招待済みユーザを表すロールが付与されています。

a. フロントエンドに認証ページ機能を追加

ログイン機能は以下のように実装します。

# GitHubでログインする場合
<a href="/.auth/login/github">Login</a>

# GitHubでログインし、リダイレクト先を指定する場合
<a href="/.auth/login/github?post_login_redirect_uri=<Static Web AppsのURL>/<任意のパス>">Login</a>

本環境では、Vue.jsでフロントエンドを実装しているため、
ページ遷移の機能を追加し、以下ファイルを作成・編集します。

  • dev-app/src/components/UserLogin.vue (作成)
  • dev-app/src/components/ChatOpenAI.vue (作成)
  • dev-app/src/App.vue (編集)
  • dev-app/src/main.js (編集)

ページ遷移の機能を追加します。

cd dev-app
# Vue2を使用している場合、vue-router3系をinstall
npm install vue-router@3.6.1

dev-app/src/components/UserLogin.vueを作成し、以下のようなコードを配置します。
ログイン後、/homeにリダイレクトする設定になっています。

dev-app/src/components/UserLogin.vue
<template>
  <div class="login-container">
    <h1>Login</h1>
    <a href="/.auth/login/github" class="btn btn-primary" >Login with GitHub</a>
  </div>
</template>

<script>
export default {
  name: "UserLogin",
  data: ()=>({
    user_data: "",
    userRoles: []
  }),
  methods:{
    LoginClicked: async function(){
      try{
        // ログイン情報の取得
        const ret = await fetch('/.auth/me');
        this.user_data = await ret.json();
        this.userRoles = this.user_data.clientPrincipal.userRoles

        if (this.userRoles.includes('invited_users')){
          console.log("Success.")
        }
        else{
          console.log("Not Authorized.")
        }
      }
      catch{
        this.user_data = ""
        this.userRoles = []
        console.log("Error.")
      }
    },
    CheckLogIn: async function(){
      try{
        // ログイン情報の取得
        const ret = await fetch('/.auth/me');
        this.user_data = await ret.json();
        this.userRoles = this.user_data.clientPrincipal.userRoles
        
        // invited_usersロールを持っているか確認
        if(this.userRoles.includes('invited_users')){
          this.$router.push({path: "home"})
          console.log("Login.")
        }else{
          console.log("Not LogIn.")
        }
      }
      catch{
        console.log("Not LogIn.")
      }
    }
  },
  mounted(){
    this.CheckLogIn()
  }

}
</script>

<style scoped>
.login-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}

.btn {
margin-top: 20px;
padding: 10px 20px;
text-decoration: none;
color: #fff;
background-color: #007bff;
border: none;
border-radius: 4px;
cursor: pointer;
}

.btn:hover {
background-color: #0056b3;
}

h1 {
font-size: 24px;
margin-bottom: 20px;
color:  #b6b2b2;
}
</style>

dev-app/src/components/ChatOpenAI.vueを作成し、以下のようなソースコードを配置します。

dev-app/src/components/ChatOpenAI.vue
<template>
    <div>
        <h1> Login Successed. </h1>
    </div>
</template>

dev-app/src/App.vueを以下のように編集します。

dev-app/src/App.vue
<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>


export default {
  name: 'App',
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

body {
  background-color: #383737; /* ダークモードの背景色 */
}
</style>

dev-app/src/main.jsを以下のように編集します。
ルーティング設定を行います。

dev-app/src/main.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import App from './App.vue'
import UserLogin from './components/UserLogin.vue';
import ChatOpenAI from './components/ChatOpenAI.vue';

Vue.config.productionTip = false

// ルーティング設定
Vue.use(VueRouter)
const router = new VueRouter({
  mode: 'history',
  routes:[
    {
      path: '/',
      component: UserLogin
    },
    {
      path: '/home',
      component: ChatOpenAI
    }
  ]
})

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

b. Static Web Appsの設定ファイルを作成

以下のファイルを作成し、アクセス前に認証を実施するリクエストパスを定義します。
また、

  • dev-app/staticwebapp.config.json
    • リクエストパス「/」は全てのユーザを許可
    • リクエストパス「/home」はinvited_usersロールを持つユーザのみアクセス許可
    • リクエストパス「/api*」はinvited_usersロールを持つユーザのみアクセス許可
dev-app/staticwebapp.config.json
{
    "$schema": "https://json.schemastore.org/staticwebapp.config.json",
    "routes": [
      {
        "route": "/", "allowedRoles" : ["anonymous"]
      }, 
      {
        "route": "/home", "allowedRoles" : ["invited_users"]
      },
      {
        "route": "/api*", "allowedRoles" : ["invited_users"]
      }
    ]
}

c. Githubに変更をアップロードし、挙動を確認

GitHubにアップロードし、Static Web Appsの画面からGitHubアクションの実行をクリックします。
CI/CDプロセスが問題なく完了していることを確認し、Static Web AppsのURLをクリックしてアプリケーションにアクセスします。

※既に一度ローカル環境で認証をしている場合、ログイン情報が保持されているため認証画面に遷移しないことがあります。その際は、プライベートモードなどでタブを開いてアクセスします。

image.png

Login with GitHubをクリックすると以下の画面に遷移します。
必要な情報を入力し、Sign inをクリックします。

image.png

サインイン後、以下のような画面に遷移すれば完了です。

image.png


4. Static Web AppsとAzure Functionsを連携

Azure FunctionsでAPIエンドポイントを作成し、Static Web Apps側からAPIを実行します。
実際の作業は以下のような流れです。

  • Vue.js (Static Web Apps)
    • フォーム(入力欄&ボタン)を作成
    • APIを叩くための関数を作成
    • ボタンが押されたら関数を実行
  • Flask (Azure Functions)
    • 特定のURIでPOSTリクエストを受け付ける
    • Vue.jsで入力されたデータを取得
    • 取得したデータを入力として実行可能な関数を作成
    • 関数の実行結果をVue.jsに送信

①Static Web Apps側の開発

はじめに、HTTPリクエストを送信するためのモジュールをインストールしておきます。

cd dev-app
npm install axios

フォーム・APIを実行する関数を作成します。
以下のファイルを作成(または編集)します。

  • dev-app/src/components/ChatOpenAI.vue
dev-app/src/components/ChatOpenAI.vue
<template>
  <div>
    <div class="chat-container">
      <div 
        v-for="message in prev_messages" 
        :key="message.id" 
        :class="['chat-message', message.isResponse ? 'response' : 'user']"
        >
        <span class="chat-message-text">{{ message.text }}</span>
      </div>
    </div>
    <div class="input-container">
      <input
        v-model="question"
        type="text"
        @keydown.enter="ExecOpenAIApi(question)"
        placeholder="質問を入力してください"
        class="chat-input"
      />
      <button 
        @click="ExecOpenAIApi(question)" 
        class="chat-button"
        :class="{ 'button-hover': isButtonHovered }"
        @mouseover="isButtonHovered = true"
        @mouseout="isButtonHovered = false"
      >
        送信</button>
    </div>
  </div>
</template>

<script>
    const axios = require('axios').create();

    export default{
        name: "ExecOpenAI",
        data:() => ({
            post_data:{
                question: ""
            },
            response: "",
            // prev_messages:[],
            prev_messages:[],
            message:{
                id: 0,
                text: "",
                isResponse: false
            },
            isButtonHovered: false
        }),
        methods: {
            ExecOpenAIApi: async function (question){
              // Azure OpenAIの呼び出し
              this.post_data.question = question;
              await axios.post("/api/question", this.post_data)
                  .then((res) =>{
                      // ユーザの入力データを会話履歴に追加
                      const UserMessage = {
                          id: Date.now(),
                          text: question,
                          isResponse: false
                      }
                      this.prev_messages.push(UserMessage);
  
                      return res.data["message"]
                  })
                  .then((answer) => {
                      // 回答を会話履歴に追加
                      const ResponseMessage = {
                          id: Date.now(),
                          text: answer,
                          isResponse: true
                      }
                      this.prev_messages.push(ResponseMessage);
                  })
                  .catch((error) => {
                      console.log(error)
                  })
            }
        }
    }
</script>


<style scoped>
.chat-container {
  max-height: 600px;
  overflow-y: scroll;
  border: 1px solid #6d6a6a;
  background-color: #555252; /* ダークモードの背景色 */
  padding: 10px;
}

.chat-message {
  padding: 10px;
  margin-bottom: 10px;
  display: flex;
}

.response {
  justify-content: flex-start;
}

.user {
  justify-content: flex-end;
}

.chat-message-text {
  word-wrap: break-word;
  max-width: 80%;
  padding: 8px;
  border-radius: 10px;
  background-color: #353333;
  color: #dad5d5;
  text-align: left;
  font-family: "Yu Gothic", "ヒラギノ角ゴシック", "Hiragino Sans", "メイリオ", Meiryo, sans-serif;
  font-size: 18px
}

.response .chat-message-text {
  background-color: #2e2d2d;
}

.user .chat-message-text {
  background-color: #2e2d2d;
}

.input-container {
  display: flex;
  margin-top: auto;
  position: sticky;
  bottom: 0;
  left: 30px;
  right: 0;
  padding: 20px;
  height: 60px;
  width: 70%;
  background-color: #1a1919; /* ダークモードの背景色 */
}

.chat-input {
  flex: 1;
  padding: 5px;
  border: 1px solid #3b3b3b; /* 入力欄の枠線の色 */
  color: #0e0d0d; /* 入力欄の文字色 */
}

.chat-button {
  margin-left: 5px;
  padding: 8px 15px;
  font-size: 16px;
  background-color: #4477c9; /* ボタンの背景色 */
  color: #ffffff;
  border: none;
  border-radius: 5px;
  transition: background-color 0.3s ease;
}

.chat-button:hover {
  background-color: #9fb2da; /* マウスが重なった時の背景色 */
}
</style>

②Azure Functions側の開発

作成・編集するファイルは以下の2つです。

  • api/flask_app/app.py
  • api/flask_app/modules/openai_client.py

Pythonパッケージのopenaiが必要となるため、先にインストールしておきます。
追加でインストールしたパッケージがAzure Functions側でもインストールされるようにrequirements.txtへの追記が必要です。

pip install openai
api/requirements.txt
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues

azure-functions
flask
openai

a. フロントエンドからのリクエストを処理

ユーザ(フロントエンド)からのPOSTリクエストを受け取り、入力を抽出します。
Azure OpenAIを実行するモジュールを呼び出します。
最後に、ユーザ(フロントエンド)へAzure OpenAIの回答を送信します。

api/flask_app/app.py
from flask import Flask, request, jsonify

# モジュールのインポート
from .modules.openai_client import OpenAIClient

app = Flask(__name__)

@app.route('/api/test', methods=['GET'])
def api_test():
    return 'This is Test'

@app.route('/api/question', methods=['POST'])
def handle_message():
    question = request.json['question']

    # Azure OpenAIのAPIを実行
    response = OpenAIClient().chat_openai(question)
    response = {'message': response}

    return jsonify(response)

if __name__ == '__main__':
    app.run()

b. Azure OpenAIをAPI経由で実行する

api/flask_app/modules/openai_client.py
import openai
import os

class OpenAIClient:
    def __init__(self):
        # Azure OpenAIの基本設定
        openai.api_type = "azure"
        openai.api_base = "https://openai-for-app.openai.azure.com/"
        openai.api_version = os.getenv('API_VERSION')
        
        # Azure OpenAIのAPIキーを設定
        openai.api_key = os.getenv('OPENAI_API_KEY')
        self.model_name = os.getenv('MODEL_NAME')

        if (openai.api_key is None) or (openai.api_base is None):
            raise ValueError("エンドポイントまたはOpenAI APIキーが設定されていません。")

    def chat_openai(self, question):
        try:
            system_message = """
            You are an AWS specialist.
            Avoid using technical jargon and explain in easy-to-understand language.
            You need to help users understand with analogies that even elementary school students can understand."
            """

            response = openai.ChatCompletion.create(
                engine=self.model_name,
                messages = [
                    {"role": "system", "content": system_message},
                    {"role": "user"  , "content": question}
                ],
                temperature=0.7,
                max_tokens=800,
                top_p=0.5,
                frequency_penalty=0,
                presence_penalty=0,
                stop=None
            )

            return response.choices[0].message.content
        
        except Exception as e:
            return "Error"


if __name__ == "__main__":
    test = OpenAIClient()
    res = test.chat_openai("これはテストです。")
    print(res)

③APIの挙動をテストする

a. Azure OpenAIを作成する

Azure OpenAIにモデルをデプロイし、API経由で実行可能な状態にしておきます。
前回リソース作成部分をまとめています。詳細はそちらをご覧ください。

b. Static Web Appsの環境変数にAzure OpenAIの情報を登録

Static Web Appsの画面に移動し、Azure OpenAIのエンドポイントとAPIキーを環境変数として登録します。
環境変数を使う主なメリットを一部紹介します。

  • クレデンシャル情報のハードコーディングを避けられます。
  • モデルを変更したい場合など、ソースコードを編集せずに済みます。

環境変数として設定するものは以下の3つです。
環境変数として設定するものは以下の4つです。
※以降、モデルのエンドポイントも環境変数と扱えるように読み替えていただければと思います。

  • モデルのエンドポイント (エンドポイントの環境変数化を失念しており、追記致しました。)
  • モデルのデプロイ名
  • モデルのバージョン
  • APIキー

プレイグラウンドから、コードの表示をクリックすると全て取得できます。

image.png

設定していきます。
Static Web Appsの画面左側タブの構成をクリックします。

image.png

追加をクリックします。
(ステージング環境を構築している場合は、環境を選択することで設定を分離できます。)

image.png

必要な情報を入力し、OKをクリックします。

  • 名前:環境変数の変数名。バックエンドで変数を読み込む際にこの名前を指定する
  • : 環境変数の値。

モデルのデプロイ名・モデルのバージョン・APIキーをそれぞれ設定し、画面上部の保存をクリックします。

image.png

以上で環境変数の設定は完了です。
API経由で実行するモデルの参照先を変更する場合は、同様の手順でエンドポイントなどを変更します。

c. ローカル環境からAPI経由でAzure OpenAIを呼び出す

本環境で使用しているStatic Web Apps×Azure Functionsをローカル環境で動かしてデバッグするためには、azure/static-web-apps-cliが必要です。(参考)

npm installでインストールし、swaコマンドを実行することで、
Static Web Apps×Azure Functionsの環境がローカルで実行されます。

# インストール
cd dev-app
npm install -g @azure/static-web-apps-cli

# 依存関係を基に必要なモジュールをインストールし、buildする
npm install && npm run build 

# ローカル環境で実行する
swa start dist --api-location ../api


### 出力 ###
 Serving static content:
[swa]   Azure-OpenAI-test\dev-app\dist
[swa]
[swa] Serving API:
[swa]   Azure-OpenAI-test\api
[swa]
[swa] Azure Static Web Apps emulator started at http://localhost:4280. Press CTRL+C to exit.       
[swa]
[swa]
[api] [2023-05-13T16:23:50.785Z] Host lock lease acquired by instance ID '000000000000000000000000083FF14C'.

プライベートブラウザでhttp://localhost:4280を開きます。

image.png

Login with GitHubをクリックします。

image.png

上記の画面にて、UsernameUser's rolesを編集します。
実際にログインを行っているわけではなく、Static Web Appsの認証フローをシミュレーションしたものとなっています。
そのため、3-①ユーザ招待リンクの生成で設定した値を入力し、本番環境をシミュレーションします。

  • Username: <ユーザ名>
  • User's roles: invited_users

上記2項目を入力します。

image.png

ログインをクリックすると、/homeにリダイレクトされます。
ユーザ名・ロール名が正しくない場合は、ログイン画面に戻ります。
このように、認証フローをシミュレーション可能です。

質問を入力し、送信ボタンまたはEnterを押下します。

image.png

以下のように、Azure OpenAIからの回答が得られます。
これでローカル環境でのデバッグは完了です。

image.png


5. GitHubに変更をアップロード

ここまでの変更をGitHubリポジトリに反映し、CI/CDプロセスを開始します。
問題なくCI/CDプロセスが完了したらStatic Web AppsのURLをクリックし、一通り動作検証を行います。


まとめ

様々なAzureサービスと連携させてみると活用方法の幅が広がりそうです。
普段はAWSに触れているので、これを機にAzureサービスについても色々と勉強しようかと思います。
また、プライベート環境の構築が完了していないため、引き続き本環境を触っていきます。

以前、ファイルをアップロードしてEmbeddingを作成し、
アップロードしたファイルの内容を基に回答を生成するという機能を作成したため、
そちらを本環境に組み込みたいと思います。

余談ですが、今回フロントエンドのデザイン部分は全てChatGPTに任せました。
例えば、本環境ではユーザの質問・Azure OpenAIの回答がチャット形式で表示されます。
これはChatGPTに以下のようにお願いしました。

チャット形式で会話履歴を表示したい。LINEのようなイメージです。
右側にユーザの質問を表示し、左側には質問への回答を表示してください。

これ以外にもプロンプトエンジニアリングで多少カスタマイズはしていますが、
話し言葉のようにお願いするだけで意図を汲み取りコードを提案してくれるのは非常に便利でした。


【追記予定】プライベートな環境を構築する (未完了)

※今後追記予定です。

アプリケーションへのパブリックアクセスを許可する場合、この手順は不要です。
また、Static Web AppsをStandardプランにする必要があります。

image.png

Static Web Appsにプライベートエンドポイントを構築することで、
アプリケーションにアクセスできる経路をプライベートな仮想ネットワーク(VNet)経由のみに限定します。
特定のVnetに所属するAzure仮想マシンや、閉域網からのアクセスに対してアプリケーションを公開し、
パブリックIPからのアクセスを拒否することが可能となります。

①仮想ネットワークの作成

以下リソースをAzureポータルから作成します。

  • 仮想ネットワーク ×1
  • サブネット ×2
    • クライアント用に用意する仮想マシンを配置
    • Static Web Apps用プライベートリンクのNICを配置

仮想ネットワークの作成画面を開き、必要な情報を入力します。

image.png

IPアドレスの設計を行うと同時に、サブネットを追加します。

image.png

他の設定はデフォルトのままとし、リソースを作成します。

②プライベートリンクの作成

Static Web Appsの画面に移り、画面左側のタブ:プライベートエンドポイントをクリックします。

image.png


9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?