当前位置: 首页 > article >正文

Forge.OpenAI.ErrorOr:优雅处理OpenAI API错误的函数式解决方案

1. 项目概述与核心价值如果你在.NET生态里折腾过OpenAI的API大概率会和我一样经历过一段“痛并快乐着”的时光。快乐在于大语言模型的能力确实让人兴奋痛则在于处理API调用中的各种异常和错误状态代码很容易变得一团糟。每次调用后你都得写一堆if-else来检查HTTP状态码、解析JSON错误体、处理网络超时还得把业务逻辑错误和系统错误分开。更头疼的是异步调用里这些错误处理逻辑散落各处维护起来简直是噩梦。这就是我最初接触Forge.OpenAI这个优秀的开源客户端库时的感受。它封装了OpenAI和Azure OpenAI的API用起来很顺手但在错误处理上它和大多数库一样采用的是传统的异常抛出模式。对于追求健壮性和明确错误流的应用来说这还不够。直到我遇到了ErrorOr这个库——它提供了一种将错误视为可返回的、类型安全的值ErrorOrT的范式灵感来源于函数式编程中的Either类型。这简直是为API客户端量身定做的理念。于是Forge.OpenAI.ErrorOr这个扩展库就诞生了。它的目标非常单纯为Forge.OpenAI库的核心服务类比如ChatCompletionService,TextCompletionService等提供一套基于ErrorOrT的、异步友好的扩展方法。让你能用一种更优雅、更函数式的方式来调用OpenAI API把所有潜在的错误网络错误、API错误、额度不足、模型不可用等都封装在一个ErrorOrT对象里返回而不是让它们以异常的形式“炸”掉你的程序流。简单来说这个库在Forge.OpenAI和你的业务代码之间架起了一座“错误安全”的桥梁。它支持的.NET版本很广从传统的.NET Framework 4.6.1到最新的.NET 9.0包括.NET Standard 2.0、.NET Core 3.1以及所有的现代.NET版本。无论是开发后端API、桌面应用还是Blazor WebAssembly/Server这样的前端项目它都能无缝集成。2. 核心设计思路与方案选型2.1 为什么选择 ErrorOr 范式在深入代码之前我们先聊聊为什么“错误作为值”比“异常”更适合API客户端场景。这并不是说异常不好异常对于真正的“异常”情况比如内存不足、空引用是完美的。但对于API调用很多“错误”其实是可预期的业务流的一部分。想象一下你调用ChatCompletion接口。服务器可能返回“模型过载”429状态码、“无效请求”400状态码、“认证失败”401状态码或者业务层面的“内容过滤”content_filter。在传统异常模式下这些都会抛出不同的HttpRequestException或自定义异常。你的代码结构会是这样try { var result await _chatService.GetAsync(chatRequest); // 处理成功结果 } catch (HttpRequestException ex) when (ex.StatusCode HttpStatusCode.TooManyRequests) { // 处理限流 } catch (HttpRequestException ex) when (ex.StatusCode HttpStatusCode.BadRequest) { // 处理无效请求 } catch (OpenAIException ex) { // 处理OpenAI返回的业务错误 } catch (Exception ex) { // 处理其他未知异常 }这段代码有几个问题流程中断异常强制跳出了正常的代码执行流逻辑被分散在try块和多个catch块中。异步处理繁琐在异步方法中try-catch的嵌套会让代码缩进很深可读性下降。错误信息弱类型在catch块里你通常只能得到一个Exception对象需要手动解析其Message或Data属性来获取具体的错误细节编译器无法帮你做类型检查。难以组合如果你想在发生特定错误比如限流时自动重试或者将多个API调用的错误合并处理用异常模式会非常别扭。而ErrorOrT范式将操作结果和潜在错误包装成一个联合体。一个方法只返回两种可能要么是成功的值T要么是一个或多个Error对象。你的代码会变得更像数据流处理ErrorOrChatCompletionResponse result await _chatService.GetErrorOrAsync(chatRequest); result.Switch( response Console.WriteLine(response.Choices[0].Message.Content), // 成功分支 errors errors.ForEach(e Console.WriteLine($错误: {e.Code} - {e.Description})) // 错误分支 );或者用更函数式的方式处理var processedResult await result .Then(response ExtractContent(response)) // 成功则继续处理 .Match( content Ok(content), // 最终成功返回内容 errors Problem(errors.First().Description) // 最终失败返回错误 );这种方式的优势立现流程线性成功和错误的处理都在同一条代码路径上逻辑更连贯。类型安全Error是一个强类型对象有Code、Description、Type等属性你可以像处理普通数据一样处理它。易于组合ErrorOr提供了Then、Map、Match等方法可以轻松地将多个操作链接起来并统一处理错误。显式性方法的签名ErrorOrT GetErrorOrAsync(...)明确告诉调用者这个操作可能会失败并返回错误迫使开发者主动处理错误情况。2.2 Forge.OpenAI.ErrorOr 的实现策略理解了ErrorOr的好处Forge.OpenAI.ErrorOr库的实现策略就清晰了。它没有重写Forge.OpenAI的底层逻辑而是采用了“装饰器”或“适配器”模式为其核心服务类提供了一套扩展方法。核心工作流程如下拦截调用扩展方法内部调用原始的Forge.OpenAI服务方法如GetAsync。捕获与转换用try-catch块包裹原始调用。捕获所有可能抛出的异常主要是HttpRequestException和OpenAIException。分类映射将捕获到的异常根据其类型、HTTP状态码、错误消息等精确地映射到预定义的Error对象上。例如一个429状态码的HttpRequestException会被映射为一个ErrorType.RateLimited类型的Error。封装返回如果调用成功将结果包装在ErrorOrT.Success(result)中返回。如果失败则返回ErrorOrT.Failure(error)。这个策略的好处是非侵入性。你现有的Forge.OpenAI代码和依赖注入配置完全不需要改动。你只是在需要更优雅错误处理的地方选择使用新的xxxErrorOrAsync方法而已。库中预定义的错误类型为了提供一致的错误处理体验库内置了一系列通用的Error对象覆盖了常见的故障场景Errors.Service.AuthenticationFailed: API密钥或终结点配置错误。Errors.Service.ModelNotFound: 请求的模型不存在或不可访问。Errors.Service.RateLimited: 触发OpenAI的速率限制。Errors.Service.QuotaExceeded: 额度或配额用尽。Errors.Service.ServerError: OpenAI服务器内部错误5xx。Errors.Service.RequestTimeout: 网络请求超时。Errors.Network.ConnectionFailed: 网络连接失败。Errors.Validation.InvalidRequest: 请求参数不符合API要求。此外对于OpenAI API返回的、带有标准错误码的业务错误如content_filter库也会尝试解析并生成对应的Error对象。3. 从安装到实战一步步集成与使用3.1 环境准备与安装首先确保你的项目已经引用了Forge.OpenAI库。Forge.OpenAI.ErrorOr是建立在它之上的。安装方式有三种推荐第一种1. 通过 .NET CLI 安装最直接打开你的项目目录在终端中运行dotnet add package Forge.OpenAI.ErrorOr这个命令会自动为你修改.csproj文件添加最新稳定版本的包引用。2. 通过 Visual Studio NuGet 包管理器在Visual Studio中右键点击你的项目 - “管理NuGet程序包”。在浏览选项卡中搜索“Forge.OpenAI.ErrorOr”选择并安装即可。图形化界面对于管理版本和依赖比较直观。3. 手动编辑项目文件如果你喜欢手动控制可以直接编辑你的.csproj文件在ItemGroup中添加PackageReference IncludeForge.OpenAI.ErrorOr Version1.0.0 / !-- 请替换为最新版本号 --注意安装Forge.OpenAI.ErrorOr时NuGet会自动解析并安装其依赖项主要是ErrorOr核心库和Forge.OpenAI库。如果你的项目之前没有安装Forge.OpenAI它会一并被引入。请确保版本兼容通常最新版本的扩展库会依赖较新版本的Forge.OpenAI。3.2 基础配置与依赖注入假设你有一个ASP.NET Core Web API项目下面是如何配置和注入服务。Program.cs 或 Startup.csusing Forge.OpenAI; using Forge.OpenAI.Interfaces.Services; using Forge.OpenAI.Services; using Forge.OpenAI.Settings; var builder WebApplication.CreateBuilder(args); // 1. 配置 OpenAI 设置 // 从 appsettings.json 读取或直接硬编码不推荐生产环境 builder.Services.ConfigureOpenAIOptions(builder.Configuration.GetSection(OpenAI)); // 2. 添加 Forge.OpenAI 的核心服务 builder.Services.AddForgeOpenAI(options { // 如果你没有在appsettings.json中配置可以在这里设置 // options.AuthenticationInfo builder.Configuration[OpenAI:ApiKey]; }); // 3. 注册你自己的服务或控制器 builder.Services.AddControllers(); var app builder.Build(); app.MapControllers(); app.Run();appsettings.json{ OpenAI: { ApiKey: 你的-OpenAI-API-密钥, // 或者使用 Azure OpenAI // ApiKey: 你的-Azure-OpenAI-API-密钥, // BaseAddress: https://你的资源名.openai.azure.com/openai/deployments/你的部署名/ } }关键点在于AddForgeOpenAI()这个扩展方法它会以单例模式注册IOpenAIService等核心服务。Forge.OpenAI.ErrorOr不需要额外的DI注册因为它提供的是扩展方法直接使用即可。3.3 核心服务扩展方法详解安装好库之后你会发现Forge.OpenAI中那些你熟悉的服务接口如IChatCompletionService,ITextCompletionService,IModerationService,IImageService等都多出了一组以ErrorOr结尾的异步方法。我们以最常用的IChatCompletionService为例对比一下传统方法和新方法传统方法 (Forge.OpenAI)ErrorOr 扩展方法 (Forge.OpenAI.ErrorOr)说明TaskHttpOperationResultChatCompletionResponse GetAsync(...)TaskErrorOrChatCompletionResponse GetErrorOrAsync(...)基础聊天补全TaskHttpOperationResultChatCompletionResponse GetAsync(...)TaskErrorOrChatCompletionResponse GetErrorOrAsync(...)支持流式响应的方法也有对应的GetErrorOrAsStreamAsync这些扩展方法位于Forge.OpenAI.ErrorOr命名空间下。为了使用方便建议在文件顶部添加引用using Forge.OpenAI.ErrorOr; // 引入扩展方法 using Forge.OpenAI.Interfaces.Services; using Forge.OpenAI.Models.ChatCompletions;现在让我们看一个完整的控制器示例展示如何使用GetErrorOrAsync。[ApiController] [Route(api/chat)] public class ChatController : ControllerBase { private readonly IChatCompletionService _chatService; public ChatController(IChatCompletionService chatService) { _chatService chatService; } [HttpPost(completion)] public async TaskIActionResult GetCompletion([FromBody] ChatCompletionRequest request) { // 使用 ErrorOr 扩展方法 ErrorOrChatCompletionResponse result await _chatService.GetErrorOrAsync(request); // 方式一使用 Match 方法同时处理成功和失败并返回最终结果 return result.Match( successResponse Ok(new { content successResponse.Choices[0].Message.Content }), errors { // 获取第一个错误通常是最相关的 var firstError errors.First(); // 根据错误类型返回不同的HTTP状态码和消息 var statusCode firstError.Type switch { ErrorType.Unauthorized StatusCodes.Status401Unauthorized, ErrorType.Forbidden StatusCodes.Status403Forbidden, ErrorType.NotFound StatusCodes.Status404NotFound, ErrorType.Conflict StatusCodes.Status409Conflict, ErrorType.Validation StatusCodes.Status400BadRequest, ErrorType.RateLimited StatusCodes.Status429TooManyRequests, ErrorType.ServiceUnavailable StatusCodes.Status503ServiceUnavailable, _ StatusCodes.Status500InternalServerError }; return Problem( detail: firstError.Description, instance: HttpContext.Request.Path, statusCode: statusCode, title: firstError.Code ); } ); } }这段代码清晰地展示了ErrorOr模式在Web API中的优势单一出口整个方法只有一个return语句逻辑清晰。错误处理集中化所有可能的错误都在Match的第二个lambda表达式中处理并且可以根据Error.Type映射到最合适的HTTP状态码。强类型错误信息firstError.Code和firstError.Description可以直接用作API错误响应的标题和详情信息更规范。3.4 流式响应与错误处理对于聊天补全流式响应Streaming能极大提升用户体验。Forge.OpenAI.ErrorOr也支持流式方法。[HttpPost(completion-stream)] public async Task GetCompletionStream([FromBody] ChatCompletionRequest request) { Response.Headers.Add(Content-Type, text/event-stream); Response.Headers.Add(Cache-Control, no-cache); Response.Headers.Add(Connection, keep-alive); // 获取流式响应的 ErrorOr 结果 ErrorOrHttpOperationResult streamResult await _chatService.GetErrorOrAsStreamAsync( request, async (ChatCompletionStreamResponseDelta delta) { // 处理每个数据块 if (!string.IsNullOrEmpty(delta.Delta?.Content)) { var data $data: {JsonSerializer.Serialize(new { content delta.Delta.Content })}\n\n; await Response.WriteAsync(data); await Response.Body.FlushAsync(); } }, CancellationToken.None ); // 流式调用完成后检查是否有错误 if (streamResult.IsError) { // 注意对于已经开始的流式响应不能更改HTTP状态码。 // 通常我们会发送一个特殊的事件来表示错误。 var errorEvent $data: [ERROR] {streamResult.FirstError.Description}\n\n; await Response.WriteAsync(errorEvent); await Response.Body.FlushAsync(); } await Response.WriteAsync(data: [DONE]\n\n); await Response.Body.FlushAsync(); }重要提示流式响应的错误处理与普通请求不同。因为HTTP响应头已经发送并开始传输数据块所以不能再更改HTTP状态码如404或500。最佳实践是在流式数据中嵌入错误信息比如发送一个特定的错误事件如data: [ERROR] ...让前端客户端来解析和处理。GetErrorOrAsStreamAsync方法会在流式传输完成后让你有机会检查整个操作是否成功这对于记录日志或触发后续清理操作非常有用。4. 高级用法与模式实践4.1 组合多个API调用ErrorOr的真正威力在于组合。假设你需要先进行内容审核审核通过后再调用ChatGPT生成内容。public async TaskErrorOrstring GenerateSafeStory(string userPrompt) { // 1. 创建审核请求 var moderationRequest new ModerationRequest { Input userPrompt }; // 2. 调用审核服务 (ErrorOr 版本) ErrorOrModerationResponse moderationResult await _moderationService.GetErrorOrAsync(moderationRequest); // 3. 使用 Then 方法只有审核成功且无违规才继续执行生成 return await moderationResult .Then(modResponse { // 检查审核结果 if (modResponse.Results.Any(r r.Flagged)) { // 如果内容违规返回一个自定义错误中断链式调用 return ErrorOrChatCompletionRequest.Failure( Error.Validation(Prompt.Rejected, 输入内容包含违规信息无法生成。) ); } // 审核通过构建聊天请求 var chatRequest new ChatCompletionRequest { Messages new ListMessage { new Message { Role user, Content userPrompt } }, Model gpt-3.5-turbo, MaxTokens 500 }; return ErrorOrChatCompletionRequest.Success(chatRequest); }) .ThenAsync(async chatRequest { // 4. 只有上一步返回 Success这里才会执行 ErrorOrChatCompletionResponse chatResult await _chatService.GetErrorOrAsync(chatRequest); return chatResult; }) .Match( chatResponse ErrorOrstring.Success(chatResponse.Choices[0].Message.Content), errors ErrorOrstring.Failure(errors) // 将链中任何一步的错误传递下去 ); }代码解析Then: 用于同步转换。如果moderationResult是成功的则执行lambda并返回一个新的ErrorOrT。如果moderationResult已经是失败的则跳过Then直接将错误传递下去。ThenAsync: 用于异步转换。最后使用Match将最终的ErrorOrChatCompletionResponse转换为ErrorOrstring我们只关心生成的文本。整个流程是线性的错误处理是隐式且自动的。如果审核失败生成步骤根本不会执行。4.2 自定义错误与错误转换虽然库提供了丰富的预定义错误但你可能需要定义自己领域的错误。// 定义你自己的错误类型 public static class MyAppErrors { public static Error ContentTooLong(int maxLength) Error.Validation( code: Content.TooLong, description: $输入内容长度超过{maxLength}字符限制。 ); public static Error InsufficientCredit(decimal required, decimal available) Error.Failure( code: Payment.InsufficientCredit, description: $积分不足。需要{required}当前仅有{available}。 ); } // 在服务层使用 public async TaskErrorOrGeneratedContent GenerateWithChecks(GenerationRequest request) { // 业务规则校验 if (request.Content.Length 1000) { return ErrorOrGeneratedContent.Failure(MyAppErrors.ContentTooLong(1000)); } // 检查用户积分假设 var userCredit await _creditService.GetUserCreditAsync(request.UserId); if (userCredit 0.1m) // 假设生成一次消耗0.1积分 { return ErrorOrGeneratedContent.Failure(MyAppErrors.InsufficientCredit(0.1m, userCredit)); } // 调用 OpenAI API var chatResult await _chatService.GetErrorOrAsync(/* ... */); // ... 后续处理 }你也可以拦截Forge.OpenAI抛出的特定异常将其转换为更符合你业务语义的错误。// 一个更复杂的错误处理中间件或包装方法示例 public async TaskErrorOrT SafeOpenAICallAsyncT(FuncTaskT openAIOperation) { try { var result await openAIOperation(); return ErrorOrT.Success(result); } catch (HttpRequestException ex) when (ex.StatusCode System.Net.HttpStatusCode.NotFound) { // 例如将“未找到”映射为更具体的“模型不可用” return ErrorOrT.Failure(Error.NotFound(OpenAI.ModelUnavailable, 请求的AI模型在当前区域不可用请检查部署名或选择其他模型。)); } catch (OpenAIException ex) when (ex.Error?.Code context_length_exceeded) { // 处理特定的OpenAI API错误 return ErrorOrT.Failure(Error.Validation(Request.ContextTooLong, 对话上下文过长请缩短输入或开启新的对话。)); } catch (Exception ex) { // 兜底记录未知异常 _logger.LogError(ex, 调用OpenAI API时发生未知错误); return ErrorOrT.Failure(Error.Unexpected(System.Unknown, 系统处理请求时发生意外错误请稍后重试。)); } }4.3 与 Result 模式或 MediatR 管道集成如果你的项目已经使用了FluentResults、Ardalis.Result等其他Result模式库或者使用了MediatR的管道行为Pipeline Behavior进行统一处理ErrorOr也能很好地融入。与 MediatR 集成示例你可以创建一个管道行为自动将ErrorOrT转换为IResultASP.NET Core的Minimal API结果或IActionResult。// 一个简单的 MediatR 管道行为用于处理 ErrorOr 返回值 public class ErrorOrBehaviorTRequest, TResponse : IPipelineBehaviorTRequest, TResponse where TRequest : IRequestTResponse where TResponse : IErrorOr // 假设你的命令/查询返回 IErrorOr 接口 { public async TaskTResponse Handle(TRequest request, RequestHandlerDelegateTResponse next, CancellationToken cancellationToken) { var result await next(); // 执行实际的Handler // 这里可以做一些全局处理比如日志记录所有错误 if (result.IsError) { var errors result.Errors; // 记录到日志系统... } return result; // 直接返回让上层控制器去 Match } } // 在控制器或Minimal API中 app.MapPost(/api/chat, async (IMediator mediator, ChatRequest request) { var command new GenerateChatCommand { Prompt request.Prompt }; ErrorOrChatResponse result await mediator.Send(command); return result.Match( success Results.Ok(success), errors Results.Problem( detail: string.Join(; , errors.Select(e e.Description)), statusCode: errors.First().Type switch { ErrorType.Validation StatusCodes.Status400BadRequest, ErrorType.NotFound StatusCodes.Status404NotFound, ErrorType.Unauthorized StatusCodes.Status401Unauthorized, _ StatusCodes.Status500InternalServerError } ) ); });5. 常见问题、排查技巧与性能考量5.1 常见错误与解决方案在实际使用中你可能会遇到以下典型问题问题现象可能原因排查步骤与解决方案调用GetErrorOrAsync后result.IsError为true错误码为Network.ConnectionFailed。1. 网络不通。2.OpenAIOptions中的BaseAddress配置错误特别是Azure OpenAI。3. 本地代理或防火墙阻止。1. 使用ping或curl测试到api.openai.com或你的Azure终结点的连通性。2.仔细检查BaseAddressAzure OpenAI的终结点格式应为https://your-resource-name.openai.azure.com/openai/deployments/deployment-name/注意末尾的斜杠和路径。OpenAI官方API则通常不需要设置BaseAddress。3. 检查系统或IDE的代理设置。错误码为Service.AuthenticationFailed。1. API密钥未设置或错误。2. Azure OpenAI中API密钥与资源不匹配或部署名错误。3. 密钥已过期或被撤销。1. 确认OpenAIOptions.ApiKey或环境变量OPENAI_API_KEY已正确设置且无多余空格。2. 对于Azure确保使用的是Azure门户中该特定部署的密钥并且BaseAddress中的部署名完全一致。3. 在OpenAI平台或Azure门户上检查密钥状态。错误码为Service.ModelNotFound。1. 请求中指定的Model名称拼写错误。2. 该模型在你的API计划中不可用如未订阅GPT-4。3. Azure OpenAI中部署名与模型不匹配。1. 核对OpenAI官方文档的模型列表如gpt-3.5-turbo,gpt-4-turbo-preview。注意Azure OpenAI中Model参数应填写部署名而不是模型名这是一个常见的混淆点。2. 检查你的API订阅级别。3. 在Azure AI Studio中确认部署的名称和关联的基础模型。错误码为Service.RateLimited。触发OpenAI或Azure的请求速率限制RPM或令牌速率限制TPM。1.实现指数退避重试这是处理429错误的标准做法。Forge.OpenAI.ErrorOr返回的错误对象非常适合用来触发重试逻辑。2. 检查你的调用频率考虑增加延迟、批量处理请求或申请提升限额。流式响应能建立但收不到数据或立即结束。1. 请求参数配置有误导致API立即返回空流或错误。2. 前端EventSource或SSE客户端处理逻辑有误。3. 服务器端在处理流时发生未捕获异常。1. 首先用非流式GetErrorOrAsync测试相同的请求参数看是否返回明确错误。2. 在服务器端流式回调函数中添加日志确认是否被调用。3. 使用Fiddler、Wireshark或浏览器开发者工具的网络选项卡查看SSE连接的事件流原始数据。依赖注入时找不到IChatCompletionService。1. 未调用builder.Services.AddForgeOpenAI()。2. 在非ASP.NET Core环境如控制台应用中手动创建服务实例。1. 确保在Program.cs或Startup.cs中正确调用了AddForgeOpenAI。2. 在控制台应用中需要手动实例化OpenAIService或使用ServiceCollection构建一个简单的DI容器。5.2 性能考量与最佳实践错误对象的创建开销ErrorOrT是一个结构体struct在大多数情况下成功路径的包装开销极小。错误路径会分配一个Error对象列表。对于性能极度敏感的场景这需要纳入考量但对于绝大多数API调用网络IO是主要开销其影响可忽略不计。避免过度包装不要在每个方法层都进行ErrorOr包装。通常只在边界层如直接调用外部API的服务层、控制器入口使用。内部业务逻辑可以继续使用异常或简单的返回值由边界层统一捕获并转换为ErrorOr。异步流与内存流式响应GetErrorOrAsStreamAsync会将数据块逐个推送到回调函数内存效率高。确保回调函数是异步的且不会阻塞避免背压问题。重试策略对于网络抖动和速率限制错误RateLimited,ServiceUnavailable强烈建议实现一个带有指数退避和抖动jitter的重试机制。你可以利用Polly这样的弹性库并结合ErrorOr的IsError和错误类型来判断是否重试。var retryPolicy PolicyErrorOrChatCompletionResponse .HandleResult(errorOrResult errorOrResult.IsError errorOrResult.FirstError.Type is ErrorType.RateLimited or ErrorType.ServiceUnavailable) .WaitAndRetryAsync(3, retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // 指数退避 TimeSpan.FromMilliseconds(Random.Shared.Next(0, 1000)) // 抖动 ); var finalResult await retryPolicy.ExecuteAsync(() _chatService.GetErrorOrAsync(chatRequest) );日志记录在全局错误处理或MediatR管道中记录下所有的错误Error对象包括其Code、Description和Type。这对于监控API健康度和诊断问题至关重要。但注意不要在日志中泄露API密钥等敏感信息Forge.OpenAI库通常会过滤掉这些信息。5.3 调试技巧启用详细日志在appsettings.Development.json中将Forge.OpenAI的日志级别设为Debug或Trace可以查看详细的HTTP请求和响应信息。{ Logging: { LogLevel: { Default: Information, Forge.OpenAI: Debug } } }检查Error对象的完整信息除了Code和DescriptionError对象可能包含一个Metadata字典里面会有更详细的原始异常信息或HTTP状态码在调试时非常有用。if (result.IsError) { var error result.FirstError; _logger.LogDebug(错误详情: {Code}, {Desc}, 元数据: {Metadata}, error.Code, error.Description, error.Metadata); }使用原始方法对比当ErrorOr扩展方法出现难以理解的行为时临时换回原始的GetAsync方法看看它抛出的具体异常是什么这能帮你判断问题是出在Forge.OpenAI库本身还是ErrorOr扩展的映射逻辑上。最后这个库的本质是提供多一种选择。如果你的项目风格偏向传统的异常处理并且团队对此很熟悉那么继续使用Forge.OpenAI原生的方式完全没有问题。但如果你正在构建一个对错误处理要求极高、希望有更明确控制流和更强类型安全性的系统或者你的团队青睐函数式风格那么Forge.OpenAI.ErrorOr提供了一套现成的、经过实践验证的工具能让你更优雅地驾驭OpenAI API调用中的各种不确定性。

相关文章:

Forge.OpenAI.ErrorOr:优雅处理OpenAI API错误的函数式解决方案

1. 项目概述与核心价值如果你在.NET生态里折腾过OpenAI的API,大概率会和我一样,经历过一段“痛并快乐着”的时光。快乐在于,大语言模型的能力确实让人兴奋;痛则在于,处理API调用中的各种异常和错误状态,代码…...

终极指南:如何在Blender中高效创建和管理VRM虚拟角色

终极指南:如何在Blender中高效创建和管理VRM虚拟角色 【免费下载链接】VRM-Addon-for-Blender VRM Importer, Exporter and Utilities for Blender 2.93 to 5.1 项目地址: https://gitcode.com/gh_mirrors/vr/VRM-Addon-for-Blender VRM Addon for Blender是…...

避坑指南:CentOS 7最小化安装下部署Zabbix 6.4最容易踩的5个雷(附解决方案)

CentOS 7最小化环境部署Zabbix 6.4的五大典型故障与实战修复手册 当你在一台刚完成最小化安装的CentOS 7服务器上部署Zabbix 6.4时,就像在雷区中穿行——稍有不慎就会触发各种依赖缺失、版本冲突和配置错误。本文将揭示五个最致命的"地雷",并提…...

绝区零一条龙:3步实现游戏全自动化的终极指南

绝区零一条龙:3步实现游戏全自动化的终极指南 【免费下载链接】ZenlessZoneZero-OneDragon 绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄 项目地址: https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon 在《绝区零》的快…...

如何在5分钟内解放你的星穹铁道游戏时间?三月七小助手完整指南

如何在5分钟内解放你的星穹铁道游戏时间?三月七小助手完整指南 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 你是否每天花费大量时间在《崩坏&#xff…...

从可视化拖拽到SDF源码:Gazebo模型编辑器的“两面性”与进阶之路

从可视化拖拽到SDF源码:Gazebo模型编辑器的“两面性”与进阶之路 当你第一次在Gazebo中拖拽出一个机器人模型时,那种所见即所得的成就感令人着迷。但随着项目复杂度提升,你会发现图形界面开始变得力不从心——为什么这个嵌套模型无法编辑&…...

LinkSwift技术方案:八大网盘直链解析与高效下载实战指南

LinkSwift技术方案:八大网盘直链解析与高效下载实战指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…...

从人口普查到App A/B测试:一文读懂整群抽样与系统抽样的实战选择

从人口普查到App A/B测试:整群抽样与系统抽样的技术决策指南 在数据驱动的决策时代,抽样方法的选择直接影响着实验结果的可靠性。想象这样一个场景:你的团队需要为一款拥有2亿用户的社交应用测试新消息通知功能,直接全量发布风险太…...

从Monkey测试到bugreport解析:一份给Android测试工程师的Crash分析实战手册

从Monkey测试到bugreport解析:Android测试工程师的Crash分析实战指南 在移动应用质量保障体系中,Crash问题始终是影响用户体验的首要威胁。根据行业数据统计,超过60%的一星应用评价直接源于未修复的崩溃问题。对于Android测试工程师而言&…...

5款惊艳VLC皮肤:告别单调界面,打造专属播放体验

5款惊艳VLC皮肤:告别单调界面,打造专属播放体验 【免费下载链接】VeLoCity-Skin-for-VLC Castom skin for VLC Player 项目地址: https://gitcode.com/gh_mirrors/ve/VeLoCity-Skin-for-VLC 你是否曾为VLC播放器那千篇一律的默认界面感到厌倦&…...

WebAI逆向工程:将网页AI服务封装为可调用API的实战指南

1. 项目概述:从WebAI到API的桥梁搭建最近在折腾一个挺有意思的项目,叫“WebAI-to-API”。这个名字听起来有点技术范儿,但说白了,它的核心目标非常直接:把那些原本只能在网页上点点划划才能用的AI模型,变成一…...

ComfyUI-Manager:如何在无网络环境中部署AI节点管理神器?

ComfyUI-Manager:如何在无网络环境中部署AI节点管理神器? 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable…...

从上帝视角到像素射线:用大白话图解LSS如何让自动驾驶汽车‘脑补’出3D世界

从上帝视角到像素射线:用大白话图解LSS如何让自动驾驶汽车‘脑补’出3D世界 想象一下,你正坐在一辆自动驾驶汽车里,眼前只有几个摄像头的2D画面,但车辆却能像鸟瞰一样"看"到周围360度的立体世界——这就是BEV&#xff0…...

魔兽争霸III终极优化指南:WarcraftHelper完整使用教程

魔兽争霸III终极优化指南:WarcraftHelper完整使用教程 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸III这个经典游戏在现…...

macOS滚动方向个性化控制:Scroll Reverser深度技术解析与实战指南

macOS滚动方向个性化控制:Scroll Reverser深度技术解析与实战指南 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 多设备输入环境下的滚动行为冲突问题 在现代macOS…...

开源代码审查平台Inspecto:从数据聚合到质量洞察的工程实践

1. 项目概述:一个面向开发者的开源代码审查与质量洞察工具 如果你是一名开发者,尤其是团队中的技术负责人或资深工程师,你一定对代码审查(Code Review)这件事又爱又恨。爱的是,它是保证代码质量、统一团队规…...

告别镜像混乱!手把手教你调试MTK平台Camera的Flip与Mirror效果(含Vendor Tag与ADB秘籍)

MTK Camera镜像效果调试实战:从Sensor驱动到应用层的全链路解决方案 当你在调试MTK平台的Camera功能时,是否经常遇到预览、拍照或录像的镜像效果不符合预期?这个问题看似简单,实则涉及从硬件Sensor到软件框架的多层处理逻辑。本文…...

轻量级Docker容器管理面板ClawPanel部署与安全配置指南

1. 项目概述与核心价值最近在折腾个人服务器和容器化应用时,发现一个挺普遍的需求:我们手头可能有好几个不同的服务,比如一个博客、一个图床、一个数据库管理面板,它们各自运行在不同的容器里。每次想看看哪个服务状态怎么样&…...

题解:AcWing 6023 合并石子

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大…...

避开RK3588 MPP解码的坑:分帧模式选择、内存配置与Info Change处理指南

RK3588 MPP解码实战避坑指南:分帧策略、内存优化与动态分辨率处理 第一次在RK3588上实现4K视频流畅解码时,那种成就感至今难忘。但当项目进入压力测试阶段,突然出现的花屏、卡顿和内存泄漏让我意识到,MPP解码器的使用远没有想象中…...

Ultracite:现代CSS框架的功能优先设计与实战应用

1. 项目概述:Ultracite,一个被低估的现代CSS框架如果你和我一样,长期在Web前端领域摸爬滚打,那么对CSS框架的“选择困难症”一定深有体会。从Bootstrap、Tailwind CSS的如日中天,到各种新兴框架的层出不穷,…...

DoL-Lyra整合包:5分钟打造你的专属游戏美化体验

DoL-Lyra整合包:5分钟打造你的专属游戏美化体验 【免费下载链接】DOL-CHS-MODS Degrees of Lewdity 整合 项目地址: https://gitcode.com/gh_mirrors/do/DOL-CHS-MODS 还在为Degrees of Lewdity游戏美化安装的繁琐步骤烦恼吗?DoL-Lyra整合包为你提…...

NixOS部署OpenClaw AI助手网关:声明式配置与零信任安全实践

1. 项目概述:在NixOS上部署一个安全的AI助手网关 如果你正在寻找一种声明式、可复现且安全的方式来部署一个能与Telegram、Slack等平台交互的AI助手网关,那么将OpenClaw与NixOS结合,无疑是一条值得探索的“优雅”路径。我最近在为一个团队搭…...

如何用Revelation光影包5步打造Minecraft电影级画质:免费开源终极方案

如何用Revelation光影包5步打造Minecraft电影级画质:免费开源终极方案 【免费下载链接】Revelation An explorative shaderpack for Minecraft: Java Edition 项目地址: https://gitcode.com/gh_mirrors/re/Revelation 还在为Minecraft原版那单调的视觉效果感…...

埃森哲揭秘:人工智能创造企业级价值的 5 种方式及企业级推广障碍

ZDNET 核心要点企业要推动发展势头,就得展示人工智能投资的早期持续成果,还需投资高质量、受管控的数据和共享工作流程。成功实现智能体转型的关键,是从孤立的人工智能转向系统性人工智能。在企业推广智能体人工智能,强大的数据基…...

3个关键问题:为什么VRM创作者需要Blender插件的深度解决方案?

3个关键问题:为什么VRM创作者需要Blender插件的深度解决方案? 【免费下载链接】VRM-Addon-for-Blender VRM Importer, Exporter and Utilities for Blender 2.93 to 5.1 项目地址: https://gitcode.com/gh_mirrors/vr/VRM-Addon-for-Blender 在虚…...

告别龟速下载!用Git LFS和SSH密钥高效克隆Hugging Face大模型(保姆级避坑指南)

高效获取Hugging Face大模型的完整技术方案 当AI工程师需要将Hugging Face上的大型语言模型部署到本地环境时,传统的下载方式往往成为效率瓶颈。一个15GB的模型文件通过浏览器下载可能需要数小时,而使用基础Git命令又容易因网络波动中断。本文将分享一套…...

ARM CP15协处理器详解:MMU、缓存与安全扩展

1. ARM系统控制协处理器(CP15)概述CP15是ARM架构中最为关键的系统控制协处理器,负责管理和配置处理器核心的各项功能模块。在ARM1176JZF-S处理器中,CP15通过一组专用寄存器实现对以下核心组件的控制:内存管理单元(MMU):包括地址转…...

别再手动调参了!用scikit-plot一键可视化你的sklearn模型性能(附完整代码)

别再手动调参了!用scikit-plot一键可视化你的sklearn模型性能(附完整代码) 每次完成一个机器学习模型的训练后,最让人头疼的环节莫过于评估模型性能。传统的做法是手动调用matplotlib绘制各种图表——从混淆矩阵到ROC曲线&#xf…...

别再只用Visio了!用StarUML画流程图,这份保姆级教程帮你搞定三大结构

从Visio到StarUML:专业流程图设计的进阶指南 在技术文档和产品设计领域,流程图是沟通复杂逻辑的通用语言。过去十年间,Microsoft Visio凭借其易用性和Office生态集成,成为了大多数人的默认选择。但当我们开始处理更复杂的系统架构…...