本地部署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發送圖片讓他進行分析,只支持文字對話。斷網也是可以繼續運行的。

如有更好的想法,歡迎大家評論區暢所欲言!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72256.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72256.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72256.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringBoot原理-02.自動配置-概述

一.自動配置 所謂自動配置&#xff0c;就是Spring容器啟動后&#xff0c;一些配置類、bean對象就自動存入了IOC容器當中&#xff0c;而不需要我們手動聲明&#xff0c;直接從IOC容器中引入即可。省去了繁瑣的配置操作。 我們可以首先將spring項目啟動起來&#xff0c;里面有一…

P10265 [GESP樣題 七級] 迷宮統計

題目描述 在神秘的幻想?陸中&#xff0c;存在著 n 個古老而神奇的迷宮&#xff0c;迷宮編號從 1 到 n。有的迷宮之間可以直接往返&#xff0c;有的可以?到別的迷宮&#xff0c;但是不能?回來。玩家小楊想挑戰?下不同的迷宮&#xff0c;他決定從 m 號迷宮出發。現在&#x…

Spring框架中的工廠模式

在Spring框架里&#xff0c;工廠模式的運用十分廣泛&#xff0c;它主要幫助我們創建和管理對象&#xff0c;讓對象的創建和使用分離&#xff0c;提高代碼的可維護性和可擴展性。下面為你詳細介紹Spring框架中工廠模式的具體體現和示例&#xff1a; 1. BeanFactory 作為工廠模式…

音視頻-WAV格式

1. WAV格式說明&#xff1a; 2. 格式說明&#xff1a; chunkId&#xff1a;通常是 “RIFF” 四個字節&#xff0c;用于標識文件類型。&#xff08;wav文件格式表示&#xff09;chunkSize&#xff1a;表示整個文件除了chunkId和chunkSize這 8 個字節外的其余部分的大小。Forma…

SQL Server Management Studio的使用

之前在https://blog.csdn.net//article/details/140961550介紹了在Windows10上安裝SQL Server 2022 Express和SSMS&#xff0c;這里整理下SSMS的簡單使用&#xff1a; SQL Server Management Studio(SSMS)是一種集成環境&#xff0c;提供用于配置、監視和管理SQL Server和數據…

數據集筆記:NUSMods API

1 介紹 NUSMods API 包含用于渲染 NUSMods 的數據。這些數據包括新加坡國立大學&#xff08;NUS&#xff09;提供的課程以及課程表的信息&#xff0c;還包括上課地點的詳細信息。 可以使用并實驗這些數據&#xff0c;它們是從教務處提供的官方 API 中提取的。 該 API 由靜態的…

劍指 Offer II 031. 最近最少使用緩存

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20031.%20%E6%9C%80%E8%BF%91%E6%9C%80%E5%B0%91%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98/README.md 劍指 Offer II 031. 最近最少使用緩存 題目描述 運用所掌握的…

uniapp 測試 IPA 包安裝到測試 iPhone

將uniapp測試IPA包安裝到測試iPhone有以下幾種方法&#xff1a; 使用Xcode安裝 確保計算機上安裝了Xcode&#xff0c;并將iOS設備通過數據線連接到計算機。打開Xcode&#xff0c;在菜單欄中選擇Window->Devices and Simulators&#xff0c;在設備列表中找到要安裝的iPhone…

vcredist_x64 資源文件分享

vcredist_x64 是 Microsoft Visual C Redistributable 的 64 位版本&#xff0c;用于在 64 位 Windows 系統上運行使用 Visual C 開發的應用程序。它包含了運行這些應用程序所需的運行時組件。 vcredist_x64 資源工具網盤下載鏈接&#xff1a;https://pan.quark.cn/s/ef56f838f…

weaviate 安裝與測試

weaviate 安裝 前提條件&#xff1a;docker安裝完成 步驟&#xff1a; 開啟docker 在終端運行命令 docker run -p 8080:8080 -p 50051:50051 cr.weaviate.io/semitechnologies/weaviate:1.29.0 weaviate 測試 python-client安裝代碼測試 import weaviate client weaviat…

機器學習:監督學習、無監督學習和強化學習

機器學習&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;AI&#xff09;的一個分支&#xff0c;它使計算機能夠從數據中學習&#xff0c;并在沒有明確編程的情況下執行任務。機器學習的核心思想是使用算法分析數據&#xff0c;識別模式&#xff0c;并做出…

自學微信小程序的第六天

DAY6 1、使用錄音API首先需要通過wx.getRecorderManager()方法獲取到一個RecorderManager實例,該實例是一個全局唯一的錄音管理器,用于實現錄音功能。 表32:RecorderManager實例的常用方法 方法名稱 說明 start() 開始錄音 pause() 暫停錄音 resume() 繼續錄音 stop() 停止…

【數據分析】上市公司市場勢力數據測算+dofile(1992-2023年)

市場勢力通常指的是公司在市場中的相對競爭力和定價能力。具有較強市場勢力的公司通常能夠控制價格、影響市場規則&#xff0c;并在競爭中占據主導地位。A股公司市場勢力數據是對中國資本市場中公司競爭力的深入分析&#xff0c;A股市場中&#xff0c;公司市場勢力的強弱不僅影…

Linux三種網絡方式

前言 發現運維啥都得會&#xff0c;這周就遇到了網絡問題自己無法解決&#xff0c;因此痛定思痛學一下。 參考文獻 你管這破玩意叫網絡&#xff1f; 橋接模式、NAT模式、僅主機模式&#xff0c;原來是這樣工作的 交換機 構成局域網&#xff0c;實現所有設備之間的通信。 …

DeepSeek + Mermaid編輯器——常規繪圖

下面這張圖出自&#xff1a;由清華大學出品的 《DeepSeek&#xff1a;從入門到精通》。 作為純文本生成模型&#xff0c;DeepSeek雖不具備多媒體內容生成接口&#xff0c;但其開放式架構允許通過API接口與圖像合成引擎、數據可視化工具等第三方系統進行協同工作&#xff0c;最終…

javaweb將上傳的圖片保存在項目文件webapp下的upload文件夾下

前端HTML表單 (upload.html) 首先&#xff0c;創建一個HTML頁面&#xff0c;允許用戶選擇并上傳圖片。 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>圖片上傳</title> </head> <…

2025最新Flask學習筆記(對照Django做解析)

前言&#xff1a;如果還沒學Django的同學&#xff0c;可以看Django 教程 | 菜鳥教程&#xff0c;也可以忽略下文所提及的Django內容&#xff1b;另外&#xff0c;由于我們接手的項目大多都是前后端分離的項目&#xff0c;所以本文會跳過對模板的介紹&#xff0c;感興趣的朋友可…

自然語言處理NLP入門 -- 第十一節NLP 實戰項目 3: 文本摘要

1. 為啥需要文本摘要&#xff1f; 還記得小時候我們要寫“讀后感”或“觀后感”嗎&#xff1f;看完一篇長長的文章、一本書&#xff0c;甚至一部電影后&#xff0c;老師總是要我們用幾句話概括主要內容。其實&#xff0c;這就跟文本摘要的核心思路一樣——把那些最有價值、最能…

算法day4 dfs搜索2題

一 糖果 我們看這個藍橋A組真題 首先我們看這個題目說有M種的糖果&#xff0c;K顆一包&#xff0c;N包糖果 第一行就是輸入M&#xff0c;K&#xff0c;N的數量 后面就是輸入每個糖果在每包里面的種類 然后問我們最少要用幾包糖果才可以把所有種類的糖果都吃一遍 如果不可以吃完…

【MySQL】窗口函數詳解(概念+練習+實戰)

文章目錄 前言1. SQL窗口函數 1.1 窗口函數概念1.2 窗口函數語法1.3 常見窗口函數 1.3.1 聚合窗口函數1.3.2 專用窗口函數 1.4 窗口函數性能比較 2. LeetCode 例題 2.1 LeetCode SQL 178&#xff1a;分數排名2.2 LeetCode SQL 184&#xff1a;最高工資2.3 LeetCode SQL 185&am…