news 2026/10/2 0:20:01

C#开发者的AI落地实践:ASP.NET MVC集成云端与本地QWen大模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#开发者的AI落地实践:ASP.NET MVC集成云端与本地QWen大模型

老实说,在Visual Studio里用C#做ASP.NET MVC开发的程序员,这两年多少都有点焦虑。AI大模型的能力确实诱人,但翻翻教程,清一色的Python、FastAPI、LangChain,感觉我们这个技术栈像是被这波浪潮落下了。这个项目的出发点很简单:用一套完整、可落地的工程思路,在VS里基于C#和ASP.NET MVC,把LLM能力真正接进自己的Web项目。具体就是三件事:用GitHub Copilot加速C#代码的编写,通过阿里云百炼的API把云端QWen(通义千问)接入MVC工程,再通过Ollama把QWen的轻量模型跑在本机,实现本地私密推理。两条链路共用一套代码,用配置一键切换。适合在.NET技术栈做Web应用、想快速集成大模型能力的开发者,也适合刚接触C#的新手用来理解AI应用到底是怎么落地的。这篇东西没有玄虚的概念,全是能跑起来的代码、设计和踩过的坑。

1. 项目整体思路与设计决策

1.1 为什么是C# + ASP.NET MVC

可能有人会问,现在都2025年了,搞AI应用为什么不用Blazor或者Vue + WebAPI,偏偏选ASP.NET MVC?我的回答是:MVC依然是存量企业系统里最扎实的那套框架,尤其是在传统行业、内部管理系统、制造业信息化这些场景中,ASP.NET MVC的占比高到超乎大多数人想象。它把路由、模型绑定、服务端渲染、表单校验都整理得明明白白,对"服务端渲染为主、局部Ajax增强为辅"这种典型业务架构来说,依然是性价比最高的选择。

做LLM应用,MVC有天然的优势。Controller本身就是现成的API入口,加一个Action就能把对话请求转发给大模型;Razor视图应付聊天界面、结果展示、后台管理页绰绰有余;和Visual Studio、NuGet生态的集成又足够顺滑。对比一下,Python方案要自己搭Web框架、自己处理跨域、自己管部署,在C#环境里这些东西框架已经帮你兜好了。

有一类坑我见得太多了:新手一上来就想把整个项目重构成微服务、事件驱动之类的复杂架构,想去适配LLM。其实LLM本质就是一个HTTP接口,不管它有多智能,你的MVC分层该怎么写还是怎么写,只是在某个Service层增加一个调用LLM的类就可以了。这个"能不动就不动"的原则,听起来保守,实际上是最稳的。

1.2 云端QWen与本地QWen的定位分工

标题里的"云端QWen"和"本机QWen"不是二选一的关系,而是两种互补的部署形态。

云端QWen走的是阿里云百炼平台的官方API,模型选择非常丰富,从qwen-turbo到qwen-plus再到qwen-max,能力阶梯很清楚。它的优势是模型智商高、上下文处理能力强、你不需要为硬件掏一分钱;代价是按Token计费、数据要离开本机网络。最适合对回复质量要求高、允许联网、有预算的场景。

本机QWen走的是本地推理路线,我在工程里用Ollama作为运行容器,把QWen系列轻量模型下载到本地跑。优势是一次性投入,免费无限调用,数据完全不出内网,适合隐私敏感、离线环境、以及反复测试的场景;缺点是模型规模受显卡显存和内存限制,一般只能跑7B、14B左右的量子化模型,复杂推理能力和云端大模型有明显差距。

在实际项目里,我是这样分工的:日常聊天、演示、原型验证都走本地模型,速度快、零成本;需要写复杂代码、做长文本总结、理解复杂指令时切到云端模型;如果客户明确要求数据不出内网,那就强制走本地。两边不是互相替代,而是按场景切换。

1.3 工程拓扑:VS、Copilot、LLM三者如何协作

再说说这个工程里三个关键角色如何协作。很多人把GitHub Copilot理解成"机器学习的运行时依赖",这是不对的。Copilot只存在开发阶段,它不会编译进最终的MVC程序里,也不会在运行时帮你推理什么,它只是你写代码时的一个AI结对程序员。

打个比方:VS是你的开发车间,Copilot是车间里经验丰富的老师傅,MVC工程是你的产品线,云端QWen是外包的专家顾问团,本机QWen是你自己购置的核心设备。老师傅帮你快速搭建流水线框架、生成样板代码;专家顾问团帮你处理高难度的疑难杂症;核心设备帮你跑日常高频任务。三者各司其职,组合起来就是一个完整、高效的开发生产环境。

从代码层面看,Copilot帮我把写Controller、Razor页面、DTO类这些耗时又琐碎的工作压缩到原来的三分之一左右。而真正和LLM打交道的HTTP调用、错误处理、成本控制、安全校验,这些核心逻辑仍然需要你自己理清楚。这就像你可以让老师傅帮你砌墙,但墙的承重结构你得自己盯着。

2. 连接云端阿里QWen的完整实现

2.1 云端接入的前置准备

连接云端QWen的第一步,是注册阿里云账号,在控制台里找到阿里云百炼平台(Model Studio),开通模型服务,然后创建API Key。这一步本身不复杂,但有两个细节很容易被人忽略。

第一,API Key不要写死在代码里。严谨的做法是放在appsettings.json里,或者更进一步放到环境变量、密钥管理服务里。因为代码一旦提交到仓库,哪怕只是私有仓库,API Key泄露的风险都非常高,别人拿到你的Key就能调用你的额度,账单可不会客气。

第二,搞清楚计费模型。百炼平台的不同模型价格差异非常大,qwen-turbo便宜、qwen-plus适中、qwen-max最贵。平时做测试、跑自动化脚本,用qwen-turbo就够了;只有真正需要复杂推理时才切到qwen-plus或者qwen-max。我见过有人拿qwen-max跑了一整天测试,第二天看账单直接傻眼。

接口地址是固定的OpenAI兼容端点:

https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions

这意味着你完全不需要去研究阿里系复杂的原生签名格式。百炼平台把OpenAI的协议原封不动地兼容了,你可以像调用任何标准OpenAI接口一样,直接把消息体POST过去。这一点对C#开发者来说是巨大的福音,意味着市面上的OpenAI SDK和代码示例,几乎都能直接套到QWen云端的调用上。

2.2 用HttpClient对接OpenAI兼容接口的核心代码

下面是这个工程里云端链路的最小实现。在ASP.NET Core MVC的Controller里,直接在Action方法中调用。

using System.Text; using System.Text.Json; public async Task<IActionResult> SendToCloud(string message) { var apiKey = _configuration["LLM:CloudApiKey"]; var payload = new { model = "qwen-plus", messages = new[] { new { role = "system", content = "你是一个专业的C#技术助手" }, new { role = "user", content = message } }, stream = false }; using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync( "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", content); if (!response.IsSuccessStatusCode) { return Json(new { error = $"云端LLM调用失败: {response.StatusCode}" }); } var result = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(result); var reply = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); return Json(new { reply = reply }); }

这里的几个要点,尤其是新手容易忽略的:

  • Authorization: Bearer是OpenAI兼容协议的标准写法,百炼平台识别这个请求头来确认你的身份。
  • 构造函数里通过IoC容器注入IConfiguration,从配置里读API Key,而不是硬编码。
  • 直接用new HttpClient()在上面的示例里是为了可读性。生产项目一定要用IHttpClientFactory,注册成单例或Typed Client,否则高并发下会出现Socket端口耗尽。

提示:生产环境不要每个请求都new一个HttpClient,请通过services.AddHttpClient()注入。连接复用带来的性能提升非常明显。

2.3 流式输出、Token消耗与成本权衡

上面代码用的是非流式调用,即stream: false。这个模式实现简单、便于调试,适合大多数管理后台、工具类应用。但它有一个很实际的体验问题:模型思考需要时间,如果生成一段200字的回复可能要几秒,前端这段时间完全没有反馈,用户会觉得"卡死了"。

要做流式效果,把payload里的stream设为true,然后在C#里逐行读取Server-Sent Events(SSE)流,解析每一条data: {...}格式的数据。这种模式适合对交互体验要求高的聊天界面,但如果你只是做一个内部工具或者后台助手,非流式的简洁性反而更香。

另一个必须理解的成本概念是Token。Token不是只算你最终得到的回复,而是请求里的所有内容都要计费——系统提示词、历史对话、用户输入、模型的输出,全部折算成Token。简单理解,输入越多越贵、回复越长越贵。所以服务端要做一件事:把历史消息控制在合理长度内,比如只保留最近10轮对话,或者对旧消息做自动摘要,否则随着聊天变长,每次请求的费用会越滚越高。

3. 连接本机QWen模型的完整实现

3.1 Ollama部署与模型下载

把QWen模型跑在本机,我推荐用Ollama。这是目前体验最省心的本地模型运行工具,下载安装包后直接装完,命令行两条命令就能把模型拉下来,不需要你手动配置Python环境、PyTorch、CUDA这些麻烦的依赖。

ollama pull qwen2.5:7b ollama run qwen2.5:7b

ollama run执行后会在终端里启动一个交互式对话窗口,这时候你直接在里面输几句中文,确认模型能正常回复、速度能接受。这一步异常重要,千万别急着写C#代码,先用命令行把模型跑通。如果终端里都出不来内容,说明模型下载有问题或者硬件资源不足,这时候去调代码只会加大排查难度。

硬件建议我也顺便整理一下:7B的QWen模型如果是4bit量化版,加载大约需要8GB左右的内存或显存,普通家用显卡可以跑;如果没有独显,纯CPU也能跑,但速度会慢到让人抓狂,一个字等好几秒是常事。想跑14B模型,建议至少有16GB的显存或者大内存,否则系统会开始吃Swap,整个机器连带卡死。

3.2 C#调用本地模型的API

Ollama在启动后默认监听http://localhost:11434。它除了提供自己的原生/api/chat接口之外,还提供了一个OpenAI兼容接口/v1/chat/completions。这个细节是整个工程里最优雅的一个设计:调用本机QWen和调用云端QWen,消息格式几乎完全一致。

public async Task<IActionResult> SendToLocal(string message) { var payload = new { model = "qwen2.5:7b", messages = new[] { new { role = "user", content = message } }, stream = false }; using var httpClient = new HttpClient(); var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync( "http://localhost:11434/v1/chat/completions", content); if (!response.IsSuccessStatusCode) { return Json(new { error = $"本地LLM调用失败: {response.StatusCode}" }); } var result = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(result); var reply = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); return Json(new { reply = reply }); }

注意我特意选了/v1/chat/completions而不是原生的/api/chat,就是为了让返回结构和云端对齐。如果你调Ollama原生接口,返回的是一个message字段加done字段的结构,也要单独写一套解析逻辑。既然云端用OpenAI兼容协议,本地也统一用OpenAI兼容协议,解析代码一次搞定,后面维护成本直接减半。

3.3 本地与云端:一条可切换的统一调用链

既然两边协议一致,我强烈建议把"发送一条消息给LLM"这个动作抽成一个服务类,这是整个工程最有价值的一个抽象。

public class LLMService { private readonly string _provider; private readonly string _cloudUrl; private readonly string _localUrl; private readonly string _apiKey; private readonly string _cloudModel; private readonly string _localModel; public LLMService(IConfiguration config) { _provider = config["LLM:Provider"] ?? "Local"; _cloudUrl = config["LLM:CloudUrl"] ?? "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"; _localUrl = config["LLM:LocalUrl"] ?? "http://localhost:11434/v1/chat/completions"; _apiKey = config["LLM:CloudApiKey"]; _cloudModel = config["LLM:CloudModel"] ?? "qwen-plus"; _localModel = config["LLM:LocalModel"] ?? "qwen2.5:7b"; } public async Task<string> GetResponseAsync(string userMessage) { var isCloud = _provider.Equals("Cloud", StringComparison.OrdinalIgnoreCase); var url = isCloud ? _cloudUrl : _localUrl; var model = isCloud ? _cloudModel : _localModel; var payload = new { model = model, messages = new[] { new { role = "system", content = "你是一个乐于助人的助手,请用简洁准确的中文回答。" }, new { role = "user", content = userMessage } }, stream = false }; using var httpClient = new HttpClient(); if (isCloud) { httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(url, content); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(result); var reply = doc.RootElement.GetProperty("choices")[0] .GetProperty("message").GetProperty("content").GetString(); return reply ?? "(模型没有返回内容)"; } }

这个服务类的设计思路是:把"在哪里调用LLM"这个环境差异,收敛成一个Provider配置项。Controller层根本不需要知道当前是云端还是本地,只要调用_llmService.GetResponseAsync()就行了。在维护多套环境、给客户做演示、做离线部署的时候,这种"一个开关切换后端"的设计真的能救命。

提示:在生产工程中,建议把LLMService注册为Scoped或Singleton,配合IHttpClientFactory注入HttpClient,而不是在方法内部每次创建新的实例。

4. GitHub Copilot在VS中的接入实战

4.1 Copilot扩展安装与身份配置

GitHub Copilot在Visual Studio里的接入,步骤简单到有点出人意料。打开VS 2022,进入"扩展"→"管理扩展"→"联机",搜索"GitHub Copilot"安装,重启VS。首次使用会要求你登录GitHub账号并授权,完成之后编辑器就会出现内联代码补全的提示。

关于日常使用,我的建议是不要把Copilot当成一个需要专门"召唤"的功能,它更像是一个有上下文感知的输入法。你正常敲代码,它会基于你当前的文件、光标位置、历史操作,给出灰色文字形式的补全候选,按Tab接受即可。需要手动触发时,按Alt+/就能在光标处调出候选列表。VS 2022的新版本还内置了Copilot Chat面板,你可以直接在IDE里和它对话,让它解释一段报错、生成单元测试、或者重构某个类。

需要提醒的是,你的Visual Studio必须能够正常连接GitHub服务,账号授权状态也要保持有效。如果遇到补全不出现的情况,先检查网络连通性和账号登录状态,而不是去乱改系统配置。

4.2 用Copilot加速MVC开发的实操场景

在这个LLM工程里,我把Copilot用得最狠的地方是框架搭建阶段。比如我要给聊天记录加一个数据库存储功能,只需要在Controller里写一个带注释的空白方法:

// 在数据库中保存一条聊天记录,包含会话ID、用户消息、模型回复、时间戳 public async Task SaveChatRecordAsync(...) { }

Copilot很快就根据注释和上下文补全了基于EF Core的增删改查样板代码,字段映射、异步方法、异常处理都有。我只需要根据实际表名调整一下实体属性,再补上数据校验逻辑,一个完整的持久化功能就从原来的半小时压缩到了五分钟。

再比如写Razor视图的时候,我输入一个<input>标签,Copilot能根据上下文直接补出Bootstrap风格的聊天输入框和发送按钮。虽然生成的样式细节仍然需要手调,但那个"从零敲一遍HTML/CSS类名"的过程被省掉了,体验很爽。

我还有一个特别推荐的用法:把"如何解析Ollama返回的JSON"这个问题直接丢给Copilot Chat。你只需要复制一段Ollama返回的样例,让它帮你设计C#的强类型模型,它会连大小写映射、可空字段处理都一并考虑进去,比自己手写DTO靠谱多了。

不过这里必须说一句冷静的话:Copilot可以帮你快速生成大量代码,但它不理解你的业务约束和架构底线。LLM调用的鉴权、输入校验、敏感信息处理、成本控制这些地方,必须自己一遍遍确认。提效归提效,工程质量的把控永远是自己的责任。

5. 完整Demo:一个MVC聊天应用

5.1 工程结构与聊天控制器设计

我搭的Demo是一个标准的ASP.NET Core MVC项目,刻意保持了最小结构,方便你直接套用。核心文件只有四个:

  • ChatController.cs:接收用户输入,调LLMService,把结果返回给前端。
  • LLMService.cs:上一节写的统一LLM调用服务。
  • Views/Chat/Index.cshtml:聊天界面。
  • appsettings.json:配置云端/本地的切换。

Controller里我加上了CancellationToken支持。这是很多人会忽略的一个细节:LLM接口通常比较慢,如果用户发了消息后立刻刷新页面或关闭浏览器,服务端应该停止等待模型返回,而不是继续挂着连接白白消耗资源。CancellationToken接入ASP.NET Core的请求管道后,用户断开时它会自动触发,你只需要把它传给HttpClient即可。

public class ChatController : Controller { private readonly LLMService _llmService; public ChatController(LLMService llmService) { _llmService = llmService; } [HttpGet] public IActionResult Index() { return View(); } [HttpPost] public async Task<IActionResult> Send(string userInput, CancellationToken ct) { if (string.IsNullOrWhiteSpace(userInput)) { return Json(new { error = "输入不能为空" }); } try { var reply = await _llmService.GetResponseAsync(userInput.Trim()); return Json(new { reply = reply }); } catch (Exception ex) { return Json(new { error = $"调用LLM时发生异常:{ex.Message}" }); } } }

关于多轮对话,这个Demo刻意没有做。原因是聊天历史可以由前端保存,每次请求时带着上下文传给大模型即可。如果你想在服务端保存历史,可以加一个从Session或数据库读取消息列表的逻辑,再把messages数组的构造从单条消息扩展成列表。

5.2 前端页面、异步调用与流式渲染

聊天界面我用Razor搭建一个简单的布局:上方是消息展示区,下方是输入框和发送按钮。完全没有引入前端框架,适合零基础的人直接抄作业。

@{ ViewData["Title"] = "QWen 本地/云端聊天测试"; } <div class="container" style="max-width: 800px; margin-top: 30px;"> <h3>QWen 聊天测试台</h3> <div id="chatBox" style="height: 450px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; border-radius: 8px;"></div> <div style="display: flex; gap: 8px; margin-top: 15px;"> <input id="userInput" type="text" class="form-control" placeholder="输入你的问题..." /> <button id="btnSend" class="btn btn-primary">发送</button> </div> </div> @section Scripts { <script> document.getElementById('btnSend').addEventListener('click', async () => { const input = document.getElementById('userInput'); const text = input.value.trim(); if (!text) return; const chatBox = document.getElementById('chatBox'); chatBox.innerHTML += '<div style="text-align: right; margin: 5px;">' + text + '</div>'; input.value = ''; const btn = document.getElementById('btnSend'); btn.disabled = true; btn.innerHTML = '等待模型回复中...'; try { const res = await fetch('@Url.Action("Send", "Chat")', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'userInput=' + encodeURIComponent(text) }); const data = await res.json(); if (data.error) { chatBox.innerHTML += '<div style="color: red; margin: 5px;">' + data.error + '</div>'; } else { chatBox.innerHTML += '<div style="text-align: left; margin: 5px; background: #f5f5f5; padding: 8px; border-radius: 8px;">' + data.reply + '</div>'; } } catch (ex) { chatBox.innerHTML += '<div style="color: red;">请求出错:' + ex.message + '</div>'; } finally { btn.disabled = false; btn.innerHTML = '发送'; chatBox.scrollTop = chatBox.scrollHeight; } }); </script> }

这里有个容易被忽略的细节:模型回复里经常带有换行符,但HTML渲染时会折叠掉,导致整段文字挤成一坨。一个简单的处理方式是渲染前把\n替换成<br>,或者给显示层加white-space: pre-wrap的CSS样式。我建议用后者,更干净。

如果是生产级聊天,还要考虑输入内容的转义问题,防止用户输入的内容被当作HTML注入。别在这个Demo里直接渲染用户输入,应该先对内容做编码处理。

5.3 配置文件与多环境切换

appsettings.json是整个工程切换云端/本地的总开关:

{ "Logging": { "LogLevel": { "Default": "Information" } }, "LLM": { "Provider": "Cloud", "CloudApiKey": "sk-你的阿里云百炼APIKey", "CloudModel": "qwen-plus", "CloudUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", "LocalUrl": "http://localhost:11434/v1/chat/completions", "LocalModel": "qwen2.5:7b" } }

平时开发调试,我会把Provider设成Local,零成本、速度快、离线可用;需要演示或验证复杂能力时,再切到Cloud用qwen-plus撑住场面。整体体验就是改一个字符串,从"自建核心车间"切换成"外包专家团队"。

强烈提醒:API Key保存在配置文件里没问题,但绝对不要把配置里的任何密钥传到前端页面,更不要因为本地接口"反正没人访问"就省略服务端校验。安全习惯一放松,后面就要用事故来补课。

6. 常见问题与排查技巧实录

6.1 高频问题速查表

现象可能原因解决思路
云端返回401API Key无效、过期或权限未开通去百炼平台检查API Key状态,确认已开通对应模型服务
云端返回404模型名写错或模型不存在核对模型名,例如qwen-turbo、qwen-plus、qwen-max
本地Ollama报connection refusedOllama服务没启动或端口不对先跑ollama serve确认服务监听11434端口
本地模型调用超时模型太大、显存不够、CPU推理太慢换更小的模型如qwen2.5:3b,或确认量化参数
返回内容中文乱码JSON序列化编码问题使用StringContent时指定Encoding.UTF8,content-type设为application/json; charset=utf-8
回复内容被截断非流式返回超长文本,前端展示不全检查返回JSON的usage字段,或改用流式输出
Token费用突然飙升历史消息无节制增长,导致输入Token重复计费服务端对历史做截断,比如只保留最近10条消息
Copilot补全不出现网络无法访问GitHub或账号授权失效检查网络连通性,重新登录GitHub账号并确认授权

6.2 踩坑记录与独家避坑技巧

第一个大坑是解析百炼返回时没判空。当模型因为内容安全过滤等原因没有返回正常回复时,choices数组可能是空的,这时候如果你直接choices[0]去取值,代码会直接抛异常。正确的姿势是先判断数组是否有元素,再取message.content。

第二个大坑是HttpClient复用时的请求头污染。云端接口需要Authorization请求头,本地Ollama不需要。如果同一个HttpClient先调了云端再调本地,Authorization头会一直带着,虽然Ollama大概率会忽略,但这种隐式的状态耦合非常危险。我建议云端和本地分别使用独立的HttpClient实例,或者在每次请求前显式清理请求头。

第三个坑是ASP.NET MVC 5(非Core版本)下使用Json()方法返回匿名对象时,默认的JavaScriptSerializer遇到复杂对象图可能会抛循环引用异常。如果你还在维护老框架,最省事的方式是返回Content(jsonString, "application/json"),把序列化完全掌控在自己手里。

第四个坑是本地模型的能力边界。Qwen2.5 7B做简单问答、摘要、改写都很不错,但遇到多步推理、复杂代码生成,它会出现前后矛盾或者一本正经地胡说八道。我现在的分工策略是:本地模型负责结构化程度高的任务,云端大模型负责开放性强、需要创造力和推理深度的任务。别对本地小模型抱有不切实际的期待,它只是一个"快而省的助理",不是"全知的专家"。

最后再分享一个真正的私藏技巧:调试LLM接口时,先在浏览器或Postman里反复确认请求和返回,再进C#代码。特别是最开始接触云端QWen的时候,用Postman发一个最简单的hello消息,把返回格式看清楚,再动手写C#调用。这个习惯帮我避开了至少一半的坑。

这个项目整套做下来,我最大的体会是:C#开发者完全不用等别人把Python的AI方案嚼碎了再喂给我们。LLM本质上就是一个HTTP接口,后端用什么语言根本不重要,重要的是你有没有一套清晰的接入思路。照着这个方案,你现在就能把云端QWen和本机QWen两条链路都搭起来,让Copilot把重复代码量压下去,剩下的就是不断调提示词、优化成本、打磨交互体验。技术在变,但只要掌握了"接口调用 + 统一抽象 + 按需切换"这套底层逻辑,下一次模型升级、换服务商,对你来说都只是改几行配置的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 0:13:55

EasyExcel导出异常 Can not close IO 根因与关流排查

凌晨两点被一个导出接口的告警叫醒&#xff0c;日志里只有一行Can not close IO&#xff0c;堆栈往上翻三层全是 EasyExcel 的类名&#xff0c;看起来像是框架自己出了问题。如果你也踩过使用 EasyExcel 导出 Excel 抛异常 Can not close IO这个坑&#xff0c;大概率已经搜过一…

作者头像 李华
网站建设 2026/10/2 0:12:30

谷粒商城实战指南:SpringBoot电商微服务搭建与避坑

简介&#xff1a;本资源是面向Java后端开发者与分布式系统学习者的微服务电商实战项目&#xff0c;聚焦高并发、高可用电商场景下的分布式架构设计与落地。项目基于Spring Cloud Alibaba技术栈&#xff0c;完整覆盖微服务拆分、Nacos服务注册发现、Gateway网关统一入口、Seata分…

作者头像 李华
网站建设 2026/10/2 0:02:24

Autosar与Simulink集成常见问题深度解析

1. 为什么AutosarSimulink组合在实际建模中“处处是坑”——一个老手的血泪复盘Autosar、Simulink、RTE、IRV——这四个词凑在一起&#xff0c;对任何做过车规级ECU开发的工程师来说&#xff0c;不是技术栈&#xff0c;而是压力测试清单。我带过三支嵌入式软件团队&#xff0c;…

作者头像 李华
网站建设 2026/10/1 23:59:47

openrig 实战:用 YAML 与 npm 统一管理 Claude Code 和 Codex 配置

1. 从"openrig"这个名字说起&#xff1a;它到底想解决什么问题第一次看到openrig这个词&#xff0c;我下意识把它拆成了两半&#xff1a;open和rig。rig在工程语境里通常指"装配好的成套设备"或者"工作台"&#xff0c;比如矿机叫 mining rig&…

作者头像 李华
网站建设 2026/10/1 23:59:33

openrig开放式铝型材机架DIY全攻略:选型、组装与散热实践

一直折腾硬件这些年&#xff0c;我越来越觉得很多设备其实不需要一个“铁盒子”捂得严严实实&#xff0c;尤其是桌面开发机、NAS、软路由、音视频调试设备这类东西。所以当周围朋友开始聊 openrig 这个概念时&#xff0c;我第一反应是&#xff1a;这不就是我们折腾了半天的开放…

作者头像 李华