ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Azure.AI.OpenAI (.NET) 实战指南:在 Agentic Awesome Skills 中构建 .NET 智能应用

Azure.AI.OpenAI (.NET) 实战指南:在 Agentic Awesome Skills 中构建 .NET 智能应用

Azure.AI.OpenAI (.NET) 实战指南:在 Agentic Awesome Skills 中构建 .NET 智能应用

【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/GitHub_Trending/an/agentic-awesome-skills

本文是 Agentic Awesome Skills(AAS)技能库中 azure-ai-openai-dotnet 技能的完整技术指南。它围绕 Azure OpenAI Service 的 .NET 官方客户端库,系统讲解如何完成 SDK 安装、环境变量配置、认证方式选型、聊天补全、结构化输出、RAG 检索增强、嵌入、图像生成、语音合成与函数调用等能力。读完本文,你将掌握用Azure.AI.OpenAI2.x 从零搭建 .NET 智能应用的完整链路,并理解 AAS 中该技能被标记为risk: critical的原因及其适用边界。

技能定位:AAS 目录中的 .NET Azure OpenAI 能力单元

在 AAS 插件 agentic-awesome-skills-claude 的技能目录中,本技能以SKILL.md的形式被组织为独立能力单元,其 frontmatter 元数据如下:

name: azure-ai-openai-dotnet description: Azure OpenAI SDK for .NET. Client library for Azure OpenAI and OpenAI services. Use for chat completions, embeddings, image generation, audio transcription, and assistants. risk: critical source: community date_added: '2026-02-27'
  • risk: critical表示该技能涉及云资源调用、密钥管理与生产级代码路径,Agent 使用前必须严格校验输入与权限边界;
  • source: community表明它来自社区贡献,质量遵循 AAS 的社区规范;
  • date_added记录技能入库时间,便于追溯版本演进。

AAS 将技能按语言与平台横向铺开:与本文同目录下还存在 azure-ai-projects-dotnet(面向 Azure AI Foundry 项目级操作的高级 SDK)等姊妹技能,本文聚焦最底层、最通用的Azure.AI.OpenAI客户端。

安装与版本说明

技能文档指定的安装方式为 NuGet 包管理:

dotnet add package Azure.AI.OpenAI # For OpenAI (non-Azure) compatibility dotnet add package OpenAI

要点:

  • 主包Azure.AI.OpenAI:面向 Azure OpenAI Service 的官方客户端,文档记录当前稳定版本为2.1.0;
  • 兼容包OpenAI:当你的代码需要同时兼容 OpenAI 官方端点(非 Azure 环境)时使用。事实上Azure.AI.OpenAI2.x 构建在OpenAISDK 之上,二者共享OpenAI.Chat、OpenAI.Embeddings、OpenAI.Images、OpenAI.Audio等命名空间,这也是后续代码中类型同时来自两个程序集的原因。

环境变量与连接配置

在编写任何代码之前,先建立统一的连接配置。技能文档给出了三组环境变量:

AZURE_OPENAI_ENDPOINT=https://<resource-name>.openai.azure.com AZURE_OPENAI_API_KEY=<api-key> # For key-based auth AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini # Your deployment name

其中:

变量作用说明
AZURE_OPENAI_ENDPOINT服务端点形如https://<资源名>.openai.azure.com,在 Azure 门户的"Keys and Endpoint"页获取
AZURE_OPENAI_API_KEY密钥认证凭证仅用于密钥认证路径;生产环境推荐改用 Entra ID(见下文)
AZURE_OPENAI_DEPLOYMENT_NAME部署名对应你在 Azure OpenAI Studio 中创建的模型部署名称,例如gpt-4o-mini

提示:在 .NET 中可通过Environment.GetEnvironmentVariable("...")读取;在 AAS/Agent 场景下,更建议结合密钥管理方案注入环境变量,避免把密钥写入代码仓库。

客户端层级架构:一个顶层客户端,五个子客户端

AzureOpenAIClient采用"工厂 + 子客户端"的层级设计,技能文档用如下结构图概括:

AzureOpenAIClient (top-level) ├── GetChatClient(deploymentName) → ChatClient ├── GetEmbeddingClient(deploymentName) → EmbeddingClient ├── GetImageClient(deploymentName) → ImageClient ├── GetAudioClient(deploymentName) → AudioClient └── GetAssistantClient() → AssistantClient
  • 顶层AzureOpenAIClient负责认证与端点配置,所有子客户端都由它派生,共享同一套凭证;
  • 每个子客户端对应一种模型能力:ChatClient(对话补全)、EmbeddingClient(文本嵌入)、ImageClient(图像生成)、AudioClient(语音转写/合成)、AssistantClient(Assistants API);
  • 子客户端通过部署名(deployment name)定位具体模型,例如GetChatClient("gpt-4o-mini")。

这一层级结构直接决定了"复用客户端实例"的最佳实践:顶层客户端创建一次即可反复派生子客户端,避免重复认证握手。

三种认证方式

技能文档覆盖了从开发到生产的完整认证路径。

1. API Key 认证(快速开始)

using Azure; using Azure.AI.OpenAI; AzureOpenAIClient client = new( new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!), new AzureKeyCredential(Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!));

AzureKeyCredential来自Azure.Core(通过using Azure;引入),适合本地开发与快速原型验证。

2. Microsoft Entra ID(生产推荐)

using Azure.Identity; using Azure.AI.OpenAI; AzureOpenAIClient client = new( new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!), new DefaultAzureCredential());

DefaultAzureCredential会按顺序尝试多种身份源(环境变量、Azure CLI 登录、托管身份、Visual Studio 凭据等),在本地开发与云端部署之间无缝切换,无需修改代码。技能文档明确将 Entra ID 列为生产环境首选,因为它避免了静态密钥的泄露与轮换成本。

3. 使用 OpenAI SDK 直连 Azure 端点

当你想用原生OpenAI命名空间的ChatClient(例如为多环境切换代码路径)时,可以通过自定义认证策略接入 Azure:

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") });

关键点:

  • BearerTokenPolicy携带 Entra ID 令牌(作用域为https://cognitiveservices.azure.com/.default);
  • OpenAIClientOptions.Endpoint必须指向 Azure 的/openai/v1兼容路径;
  • #pragma warning disable OPENAI001用于抑制 SDK 对"自定义认证策略"场景的兼容性警告。

聊天补全:五种典型调用形态

ChatClient是使用频率最高的子客户端,技能文档给出了五种典型场景。

基础对话

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);

注意CompleteChat接受IEnumerable<ChatMessage>,这里直接以内联集合表达式传入SystemChatMessage与UserChatMessage。

异步对话

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}");

异步版本返回ChatCompletion,其中Usage.TotalTokenCount可用于 Token 用量监控(对应最佳实践第 7 条)。

流式输出

await foreach (StreamingChatCompletionUpdate update in chatClient.CompleteChatStreamingAsync(messages)) { if (update.ContentUpdate.Count > 0) { Console.Write(update.ContentUpdate[0].Text); } }

CompleteChatStreamingAsync返回IAsyncEnumerable<StreamingChatCompletionUpdate>,逐块推送增量文本,适合长回答场景以改善交互体验(对应最佳实践第 4 条)。

带采样参数配置

ChatCompletionOptions options = new() { MaxOutputTokenCount = 1000, Temperature = 0.7f, TopP = 0.95f, FrequencyPenalty = 0, PresencePenalty = 0 }; ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options);
参数类型作用
MaxOutputTokenCountint输出 Token 上限,控制回答长度
Temperaturefloat采样温度,值越高随机性越强
TopPfloat核采样概率阈值,与 Temperature 二选一调节即可
FrequencyPenaltyfloat频率惩罚,抑制重复表述
PresencePenaltyfloat存在惩罚,鼓励引入新话题

多轮对话

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));

多轮对话的本质是把历史消息(含系统、用户、助手消息)整体重新发送,每次请求后把新的助手回复追加回messages,形成上下文延续。

结构化输出:JSON Schema 约束

为了让模型输出严格符合程序可解析的结构,ChatResponseFormat.CreateJsonSchemaFormat支持以 JSON Schema 约束响应:

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")}");

实操要点:

  • jsonSchemaFormatName为该格式命名;
  • jsonSchema使用BinaryData.FromBytes("""..."""u8.ToArray())传入 UTF-8 编码的 Schema 文本(C# 11 原始字符串字面量 +u8后缀);
  • jsonSchemaIsStrict: true开启严格模式——Schema 中必须显式声明additionalProperties: false并列出所有required字段;
  • 解析时用JsonDocument读取结构化字段,从而避免手工正则解析模型输出。

推理模型(o1 / o4-mini)

针对 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);
  • ReasoningEffortLevel控制推理投入:Low/Medium/High,值越高推理链越长、耗时与 Token 成本越高;
  • MaxOutputTokenCount需要为推理模型的思考过程预留较大空间(示例中给出 100000);
  • 推理模型通常使用DeveloperChatMessage作为系统级指令载体,替代传统的SystemChatMessage。

接入 Azure AI Search 实现 RAG

技能文档演示了如何通过AddDataSource将 Azure AI Search 索引挂载为知识源,让模型基于检索到的文档作答并返回引用:

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}"); }

关键类型:

类型作用
AzureSearchChatDataSource声明 Azure AI Search 数据源(端点、索引名、认证)
DataSourceAuthentication.FromApiKey以 API Key 认证搜索服务
ChatMessageContext从补全结果中提取的消息上下文
ChatCitation模型作答时引用的文档片段,可溯源校验

GetMessageContext()返回的Intent与Citations让 RAG 结果可解释、可审计——这在企业合规场景中是关键能力。#pragma warning disable AOAI001用于抑制对 Azure 扩展 API 的警告。

嵌入:单条与批量

EmbeddingClient用于将文本转为向量,是语义检索与向量库写入的前置步骤。

单条嵌入

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"); }
  • embedding.ToFloats()将嵌入结果转为ReadOnlyMemory<float>,可直接用于余弦相似度计算或写入向量数据库;
  • 批量接口返回OpenAIEmbeddingCollection,每个元素的Index与输入列表顺序对应。

图像生成(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}");

ImageGenerationOptions支持配置:

  • Size:输出尺寸(如W1024xH1024);
  • Quality:生成质量(High/Standard);
  • Style:风格倾向(Vivid/Natural)。

GeneratedImage.ImageUri返回生成的图片地址,可进一步下载或持久化。

音频:转写与语音合成

AudioClient同时覆盖语音转文本(Whisper)与文本转语音(TTS)两条链路。

语音转写(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);
  • ResponseFormat.Verbose返回包含置信度、分段时间戳等丰富信息的转写结果;
  • Language可显式指定音频语言以提升准确率。

文本转语音(TTS)

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());
  • 支持预设音色(如Alloy);
  • SpeedRatio控制语速,ResponseFormat.Mp3指定输出编码;
  • 返回的BinaryData可直接落盘为音频文件。

函数调用(Tools)

函数调用让模型能够决定"何时调用哪个工具",是实现 Agent 化应用的核心机制。技能文档以天气查询为例:

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}"); } }

完整调用流程:

  1. 定义工具:ChatTool.CreateFunctionTool传入函数名、描述与 JSON Schema 参数定义(含required、enum等约束);
  2. 注册工具:将ChatTool加入ChatCompletionOptions.Tools集合;
  3. 接收调用请求:当completion.FinishReason == ChatFinishReason.ToolCalls时,从completion.ToolCalls读取模型要求调用的函数名与参数;
  4. 执行并回传:本地执行函数后,把工具结果以新的消息回合回传给模型,模型据此生成最终答复。

安全提醒:从模型收到的FunctionArguments是字符串 JSON,必须校验后再执行(对应最佳实践第 8 条),防止注入恶意参数。

关键类型速查表

类型用途
AzureOpenAIClient顶层客户端,Azure OpenAI 的入口
ChatClient聊天补全
EmbeddingClient文本嵌入
ImageClient图像生成(DALL-E)
AudioClient音频转写 / TTS
ChatCompletion聊天响应对象
ChatCompletionOptions请求配置(采样参数、工具、数据源等)
StreamingChatCompletionUpdate流式响应分块
ChatMessage消息基类型
SystemChatMessage系统提示词
UserChatMessage用户输入
AssistantChatMessage助手回复
DeveloperChatMessage开发者消息(推理模型)
ChatTool函数 / 工具定义
ChatToolCall工具调用请求

最佳实践清单

技能文档总结了 8 条生产级实践:

  1. 生产环境使用 Entra ID—— 避免 API Key,改用DefaultAzureCredential;
  2. 复用客户端实例—— 顶层客户端创建一次,跨请求共享(天然契合上文的客户端层级设计);
  3. 处理限流—— 对 429 响应实现指数退避重试;
  4. 长回答用流式——CompleteChatStreamingAsync提升用户体验;
  5. 设置合理超时—— 长补全任务需要延长超时时间;
  6. 使用结构化输出—— JSON Schema 保证响应格式一致,便于程序消费;
  7. 监控 Token 用量—— 跟踪completion.Usage控制成本;
  8. 校验工具调用—— 执行前必须校验函数参数。

错误处理模式

SDK 将服务端错误统一包装为RequestFailedException,可按 HTTP 状态码分治:

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}"); }

状态码速查:

状态码含义处理建议
429触发限流退避后重试(可结合指数退避)
400请求参数错误检查消息结构、Schema、部署名
其他服务端错误记录状态码与消息,排查配额/网络

相关 SDK 生态与扩展方向

SDK用途安装命令
Azure.AI.OpenAIAzure OpenAI 客户端(本文主体)dotnet add package Azure.AI.OpenAI
OpenAIOpenAI 兼容客户端dotnet add package OpenAI
Azure.Identity认证(Entra ID)dotnet add package Azure.Identity
Azure.Search.DocumentsAI Search 客户端,用于 RAGdotnet add package Azure.Search.Documents

在此基础上可进一步扩展:

  • 项目级编排:进阶场景可参考 AAS 中的 azure-ai-projects-dotnet 技能,它面向 Azure AI Foundry 项目,覆盖 agents、connections、datasets、deployments、evaluations、indexes 等高层操作,可与本文的底层客户端组合使用;
  • 官方参考资料:本文所用 API 的 NuGet 包页面、Microsoft Learn API 参考、1.0→2.0 迁移指南以及 Quickstart 均可通过官方文档渠道获取(具体版本行为以你安装的 SDK 版本为准,文档记录 2.1.0 为当前稳定版)。

适用边界与注意事项

作为 AAS 技能,本文所述内容的使用边界如下:

  • 仅在任务明确匹配上述能力范围时使用(聊天、嵌入、图像、音频、助手、RAG 等);
  • 输出结果不能替代环境特定环境的验证、测试或专家评审——尤其是涉及生产部署、合规与安全审查的场景;
  • 当所需输入、权限、安全边界或成功标准缺失时,应暂停并请求澄清,而不是猜测默认值继续执行;
  • 认证凭证(API Key 或 Entra ID 权限)属于高敏感信息,在 Agent 工作流中应遵循最小权限原则与密钥管理规范。

结合技能文档与 AAS 目录结构可以推断:本技能适合作为 Agent 在处理 ".NET 项目 + Azure OpenAI 集成" 类任务时的能力说明书,Agent 可按本文的代码骨架快速生成可运行的集成代码,再交由开发者针对具体环境验证。

【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/GitHub_Trending/an/agentic-awesome-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进