推荐使用 OpenAI.ObjectModels SDK 而非手写 HttpClient,因其已内置重试、超时、SSE 流解析、错误码映射及函数调用支持,避免 429 限流、角色字段拼错、Content-Type 缺失、null content 异常等高频问题。

直接用 HttpClient 调 OpenAI API 是可行的,但不推荐手写——容易漏掉重试、超时、流式响应解析、错误码映射等关键逻辑;真正省心又健壮的做法是用官方支持的 OpenAI.ObjectModels(社区主流 SDK),它已适配 v1 接口且持续维护。
为什么别自己封装 HttpClient 调 /v1/chat/completions
看似几行代码就能发请求,实际踩坑密集:
-
429 Too Many Requests时没指数退避,直接触发限流熔断 -
stream: true返回的是 chunked SSE 流,手动解析data: {...}行易丢帧或阻塞 -
system/user/assistant角色字段拼错成role小写以外的写法,API 静默忽略整条消息 - 没设
Content-Type: application/json或Authorization: Bearer sk-xxx,返回401 Unauthorized却误判为 key 过期 - 响应中
choices[0].message.content可能为null(比如函数调用场景),空引用异常频发
用 OpenAI.ObjectModels 发起标准 chat 请求
这个包不是官方出品但被广泛采用,NuGet 安装后即可开干,核心对象是 OpenAIService:
<pre class="brush:php;toolbar:false;">var openAiService = new OpenAIService(new OpenAiOptions()
{
ApiKey = "sk-xxx",
Organization = "org-xxx", // 可选
BaseUrl = "https://api.openai.com/v1" // 可替换为代理地址
});
<p>var completionRequest = new ChatCompletionCreateRequest()
{
Model = Models.Chat.Gpt_4_Turbo,
Messages = new List<chatmessage>
{
new ChatMessage(ChatRole.System, "你是一个严谨的代码助手"),
new ChatMessage(ChatRole.User, "用 C# 写一个计算斐波那契第 10 项的递归方法")
},
Temperature = 0.3
};</chatmessage></p><p>var response = await openAiService.ChatCompletion.CreateCompletion(completionRequest);
if (response.Successful)
{
Console.WriteLine(response.Choices.First().Message.Content);
}
else
{
Console.WriteLine($"Error: {response.Error?.Message}");
}</p>
注意点:
Models.Chat.Gpt_4_Turbo 是枚举值,比硬写字符串"gpt-4-turbo"更安全-
response.Successful已内部处理 HTTP 状态码和 body 错误字段,不用再手动判断response.StatusCode == 200 - 若要流式响应,把
CreateCompletion换成CreateCompletionStreaming,它返回IAsyncEnumerable
处理函数调用(Function Calling)的典型陷阱
OpenAI 的函数调用不是真执行函数,而是让模型返回 JSON 格式的调用意图,你得自己解析并 dispatch。SDK 支持但默认不启用:
- 必须显式设置
Functions列表,并确保Function对象的Name和Description符合 OpenAI 规范(不能含空格、特殊字符) - 模型返回的
FinishReason是"function_call",此时Message.Content为空,真正参数在Message.FunctionCall.Arguments字符串里(需JsonSerializer.Deserialize) - 调用完真实函数后,要把结果以
role: "function"的消息重新塞进上下文,再发一次请求——SDK 不自动做这步 - 别把
Function的Parameters写成 C# 类型,它必须是 OpenAPI 3.0 兼容的 JSON Schema 对象(如{ "type": "object", "properties": { "city": { "type": "string" } } })
最常被忽略的是:模型返回的 function_call.arguments 是字符串而非对象,且可能格式不合法(比如多逗号、单引号),直接 JsonConvert.DeserializeObject 会崩;务必加 try-catch 并 fallback 到日志告警。










