Vinc3nt's Life

在本地運行 DeepSeek R1:整合 VS Code 開發環境

2025-01-27
develop
productivity
ai
ai-agent
deepseek
deepseek-r1
ollama
chatbox
continue
cline
最後更新:2025-01-26
24分鐘
4640字

還記得在 OpenAI 最火熱的 2023 年,我一直想要在本地部署自己的 LLM API 來玩玩。不過在那時,除了開源 LLM 與頂級 LLM 的表現有著天壤之別外,我那貧弱的 8GB VRAM 也讓我打消了這個念頭。

這幾天 AI 圈討論最熱絡的新聞,莫過於 DeepSeek 發布的 R1 模型。先撇開風險與審查的疑慮不談,這個模型的表現確實令眾人驚豔。它不僅是當前開源模型中,在程式競賽領域能觸及 OpenAI o1 水準的佼佼者,更推出了多個「蒸餾」版本,讓像我這樣使用老舊顯示卡的開發者也能在本地運行。這也是促使我寫下這篇文章的原因。

既然有了可用的模型以及堪用的硬體,那就讓我們開始動手玩玩看吧!在開始之前,我們先來了解一些相關知識。

DeepSeek-R1 模型的特點

DeepSeek R1 是中國 AI 實驗室 DeepSeek 近期推出的開源推理模型。在多項 AI 基準測試中,其表現可與 OpenAI 的 o1 模型相媲美。

default 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 的特色與優勢:

  1. 豐富的模型庫:提供涵蓋通用用途和特定領域的預訓練模型,並持續擴展。
  2. 簡單上手:即便是零技術背景的使用者,也能輕鬆安裝和操作。
  3. 本地運行,保障隱私:所有模型運行與資料處理均在本地完成,有效保護使用者隱私和資料安全。
  4. 跨平台支援:適用於 macOS、Linux 以及 Windows,滿足不同平台需求。
  5. 靈活與可客製化:允許使用者根據需求自定義模型設定,包括系統提示詞、推理溫度和上下文視窗長度等參數。

安裝 Ollama

首先,讓我們前往 Ollama 的下載頁面,下載並執行 Windows 版本的安裝程式。

安裝完成後,我們可以使用以下指令來驗證是否安裝成功:

Terminal window
1
ollama -v
2
# output
3
Warning: could not connect to a running Ollama instance
4
Warning: client version is 0.5.7

Ollama 指令介紹

想要了解 Ollama 提供哪些功能,我們可以透過 ollama -h 指令查看完整的指令列表:

Terminal window
1
ollama -h
2
# output
3
Large language model runner
4
5
Usage:
6
ollama [flags]
7
ollama [command]
8
9
Available 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 # 將模型上傳至 registry
17
list List models # 列出可用模型
18
ps List running models # 列出正在執行的模型
19
cp Copy a model # 複製模型
20
rm Remove a model # 刪除模型
21
help Help about any command
22
23
Flags:
24
-h, --help help for ollama
25
-v, --version Show version information
26
27
Use "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:

default

根據 GPU System Requirements for Running DeepSeek-R1 這篇文章的建議,我的配置最適合使用 7B 參數量的模型:

default

運行 DeepSeek-R1 7B 模型

接下來,讓我們在終端機中輸入以下指令來運行模型:

Terminal window
1
ollama run deepseek-r1:7b

與 Docker 類似,當我們第一次運行時,Ollama 需要先下載模型。由於模型檔案高達 4.7GB,這個過程可能需要一些時間,請耐心等待。

下載完成後,系統會自動進入互動介面:

Terminal window
1
ollama run deepseek-r1
2
# output
3
pulling manifest
4
pulling 96c415656d37... 100% ▕████████████████████████████████████████████████████████▏ 4.7 GB
5
pulling 369ca498f347... 100% ▕████████████████████████████████████████████████████████▏ 387 B
6
pulling 6e4c38e1172f... 100% ▕████████████████████████████████████████████████████████▏ 1.1 KB
7
pulling f4d24e9138dd... 100% ▕████████████████████████████████████████████████████████▏ 148 B
8
pulling 40fb844194b2... 100% ▕████████████████████████████████████████████████████████▏ 487 B
9
verifying sha256 digest
10
writing manifest
11
success
12
>>>

這樣就安裝運行完成了

在互動介面輸入 /?,可以查看可用的指令:

Terminal window
1
>>> /?
2
Available Commands:
3
/set Set session variables
4
/show Show model information
5
/load <model> Load a session or model
6
/save <model> Save your current session
7
/clear Clear session context
8
/bye Exit
9
/?, /help Help for a command
10
/? shortcuts Help for keyboard shortcuts
11
12
Use """ to begin a multi-line message.

讓我們來測試一個經典問題,看看 DeepSeek-R1 7B 的表現如何:

1
Question: Which is larger, 9.8 or 9.11?

default

結果令人驚喜!即使是較小規模的模型,依然展現出了優秀的推理能力。這說明了 DeepSeek-R1 在模型優化方面做得相當出色。

提供本地 API

首先,讓我們使用以下指令離開互動介面:

Terminal window
1
>>> /bye

接著,我們需要執行以下指令來啟動模型和 HTTP 伺服器:

Terminal window
1
ollama serve
2
# output
3
2025/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:]"
4
time=2025-01-26T20:13:03.361+08:00 level=INFO source=images.go:432 msg="total blobs: 5"
5
time=2025-01-26T20:13:03.362+08:00 level=INFO source=images.go:439 msg="total unused blobs removed: 0"
6
time=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)"
7
time=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]"
8
time=2025-01-26T20:13:03.365+08:00 level=INFO source=gpu.go:226 msg="looking for compatible GPUs"
9
time=2025-01-26T20:13:03.365+08:00 level=INFO source=gpu_windows.go:167 msg=packages count=1
10
time=2025-01-26T20:13:03.366+08:00 level=INFO source=gpu_windows.go:214 msg="" package=0 cores=6 efficiency=0 threads=12
11
time=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:

1
http://localhost
2
https://localhost
3
http://localhost:*
4
https://localhost:*
5
http://127.0.0.1
6
https://127.0.0.1
7
http://127.0.0.1:*
8
https://127.0.0.1:*
9
http://0.0.0.0
10
https://0.0.0.0
11
http://0.0.0.0:*
12
https://0.0.0.0:*
13
app://*
14
file://*
15
tauri://*
1 collapsed line
16
vscode-webview://*

疑難排解

在執行 ollama serve 指令時,可能會遇到這樣的錯誤訊息:

Terminal window
1
ollama serve
2
Error: 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 指令終止該程序:

Terminal window
1
lsof -i:11434
2
# 查看 11434 端口占用

而 Windows 使用者除了可以在工作管理員中查看外,還需要特別注意系統匣(System Tray)中是否有 Ollama 的背景程式正在執行:

default

這裡有一個需要特別注意的地方:即使 Ollama 在背景執行並佔用了通訊埠,但實際上並未載入模型,因此不會佔用顯示卡記憶體(VRAM)。這是一個容易被忽略的細節。

整合 Ollama API

現在讓我們來看看如何將 Ollama API 整合到其他服務中。

與 ChatBox 的整合應用

ChatBox 是一款優秀的開源 LLM 桌面客戶端,我在先前的文章 GenAI 新選擇:馬克斯的 Grok API 配置與 Chatbox 整合 中已經詳細介紹過,在此不再贅述。

ChatBox 內建支援 Ollama 整合,設定相當簡單:只需在介面中選擇想要使用的模型,儲存設定即可完成整合。

default

以下是實際使用的效果展示:

default

雖然相較於直接使用互動介面,回應速度稍慢,但整體表現仍在可接受的範圍內。

讓我們回到終端機查看執行記錄:

Terminal window
1
...
2
time=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"
3
time=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"
4
time=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"
5
time=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"
6
time=2025-01-26T20:15:54.447+08:00 level=INFO source=sched.go:449 msg="loaded runners" count=1
7
time=2025-01-26T20:15:54.447+08:00 level=INFO source=server.go:555 msg="waiting for llama runner to start responding"
8
time=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"
9
time=2025-01-26T20:15:54.665+08:00 level=INFO source=runner.go:936 msg="starting go runner"
10
ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no
11
ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no
12
ggml_cuda_init: found 1 CUDA devices:
13
Device 0: NVIDIA GeForce GTX 1070 Ti, compute capability 6.1, VMM: yes
14
time=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=6
15
time=2025-01-26T20:15:54.747+08:00 level=INFO source=.:0 msg="Server listening on 127.0.0.1:51200"
182 collapsed lines
16
time=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"
17
llama_load_model_from_file: using device CUDA0 (NVIDIA GeForce GTX 1070 Ti) - 7173 MiB free
18
llama_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))
19
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
20
llama_model_loader: - kv 0: general.architecture str = qwen2
21
llama_model_loader: - kv 1: general.type str = model
22
llama_model_loader: - kv 2: general.name str = DeepSeek R1 Distill Qwen 7B
23
llama_model_loader: - kv 3: general.basename str = DeepSeek-R1-Distill-Qwen
24
llama_model_loader: - kv 4: general.size_label str = 7B
25
llama_model_loader: - kv 5: qwen2.block_count u32 = 28
26
llama_model_loader: - kv 6: qwen2.context_length u32 = 131072
27
llama_model_loader: - kv 7: qwen2.embedding_length u32 = 3584
28
llama_model_loader: - kv 8: qwen2.feed_forward_length u32 = 18944
29
llama_model_loader: - kv 9: qwen2.attention.head_count u32 = 28
30
llama_model_loader: - kv 10: qwen2.attention.head_count_kv u32 = 4
31
llama_model_loader: - kv 11: qwen2.rope.freq_base f32 = 10000.000000
32
llama_model_loader: - kv 12: qwen2.attention.layer_norm_rms_epsilon f32 = 0.000001
33
llama_model_loader: - kv 13: general.file_type u32 = 15
34
llama_model_loader: - kv 14: tokenizer.ggml.model str = gpt2
35
llama_model_loader: - kv 15: tokenizer.ggml.pre str = qwen2
36
llama_model_loader: - kv 16: tokenizer.ggml.tokens arr[str,152064] = ["!", "\"", "#", "$", "%", "&", "'", ...
37
llama_model_loader: - kv 17: tokenizer.ggml.token_type arr[i32,152064] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
38
llama_model_loader: - kv 18: tokenizer.ggml.merges arr[str,151387] = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...
39
llama_model_loader: - kv 19: tokenizer.ggml.bos_token_id u32 = 151646
40
llama_model_loader: - kv 20: tokenizer.ggml.eos_token_id u32 = 151643
41
llama_model_loader: - kv 21: tokenizer.ggml.padding_token_id u32 = 151643
42
llama_model_loader: - kv 22: tokenizer.ggml.add_bos_token bool = true
43
llama_model_loader: - kv 23: tokenizer.ggml.add_eos_token bool = false
44
llama_model_loader: - kv 24: tokenizer.chat_template str = {% if not add_generation_prompt is de...
45
llama_model_loader: - kv 25: general.quantization_version u32 = 2
46
llama_model_loader: - type f32: 141 tensors
47
llama_model_loader: - type q4_K: 169 tensors
48
llama_model_loader: - type q6_K: 29 tensors
49
llm_load_vocab: special_eos_id is not in special_eog_ids - the tokenizer config may be incorrect
50
llm_load_vocab: special tokens cache size = 22
51
llm_load_vocab: token to piece cache size = 0.9310 MB
52
llm_load_print_meta: format = GGUF V3 (latest)
53
llm_load_print_meta: arch = qwen2
54
llm_load_print_meta: vocab type = BPE
55
llm_load_print_meta: n_vocab = 152064
56
llm_load_print_meta: n_merges = 151387
57
llm_load_print_meta: vocab_only = 0
58
llm_load_print_meta: n_ctx_train = 131072
59
llm_load_print_meta: n_embd = 3584
60
llm_load_print_meta: n_layer = 28
61
llm_load_print_meta: n_head = 28
62
llm_load_print_meta: n_head_kv = 4
63
llm_load_print_meta: n_rot = 128
64
llm_load_print_meta: n_swa = 0
65
llm_load_print_meta: n_embd_head_k = 128
66
llm_load_print_meta: n_embd_head_v = 128
67
llm_load_print_meta: n_gqa = 7
68
llm_load_print_meta: n_embd_k_gqa = 512
69
llm_load_print_meta: n_embd_v_gqa = 512
70
llm_load_print_meta: f_norm_eps = 0.0e+00
71
llm_load_print_meta: f_norm_rms_eps = 1.0e-06
72
llm_load_print_meta: f_clamp_kqv = 0.0e+00
73
llm_load_print_meta: f_max_alibi_bias = 0.0e+00
74
llm_load_print_meta: f_logit_scale = 0.0e+00
75
llm_load_print_meta: n_ff = 18944
76
llm_load_print_meta: n_expert = 0
77
llm_load_print_meta: n_expert_used = 0
78
llm_load_print_meta: causal attn = 1
79
llm_load_print_meta: pooling type = 0
80
llm_load_print_meta: rope type = 2
81
llm_load_print_meta: rope scaling = linear
82
llm_load_print_meta: freq_base_train = 10000.0
83
llm_load_print_meta: freq_scale_train = 1
84
llm_load_print_meta: n_ctx_orig_yarn = 131072
85
llm_load_print_meta: rope_finetuned = unknown
86
llm_load_print_meta: ssm_d_conv = 0
87
llm_load_print_meta: ssm_d_inner = 0
88
llm_load_print_meta: ssm_d_state = 0
89
llm_load_print_meta: ssm_dt_rank = 0
90
llm_load_print_meta: ssm_dt_b_c_rms = 0
91
llm_load_print_meta: model type = 7B
92
llm_load_print_meta: model ftype = Q4_K - Medium
93
llm_load_print_meta: model params = 7.62 B
94
llm_load_print_meta: model size = 4.36 GiB (4.91 BPW)
95
llm_load_print_meta: general.name = DeepSeek R1 Distill Qwen 7B
96
llm_load_print_meta: BOS token = 151646 '<|begin▁of▁sentence|>'
97
llm_load_print_meta: EOS token = 151643 '<|end▁of▁sentence|>'
98
llm_load_print_meta: EOT token = 151643 '<|end▁of▁sentence|>'
99
llm_load_print_meta: PAD token = 151643 '<|end▁of▁sentence|>'
100
llm_load_print_meta: LF token = 148848 'ÄĬ'
101
llm_load_print_meta: FIM PRE token = 151659 '<|fim_prefix|>'
102
llm_load_print_meta: FIM SUF token = 151661 '<|fim_suffix|>'
103
llm_load_print_meta: FIM MID token = 151660 '<|fim_middle|>'
104
llm_load_print_meta: FIM PAD token = 151662 '<|fim_pad|>'
105
llm_load_print_meta: FIM REP token = 151663 '<|repo_name|>'
106
llm_load_print_meta: FIM SEP token = 151664 '<|file_sep|>'
107
llm_load_print_meta: EOG token = 151643 '<|end▁of▁sentence|>'
108
llm_load_print_meta: EOG token = 151662 '<|fim_pad|>'
109
llm_load_print_meta: EOG token = 151663 '<|repo_name|>'
110
llm_load_print_meta: EOG token = 151664 '<|file_sep|>'
111
llm_load_print_meta: max token length = 256
112
llm_load_tensors: offloading 28 repeating layers to GPU
113
llm_load_tensors: offloading output layer to GPU
114
llm_load_tensors: offloaded 29/29 layers to GPU
115
llm_load_tensors: CPU model buffer size = 292.36 MiB
116
llm_load_tensors: CUDA0 model buffer size = 4168.09 MiB
117
llama_new_context_with_model: n_seq_max = 4
118
llama_new_context_with_model: n_ctx = 8192
119
llama_new_context_with_model: n_ctx_per_seq = 2048
120
llama_new_context_with_model: n_batch = 2048
121
llama_new_context_with_model: n_ubatch = 512
122
llama_new_context_with_model: flash_attn = 0
123
llama_new_context_with_model: freq_base = 10000.0
124
llama_new_context_with_model: freq_scale = 1
125
llama_new_context_with_model: n_ctx_per_seq (2048) < n_ctx_train (131072) -- the full capacity of the model will not be utilized
126
llama_kv_cache_init: kv_size = 8192, offload = 1, type_k = 'f16', type_v = 'f16', n_layer = 28, can_shift = 1
127
llama_kv_cache_init: CUDA0 KV buffer size = 448.00 MiB
128
llama_new_context_with_model: KV self size = 448.00 MiB, K (f16): 224.00 MiB, V (f16): 224.00 MiB
129
llama_new_context_with_model: CUDA_Host output buffer size = 2.38 MiB
130
llama_new_context_with_model: CUDA0 compute buffer size = 492.00 MiB
131
llama_new_context_with_model: CUDA_Host compute buffer size = 23.01 MiB
132
llama_new_context_with_model: graph nodes = 986
133
llama_new_context_with_model: graph splits = 2
134
time=2025-01-26T20:15:57.207+08:00 level=INFO source=server.go:594 msg="llama runner started in 2.76 seconds"
135
llama_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))
136
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
137
llama_model_loader: - kv 0: general.architecture str = qwen2
138
llama_model_loader: - kv 1: general.type str = model
139
llama_model_loader: - kv 2: general.name str = DeepSeek R1 Distill Qwen 7B
140
llama_model_loader: - kv 3: general.basename str = DeepSeek-R1-Distill-Qwen
141
llama_model_loader: - kv 4: general.size_label str = 7B
142
llama_model_loader: - kv 5: qwen2.block_count u32 = 28
143
llama_model_loader: - kv 6: qwen2.context_length u32 = 131072
144
llama_model_loader: - kv 7: qwen2.embedding_length u32 = 3584
145
llama_model_loader: - kv 8: qwen2.feed_forward_length u32 = 18944
146
llama_model_loader: - kv 9: qwen2.attention.head_count u32 = 28
147
llama_model_loader: - kv 10: qwen2.attention.head_count_kv u32 = 4
148
llama_model_loader: - kv 11: qwen2.rope.freq_base f32 = 10000.000000
149
llama_model_loader: - kv 12: qwen2.attention.layer_norm_rms_epsilon f32 = 0.000001
150
llama_model_loader: - kv 13: general.file_type u32 = 15
151
llama_model_loader: - kv 14: tokenizer.ggml.model str = gpt2
152
llama_model_loader: - kv 15: tokenizer.ggml.pre str = qwen2
153
llama_model_loader: - kv 16: tokenizer.ggml.tokens arr[str,152064] = ["!", "\"", "#", "$", "%", "&", "'", ...
154
llama_model_loader: - kv 17: tokenizer.ggml.token_type arr[i32,152064] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
155
llama_model_loader: - kv 18: tokenizer.ggml.merges arr[str,151387] = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...
156
llama_model_loader: - kv 19: tokenizer.ggml.bos_token_id u32 = 151646
157
llama_model_loader: - kv 20: tokenizer.ggml.eos_token_id u32 = 151643
158
llama_model_loader: - kv 21: tokenizer.ggml.padding_token_id u32 = 151643
159
llama_model_loader: - kv 22: tokenizer.ggml.add_bos_token bool = true
160
llama_model_loader: - kv 23: tokenizer.ggml.add_eos_token bool = false
161
llama_model_loader: - kv 24: tokenizer.chat_template str = {% if not add_generation_prompt is de...
162
llama_model_loader: - kv 25: general.quantization_version u32 = 2
163
llama_model_loader: - type f32: 141 tensors
164
llama_model_loader: - type q4_K: 169 tensors
165
llama_model_loader: - type q6_K: 29 tensors
166
llm_load_vocab: special_eos_id is not in special_eog_ids - the tokenizer config may be incorrect
167
llm_load_vocab: special tokens cache size = 22
168
llm_load_vocab: token to piece cache size = 0.9310 MB
169
llm_load_print_meta: format = GGUF V3 (latest)
170
llm_load_print_meta: arch = qwen2
171
llm_load_print_meta: vocab type = BPE
172
llm_load_print_meta: n_vocab = 152064
173
llm_load_print_meta: n_merges = 151387
174
llm_load_print_meta: vocab_only = 1
175
llm_load_print_meta: model type = ?B
176
llm_load_print_meta: model ftype = all F32
177
llm_load_print_meta: model params = 7.62 B
178
llm_load_print_meta: model size = 4.36 GiB (4.91 BPW)
179
llm_load_print_meta: general.name = DeepSeek R1 Distill Qwen 7B
180
llm_load_print_meta: BOS token = 151646 '<|begin▁of▁sentence|>'
181
llm_load_print_meta: EOS token = 151643 '<|end▁of▁sentence|>'
182
llm_load_print_meta: EOT token = 151643 '<|end▁of▁sentence|>'
183
llm_load_print_meta: PAD token = 151643 '<|end▁of▁sentence|>'
184
llm_load_print_meta: LF token = 148848 'ÄĬ'
185
llm_load_print_meta: FIM PRE token = 151659 '<|fim_prefix|>'
186
llm_load_print_meta: FIM SUF token = 151661 '<|fim_suffix|>'
187
llm_load_print_meta: FIM MID token = 151660 '<|fim_middle|>'
188
llm_load_print_meta: FIM PAD token = 151662 '<|fim_pad|>'
189
llm_load_print_meta: FIM REP token = 151663 '<|repo_name|>'
190
llm_load_print_meta: FIM SEP token = 151664 '<|file_sep|>'
191
llm_load_print_meta: EOG token = 151643 '<|end▁of▁sentence|>'
192
llm_load_print_meta: EOG token = 151662 '<|fim_pad|>'
193
llm_load_print_meta: EOG token = 151663 '<|repo_name|>'
194
llm_load_print_meta: EOG token = 151664 '<|file_sep|>'
195
llm_load_print_meta: max token length = 256
196
llama_model_load: vocab only - skipping tensors
197
[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

default

設定 Chat Model

在設定檔中,找到對應的區塊並加入以下內容:

config.json
1
{
2
"models": [
3
{
4
"title": "Deepseek-R1 7B",
5
"provider": "ollama",
6
"model": "deepseek-r1:latest"
7
}
8
]
9
}

如果有綁定特定的 IP 位址或網域名稱,則需要加入以下內容:

config.json
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 進行測試:

default

設定自動補全

在設定檔中,找到對應的區塊並加入以下內容:

config.json
1
{
2
"tabAutocompleteModel": [
3
{
4
"title": "Deepseek-R1 7B",
5
"provider": "ollama",
6
"model": "deepseek-r1:latest"
7
}
8
]
9
}

設定完成後,點擊右下角的 Continue 按鈕,在選單中切換成剛剛設定的 R1 模型:

default

讓我們來試試看效果:

default

實際使用後,只能說基本功能是可用的,但程式碼補全的能力還有待加強。

或許經過微調後表現會更好,但這並非我的專業領域。

Cline:命令列開發助手

Cline 是一個強大的 VS Code 命令列增強工具,其主要功能包括:

  • 智慧建立與編輯檔案
  • 協助探索大型專案結構
  • 整合瀏覽器操作
  • 安全執行終端機指令(需要使用者授權)

Cline 能夠實現 Cursor AI Pane 中大部分的 Composer 功能,它可以將複雜的軟體開發任務拆解成多個小型任務,並透過上述工具逐步完成。

在 Cline 中設定 Ollama

相較於 Continue,在 Cline 中設定 Ollama 顯得相當簡單,只需在設定頁面中選擇對應的模型即可:

default

然而,在實際測試時,我發現雖然基本的對話功能可以正常運作,但 Cline 無法正常讀寫檔案以及執行終端機指令:

default

經過查證,這似乎是一個已知問題,詳細資訊請參考 Issue #1418。目前只能等待社群提供解決方案。

總結與展望

蒸餾版 DeepSeek R1 的問世,為計算資源有限的獨立開發者提供了一個可行的本地 LLM 解決方案。雖然像 R1:7B 這樣的小型模型還無法勝任複雜的 AI Agent 任務,但作為 RAG(檢索增強生成)系統的基礎模型已經綽綽有餘。

當然,如果預算允許,目前主流的 AI 整合應用都已支援 R1 API,例如 Cursor:

default

之後我會嘗試基於 DeepSeek R1 本地模型來搭建個人的 RAG 系統,敬請期待!

參考資料

本文標題:在本地運行 DeepSeek R1:整合 VS Code 開發環境
文章作者:Vincent Lin
發布時間:2025-01-27