Prompt flowでブログ記事の紹介をXにポストする

みなさん、こんにちは。サイオステクノロジー武井です。今回は、Azureが提供するAIアプリを開発するための統合開発環境「Prompt flow」を使って、ブログ記事の紹介をXにPostする方法を説明致します。

統合開発環境Prompt flow

まずは、今回のキモとなる統合開発環境「Prompt flow」について説明致します。Prompt flowをご存じの方はこの章を読み飛ばして頂いて構いません。

Azure OpenAI Serviceには、フローを開発するための便利機能がたくさん詰まっている「Prompt flow」という統合開発環境があります。

フローの開発・テスト・評価・デプロイなど、フローのリリースに至るまでの様々な支援機能がある開発環境です。

ここであるユースケースを想定し、「Prompt flowを使わない場合」と「Prompt flowを使う場合」で、その便利さをわかりみ深く説明したいと思います。

で、そのユースケースとは、以下になります。

特定のURLの記事のカテゴリを分類する。 

例えばアプリの紹介記事だったら「App」、アカデミックな内容だったら「Academic」などに分類します。

そして、この要件を満たすフローを以下のように設計したとします。

それぞれの処理を以下に説明します。

■ 指定したURLからコンテンツを取得する

一番最初に与えられるInputであるURLにアクセスし、その中身のテキストを取得します。画像データなどのバイナリは取得しません。この取得したテキストを以降では「コンテンツ」と呼ぶことにします。

■ コンテンツを要約する

Azure OpenAI Serviceに、先程取得したコンテンツの要約を依頼します。

■ 要約したコンテンツをもとにカテゴリを決定する

先程要約したコンテンツをもとに、Azure OpenAI Serviceにカテゴリの決定を依頼します。

■ データを整形する

APIなどで取得しやすいようにデータを整形します。具体的にはJSONに変換します。

Prompt flowを使わない場合

では先程のユースケースに基づいたフローを、Prompt flowを使わないで開発する場合を考えてみましょう。簡単に図にまとめてみました。左がフローを開発するために必要なプロセス、左がそのプロセスを実現するための手段になります(字が細かいので拡大して見て下さい)。

それぞれのプロセスと実現手段の詳細を以下に記載します。

■ 要件をヒアリングしてフローを設計する

これは、Prompt flowを使う前のプロセスですので、Prompt flowを使わない場合でも使った場合でも変わりはありません。そしてフローは先程、説明したものを設計のアウトプットとして、以降のプロセスで利用します。

■ フローを開発する

設計したフローをもとに、Visual Studio Codeなどのエディタを用いて開発を行います。フローの中ではAzure OpenAI Serviceにアクセスする部分があるので、SDKや汎用的なライブラリであるLangChainなどを使ってコードを書く必要があります。Azure OpenAI Serviceに接続するための資格情報も厳密に管理する必要があります。

■ 大量のデータでテストを行う

設計・開発したフローやプロンプトの妥当性を図るために、大量のデータでテストを実施します。記事のURLと、その記事の想定カテゴリを定義したテストデータを用意して、開発したフローに読み込ませ、実行結果と期待値の比較を行います。

例えば以下のようなCSVデータを用意します。

https://tech-lab.sios.jp/archives/37250,Academic
https://contoso.com/healthcare,App

1列目は記事のURL、2列目はその記事がカテゴライズされるであろうカテゴリになります。

1行目のhttps://tech-lab.sios.jp/archives/37250はAcademicのカテゴリ、2行目のhttps://contoso.com/healthcareはAppのカテゴリにカテゴライズされることを想定しています。

このテストデータのCSVを読み込み、フローによる出力結果と期待値を比較するテスト用プログラムを書く必要があります。

まぁ、通常の開発プロセスではありますが、だるい作業ではあります。

■ 本番環境にデプロイしてエンドユーザーに利用してもらう

開発とテストが終わったらいよいよリリースです。Azureであれば、Virtual MachineやApp Serviceなどのコンピューティングリソースを用意して、開発したフローが動くアプリケーションをビルドしてデプロイして、、、という作業が必要になります。

うーん、めんどくさい。

そしてユーザーのフィードバックがあった場合には、「フローを開発する」のプロセスに戻り、開発・テスト・リリースというプロセスを繰り返します。

Prompt flowを使う場合

では、Prompt flowを使った場合の開発プロセスを説明します。先程と同様に図にまとめてみました(字が細かいので拡大して見て下さい)。

■ 要件をヒアリングしてフローを設計する

これは先程と同様、Prompt flowを使う前のプロセスですので、Prompt flowを使わない場合でも使った場合でも変わりはありません。

■ フローを開発する

Webで提供されている専用の開発環境「Machine Learning Studio」内にある「Prompt flow」を用いて行います。Visual Studio Codeなどの統合開発環境の用意及びそれを動かすためのコンピューティングリソースは不要です。また、Azure OpenAI ServiceやOpenAIに接続するためのライブラリや、それを用いた実装も不要です。あらかじめ、OpenAIやAzure OpenAI Serviceの接続情報を定義しておけば、テンプレートに基づいてプロンプトを書くだけで、自動でOpenAIやAzure OpenAI Serviceに接続してくれます。

■ 大量のデータでテストを行う

Batch runという機能が用意されており、テストデータと期待値が定義された所定のCSVを読み込ませるだけで、テストの実行と結果の出力まで自動でやってくれます。

■ 本番環境にデプロイしてエンドユーザーに利用してもらう

フローを動作させるためのVirtual MachineやApp Serviceなどのコンピューティングリソースをわざわざ用意する必要はありません。「Deploy」というボタンをクリックし、以降のウィザードに従い、インスタンスサイズなどを指定するだけで、フローを動かすための環境が作成され、エンドポイントの発行も自動で行われます。

Prompt flowでブログ記事の紹介をXにポストする

では、本章では、Prompt flowでブログ記事紹介をXにポストするための手順をご紹介したいと思います。

そもそも何するもの?

ブログを書いたはいいですが、書いただけだだと誰も見てくれず、やはりPRは必要になりますよね。そこで、私はXで定期的にポストして、書いたブログ記事のPRをしていました。ただ、正直面倒です。X Premiumに加入すればその制限は撤廃できるものの、Xは通常では140字以内という制限があり、それに収まるようブログ記事のPR文章を調整しなければいけませんし、Xにポストするというのも手間です。XへのポストはAPIで自動化はできるものの、140字以内のブログ記事PR文作成は自動化は難しいです。

そこで、生成AIの出番です。

Azureが提供する生成AIのサービスである「Azure OpenAI Service」にブログ記事のまとめをしてもらって、それをXにポストするというのを自動化してもらうのです。

具体的には、書いたブログ記事の中からTipsを一つ作成し、合わせてそのTipsに関連する画像もXにPostするというものです。

一つのブログ記事にはたくさんの役立つ情報(これを以降Tipsと呼びます)が詰まっています。ブログ記事の要約だと毎回同じような文章になってしまいますが、Tipsを抽出するのであれば、一つのブログ記事の中から様々な内容のPR文が生まれてきます。

さらにそのTipsに関連した画像も付与することによって、テキストのみの簡素なPRから、わかりみ深く視覚に訴えたPRとなり、よりブログにアクセスしてくれることになるでしょう。

図解すると以下のようなイメージになります(クリックして拡大してご覧下さい)。

フローは、Azure OpenAI Serviceによって、ブログ記事から以下の2つを抽出します。

  • 何かしら役に立ちそうなTips
  • Tipsに関連した画像のURL

「記事のURL」はフィードから取得します。

これらをがっちゃんこして、以下のポストを作り上げます。

 

ブログ記事を書いて公開して、フローを定期的に実行するようにすれば、フローが勝手にRSSフィードからブログ記事の内容を取得して、PR文を作成してくれて、Xにポストまでしてくれます。今まで手作業で行っていたのが全自動化されますねヮ(゚д゚)ォ!

処理の流れ

大まかな処理の流れを以下に記載します。真ん中の「Prompt flowで動くフロー(以降、フローと呼びます)」というのが今回Prompt flowで作成する部分です。

 

  1. フローが、WordPressのRSSフィードのURL(本ブログだったらhttps://tech-lab.sios.jp/feed)にアクセスしてRSSフィードからブログ記事の内容を取得する。
  2. フローが、Azure OpenAI Serviceにプロンプトを渡して「Tips」「Tipsに関連した画像のURL」の取得を依頼します。
  3. フローは、2のプロンプトで得られた回答を受け取ります。
  4. フローは、3の回答とフィードのURLより、「ブログ記事のTips」「Tipsに関連した画像」「ブログ記事のURL」をガッチャンコしてXにポストします。

フローの設計

この処理の流れを実現するフローを以下のように設計します。

 

 

WordPressのRSSフィードのURL / ハッシュタグ

どのようなプログラムでも入力値は必要になります。このフローの入力値は、以下の「WordPressのRSSフィードのURL」「ハッシュタグ」の2つになります。

「WordPressのRSSフィードのURL」は、PR対象のブログの本文を取得するために必要なものです。

「ハッシュタグ」は最終的にXにポストするブログに固定値として付与するものです。例えばAzure関連のブログには「#Azure」などつけるとよいでしょう。これは本フローからポストするPR文全てに固定値として付与されます。

RSSフィードからランダムに記事を1件抽出する

RSSのフィードからランダムに記事を1件抽出します。毎回異なる記事をバランスよくPRするためです。

RSS画像へのリンクURL付きTipsを作成する

Azure OpenAI Serviceに依頼して、先程取得した記事のリンクURL付きTipsを作成します。Azure OpenAI Serviceから期待する回答は以下のようになります。

AzureのApp Serviceは多機能でアプリの公開を簡単に行うことが可能。またスケールアップ・スケールアウトによる性能調整や、Azure AADとの認証統合なども可能。詳しくはこのブログを見てね! → tech-lab.sios.jp/archives/36497 #Azure #AppService

https://tech-lab.sios.jp/wp-content/uploads/2021/06/Screen-Shot-2021-06-12-at-1.37.18.png

画像を取得してXにPostする

Azure OpenAI Serviceから先程取得した回答をもとにXにポストします。Xにポストする内容は以下のとおりです。

  • 画像のURLを除くテキスト部分
  • 画像のURLから取得した画像ファイル

Xにポストした文章

Xにポストした文章を最終的な出力とします。

環境とランタイムとコンピューティングインスタンスとフローの関係

フローもその実態はアプリケーションです。アプリケーションが動作するには、その実行基盤が必要であり、フローはVirtual Machine上で動作します。その原理をここでは説明致します。

まず「コンピューティングインスタンス」なるものがありますが、これはVirtual Machineそのものです。まずはコンピューティングインスタンスがないと始まりません。

そして「環境」というものがあります。これはDockerイメージに相当します。設定ファイルもDockerfileを使って作成します。

環境から「ランタイム」と言われるものが作成されます。これはDockerコンテナそのものであり、このDockerコンテナの上でフローは動作します。

上図にある通り、一つの環境から複数のランタイムを作成し、複数のVirtual Machineに配置することが可能です。また、複数のランタイムを一つのVirtual Machine上で動作させることも可能です。

環境については、シンプルなフローを作成するのであれば、標準で用意されている環境で十分です。ただし、今回はXにポストしたり、RSSフィードのURLを解析するのに、特殊なPythonのモジュールを使う必要があり、そのモジュールは標準で用意された環境には入っていません。なので、それらのモジュールをインストール済みの環境(Dockerfile)を作成し、それをもとにランタイムを作成します。

作業の流れ

このフローを作るまでの作業の流れを説明します。以下の図が作業イメージになります(字がちっちゃいのでクリックして拡大して見て下さい)。

1. 環境の作成

環境を作成します。Dockerfileを新たに定義し、そのDockrfileでは標準で用意されているDockerイメージをベースとしつつ、今回のフローを動作するのに必要はPythonのモジュールをpipでインストールするといった定義をします。

2. 接続(開発環境)の定義

Prompt flowでは、Azure OpenAI Serviceへの接続や、Xへの接続に利用する資格情報(APIキーやトークンなど)をフローの中に定義せず、外部から与えることが可能です。その資格情報を定義するために必要なのが「接続」になります。今回は開発環境と本番環境の両方を作成するのですが、ここではまず開発環境用の接続を作成します。

3. コンピューティングインスタンス(開発環境)の作成

先程説明したコンピューティングインスタンスを作成します。

4. 環境からランタイム(開発環境)を作成

環境(Dockerイメージ)からランタイム(Dockerコンテナ)を作成し、フローを動かす基盤を作ります。

5. フローの実装

先程設計したフローに基づき、フローを実装します。

6. 接続(本番環境)の定義

本番環境用の接続を作成します。

7.本番環境へのデプロイ

本番環境へデプロイをします。Prompt flow上では、ボタンポチって感じでできますが、裏では、コンピューティングインスタンスの作成、環境をベースとしたランタイムの作成、フローのデプロイが行われます。

作ってみよう

流れがわかったところで実際に作ってみましょう。まずは下準備として、ワークスペースを作成します。

ワークスペースは開発環境を管理する最上位の単位になります。プロジェクトや機能ごとにワークスペースを作成します。

ワークスペースを作成するためには、以下のURLにアクセスして、Machine Learning Studioを開きます。

https://ml.azure.com/

画面左部メニューにある「ワークスペース」をクリックします。画面左部に「+新規」が表示されますので、それをクリックします。

 

「ワークスペース名」はワークスペースを一意に識別できる任意の名前を入力します。「サブスクリプション」「リソースグループ」「リージョン」はAzure OpenAI Serviceがあるリソースと同等のものにします。最後に「作成」をクリックします。

 

1. 環境の作成

以下のDockerfileを作成します。mcr.microsoft.com/azureml/promptflow/promptflow-runtime-stableをベースイメージとして、requirements.txt(後ほど作成)をもとにpipで必要なPythonモジュールをインストールします。

FROM mcr.microsoft.com/azureml/promptflow/promptflow-runtime-stable
COPY ./* ./
RUN pip install -r requirements.txt

必要なPythonモジュールをインストールするためのrequirements.txtを定義します。

feedparser == 6.0.10
tweepy == 4.14.0
langchain == 0.0.281

環境を構築するためのenvironment.yamlを作成します。

$schema: https://azuremlschemas.azureedge.net/latest/environment.schema.json
name: blog-tips-generator-demo
build:
  path: .

以下のコマンドを実行します。

$ az ml environment create -f environment.yaml --subscription [サブスクリプションのID] -g [ワークスペースを作成したリソースグループ名] -w [ワークスペース名]

Machine Learning Studioから「環境」→「カスタム環境」の順にクリックして、先程作成した環境が以下のように表示されていれば成功です。

 

2. 接続(開発環境)の定義

2つの接続(Azure OpenAI ServiceとXへの接続)を作成します。

まずはAzure OpenAI Serviceへの接続です。「プロンプトフロー」→「接続」→「+作成」→「Azure OpenAI」の順にクリックします。

 

以下のような画面が表示されます。

 

以下の内容に従い、必要事項を入力して、「保存」をクリックします。

名前

接続を一意に識別できる任意の名前を入力します。

プロバイダー
接続先の種別になります。OpenAIやAzure OpenAI Service、Azure Cognitive Searchなどがありますが、ここではAzure OpenAI Serviceに接続したいので、「Azure OpenAI」を選択します。
サブスクリプションID
接続先のAzure OpenAI Serviceがあるサブスクリプションを選択します。
Azure OpenAIアカウント名

接続したいAzure OpenAI Serviceのリソースを選択します。

API key
接続したいAzure OpenAI ServiceのAPIキーを取得します。取得方法については、「デプロイを指定してAPIを発行」を参考にして下さい。
API base
接続したいAzure OpenAI ServiceのAPI baseを取得します。「デプロイを指定してAPIを発行」に記載の「②Language APIs」の値です。
API type
「azure」を指定します。
API version
APIのバージョンを指定します。バージョンについてはこちらのURLに記載があります。

 

次にXへの接続を作成します。「プロンプトフロー」→「接続」→「+作成」→「Custom」の順にクリックします。

 

 

「名前」には接続を一意に識別できる任意の名前を入力します。「プロバイダー」は「カスタム」とします。「キーと値のペア」はXのAPIを実行するのに必要な4つの設定値「ACCESS_TOKEN」「ACCESS_TOKEN_SECRET」「CONSUMER_KEY」「CONSUMER_SECRET」のキーと値を入力します。その際「シークレットである」をチェックして下さい。最後に「保存」をクリックします。ちなみにこれらの値はAzure Key Vaultに保存されます。

 

3. コンピューティングインスタンス(開発環境)の作成 / 4.環境からランタイム(開発環境)を作成

「プロンプトフロー」→「フロー」→「作成」の順にクリックします。

 

様々なフローのテンプレートが表示されます。今回はゼロから作成するので、「標準フロー」をクリックします。

 

フローの名前を入力して、「作成」をクリックします。

 

すでにデフォルトでいくつかの入力、出力、ノードが作成されているので、これらを削除します。下図赤枠のゴミ箱マークをクリックしてすべて削除して、最後に「保存」をクリックします。

 

プロンプトフロー画面上部にて、「+ランタイムの追加」→「コンピューティングインスタンスランタイム」の順にクリックします。

 

「ランタイム名」にはランタイムを一意に識別する任意の名称を入力します。次に「Azure ML コンピューティングインスタンスを作成する」をクリックします。

 

「コンピューティング名」には、コンピューティングインスタンスを一意に識別する任意の名称を入力して下さい。「仮想マシンの種類」はCPUをチェックして下さい。「仮想マシンのサイズ」はフローに応じて必要なスペックのものを選択して下さい。最後に「確認と作成」をクリックします。他にも色々と細かい設定項目があるのですが、今回は割愛します。

 

以下の内容に問題がないことを確認して「作成」をクリックします。

 

「Azure ML コンピューティングインスタンスを選択する」をクリックして、先程作成したコンピューティングインスタンスが「実行中」であることを確認して、そのインスタンスを選択します。カスタムアプリケーション」は「新規」にチェックして下さい。「環境」は「カスタマイズされた環境を使用する」にチェックを入れて、先程作成した環境を選択して、「作成」をクリックします。

 

ランタイムの作成にはちょっと時間がかかります。「ランタイム」の部分が、以下のような表示になっていればOKです。

4. フローの実装

ではフローの中身を実装していきます。「フローの設計」で定義した内容に基づいてフローを組み立てていきます。

入力

入力値を定義します。

「+入力の追加」をクリックします。

 

以下のように入力します。feedはRSSフィードのURLを格納する変数、hash_tagsはXにポストするPR文章の末尾につくハッシュタグになります。

Feedからランダムに記事を1件抽出する

ノードには様々な種類があります。Pythonのコードを実行するノード、OpenAIやAzure OpenAI ServiceのようなLLMにアクセスするノードなど様々です。

今回作成するノードでは、WordPressのRSSフィードのURLからランダムに記事の情報を1件取得する処理をPythonで実行しますので、Pythonのノードを作成します。ということで、下図の①をクリックします。そして②の部分にノード名「get_entry」を入力して、「追加」をクリックします。

 

下図のようなノードが出来上がります。そして赤枠の部分に、WordPressのRSSフィードのURLからランダムに記事の情報を1件取得するコードを入力する必要があります。

 

以下が、WordPressのRSSフィードのURLからランダムに記事の情報を1件取得するコードになります。詳細な処理の内容はコメントに記載しております。

from promptflow import tool
import feedparser
import random

# このアノテーションが付与されてる関数がPrompt flowから呼び出されます。
# このコード内で一つしか定義できません。
@tool
def echo(feed: str) -> str:
    all_entries = []
    current_page = 1
    max_iterations = 500 # 無限ループにならないよう、念のため最大ループ回数を設定しておく

    while True:
        # 与えられたフィードのURLから記事の情報を取得する。
        # 通常では最新の記事情報数件しか取得できないが
        # クエリパラメータpagedをつけることで全件取得できる。
        feed_url = f"{feed}?paged={current_page}"
        feed = feedparser.parse(feed_url)
        entries = feed.entries
        if not entries:
            break
        for entry in entries:
            # 各エントリからcontentとlinkを抽出し、辞書に格納
            # 最終的には以下のようなJSONを返すようにディクショナリを定義する。
            # {
            #   "content": [記事の内容]
            #   "link": [記事のリンクのURL]
            # }
            all_entries.append({'content': entry.content[0].value, 'link': entry.link})
        current_page += 1
        if current_page > max_iterations:
            break  # 最大ループ回数に達したらループを終了

    # 取得した記事の中からランダムに1件取得する。
    entry = random.choice(all_entries)
    return entry

 

先のコードを入力します。そして、「入力値の検証と解析」をクリックします。

 

すると「入力」の「名前」の部分が「feed」になります。これは、@toolデコレーターが付与された関数の引数名に置き換えられた形になります。そして、「値」の部分のセレクトボックスで「${input.feed}」「${input.hash_tags}」という値が選択可能となっております。これは先の「入力」で定義した入力値になります。つまり入力のところで定義した変数は${input.入力の名前}で定義されるということです。ということで「${input.feed}」を選択します。

 

すると、画面右部のグラフで、「inputs」(入力)と「get_entry」が矢印で繋がりました。これは、「inputs」(入力)で定義した入力値feedが、関数get_entryの引数に渡されていることを意味します。こんな感じで、前のノードの出力結果を次のノードに渡していきます。基本的には、これ以降でやる作業についても、この流れでのノードを繋げていきます。まさにフローの構築ですね。

画像へのリンクURL付きTipsを作成する

「+LLM」をクリックします。これはOpenAIやAzure OpenAI ServiceのようなLLMを利用するためのノードになります。

 

「generate_tips」と入力して、「追加」をクリックします。

 

「接続」のセレクトボックスをクリックして、先程作成したAzure OpenAI Serviceへの接続を選択します。これでこのノードからAzure OpenAI Serviceに接続できるようになりました。

 

新たに「API」というセレクトボックスが現れます。これは使うAPIの種類を表すものであり、「chat」「completion」のどちらかを選択するのですが、completionは「チャット」で説明したように非推奨なので、「chat」を選択します。「deployment_name」は作成済みのデプロイを選択します。

 

LLMのノードではプロンプトを与える必要があります(当然ですね)。そのプロンプトは以下になります。Prompt flowではLLMに与えるプロンプトはjinja2というテンプレートをベースに作成します。テンプレートの説明はコメント文({# #}で囲まれたところ)に記載しております。

{# systemのロールにてAIにキャラ付けをしています #}
system:
あなたは、与えられたブログの記事から、なにか画像を一つ引用して、そのブログにある役に立つTIPSを一つ作成するAIアシスタントです。
プロンプトには、「記事の本文」「記事のURL」「ハッシュタグ」が与えられます。それをもとに、記事内にある画像を引用した役に立つTIPSを一つ作成してください。これを「TIPS」と呼びます。

次に、「TIPS」の後に「詳しくはこのブログを見てね!」という文言とともに、記事のURLを挿入してください。これを「記事のURLへのリンク」と呼びます。

次に、「記事のURLへのリンク」の後に「# ハッシュタグ」で指定されたハッシュタグを挿入してください。これを「ハッシュタグ」と呼びます。

ここまでで生成したテキスト(TIPS + 記事のURLへのリンク + ハッシュタグ)は、X(旧Twitter)の制限である140字を超えないようにして下さい。

そして最後に一旦改行して、最後の行には引用した画像のURLを挿入してください。挿入するURLには余計な文字は入れないでください。

もし引用するための適当な画像が見つからない場合は、「no image」という文字列を最後の行に挿入して下さい。

プロンプトの例は以下のとおりです。

# 本文
ここに記事の本文が入ります。

# 記事のURL
ここに記事のURLが入ります。

# ハッシュタグ
ここにハッシュタグが入ります。

{# Few-shots Learningによって、あらかじめ質問と回答のサンプルを与えてあげて、AIがより正確な回答をできるようにします #}
user:
# 本文
AzureのApp Serviceは多機能でアプリの公開を簡単に行うことが可能。
またスケールアップ・スケールアウトによる性能調整や、Azure Active Directoryとの認証統合なども可能で、
とにかくすごい機能ばかりです。
<img decoding="async" fetchpriority="high" id="fancybox-img" class="" src="https://tech-lab.sios.jp/wp-content/uploads/2021/06/Screen-Shot-2021-06-12-at-1.37.18.png" alt="" width="601" height="283" />

# 記事のURL
tech-lab.sios.jp/archives/36497

# ハッシュタグ
#Azure #AppService

assistant:
AzureのApp Serviceは多機能でアプリの公開を簡単に行うことが可能。またスケールアップ・スケールアウトによる性能調整や、Azure AADとの認証統合なども可能。詳しくはこのブログを見てね! → tech-lab.sios.jp/archives/36497 #Azure #AppService
https://tech-lab.sios.jp/wp-content/uploads/2021/06/Screen-Shot-2021-06-12-at-1.37.18.png

user:
# 本文
仕組みとしては、特定のメールボックスを定期的にフェッチして、そのメールアドレス宛の問い合わせ内容をデータベースに格納し、「対応中」「完了」というステータスをつけたり、その他、チケット管理に必要な色々な情報を付与します。

今回やりたいことは、アレクサに「ヘルプデスクの未処理のチケット数を教えて」と話しかけると、「12件のチケットがあります」みたいな感じで、未処理のチケット数を返してくれるスキルを開発することです。

# 記事のURL
tech-lab.sios.jp/archives/36497

# ハッシュタグ
#Azure #駆け出しエンジニアと繋がりたい

assistant:
アレクサをカスタマイズし、OTRSの未処理のチケット数を教えてもらう方法を学べます。Lambdaを使うことでサーバーレスを実現しています!!料金も節約できます。詳しくはこのブログを見てね! → tech-lab.sios.jp/archives/8122 #Azure #駆け出しエンジニアと繋がりたい
no image

{# AIに投げる最終的な質問です。{{...}}はプレースホルダーであり、後ほど説明しますが、AIから与えられた回答によって置換します #}
user:
# 本文
{{article_content}}

# 記事のURL
{{aricle_url_without_scheme}}

# ハッシュタグ
{{hash_tags}}

 

上記のコードを入力して、先程と同様に「入力の検証と解析」をクリックします。すると先のコード内の{{}}で囲まれたプレースホルダーの部分が、入力の名前として設定されます。

以下の通り設定します。

article_content
ノードの出力は${ノード名.output}で取得できます。よって、get_entryノードの出力は${get_entry.output}で取得できます。さらにget_entryの出力は以下の形式となります。
{
  "content": [ブログ記事の本文],
  "link": [ブログ記事のURL]
}
article_contentにはブログ記事の本文を入れたいので、ここに設定する値は、${get_entry.output.content}になります。
article_url_without_scheme
article_contentと同様の理由で、ここに設定する値は${get_entry.output.link}となります。
hash_tags
PR文の最後に挿入するハッシュタグです。入力のところで定義したハッシュタグを設定したいので、${input.hash_tags}という値を設定します。

 

グラフの部分がだんだん出来上がってきました。

画像を取得してXにPostする

Pythonのノードを作成します。「Python」をクリックします。

 

「post_tips」と入力して、「追加」をクリックします。

 

下図のようなノードが出来上がります。そして赤枠の部分に、画像を取得してXにポストするコードを入力する必要があります。

 

以下が、画像を取得してXにポストするコードになります。詳細な処理の内容はコメントに記載しております。

from promptflow import tool
import tweepy
import requests
from io import BytesIO
from promptflow.connections import CustomConnection

# このアノテーションが付与されてる関数がPrompt flowから呼び出されます。
# このコード内で一つしか定義できません。
# 引数myconnは、作成した接続の情報がPrompt flowによって与えられます。
@tool
def post_tips(tips: str, myconn:CustomConnection) -> str:
    image_url = tips.strip().split('\n')[-1]
    tips_without_image_url = tips.replace(image_url, '')

    # 接続で定義した資格情報を取り出します
    consumer_key = myconn.X_CONSUMER_KEY
    consumer_secret = myconn.X_CONSUMER_SECRET
    access_token = myconn.X_ACCESS_TOKEN
    access_token_secret = myconn.X_ACCESS_TOKEN_SECRET

    # Xに接続します
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)

    api = tweepy.API(auth)
    client = tweepy.Client(
        consumer_key=consumer_key,
        consumer_secret=consumer_secret,
        access_token=access_token,
        access_token_secret=access_token_secret)

    # もしブログ記事の中に画像がなかった場合、つまり文末に「no image」という文字列があった場合には画像は添付しません。
    # 画像がある場合は、URLから画像を取得して、添付してポストします。
    if image_url != "no image":
        try:
            image_data = requests.get(image_url).content
            image_file = BytesIO(image_data)
            media = api.media_upload("image.jpg", file=image_file)
            client.create_tweet(text=tips_without_image_url, media_ids=[media.media_id])
        except Exception as e:
            client.create_tweet(text=tips_without_image_url)
    else:
        client.create_tweet(text=tips_without_image_url)

    # Xにポストした文章を返します。
    return tips

 

先のコードを入力します。そして、「入力値の検証と解析」をクリックしますと「入力」の「名前」の部分が「myconn」「tips」になります。これは、@toolデコレーターが付与された関数の引数名に置き換えられた形になります。

以下の通り設定します。

myconn
この引数には、「2. 接続(開発環境)の定義」で設定した接続の情報を設定します。x-auth-devを選択します。
tips
ノード「generate_tips」の出力を設定します。

出力

出力を定義します。今回は、テストまでは行わないので、出力された値は使いませんが、とりあえず何らかの値を出力しておくこととします。「+出力を追加する」をクリックします。

 

ノード「post_tips」で出力された値を、最終的な出力とします。以下のように設定します。

 

これで完了です。グラフもいい感じに出来上がりました。

 

下図の「実行」ボタンをクリックすると、フローを動かすことができます。

 

成功すると以下のように表示されます。

 

以下のようにXにポストされます。

6. 接続(本番環境)の定義

本番環境用の接続を定義します。「2. 接続(検証環境)の定義」で実施した手順と同様に本番環境用の接続を定義します。接続の名前は以下とします。

  • Azure OpenAI Serviceへの接続: aoai-auth-prd
  • Xへの接続: x-auth-prd

7. 本番環境へのデプロイ

本番環境にデプロイします。以下の画面の「デプロイ」をクリックします。

 

以下のような画面が表示されます。

 

以下の内容に基づいて必要事項を入力して、「次へ」をクリックします。以下の項目以外はデフォルトでOKです。

エンドポイント

「新規」「既存」のどちらかを選択します。今回は新しく作成するので「新規」になります。既存のエンドポイントを修正したい場合は「既存」を選択します。

エンドポイント名
エンドポイントを一意に識別する名称を入力します。
デプロイ名
このデプロイを一意に識別する名称を入力します。

仮想マシン

フローを動作させるVirtual Machineのインスタンスサイズを選択します。

インスタンス数
フローを動作させるVirtual Machineのインスタンス数を入力します。

 

以下のような画面が表示されます。

以下の内容に基づいて必要事項を入力して、「次へ」をクリックします。以下の項目以外はデフォルトでOKです。

認証の種類
「トークンベースの認証」の場合は、「Microsoft Entra IDによる認証」で説明したように、Microsoft Entra IDから払い出されるトークンによって認証することができます。「キーベースの認証」は「デプロイを指定してAPIを発行」で説明したような無期限のAPIキーによる認証です。今回は「キーベースによる認証」を選択します。

IDの種類

エンドポイントを発行すると、そのコンピューティングリソースを識別するマネージドIDが発行されます。そして、このマネージドIDがAzure Machine Learningワークスペースにアクセスできる権限を付与しなければなりません。それは後の手順で説明します。ここでは「システム割り当て」「ユーザー割り当て」を選択します。それぞれの内容についてはこちらのブログを参照いただくとして、今回は手順の簡略化のために「システム割り当て」を選択します。

 

以下のような画面が表示されます。重要なのは「環境」の部分で、本番環境で動作するランタイムも検証環境と同様に、あらかじめ作成した環境をもとにランタイムを作成する必要はあります。よって、「環境」は「カスタマイズされた環境を使用する」にチェックをして、「環境の作成」で作成した環境を選択します。そして「確認と作成」をクリックします。

 

確認画面が表示されますので、内容に問題なければ「確認」をクリックします。

 

次にエンドポイント用にデプロイされたコンピューティングリソース(Virtual Machine)から、Azure Machine Learningワークスペースのリソースにアクセスできるよう、マネージドIDに対して権限を付与します。今回作成したAzure Machine Learningワークスペースのリソースにアクセスして、「アクセス制御(IAM)」→「+追加」→「ロールの割り当ての追加」の順にクリックします。

「AzureMLデータ科学者」を選択して、「次へ」をクリックします。

「マネージドID」→「+メンバーの選択」の順にクリックします。「サブスクリプション」は先程デプロイ用のコンピューティングリソース(Virtual Machine) を作成したサブスクリプションを、「マネージドID」は「すべてのシステム割り当てマネージドID」を選択します。「選択」の部分では、先程のデプロイしたコンピューティングリソース(Virtual Machine) の名前を入力して表示されたマネージドIDを選択して、「選択」をクリックします。

「レビューと割り当て」をクリックします。

「レビューと割り当て」をクリックします。

これで完了です。では動作確認のためにエンドポイントにAPIを発行してみましょう。

Machine Learning Studio(https://ml.azure.com/)にアクセスして、画面左部メニューの「エンドポイント」をクリックします。すると、画面右部に先程作成したエンドポイントが表示されますので、それをクリックします。

「使用」のタブをクリックします。「デプロイ」「RESTエンドポイント」「主キー」の値をメモります。

先程メモした情報をもとにcurlコマンドで以下のHTTPリクエストを発行します。

$ curl -X POST "[先程メモしたRESTエンドポイント]" \
     -H "Authorization: Bearer [先程メモした主キー]" \
     -H "Content-Type: application/json" \
     -H "azureml-model-deployment: [先程メモしたデプロイ]" \
     -d '{"feed":"https://tech-lab.sios.jp/feed","hash_tags":"#Azure"}'

作成したフローの仕様・設定に基づいた応答が返ってきました。

Prompt flowを使うと、ローカルにVisual Studio Codeのような専用の開発環境の用意は不要でした。また、LLMにアクセスするためのライブラリ(LangChainなど)も使わなくてすみます。大量テストの実行も専用のテストツールを作らず、ノーコードで実現できますし、APIのエンドポイントを作成するときも、わざわざVirtual Machineを用意する必要もありません。全部Prompt flowが肩代わりしてくれます。

LLMのフローを作成するには、相当便利なサービスであることがおわかり頂けかと思います。

まとめ

いかがでしょうか?ちょっと長くななりましたが、Prompt flowの概要をわかりみ深く説明させていただきました。これからPrompt flow始めるための取っ掛かりとなれば幸いです。ではHappy Azure!!

 

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です