Azure Functions でAOAIのAPIを実行する

Azure OpenAI ServiceのAPIをFunctionsから実行する

# 環境とプロジェクトのひな型を用意する
前回書いたこの記事を参考に用意する

https://qiita.com/t-sho/items/857f330122f9e45d7845

# Azure OpenAI Serviceのリソース作成
Azure Portalからリソースを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/04bcc1f7-5b74-f5b4-63ba-1bf854018cfd.png)

リソース作成後、Azure OpenAI Studioを起動する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/dda65053-36d8-7dd6-ca51-c97a72453951.png)

言語モデルをデプロイするため、左のメニューからデプロイを選択し、新しいデプロイの作成を行う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/4dba128b-5558-a6ec-bc92-5ea7d0e1824a.png)

使用したいモデルを選択し、デプロイ時の名前を適当に決めて作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/468535e5-f386-2012-97da-b899d41e087d.png)

ちょっと待ってから、左のメニューのチャットを選択し、
適当に何か送信し応答が返ってきたらリソースの作成は完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/c3b4d524-6716-22ca-27ef-4c12c0eb9a71.png)



# 必要なライブラリを追加インストールする
## requirements.txtを編集する
「langchain-openai」を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/01e5eab5-b10b-48e6-28be-3b88faed1288.png)


## 仮想環境をアクティベート
VSCodeのターミナルでプロジェクトの仮想環境をアクティベートする
```
.\.venv\Scripts\activate
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/ed273f1c-2859-d475-ec5d-bcc617902bc5.png)

## ライブラリのインストールを実行
```
pip install -r requirements.txt 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/40cd061e-7eae-8e77-78c6-4969066af766.png)

## local.settings.jsonに環境変数を追記
Valuesの要素に追記する
```
"AZURE_OPENAI_API_KEY":"Your Azure OpenAI Key",
"AZURE_OPENAI_ENDPOINT":"Your Azure OpenAI Endpoint",
"AZURE_OPENAI_API_VERSION":"Your Azure OpenaI API Version",
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME":"Your OpenAI Deployment NAme"
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/cadbb1b8-0bc1-abb0-4c92-ae24ba57d1e4.png)

各変数に設定する値は、Azure OpenAI Studioのチャットプレイグラウンドで「コードの表示」のところから取得する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/bfe1a2d3-41d8-7304-4349-0fdcf384ff37.png)

# 関数の本体コードを書く
HTTPのリクエストをトリガーにする関数を作る。
要求の本文で以下のようなJSONを受け取る想定にしておく
```
{"messagelist":[
    {"type":"system","message":"あなたは優秀なアシスタントです。日本語で回答してください"},
    {"type":"human","message":"おすすめのプログラミング言語を教えて"}
    ]
}
```
会話を重ねる場合は、リストに追加された状態で来るイメージ。
会話していくとテキストの量が増えるので、CosmosDBとかに突っ込んでおいて必要最低限やり取りするだけで良いかもだが、今回はテストなのでこの形式で進める
```
{"messagelist":[
    {"type":"system","message":"あなたは優秀なアシスタントです。日本語で回答してください"},
    {"type":"human","message":"おすすめのプログラミング言語を教えて"},
    {"type":"ai","message":"python,C#,javaの3つです"},
    {"type":"human","message":"C#の理由を簡潔に教えて"}
    ]
}
```

関数の中身はこんな感じにしておく
```python:function_app.py
import azure.functions as func
import logging
import os
from langchain_core.messages import HumanMessage,SystemMessage,AIMessage
from langchain_openai import AzureChatOpenAI
#import json

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="http_trigger_simple_chat")
def http_trigger_simple_chat(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    model = AzureChatOpenAI(
        openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],
        azure_deployment=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
    )

    data = req.get_json()
    #print(data)
    message_lists = []
    for item in data['messagelist']:
        if item['type'] == 'system':
            message_lists.append(SystemMessage(content=item['message']))
        elif item['type'] == 'human':
            message_lists.append(HumanMessage(content=item['message']))
        elif item['type'] == 'ai':
            message_lists.append(AIMessage(content=item['message']))
    
    #print(message_lists)
    response = model.invoke(message_lists)
    #print(response.content)

    if response.content:
        return func.HttpResponse(response.content)
    else :
        return func.HttpResponse(
            "This HTTP triggered function executed successfully. But an unexpected error occurred with the Azure OpenAI Service API.",
            status_code=200
        )
```

# ローカルで実行
コマンドパレットからエミュレータ起動
```
Azurite:Start
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/55a3ef34-f510-7763-454d-f18ce86e5383.png)

F5キーでプログラム実行する。実行するとrequirements.txtに書かれたライブラリのインストールが実行され、ローカルのエンドポイントが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/ca69cb8b-dc93-447d-56c5-818eb9707609.png)

ローカルのワークスペースから関数を実行するが、もしエラーが表示されていたら最新化する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/61c431b0-f2d8-f237-9d41-700591d808be.png)

関数を右クリックし、「Execute Function now...」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/0fa4542c-cc63-77cd-f2af-ce51df9bd295.png)

コマンドパレットの表示エリアにHTTPリクエストの要求本文を入力する。
ここでは先ほどの入力想定のJSONをコピペしてEnter
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/4674b9e6-d610-61d9-cbbd-53481058cdcd.png)

うまくいけば通知エリアに応答文が通知される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/8b3add43-890b-46ca-adad-9d43a1c639ac.png)

# Azureにデプロイする
ワークスペースのFunctionsのアイコンから「Deploy to Function App」選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/cf7a349c-fade-3e06-9607-30b6954d9f82.png)

ここでは、前回記事でデプロイした関数を指定して上書きするが、新しくリソースを作成しても良い
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/d4dc7360-ad86-a98a-15b4-335ece8aa99e.png)

うまくいけばSucceededが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/4270b5b7-6a21-73ca-30ae-5b8426ad359f.png)

# Azure上で環境変数を設定する
ローカル実行する際にAPI Keyなどをlocal.settings.jsonに書き込んだが、Azure上で実行する場合はポータルから環境変数を設定しておく必要がある（環境変数の呼び出す場所が違うため）
デプロイした関数の左のメニューから環境変数を開き「Add application setting」からlocal.settings.jsonで設定した項目と同じように設定を追加する。
追加後は「適応」をクリックして設定を保存する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/1e628e4b-1ac2-1c89-0d83-ada9cc2f77bd.png)

VSCode以外から実行してみる
コマンドプロンプト起動して下記を実行してみる。
関数URLはAzurePortal上から取得して書き換える（認証レベルをFUNCTIONにしてる場合は関数キー付きを使用する）
```winbatch:command
curl -X POST "https://example.com/api" -H "Content-Type: application/json" -d "{\"messagelist\":[{\"type\":\"system\",\"message\":\"あなたは優秀なアシスタントです。日本語で回答してください\"},{\"type\":\"human\",\"message\":\"おすすめのプログラミング言語を教えて\"},{\"type\":\"ai\",\"message\":\"python,C#,javaの3つです\"},{\"type\":\"human\",\"message\":\"C#の理由を簡潔に教えて\"}]}"
```

うまくいけば応答が返ってくるはず
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/ebb65c5b-b9fd-da0a-7029-3f4b9013d23b.png)

おわり