Azure.AI.OpenAI (.NET)
GPT-4, GPT-4o, 임베딩, DALL-E, Whisper를 포함한 OpenAI 모델에 액세스할 수 있는 Azure OpenAI Service용 클라이언트 라이브러리입니다.
설치
dotnet add package Azure.AI.OpenAI
# OpenAI(비 Azure) 호환성을 위해
dotnet add package OpenAI
현재 버전: 2.1.0 (안정화)
환경 변수
AZURE_OPENAI_ENDPOINT=https://<resource-name>.openai.azure.com # 필수: Azure OpenAI 엔드포인트
AZURE_OPENAI_API_KEY=<api-key> # AzureKeyCredential 인증에만 필요
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini # 필수: 모델 배포 이름
AZURE_TOKEN_CREDENTIALS=prod # 프로덕션에서 DefaultAzureCredential을 사용하는 경우에만 필요
클라이언트 계층 구조
AzureOpenAIClient (최상위)
├── GetChatClient(deploymentName) → ChatClient
├── GetEmbeddingClient(deploymentName) → EmbeddingClient
├── GetImageClient(deploymentName) → ImageClient
├── GetAudioClient(deploymentName) → AudioClient
└── GetAssistantClient() → AssistantClient
인증
API 키 인증
using Azure;
using Azure.AI.OpenAI;
AzureOpenAIClient client = new(
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
new AzureKeyCredential(Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!));
Microsoft Entra 토큰 자격 증명
using Azure.Identity;
using Azure.AI.OpenAI;
// 로컬 개발: DefaultAzureCredential. 프로덕션: AZURE_TOKEN_CREDENTIALS=prod 또는 AZURE_TOKEN_CREDENTIALS=<specific_credential> 설정
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// 또는 프로덕션에서 특정 자격 증명을 직접 사용:
// https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes 참조
// var credential = new ManagedIdentityCredential();
AzureOpenAIClient client = new(
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
credential);
Azure에서 OpenAI SDK 직접 사용
using Azure.Identity;
using OpenAI;
using OpenAI.Chat;
using System.ClientModel.Primitives;
#pragma warning disable OPENAI001
BearerTokenPolicy tokenPolicy = new(
new DefaultAzureCredential(),
"https://cognitiveservices.azure.com/.default");
ChatClient client = new(
model: "gpt-4o-mini",
authenticationPolicy: tokenPolicy,
options: new OpenAIClientOptions()
{
Endpoint = new Uri("https://YOUR-RESOURCE.openai.azure.com/openai/v1")
});
채팅 완성
기본 채팅
using Azure.AI.OpenAI;
using OpenAI.Chat;
AzureOpenAIClient azureClient = new(
new Uri(endpoint),
new DefaultAzureCredential());
ChatClient chatClient = azureClient.GetChatClient("gpt-4o-mini");
ChatCompletion completion = chatClient.CompleteChat(
[
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("What is Azure OpenAI?")
]);
Console.WriteLine(completion.Content[0].Text);
비동기 채팅
ChatCompletion completion = await chatClient.CompleteChatAsync(
[
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("Explain cloud computing in simple terms.")
]);
Console.WriteLine($"Response: {completion.Content[0].Text}");
Console.WriteLine($"Tokens used: {completion.Usage.TotalTokenCount}");
스트리밍 채팅
await foreach (StreamingChatCompletionUpdate update
in chatClient.CompleteChatStreamingAsync(messages))
{
if (update.ContentUpdate.Count > 0)
{
Console.Write(update.ContentUpdate[0].Text);
}
}
옵션이 있는 채팅
ChatCompletionOptions options = new()
{
MaxOutputTokenCount = 1000,
Temperature = 0.7f,
TopP = 0.95f,
FrequencyPenalty = 0,
PresencePenalty = 0
};
ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options);
다중 턴 대화
List<ChatMessage> messages = new()
{
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("Hi, can you help me?"),
new AssistantChatMessage("Of course! What do you need help with?"),
new UserChatMessage("What's the capital of France?")
};
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
messages.Add(new AssistantChatMessage(completion.Content[0].Text));
구조화된 출력 (JSON 스키마)
using System.Text.Json;
ChatCompletionOptions options = new()
{
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
jsonSchemaFormatName: "math_reasoning",
jsonSchema: BinaryData.FromBytes("""
{
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
"""u8.ToArray()),
jsonSchemaIsStrict: true)
};
ChatCompletion completion = await chatClient.CompleteChatAsync(
[new UserChatMessage("How can I solve 8x + 7 = -23?")],
options);
using JsonDocument json = JsonDocument.Parse(completion.Content[0].Text);
Console.WriteLine($"Answer: {json.RootElement.GetProperty("final_answer")}");
추론 모델 (o1, o4-mini)
ChatCompletionOptions options = new()
{
ReasoningEffortLevel = ChatReasoningEffortLevel.Low,
MaxOutputTokenCount = 100000
};
ChatCompletion completion = await chatClient.CompleteChatAsync(
[
new DeveloperChatMessage("You are a helpful assistant"),
new UserChatMessage("Explain the theory of relativity")
], options);
Azure AI Search 통합 (RAG)
using Azure.AI.OpenAI.Chat;
#pragma warning disable AOAI001
ChatCompletionOptions options = new();
options.AddDataSource(new AzureSearchChatDataSource()
{
Endpoint = new Uri(searchEndpoint),
IndexName = searchIndex,
Authentication = DataSourceAuthentication.FromApiKey(searchKey)
});
ChatCompletion completion = await chatClient.CompleteChatAsync(
[new UserChatMessage("What health plans are available?")],
options);
ChatMessageContext context = completion.GetMessageContext();
if (context?.Intent is not null)
{
Console.WriteLine($"Intent: {context.Intent}");
}
foreach (ChatCitation citation in context?.Citations ?? [])
{
Console.WriteLine($"Citation: {citation.Content}");
}
임베딩
using OpenAI.Embeddings;
EmbeddingClient embeddingClient = azureClient.GetEmbeddingClient("text-embedding-ada-002");
OpenAIEmbedding embedding = await embeddingClient.GenerateEmbeddingAsync("Hello, world!");
ReadOnlyMemory<float> vector = embedding.ToFloats();
Console.WriteLine($"Embedding dimensions: {vector.Length}");
배치 임베딩
List<string> inputs = new()
{
"First document text",
"Second document text",
"Third document text"
};
OpenAIEmbeddingCollection embeddings = await embeddingClient.GenerateEmbeddingsAsync(inputs);
foreach (OpenAIEmbedding emb in embeddings)
{
Console.WriteLine($"Index {emb.Index}: {emb.ToFloats().Length} dimensions");
}
이미지 생성 (DALL-E)
using OpenAI.Images;
ImageClient imageClient = azureClient.GetImageClient("dall-e-3");
GeneratedImage image = await imageClient.GenerateImageAsync(
"A futuristic city skyline at sunset",
new ImageGenerationOptions
{
Size = GeneratedImageSize.W1024xH1024,
Quality = GeneratedImageQuality.High,
Style = GeneratedImageStyle.Vivid
});
Console.WriteLine($"Image URL: {image.ImageUri}");
오디오 (Whisper)
전사
using OpenAI.Audio;
AudioClient audioClient = azureClient.GetAudioClient("whisper");
AudioTranscription transcription = await audioClient.TranscribeAudioAsync(
"audio.mp3",
new AudioTranscriptionOptions
{
ResponseFormat = AudioTranscriptionFormat.Verbose,
Language = "en"
});
Console.WriteLine(transcription.Text);
텍스트 음성 변환
BinaryData speech = await audioClient.GenerateSpeechAsync(
"Hello, welcome to Azure OpenAI!",
GeneratedSpeechVoice.Alloy,
new SpeechGenerationOptions
{
SpeedRatio = 1.0f,
ResponseFormat = GeneratedSpeechFormat.Mp3
});
await File.WriteAllBytesAsync("output.mp3", speech.ToArray());
함수 호출 (도구)
ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
functionName: "get_current_weather",
functionDescription: "Get the current weather in a given location",
functionParameters: BinaryData.FromString("""
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
"""));
ChatCompletionOptions options = new()
{
Tools = { getCurrentWeatherTool }
};
ChatCompletion completion = await chatClient.CompleteChatAsync(
[new UserChatMessage("What's the weather in Seattle?")],
options);
if (completion.FinishReason == ChatFinishReason.ToolCalls)
{
foreach (ChatToolCall toolCall in completion.ToolCalls)
{
Console.WriteLine($"Function: {toolCall.FunctionName}");
Console.WriteLine($"Arguments: {toolCall.FunctionArguments}");
}
}
주요 형식 참조
| 형식 | 용도 |
|---|---|
AzureOpenAIClient | Azure OpenAI용 최상위 클라이언트 |
ChatClient | 채팅 완성 |
EmbeddingClient | 텍스트 임베딩 |
ImageClient | 이미지 생성 (DALL-E) |
AudioClient | 오디오 전사/TTS |
ChatCompletion | 채팅 응답 |
ChatCompletionOptions | 요청 구성 |
StreamingChatCompletionUpdate | 스트리밍 응답 청크 |
ChatMessage | 기본 메시지 형식 |
SystemChatMessage | 시스템 프롬프트 |
UserChatMessage | 사용자 입력 |
AssistantChatMessage | 어시스턴트 응답 |
DeveloperChatMessage | 개발자 메시지 (추론 모델) |
ChatTool | 함수/도구 정의 |
ChatToolCall | 도구 호출 요청 |
모범 사례
- 프로덕션에서는 Entra ID 사용 — API 키를 피하고
DefaultAzureCredential사용 - 클라이언트 인스턴스 재사용 — 한 번 생성하고 요청 간 공유
- 속도 제한 처리 — 429 오류에 대한 지수 백오프 구현
- 긴 응답에는 스트리밍 사용 — 더 나은 UX를 위해
CompleteChatStreamingAsync사용 - 적절한 시간 제한 설정 — 긴 완성에는 확장된 시간 제한이 필요할 수 있음
- 구조화된 출력 사용 — JSON 스키마는 일관된 응답 형식을 보장
- 토큰 사용량 모니터링 — 비용 관리를 위해
completion.Usage추적 - 도구 호출 검증 — 실행 전에 항상 함수 인수 검증
오류 처리
using Azure;
try
{
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
}
catch (RequestFailedException ex) when (ex.Status == 429)
{
Console.WriteLine("Rate limited. Retry after delay.");
await Task.Delay(TimeSpan.FromSeconds(10));
}
catch (RequestFailedException ex) when (ex.Status == 400)
{
Console.WriteLine($"Bad request: {ex.Message}");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"Azure OpenAI error: {ex.Status} - {ex.Message}");
}
관련 SDK
| SDK | 용도 | 설치 |
|---|---|---|
Azure.AI.OpenAI | Azure OpenAI 클라이언트 (이 SDK) | dotnet add package Azure.AI.OpenAI |
OpenAI | OpenAI 호환성 | dotnet add package OpenAI |
Azure.Identity | 인증 | dotnet add package Azure.Identity |
Azure.Search.Documents | RAG용 AI Search | dotnet add package Azure.Search.Documents |