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

本地部署deepseek大模型后使用c# winform调用(可离线)

        介于最近deepseek的大火,我就在想能不能用winform也玩一玩本地部署,于是经过查阅资料,然后了解到ollama部署deepseek,最后用ollama sharp NUGet包来实现winform调用ollama 部署的deepseek。

        本项目使用Vs2022和.net 8.0开发,ollama sharp 使用的是最新版本。也可以使用.net farmwork 4.7.2开发,但是ollama sharp 没办法使用最新的,只能使用3.几的版本,3点几的版本有问题,因为ollama sharp提供的交互方法不是异步的,这就会导致,大模型如果回复你一个很长的的问题的时候,就会突然中断,最后我就彻底放弃了,发现最新版本的ollama sharp的交互方法是异步的,最后抱着试一试的心态,果然成功了,让写个4000字的论文框架,基本上回答时间在2分钟左右也不会中断,(2分钟是因为我的内存有点少,显卡还行吧)。效果还是很不错的,本人使用的deepseek r1 14b的大模型,4060的显卡,16G的内存,回复速度还是很快的,内存基本上跑80%左右。显卡40%上下浮动。

展示图

下载ollama

地址:奥拉马

下载Windows版本然后进行安装就好了,安装完成以后,我们可以在系统环境变量里面添加这两个

第二个是利用ollama下载的大模型的位置,C盘不够的可以加这个变量,如果C盘够多可以忽略,最好设置完以后重启一下电脑再安装ollama,安装好以后可以打开cmd 如图所示:如果是这样,说明你已经安装成功了,

利用ollama安装deepseek r1 14b

这里我们还是打开ollama网站,打开

如果说内存在32G可以选择32b的体验一下,应该会比14b更好用些,最后点击箭头所指的地方复制下来打开cmd,直接ctrl+c复制然后回车他就会自动下载,这里有个小技巧:他下载会越来越慢,我们可以按一下ctrl+c,再按一下键盘的上方向键他就会接着下载,这个时候慢慢就快起来了。

下载完成后我们新打开一个cmd输入ollama list这个可以查看我们已经下载下来的大模型

补充一点:还可以使用ollama rm 大模型的Name进行删除

Ollama Sharp

awaescher/OllamaSharp:在 .NET 中使用 Ollama API 的最简单方法 

上面的是链接地址,这是github里面的一个开源项目,使用之前可以看看他的介绍以及使用方法,知其然,知其所以然。

winform 连接大模型

我们打开我们的vs2022。创建新工程,一定要选择后面不带括号.netfarmwork的,才会用到8,.0框架

我们进去以后先添加nuget包,找到依赖项,右键管理NUGET包,打开以后搜索ollama sharp

这里我已经安装过了

等待安装成功以后,我们打开我们窗体的设计器,在左侧的工具箱添加一下的控件

listbox主要用来展示安装的大模型

richtextbox主要用来展示用户输入的文字和deepseek回复的文字

textBox读取用户输入的文字

一个发送按钮一个取消思考按钮

附上源代码:

using OllamaSharp.Models;
using OllamaSharp;
using System.Text.RegularExpressions;namespace WinFormsApp1
{public partial class Form1 : Form{private Uri uri;private OllamaApiClient ollama;private List<Model> models;private bool connect;static ManualResetEvent resetEvent = new ManualResetEvent(false);private CancellationTokenSource cancellationTokenSource;int step = 0;private bool mIsCancel = false;public Form1(){InitializeComponent();}private async void Form1_Load(object sender, EventArgs e){richTextBox1.AppendText("稍等,我正在加载模型。。。。。" + Environment.NewLine);uri = new Uri("http://localhost:11434");ollama = new OllamaApiClient(uri);connect = await ollama.IsRunningAsync();models = (await ollama.ListLocalModelsAsync()).ToList();mSelectItem = 0;LoadModles();step = 1;richTextBox1.AppendText("请在上方选择你要使用的模型,单击即可" + Environment.NewLine);}/// <summary>/// 流程交互/// </summary>public void WorkFololw(){Task.Run(() =>{while (true){Thread.Sleep(200);string cleanText = "";if (textBox2.Text != ""){cleanText = textBox2.Text;}switch (step){case 1:Thread.Sleep(100);if (models.Count == 0){return;}ollama.SelectedModel = models.ToArray()[mSelectItem].Name; // 选择模型名称Log("我已经准备好了小帅哥快来玩呀!", 0, Color.Black);step = 2;break;case 2:if (cleanText.Contains("\r\n")){var prompt = textBox2.Text; // 从文本框读取提示词Log(Environment.NewLine + "用户哥:" + textBox2.Text.TrimEnd('\r', '\n') + Environment.NewLine, 0, Color.Blue);var keepChatting = true;var chat = new Chat(ollama, prompt);Invoke(new Action(() =>{button2.Visible = true;richTextBox1.AppendText("deepSeek-R1>:" + Environment.NewLine);}));BeginSiKao(keepChatting, chat, "");step = 3;}break;case 3:if (cleanText.Contains("\r\n"))step = 2;break;}}});}/// <summary>/// 开始思考/// </summary>/// <param name="keepChatting"></param>/// <param name="chat"></param>public async void BeginSiKao(bool keepChatting, Chat chat, string mImageMsg){//开始聊天await BeginChat(keepChatting, chat, mImageMsg);}/// <summary>/// 加载本地大模型/// </summary>public void LoadModles(){if (models.Any()){foreach (var model in models){if (model.Name.Contains("v2")){Log($"大模型:{model.Name} {model.Size / 1024 / 1024} MB", 1, Color.MediumSeaGreen); // 输出模型名称和大小}Invoke(new Action(() =>{listBox1.Items.Add($"大模型:{model.Name} {model.Size / 1024 / 1024} MB");}));}}else{Log("没有大模型环境,请自行下载大模型", 1, Color.Red);return;}}/// <summary>/// 开始聊天/// </summary>/// <param name="keepChatting"></param>/// <param name="chat"></param>/// <returns></returns>public async Task BeginChat(bool keepChatting, Chat chat, string ImageMsg){cancellationTokenSource = new CancellationTokenSource();var tokenx = cancellationTokenSource.Token;Invoke(new Action(() =>{button1.Text = "思考回答中...";}));string message;message = textBox2.Text.TrimEnd('\r', '\n'); // 从文本框读取用户输入的消息if (message == ""){message = ImageMsg;}Clear(); // 清空文本框以便用户输入下一条消息Task sendTask = Task.Run(async () =>{if (string.IsNullOrEmpty(message.Trim())){return;}bool isFirstToken = true;try{string mmsf = "";await foreach (var answerToken in chat.SendAsync(message)){// 如果取消了操作,提前退出if (cancellationTokenSource.Token.IsCancellationRequested){continue;}if (answerToken != "<think>" && answerToken != "</think>"){mmsf += answerToken;// 使用Invoke更新UIrichTextBox1.Invoke(new Action(() =>{if (isFirstToken){richTextBox1.Focus();isFirstToken = false;}richTextBox1.AppendText(answerToken.Trim());}));}}string newmsg = "";if (mmsf.Contains("```sql")) {newmsg= FormatSql(mmsf);// 使用Invoke更新UIrichTextBox1.Invoke(new Action(() =>{if (isFirstToken){richTextBox1.Focus();isFirstToken = false;}richTextBox1.AppendText(newmsg.Trim());}));}}catch (OperationCanceledException){// 处理取消操作时的异常Invoke(new Action(() =>{if (button1.Text == "思考回答中..."){button1.Text = "发送";}}));}catch (Exception ex){if (mIsCancel == true){// 捕获其他类型的异常并记录Log(Environment.NewLine + $"用户哥取消了回答", 0, Color.Red);mIsCancel = false;}else{// 捕获其他类型的异常并记录Log(Environment.NewLine + $"哎呦出错了" + ex, 0, Color.Red);}}});await sendTask;Invoke(new Action(() =>{button2.Visible = false;  // 隐藏取消按钮button1.Text = "发送";textBox2.Focus();richTextBox1.AppendText(Environment.NewLine);}));}/// <summary>/// 清空输入文本框/// </summary>public void Clear(){Invoke(new Action(() =>{textBox2.Clear();}));}/// <summary>/// 更新控件的一些值或者追加文字/// </summary>/// <param name="message"></param>/// <param name="mtype">0:追加文字,1:大模型使用</param>public void Log(string message, int mtype, Color color){if (mtype == 0){Invoke(new Action(() =>{richTextBox1.AppendText(message + Environment.NewLine);textBox2.Focus();}));}else{Invoke(new Action(() =>{label1.Text = message;label1.ForeColor = color;textBox2.Focus();}));}}/// <summary>/// 发送按钮/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){if (models.Count == 0){MessageBox.Show("没有大模型环境,怎么玩啊!");return;}if (button1.Text == "思考回答中..."){MessageBox.Show("正想着呢,别点了爷们");}else{string mm = textBox2.Text;textBox2.Text = "用户哥:" + mm + Environment.NewLine;Log(textBox2.Text.TrimEnd('\r', '\n'), 0, Color.Blue);var prompt = mm; // 从文本框读取提示词var keepChatting = true;var chat = new Chat(ollama, prompt);Invoke(new Action(() =>{richTextBox1.AppendText("deepSeek-R1>:");}));BeginSiKao(keepChatting, chat, "");step = 3;if (button2.Visible == false){button2.Visible = true;}}}/// <summary>/// 取消回答/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button2_Click(object sender, EventArgs e){mIsCancel = true;StopThinking();}public void StopThinking(){cancellationTokenSource?.Cancel();  // 取消当前的操作Invoke(new Action(() =>{button2.Visible = false;  // 隐藏取消按钮button1.Enabled = true;  // 恢复发送按钮textBox2.Focus();  // 让用户可以继续输入}));}private int mSelectItem = 99;private void listBox1_SelectedIndexChanged(object sender, EventArgs e){mSelectItem = listBox1.SelectedIndex;WorkFololw();}public static string FormatSql(string input){// 移除开头的 sql 和多余的空格input = input.Trim();// 用正则表达式找到从 sql 开头到下一个结束符号的 SQL 代码string pattern = @"`sql(.*?)```";var match = Regex.Match(input, pattern, RegexOptions.Singleline);if (match.Success){// 获取 sql 语句部分string sql = match.Groups[1].Value.Trim();// 分析 SQL 的每个部分并格式化return FormatSqlServerCreateTable(sql);}return input;}private static string FormatSqlServerCreateTable(string sql){// 分割 SQL 语句sql = sql.Replace("CREATETABLE", "CREATE TABLE").Replace("NOTNULL", "NOT NULL").Replace("VARCHAR", "VARCHAR").Replace("NVARCHAR", "NVARCHAR").Replace("CHECK", "CHECK").Replace("PRIMARYKEY", "PRIMARY KEY").Replace("UNIQUE", "UNIQUE").Replace("CHAR", "CHAR").Replace("DATENOTNULL", "DATE NOT NULL").Replace("TEXT", "TEXT").Replace("--", "-- "); // 确保注释有一个空格// 添加换行和缩进string formattedSql = "";int indentationLevel = 0;bool insideComment = false;for (int i = 0; i < sql.Length; i++){char currentChar = sql[i];// 检查是否进入注释if (i < sql.Length - 1 && sql.Substring(i, 2) == "--"){insideComment = true;}// 增加缩进处理if (currentChar == '('){formattedSql += " (";indentationLevel++;}else if (currentChar == ')'){formattedSql += "\n" + new string(' ', indentationLevel * 4) + ")";indentationLevel--;}else if (currentChar == ','){formattedSql += ",\n" + new string(' ', indentationLevel * 4);}else{if (insideComment){formattedSql += currentChar;if (currentChar == '\n'){insideComment = false;}}else{formattedSql += currentChar;}}}return formattedSql;}}
}

有些地方有些小bug,比如取消思考没有进行细节的处理,但是不影响正常的使用,

整体的逻辑就是:窗体启动时候在线程里面进行一个死循环,只要textBox文本框里面出现回车就根据变量step的值来进行对应的操作。目前无法给deepseek发送图片让他进行分析,只支持文字对话。断网也是可以继续运行的。

如有更好的想法,欢迎大家评论区畅所欲言!

相关文章:

本地部署deepseek大模型后使用c# winform调用(可离线)

介于最近deepseek的大火&#xff0c;我就在想能不能用winform也玩一玩本地部署&#xff0c;于是经过查阅资料&#xff0c;然后了解到ollama部署deepseek,最后用ollama sharp NUGet包来实现winform调用ollama 部署的deepseek。 本项目使用Vs2022和.net 8.0开发&#xff0c;ollam…...

Python----数据分析(Numpy:安装,数组创建,切片和索引,数组的属性,数据类型,数组形状,数组的运算,基本函数)

一、 Numpy库简介 1.1、概念 NumPy(Numerical Python)是一个开源的Python科学计算库&#xff0c;旨在为Python提供 高性能的多维数组对象和一系列工具。NumPy数组是Python数据分析的基础&#xff0c;许多 其他的数据处理库&#xff08;如Pandas、SciPy&#xff09;都依赖于Num…...

Leetcode-最大矩形(单调栈)

一、题目描述 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 输入&#xff1a;matrix [["1","0","1","0","0"],["1","0&…...

域内委派维权

为某个服务账户配置 krbtgt 用户的非约束性委派或基于资源的约束性委派。这里我的 krbtgt 的基于资源约束性委派我利用不了&#xff0c;所以使用的是域控的机器账户 dc01$ 进行维权。 抓取所有 hash。 mimikatz.exe "privilege::debug" "lsadump::dcsync /doma…...

leetcode---LCR 140.训练计划

给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号&#xff0c;请查找并返回倒数第 cnt 个训练项目编号。 示例 1&#xff1a; 输入&#xff1a;head [2,4,7,8], cnt 1 输出&#xff1a;8 提示&#xff1a; 1 < head.length < 1000 < head[i] <…...

Linux基础 -- ARM 32位常用机器码(指令)整理

ARM 32位常用机器码&#xff08;指令&#xff09;整理 1. 数据处理指令&#xff08;运算、逻辑、比较&#xff09; 指令含义示例备注MOV赋值&#xff08;寄存器传输&#xff09;MOV R0, R1直接将 R1 复制到 R0MVN取反MVN R0, R1R0 ~R1ADD加法ADD R0, R1, R2R0 R1 R2ADC带进…...

内存中的缓存区

在 Java 的 I/O 流设计中&#xff0c;BufferedInputStream 和 BufferedOutputStream 的“缓冲区”是 内存中的缓存区&#xff08;具体是 JVM 堆内存的一部分&#xff09;&#xff0c;但它们的作用是优化数据的传输效率&#xff0c;并不是直接操作硬盘和内存之间的缓存。以下是详…...

基于 Spring Boot 的 +Vue“宠物咖啡馆平台” 系统的设计与实现

大家好&#xff0c;今天要和大家聊的是一款基于 Spring Boot 的 “宠物咖啡馆平台” 系统的设计与实现。项目源码以及部署相关事宜请联系我&#xff0c;文末附上联系方式。 项目简介 基于 Spring Boot 的 “宠物咖啡馆平台” 系统设计与实现的主要使用者分为 管理员、用户 和…...

LeetCode 解题思路 7(Hot 100)

解题思路&#xff1a; 初始化窗口元素&#xff1a; 遍历前 k 个元素&#xff0c;构建初始单调队列。若当前索引对应值大于等于队尾索引对应值&#xff0c;移除队尾索引&#xff0c;将当前索引加入队尾。遍历结束时当前队头索引即为当前窗口最大值&#xff0c;将其存入结果数组…...

linux-Dockerfile及docker-compose.yml相关字段用途

文章目录 计算机系统5G云计算LINUX Dockerfile及docker-conpose.yml相关字段用途一、Dockerfile1、基础指令2、.高级指令3、多阶段构建指令 二、Docker-Compose.yml1、服务定义&#xff08;services&#xff09;2、高级服务配置3、网络配置 (networks)4、卷配置 (volumes)5、扩…...

deepseek部署:ELK + Filebeat + Zookeeper + Kafka

## 1. 概述 本文档旨在指导如何在7台机器上部署ELK&#xff08;Elasticsearch, Logstash, Kibana&#xff09;堆栈、Filebeat、Zookeeper和Kafka。该部署方案适用于日志收集、处理和可视化场景。 ## 2. 环境准备 ### 2.1 机器分配 | 机器编号 | 主机名 | IP地址 | 部署组件 |-…...

微软Office 2016-2024 x86直装版 v16.0.18324 32位

微软 Office 是一款由微软公司开发的办公软件套装&#xff0c;能满足各种办公需求。包含 Word、Excel、PowerPoint、Outlook 和 OneNote 等软件。Word 有强大文档编辑功能和多人协作&#xff1b;Excel 可处理分析大量数据及支持宏编程&#xff1b;PowerPoint 用于制作演示文稿且…...

CMake宏定义管理:如何优雅处理第三方库的宏冲突

在C/C项目开发中&#xff0c;我们常常会遇到这样的困境&#xff1a; 当引入一个功能强大的第三方库时&#xff0c;却发现它定义的某个宏与我们的项目产生冲突。比如&#xff1a; 库定义了 BUFFER_SIZE 1024&#xff0c;而我们需要 BUFFER_SIZE 2048库内部使用 DEBUG 宏控制日志…...

【SpringCloud】Gateway

目录 一、网关路由 1.1.认识网关 1.2.快速入门? 1.2.1.引入依赖 1.2.2.配置路由 二、网关登录校验 2.1.Gateway工作原理 ?2.2.自定义过滤器 2.3.登录校验 2.4.微服务获取用户 2.4.1.保存用户信息到请求头 2.4.2.拦截器获取用户? ?2.5.OpenFeign传递用户 三、…...

Maven入门教程

一、Maven简介 Maven 是一个基于项目对象模型&#xff08;Project Object Model&#xff09;的构建工具&#xff0c;用于管理 Java 项目的依赖、构建流程和文档生成。它的核心功能包括&#xff1a; 依赖管理(Dependency Management)&#xff1a;自动下载和管理第三方库&#x…...

大数据与金融科技:革新金融行业的动力引擎

大数据与金融科技&#xff1a;革新金融行业的动力引擎 在今天的金融行业&#xff0c;大数据与金融科技的结合正在以惊人的速度推动着金融服务的创新与变革。通过精准的数据分析与智能化决策&#xff0c;金融机构能够更高效地进行风险管理、客户服务、资产管理等一系列关键操作…...

Autosar RTE配置-Port Update配置及使用-基于ETAS工具

文章目录 前言Autosar Rte中enableUpdate参数定义ETAS工具中的配置生成代码分析总结前言 在E2E校验中,需要对Counter进行自增,但每个报文周期不一样,导致自增的周期不一样。且Counter应该在收到报文之后才进行自增。基于这些需求,本文介绍使用RTE Port中的参数enableUpdat…...

【AVRCP】深入理解蓝牙音频 / 视频远程控制规范:从基础到应用

AVRCP&#xff08;Audio/Video Remote Control Profile&#xff09;作为蓝牙音频 / 视频控制领域的重要规范&#xff0c;通过其完善的协议架构、丰富的功能分类以及对用户需求的深入考量&#xff0c;为我们带来了便捷、高效的音频 / 视频设备控制体验。无论是在日常生活中的音乐…...

AWS SQS跨账户访问失败排查指南

引言 在使用AWS SQS(Simple Queue Service)时,跨账户访问是常见的业务场景。例如,账户A的应用程序向队列发送消息,账户B的消费者从队列拉取消息。尽管AWS官方文档明确支持此类配置,但在实际应用中,由于权限模型的复杂性,开发者和运维人员常会遇到“策略已配置但无法接…...

算法训练(leetcode)二刷第三十八天 | 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和、392. 判断子序列

刷题记录 1143. 最长公共子序列1035. 不相交的线53. 最大子数组和动态规划优化版 392. 判断子序列 1143. 最长公共子序列 leetcode题目地址 本题和300. 最长递增子序列相似&#xff08;题解&#xff09;。 使用动态规划&#xff1a; dp数组含义&#xff1a;dp[i][j]表示 以…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...