penAIClient和AzureOpenAIClient是一个与OpenAI的API进行交互的客户端我们可以指定模型名称调用其GetChatClient方法来获取一个对应的ChatClient对象。虽然名字雷同但是这个ChatClient类型可没有实现IChatClient接口我们需要调用为它定义的扩展方法AsIChatClient来将它转换成一个实现了IChatClient接口的对象。public class AzureOpenAIClient { public override ChatClient GetChatClient(string deploymentName); public override ResponsesClient GetResponsesClient(); } public class OpenAIClient { public virtual ChatClient GetChatClient(string model); public virtual ResponsesClient GetResponsesClient(); } public static class OpenAIClientExtensions { public static IChatClient AsIChatClient(this ChatClient chatClient); public static IChatClient AsIChatClient(this ResponsesClient responseClient, string? defaultModelId null); }前面说过GetChatClient返回的ChatClient对象采用基于文本补全的无状态的Completion API来与模型进行交互如果需要采用有状态的Responses API需要调用GetResponsesClient方法来获取一个ResponsesClient对象。系统依然为ResponsesClient对象定义了一个AsIChatClient的扩展方法来将它转换成一个实现了IChatClient接口的对象。如果使用的是基于Microsoft Foundry的AIProjectClient客户端。由于它的基类是ClientConnectionProviderExtensions我们可以调用其扩展方法GetProjectOpenAIClient得到一个ProjectOpenAIClient对象。由于ProjectOpenAIClient继承自OpenAIClient我们同样可以调用为它定义的AsIChatClient扩展方法来将它转换成一个实现了IChatClient接口的对象。public class AIProjectClient : ClientConnectionProvider public static class ClientConnectionProviderExtensions { public static ProjectOpenAIClient GetProjectOpenAIClient( this ClientConnectionProvider connectionProvider, ProjectOpenAIClientOptions options null); } public class ProjectOpenAIClient : OpenAIClient2. 模拟Agent的ReAct循环接下来我们看看一个利用OpenAIClient创建的IChatClient对象在调用LLM的时候提供的请求和响应内容是什么样子的。下面的代码模拟了一个Agent内部的执行流程ReAct循环我们使用这个Agent来根据苏州的天气给出一些着装建议。我们根据OpenAIClient创建了对应的IChatClient对象整个流程涉及两次针对它的调用。两次调用使用同一个ChatOptions对象我们为这个ChatOptions设置了系统指令你是一个深谙养身之道的时尚顾问并注册了一个用于查询天气的工具GetWeather。using dotenv.net; using Microsoft.Extensions.AI; using OpenAI; using System.ClientModel; using System.ComponentModel; DotEnv.Load(); var model Environment.GetEnvironmentVariable(MODEL)!; var apiKey Environment.GetEnvironmentVariable(API_KEY)!; var openAIUrl Environment.GetEnvironmentVariable(OPENAI_URL)!; var openAIClient new OpenAIClient( credential: new ApiKeyCredential(key: apiKey), options: new OpenAIClientOptions { Endpoint new Uri(openAIUrl) }); var chatClient openAIClient.GetResponsesClient().AsIChatClient(defaultModelId:model); var options new ChatOptions { Instructions 你是一个深谙养身之道的时尚顾问。, Tools [AIFunctionFactory.Create(GetWeather)] }; var message new ChatMessage(role: ChatRole.User, content: 根据苏州的天气给我一些着装建议。); ListChatMessage messages [message]; // First turn: user - assistant (with function call) var response await chatClient.GetResponseAsync( messages: messages, options: options); messages.AddRange(response.Messages); var functionCall response.Messages.Last().Contents.OfTypeFunctionCallContent().Single(); var tool options.Tools.Single(t t.Name functionCall.Name); var toolResult await ((AIFunction)tool).InvokeAsync(new AIFunctionArguments(functionCall.Arguments)); var toolResultMessage new ChatMessage(ChatRole.Tool, [new FunctionResultContent(functionCall.CallId, toolResult)]); messages.Add(toolResultMessage); // Second turn: user - assistant (with tool result) response await chatClient.GetResponseAsync( messages: messages, options: options); Console.WriteLine(response.Messages.Last().Text); static string GetWeather([Description(Location for weather query)] string location) ${location} 当前晴朗气温为25°C。;我们指定查询根据苏州的天气给我一些着装建议和ChatOptions调用IChatClient对象。LLM经过推理任务需要调用工具函数GetWeather来获取苏州的天气信息所以响应消息的内容列表会包含一个FunctionCallContent。在手工将响应消息添加到消息列表中后我们利用FunctionCallContent从注册的工具列表中找到对应的工具。我们将LLM提供的输入参数从FunctionCallContent提取出来后调用工具函数GetWeather得到对应的结果。接下来我们针对工具的返回结果创建一个角色为Tool的ChatMessage对象并将它添加到消息列表中。最后我们再次调用IChatClient对象来获取LLM的最终回复。此时LLM就可以根据工具的返回结果来生成最终如下所示的答案好的我们就顺着苏州此刻**25°C、晴朗**的状态从**养身 时尚**两个角度来搭配。 --- ## ️ 今日苏州着装总思路 **关键词清爽透气、遮阳不闷、早晚微调** 25°C 属于非常舒适的温度但苏州湿度通常不低**选对面料比堆叠衣服更重要**。 --- ## 上装建议 - **首选** - 棉麻衬衫浅色系米白、浅灰、雾蓝 - 薄款针织或天丝T恤 - **养身理由** - 棉麻、天丝透气吸湿减少湿热闷汗对皮肤和气血运行更友好 - **小技巧** - 避免紧身、化纤材质容易“闷火生湿” --- ## 下装建议 - **推荐** - 九分直筒裤 / 轻薄阔腿裤 - 膝下A字裙或真丝半裙 - **颜色** - 浅卡其、灰绿、烟粉色有“降燥感” - **养身点** - 不勒腹、不裹腿有助于脾胃与下肢血液循环 --- ## 鞋履选择 - **白色/浅色透气运动鞋** - **软底乐福鞋 / 平底凉鞋包后跟更养脚** - 避免全天穿完全平底或过硬的鞋对足底经络不友好 --- ## 随身加一件很关键 - **薄开衫 / 防晒衬衫** - 室内空调 早晚微风时护住肩颈 - 肩颈保暖 少落枕、少疲劳 --- ## ️ 配饰与养身小细节 - **帽子或遮阳伞**防晒就是防“耗气” - **天然材质包袋**帆布、草编更符合当下季节气场 - **配色不宜过于浓烈**春夏交替宜“柔不宜躁” --- 如果你愿意告诉我 - 是**上班 / 休闲 / 约会 / 出游** - 或偏**中性、优雅、运动风** 我可以直接帮你搭一整套「今天就能穿出门」的苏州限定穿搭 这是第一轮调用LLM提供的请求和得到的响应内容{ model: gpt-5.2-chat, tools: [ { type: function, name: _Main_g_GetWeather_0_1, description: , parameters: { type: object, required: [ location ], properties: { location: { description: Location for weather query, type: string } }, additionalProperties: false }, strict: null } ], input: [ { type: message, role: user, content: [ { type: input_text, text: 根据苏州的天气给我一些着装建议。 } ] } ], instructions: 你是一个深谙养身之道的时尚顾问。 }{ id: resp_08fd9fcf3071918b006a000a00f53081938f105b04d924cb63, object: response, created_at: 1778387456, status: completed, background: false, completed_at: 1778387457, content_filters: [ { blocked: false, source_type: prompt, content_filter_raw: [], content_filter_results: { hate: { filtered: false, severity: safe }, sexual: { filtered: false, severity: safe }, violence: { filtered: false, severity: safe }, self_harm: { filtered: false, severity: safe } }, content_filter_offsets: { start_offset: 0, end_offset: 49, check_offset: 0 } }, { blocked: false, source_type: completion, content_filter_raw: [], content_filter_results: { hate: { filtered: false, severity: safe }, sexual: { filtered: false, severity: safe }, violence: { filtered: false, severity: safe }, self_harm: { filtered: false, severity: safe } }, content_filter_offsets: { start_offset: 0, end_offset: 1170, check_offset: 0 } } ], error: null, frequency_penalty: 0.0, incomplete_details: null, instructions: 你是一个深谙养身之道的时尚顾问。, max_output_tokens: null, max_tool_calls: null, model: gpt-5.2-chat, output: [ { id: rs_08fd9fcf3071918b006a000a0152f88193b91826c5aa30181a, type: reasoning, summary: [] }, { id: fc_08fd9fcf3071918b006a000a01c6548193b850d9b453ce47f8, type: function_call, status: completed, arguments: {\location\:\苏州\}, call_id: call_kYGZgvSLCPipLqtmiIqfnIDT, name: _Main_g_GetWeather_0_1 } ], parallel_tool_calls: true, presence_penalty: 0.0, previous_response_id: null, prompt_cache_key: null, prompt_cache_retention: null, reasoning: { effort: medium, summary: null }, safety_identifier: null, service_tier: default, store: true, temperature: 1.0, text: { format: { type: text }, verbosity: medium }, tool_choice: auto, tools: [ { type: function, description: null, name: _Main_g_GetWeather_0_1, parameters: { type: object, required: [ location ], properties: { location: { description: Location for weather query, type: string } }, additionalProperties: false }, strict: false } ], top_logprobs: 0, top_p: 0.85, truncation: disabled, usage: { input_tokens: 83, input_tokens_details: { cached_tokens: 0 }, output_tokens: 43, output_tokens_details: { reasoning_tokens: 0 }, total_tokens: 126 }, user: null, metadata: {} }这是第二轮调用LLM提供的请求和得到的响应内容{ model: gpt-5.2-chat, tools: [ { type: function, name: _Main_g_GetWeather_0_1, description: , parameters: { type: object, required: [ location ], properties: { location: { description: Location for weather query, type: string } }, additionalProperties: false }, strict: null } ],