PowerShellコラム:REST API経由でCognitive Services + Azure OpenAIを実行してみた 3

Microsoft

2024.01.05

はじめに

本稿は「PowerShellコラム:REST API経由でCognitive Services + Azure OpenAIを実行してみた 2」の続きとなります。

前回はAzure OpenAIのリソースを作成し、ファイアウォールの設定を行いました。
続いてChatGPTをデプロイし、そのエンドポイントとAPIキーを取得しました。

今回はこれを利用してPowerShellからChatGPTを呼び出します。

サンプル(ダウンロード)

コードでは「PowerShell コラム:Cognitive Servicesでクリップボードの画像のテキストを日本語に翻訳する」で使用したCognitive Servicesリソース(Computer Vision Read API)を使用しているので、実際に動かしてみたい方はこちらの記事もご参照ください。

サンプル(ZIP:18.0KB)

ファイル 概要
PromptoSetting.xml ChatGPTの SystemMessage と、応答及びその例を指定するXML
Readme.txt 簡単な使い方
StartWithProxy.bat
StartWithProxy.ps1
認証プロキシ環境から Translate.ps1 を実行するためのバッチとスクリプト
Translate.bat
Translate.ps1
スクリプトを呼び出すバッチと、スクリプト本体
翻訳サンプル画像 例に使用した画像と、テストに使用した画像

OpenAIを使用するためのサンプルコード

エンドポイントとAPIキーの指定

Azure OpenAI Studioの「チャット」の「サンプルコード」から、エンドポイントとキーを指定します。

# ChatGptのAPIキーとエンドポイント
$OpenAiApiKey   = ""
$OpenAiEndpoint = "<エンドポイント>"
$OpenAiRequestHeaders = @{
    "Api-Key" = $OpenAiApiKey
    "Content-Type" = "application/json;charset=utf-8"
}

リクエストのテンプレートを用意する

リクエストのJSONをヒアストリングで用意しました。
今回はREST APIの結果を __CONTENT_USER__ の位置に入れて送るのですが、これ自体がJSONなのでエスケープする必要があります。

[regex]::escape() のようなエスケープがJSON用になさそうだったので、ConvertFrom-Json で一度PSCustomObjectに変換しました。

# max_tokens : 応答の最大トークン数。システムプロンプトや例と合わせて合計が4096を超えないようにする。
# temperture: 2に近いほど創造性豊かに、ランダム性が増える。既定は1。
# top_p : 選択肢に含める範囲。1 で全体、 0.1 で上位10%。既定は1。
$OpenAiRequestBodyTemplate = @'
{
    "messages":[
        {
            "role": "system",
            "content": "__SYSTEM_MESSAGE__"
        },
        {
            "role": "user",
            "content": "__EXAMPLE_USER__"
        },
        {
            "role": "assistant",
            "content": "__EXAMPLE_ASSISTANT__"
        },
        {
            "role": "user",
            "content": "__CONTENT_USER__"
        }
    ],
    "temperature": 0.5,
    "top_p": 0.95,
    "frequency_penalty": 0,
    "presence_penalty": 0,
    "max_tokens": 2000,
    "stop": null
}
'@
 
…中略…
 
# 値を入れるときにJSONと競合する文字がエスケープされるよう、一度PSCustomObjectを介する
$OpenAIRequestBodyObject = ConvertFrom-Json -InputObject $OpenAiRequestBodyTemplate

リクエスト(JSON)を作る

XMLファイルとRead APIの結果をそれぞれ作成したPSCustomObjectに値を入れます。
最後に「ConvertTo-Json」を実行することでJSON文字列に戻すことで、適切にエスケープされた文字列が作成されます。

(…略…)
# ChatGPTによる変換
# ReadAPI のJSON の一部を入力に与える。
# 「"boundingBox":  "706 630 780 632 780 678 706 677"」のように正しいJSONではないが,AIはうまく判定してくれる\
$analyzeResult_line_json = $ReadStatus.analyzeResult.readresults.lines | Select-Object BoundingBox,Text | ConvertTo-Json -Depth 1
 
$PromptoXML = [xml](Get-Content -LiteralPath $PromptoSettingPath -Encoding UTF8)
# システムルール、例、ReadAPI空の応答をそれぞれ格納。XMLの参照はPowershellを使っていても大文字小文字区別するので注意。
($OpenAiRequestBodyObject.messages | Where-Object {$_.content -eq "__SYSTEM_MESSAGE__"}).content = $PromptoXML.data.systemmessage
($OpenAiRequestBodyObject.messages | Where-Object {$_.content -eq "__EXAMPLE_USER__"}).content = $PromptoXML.data.user
($OpenAiRequestBodyObject.messages | Where-Object {$_.content -eq "__EXAMPLE_ASSISTANT__"}).content = $PromptoXML.data.assistant
($OpenAiRequestBodyObject.messages | Where-Object {$_.content -eq "__CONTENT_USER__"}).content = $analyzeResult_line_json
$OpenAiRequestBody = $OpenAiRequestBodyObject | ConvertTo-Json

ChatGPTにリクエストを送って結果を受け取る

Invoke-WebRequestでPOSTします。
Azure OpenAIから受け取った結果を「Content」プロパティなどで受け取ると文字化けしてしまいました。
RawContentStreamから配列を作成してエンコードすることで読み取ることができます。

$OpenAiResponse    = Invoke-WebRequest -Method Post `
-Uri $OpenAiEndpoint `
-Headers $OpenAiRequestHeaders `
-Body ([System.Text.Encoding]::UTF8.GetBytes( $OpenAiRequestBody ) )
        
# OpenAI からのResponseに文字コードの情報がないため、RawContentStream から変換する
$OpenAiResponseObj = $null
$rawContentStreamArray = $OpenAiResponse.RawContentStream.ToARray()
$OpenAiResponseObj = ([Text.Encoding]::UTF8.GetString($rawContentStreamArray)) | ConvertFrom-Json
 
Write-Host "■ 翻訳結果(OpenAI ChatGPT 3.5turbo)"
Write-Host $OpenAiResponseObj.choices.message.content

必要に応じてSystemMessageや例などのパラメータを変更する

設定ファイルは別にしているので、このファイルを書き換えてスクリプトを続行することですぐに結果を確認することができます。
詳しく書くことで結果の精度が上がる傾向はありますが、4096というトークンの上限があるのでうまく調整する必要があります。
英語のほうがトークンは節約できるらしいので、ある程度の方向性が定まったら英語に翻訳するほうが良いかもしれませんね。

SystemMessage

アシスタントのふるまいを定義します。今回は無機質なので人格などはほとんど入れていません。

SystemMessage

Userの入力とAssistant の応答を一つ、例として入れています。入力と期待値ですね。
これが毎回送られるので、応答に使えるトークンが足りなくなりました。実際業務で使う際は、前処理とかが重要そう。

Userの入力例

Assistantの応答例

おわりに

今回はOpenAIのChatGPTを使って画像から翻訳をしてみました。

段組みをうまく処理してくれるとよかったのですが、間隔が狭かったり左右をつなげても意味が通るような文章が前半にあったりするとなかなかうまくいかないことが分かりました。

プロンプトの調整はトークンの上限もあり、なかなか奥深いですね。

いずれは入力と期待値、初期のシステムメッセージを用意して「この期待値になるまでシステムメッセージを改善せよ」ってPDCAサイクルをAI自身に回してもらいたいです。

  • 文中の商品名、会社名、団体名は、一般に各社の商標または登録商標です。

PowerShellコラム:REST API経由でCognitive Services + Azure OpenAIを実行してみた 3