
.NET Core Web API集成SmallThinker-3B-Preview模型服务详解你是不是也遇到过这样的场景手头有一个不错的AI模型服务比如最近挺火的SmallThinker-3B-Preview想把它集成到自己的.NET Core Web API项目里给用户提供智能文本处理能力。但一上手就发现从简单的HttpClient调用到生产级的稳定集成中间还有不少坑要填。今天我就以一个.NET老开发的身份带你走一遍完整的集成流程。我们不只讲怎么调通接口更会聊聊怎么把这件事做得更专业、更可靠。比如怎么优雅地管理HttpClient的生命周期怎么用强类型对象让代码更清晰以及怎么应对网络服务偶尔的“小脾气”。最后我们会一起实现一个文本摘要的接口把整个流程串起来。1. 开篇明确我们的目标在开始敲代码之前我们先搞清楚要做什么。假设SmallThinker-3B-Preview模型服务已经部署好了它提供了一个HTTP API端点比如http://your-model-service/v1/chat/completions。我们的任务是在一个ASP.NET Core Web API项目中创建一个可复用的、健壮的服务层来调用这个模型并对外提供我们自己的API。这样做的好处很明显业务逻辑和模型调用解耦了。以后模型服务地址变了、升级了或者我们要换一个模型只需要改动服务层内部的实现对外提供的API接口可以保持不变调用方完全无感知。整个教程会分为几个清晰的步骤首先创建项目基础结构然后构建核心的模型调用服务接着处理网络调用的稳定性最后在控制器里使用它。我们一步步来。2. 第一步准备项目环境万事开头难但准备工作做好了后面就顺了。我们从一个干净的Web API项目开始。2.1 创建新项目打开你的终端或命令行工具找一个合适的目录执行下面的命令。这里我用的是.NET 8但.NET 6/7的步骤基本一样。dotnet new webapi -n SmartModelIntegration cd SmartModelIntegration这个命令会创建一个名为SmartModelIntegration的新Web API项目并包含了基础的WeatherForecast示例。我们可以先把那个示例控制器删掉保持项目干净。2.2 安装必要的NuGet包我们需要几个额外的包来增强功能Microsoft.Extensions.Http用于注册和配置IHttpClientFactory这是现代.NET中管理HttpClient的最佳实践。Polly.Extensions.Http用来给我们的HttpClient添加重试、熔断等弹性策略让调用更稳定。System.Text.Json通常已经包含我们用它来序列化和反序列化JSON数据。在项目目录下运行dotnet add package Microsoft.Extensions.Http dotnet add package Polly.Extensions.Http好了项目骨架和工具包就位接下来我们进入核心部分——定义数据模型。3. 第二步定义强类型的数据契约直接使用dynamic或者匿名对象来处理JSON虽然快但不利于维护和调试。为模型服务的请求和响应定义明确的类能让代码意图更清晰也能享受到编译时检查的好处。我们在项目里创建一个Models文件夹然后在里面添加两个类。3.1 定义请求模型首先定义调用SmallThinker模型所需的请求体。根据常见的聊天补全API格式它通常需要消息列表和模型名称等参数。// Models/ThinkerRequest.cs namespace SmartModelIntegration.Models; public class ThinkerRequest { public ListChatMessage Messages { get; set; } new(); public string Model { get; set; } smallthinker-3b-preview; public double? Temperature { get; set; } 0.7; public int? MaxTokens { get; set; } 500; // 可以根据需要添加其他参数如top_p, stream等 } public class ChatMessage { public string Role { get; set; } user; // system, user, assistant public string Content { get; set; } string.Empty; }这个ThinkerRequest类封装了所有必要的参数。Messages列表是对话的核心Model字段指定使用的模型Temperature和MaxTokens控制生成文本的随机性和长度。3.2 定义响应模型接下来定义模型服务返回的响应结构。我们关注最核心的回复内容。// Models/ThinkerResponse.cs namespace SmartModelIntegration.Models; public class ThinkerResponse { public string Id { get; set; } string.Empty; public string Object { get; set; } string.Empty; public long Created { get; set; } public ListChatCompletionChoice Choices { get; set; } new(); public UsageInfo Usage { get; set; } new(); } public class ChatCompletionChoice { public int Index { get; set; } public ChatMessage Message { get; set; } new(); public string FinishReason { get; set; } string.Empty; } public class UsageInfo { public int PromptTokens { get; set; } public int CompletionTokens { get; set; } public int TotalTokens { get; set; } }定义好这些类之后我们在代码里就能用thinkerResponse.Choices[0].Message.Content这样清晰的方式来获取AI生成的文本了而不是在一堆JObject里摸索。4. 第三步构建模型调用服务这是集成的心脏部分。我们将创建一个服务类专门负责和SmallThinker的API对话。这里的关键是使用IHttpClientFactory来创建HttpClient而不是直接new HttpClient()前者能更好地管理连接和避免端口耗尽问题。在项目根目录创建一个Services文件夹然后添加服务接口和实现。4.1 定义服务接口先定义一个接口这符合依赖注入的原则也方便后续单元测试。// Services/ISmallThinkerService.cs namespace SmartModelIntegration.Services; public interface ISmallThinkerService { Taskstring GetChatCompletionAsync(string userMessage, CancellationToken cancellationToken default); TaskThinkerResponse GetChatCompletionDetailedAsync(ThinkerRequest request, CancellationToken cancellationToken default); }接口提供了两个方法一个简单的只发用户消息一个详细的可以完全控制请求参数。4.2 实现服务类现在来实现这个接口。注意看构造函数它注入了一个IHttpClientFactory。// Services/SmallThinkerService.cs using System.Text; using System.Text.Json; using Microsoft.Extensions.Options; using SmartModelIntegration.Models; namespace SmartModelIntegration.Services; public class SmallThinkerService : ISmallThinkerService { private readonly HttpClient _httpClient; private readonly JsonSerializerOptions _jsonOptions; private readonly ILoggerSmallThinkerService _logger; // 建议将BaseUrl等配置放在appsettings.json中 private const string BaseUrl http://your-model-service:port; // 替换为你的实际地址 private const string ApiEndpoint /v1/chat/completions; public SmallThinkerService(IHttpClientFactory httpClientFactory, ILoggerSmallThinkerService logger) { // 使用具名的HttpClient配置可以在Program.cs中统一管理 _httpClient httpClientFactory.CreateClient(SmallThinkerClient); _logger logger; _jsonOptions new JsonSerializerOptions { PropertyNameCaseInsensitive true }; } public async Taskstring GetChatCompletionAsync(string userMessage, CancellationToken cancellationToken default) { var request new ThinkerRequest { Messages new ListChatMessage { new ChatMessage { Role user, Content userMessage } } }; var response await GetChatCompletionDetailedAsync(request, cancellationToken); return response.Choices?.FirstOrDefault()?.Message?.Content ?? 模型未返回有效内容。; } public async TaskThinkerResponse GetChatCompletionDetailedAsync(ThinkerRequest request, CancellationToken cancellationToken default) { var url ${BaseUrl}{ApiEndpoint}; var jsonContent JsonSerializer.Serialize(request, _jsonOptions); var httpContent new StringContent(jsonContent, Encoding.UTF8, application/json); _logger.LogInformation(调用模型服务: {Url}, url); var response await _httpClient.PostAsync(url, httpContent, cancellationToken); response.EnsureSuccessStatusCode(); // 如果状态码不是2xx会抛出HttpRequestException var responseString await response.Content.ReadAsStringAsync(cancellationToken); var thinkerResponse JsonSerializer.DeserializeThinkerResponse(responseString, _jsonOptions); if (thinkerResponse null) { throw new InvalidOperationException(反序列化模型响应失败。); } _logger.LogInformation(模型调用成功消耗Token数: {TotalTokens}, thinkerResponse.Usage.TotalTokens); return thinkerResponse; } }代码逻辑很直白序列化请求对象发送POST请求检查响应状态然后反序列化返回结果。日志记录能帮助我们在出问题时快速定位。5. 第四步配置依赖注入与弹性策略服务写好了怎么让它融入ASP.NET Core的血液里呢通过依赖注入。同时我们给HttpClient穿上“防弹衣”——加上Polly重试策略。打开Program.cs文件我们来添加配置。5.1 注册HttpClient与Polly策略在Program.cs的builder.Services配置部分添加如下代码// Program.cs using Polly; using Polly.Extensions.Http; var builder WebApplication.CreateBuilder(args); // ... 其他服务注册 ... // 1. 注册模型服务 builder.Services.AddScopedISmallThinkerService, SmallThinkerService(); // 2. 配置具名的HttpClient并添加Polly策略 builder.Services.AddHttpClient(SmallThinkerClient, client { // 这里可以配置默认的BaseAddress但我们在服务类里已经指定了完整URL。 // client.BaseAddress new Uri(http://your-model-service:port/); client.DefaultRequestHeaders.Add(Accept, application/json); // 如果需要API Key在这里添加 // client.DefaultRequestHeaders.Authorization new AuthenticationHeaderValue(Bearer, your-api-key); }) .AddPolicyHandler(GetRetryPolicy()) // 添加重试策略 .AddPolicyHandler(GetCircuitBreakerPolicy()); // 添加熔断器策略可选 // ... 后续的 app.MapControllers() 等 ...5.2 定义Polly策略方法在Program.cs文件中添加两个私有方法可以放在文件末尾来定义策略// 定义重试策略对于网络波动、短暂的5xx错误或408超时进行重试 static IAsyncPolicyHttpResponseMessage GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() // 处理5xx, 408, 502等 .OrResult(msg msg.StatusCode System.Net.HttpStatusCode.TooManyRequests) // 也处理429请求过多 .WaitAndRetryAsync(3, retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指数退避2, 4, 8秒 onRetry: (outcome, timespan, retryAttempt, context) { // 可以在这里记录日志 var logger context.GetLogger(); logger?.LogWarning(第 {RetryAttempt} 次重试延迟 {Delay}ms。触发原因: {StatusCode}, retryAttempt, timespan.TotalMilliseconds, outcome.Result?.StatusCode.ToString() ?? outcome.Exception.Message); }); } // 定义熔断器策略当失败率达到阈值时快速失败避免拖垮系统 static IAsyncPolicyHttpResponseMessage GetCircuitBreakerPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); // 5次连续失败后熔断30秒 }这样配置后我们的SmallThinkerService在调用模型API时就自动具备了重试和熔断的能力。比如遇到网络闪断它会自动重试3次如果远端服务完全不可用连续失败5次后会在30秒内快速返回失败而不再真正发起请求保护了我们自己的服务。6. 第五步在控制器中使用服务最后一步就是把我们打造好的服务用起来。创建一个新的控制器提供一个简单的文本摘要接口。在Controllers文件夹下新建一个SummarizeController.cs文件。// Controllers/SummarizeController.cs using Microsoft.AspNetCore.Mvc; using SmartModelIntegration.Services; namespace SmartModelIntegration.Controllers; [ApiController] [Route(api/[controller])] public class SummarizeController : ControllerBase { private readonly ISmallThinkerService _thinkerService; private readonly ILoggerSummarizeController _logger; public SummarizeController(ISmallThinkerService thinkerService, ILoggerSummarizeController logger) { _thinkerService thinkerService; _logger logger; } [HttpPost] public async TaskIActionResult SummarizeText([FromBody] SummarizeRequest request) { if (string.IsNullOrWhiteSpace(request?.Text)) { return BadRequest(请求文本不能为空。); } try { _logger.LogInformation(开始处理文本摘要请求长度: {Length}, request.Text.Length); // 构造一个更明确的提示词引导模型进行摘要 var userPrompt $请将以下文本总结为一段简洁的摘要\n\n{request.Text}; var summary await _thinkerService.GetChatCompletionAsync(userPrompt); _logger.LogInformation(文本摘要生成成功。); return Ok(new SummarizeResponse { Summary summary.Trim() }); } catch (HttpRequestException ex) { _logger.LogError(ex, 调用模型服务时发生网络错误。); return StatusCode(503, 模型服务暂时不可用请稍后重试。); } catch (Exception ex) { _logger.LogError(ex, 处理摘要请求时发生未知错误。); return StatusCode(500, 服务器内部错误。); } } } // 请求和响应的DTO放在同一个文件里方便查看也可以单独放 public class SummarizeRequest { public string Text { get; set; } string.Empty; } public class SummarizeResponse { public string Summary { get; set; } string.Empty; }这个控制器很简单接收一段文本通过ISmallThinkerService调用模型生成摘要然后返回结果。注意我们做了基本的参数校验和异常处理并记录了日志这对于生产环境很重要。7. 跑起来看看效果所有代码都写完了让我们启动项目测试一下。运行项目在终端里执行dotnet run。准备测试使用Postman、curl或者你喜欢的任何API测试工具。发送请求URL:POST http://localhost:port/api/summarizeBody (JSON):{ text: 人工智能是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。人工智能从诞生以来理论和技术日益成熟应用领域也不断扩大可以设想未来人工智能带来的科技产品将会是人类智慧的‘容器’。 }查看响应你应该会收到一个JSON响应里面包含了由SmallThinker模型生成的文本摘要。整个过程走下来你会发现集成一个外部模型服务到.NET Core API里并不是简单写个HttpClient调用就完事了。通过服务抽象、依赖注入、弹性策略这些步骤我们构建了一个更健壮、更易维护的集成方案。当你的模型服务需要升级或者你要测试另一个模型时大部分改动都集中在SmallThinkerService这个类里控制器和其他业务逻辑几乎不用动。当然这只是个起点。在实际项目中你可能还需要考虑配置中心管理模型地址、更完善的监控和指标收集、请求响应的缓存、以及更复杂的提示词工程等等。但有了这个坚实的基础后续的扩展都会顺畅很多。希望这个详细的步骤能帮你省下一些摸索的时间。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。