1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure OpenAI Response API をpythonで試す

Posted at

Azure OpenAI Response API を試してみました。基本的にリンク先の内容に準じて確認しています。

これらは注意点です。

  • 使えるリージョン限定
  • 使えるモデルが限定
  • Web検索は使用できない
  • トレースは試しましたが、残らなかったです
  • 入力候補を試しましたが、残らなかったです(APIにパラメータはあるのに)

試したこと

環境

Ubuntu 22.4.05 で Python3.11.7 で試しています。
パッケージは以下の通り。

  • azure-ai-projects: 1.0.0

共通箇所

import base64
from pprint import pprint

from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential, get_bearer_token_provider

MODEL = "gpt-4.1-nano"

project = AIProjectClient(
  endpoint="https://<Resource>.services.ai.azure.com/api/projects/<project>",
  credential=DefaultAzureCredential())

client = project.get_openai_client(api_version="2025-04-01-preview")

基本使い方

まずは、基本の使い方です。
instructionがシステムプロンプトの役割をします。inputがシンプルなのがいいですね。

response = client.responses.create(
  model=MODEL,
  instructions="You are a helpful assistant.",
  input="This is a test.",
)

print(response.model_dump_json(indent=2)) 
結果
{
  "id": "resp_689705ce028c819ab9a27f2d18cccdb704fa2fc07ff11bcc",
  "created_at": 1754727886.0,
  "error": null,
  "incomplete_details": null,
  "instructions": "You are a helpful assistant.",
  "metadata": {
    "purpose": "response-test"
  },
  "model": "gpt-4.1-nano",
  "object": "response",
  "output": [
    {
      "id": "msg_689705ce6998819aace609464bdc3daa04fa2fc07ff11bcc",
      "content": [
        {
          "annotations": [],
          "text": "Hello! I see you've sent a test message. How can I assist you today?",
          "type": "output_text"
        }
      ],
      "role": "assistant",
      "status": "completed",
      "type": "message"
    }
  ],
  "parallel_tool_calls": true,
  "temperature": 1.0,
  "tool_choice": "auto",
  "tools": [],
  "top_p": 1.0,
  "max_output_tokens": null,
  "previous_response_id": null,
  "reasoning": {
    "effort": null,
    "generate_summary": null,
    "summary": null
  },
  "service_tier": "default",
  "status": "completed",
  "text": {
    "format": {
      "type": "text"
    }
  },
  "truncation": "disabled",
  "usage": {
    "input_tokens": 22,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 18,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 40
  },
  "user": null,
  "background": false,
  "content_filters": null,
  "max_tool_calls": null,
  "prompt_cache_key": null,
  "safety_identifier": null,
  "store": true
}

以下の方法で、後で結果を取得できます。30日菅保持されるそうです。

既定では、応答データは 30 日間保持されます。

print(client.responses.retrieve(response.id).model_dump_json(indent=2))

マルチターン(2回目)

previous_response_idを指定することで、マルチターンにできます。2度目のコールで履歴を渡さなくて済むのが便利です(ただ、内部的には前の履歴をLLMに渡しており、その分のToken課金もされています)。
response.idは1回目も2回目以降もずっと一意なので、途中からやり直す(N回目の履歴までのIDを取得、渡す)ことはできません(このブログに書いたIDは別になっていますが、単純に別セッションで実行したから)。
前のターンのinstructionは引き継がないので、再度指定するか、別のinsutrutionを渡すかします。

second_response = client.responses.create(
    model=MODEL,
    previous_response_id=response.id,
    input="さっき何を言ったの?",
#    下の形式でもOK    
#    input=[{"role": "user", "content": "さっき何を言ったの?"}],
)
print(second_response.model_dump_json(indent=2)) 
結果
{
  "id": "resp_6896f3681efc819abdec84ccf94da5810382c6866b5581f7",
  "created_at": 1754723176.0,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "metadata": {
    "purpose": "response-test"
  },
  "model": "gpt-4.1-nano",
  "object": "response",
  "output": [
    {
      "id": "msg_6896f36888d8819ab100a9f3d93406d00382c6866b5581f7",
      "content": [
        {
          "annotations": [],
          "text": "あなたが「これがテストです」と言ったのを覚えています。",
          "type": "output_text"
        }
      ],
      "role": "assistant",
      "status": "completed",
      "type": "message"
    }
  ],
  "parallel_tool_calls": true,
  "temperature": 1.0,
  "tool_choice": "auto",
  "tools": [],
  "top_p": 1.0,
  "max_output_tokens": null,
  "previous_response_id": "resp_6896f2e478f4819aa584902c63a541380382c6866b5581f7",
  "reasoning": {
    "effort": null,
    "generate_summary": null,
    "summary": null
  },
  "service_tier": "default",
  "status": "completed",
  "text": {
    "format": {
      "type": "text"
    }
  },
  "truncation": "disabled",
  "usage": {
    "input_tokens": 47,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 18,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 65
  },
  "user": null,
  "background": false,
  "content_filters": null,
  "max_tool_calls": null,
  "prompt_cache_key": null,
  "safety_identifier": null,
  "store": true
}

ストリーミング

ストリームでの取得。

response = client.responses.create(
    input="1000文字程度の小噺をして",
    model=MODEL,
    stream=True,
)

for event in response:
    if event.type == 'response.output_text.delta':
        print(event.delta, end='')

Tool

Tool呼出。

response = client.responses.create(  
    model=MODEL,
    tools=[  
        {  
            "type": "function",  
            "name": "get_weather",  
            "description": "Get the weather for a location",  
            "parameters": {  
                "type": "object",  
                "properties": {  
                    "location": {"type": "string"},  
                },  
                "required": ["location"],  
            },  
        }  
    ],  
    input="What's the weather in San Francisco?",
)  

print(response.model_dump_json(indent=2))  
結果
{
  "id": "resp_6896f5196414819b9dc0aa542f3e4b000526d04af4c796fa",
  "created_at": 1754723609.0,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "metadata": {
    "purpose": "response-test"
  },
  "model": "gpt-4.1-nano",
  "object": "response",
  "output": [
    {
      "arguments": "{\"location\":\"San Francisco\"}",
      "call_id": "call_dD9gAonX1GxjsNU0qtebOojA",
      "name": "get_weather",
      "type": "function_call",
      "id": "fc_6896f519f59c819ba52e0d89e45da6210526d04af4c796fa",
      "status": "completed"
    }
  ],
  "parallel_tool_calls": true,
  "temperature": 1.0,
  "tool_choice": "auto",
  "tools": [
    {
      "name": "get_weather",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string"
          }
        },
        "required": [
          "location"
        ]
      },
      "strict": true,
      "type": "function",
      "description": "Get the weather for a location"
    }
  ],
  "top_p": 1.0,
  "max_output_tokens": null,
  "previous_response_id": null,
  "reasoning": {
    "effort": null,
    "generate_summary": null,
    "summary": null
  },
  "service_tier": "default",
  "status": "completed",
  "text": {
    "format": {
      "type": "text"
    }
  },
  "truncation": "disabled",
  "usage": {
    "input_tokens": 45,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 32,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 77
  },
  "user": null,
  "background": false,
  "content_filters": null,
  "max_tool_calls": null,
  "prompt_cache_key": null,
  "safety_identifier": null,
  "store": true
}

先ほどの結果をinputに入れて、元のPrompt「What's the weather in San Francisco?」に回答させる

input = []  
for output in response.output:  
    if output.type == "function_call":  
        match output.name:  
            case "get_weather":  
                input.append(  
                    {  
                        "type": "function_call_output",  
                        "call_id": output.call_id,  
                        "output": '{"temperature": "70 degrees"}',  
                    }  
                )  
            case _:  
                raise ValueError(f"Unknown function call: {output.name}")  
  
second_response = client.responses.create(  
    model=MODEL,  
    previous_response_id=response.id,  
    input=input,
)  

print(f"{input=}")
print(second_response.model_dump_json(indent=2)) 
結果
input=[{'type': 'function_call_output', 'call_id': 'call_LeGcTqo26z5V6mYTfsW9HXpC', 'output': '{"temperature": "70 degrees"}'}]
{
  "id": "resp_68975675b99c8198989d1c880fc5520d04fd65c204c1ce76",
  "created_at": 1754748533.0,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "metadata": {},
  "model": "gpt-4.1-nano",
  "object": "response",
  "output": [
    {
      "id": "msg_6897567645408198a7dd7b17617467d104fd65c204c1ce76",
      "content": [
        {
          "annotations": [],
          "text": "The current temperature in San Francisco is approximately 70 degrees.",
          "type": "output_text"
        }
      ],
      "role": "assistant",
      "status": "completed",
      "type": "message"
    }
  ],
  "parallel_tool_calls": true,
  "temperature": 1.0,
  "tool_choice": "auto",
  "tools": [],
  "top_p": 1.0,
  "max_output_tokens": null,
  "previous_response_id": "resp_6897567463c88198a3d0406072262e5804fd65c204c1ce76",
  "reasoning": {
    "effort": null,
    "generate_summary": null,
    "summary": null
  },
  "service_tier": "default",
  "status": "completed",
  "text": {
    "format": {
      "type": "text"
    }
  },
  "truncation": "disabled",
  "usage": {
    "input_tokens": 42,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 13,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 55
  },
  "user": null,
  "background": false,
  "content_filters": null,
  "max_tool_calls": null,
  "prompt_cache_key": null,
  "safety_identifier": null,
  "store": true
}

画像(URL)読込

画像をURLから読み込んで内容を答えさせる。

response = client.responses.create(
    model=MODEL,
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_text", "text": "この画像は何?"},
                {
                    "type": "input_image",
                    "image_url": "https://pbs.twimg.com/media/EJr1V-8UUAAV9Qj?format=jpg&name=large",
                },
            ],
        }
    ],
)

print(response.model_dump_json(indent=2))

ちなみに画像はこいつ。深い意味までは読み取れていないですね。
image.png

結果
{
  "id": "resp_6896f6868ebc819a90904f222f842f07097b2ce2873828ab",
  "created_at": 1754723974.0,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "metadata": {
    "purpose": "response-test"
  },
  "model": "gpt-4.1-nano",
  "object": "response",
  "output": [
    {
      "id": "msg_6896f6886c44819aa919522abc133bb0097b2ce2873828ab",
      "content": [
        {
          "annotations": [],
          "text": "この画像は、猫のキャラクターが人間のように洋服を着て、テーブルに座っているイラストです。猫は赤い制服のような服を着ており、にこやかに笑っている様子です。",
          "type": "output_text"
        }
      ],
      "role": "assistant",
      "status": "completed",
      "type": "message"
    }
  ],
  "parallel_tool_calls": true,
  "temperature": 1.0,
  "tool_choice": "auto",
  "tools": [],
  "top_p": 1.0,
  "max_output_tokens": null,
  "previous_response_id": null,
  "reasoning": {
    "effort": null,
    "generate_summary": null,
    "summary": null
  },
  "service_tier": "default",
  "status": "completed",
  "text": {
    "format": {
      "type": "text"
    }
  },
  "truncation": "disabled",
  "usage": {
    "input_tokens": 1550,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 46,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 1596
  },
  "user": null,
  "background": false,
  "content_filters": null,
  "max_tool_calls": null,
  "prompt_cache_key": null,
  "safety_identifier": null,
  "store": true
}

PDF読込(エンコード実施)

PDFの読込(アップロードではなく、エンコードして渡す)。

with open("./data/PerksPlus.pdf", "rb") as f:
    data = f.read()

base64_string = base64.b64encode(data).decode("utf-8")

response = client.responses.create(
    model=MODEL,
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_file",
                    "filename": "test.pdf",
                    "file_data": f"data:application/pdf;base64,{base64_string}",
                },
                {
                    "type": "input_text",
                    "text": "Summarize this PDF",
                },
            ],
        },
    ]
)

print(response.output_text)

ちなみに、PDF内に絵が多いとこんなエラー。

BadRequestError: Error code: 400 - 
{'error': 
  {'message': 'Too many images in request: 11, maximum allowed: 10.', 
   'type': 'invalid_request_error', 'param': None, 'code': None}}

MCPリモートサーバー使用

Microsoft Learn MCP Server 使います。

LEARN_MCP_TOOL = {
    "type": "mcp",
    "server_label": "learn",
    "server_url": "https://learn.microsoft.com/api/mcp",
    "require_approval": "never",
}

def ask_learn(question: str) -> str:

    # 1回目: 通常の問い合わせ。モデルが必要に応じて MCP を呼び出します。
    resp = client.responses.create(
        model=MODEL,
        input=question,
        tools=[LEARN_MCP_TOOL],
        instructions=(
            "If the user's question mentions Microsoft/Azure technologies, "
            "use the MCP tool labeled 'learn' to fetch up-to-date docs from Microsoft Learn."
        ),
    )

    # 承認が必要な場合は mcp_approval_request が返ってくるので承認して再実行
    approval_ids = [
        item.id
        for item in (resp.output or [])
        if getattr(item, "type", "") == "mcp_approval_request"
        and getattr(item, "server_label", "") == "learn"
    ]
    for approval_id in approval_ids:
        resp = client.responses.create(
            model=MODEL,
            previous_response_id=resp.id,
            tools=[LEARN_MCP_TOOL],
            input=[{
                "type": "mcp_approval_response",
                "approve": True,
                "approval_request_id": approval_id,
            }],
        )

    # 最終テキストを返す
    return resp.output_text

question = "Azure Storage アカウントを az cli で作成する手順を、コード例と注意点込みで教えて。"
print(ask_learn(question))
結果
Azure StorageアカウントをAzure CLI (`az`) で作成する手順を、例コードと注意点を含めてご紹介します。

### コマンド例

```bash
az storage account create \
  --name <your-unique-account-name> \
  --resource-group <your-resource-group> \
  --location eastus \
  --sku Standard_RAGRS \
  --kind StorageV2 \
  --min-tls-version TLS1_2 \
  --allow-blob-public-access false
```

### 各パラメータのポイント
- `<your-unique-account-name>`:Azure全体でユニークな名前にしてください。
- `<your-resource-group>`:既存のリソースグループ名を指定します。ない場合は作成が必要です。
- `--location`:リージョンを指定します。
- `--sku`:冗長性の設定です。例では地域冗長ストレージ(RA-GRS)を使用。
- `--kind StorageV2`:汎用 v2 ストレージアカウントを作成。
- `--min-tls-version TLS1_2`:セキュリティ向上のために TLS 1.2 を最小バージョンに設定。
- `--allow-blob-public-access false`:Blobの公開アクセスを禁止。

### 注意点
...
- 必要に応じて他のオプション(ネットワーク制御やデータレイク機能など)も指定可能です。
- `az storage account create` コマンドを実行する前に、Azure CLI にログインしておく必要があります(例:`az login`)。

これにより、Azure CLI で効率的にAzureストレージアカウントを作成できます。

その他

以下の方法を試しましたが、両方ともに Response APIではできませんでした。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?