見出し画像

Azure Functions(関数アプリ)に初成功で嬉しすぎる件について

こんばんは! 出戻りガツオ🐟です。

前々から苦戦して成功体験がないAzure Functions(関数アプリ)
やっと成功したーーーーー🙌🙌

Pythonはそれなりに付き合っている経験が長いのですが・・・
Linuxって何???というのとデバッグがしづらかったりと四苦八苦。

やっと成功しましたので内容シェアします!
動画はコチラ!

Azure Functions(関数アプリ)とは??


Azure上で動かせるサーバーレスアプリケーションです!
従量課金制でスクリプトを動かせるAzureの機能です。

トリガーのバラエティも豊富ですが、代表的な一例としてはHTTP TriggerオリジナルAPIのように使えます!魅力👀✨

ですが・・・環境面の知識が疎くて四苦八苦。
requiremts.txt(ライブラリ)によって上手にいかなかったり…

特にLLM(Large Language Model)のライブラリは活用できず悔しい思いをしました。

Azure Functions(関数アプリ)の優位性

Azure Functionsを使うメリットとして下記が挙げられます。

  • Webhook設定をして関数を随時呼びだせること

  • Power Apps経由のコネクタ連携よりもHTTP要求の工夫の余地があること

コネクタ経由もといAPI経由で動くことについて

HTTP要求の送信、受信に時間が大幅にかかってしまうと、アプリそのものの機能が使えない場合があります。
これは制限事項に当てはまってしまうケースです。

Azure Functionsを利用することで、APIの利用をAzure Functions内で完了することができたり、 Azure Service Busを使うことで非同期処理が使えたりと、活用の幅を広げることができます!

  • PL-400対策で勉強したのですが、試験は勉強不足でした・・・(´;ω;`)
    次は受かる!

今回実行したこと

下記の機能の連携を実現しています。
とある方から依頼?アイディアをいただき作っているものです。

Qiitaや技術的なブログもUpする予定ですのでお待ちください!

  • LINE Messaging API

  • Azure Functions(関数アプリ)

  • Azure Computer Vision

  • Azure OpenAI(gpt 3.5 turbo)

import azure.functions as func
import os
import requests
import json
import logging

# Constants
LINE_REPLY_API = 'https://api.line.me/v2/bot/message/reply'
LINE_ACCESS_TOKEN = os.environ['LINE_ACCESS_TOKEN']
COMPUTER_VISION_URL = os.environ['COMPUTER_VISION_URL']
COMPUTER_VISION_KEY = os.environ['COMPUTER_VISION_KEY']
OPENAI_API_URL = os.environ['OPENAI_API_URL']
OPENAI_API_KEY = os.environ['OPENAI_API_KEY']

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

@app.route(route="azure_line_image_interpreter", methods=['POST'])
def azure_line_image_interpreter(req: func.HttpRequest) -> func.HttpResponse:
    """
    Main function that handles POST requests.
    Processes the images received from LINE bot, extracts the text, 
    and sends a summarized reply.
    """    
    logging.info('Python HTTP trigger function processed a request.')

    body = req.get_json()

    logging.info(body)
    events = body['events']

    for event in events:
        if event['type'] == 'message':
            image_id = event['message']['id']
            # message = event['message']['text']
            image_content = get_image_content_from_line(image_id)
            extracted_text = extract_text_from_image(image_content)
            summarize_text = summarize_text_ocr_result(extracted_text)
            reply_to_line(event['replyToken'], summarize_text)

    return func.HttpResponse(
            "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
            status_code=200
    )

def get_image_content_from_line(image_id):
    """
    Fetches the image content from LINE messenger using the message ID.
    """
    headers = {
        'Authorization': f'Bearer {LINE_ACCESS_TOKEN}'
    }
    response = requests.get(f'https://api-data.line.me/v2/bot/message/{image_id}/content', headers=headers)
    return response.content

def extract_text_from_image(image_data):
    """
    Uses the Azure Computer Vision service to extract text from the given image data.
    """
    headers = {
        'Content-Type': 'application/octet-stream',
        'Ocp-Apim-Subscription-Key': COMPUTER_VISION_KEY
    }
    response = requests.post(COMPUTER_VISION_URL, headers=headers, data=image_data)
    data = response.json()
    return data['readResult']['content']

def summarize_text_ocr_result(extracted_text):
    """
    Uses the OpenAI API to summarize the extracted text and translates it to Japanese.
    """
    headers = {
        'Content-Type': 'application/json',
        'api-key': OPENAI_API_KEY
    }
    payload = {
                "messages": [
                    {
                        "role": "system",
                        "content": "#Instructions\nYou are a professional editor. Please summarize the text you are entering according to the following constraints.\n\n#Constraints\n・Provide machine-translated transcriptions of the text. Please infer the content of the document from the text and state what kind of document it is at the beginning.\n・Don't leave out important keywords.\n・Don't change the meaning of the text.\n・Summarize the content in a concise manner.\n・Summarize the input text within 300 characters, including punctuation, and output it in Japanese.\n・Don't change the numerical values in the sentences."
                    },
                    {
                        "role": "user",
                        "content": extracted_text
                    }
                ],
                "max_tokens": 500,
                "temperature": 0.7,
                "frequency_penalty": 0,
                "presence_penalty": 0,
                "top_p": 0.95,
            }
    response = requests.post(OPENAI_API_URL, headers=headers, data=json.dumps(payload))
    data = response.json()
    print(data)
    return data['choices'][0]['message']['content']

def reply_to_line(reply_token, message):
    """
    Sends the summarized text back to the LINE bot.
    """
    headers = {
        'Authorization': f'Bearer {LINE_ACCESS_TOKEN}',
        'Content-Type': 'application/json'
    }
    data = {
        'replyToken': reply_token,
        'messages': [{'type': 'text', 'text': message}]
    }
    requests.post(LINE_REPLY_API, headers=headers, data=json.dumps(data))

アクション

  1. LINEから撮った写真をバイナリ化する

  2. バイナリ化した写真を文字認識する

  3. OpenAIで要約し、POSTする

ざっくりこんな感じです!

詳細は今度Qiitaに挙げます

Comment

Computer VisionのAPIがバイナリで使えることが本当に良かった・・
そしてOCRも今LLMが組み込まれ、精度がとても良いです!

何より成功したことが嬉しいぃい🎉!

最後に

駆け足になりましたが、今回のnoteはここまで!
明日からはじめての職場で超緊張です!

這いつくばってバリューだします!

See you next time♬

いいなと思ったら応援しよう!