本記事は、前回の記事の続きとなります。もし良ければ、前回の記事もご覧ください。
今回は「Azere Cognitive SearchとAzure Form Recognizerの連携を試してみた ~②Azure Functions編~」ということで、Azure FunctionsからForm RecognizerのAPIを呼び出す部分について解説します。
フローでいうと③->④、⑥->⑦の部分となります。具体的にはAzure Functionsのコードを記述・デプロイして、外部からAPIを叩いて正しいレスポンスが得られることをゴールとします。
開発環境の用意
筆者のAzure Functionsの開発環境は下記の通りです。
- Ubuntu 20.04 on WSL 2
- VSCode
- Python 3.8
WSLを使うかどうかは好みですが、VSCodeはFunctionsのコードを開発する上でとても便利なので、入れておくことを推奨します。VSCodeの拡張機能としてAzure AccountとAzure Functionsも入れておきましょう。
コード開発
それではFuntionsのコードを書いていきます。といっても、Cgnitive SearchからForm Recognizerを呼び出すためのテンプレートがGitHubに公開されているので、git clone
して、必要な部分だけ切り出します。
$ git clone https://github.com/Azure-Samples/azure-search-power-skills.git
$ cp -r azure-search-power-skills/Vision/FormRecognizer ./
$ code FormRecognizer
コードのレポジトリはこちらです。
FormRecognizerのディレクトリをVSCodeで開くと、Functionsの拡張機能が勝手にフォルダを認識し、Yesをクリックすると開発に必要な設定(.venvや.vscode)を入れてくれます。RuntimeはPythonを選択しましょう。
こちらのコードですが、GitHubにもかいてある通りFormRecognizerのエンドポイントとキーを環境変数として設定する必要があります。
This function requires a FORMS_RECOGNIZER_ENDPOINT and a FORMS_RECOGNIZER_KEY settings set to a valid Azure Forms Recognizer API key and to your custom Form Recognizer 2.1-preview endpoint. If running locally, this can be set in your project's local environment variables. This ensures your key won't be accidentally checked in with your code. If running in an Azure function, this can be set in the application settings.
ルートディレクトリ(FormRecognizer/
)にlocal.settings.json
を作成することで、Functionsのローカル実行時環境変数等の設定を読み取ってくれます。下記の通りFORM_RECOGNIZER_ENDPOINT
とFORM_RECOGNIZER_KEY
を記述します。
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"FORM_RECOGNIZER_ENDPOINT": "https://formrecognizer-test-1234.cognitiveservices.azure.com/",
"FORM_RECOGNIZER_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
FORM_RECOGNIZER_ENDPOINT
とFORM_RECOGNIZER_KEY
はAzure Portalから取得します。
Azure Functions Core Tools のインストール
次にFunctionsのCLIをインストールします。OS毎にインストールの方法が異なるので、こちらの公式ドキュメントをご参照ください。
Ubuntuの場合は下記のコマンドを実行します。
$ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
$ sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
$ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'
$ sudo apt-get update
$ sudo apt-get install azure-functions-core-tools-4
ローカル実行
一通り実行の準備が整ったので、ローカルでコードを実行してみます。
Pythonの仮想環境に入って、azure-ai-formrecognizer
のバージョンを上書きします。
$ source .venv/bin/activate
$ func start
Found Python version 3.8.10 (python3).
Azure Functions Core Tools
Core Tools Version: 4.0.4653 Commit hash: N/A (64-bit)
Function Runtime Version: 4.6.1.18388
Functions:
AnalyzeDocument: [GET,POST] http://localhost:7071/api/AnalyzeDocument
For detailed output, run func with --verbose flag.
[2022-08-25T06:38:55.703Z] Worker process started and initialized.
[2022-08-25T06:39:00.230Z] Host lock lease acquired by instance ID '0000000000000000000000007EA5C856'.
上記のような出力が表示されれば、正しく動作しています。
コードテスト
次にコードをテストしましょう。GitHubのドキュメントによると、APIの入力フォーマットは下記の通りになります。
{
"values": [
{
"recordId": "record1",
"data": {
"model": "prebuilt-invoice",
"formUrl": "https://github.com/Azure-Samples/azure-search-power-skills/raw/master/SampleData/Invoice_4.pdf",
"formSasToken": "?st=sasTokenThatWillBeGeneratedByCognitiveSearch"
}
}
]
}
今回モデルはカスタムモデルを使うので、modelの部分は前回作成したtrain-receipt-model-v2
を使います。
formUrl
とformSasToken
はAzure Data Lake StorageにアップロードしたpdfファイルからSAS生成で取得しましょう。
入力データとしてinput.json
をルート以下に作りました。
{
"values": [
{
"recordId": "record1",
"data": {
"model": "train-receipt-model-v2",
"formUrl": "https://sampledata234.blob.core.windows.net/data/pdf/train-receipt/%E6%96%B0%E5%B9%B9%E7%B7%9A_220728.pdf",
"formSasToken": "?sp=r&st=2022-08-25T06:53:43Z&se=2023-07-13T14:53:43Z&spr=https&sv=2021-06-08&sr=b&sig=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
]
}
formSasToken
の先頭には必ず"?"を付けましょう。PortalからBLOB SAS トークンをそのままコピーすると先頭に"?"がついていませんのでご注意を。
このJSONファイルを入力に、APIを叩いてみましょう。
$ curl -X POST -H "Content-Type: application/json" -d '@input.json' http://localhost:7071/api/AnalyzeDocument
レスポンスがこちらです。
{
"values": [
{
"recordId": "record1",
"data": {
"documents": [
{
"name": "山田太郎",
"card_company": "JCB",
"from": "品川",
"to": "新大阪"
}
],
"tables": [
...
kvpのフィールドを見ると、前回取得できていた日時とか合計金額が消えていることが分かります。Form Recognizerが返すレスポンスを見てみると日時や合計金額は正しく取得できていました。実際にはFunctionsの出力結果に変換する際、String型のフィールドのみkvpに追加する、というコードになっていました。
def get_fields(result):
fields = []
for idx, doc in enumerate(result.documents):
kvp = {}
for kv_pair in doc.fields.items():
if kv_pair[1].value_type == "string": #この部分
kvp[kv_pair[0]] = kv_pair[1].content
elif kv_pair[1].value_type == "list":
line_items = []
items = {}
for rows in kv_pair[1].value:
for row in rows.value.items():
items[row[0]] = row[1].content
line_items.append(items)
kvp[kv_pair[0]] = line_items
fields.append(kvp)
return fields
なので、下記の通り、思い切って全フィールドをstring型に変換してkvpに追加する、というコードに書き換えました。
def get_fields(result):
fields = []
for idx, doc in enumerate(result.documents):
kvp = {}
for kv_pair in doc.fields.items():
kvp[kv_pair[0]] = str(kv_pair[1].content) # この部分
fields.append(kvp)
return fields
もう一度APIを叩くとすべてのフィールドが取得できるようになっていました。
{
"values": [
{
"recordId": "record1",
"data": {
"documents": [
{
"name": "山田太郎",
"reservation_number": "2010",
"total_amount": "¥14,720",
"date_of_purchase": "2022年7月27日",
"date_of_departure": "7月28日",
"card_company": "JCB",
"from": "品川",
"to": "新大阪"
}
],
...
今回は簡単なコード修正にとどめておきますが、Functionsでのレスポンスがほとんどそのままインデックスになるので、このフェーズで変数型のチェックや値のチェック、必要に応じてデータ加工を行うべきです。今回で言うと、"total_amount"の"¥"や","を削除する、"date_of_departure"を"yyyymmdd"に変換する、等です。
コードデプロイ
Azure Accountsの拡張機能からAzureにSign Inすると下記のようにサブスクリプションのリソースが一覧で確認できるようになります。Function Appを右クリックしFuntion Appのリソースを作成しましょう。リソース名を適当に設定し、ランタイムはPythonを選択します。リソースがが作成できたことを確認しましょう。
次にWORKSPACEを見ると、先ほどローカルで実行したFunctionsのプロジェクトが確認できます。右上のアイコンからローカルプロジェクトをAzureにデプロイします。リソースは先ほど作成したものを選択します。
Azure Portal側からもデプロイできていることが確認できます。
次に環境変数を設定します。[構成]->[アプリケーション設定]->[新しいアプリケーション設定]をクリックします。
先ほどローカルで設定した環境変数FORM_RECOGNIZER_ENDPOINT
とFORM_RECOGNIZER_KEY
を同様に設定しましょう。
Functionsのテスト
最後に[コードとテスト]->[テストと実行]をクリックし、ボディに先ほど作成したinput.json
の内容をコピペし、実行をクリックします。
レスポンスが返ってくることを確認できました。
まとめ
今回はCognitive SearchとForm Recognizerの連携部分でAzure Functionsについて解説しました。少しコードを読み解いて改修する部分はありますが、テンプレートが用意されているのでそれほど手間はかからないと思います。次回はCognitive Searchの設定部分について解説予定です。