openai-dotnet
openai-dotnet 是 OpenAI 官方推出的 .NET 开发库,旨在帮助开发者轻松地在 .NET 应用程序中集成 OpenAI 的强大功能。它封装了复杂的 REST API 调用细节,让程序员无需处理繁琐的 HTTP 请求和 JSON 解析,即可直接通过简洁的代码调用聊天补全、图像生成、语音转录、文本嵌入以及智能助手等核心服务。
对于使用 C# 或 F# 进行开发的工程师而言,openai-dotnet 解决了手动对接 API 时容易出现的认证管理、错误重试及数据序列化等痛点。该库由 OpenAI 与微软合作,基于标准的 OpenAPI 规范自动生成,确保了接口更新的及时性与稳定性。其技术亮点包括原生支持异步编程模型、流式输出(Streaming)、函数调用(Function Calling)以及结构化数据输出,同时完美兼容依赖注入模式,便于构建可测试、高可用的企业级应用。此外,它还特别优化了对 Azure OpenAI 服务的支持,方便云原生架构的平滑迁移。无论是初创团队的快速原型开发,还是大型企业系统的智能化升级,openai-dotnet 都是 .NET 生态中接入大模型能力的首选桥梁。
使用场景
某电商企业的 .NET 开发团队正在构建一个智能客服系统,需要让后端服务实时调用大模型处理用户咨询并生成个性化回复。
没有 openai-dotnet 时
- 开发人员必须手动编写复杂的 HTTP 请求代码来处理认证头、序列化 JSON 负载及解析响应,极易因字段拼写错误导致运行时异常。
- 实现流式输出(Streaming)以改善用户体验时,需底层处理 SSE 事件流解析,代码冗长且难以维护异步状态。
- 缺乏内置的重试机制和错误分类,网络波动或限流时服务容易直接崩溃,稳定性完全依赖人工兜底。
- 集成函数调用(Function Calling)或结构化输出时,需自行定义复杂的 Schema 映射逻辑,开发周期被大幅拉长。
- 单元测试困难,无法轻松模拟 API 客户端行为,导致每次修改都需消耗真实的 API 额度进行验证。
使用 openai-dotnet 后
- 通过强类型的
OpenAIClient和ChatClient类,仅需几行代码即可完成认证与对话调用,彻底告别手写 HTTP 底层逻辑。 - 原生支持异步流式接口,开发者可直接遍历响应内容块,轻松实现打字机效果的实时回复,代码简洁清晰。
- 库内建自动重试策略与详细的异常体系,能智能识别瞬态故障并恢复,显著提升了生产环境的鲁棒性。
- 利用泛型约束和内置辅助方法,轻松定义函数参数与结构化输出格式,将复杂的功能集成时间从数天缩短至数小时。
- 提供完善的 Mock 支持,允许在测试环境中注入假客户端,既节省了 API 成本又实现了高效的自动化测试。
openai-dotnet 将繁琐的 API 交互转化为直观的 .NET 对象操作,让开发者能专注于业务逻辑而非通信细节,极大加速了 AI 应用在微软技术栈中的落地。
运行环境要求
- Windows
- macOS
- Linux
未说明
未说明

快速开始
OpenAI .NET API 库
OpenAI .NET 库为 .NET 应用程序提供了便捷的 OpenAI REST API 访问接口。该库是根据我们的 OpenAPI 规范,并与 Microsoft 合作生成的。
目录
- 快速入门
- 使用客户端库
- 如何使用依赖注入
- 如何使用带有流式传输的聊天完成
- 如何使用带有工具和函数调用的聊天完成
- 如何使用带有结构化输出的聊天完成
- 如何使用带有音频的聊天完成
- 如何使用带有流式传输和推理的响应
- 如何使用带有文件搜索的响应
- 如何使用带有网络搜索的响应
- 如何生成文本嵌入
- 如何生成图像
- 如何转录音频
- 如何使用带有检索增强生成 (RAG) 的助手
- 如何使用带有流式传输和视觉功能的助手
- 如何与 Azure OpenAI 集成
- 高级场景
快速入门
先决条件
要调用 OpenAI REST API,您需要一个 API 密钥。要获取 API 密钥,请先 创建一个新的 OpenAI 账户 或 登录。然后,前往 API 密钥页面,选择“创建新密钥”,并可为密钥命名。请务必将您的 API 密钥保存在安全的地方,并且不要与任何人共享。
安装 NuGet 包
通过 IDE 或在 .NET CLI 中运行以下命令,将客户端库添加到您的 .NET 项目中:
dotnet add package OpenAI
请注意,下面包含的代码示例是使用 .NET 10 编写的。OpenAI .NET 库兼容所有 .NET Standard 2.0 应用程序,但本文档中部分代码示例所使用的语法可能依赖于较新的语言特性。
使用客户端库
该库的完整 API 可以在 OpenAI.netstandard2.0.cs 文件中找到,并且有许多 代码示例 可供参考。例如,以下代码片段展示了聊天完成 API 的基本用法:
ChatClient client = new(model: "gpt-5.1", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatCompletion completion = client.CompleteChat("说‘这是一个测试。’");
Console.WriteLine($"[助理]: {completion.Content[0].Text}");
虽然您可以直接将 API 密钥作为字符串传递,但我们强烈建议将其存储在安全位置,并像上面示例那样通过环境变量或配置文件来访问,以避免将其存储在源代码控制中。
使用自定义基础 URL 和 API 密钥
如果您需要连接到其他 API 端点(例如代理或自托管的 OpenAI 兼容大模型),可以使用 ApiKeyCredential 和 OpenAIClientOptions 指定自定义的基础 URL 和 API 密钥:
ChatClient client = new(
model: "MODEL_NAME",
credential: new ApiKeyCredential(Environment.GetEnvironmentVariable("OPENAI_API_KEY")),
options: new OpenAIClientOptions()
{
Endpoint = new Uri("https://YOUR_BASE_URL")
});
将 MODEL_NAME 替换为您的模型名称,将 BASE_URL 替换为您的端点 URI。这在使用 OpenAI 兼容 API 或自定义部署时非常有用。
命名空间组织
该库按照 OpenAI REST API 中的功能区域划分为不同的命名空间。每个命名空间都包含相应的客户端类。
| 命名空间 | 客户端类 |
|---|---|
OpenAI.Assistants |
AssistantClient |
OpenAI.Audio |
AudioClient |
OpenAI.Batch |
BatchClient |
OpenAI.Chat |
ChatClient |
OpenAI.Embeddings |
EmbeddingClient |
OpenAI.Evals |
EvaluationClient |
OpenAI.FineTuning |
FineTuningClient |
OpenAI.Files |
OpenAIFileClient |
OpenAI.Images |
ImageClient |
OpenAI.Models |
OpenAIModelClient |
OpenAI.Moderations |
ModerationClient |
OpenAI.Realtime |
RealtimeClient |
OpenAI.Responses |
ResponsesClient |
OpenAI.VectorStores |
VectorStoreClient |
使用异步 API
每个执行同步 API 调用的客户端方法,在同一客户端类中都有对应的异步变体。例如,ChatClient 的 CompleteChat 方法的异步变体是 CompleteChatAsync。要使用异步版本重写上述调用,只需对相应的异步方法使用 await 即可:
ChatCompletion completion = await client.CompleteChatAsync("说‘这是一个测试。’");
使用 OpenAIClient 类
除了上述命名空间之外,还有一个父级的 OpenAI 命名空间:
using OpenAI;
该命名空间包含 OpenAIClient 类,当您需要同时使用多个功能模块的客户端时,它能提供一些便利。具体来说,您可以使用此客户端的实例来创建其他客户端,并让它们共享相同的实现细节,这样可能会更高效。
您可以通过指定所有客户端都将用于身份验证的 API 密钥来创建一个 OpenAIClient:
OpenAIClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
接下来,例如要创建一个 AudioClient 实例,您可以调用 OpenAIClient 的 GetAudioClient 方法,并传入 AudioClient 将使用的 OpenAI 模型,就像直接使用 AudioClient 构造函数一样。如果需要,您还可以创建更多相同类型的客户端,以针对不同的模型。
AudioClient ttsClient = client.GetAudioClient("tts-1");
AudioClient whisperClient = client.GetAudioClient("whisper-1");
如何使用依赖注入
OpenAI 客户端是线程安全的,可以安全地作为单例注册到 ASP.NET Core 的依赖注入容器中。这样做可以最大化资源效率并重用 HTTP 连接。
在您的 Program.cs 中将 ChatClient 注册为单例:
builder.Services.AddSingleton<ChatClient>(serviceProvider =>
{
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
var model = "gpt-5.1";
return new ChatClient(model, apiKey);
});
然后在您的控制器或服务中注入并使用该客户端:
[ApiController]
[Route("api/[controller]")]
public class ChatController : ControllerBase
{
private readonly ChatClient _chatClient;
public ChatController(ChatClient chatClient)
{
_chatClient = chatClient;
}
[HttpPost("complete")]
public async Task<IActionResult> CompleteChat([FromBody] string message)
{
ChatCompletion completion = await _chatClient.CompleteChatAsync(message);
return Ok(new { response = completion.Content[0].Text });
}
}
如何使用流式聊天补全
当您请求聊天补全时,默认行为是服务器会先完整生成结果,然后再将其作为一个响应一次性返回。因此,对于较长的聊天补全,可能需要等待几秒钟才能收到服务器的回复。为了解决这个问题,OpenAI REST API 支持在生成过程中逐步流式返回部分结果,这样您可以在补全尚未完成时就开始处理其开头部分。
客户端库提供了一种便捷的方式来处理流式聊天补全。如果您想用流式方式重写上一节中的示例,而不是调用 ChatClient 的 CompleteChat 方法,而是改用 CompleteChatStreaming 方法:
CollectionResult<StreamingChatCompletionUpdate> completionUpdates = client.CompleteChatStreaming("Say 'this is a test.'");
请注意,返回值是一个 CollectionResult<StreamingChatCompletionUpdate> 实例,您可以对其进行枚举,以便在流式响应块到达时逐个处理:
CollectionResult<StreamingChatCompletionUpdate> completionUpdates = client.CompleteChatStreaming("Say 'this is a test.'");
Console.Write($"[ASSISTANT]: ");
foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
{
if (completionUpdate.ContentUpdate.Count > 0)
{
Console.Write(completionUpdate.ContentUpdate[0].Text);
}
}
或者,您也可以异步地执行此操作:调用 CompleteChatStreamingAsync 方法获取一个 AsyncCollectionResult<StreamingChatCompletionUpdate>,然后使用 await foreach 来对其进行枚举:
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates = client.CompleteChatStreamingAsync("Say 'this is a test.'");
Console.Write($"[ASSISTANT]: ");
await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
{
if (completionUpdate.ContentUpdate.Count > 0)
{
Console.Write(completionUpdate.ContentUpdate[0].Text);
}
}
如何在聊天补全中使用工具和函数调用
在本示例中,您有两个函数。第一个函数可以获取用户的当前地理位置(例如,通过轮询用户设备的位置服务 API),而第二个函数可以根据给定位置查询天气情况(例如,通过调用某个第三方天气服务的 API)。您希望模型能够在认为有必要获取这些信息以响应用户请求时,调用这些函数,从而生成聊天补全。为了便于说明,我们考虑以下代码:
static string GetCurrentLocation()
{
// 在此处调用位置 API。
return "旧金山";
}
static string GetCurrentWeather(string location, string unit = "celsius")
{
// 在此处调用天气 API。
return $"31 {unit}";
}
首先,使用静态的 CreateFunctionTool 方法创建两个 ChatTool 实例来描述每个函数:
ChatTool getCurrentLocationTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentLocation),
functionDescription: "获取用户的当前位置"
);
ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentWeather),
functionDescription: "获取给定位置的当前天气",
functionParameters: BinaryData.FromBytes("""
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市和州,例如波士顿,马萨诸塞州"
},
"unit": {
"type": "string",
"enum": [ "celsius", "fahrenheit" ],
"description": "要使用的温度单位。可根据指定位置推断。"
}
},
"required": [ "location" ]
}
"""u8.ToArray())
);
接下来,创建一个 ChatCompletionOptions 实例,并将这两个工具添加到其 Tools 属性中。您将在调用 ChatClient 的 CompleteChat 方法时,将 ChatCompletionOptions 作为参数传递。
List<ChatMessage> messages =
[
new UserChatMessage("今天天气怎么样?"),
];
ChatCompletionOptions options = new()
{
Tools = { getCurrentLocationTool, getCurrentWeatherTool },
};
当生成的 ChatCompletion 的 FinishReason 属性等于 ChatFinishReason.ToolCalls 时,这意味着模型已经确定在助手能够做出适当回应之前,必须调用一个或多个工具。在这种情况下,您需要先调用 ChatCompletion 中 ToolCalls 指定的函数,然后再次调用 ChatClient 的 CompleteChat 方法,并将函数的结果作为附加的 ChatRequestToolMessage 传递。根据需要重复此过程。
bool requiresAction;
do
{
requiresAction = false;
ChatCompletion completion = client.CompleteChat(messages, options);
switch (completion.FinishReason)
{
case ChatFinishReason.Stop:
{
// 将助手消息添加到对话历史中。
messages.Add(new AssistantChatMessage(completion));
break;
}
case ChatFinishReason.ToolCalls:
{
// 首先,将包含工具调用的助手消息添加到对话历史中。
messages.Add(new AssistantChatMessage(completion));
// 然后,为每个已解析的工具调用添加一条新的工具消息。
foreach (ChatToolCall toolCall in completion.ToolCalls)
{
switch (toolCall.FunctionName)
{
case nameof(GetCurrentLocation):
{
string toolResult = GetCurrentLocation();
messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
break;
}
case nameof(GetCurrentWeather):
{
// 模型希望用于调用函数的参数是以字符串形式表示的 JSON 对象,基于工具定义中指定的模式。需要注意的是,模型可能会“幻觉”出无效的参数。因此,在调用函数之前,务必进行适当的解析和验证。
using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments);
bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
if (!hasLocation)
{
throw new ArgumentNullException(nameof(location), "缺少位置参数。");
}
string toolResult = hasUnit
? GetCurrentWeather(location.GetString(), unit.GetString())
: GetCurrentWeather(location.GetString());
messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
break;
}
default:
{
// 处理其他未预期的调用。
throw new NotImplementedException();
}
}
}
requiresAction = true;
break;
}
case ChatFinishReason.Length:
throw new NotImplementedException("由于 MaxTokens 参数或令牌限制超出而导致模型输出不完整。");
case ChatFinishReason.ContentFilter:
throw new NotImplementedException("因内容过滤器标记而省略了部分内容。");
case ChatFinishReason.FunctionCall:
throw new NotImplementedException("已被工具调用取代,现已弃用。");
default:
throw new NotImplementedException(completion.FinishReason.ToString());
}
} while (requiresAction);
如何在聊天补全中使用结构化输出
自 gpt-4o-mini、gpt-4o-mini-2024-07-18 和 gpt-4o-2024-08-06 模型快照起,聊天补全和助手 API 中的顶级响应内容以及工具调用均支持结构化输出。有关该功能的信息,请参阅 结构化输出指南。
要使用结构化输出来约束聊天补全文本内容,请按照以下示例设置适当的 ChatResponseFormat:
List<ChatMessage> messages =
[
new UserChatMessage("如何解方程 8x + 7 = -23?"),
];
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 = client.CompleteChat(messages, options);
using JsonDocument structuredJson = JsonDocument.Parse(completion.Content[0].Text);
Console.WriteLine($"最终答案:{structuredJson.RootElement.GetProperty("final_answer")}");
Console.WriteLine("推理步骤:");
foreach (JsonElement stepElement in structuredJson.RootElement.GetProperty("steps").EnumerateArray())
{
Console.WriteLine($" - 解释:{stepElement.GetProperty("explanation")}");
Console.WriteLine($" 结果:{stepElement.GetProperty("output")}");
}
如何在聊天补全中使用音频
自 gpt-4o-audio-preview 模型起,聊天补全可以处理音频输入和输出。
此示例演示:
- 使用支持的
gpt-4o-audio-preview模型配置客户端 - 在聊天补全请求中提供用户音频输入
- 请求聊天补全操作返回模型音频输出
- 从
ChatCompletion实例中获取音频输出 - 将之前的音频输出用作
ChatMessage对话历史
// 聊天音频输入和输出仅在特定模型上受支持,自 gpt-4o-audio-preview 开始
ChatClient client = new("gpt-5.1", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
// 通过将音频内容部分添加到用户消息中,可向请求提供输入音频
string audioFilePath = Path.Combine("Assets", "realtime_whats_the_weather_pcm16_24khz_mono.wav");
byte[] audioFileRawBytes = File.ReadAllBytes(audioFilePath);
BinaryData audioData = BinaryData.FromBytes(audioFileRawBytes);
List<ChatMessage> messages =
[
new UserChatMessage(ChatMessageContentPart.CreateInputAudioPart(audioData, ChatInputAudioFormat.Wav)),
];
// 通过配置 `ChatCompletionOptions` 以包含适当的 `ResponseModalities` 值及相应的 `AudioOptions`,即可请求输出音频。
ChatCompletionOptions options = new()
{
ResponseModalities = ChatResponseModalities.Text | ChatResponseModalities.Audio,
AudioOptions = new(ChatOutputAudioVoice.Alloy, ChatOutputAudioFormat.Mp3),
};
ChatCompletion completion = client.CompleteChat(messages, options);
void PrintAudioContent()
{
if (completion.OutputAudio is ChatOutputAudio outputAudio)
{
Console.WriteLine($"响应音频转录:{outputAudio.Transcript}");
string outputFilePath = $"{outputAudio.Id}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
outputFileStream.Write(outputAudio.AudioBytes);
}
Console.WriteLine($"响应音频已写入文件:{outputFilePath}");
Console.WriteLine($"可在后续请求中有效至:{outputAudio.ExpiresAt}");
}
}
PrintAudioContent();
// 若要引用之前的音频输出,可从先前的 `ChatCompletion` 创建助理消息,使用先前的响应内容部分,或使用 `ChatMessageContentPart.CreateAudioPart(string)` 手动实例化一个部分。
messages.Add(new AssistantChatMessage(completion));
messages.Add("你能用海盗口吻再说一遍吗?");
completion = client.CompleteChat(messages, options);
PrintAudioContent();
流式传输具有高度并行性:StreamingChatCompletionUpdate 实例可能包含一个 OutputAudioUpdate,其中可能包括以下内容:
- 流式音频内容的
Id,一旦流式响应完成,后续的AssistantChatMessage实例可通过ChatAudioReference引用该Id;此值可能出现在多个StreamingChatCompletionUpdate实例中,但只要存在,其值始终相同。 ExpiresAt值,用于描述Id在后续请求中不再适用于ChatAudioReference的时间;此值通常只出现一次,在最后一个StreamingOutputAudioUpdate中。- 递增的
TranscriptUpdate和/或AudioBytesUpdate值,这些值可以逐步接收,拼接后即可形成整个响应的完整音频转录和音频输出;此类更新通常会多次出现。
如何结合流式传输和推理使用响应
ResponsesClient client = new(
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
CreateResponseOptions options = new()
{
Model = "gpt-5.1",
ReasoningOptions = new ResponseReasoningOptions()
{
ReasoningEffortLevel = ResponseReasoningEffortLevel.High,
},
};
options.InputItems.Add(ResponseItem.CreateUserMessageItem("在扑克游戏中,获胜的最佳策略是什么?"));
ResponseResult response = await client.CreateResponseAsync(options);
CreateResponseOptions streamingOptions = new()
{
Model = "gpt-5.1",
ReasoningOptions = new ResponseReasoningOptions()
{
ReasoningEffortLevel = ResponseReasoningEffortLevel.High,
},
StreamingEnabled = true,
};
streamingOptions.InputItems.Add(ResponseItem.CreateUserMessageItem("在扑克游戏中,获胜的最佳策略是什么?"));
await foreach (StreamingResponseUpdate update
in client.CreateResponseStreamingAsync(streamingOptions))
{
if (update is StreamingResponseOutputItemAddedUpdate itemUpdate
&& itemUpdate.Item is ReasoningResponseItem reasoningItem)
{
Console.WriteLine($"[推理] ({reasoningItem.Status})");
}
else if (update is StreamingResponseOutputItemAddedUpdate itemDone
&& itemDone.Item is ReasoningResponseItem reasoningDone)
{
Console.WriteLine($"[推理完成] ({reasoningDone.Status})");
}
else if (update is StreamingResponseOutputTextDeltaUpdate delta)
{
Console.Write(delta.Delta);
}
}
如何结合文件搜索使用响应
ResponsesClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
string vectorStoreId = "vs-123";
ResponseTool fileSearchTool
= ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreId]);
CreateResponseOptions options = new()
{
Model = "gpt-5.1",
Tools = { fileSearchTool }
};
options.InputItems.Add(ResponseItem.CreateUserMessageItem("根据现有文件,秘密数字是多少?"));
ResponseResult response = await client.CreateResponseAsync(options);
foreach (ResponseItem outputItem in response.OutputItems)
{
if (outputItem is FileSearchCallResponseItem fileSearchCall)
{
Console.WriteLine($"[文件搜索] ({fileSearchCall.Status}): {fileSearchCall.Id}");
foreach (string query in fileSearchCall.Queries)
{
Console.WriteLine($" - {query}");
}
}
else if (outputItem is MessageResponseItem message)
{
Console.WriteLine($"[{message.Role}] {message.Content.FirstOrDefault()?.Text}");
}
}
如何结合网络搜索使用响应
ResponsesClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
CreateResponseOptions options = new()
{
Model = "gpt-5.1",
Tools = { ResponseTool.CreateWebSearchTool() },
};
options.InputItems.Add(ResponseItem.CreateUserMessageItem("今天有什么令人开心的新闻标题?"));
ResponseResult response = await client.CreateResponseAsync(options);
foreach (ResponseItem item in response.OutputItems)
{
if (item is WebSearchCallResponseItem webSearchCall)
{
Console.WriteLine($"[调用网络搜索]({webSearchCall.Status}) {webSearchCall.Id}");
}
else if (item is MessageResponseItem message)
{
Console.WriteLine($"[{message.Role}] {message.Content?.FirstOrDefault()?.Text}");
}
}
如何生成文本嵌入
在这个示例中,您希望创建一个旅行规划网站,允许客户输入一段描述他们理想酒店的文字提示,然后系统会提供与该描述高度匹配的酒店推荐。为了实现这一点,可以使用文本嵌入来衡量文本字符串之间的相关性。具体来说,您可以为每家酒店的描述生成嵌入,并将其存储到向量数据库中,从而构建一个可查询的索引,通过客户输入的提示嵌入进行检索。
要生成文本嵌入,请使用 OpenAI.Embeddings 命名空间中的 EmbeddingClient:
string description = "如果您喜欢豪华酒店,这是镇上最好的酒店。这里有一座令人惊叹的无边泳池、水疗中心"
+ "以及非常乐于助人的礼宾服务。地理位置也十分优越——位于市中心,靠近所有旅游景点。我们强烈推荐这家酒店。";
OpenAIEmbedding embedding = client.GenerateEmbedding(description);
ReadOnlyMemory<float> vector = embedding.ToFloats();
请注意,生成的嵌入是一个浮点数列表(也称为向量),以 ReadOnlyMemory<float> 的形式表示。默认情况下,使用 text-embedding-3-small 模型时,嵌入向量的长度为 1536;而使用 text-embedding-3-large 模型时,则为 3072。一般来说,较大的嵌入效果更好,但计算、内存和存储成本也会更高。您可以通过创建 EmbeddingGenerationOptions 类的实例,设置 Dimensions 属性,并将其作为参数传递给 GenerateEmbedding 方法,来降低嵌入的维度:
string description = "如果您喜欢豪华酒店,这是镇上最好的酒店。";
EmbeddingGenerationOptions options = new() { Dimensions = 512 };
OpenAIEmbedding embedding = client.GenerateEmbedding(description, options);
如何生成图像
在本示例中,您希望构建一个应用程序,帮助室内设计师基于最新的设计趋势快速制作新想法的原型。作为创作过程的一部分,室内设计师只需用提示描述脑海中的场景,即可使用此应用生成用于获取灵感的图像。正如预期,高质量、极具视觉冲击力且细节丰富的图像能为该应用场景带来最佳效果。
要生成图像,可以使用 OpenAI.Images 命名空间中的 ImageClient:
ImageClient client = new("dall-e-3", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
生成图像始终需要一个描述生成内容的 prompt。为了进一步根据您的具体需求定制图像生成,您可以创建 ImageGenerationOptions 类的实例,并相应地设置 Quality、Size 和 Style 属性。请注意,您还可以将 ImageGenerationOptions 的 ResponseFormat 属性设置为 GeneratedImageFormat.Bytes,以便以 BinaryData 格式接收生成的 PNG 图像(而不是默认的远程 Uri),如果这对您的用例更方便的话。
string prompt = "客厅的设计理念融合了斯堪的纳维亚的简约风格与日本的极简主义,营造出宁静而温馨的氛围。这是一个邀请人们放松身心的空间,充满自然光和新鲜空气。采用中性色调,包括白色、米色、灰色和黑色等颜色,以创造和谐感。家具选用线条简洁、略带弧度的光滑木质家具,增添温暖与优雅。陶瓷花盆中的绿植和鲜花为空间注入色彩与生机,它们可以作为视觉焦点,拉近人与自然的距离。柔软的纺织品和靠垫采用天然面料,为空间带来舒适与柔和,同时也能起到点缀作用,增加对比与质感。";
ImageGenerationOptions options = new()
{
Quality = GeneratedImageQuality.High,
Size = GeneratedImageSize.W1792xH1024,
Style = GeneratedImageStyle.Vivid,
ResponseFormat = GeneratedImageFormat.Bytes
};
最后,通过将提示和 ImageGenerationOptions 实例作为参数传递给 ImageClient 的 GenerateImage 方法来调用它:
GeneratedImage image = client.GenerateImage(prompt, options);
BinaryData bytes = image.ImageBytes;
出于演示目的,您可以将生成的图像保存到本地存储:
using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.png");
bytes.ToStream().CopyTo(stream);
如何转录音频
在本示例中,使用 Whisper 语音转文本模型对音频文件进行转录,同时包含单词级别和音频片段级别的时间戳信息。
AudioClient client = new("whisper-1", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
string audioFilePath = Path.Combine("Assets", "audio_houseplant_care.mp3");
AudioTranscriptionOptions options = new()
{
ResponseFormat = AudioTranscriptionFormat.Verbose,
TimestampGranularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment,
};
AudioTranscription transcription = client.TranscribeAudio(audioFilePath, options);
Console.WriteLine("转录结果:");
Console.WriteLine($"{transcription.Text}");
Console.WriteLine();
Console.WriteLine($"单词:");
foreach (TranscribedWord word in transcription.Words)
{
Console.WriteLine($" {word.Word,15} : {word.StartTime.TotalMilliseconds,5:0} - {word.EndTime.TotalMilliseconds,5:0}");
}
Console.WriteLine();
Console.WriteLine($"片段:");
foreach (TranscribedSegment segment in transcription.Segments)
{
Console.WriteLine($" {segment.Text,90} : {segment.StartTime.TotalMilliseconds,5:0} - {segment.EndTime.TotalMilliseconds,5:0}");
}
如何使用带有检索增强生成(RAG)的助手
在本示例中,您有一个包含不同产品月度销售信息的 JSON 文档,希望构建一个能够分析该文档并回答相关问题的助手。
为此,您可以同时使用 OpenAI.Files 命名空间中的 OpenAIFileClient 和 OpenAI.Assistants 命名空间中的 AssistantClient。
重要提示:助手 REST API 目前处于测试阶段。因此,其细节可能会发生变化,相应的 AssistantClient 被标记为 [Experimental]。要使用它,您必须先禁用 OPENAI001 警告。
OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
OpenAIFileClient fileClient = openAIClient.GetOpenAIFileClient();
AssistantClient assistantClient = openAIClient.GetAssistantClient();
以下是该 JSON 文档可能的样子:
Stream document = BinaryData.FromBytes("""
{
"description": "本文档包含 Contoso 公司产品的销售历史数据。",
"sales": [
{
"month": "一月",
"by_product": {
"113043": 15,
"113045": 12,
"113049": 2
}
},
{
"month": "二月",
"by_product": {
"113045": 22
}
},
{
"month": "三月",
"by_product": {
"113045": 16,
"113055": 5
}
}
]
}
"""u8.ToArray()).ToStream();
使用 OpenAIFileClient 的 UploadFile 方法将此文档上传至 OpenAI,并确保使用 FileUploadPurpose.Assistants 目的,以便您的助手日后可以访问该文件:
OpenAIFile salesFile = fileClient.UploadFile(
document,
"monthly_sales.json",
FileUploadPurpose.Assistants);
使用 AssistantCreationOptions 类的实例创建一个新的助手,并对其进行自定义。在此示例中,我们设置了以下内容:
- 助手的友好名称,将在 Playground 中显示;
- 助手应具备的工具定义实例;这里我们使用
FileSearchToolDefinition来处理刚刚上传的销售文档,以及CodeInterpreterToolDefinition以便对数值数据进行分析和可视化; - 助手与其工具一起使用的资源;此处我们使用
VectorStoreCreationHelper类型自动创建一个新的向量存储,用于索引销售文件;或者您也可以使用VectorStoreClient单独管理向量存储。
AssistantCreationOptions assistantOptions = new()
{
Name = "示例:Contoso 销售 RAG",
Instructions =
"您是一位助手,负责查找销售数据,并根据用户查询帮助可视化相关信息。当被要求生成图表或其他可视化内容时,请使用代码解释器工具来完成。",
Tools =
{
new FileSearchToolDefinition(),
new CodeInterpreterToolDefinition(),
},
ToolResources = new()
{
FileSearch = new()
{
NewVectorStores =
{
new VectorStoreCreationHelper([salesFile.Id]),
}
}
},
};
Assistant assistant = assistantClient.CreateAssistant("gpt-5.1", assistantOptions);
接下来,创建一个新的线程。为了演示目的,您可以包含一条初始用户消息,询问某个产品的销售信息,然后使用 AssistantClient 的 CreateThreadAndRun 方法启动它:
ThreadCreationOptions threadOptions = new()
{
InitialMessages = { "产品 113045 在二月份的销售情况如何?请绘制其随时间的变化趋势图。" }
};
ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Id, threadOptions);
轮询运行状态,直到其不再处于排队或进行中状态:
do
{
Thread.Sleep(TimeSpan.FromSeconds(1));
threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id);
} while (!threadRun.Status.IsTerminal);
如果一切顺利,运行的最终状态将是 RunStatus.Completed。
最后,您可以使用 AssistantClient 的 GetMessages 方法获取与该线程相关的消息,其中现在包含了助手对初始用户消息的响应。
为了演示目的,您可以将这些消息打印到控制台,并将助手生成的任何图像保存到本地存储中:
CollectionResult<ThreadMessage> messages
= assistantClient.GetMessages(threadRun.ThreadId, new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending });
foreach (ThreadMessage message in messages)
{
Console.Write($"[{message.Role.ToString().ToUpper()}]: ");
foreach (MessageContent contentItem in message.Content)
{
if (!string.IsNullOrEmpty(contentItem.Text))
{
Console.WriteLine($"{contentItem.Text}");
if (contentItem.TextAnnotations.Count > 0)
{
Console.WriteLine();
}
// 如果有注释,则一并显示。
foreach (TextAnnotation annotation in contentItem.TextAnnotations)
{
if (!string.IsNullOrEmpty(annotation.InputFileId))
{
Console.WriteLine($"* 文件引用,文件 ID:{annotation.InputFileId}");
}
if (!string.IsNullOrEmpty(annotation.OutputFileId))
{
Console.WriteLine($"* 文件输出,新文件 ID:{annotation.OutputFileId}");
}
}
}
if (!string.IsNullOrEmpty(contentItem.ImageFileId))
{
OpenAIFile imageInfo = fileClient.GetFile(contentItem.ImageFileId);
BinaryData imageBytes = fileClient.DownloadFile(contentItem.ImageFileId);
using FileStream stream = File.OpenWrite($"{imageInfo.Filename}.png");
imageBytes.ToStream().CopyTo(stream);
Console.WriteLine($"<图片:{imageInfo.Filename}.png>");
}
}
Console.WriteLine();
}
输出结果可能如下所示:
[USER]:产品 113045 在二月份的销售情况如何?请绘制其随时间的变化趋势图。
[ASSISTANT]:产品 113045 在二月份售出了 22 件【4:0†monthly_sales.json】。
现在,我将生成一张图表来展示其销售趋势。
* 文件引用,文件 ID:file-hGOiwGNftMgOsjbynBpMCPFn
[ASSISTANT]:<图片:015d8e43-17fe-47de-af40-280f25452280.png>
过去三个月内,产品 113045 的销售趋势显示:
- 一月份售出 12 件。
- 二月份售出 22 件,表明销售显著增长。
- 三月份销量略有下降,为 16 件。
上图直观地展示了这一趋势,显示出二月份的销售峰值。
如何使用带有流式传输和视觉功能的助手
此示例展示了如何使用 v2 助手 API 向助手提供图像数据,然后流式传输运行的响应。
与之前一样,您将使用 OpenAIFileClient 和 AssistantClient:
OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
OpenAIFileClient fileClient = openAIClient.GetOpenAIFileClient();
AssistantClient assistantClient = openAIClient.GetAssistantClient();
对于本示例,我们将同时使用来自本地文件的图像数据以及位于 URL 的图像。对于本地数据,我们以 Vision 上传目的上传文件,这也将允许稍后下载和检索该文件。
OpenAIFile pictureOfAppleFile = fileClient.UploadFile(
Path.Combine("Assets", "images_apple.png"),
FileUploadPurpose.Vision);
Uri linkToPictureOfOrange = new("https://raw.githubusercontent.com/openai/openai-dotnet/refs/heads/main/examples/Assets/images_orange.png");
接下来,创建一个具有视觉能力的模型(如 gpt-4o)的新助手,并创建一个引用了图像信息的线程:
Assistant assistant = assistantClient.CreateAssistant(
"gpt-5.1",
new AssistantCreationOptions()
{
Instructions = "当被问到问题时,尽量用非常简洁的方式回答。"
+ "只要可行,就优先使用一句话来回答。"
});
AssistantThread thread = assistantClient.CreateThread(new ThreadCreationOptions()
{
InitialMessages =
{
new ThreadInitializationMessage(
OpenAI.Assistants.MessageRole.User,
[
"你好,助手!请帮我比较这两张图片:",
MessageContent.FromImageFileId(pictureOfAppleFile.Id),
MessageContent.FromImageUri(linkToPictureOfOrange),
]),
}
});
在助手和线程准备就绪后,使用 CreateRunStreaming 方法获取可枚举的 CollectionResult<StreamingUpdate>。然后您可以使用 foreach 遍历此集合。对于异步调用模式,请使用 CreateRunStreamingAsync,并使用 await foreach 遍历 AsyncCollectionResult<StreamingUpdate>。请注意,CreateThreadAndRunStreaming 和 SubmitToolOutputsToRunStreaming 也有流式传输版本。
CollectionResult<StreamingUpdate> streamingUpdates = assistantClient.CreateRunStreaming(
thread.Id,
assistant.Id,
new RunCreationOptions()
{
AdditionalInstructions = "如果可能的话,在比较事物时试着加入一些双关语。",
});
最后,为了处理到达的 StreamingUpdates,您可以使用基类 StreamingUpdate 上的 UpdateKind 属性,或者将其向下转换为特定所需的更新类型,例如用于 thread.message.delta 事件的 MessageContentUpdate 或用于流式工具调用的 RequiredActionUpdate。
foreach (StreamingUpdate streamingUpdate in streamingUpdates)
{
if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated)
{
Console.WriteLine($"--- 运行已开始!---");
}
if (streamingUpdate is MessageContentUpdate contentUpdate)
{
Console.Write(contentUpdate.Text);
}
}
这将产生如下所示的流式输出:
--- 运行已开始!---
第一张图片描绘了一颗红绿相间的多彩苹果,而第二张图片则展示了一个有着明亮、纹理丰富的橙色果皮的橙子;可以说这是在比较苹果和橙子呢!
如何使用 Azure OpenAI
从 OpenAI 切换到 Azure OpenAI 很简单,在大多数情况下几乎不需要更改代码。要快速上手,请访问 https://aka.ms/openai/start 查看入门套件。如果您想了解端点切换的工作原理,也可以阅读:https://aka.ms/openai/switch。
使用 Microsoft Entra ID 安全访问(无需 API 密钥)
入门套件包含示例,演示如何使用 Microsoft Entra ID 而不是 API 密钥安全地调用 Azure OpenAI。这是生产场景中推荐的方法。以下是入门套件中使用 Entra ID 的 .NET 示例的直接链接:https://github.com/Azure-Samples/azure-openai-starter/blob/main/src/dotnet/responses_example_entra.cs
以下是使用 .NET 的 OpenAI SDK 结合 Azure OpenAI 和 Entra ID 的核心模式:
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT 是必需的。");
var client = new ResponsesClient(
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
new OpenAIClientOptions { Endpoint = new Uri($"{endpoint}/openai/v1/") }
);
var response = await client.CreateResponseAsync("gpt-5-mini", "Hello world!");
Console.WriteLine(response.Value.GetOutputText());
为什么这样可行
- 使用一个 OpenAI SDK:您使用官方的 .NET 版 OpenAI SDK。Azure OpenAI 只是您让客户端库指向的一个不同端点。
- 统一的 /openai/v1/ 端点:Azure OpenAI 使用与 OpenAI 相同的路径结构,因此大多数客户端代码可以保持不变。
- 企业级身份验证:结合 Microsoft Entra ID 的 Azure Identity SDK 允许您在不存储密钥的情况下访问 Azure OpenAI。
- 即插即用的模型切换:只要 Azure 模型部署与模型名称相同,就可以随意更换“gpt-5-mini”或其他任何模型。
高级场景
使用协议方法
除了使用强类型请求和响应对象的客户端方法外,.NET 库还提供了_协议方法_,允许更直接地访问 REST API。协议方法采用“二进制输入、二进制输出”的方式,接受 BinaryContent 作为请求体,并以 BinaryData 作为响应体。
例如,要使用 ChatClient 的 CompleteChat 方法的协议方法变体,可以将请求体作为 BinaryContent 传递:
BinaryData input = BinaryData.FromBytes("""
{
"model": "gpt-5.1",
"messages": [
{
"role": "user",
"content": "说‘这是一个测试。’"
}
]
}
"""u8.ToArray());
using BinaryContent content = BinaryContent.Create(input);
ClientResult result = client.CompleteChat(content);
BinaryData output = result.GetRawResponse().Content;
using JsonDocument outputAsJson = JsonDocument.Parse(output.ToString());
string message = outputAsJson.RootElement
.GetProperty("choices")[0]
.GetProperty("message")
.GetProperty("content")
.GetString();
Console.WriteLine($"[ASSISTANT]: {message}");
请注意,您可以调用结果 ClientResult 的 GetRawResponse 方法,并通过 PipelineResponse 的 Content 属性获取响应体 BinaryData。
模拟客户端进行测试
OpenAI .NET 库的设计支持模拟,提供了以下关键特性:
- 客户端方法被声明为虚方法,以便可以被重写。
- 提供模型工厂,用于实例化缺少公共构造函数的 API 输出模型。
为了说明模拟的工作原理,假设您想使用 Moq 库验证以下方法的行为:给定一个音频文件路径,判断该文件是否包含指定的秘密单词:
bool ContainsSecretWord(AudioClient client, string audioFilePath, string secretWord)
{
AudioTranscription transcription = client.TranscribeAudio(audioFilePath);
return transcription.Text.Contains(secretWord);
}
创建 AudioClient 和 ClientResult<AudioTranscription> 的模拟对象,设置将被调用的方法和属性,然后测试 ContainsSecretWord 方法的行为。由于 AudioTranscription 类没有公共构造函数,必须通过 OpenAIAudioModelFactory 静态类来实例化:
// 实例化模拟对象和 AudioTranscription 对象。
Mock<AudioClient> mockClient = new();
AudioTranscription transcription = OpenAIAudioModelFactory.AudioTranscription(text: "我发誓昨天真的看到一颗苹果在飞!");
// 设置模拟对象的属性和方法。
mockClient
.Setup(client => client.TranscribeAudio(It.IsAny<string>()))
.Returns(ClientResult.FromValue(transcription, Mock.Of<PipelineResponse>()));
// 执行验证。
AudioClient client = mockClient.Object;
bool containsSecretWord = ContainsSecretWord(client, "<audioFilePath>", "apple");
Assert.That(containsSecretWord, Is.True);
除 OpenAI.Assistants 和 OpenAI.VectorStores 命名空间外,所有命名空间都有对应的模型工厂,以支持模拟;这两个命名空间的模型工厂也将很快推出。
自动重试错误
默认情况下,客户端类会使用指数退避策略,对以下错误最多自动重试三次:
- 408 请求超时
- 429 请求过多
- 500 服务器内部错误
- 502 网关错误
- 503 服务不可用
- 504 网关超时
可观测性
OpenAI .NET 库支持基于 OpenTelemetry 的实验性分布式追踪和指标功能。有关详细信息,请参阅 使用 OpenTelemetry 进行可观性。
版本历史
OpenAI_2.10.02026/04/04OpenAI_2.9.12026/03/02OpenAI_2.9.02026/02/27OpenAI_2.8.02025/12/11OpenAI_2.7.02025/11/13OpenAI_2.6.02025/10/31OpenAI_2.5.02025/09/24OpenAI_2.4.02025/09/06OpenAI_2.3.02025/08/04OpenAI_2.2.02025/07/03OpenAI_2.2.0-beta.42025/03/19OpenAI_2.2.0-beta.32025/03/12OpenAI_2.2.0-beta.22025/02/18OpenAI_2.2.0-beta.12025/02/07OpenAI_2.1.02024/12/04OpenAI_2.1.0-beta.22024/11/04OpenAI_2.1.0-beta.12024/10/01OpenAI_2.0.02024/09/30OpenAI_2.0.0-beta.132024/09/27OpenAI_2.0.0-beta.122024/09/20常见问题
相似工具推荐
openclaw
OpenClaw 是一款专为个人打造的本地化 AI 助手,旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚,能够直接接入你日常使用的各类通讯渠道,包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息,OpenClaw 都能即时响应,甚至支持在 macOS、iOS 和 Android 设备上进行语音交互,并提供实时的画布渲染功能供你操控。 这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地,用户无需依赖云端服务即可享受快速、私密的智能辅助,真正实现了“你的数据,你做主”。其独特的技术亮点在于强大的网关架构,将控制平面与核心助手分离,确保跨平台通信的流畅性与扩展性。 OpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者,以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力(支持 macOS、Linux 及 Windows WSL2),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你
stable-diffusion-webui
stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。
everything-claude-code
everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上
ComfyUI
ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。
gemini-cli
gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。
markitdown
MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具,专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片(含 OCR)、音频(含语音转录)、HTML 乃至 YouTube 链接等多种格式的解析,能够精准提取文档中的标题、列表、表格和链接等关键结构信息。 在人工智能应用日益普及的今天,大语言模型(LLM)虽擅长处理文本,却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点,它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式,成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外,它还提供了 MCP(模型上下文协议)服务器,可无缝集成到 Claude Desktop 等 LLM 应用中。 这款工具特别适合开发者、数据科学家及 AI 研究人员使用,尤其是那些需要构建文档检索增强生成(RAG)系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性,但其核心优势在于为机器