Azure OpenAI設定

EXCEL

VBA/PowerShellからAzure OpenAI APIを極める:HTTP/JSON層からの徹底解剖

導入(問題設定)

ExcelマクロやPowerShellスクリプトが根付く現場において、業務効率化は永遠の課題です。定型的なテキスト処理、データ分類、要約、あるいは自由形式の質問応答など、これまで人の手で行われてきた作業に、いまや生成AIの力が期待されています。しかし、既存の業務システムや社内規定の制約により、最新のPythonライブラリや高度なSDKの導入が難しいケースは少なくありません。

本記事では、こうした環境下でもAzure OpenAI Serviceの強力なテキスト生成能力を享受できるよう、VBAおよびPowerShellから直接REST APIを呼び出す方法を、HTTP/JSONの低レベルな視点から徹底的に解説します。単なるHowToに留まらず、内部動作、境界条件、そして開発者が陥りがちな落とし穴まで深掘りすることで、堅牢で実践的なソリューション構築のための知見を提供します。

理論の要点

Azure OpenAI ServiceのREST APIは、HTTPプロトコル上でJSON形式のデータをやり取りすることで、AIモデルとの対話を可能にします。その核心を理解するために、以下の要素を押さえます。

1. エンドポイントと認証

Azure OpenAI ServiceのエンドポイントURLは、Azureリソース名、デプロイ名、およびAPIバージョンによって構成されます。 https://{your-resource-name}.openai.azure.com/openai/deployments/{your-deployment-name}/chat/completions?api-version={api-version}

  • {your-resource-name}: Azure OpenAI Serviceリソースのホスト名。
  • {your-deployment-name}: デプロイしたモデル(例: gpt-35-turbo)の名前。
  • {api-version}: 使用するAPIのバージョン(例: 2023-05-15)。

認証には、APIキー認証(推奨)を使用します。HTTPリクエストヘッダに api-key: YOUR_API_KEY を付与します。Authorization: Bearer YOUR_API_KEY も利用可能ですが、Azure OpenAIでは api-key ヘッダが慣例です。

2. リクエストとレスポンスの構造

モデルとの対話は、JSON形式で構成されたリクエストボディをPOSTすることで行われます。

リクエストボディの主要プロパティ

プロパティ名 必須/任意 説明
messages Array 必須 モデルへのプロンプト履歴。各要素は role (string) と content (string) を持つオブジェクト。rolesystem (全体の振る舞い), user (ユーザーの質問), assistant (モデルの過去の回答) があります。
model String 任意 使用するモデルのID。Azure OpenAIでは{your-deployment-name}で指定されるため、通常は空か、デプロイ名と一致させる。OpenAI APIのmodelとは挙動が異なる場合があるため、Azure OpenAIではエンドポイントURLのデプロイ名に依存するのが安全です。ただし、一部のAPIバージョンではボディ内modelが必須の場合があるため、公式ドキュメントのAPIバージョンごとの仕様を確認すること。
temperature Number 任意 出力のランダム性(0.0~2.0)。0.0に近いほど決定論的、2.0に近いほど創造的になります。デフォルトは1.0。
max_tokens Integer 任意 生成される応答の最大トークン数。入力プロンプトと出力の両方でモデルのコンテキストウィンドウをオーバーしないよう注意が必要です。
top_p Number 任意 サンプリング戦略。temperature とは排他的に使用されます。累積確率 top_p まで含まれるトークン候補からサンプリングします。
frequency_penalty Number 任意 生成されるトークンが、すでにプロンプトや出力に存在するトークンである場合にペナルティを課す度合い(-2.0~2.0)。値が大きいほど繰り返しを減らします。
presence_penalty Number 任意 生成されるトークンが、プロンプトや出力に存在するトークンに関わらず、新しいトピックを導入する度合い(-2.0~2.0)。値が大きいほど新しいトピックを導入しやすくなります。
stream Boolean 任意 レスポンスをストリーミング形式で受け取るかどうか。trueの場合、応答が少しずつ返されます。リアルタイムアプリケーション向け。今回は非ストリーミングで解説。

レスポンスボディの主要プロパティ

プロパティ名 説明
id String チャット補完セッションのユニークID。
object String オブジェクトのタイプ(例: chat.completion)。
created Integer タイムスタンプ(Unix時間)。
model String 使用されたモデルのID。
choices Array 生成された応答の配列。各要素は message (オブジェクト), finish_reason (String), index (Integer) を持つ。
message Object role (String) と content (String) を持つ。モデルの回答が content に含まれます。
usage Object トークンの使用状況。prompt_tokens, completion_tokens, total_tokens を含む。

3. HTTPクライアントの選択

  • VBA: WinHttp.WinHttpRequest.5.1 (推奨) または MSXML2.XMLHTTP60 を使用します。これらはCOMオブジェクトであり、特別な参照設定なしで利用できる場合が多いですが、OSやOfficeのバージョンによっては参照設定が必要な場合があります。
  • PowerShell: Invoke-RestMethod コマンドレットを使用します。これはHTTPリクエストの送信とJSONレスポンスのオブジェクト化を自動で行ってくれる、非常に強力なコマンドレットです。

4. JSONのパース

  • VBA: WinHttpRequest が返すすべてのレスポンスは生の文字列です。JSON文字列をVBAのオブジェクトとして扱うには、自前でパースロジックを実装するか、外部ライブラリ(JSONConverter.basなど)を利用する必要があります。本記事では外部ライブラリなしで、基礎的なパース方法を解説します。
  • PowerShell: Invoke-RestMethod は、Content-Type: application/json ヘッダを持つレスポンスを自動的にPowerShellのオブジェクト(PSCustomObject)に変換してくれます。このため、JSONパースの労力はVBAに比べて格段に少ないです。

Azure OpenAI API 呼び出しフロー

graph TD
    A["開始"] --> B{"環境変数/設定からAPIキー, エンドポイントURL取得"};
    B --> C{"プロンプト構築 (messages配列)"};
    C --> D{"JSONリクエストボディ生成"};
    D --> E{"HTTPクライアント初期化 (VBA:WinHttpRequest / PS:Invoke-RestMethod)"};
    E --> F{"HTTPヘッダ設定 (Content-Type, api-key)"};
    F --> G{"POSTリクエスト送信"};
    G --> H{"HTTPレスポンス受信"};
    H --> I{"ステータスコード確認"};
    I -- 200 OK --> J{"JSONレスポンスパース"};
    I -- 4xx/5xxエラー --> K{"エラーハンドリング & リトライ"};
    J --> L{"生成されたテキスト抽出"};
    L --> M["結果表示/利用"];
    K --> M;
    M --> N["終了"];

実装(最小→堅牢化)

ここでは、VBAとPowerShellの両方で実装例を示します。環境変数は設定済みと仮定します。

前提条件

  • Azure OpenAI Serviceリソースがデプロイ済みであること。
  • gpt-3.5-turbo または gpt-4 モデルが your-deployment-name としてデプロイされていること。
  • APIキーとエンドポイントURLが取得済みであること。

VBA (Microsoft Excel VBA)

最小実装

VBAでHTTPリクエストを送信し、JSONレスポンスから必要な情報を抽出する最小限のコードです。エラーハンドリングは含まれていません。

Attribute VB_Name = "Module1"
Option Explicit

Private Const AZURE_OPENAI_ENDPOINT As String = "https://{your-resource-name}.openai.azure.com/openai/deployments/{your-deployment-name}/chat/completions?api-version=2023-05-15"
Private Const AZURE_OPENAI_API_KEY As String = "YOUR_API_KEY" ' ⚠️ 本番環境では直接記述せず、より安全な方法で管理してください

Public Sub CallAzureOpenAIChatMinimal()
    Dim req As Object ' WinHttpRequest.5.1
    Dim sURL As String
    Dim sAPIKey As String
    Dim sPrompt As String
    Dim sRequestBody As String
    Dim sResponse As String
    Dim sGeneratedText As String

    ' --- 設定値 ---
    sURL = Replace(AZURE_OPENAI_ENDPOINT, "{your-resource-name}", "YOUR_RESOURCE_NAME") ' 例: my-aoai-resource
    sURL = Replace(sURL, "{your-deployment-name}", "YOUR_DEPLOYMENT_NAME") ' 例: gpt-35-turbo-deploy
    sAPIKey = AZURE_OPENAI_API_KEY
    sPrompt = "VBAでAzure OpenAI APIを呼び出す方法を簡潔に教えてください。"

    ' --- リクエストボディの構築 ---
    sRequestBody = "{""messages"": [{""role"": ""system"", ""content"": ""You are a helpful assistant.""}," & _
                   "{""role"": ""user"", ""content"": """ & EscapeJsonString(sPrompt) & """}]," & _
                   """temperature"": 0.7," & _
                   """max_tokens"": 50}"

    Set req = CreateObject("WinHttp.WinHttpRequest.5.1")

    With req
        .Open "POST", sURL, False ' 同期処理
        .SetRequestHeader "Content-Type", "application/json"
        .SetRequestHeader "api-key", sAPIKey
        .Send sRequestBody

        sResponse = .ResponseText
    End With

    ' --- レスポンスのパース (簡易版) ---
    ' "content": "..." の値を取得
    Dim iStart As Long, iEnd As Long
    iStart = InStr(sResponse, """content"": """)
    If iStart > 0 Then
        iStart = iStart + Len("""content"": """)
        iEnd = InStr(iStart, sResponse, """")
        If iEnd > 0 Then
            sGeneratedText = Mid(sResponse, iStart, iEnd - iStart)
            Debug.Print "生成されたテキスト: " & sGeneratedText
        Else
            Debug.Print "エラー: contentの終わりが見つかりません。"
            Debug.Print "レスポンス: " & sResponse
        End If
    Else
        Debug.Print "エラー: contentが見つかりません。"
        Debug.Print "レスポンス: " & sResponse
    End If

    Set req = Nothing
End Sub

' JSON文字列のエスケープ処理
' ダブルクォーテーションを \" に変換
Private Function EscapeJsonString(ByVal inputString As String) As String
    EscapeJsonString = Replace(inputString, """", "\""")
    ' 他にもエスケープすべき文字(改行、タブなど)があれば追加
End Function

VBAの落とし穴と境界条件: * JSONパースの困難さ: 上記のInStrベースのパースは、単純なcontent抽出には使えますが、ネストが深いJSONや、エスケープ文字を含むJSONには対応できません。正規表現 (VBScript.RegExp) を使うか、専用のJSONパーサークラスを自作する必要があります。 * 同期処理のブロック: .Open "POST", sURL, False は同期処理です。API応答が遅い場合、Excelがフリーズします。非同期 (True) で処理し、OnReadyStateChangeイベントをハンドリングする堅牢化が必要です。 * 64bit環境におけるPtrSafe / LongPtr: WinHttp.WinHttpRequest はCOMオブジェクトであり、VBAからCOMを呼び出す際にDeclareステートメントは通常使用しません。そのため、PtrSafeLongPtrは直接関係しません。しかし、もしWinAPIを直接呼び出してHTTP通信を行う場合は、64bit環境でポインタのサイズが変わるため、これらのキーワードが必須となります。COMオブジェクトを介する場合は、VBAがCOMインターフェースを介してDLLを呼び出すため、VBA開発者が直接PtrSafe等を意識する必要はありません。

堅牢化

上記の最小実装に、エラーハンドリング、タイムアウト設定、そして簡素ながらももう少し実用的なJSONパースを追加します。非同期処理はイベントドリブンな構造になるため、ここでは省略し、同期処理の堅牢化に焦点を当てます。

Attribute VB_Name = "Module1"
Option Explicit

Private Const AZURE_OPENAI_ENDPOINT As String = "https://{your-resource-name}.openai.azure.com/openai/deployments/{your-deployment-name}/chat/completions?api-version=2023-05-15"
Private Const AZURE_OPENAI_API_KEY As String = "YOUR_API_KEY" ' ⚠️ 本番環境では直接記述せず、より安全な方法で管理してください

' WinHttpRequestの定数 (WinHttpRequest.dllを直接参照しない場合)
Private Const WHR_SETREQUESTHEADER As Long = 0
Private Const WHR_OPTION_ENABLE_REDIRECTS As Long = 6
Private Const WHR_OPTION_TIMEOUT As Long = 0 ' タイムアウト設定用オプション

Public Sub CallAzureOpenAIChatRobust()
    Dim req As Object ' WinHttpRequest.5.1
    Dim sURL As String
    Dim sAPIKey As String
    Dim sPrompt As String
    Dim sRequestBody As String
    Dim sResponse As String
    Dim sGeneratedText As String
    Dim lAttempts As Long
    Const MAX_RETRIES As Long = 3
    Const RETRY_DELAY_SEC As Long = 2 ' 初期リトライ遅延秒数

    ' --- 設定値 ---
    sURL = Replace(AZURE_OPENAI_ENDPOINT, "{your-resource-name}", "YOUR_RESOURCE_NAME")
    sURL = Replace(sURL, "{your-deployment-name}", "YOUR_DEPLOYMENT_NAME")
    sAPIKey = AZURE_OPENAI_API_KEY
    sPrompt = "VBAでAzure OpenAI APIを呼び出す方法を簡潔に教えてください。エラーハンドリングとタイムアウトについても触れてください。"

    ' --- リクエストボディの構築 ---
    sRequestBody = "{""messages"": [{""role"": ""system"", ""content"": ""You are a helpful assistant.""}," & _
                   "{""role"": ""user"", ""content"": """ & EscapeJsonString(sPrompt) & """}]," & _
                   """temperature"": 0.7," & _
                   """max_tokens"": 100}" ' トークン数を増やして少し長めの応答を期待

    Set req = CreateObject("WinHttp.WinHttpRequest.5.1")

    For lAttempts = 1 To MAX_RETRIES
        On Error GoTo ErrorHandler ' エラーハンドラ設定

        With req
            .Option(WHR_OPTION_TIMEOUT) = 30000 ' 30秒のタイムアウト (ミリ秒指定)
            .Open "POST", sURL, False ' 同期処理
            .SetRequestHeader "Content-Type", "application/json"
            .SetRequestHeader "api-key", sAPIKey
            .Send sRequestBody

            sResponse = .ResponseText

            If .Status >= 200 And .Status < 300 Then
                ' 成功: レスポンスのパース
                sGeneratedText = ParseJsonContent(sResponse)
                If Len(sGeneratedText) > 0 Then
                    Debug.Print "生成されたテキスト (試行 " & lAttempts & "): " & sGeneratedText
                    Exit For ' 成功したらループを抜ける
                Else
                    Debug.Print "警告: レスポンスからテキストを抽出できませんでした。ステータス: " & .Status & ", レスポンス: " & sResponse
                    GoTo NextAttempt ' パース失敗は次の試行へ
                End If
            Else
                Debug.Print "HTTPエラー発生 (試行 " & lAttempts & "): " & .Status & " " & .StatusText
                Debug.Print "レスポンス: " & sResponse
                GoTo NextAttempt ' エラー発生は次の試行へ
            End If
        End With

NextAttempt:
        If lAttempts < MAX_RETRIES Then
            Debug.Print "リトライします。待機時間: " & (RETRY_DELAY_SEC * (2 ^ (lAttempts - 1))) & "秒..."
            Application.Wait Now + TimeSerial(0, 0, RETRY_DELAY_SEC * (2 ^ (lAttempts - 1))) ' 指数バックオフ
        Else
            Debug.Print "最大リトライ回数を超えました。"
            sGeneratedText = "エラー: AI応答を取得できませんでした。"
        End If
    Next lAttempts

CleanUp:
    Set req = Nothing
    Exit Sub

ErrorHandler:
    Debug.Print "実行時エラー: " & Err.Number & " - " & Err.Description
    ' ネットワークエラーやタイムアウトの場合など
    If Err.Number = -2147012894 Then ' WinHTTP Error 12002: ERROR_WINHTTP_TIMEOUT
        Debug.Print "タイムアウトが発生しました。"
    End If
    Resume NextAttempt ' エラーが発生しても次の試行へ
End Sub

' JSON文字列のエスケープ処理
Private Function EscapeJsonString(ByVal inputString As String) As String
    ' JSON文字列に含まれる可能性のある特殊文字をエスケープ
    ' 必要に応じて、\n, \r, \t, \f, \b なども追加
    EscapeJsonString = Replace(inputString, "\", "\\")
    EscapeJsonString = Replace(EscapeJsonString, """", "\""")
End Function

' 簡易JSONパース関数 (正規表現を使用)
' JSONレスポンスから "content" フィールドの値を抽出
Private Function ParseJsonContent(ByVal jsonString As String) As String
    Dim regEx As Object
    Dim matches As Object

    Set regEx = CreateObject("VBScript.RegExp")
    With regEx
        .Pattern = """content"":\s*""((?:[^""]|(?<=\\)"")*)""" ' エスケープされたダブルクォーテーションも考慮
        .Global = False
        .IgnoreCase = False
    End With

    Set matches = regEx.Execute(jsonString)

    If matches.Count > 0 Then
        ' 最初のマッチの、1つ目のキャプチャグループがcontentの値
        ParseJsonContent = Replace(matches(0).SubMatches(0), "\""", """") ' エスケープされたダブルクォーテーションを元に戻す
    Else
        ParseJsonContent = ""
    End If

    Set regEx = Nothing
End Function

堅牢化のポイント: * エラーハンドリング: On Error GoTo とエラーコードによる分岐。特にWinHTTPのエラーコードは把握しておくべきです。 * リトライロジック: ネットワークの一時的な問題やAPIのレート制限に対応するため、指数バックオフを伴うリトライ処理を実装しました。 * タイムアウト設定: req.Option(WHR_OPTION_TIMEOUT) = 30000 でタイムアウトを明示的に設定。 * JSONパースの強化: VBScript.RegExp オブジェクトを使用して、"content": "..." の値をより確実に抽出します。エスケープされたダブルクォーテーションも考慮に入れています。これにより、より柔軟なJSON構造に対応できますが、汎用的なJSONパーサーではありません。本格的なパースには外部ライブラリ (JsonConverter.basなど) の導入が最善策です。

PowerShell

最小実装

Invoke-RestMethodは非常に強力で、HTTPリクエストの送信、JSONの自動パース、エラーハンドリングの基本的な部分を一度に担ってくれます。

# Azure OpenAI設定
$resourceName = "YOUR_RESOURCE_NAME" # 例: my-aoai-resource
$deploymentName = "YOUR_DEPLOYMENT_NAME" # 例: gpt-35-turbo-deploy
$apiVersion = "2023-05-15"
$apiKey = "YOUR_API_KEY" # ⚠️ 本番環境では直接記述せず、より安全な方法で管理してください

# エンドポイントURL構築
$endpoint = "https://$($resourceName).openai.azure.com/openai/deployments/$($deploymentName)/chat/completions?api-version=$($apiVersion)"

# プロンプト
$prompt = "PowerShellでAzure OpenAI APIを呼び出す方法を簡潔に教えてください。"

# リクエストボディ
$body = @{
    messages = @(
        @{ role = "system"; content = "You are a helpful assistant." },
        @{ role = "user"; content = $prompt }
    )
    temperature = 0.7
    max_tokens = 50
} | ConvertTo-Json -Compress # JSON文字列に変換

# HTTPヘッダ
$headers = @{
    "Content-Type" = "application/json"
    "api-key" = $apiKey
}

# API呼び出し
try {
    $response = Invoke-RestMethod -Uri $endpoint -Method Post -Headers $headers -Body $body

    # レスポンスからテキスト抽出
    $generatedText = $response.choices[0].message.content
    Write-Host "生成されたテキスト: $($generatedText)"

}
catch {
    Write-Error "API呼び出し中にエラーが発生しました: $($_.Exception.Message)"
    if ($_.Exception.Response) {
        $errorResponse = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($errorResponse)
        $responseContent = $reader.ReadToEnd()
        Write-Error "詳細レスポンス: $($responseContent)"
    }
}

堅牢化

Invoke-RestMethodをさらに堅牢にするため、詳細なエラーハンドリング、リトライロジック、セキュアなAPIキー管理(ここでは解説のみ)を追加します。

# Azure OpenAI設定
$resourceName = "YOUR_RESOURCE_NAME"
$deploymentName = "YOUR_DEPLOYMENT_NAME"
$apiVersion = "2023-05-15"
# APIキーは、以下のようにSecureStringとして保存するか、Azure Key Vaultなどから取得するべきです。
# $apiKey = Read-Host -Prompt "Azure OpenAI API Key" -AsSecureString
# $apiKey = (ConvertTo-SecureString "YOUR_API_KEY" -AsPlainText -Force) # テスト目的のみ
$apiKey = "YOUR_API_KEY" # ⚠️ 本番環境では直接記述せず、より安全な方法で管理してください

# エンドポイントURL構築
$endpoint = "https://$($resourceName).openai.azure.com/openai/deployments/$($deploymentName)/chat/completions?api-version=$($apiVersion)"

# プロンプト
$prompt = "PowerShellでAzure OpenAI APIを呼び出す方法を簡潔に教えてください。エラーハンドリングとリトライについても触れてください。"

# リクエストボディ
$body = @{
    messages = @(
        @{ role = "system"; content = "You are a helpful assistant." },
        @{ role = "user"; content = $prompt }
    )
    temperature = 0.7
    max_tokens = 100
    # stream = $false # 必要に応じて
} | ConvertTo-Json -Compress

# HTTPヘッダ
$headers = @{
    "Content-Type" = "application/json"
    "api-key" = $apiKey # SecureStringを直接渡すことはできないため、PlainTextにするか、Invoke-WebRequestの-Credentialを使うか検討
}

# リトライ設定
$maxRetries = 3
$initialDelaySeconds = 2
$generatedText = "エラー: AI応答を取得できませんでした。" # デフォルト値

for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
    Write-Host "API呼び出し試行: $($attempt)/$($maxRetries)" -ForegroundColor Cyan
    try {
        $response = Invoke-RestMethod -Uri $endpoint -Method Post -Headers $headers -Body $body -TimeoutSec 30 -ErrorAction Stop -StatusCodeVariable 'statusCode'

        if ($statusCode -ge 200 -and $statusCode -lt 300) {
            # 成功時の処理
            $generatedText = $response.choices[0].message.content
            Write-Host "生成されたテキスト (試行 $($attempt)): $($generatedText)" -ForegroundColor Green
            break # 成功したらループを抜ける
        } else {
            Write-Warning "HTTPステータスコードエラー (試行 $($attempt)): $($statusCode)"
            # Invoke-RestMethodは通常、非2xxステータスで例外を投げるため、このブロックは保険的
        }
    }
    catch {
        $errorMessage = $_.Exception.Message
        $httpStatusCode = if ($_.Exception.Response) { $_.Exception.Response.StatusCode } else { "N/A" }
        Write-Error "API呼び出し中にエラーが発生しました (試行 $($attempt)): $($errorMessage) (HTTP Status: $($httpStatusCode))"

        if ($_.Exception.Response) {
            try {
                # エラーレスポンスボディを読み取る
                $errorResponseContent = (New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())).ReadToEnd() | ConvertFrom-Json
                Write-Error "詳細エラー: $($errorResponseContent | ConvertTo-Json -Depth 5)"

                # 特定のエラーコードに対する追加処理(例: レート制限)
                if ($errorResponseContent.error.code -eq "429") {
                    Write-Warning "レート制限に達しました。リトライを試みます。"
                }
            }
            catch {
                Write-Error "エラーレスポンスのパースに失敗しました: $($_.Exception.Message)"
            }
        }
    }

    if ($attempt -lt $maxRetries) {
        $delay = $initialDelaySeconds * [math]::Pow(2, ($attempt - 1)) # 指数バックオフ
        Write-Host "リトライします。待機時間: $($delay)秒..." -ForegroundColor Yellow
        Start-Sleep -Seconds $delay
    }
}

if ($generatedText -eq "エラー: AI応答を取得できませんでした。") {
    Write-Error "Azure OpenAI APIから有効な応答を取得できませんでした。"
}

# 最終結果の表示(エラーだった場合はエラーメッセージ)
Write-Host "`n--- 最終結果 ---`n$($generatedText)"

堅牢化のポイント: * try/catch ブロック: PowerShellの例外処理の標準的な方法。Invoke-RestMethodはHTTPステータスコードが200番台以外の場合、自動的に例外を投げます。 * ErrorAction Stop: Invoke-RestMethodで例外が発生した際に、catchブロックに処理を移すためにStopを指定します。 * StatusCodeVariable: HTTPステータスコードを直接捕捉し、条件分岐に利用します。 * リトライロジック: VBAと同様に指数バックオフ戦略を採用。レート制限(HTTP 429)など一時的なエラーからの回復を試みます。 * 詳細なエラーレスポンスの取得: _.Exception.Response.GetResponseStream() を使って、HTTPエラー発生時のレスポンスボディ(JSON形式のことが多い)を取得し、詳細なエラー情報をログ出力します。 * タイムアウト: -TimeoutSec パラメータで明示的にタイムアウトを設定します。

ベンチ/検証

作成したスクリプトの性能と信頼性を評価するための基本的な検証観点と計測方法です。

計測方法

  • VBA: Timer関数を用いて開始時刻と終了時刻を記録し、差分で処理時間を算出します。
    Dim startTime As Double
    startTime = Timer
    ' --- API呼び出し処理 ---
    Debug.Print "処理時間: " & (Timer - startTime) & "秒"
    
  • PowerShell: Measure-Commandコマンドレットを利用すると、スクリプトブロックの実行時間を簡単に計測できます。
    Measure-Command {
        # --- API呼び出し処理 ---
    } | Select-Object TotalSeconds
    

テスト観点

  1. 基本的な機能: 正常なプロンプトで期待通りの応答が返されるか。
  2. パフォーマンス:
    • 様々なmax_tokens値(短い、長い)でのレスポンスタイム。
    • 連続して複数回APIを呼び出した際の安定した応答速度。
  3. エラーハンドリング:
    • 無効なAPIキー/エンドポイントでの 401 Unauthorized404 Not Found エラーが適切に処理されるか。
    • ネットワーク障害(インターネット接続切断など)時の挙動。
    • タイムアウト設定が機能し、指定時間内に応答がない場合に適切に処理されるか。
    • APIのレート制限(429 Too Many Requests)が発生した場合にリトライロジックが機能するか。
  4. プロンプトの複雑性:
    • 非常に長いプロンプト(モデルのトークン上限に近い)を送信した場合。
    • 特殊文字やエスケープが必要な文字(", \nなど)を含むプロンプトでの挙動。
  5. JSONパースの堅牢性 (VBAのみ):
    • モデルが予期せぬ形式のJSON(例: contentが空、または構造が異なる)を返した場合に、パースが失敗しないか、あるいは適切にエラーを報告するか。
  6. セキュリティ:
    • APIキーがコードにハードコードされていないか(本番環境では)。環境変数や設定ファイルからの読み込みが正しく行われるか。

応用例/代替案

応用例

  1. Excelデータの一括処理:
    • 顧客の問い合わせ履歴(Excelシート)をAIで要約し、次のアクションを推奨する列を追加。
    • 商品レビューを分析し、ポジティブ/ネガティブな評価を自動分類。
    • 定型レポートの草稿を自動生成し、Excelシートに出力。
  2. PowerShellによる自動化:
    • ログファイルから異常パターンを抽出し、AIで原因分析のヒントを得る。
    • メールの件名と本文から、内容を分類し、適切な担当者への振り分けを提案。
    • ドキュメントの生成(例: PowerShellスクリプトのコメントから概要を生成)。

代替案

  1. Python + Azure OpenAI SDK (または requests ライブラリ):
    • 最も推奨されるアプローチ。SDKはAPIのラッパーを提供し、認証、リトライ、ストリーミングなどを抽象化してくれます。requestsライブラリもHTTP通信を容易にします。VBA/PowerShellからのPython呼び出しを検討する価値はあります。
  2. Azure Logic Apps / Power Automate:
    • ローコード/ノーコードでAzure OpenAIとの連携フローを構築できます。複雑なプログラミングなしに、様々なSaaSやオンプレミスシステムとの連携が可能です。
  3. Azure Functions / Web Apps:
    • VBA/PowerShellから直接APIを叩く代わりに、Azure FunctionsなどでAPIをラップするミドルウェア層を構築する方法です。これにより、APIキーの管理を一元化し、レート制限ロジックをサーバー側で実装し、クライアント側(VBA/PowerShell)はよりシンプルな呼び出しで済ませられます。
  4. 既存のRPAツール:
    • UiPath, Power Automate DesktopなどのRPAツールは、HTTPリクエストアクティビティを提供しており、VBA/PowerShellと同様にAPI連携が可能です。

まとめ

VBAやPowerShellからAzure OpenAI ServiceのREST APIを直接呼び出す手法は、既存の環境に新たな依存関係を持ち込まずにAIの能力を統合する強力な手段です。低レベルなHTTP/JSONの挙動を理解することで、APIキー認証の仕組み、リクエスト/レスポンスの構造、そしてエラー処理の基本を深く把握できます。

本記事では、最小限の実装から始まり、タイムアウト、リトライ、堅牢なJSONパース(VBAでの正規表現活用)といった要素を盛り込みながら、実践的な堅牢化の指針を示しました。特にVBAではJSONパースが課題となりますが、PowerShellのInvoke-RestMethodはその点において非常に優れています。

直接APIを叩くことは、細かな制御が可能であるというメリットがある一方で、認証情報の安全な管理、エラーハンドリング、レート制限への対応など、開発者が多くの責任を負うことになります。しかし、これらの課題を適切に乗り越えれば、あなたのVBA/PowerShellスクリプトは、生成AIによって新たな次元の自動化を実現するでしょう。

参考リンク

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました