openai-dotnet

GitHub
2.6k 387 简单 1 次阅读 昨天MIT语言模型图像音频开发框架Agent
AI 解读 由 AI 自动生成,仅供参考

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 后

  • 通过强类型的 OpenAIClientChatClient 类,仅需几行代码即可完成认证与对话调用,彻底告别手写 HTTP 底层逻辑。
  • 原生支持异步流式接口,开发者可直接遍历响应内容块,轻松实现打字机效果的实时回复,代码简洁清晰。
  • 库内建自动重试策略与详细的异常体系,能智能识别瞬态故障并恢复,显著提升了生产环境的鲁棒性。
  • 利用泛型约束和内置辅助方法,轻松定义函数参数与结构化输出格式,将复杂的功能集成时间从数天缩短至数小时。
  • 提供完善的 Mock 支持,允许在测试环境中注入假客户端,既节省了 API 成本又实现了高效的自动化测试。

openai-dotnet 将繁琐的 API 交互转化为直观的 .NET 对象操作,让开发者能专注于业务逻辑而非通信细节,极大加速了 AI 应用在微软技术栈中的落地。

运行环境要求

操作系统
  • Windows
  • macOS
  • Linux
GPU

未说明

内存

未说明

依赖
notes该工具是 .NET 客户端库,非本地运行的 AI 模型,因此无需 GPU 或特定内存配置。主要依赖 .NET 运行环境(兼容 .NET Standard 2.0 及以上,示例基于 .NET 10)。使用时需配置 OpenAI API Key,支持通过环境变量注入。若用于 ASP.NET Core,建议将客户端注册为单例以优化连接复用。
python不适用
.NET Standard 2.0+
NuGet package: OpenAI
openai-dotnet hero image

快速开始

OpenAI .NET API 库

NuGet 稳定版本

OpenAI .NET 库为 .NET 应用程序提供了便捷的 OpenAI REST API 访问接口。该库是根据我们的 OpenAPI 规范,并与 Microsoft 合作生成的。

目录

快速入门

先决条件

要调用 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 兼容大模型),可以使用 ApiKeyCredentialOpenAIClientOptions 指定自定义的基础 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 调用的客户端方法,在同一客户端类中都有对应的异步变体。例如,ChatClientCompleteChat 方法的异步变体是 CompleteChatAsync。要使用异步版本重写上述调用,只需对相应的异步方法使用 await 即可:

ChatCompletion completion = await client.CompleteChatAsync("说‘这是一个测试。’");

使用 OpenAIClient

除了上述命名空间之外,还有一个父级的 OpenAI 命名空间:

using OpenAI;

该命名空间包含 OpenAIClient 类,当您需要同时使用多个功能模块的客户端时,它能提供一些便利。具体来说,您可以使用此客户端的实例来创建其他客户端,并让它们共享相同的实现细节,这样可能会更高效。

您可以通过指定所有客户端都将用于身份验证的 API 密钥来创建一个 OpenAIClient

OpenAIClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

接下来,例如要创建一个 AudioClient 实例,您可以调用 OpenAIClientGetAudioClient 方法,并传入 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 支持在生成过程中逐步流式返回部分结果,这样您可以在补全尚未完成时就开始处理其开头部分。

客户端库提供了一种便捷的方式来处理流式聊天补全。如果您想用流式方式重写上一节中的示例,而不是调用 ChatClientCompleteChat 方法,而是改用 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 属性中。您将在调用 ChatClientCompleteChat 方法时,将 ChatCompletionOptions 作为参数传递。

List<ChatMessage> messages =
[
    new UserChatMessage("今天天气怎么样?"),
];

ChatCompletionOptions options = new()
{
    Tools = { getCurrentLocationTool, getCurrentWeatherTool },
};

当生成的 ChatCompletionFinishReason 属性等于 ChatFinishReason.ToolCalls 时,这意味着模型已经确定在助手能够做出适当回应之前,必须调用一个或多个工具。在这种情况下,您需要先调用 ChatCompletionToolCalls 指定的函数,然后再次调用 ChatClientCompleteChat 方法,并将函数的结果作为附加的 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-minigpt-4o-mini-2024-07-18gpt-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 模型起,聊天补全可以处理音频输入和输出。

此示例演示:

  1. 使用支持的 gpt-4o-audio-preview 模型配置客户端
  2. 在聊天补全请求中提供用户音频输入
  3. 请求聊天补全操作返回模型音频输出
  4. ChatCompletion 实例中获取音频输出
  5. 将之前的音频输出用作 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 类的实例,并相应地设置 QualitySizeStyle 属性。请注意,您还可以将 ImageGenerationOptionsResponseFormat 属性设置为 GeneratedImageFormat.Bytes,以便以 BinaryData 格式接收生成的 PNG 图像(而不是默认的远程 Uri),如果这对您的用例更方便的话。

string prompt = "客厅的设计理念融合了斯堪的纳维亚的简约风格与日本的极简主义,营造出宁静而温馨的氛围。这是一个邀请人们放松身心的空间,充满自然光和新鲜空气。采用中性色调,包括白色、米色、灰色和黑色等颜色,以创造和谐感。家具选用线条简洁、略带弧度的光滑木质家具,增添温暖与优雅。陶瓷花盆中的绿植和鲜花为空间注入色彩与生机,它们可以作为视觉焦点,拉近人与自然的距离。柔软的纺织品和靠垫采用天然面料,为空间带来舒适与柔和,同时也能起到点缀作用,增加对比与质感。";

ImageGenerationOptions options = new()
{
    Quality = GeneratedImageQuality.High,
    Size = GeneratedImageSize.W1792xH1024,
    Style = GeneratedImageStyle.Vivid,
    ResponseFormat = GeneratedImageFormat.Bytes
};

最后,通过将提示和 ImageGenerationOptions 实例作为参数传递给 ImageClientGenerateImage 方法来调用它:

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 命名空间中的 OpenAIFileClientOpenAI.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();

使用 OpenAIFileClientUploadFile 方法将此文档上传至 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);

接下来,创建一个新的线程。为了演示目的,您可以包含一条初始用户消息,询问某个产品的销售信息,然后使用 AssistantClientCreateThreadAndRun 方法启动它:

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

最后,您可以使用 AssistantClientGetMessages 方法获取与该线程相关的消息,其中现在包含了助手对初始用户消息的响应。

为了演示目的,您可以将这些消息打印到控制台,并将助手生成的任何图像保存到本地存储中:

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 向助手提供图像数据,然后流式传输运行的响应。

与之前一样,您将使用 OpenAIFileClientAssistantClient

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>。请注意,CreateThreadAndRunStreamingSubmitToolOutputsToRunStreaming 也有流式传输版本。

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 作为响应体。

例如,要使用 ChatClientCompleteChat 方法的协议方法变体,可以将请求体作为 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}");

请注意,您可以调用结果 ClientResultGetRawResponse 方法,并通过 PipelineResponseContent 属性获取响应体 BinaryData

模拟客户端进行测试

OpenAI .NET 库的设计支持模拟,提供了以下关键特性:

  • 客户端方法被声明为虚方法,以便可以被重写。
  • 提供模型工厂,用于实例化缺少公共构造函数的 API 输出模型。

为了说明模拟的工作原理,假设您想使用 Moq 库验证以下方法的行为:给定一个音频文件路径,判断该文件是否包含指定的秘密单词:

bool ContainsSecretWord(AudioClient client, string audioFilePath, string secretWord)
{
    AudioTranscription transcription = client.TranscribeAudio(audioFilePath);
    return transcription.Text.Contains(secretWord);
}

创建 AudioClientClientResult<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.AssistantsOpenAI.VectorStores 命名空间外,所有命名空间都有对应的模型工厂,以支持模拟;这两个命名空间的模型工厂也将很快推出。

自动重试错误

默认情况下,客户端类会使用指数退避策略,对以下错误最多自动重试三次:

  • 408 请求超时
  • 429 请求过多
  • 500 服务器内部错误
  • 502 网关错误
  • 503 服务不可用
  • 504 网关超时

可观测性

OpenAI .NET 库支持基于 OpenTelemetry 的实验性分布式追踪和指标功能。有关详细信息,请参阅 使用 OpenTelemetry 进行可观性

版本历史

OpenAI_2.10.02026/04/04
OpenAI_2.9.12026/03/02
OpenAI_2.9.02026/02/27
OpenAI_2.8.02025/12/11
OpenAI_2.7.02025/11/13
OpenAI_2.6.02025/10/31
OpenAI_2.5.02025/09/24
OpenAI_2.4.02025/09/06
OpenAI_2.3.02025/08/04
OpenAI_2.2.02025/07/03
OpenAI_2.2.0-beta.42025/03/19
OpenAI_2.2.0-beta.32025/03/12
OpenAI_2.2.0-beta.22025/02/18
OpenAI_2.2.0-beta.12025/02/07
OpenAI_2.1.02024/12/04
OpenAI_2.1.0-beta.22024/11/04
OpenAI_2.1.0-beta.12024/10/01
OpenAI_2.0.02024/09/30
OpenAI_2.0.0-beta.132024/09/27
OpenAI_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),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你

349.3k|★★★☆☆|1周前
Agent开发框架图像

stable-diffusion-webui

stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。

162.1k|★★★☆☆|1周前
开发框架图像Agent

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 真正成长为懂上

154.3k|★★☆☆☆|今天
开发框架Agent语言模型

ComfyUI

ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。

108.3k|★★☆☆☆|3天前
开发框架图像Agent

gemini-cli

gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。

100.8k|★★☆☆☆|4天前
插件Agent图像

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 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性,但其核心优势在于为机器

93.4k|★★☆☆☆|1周前
插件开发框架