還記得在 OpenAI 最火熱的 2023 年,我一直想要在本地部署自己的 LLM API 來玩玩。不過在那時,除了開源 LLM 與頂級 LLM 的表現有著天壤之別外,我那貧弱的 8GB VRAM 也讓我打消了這個念頭。
這幾天 AI 圈討論最熱絡的新聞,莫過於 DeepSeek 發布的 R1 模型。先撇開風險與審查的疑慮不談,這個模型的表現確實令眾人驚豔。它不僅是當前開源模型中,在程式競賽領域能觸及 OpenAI o1 水準的佼佼者,更推出了多個「蒸餾」版本,讓像我這樣使用老舊顯示卡的開發者也能在本地運行。這也是促使我寫下這篇文章的原因。
既然有了可用的模型以及堪用的硬體,那就讓我們開始動手玩玩看吧!在開始之前,我們先來了解一些相關知識。
DeepSeek-R1 模型的特點
DeepSeek R1 是中國 AI 實驗室 DeepSeek 近期推出的開源推理模型。在多項 AI 基準測試中,其表現可與 OpenAI 的 o1 模型相媲美。
DeepSeek R1 在各項基準測試的表現
一般來說,參數量與模型的解決問題能力成正比。R1 完整版包含了 6,710 億個參數,遠超許多現有模型。為了照顧不同使用者的需求,DeepSeek 也提供了多個「精簡版」模型,參數量從 15 億到 700 億不等。其中最小版本甚至可以在一般筆記型電腦上運行。雖然完整版需要更高規格的硬體支援,但透過 DeepSeek 的 API 使用時,成本僅為 OpenAI o1 的 5%-10%。
值得一提的是,R1 已在 AI 開發平台 Hugging Face 上以 MIT 許可證發布,使用者可以在無限制條件下進行商業應用。
Ollama:本地 LLM 運行框架
Ollama 是一款開源的本地大型語言模型(LLM)運行框架,專為簡化在本地環境中使用和管理大型語言模型而設計。它支援多種開源模型,如 Llama 3.3、Phi 4、DeepSeek R1、Mistral 和 Gemma,並可在 macOS、Linux 和 Windows 平台上運行。
Ollama 的特色與優勢:
- 豐富的模型庫:提供涵蓋通用用途和特定領域的預訓練模型,並持續擴展。
- 簡單上手:即便是零技術背景的使用者,也能輕鬆安裝和操作。
- 本地運行,保障隱私:所有模型運行與資料處理均在本地完成,有效保護使用者隱私和資料安全。
- 跨平台支援:適用於 macOS、Linux 以及 Windows,滿足不同平台需求。
- 靈活與可客製化:允許使用者根據需求自定義模型設定,包括系統提示詞、推理溫度和上下文視窗長度等參數。
安裝 Ollama
首先,讓我們前往 Ollama 的下載頁面,下載並執行 Windows 版本的安裝程式。
安裝完成後,我們可以使用以下指令來驗證是否安裝成功:
1ollama -v2# output3Warning: could not connect to a running Ollama instance4Warning: client version is 0.5.7
Ollama 指令介紹
想要了解 Ollama 提供哪些功能,我們可以透過 ollama -h
指令查看完整的指令列表:
1ollama -h2# output3Large language model runner4
5Usage:6 ollama [flags]7 ollama [command]8
9Available Commands:10 serve Start ollama # 啟動 Ollama http 伺服器11 create Create a model from a Modelfile # 依照 Modelfile 建立模型12 show Show information for a model # 查看指定模型資訊13 run Run a model # 執行指定模型14 stop Stop a running model # 停止正在執行的模型15 pull Pull a model from a registry # 從 registry 下載模型12 collapsed lines
16 push Push a model to a registry # 將模型上傳至 registry17 list List models # 列出可用模型18 ps List running models # 列出正在執行的模型19 cp Copy a model # 複製模型20 rm Remove a model # 刪除模型21 help Help about any command22
23Flags:24 -h, --help help for ollama25 -v, --version Show version information26
27Use "ollama [command] --help" for more information about a command.
對於一般使用者來說,如果沒有自行訓練或開發模型的需求,我們主要會用到以下幾個常用指令:
serve
:啟動 Ollama HTTP 伺服器show
:查看特定模型的詳細資訊run
:執行指定的模型stop
:停止執行中的模型pull
:從遠端倉庫下載模型list
:列出本地已安裝的模型ps
:顯示目前執行中的模型rm
:移除不需要的模型
如果你有使用過 Docker,應該會發現這些指令的概念和用法都非常相似。在接下來的操作中,我們就能親自體驗這些指令的實際應用了。
選擇合適的 DeepSeek-R1 模型
在運行之前,我們需要根據自己電腦的硬體配置來選擇合適的模型大小。
以我的電腦為例,搭載 NVIDIA 1070 Ti 顯示卡,配備 8GB 的 VRAM:
根據 GPU System Requirements for Running DeepSeek-R1 這篇文章的建議,我的配置最適合使用 7B 參數量的模型:
運行 DeepSeek-R1 7B 模型
接下來,讓我們在終端機中輸入以下指令來運行模型:
1ollama run deepseek-r1:7b
與 Docker 類似,當我們第一次運行時,Ollama 需要先下載模型。由於模型檔案高達 4.7GB,這個過程可能需要一些時間,請耐心等待。
下載完成後,系統會自動進入互動介面:
1ollama run deepseek-r12# output3pulling manifest4pulling 96c415656d37... 100% ▕████████████████████████████████████████████████████████▏ 4.7 GB5pulling 369ca498f347... 100% ▕████████████████████████████████████████████████████████▏ 387 B6pulling 6e4c38e1172f... 100% ▕████████████████████████████████████████████████████████▏ 1.1 KB7pulling f4d24e9138dd... 100% ▕████████████████████████████████████████████████████████▏ 148 B8pulling 40fb844194b2... 100% ▕████████████████████████████████████████████████████████▏ 487 B9verifying sha256 digest10writing manifest11success12>>>
這樣就安裝運行完成了
在互動介面輸入 /?
,可以查看可用的指令:
1>>> /?2Available Commands:3 /set Set session variables4 /show Show model information5 /load <model> Load a session or model6 /save <model> Save your current session7 /clear Clear session context8 /bye Exit9 /?, /help Help for a command10 /? shortcuts Help for keyboard shortcuts11
12Use """ to begin a multi-line message.
讓我們來測試一個經典問題,看看 DeepSeek-R1 7B 的表現如何:
1Question: Which is larger, 9.8 or 9.11?
結果令人驚喜!即使是較小規模的模型,依然展現出了優秀的推理能力。這說明了 DeepSeek-R1 在模型優化方面做得相當出色。
提供本地 API
首先,讓我們使用以下指令離開互動介面:
1>>> /bye
接著,我們需要執行以下指令來啟動模型和 HTTP 伺服器:
1ollama serve2# output32025/01/26 20:13:03 routes.go:1187: INFO server config env="map[CUDA_VISIBLE_DEVICES: GPU_DEVICE_ORDINAL: HIP_VISIBLE_DEVICES: HSA_OVERRIDE_GFX_VERSION: HTTPS_PROXY: HTTP_PROXY: NO_PROXY: OLLAMA_DEBUG:false OLLAMA_FLASH_ATTENTION:false OLLAMA_GPU_OVERHEAD:0 OLLAMA_HOST:http://127.0.0.1:11434 OLLAMA_INTEL_GPU:false OLLAMA_KEEP_ALIVE:5m0s OLLAMA_KV_CACHE_TYPE: OLLAMA_LLM_LIBRARY: OLLAMA_LOAD_TIMEOUT:5m0s OLLAMA_MAX_LOADED_MODELS:0 OLLAMA_MAX_QUEUE:512 OLLAMA_MODELS:C:\\Users\\vince987\\.ollama\\models OLLAMA_MULTIUSER_CACHE:false OLLAMA_NOHISTORY:false OLLAMA_NOPRUNE:false OLLAMA_NUM_PARALLEL:0 OLLAMA_ORIGINS:[http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://*] OLLAMA_SCHED_SPREAD:false ROCR_VISIBLE_DEVICES:]"4time=2025-01-26T20:13:03.361+08:00 level=INFO source=images.go:432 msg="total blobs: 5"5time=2025-01-26T20:13:03.362+08:00 level=INFO source=images.go:439 msg="total unused blobs removed: 0"6time=2025-01-26T20:13:03.364+08:00 level=INFO source=routes.go:1238 msg="Listening on 127.0.0.1:11434 (version 0.5.7)"7time=2025-01-26T20:13:03.365+08:00 level=INFO source=routes.go:1267 msg="Dynamic LLM libraries" runners="[cpu cpu_avx cpu_avx2 cuda_v11_avx cuda_v12_avx rocm_avx]"8time=2025-01-26T20:13:03.365+08:00 level=INFO source=gpu.go:226 msg="looking for compatible GPUs"9time=2025-01-26T20:13:03.365+08:00 level=INFO source=gpu_windows.go:167 msg=packages count=110time=2025-01-26T20:13:03.366+08:00 level=INFO source=gpu_windows.go:214 msg="" package=0 cores=6 efficiency=0 threads=1211time=2025-01-26T20:13:03.551+08:00 level=INFO source=types.go:131 msg="inference compute" id=GPU-68a357c1-168f-a686-c7c9-18bdec317d6f library=cuda variant=v12 compute=6.1 driver=12.2 name="NVIDIA GeForce GTX 1070 Ti" total="8.0 GiB" available="7.0 GiB"
設定完成後,我們就可以透過以下任一網址來存取 API:
1http://localhost2https://localhost3http://localhost:*4https://localhost:*5http://127.0.0.16https://127.0.0.17http://127.0.0.1:*8https://127.0.0.1:*9http://0.0.0.010https://0.0.0.011http://0.0.0.0:*12https://0.0.0.0:*13app://*14file://*15tauri://*1 collapsed line
16vscode-webview://*
疑難排解
在執行 ollama serve
指令時,可能會遇到這樣的錯誤訊息:
1ollama serve2Error: listen tcp 127.0.0.1:11434: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
這個錯誤表示該通訊埠(Port)已被其他程式佔用。
對於 Linux/Mac 使用者,可以使用以下指令找出佔用通訊埠的程序 ID(PID),然後透過 kill 指令終止該程序:
1lsof -i:114342# 查看 11434 端口占用
而 Windows 使用者除了可以在工作管理員中查看外,還需要特別注意系統匣(System Tray)中是否有 Ollama 的背景程式正在執行:
這裡有一個需要特別注意的地方:即使 Ollama 在背景執行並佔用了通訊埠,但實際上並未載入模型,因此不會佔用顯示卡記憶體(VRAM)。這是一個容易被忽略的細節。
整合 Ollama API
現在讓我們來看看如何將 Ollama API 整合到其他服務中。
與 ChatBox 的整合應用
ChatBox 是一款優秀的開源 LLM 桌面客戶端,我在先前的文章 GenAI 新選擇:馬克斯的 Grok API 配置與 Chatbox 整合 中已經詳細介紹過,在此不再贅述。
ChatBox 內建支援 Ollama 整合,設定相當簡單:只需在介面中選擇想要使用的模型,儲存設定即可完成整合。
以下是實際使用的效果展示:
雖然相較於直接使用互動介面,回應速度稍慢,但整體表現仍在可接受的範圍內。
讓我們回到終端機查看執行記錄:
1...2time=2025-01-26T20:15:54.397+08:00 level=INFO source=sched.go:714 msg="new model will fit in available VRAM in single GPU, loading" model=C:\Users\vince987\.ollama\models\blobs\sha256-96c415656d377afbff962f6cdb2394ab092ccbcbaab4b82525bc4ca800fe8a49 gpu=GPU-68a357c1-168f-a686-c7c9-18bdec317d6f parallel=4 available=6446669824 required="5.6 GiB"3time=2025-01-26T20:15:54.422+08:00 level=INFO source=server.go:104 msg="system memory" total="63.9 GiB" free="40.0 GiB" free_swap="39.6 GiB"4time=2025-01-26T20:15:54.423+08:00 level=INFO source=memory.go:356 msg="offload to cuda" layers.requested=-1 layers.model=29 layers.offload=29 layers.split="" memory.available="[6.0 GiB]" memory.gpu_overhead="0 B" memory.required.full="5.6 GiB" memory.required.partial="5.6 GiB" memory.required.kv="448.0 MiB" memory.required.allocations="[5.6 GiB]" memory.weights.total="4.1 GiB" memory.weights.repeating="3.7 GiB" memory.weights.nonrepeating="426.4 MiB" memory.graph.full="478.0 MiB" memory.graph.partial="730.4 MiB"5time=2025-01-26T20:15:54.439+08:00 level=INFO source=server.go:376 msg="starting llama server" cmd="C:\\Users\\vince987\\AppData\\Local\\Programs\\Ollama\\lib\\ollama\\runners\\cuda_v12_avx\\ollama_llama_server.exe runner --model C:\\Users\\vince987\\.ollama\\models\\blobs\\sha256-96c415656d377afbff962f6cdb2394ab092ccbcbaab4b82525bc4ca800fe8a49 --ctx-size 8192 --batch-size 512 --n-gpu-layers 29 --threads 6 --no-mmap --parallel 4 --port 51200"6time=2025-01-26T20:15:54.447+08:00 level=INFO source=sched.go:449 msg="loaded runners" count=17time=2025-01-26T20:15:54.447+08:00 level=INFO source=server.go:555 msg="waiting for llama runner to start responding"8time=2025-01-26T20:15:54.448+08:00 level=INFO source=server.go:589 msg="waiting for server to become available" status="llm server error"9time=2025-01-26T20:15:54.665+08:00 level=INFO source=runner.go:936 msg="starting go runner"10ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no11ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no12ggml_cuda_init: found 1 CUDA devices:13 Device 0: NVIDIA GeForce GTX 1070 Ti, compute capability 6.1, VMM: yes14time=2025-01-26T20:15:54.744+08:00 level=INFO source=runner.go:937 msg=system info="CUDA : ARCHS = 600,610,620,700,720,750,800,860,870,890,900 | USE_GRAPHS = 1 | PEER_MAX_BATCH_SIZE = 128 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | LLAMAFILE = 1 | AARCH64_REPACK = 1 | cgo(clang)" threads=615time=2025-01-26T20:15:54.747+08:00 level=INFO source=.:0 msg="Server listening on 127.0.0.1:51200"182 collapsed lines
16time=2025-01-26T20:15:54.950+08:00 level=INFO source=server.go:589 msg="waiting for server to become available" status="llm server loading model"17llama_load_model_from_file: using device CUDA0 (NVIDIA GeForce GTX 1070 Ti) - 7173 MiB free18llama_model_loader: loaded meta data with 26 key-value pairs and 339 tensors from C:\Users\vince987\.ollama\models\blobs\sha256-96c415656d377afbff962f6cdb2394ab092ccbcbaab4b82525bc4ca800fe8a49 (version GGUF V3 (latest))19llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.20llama_model_loader: - kv 0: general.architecture str = qwen221llama_model_loader: - kv 1: general.type str = model22llama_model_loader: - kv 2: general.name str = DeepSeek R1 Distill Qwen 7B23llama_model_loader: - kv 3: general.basename str = DeepSeek-R1-Distill-Qwen24llama_model_loader: - kv 4: general.size_label str = 7B25llama_model_loader: - kv 5: qwen2.block_count u32 = 2826llama_model_loader: - kv 6: qwen2.context_length u32 = 13107227llama_model_loader: - kv 7: qwen2.embedding_length u32 = 358428llama_model_loader: - kv 8: qwen2.feed_forward_length u32 = 1894429llama_model_loader: - kv 9: qwen2.attention.head_count u32 = 2830llama_model_loader: - kv 10: qwen2.attention.head_count_kv u32 = 431llama_model_loader: - kv 11: qwen2.rope.freq_base f32 = 10000.00000032llama_model_loader: - kv 12: qwen2.attention.layer_norm_rms_epsilon f32 = 0.00000133llama_model_loader: - kv 13: general.file_type u32 = 1534llama_model_loader: - kv 14: tokenizer.ggml.model str = gpt235llama_model_loader: - kv 15: tokenizer.ggml.pre str = qwen236llama_model_loader: - kv 16: tokenizer.ggml.tokens arr[str,152064] = ["!", "\"", "#", "$", "%", "&", "'", ...37llama_model_loader: - kv 17: tokenizer.ggml.token_type arr[i32,152064] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...38llama_model_loader: - kv 18: tokenizer.ggml.merges arr[str,151387] = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...39llama_model_loader: - kv 19: tokenizer.ggml.bos_token_id u32 = 15164640llama_model_loader: - kv 20: tokenizer.ggml.eos_token_id u32 = 15164341llama_model_loader: - kv 21: tokenizer.ggml.padding_token_id u32 = 15164342llama_model_loader: - kv 22: tokenizer.ggml.add_bos_token bool = true43llama_model_loader: - kv 23: tokenizer.ggml.add_eos_token bool = false44llama_model_loader: - kv 24: tokenizer.chat_template str = {% if not add_generation_prompt is de...45llama_model_loader: - kv 25: general.quantization_version u32 = 246llama_model_loader: - type f32: 141 tensors47llama_model_loader: - type q4_K: 169 tensors48llama_model_loader: - type q6_K: 29 tensors49llm_load_vocab: special_eos_id is not in special_eog_ids - the tokenizer config may be incorrect50llm_load_vocab: special tokens cache size = 2251llm_load_vocab: token to piece cache size = 0.9310 MB52llm_load_print_meta: format = GGUF V3 (latest)53llm_load_print_meta: arch = qwen254llm_load_print_meta: vocab type = BPE55llm_load_print_meta: n_vocab = 15206456llm_load_print_meta: n_merges = 15138757llm_load_print_meta: vocab_only = 058llm_load_print_meta: n_ctx_train = 13107259llm_load_print_meta: n_embd = 358460llm_load_print_meta: n_layer = 2861llm_load_print_meta: n_head = 2862llm_load_print_meta: n_head_kv = 463llm_load_print_meta: n_rot = 12864llm_load_print_meta: n_swa = 065llm_load_print_meta: n_embd_head_k = 12866llm_load_print_meta: n_embd_head_v = 12867llm_load_print_meta: n_gqa = 768llm_load_print_meta: n_embd_k_gqa = 51269llm_load_print_meta: n_embd_v_gqa = 51270llm_load_print_meta: f_norm_eps = 0.0e+0071llm_load_print_meta: f_norm_rms_eps = 1.0e-0672llm_load_print_meta: f_clamp_kqv = 0.0e+0073llm_load_print_meta: f_max_alibi_bias = 0.0e+0074llm_load_print_meta: f_logit_scale = 0.0e+0075llm_load_print_meta: n_ff = 1894476llm_load_print_meta: n_expert = 077llm_load_print_meta: n_expert_used = 078llm_load_print_meta: causal attn = 179llm_load_print_meta: pooling type = 080llm_load_print_meta: rope type = 281llm_load_print_meta: rope scaling = linear82llm_load_print_meta: freq_base_train = 10000.083llm_load_print_meta: freq_scale_train = 184llm_load_print_meta: n_ctx_orig_yarn = 13107285llm_load_print_meta: rope_finetuned = unknown86llm_load_print_meta: ssm_d_conv = 087llm_load_print_meta: ssm_d_inner = 088llm_load_print_meta: ssm_d_state = 089llm_load_print_meta: ssm_dt_rank = 090llm_load_print_meta: ssm_dt_b_c_rms = 091llm_load_print_meta: model type = 7B92llm_load_print_meta: model ftype = Q4_K - Medium93llm_load_print_meta: model params = 7.62 B94llm_load_print_meta: model size = 4.36 GiB (4.91 BPW)95llm_load_print_meta: general.name = DeepSeek R1 Distill Qwen 7B96llm_load_print_meta: BOS token = 151646 '<|begin▁of▁sentence|>'97llm_load_print_meta: EOS token = 151643 '<|end▁of▁sentence|>'98llm_load_print_meta: EOT token = 151643 '<|end▁of▁sentence|>'99llm_load_print_meta: PAD token = 151643 '<|end▁of▁sentence|>'100llm_load_print_meta: LF token = 148848 'ÄĬ'101llm_load_print_meta: FIM PRE token = 151659 '<|fim_prefix|>'102llm_load_print_meta: FIM SUF token = 151661 '<|fim_suffix|>'103llm_load_print_meta: FIM MID token = 151660 '<|fim_middle|>'104llm_load_print_meta: FIM PAD token = 151662 '<|fim_pad|>'105llm_load_print_meta: FIM REP token = 151663 '<|repo_name|>'106llm_load_print_meta: FIM SEP token = 151664 '<|file_sep|>'107llm_load_print_meta: EOG token = 151643 '<|end▁of▁sentence|>'108llm_load_print_meta: EOG token = 151662 '<|fim_pad|>'109llm_load_print_meta: EOG token = 151663 '<|repo_name|>'110llm_load_print_meta: EOG token = 151664 '<|file_sep|>'111llm_load_print_meta: max token length = 256112llm_load_tensors: offloading 28 repeating layers to GPU113llm_load_tensors: offloading output layer to GPU114llm_load_tensors: offloaded 29/29 layers to GPU115llm_load_tensors: CPU model buffer size = 292.36 MiB116llm_load_tensors: CUDA0 model buffer size = 4168.09 MiB117llama_new_context_with_model: n_seq_max = 4118llama_new_context_with_model: n_ctx = 8192119llama_new_context_with_model: n_ctx_per_seq = 2048120llama_new_context_with_model: n_batch = 2048121llama_new_context_with_model: n_ubatch = 512122llama_new_context_with_model: flash_attn = 0123llama_new_context_with_model: freq_base = 10000.0124llama_new_context_with_model: freq_scale = 1125llama_new_context_with_model: n_ctx_per_seq (2048) < n_ctx_train (131072) -- the full capacity of the model will not be utilized126llama_kv_cache_init: kv_size = 8192, offload = 1, type_k = 'f16', type_v = 'f16', n_layer = 28, can_shift = 1127llama_kv_cache_init: CUDA0 KV buffer size = 448.00 MiB128llama_new_context_with_model: KV self size = 448.00 MiB, K (f16): 224.00 MiB, V (f16): 224.00 MiB129llama_new_context_with_model: CUDA_Host output buffer size = 2.38 MiB130llama_new_context_with_model: CUDA0 compute buffer size = 492.00 MiB131llama_new_context_with_model: CUDA_Host compute buffer size = 23.01 MiB132llama_new_context_with_model: graph nodes = 986133llama_new_context_with_model: graph splits = 2134time=2025-01-26T20:15:57.207+08:00 level=INFO source=server.go:594 msg="llama runner started in 2.76 seconds"135llama_model_loader: loaded meta data with 26 key-value pairs and 339 tensors from C:\Users\vince987\.ollama\models\blobs\sha256-96c415656d377afbff962f6cdb2394ab092ccbcbaab4b82525bc4ca800fe8a49 (version GGUF V3 (latest))136llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.137llama_model_loader: - kv 0: general.architecture str = qwen2138llama_model_loader: - kv 1: general.type str = model139llama_model_loader: - kv 2: general.name str = DeepSeek R1 Distill Qwen 7B140llama_model_loader: - kv 3: general.basename str = DeepSeek-R1-Distill-Qwen141llama_model_loader: - kv 4: general.size_label str = 7B142llama_model_loader: - kv 5: qwen2.block_count u32 = 28143llama_model_loader: - kv 6: qwen2.context_length u32 = 131072144llama_model_loader: - kv 7: qwen2.embedding_length u32 = 3584145llama_model_loader: - kv 8: qwen2.feed_forward_length u32 = 18944146llama_model_loader: - kv 9: qwen2.attention.head_count u32 = 28147llama_model_loader: - kv 10: qwen2.attention.head_count_kv u32 = 4148llama_model_loader: - kv 11: qwen2.rope.freq_base f32 = 10000.000000149llama_model_loader: - kv 12: qwen2.attention.layer_norm_rms_epsilon f32 = 0.000001150llama_model_loader: - kv 13: general.file_type u32 = 15151llama_model_loader: - kv 14: tokenizer.ggml.model str = gpt2152llama_model_loader: - kv 15: tokenizer.ggml.pre str = qwen2153llama_model_loader: - kv 16: tokenizer.ggml.tokens arr[str,152064] = ["!", "\"", "#", "$", "%", "&", "'", ...154llama_model_loader: - kv 17: tokenizer.ggml.token_type arr[i32,152064] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...155llama_model_loader: - kv 18: tokenizer.ggml.merges arr[str,151387] = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...156llama_model_loader: - kv 19: tokenizer.ggml.bos_token_id u32 = 151646157llama_model_loader: - kv 20: tokenizer.ggml.eos_token_id u32 = 151643158llama_model_loader: - kv 21: tokenizer.ggml.padding_token_id u32 = 151643159llama_model_loader: - kv 22: tokenizer.ggml.add_bos_token bool = true160llama_model_loader: - kv 23: tokenizer.ggml.add_eos_token bool = false161llama_model_loader: - kv 24: tokenizer.chat_template str = {% if not add_generation_prompt is de...162llama_model_loader: - kv 25: general.quantization_version u32 = 2163llama_model_loader: - type f32: 141 tensors164llama_model_loader: - type q4_K: 169 tensors165llama_model_loader: - type q6_K: 29 tensors166llm_load_vocab: special_eos_id is not in special_eog_ids - the tokenizer config may be incorrect167llm_load_vocab: special tokens cache size = 22168llm_load_vocab: token to piece cache size = 0.9310 MB169llm_load_print_meta: format = GGUF V3 (latest)170llm_load_print_meta: arch = qwen2171llm_load_print_meta: vocab type = BPE172llm_load_print_meta: n_vocab = 152064173llm_load_print_meta: n_merges = 151387174llm_load_print_meta: vocab_only = 1175llm_load_print_meta: model type = ?B176llm_load_print_meta: model ftype = all F32177llm_load_print_meta: model params = 7.62 B178llm_load_print_meta: model size = 4.36 GiB (4.91 BPW)179llm_load_print_meta: general.name = DeepSeek R1 Distill Qwen 7B180llm_load_print_meta: BOS token = 151646 '<|begin▁of▁sentence|>'181llm_load_print_meta: EOS token = 151643 '<|end▁of▁sentence|>'182llm_load_print_meta: EOT token = 151643 '<|end▁of▁sentence|>'183llm_load_print_meta: PAD token = 151643 '<|end▁of▁sentence|>'184llm_load_print_meta: LF token = 148848 'ÄĬ'185llm_load_print_meta: FIM PRE token = 151659 '<|fim_prefix|>'186llm_load_print_meta: FIM SUF token = 151661 '<|fim_suffix|>'187llm_load_print_meta: FIM MID token = 151660 '<|fim_middle|>'188llm_load_print_meta: FIM PAD token = 151662 '<|fim_pad|>'189llm_load_print_meta: FIM REP token = 151663 '<|repo_name|>'190llm_load_print_meta: FIM SEP token = 151664 '<|file_sep|>'191llm_load_print_meta: EOG token = 151643 '<|end▁of▁sentence|>'192llm_load_print_meta: EOG token = 151662 '<|fim_pad|>'193llm_load_print_meta: EOG token = 151663 '<|repo_name|>'194llm_load_print_meta: EOG token = 151664 '<|file_sep|>'195llm_load_print_meta: max token length = 256196llama_model_load: vocab only - skipping tensors197[GIN] 2025/01/26 - 20:16:12 | 200 | 18.2781341s | 127.0.0.1 | POST "/api/chat"
與 VS Code 的整合應用
現在的軟體工程師,已經離不開像 Cursor 或 Windsurf 這類具備 AI Agent 功能的 IDE。不過這些工具通常需要按月收費,而且使用額度相當有限。
既然我們已經有了自己的 LLM API,不妨來試試 VS Code 搭配 Continue 和 Cline 的組合。
Continue:VS Code 的 AI 助手
Continue 是一個功能豐富的 VS Code AI 助手擴充套件,其主要功能包括:
- 基於 AI 的程式碼自動完成
- 根據上下文生成程式碼片段
- 支援多種程式語言
- 提供智慧程式碼建議
- 解釋程式碼並提供重構建議
- 支援多個 AI 模型,包括 OpenAI、Anthropic 等
Continue 不僅可以實現 Cursor AI Pane 中的大部分 CHAT
功能和程式碼補全,更重要的是它還支援本地模型的 API。
在 Continue 中設定 Ollama
由於 Continue 的 Ollama Provider 並未預設提供我們的模型選項,因此需要手動進行設定。
可以參考 Continue - Ollama Provider 文件。
點擊 Add Chat Model
後,在畫面下方找到 config file
超連結,即可開啟設定檔 config.json
。
設定 Chat Model
在設定檔中,找到對應的區塊並加入以下內容:
1{2 "models": [3 {4 "title": "Deepseek-R1 7B",5 "provider": "ollama",6 "model": "deepseek-r1:latest"7 }8 ]9}
如果有綁定特定的 IP 位址或網域名稱,則需要加入以下內容:
1{2 "models": [3 {4 "title": "Deepseek-R1 7B",5 "provider": "ollama",6 "model": "deepseek-r1:latest",7 "apiBase": "http://<my endpoint>:11434"8 }9 ]10}
請注意,模型名稱需要依照 ollama list
指令顯示的名稱來輸入。在我的環境中,本地的 R1 7B 模型顯示為 deepseek-r1:latest
。
完成設定後,讓我們回到 Continue 進行測試:
設定自動補全
在設定檔中,找到對應的區塊並加入以下內容:
1{2 "tabAutocompleteModel": [3 {4 "title": "Deepseek-R1 7B",5 "provider": "ollama",6 "model": "deepseek-r1:latest"7 }8 ]9}
設定完成後,點擊右下角的 Continue
按鈕,在選單中切換成剛剛設定的 R1 模型:
讓我們來試試看效果:
實際使用後,只能說基本功能是可用的,但程式碼補全的能力還有待加強。
或許經過微調後表現會更好,但這並非我的專業領域。
Cline:命令列開發助手
Cline 是一個強大的 VS Code 命令列增強工具,其主要功能包括:
- 智慧建立與編輯檔案
- 協助探索大型專案結構
- 整合瀏覽器操作
- 安全執行終端機指令(需要使用者授權)
Cline 能夠實現 Cursor AI Pane 中大部分的 Composer
功能,它可以將複雜的軟體開發任務拆解成多個小型任務,並透過上述工具逐步完成。
在 Cline 中設定 Ollama
相較於 Continue,在 Cline 中設定 Ollama 顯得相當簡單,只需在設定頁面中選擇對應的模型即可:
然而,在實際測試時,我發現雖然基本的對話功能可以正常運作,但 Cline 無法正常讀寫檔案以及執行終端機指令:
經過查證,這似乎是一個已知問題,詳細資訊請參考 Issue #1418。目前只能等待社群提供解決方案。
總結與展望
蒸餾版 DeepSeek R1 的問世,為計算資源有限的獨立開發者提供了一個可行的本地 LLM 解決方案。雖然像 R1:7B
這樣的小型模型還無法勝任複雜的 AI Agent 任務,但作為 RAG(檢索增強生成)系統的基礎模型已經綽綽有餘。
當然,如果預算允許,目前主流的 AI 整合應用都已支援 R1 API,例如 Cursor:
之後我會嘗試基於 DeepSeek R1 本地模型來搭建個人的 RAG 系統,敬請期待!