Chat Completion APIの結果をAzure AI Foundryでトレースと監視できるようにしてみました。
評価メトリックやRisk&Safetyは出力していません。
Agentに関してはこちらで書いています。
前提
- Log Analytics Workspaceのリソース作成済
- Application insightsのリソース作成済
- Azure AI Foundreyのリソース作成し、モデルもデプロイ済
ちなみにAzure AI Foundry画面から「リソース配分状況」は特に設定なしに参照可能です。
Step
1. Application Insights のリソース接続
Azure AI Foundry画面のメニュー「監視」を開いて、「アプリケーション分析」タブからリソース接続実施
2. プログラム作成
プログラム環境
種類 | Version | 備考 |
---|---|---|
OS | Ubuntu22.04.5 LTS | WSL2で動かしています |
Python | 3.13.2 | |
Poetry | 2.1.3 | 仮想環境の管理に使用 |
Python パッケージ
種類 | Version | 備考 |
---|---|---|
azure-monitor-opentelemetry | 1.6.11 | |
azure-ai-projects | 1.0.0b12 | |
opentelemetry-instrumentation-openai-v2 | 2.1b0 | |
openai | 1.97.0 |
プログラム全体
まずはプログラム全体です。
シンプルにトレース残すパターンとspanでまとめるパターンの2種類に分けています。
import os
from datetime import datetime
from logging import Formatter
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import trace
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
os.environ["OTEL_RESOURCE_ATTRIBUTES"]="service.namespace=my-namespace,service.instance.id=my-instance"
os.environ["OTEL_SERVICE_NAME"]="テストアプリ"
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
OpenAIInstrumentor().instrument()
project_client = AIProjectClient(
credential=DefaultAzureCredential(),
endpoint="https://<your-resource>.services.ai.azure.com/api/projects/<your-project>",
)
connection_string = project_client.telemetry.get_connection_string()
configure_azure_monitor(connection_string=connection_string,
logging_formatter=Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
)
client = project_client.inference.get_azure_openai_client(api_version="2024-10-21")#2025-02-01-preview")
response = client.chat.completions.create(
model="gpt-4.1-nano",
messages=[
{"role": "user", "content": "あなたは誰?"},
],
)
print(response.choices[0].message.content)
tracer = trace.get_tracer(__name__)
def build_prompt_with_context(message: str) -> str:
return [{'role': 'developer', 'content': "あなたは頼りになるAIアシスタントです"},
{'role': 'user', 'content': message}]
@tracer.start_as_current_span("まとめるspan")
def assess_claims_with_context(messages):
responses = []
current_span = trace.get_current_span()
current_span.set_attribute("operation.custom_attribute", str(datetime.now()))
for message in messages:
response = client.chat.completions.create(
model="gpt-4.1-nano",
messages=build_prompt_with_context(message=message),
)
responses.append(response.choices[0].message.content.strip('., '))
return responses
assess_claims_with_context(["今何時?", "きょうは何日?"])
共通部分
部分的に解説します。
環境変数設定
面倒だったのでPyenv使わずに直接設定。
最初の2行で、画面のフィルタに使う「アプリケーション」の設定。ここに何も設定しないとunknown_service
になります。
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
はトレースにメッセージ入出力を残すための設定です。
os.environ["OTEL_RESOURCE_ATTRIBUTES"]="service.namespace=my-namespace,service.instance.id=my-instance"
os.environ["OTEL_SERVICE_NAME"]="テストアプリ"
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
こちらを参考にしました。
監視とトレース開始
監視とトレースを開始します
OpenAIInstrumentor().instrument()
監視の設定
接続文字列を取得して監視設定。loggingのformatterは設定しないとエラー起きたので設定しています。前に試した時は不要だったので、バージョンに依存しているかもしれません。
connection_string = project_client.telemetry.get_connection_string()
configure_azure_monitor(connection_string=connection_string,
logging_formatter=Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
)
シンプルにトレース残すパターン
API実行
特筆することはないです。
client = project_client.inference.get_azure_openai_client(api_version="2024-10-21")#2025-02-01-preview")
response = client.chat.completions.create(
model="gpt-4.1-nano",
messages=[
{"role": "user", "content": "あなたは誰?"},
],
)
print(response.choices[0].message.content)
トレース結果
Azure AI Foundry画面上にトレースが表示されるようになりました。
{
"name": "chat gpt-4.1-nano",
"context": {
"trace_id": "faf76a72b9d50df2bc4be8eb1c4ad68a",
"span_id": "d898f8acf0242d5d",
"thread_id": "undefined",
"trace_state": "undefined"
},
"kind": "GenAI",
"parent_id": "undefined",
"start_time": "2025-07-21T06:10:33.1151180Z",
"end_time": "2025-07-21T06:10:40.014Z",
"status": {
"status_code": "Ok",
"description": "0 undefined"
},
"attributes": {
"span_type": "GenAI",
"_MS.ResourceAttributeId": "04387455-8e56-48b9-a22d-978213d336da",
"gen_ai.operation.name": "chat",
"gen_ai.system": "openai",
"gen_ai.request.model": "gpt-4.1-nano",
"gen_ai.response.model": "gpt-4.1-nano-2025-04-14",
"gen_ai.response.finish_reasons": "('stop',)",
"gen_ai.response.id": "chatcmpl-Bve1OUMOno6IfcK4F7NeScCn4bAUa",
"gen_ai.usage.input_tokens": 11,
"gen_ai.usage.output_tokens": 62,
"gen_ai.usage.total_tokens": "73"
},
"events": [
{
"name": "gen_ai.user.message",
"timestamp": "2025-07-21T06:10:33.1152830Z",
"attributes": {
"gen_ai.event.content": {
"content": "あなたは誰?"
},
"gen_ai.system": "openai",
"event.name": "gen_ai.user.message"
}
},
{
"name": "gen_ai.choice",
"timestamp": "2025-07-21T06:10:40.0142070Z",
"attributes": {
"gen_ai.event.content": {
"role": "assistant",
"content": "こんにちは!私はOpenAIが開発したAI、ChatGPTです。あなたのお手伝いをしたり、質問にお答えしたりするためにここにいます。何か知りたいことや話したいことがあれば、遠慮なく教えてください!"
},
"gen_ai.system": "openai",
"event.name": "gen_ai.choice"
}
}
],
"external_event_data_uris": "undefined",
"span_json_uri": "undefined",
"links": "undefined",
"resource": "undefined"
}
spanにまとめるパターン
tracer取得
tracer = trace.get_tracer(__name__)
API呼出
デコレーター@tracer.start_as_current_span
を使ってまとめる幅を定義します。これで、SPAN単位でAI Foundry上で見ることができます。RAGなどのときに便利かと思います。
カスタム属性operationcustom_attribute
も試しに追加しています(spanの親側に追加)。
def build_prompt_with_context(message: str) -> str:
return [{'role': 'developer', 'content': "あなたは頼りになるAIアシスタントです"},
{'role': 'user', 'content': message}]
@tracer.start_as_current_span("まとめるspan")
def assess_claims_with_context(messages):
responses = []
current_span = trace.get_current_span()
current_span.set_attribute("operation.custom_attribute", str(datetime.now()))
for message in messages:
response = client.chat.completions.create(
model="gpt-4.1-nano",
messages=build_prompt_with_context(message=message),
)
responses.append(response.choices[0].message.content.strip('., '))
return responses
assess_claims_with_context(["今何時?", "きょうは何日?"])