OpenAI 接口
OpenAI 接口经历了 Completions、Chat Completions、Responses 三个版本,目前主流还是 Chat Completions。
OpenAI 的 Responses 接口协议版本出现已经很久了,不过国内很多模型厂商依然还是 Chat Completions 接口格式,需要通过协议转换后才能在 Codex 使用。所以有些新手或非程序员使用国产模型时可能会有困惑,配置到 Codex 会报错,因为目前最新的 Codex 只支持 Responses 接口格式。
第一代 Completions(/v1/completions,传统文本补全)最早 GPT-3 时代主力接口,单轮续写,入参 prompt,无多轮对话结构,现已标记为Legacy 遗留接口,仅兼容 instruct 类老模型(text-davinci 系列)。不建议再使用。
第二代:Chat Completions(/v1/chat/completions,对话补全)2023 年推出,适配 GPT-3.5-turbo/GPT-4,用 messages 多轮对话结构,成为行业标准,至今仍可正常使用,目前还是主流。
第三代:Responses(/v1/responses,智能体统一接口)2025 年 3 月发布,定位为下一代统一接口,整合对话、工具调用、联网检索、文件检索、多模态有状态会话,是面向 Agent 场景的升级超集,OpenAI 建议新项目优先使用,计划 2026 中期逐步弱化旧接口。
你可以通过社区一些人整理的 OpenAI 的接口文档,了解接口的详细参数:
https://www.postman.com/devrel/openai/folder/euam005/openai-api-capabilities?sideView=agentMode
https://shalk.github.io/openai-swagger-ui/
下面是 OpenAI-compatible 和 Anthropic-compatible 的能力对比,其中 /v1/message 是 Anthropic 接口格式,其它两个是 OpenAI 格式。
| Feature | /v1/responses | /v1/chat/completions | /v1/messages |
|---|---|---|---|
| Streaming 流式输出 | ✅ | ✅ | ✅ |
| Stateful chat 有状态会话 | ✅ | ❌ | ❌ |
| Remote MCPs 远程 MCP 服务 | ✅ | ❌ | ❌ |
| Custom tools 自定义工具(函数调用) | ✅ | ✅ | ✅ |
| Include assistant messages in the request 请求里带 assistant 历史消息 | ✅ | ✅ | ✅ |
表格信息来源是:LM Studio 文档。
接口格式对比
下面是最传统的 Completions 接口,接口也是支持流式返回的,主要特点是 /v1/completions 接口只有 prompt 参数,没有 message 参数,所以需要自己拼接上下文到 prompt 字段。
curl --location 'http://127.0.0.1:1234/v1/completions" \
-H "Authorization: Bearer $NEWAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen/qwen3.5-9b",
"prompt": "世界七大奇迹.",
"stream": true
}'
当然,接口是支持流式返回的:

最后正常结束返回的结构:
{
"id": "cmpl-f02hm1ui4d8q2d36qls9c",
"object": "text_completion",
"created": 1783647489,
"model": "qwen/qwen3.5-9b",
"choices": [
{
"index": 0,
"text": "",
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 4,
"completion_tokens": 524,
"total_tokens": 528
},
"stats": {
"total_draft_tokens_count": 0,
"accepted_draft_tokens_count": 0,
"rejected_draft_tokens_count": 0,
"ignored_draft_tokens_count": 0
}
}
目前最常用的 Chat Completions 接口,请求体里用 messages 数组描述多轮对话:
curl --location 'http://127.0.0.1:1234/v1/chat/completions' \
--header 'Authorization: Bearer $NEWAPI_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "qwen/qwen3.5-9b",
"stream": true,
"messages": [
{
"role": "system",
"content": "以简短的话回答"
},
{
"role": "user",
"content": "世界七大奇迹"
},
{
"role": "user",
"content": "属于中国的"
}
]
}'
返回结构大致如下:
{
"id": "chatcmpl-2rdnm653viwc3f93ea5i",
"object": "chat.completion.chunk",
"created": 1783647927,
"model": "qwen/qwen3.5-9b",
"system_fingerprint": "qwen/qwen3.5-9b",
"choices": [
{
"index": 0,
"delta": {
"content": "长城"
},
"logprobs": null,
"finish_reason": null
}
]
}
而第三代 Responses 接口则用 input 而不是 messages,并且原生支持推理强度、状态续接等能力,也就是说服务器会保存对话记录,后续只需要推送当前最新用户问题即可,不需要拼接提交完整的对话历史。
curl --location 'http://127.0.0.1:1234/v1/responses' \
--header 'Authorization: Bearer $NEWAPI_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "qwen/qwen3.5-9b",
"stream": true,
"input": "以简短的话回答,世界七大奇迹",
"reasoning": {
"effort": "medium"
}
}'
流式返回第一条对话会带有 responses.id:
{
"type": "response.created",
"response": {
"id": "resp_ab7c283675e4e46e3c0c04826db352593b0637bfdc747895",
"object": "response",
...
后续在 previous_response_id 参数贴上 responses.id ,无需拼接完整上下文对话历史。
curl --location 'http://127.0.0.1:1234/v1/responses' \
--header 'Authorization: Bearer $NEWAPI_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "qwen/qwen3.5-9b",
"stream": true,
"input": "只属于中国的",
"previous_response_id": "resp_ab7c283675e4e46e3c0c04826db352593b0637bfdc747895",
"reasoning": {
"effort": "medium"
}
}'
或者直接以无状态的形式,将完整上下文内容贴到 input 中,input 既可以单条,也可以设置为对话历史数组,可包含消息、推理、工具调用等不同类型。
{
"model": "qwen/qwen3.5-9b",
"stream": true,
"input": [
{
"type": "message",
"role": "system",
"content": "简单回答"
},
{
"type": "message",
"role": "user",
"content": "世界七大奇迹"
},
{
"type": "message",
"role": "user",
"content": "属于中国的"
}
],
"reasoning": {
"effort": "medium"
}
}
Responses 格式的接口,可以更加方便地通过 type 字段和其它字段,有效状态判断 Agent 执行状态和编程逻辑,提升用户交互体验,对开发者更加友好,对于设计联网、检索、多轮工具链、推理等状态和能力交互,也很方便。

对于原生 API 接口的介绍就到此为止,本系列教程主要讲解原理和 Agent 逻辑,后续以 SDK 为主,不详细介绍接口的具体参数和能力,感兴趣的读者请移步到官方文档了解。
SDK 接入准备
本文所有示例基于官方 .NET SDK,SDK 源码和 nuget 地址:
https://github.com/openai/openai-dotnet
https://www.nuget.org/packages/OpenAI
笔者当前使用版本是 OpenAI 2.12.0。
OpenAI 2.13.0 在使用本地模型,如 LM Studio 时,流式输出会有 bug。
先看看 OpenAI 官方的 SDK 有哪些内容:
| 命名空间 | 客户端类 | 用途 |
|---|---|---|
OpenAI.Chat | ChatClient | 对话补全(Chat Completions) |
OpenAI.Responses | ResponsesClient | 第三代 Responses 接口、深度推理 |
OpenAI.Embeddings | EmbeddingClient | 向量化 |
OpenAI.Audio | AudioClient | 语音合成 / 语音识别 |
OpenAI.Images | ImageClient | 图像生成 |
OpenAI.Models | OpenAIModelClient | 列出 / 查询模型 |
OpenAI.Files | OpenAIFileClient | 文件上传与管理 |
创建客户端
我们最终用来请求服务器的是 ChatClient、EmbeddingClient 等客户端类型对象,默认内置地址是连到官方 https://api.openai.com 的。
using OpenAI.Chat;
ChatClient client = new(model: "gpt-4o", apiKey: "你的密钥");
如果要自定义服务器地址,就需要通过 OpenAIClientOptions.Endpoint 指定地址:
using OpenAI.Chat;
using System.ClientModel;
ChatClient client = new(
model: "gpt-4o",
credential: new ApiKeyCredential("你的密钥"),
options: new OpenAIClientOptions { Endpoint = new Uri("https://你的服务器地址/v1") });
如果要进行比较多的其它操作,可以使用 OpenAIClient 工厂模式在相同的端点下创建其它类型的客户端,可以同时使用对话、向量化、语音等功能。
using OpenAI;
OpenAIClient factory = new(
credential: new ApiKeyCredential("你的密钥"),
options: new OpenAIClientOptions { Endpoint = new Uri("https://你的服务器地址/v1") });
// 工厂方法会自动复用上面的密钥和 Endpoint
ChatClient chat = factory.GetChatClient("qwen/qwen3.5-9b");
EmbeddingClient embedding = factory.GetEmbeddingClient("text-embedding-3-small");
注意 ChatClient、EmbeddingClient、AudioClient、ImageClient 在创建时就要绑定模型名称,因为它们的所有请求都会带上这个模型,而 OpenAIModelClient、OpenAIFileClient 这类不绑定模型。
记录日志
记录 SDK 的原生 http 请求是非常重要的一个事情,有助于帮助我们理解接口内容和诊断问题,以下 DelegatingHandler 可以拦截 http 请求,输出原始请求和响应内容,但是因为这是拦截,使用时会导致流式变成了同步等待,所以只有在调试情况下建议使用。
笔者还没有找到 DelegatingHandler 记录日志还能保持流式返回的方案,如果你有,欢迎微信讨论。
public sealed class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler? inner = null) : base(inner ?? new HttpClientHandler()) { }
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken ct)
{
if (request.Content != null)
{
await request.Content.LoadIntoBufferAsync(ct);
Console.WriteLine($"--> {request.Method} {request.RequestUri}");
Console.WriteLine(await request.Content.ReadAsStringAsync(ct));
}
var response = await base.SendAsync(request, ct);
if (response.Content != null)
{
var origin = await response.Content.ReadAsStreamAsync(ct);
var ms = new MemoryStream();
await origin.CopyToAsync(ms, ct);
await origin.DisposeAsync();
Console.WriteLine($"<-- {(int)response.StatusCode}");
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
ms.Position = 0;
response.Content = new StreamContent(ms);
response.Content.Headers.ContentType =
new MediaTypeHeaderValue(response.Content.Headers.ContentType?.MediaType ?? "application/json");
}
return response;
}
}
使用拦截器:
OpenAIClient factory = new(
credential: new ApiKeyCredential("1234"),
options: new OpenAIClientOptions
{
Endpoint = new Uri("http://127.0.0.1:1234/v1"),
Transport = new HttpClientPipelineTransport(new HttpClient(new LoggingHandler()))
});
列出所有模型
GET /v1/models 用来查询当前账号可用哪些模型。它返回一个 data 数组,每项包含模型 id、创建时间和归属:
curl "https://你的服务器地址/v1/models" \
-H "Authorization: Bearer $NEWAPI_API_KEY"
返回示例:
{
"data": [
{
"id": "qwen/qwen3.5-9b",
"object": "model",
"owned_by": "organization_owner"
},
{
"id": "google/gemma-4-12b",
"object": "model",
"owned_by": "organization_owner"
}
],
"object": "list"
}

之所以介绍这个接口是有原因的,因为我们在设计 Agent 平台时,往往会碰到对接不同厂商渠道和模型,如果让用户手动填写模型名称,那就会很麻烦,所以 MaomiAgent 做了一个很好用的接入模型功能。

对应到 SDK,使用 OpenAIModelClient 即可,它返回的 OpenAIModelCollection 本身是一个只读集合,可以直接 foreach 遍历。
using OpenAI;
using OpenAI.Models;
OpenAIModelClient client = new(
credential: new System.ClientModel.ApiKeyCredential("你的密钥"),
options: new OpenAIClientOptions { Endpoint = new Uri("http://127.0.0.1:1234/v1") });
OpenAIModelCollection models = client.GetModels();
foreach (OpenAIModel model in models)
{
Console.WriteLine($"模型: {model.Id} 归属: {model.OwnedBy} 创建: {model.CreatedAt:yyyy-MM-dd}");
}

至于嵌入、图片生成这些接口,这里就不再讨论。