Featured image of post 为使用 Ollama 部署的 DeepSeek V3 启用函数调用功能

为使用 Ollama 部署的 DeepSeek V3 启用函数调用功能

通过自定义 Ollama Modelfile 使 DeepSeek V3 支持函数调用

引言

DeepSeek V3 是一个强大的开源语言模型,而Ollama则提供了便捷的本地部署方案。本文将介绍如何通过自定义ModelfileDeepSeek V3添加函数调用功能,从而支持一些常见的agent框架。

准备工作

1
2
3
4
5
# 确保已经安装了 Ollama
ollama --version

# 拉取 DeepSeek V3 模型
ollama pull deepseek-v3

自定义 Modelfile

创建一个新的 Modelfile

1
vim deepseek-v3t.modelfile

把以下内容复制进去:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
FROM deepseek-v3:latest
TEMPLATE """{{- if .Messages }}
{{- if or .System .Tools }}
{{- if .System }}
{{ .System }}
{{- end }}
{{- if .Tools }}
{{- end }}
{{- end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 }}
{{- if eq .Role "user" }}<|User|>
{{- if and $.Tools $last }}
Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.
Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
{{ $.Tools }}
{{- end }}
{{ .Content }}
{{- if $last }}<|Assistant|>{{ end }}
{{- else if eq .Role "assistant" }}
<|Assistant|>
{{- if .ToolCalls }}
<|tool▁calls▁begin|>
{{- range .ToolCalls }}
<|tool▁call▁begin|>
{"name": "{{ .Function.Name }}", "parameters": {{ .Function.Arguments }}}
<|tool▁call▁end|>
{{- end }}
<|tool▁calls▁end|>
{{- else }}
{{ .Content }}
{{- if not $last }}<|end▁of▁sentence|>{{- end }}
{{- end }}
{{- else if eq .Role "tool" }}
<|tool▁outputs▁begin|>
<|tool▁output▁begin|>
{{ .Content }}
<|tool▁output▁end|>
<|tool▁outputs▁end|>
{{- if and $last (ne .Role "assistant") }}<|Assistant|>{{- end }}
{{- end }}
{{- end }}
{{- else }}
{{- if .System }}
{{ .System }}
{{- end }}
{{- if .Prompt }}
<|User|>
{{ .Prompt }}
{{- end }}
<|Assistant|>
{{ .Response }}
{{- if .Response }}{{ end }}
{{- end }}"""

使用自定义 Modelfile

1
ollama create deepseek-v3t -f deepseek-v3t.modelfile

使用示例

任选一个你喜欢的agent框架,调用 openai 兼容的 sdk,就可以直接使用了。 这里以pydantic-ai为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from openai import AsyncOpenAI
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIModel

openai_client = AsyncOpenAI(
    api_key="test",
    base_url="http://localhost:11434/v1",
)
# 如果你创建的模型名称不是 deepseek-v3t,需要自己修改
model = OpenAIModel("deepseek-v3t", openai_client=openai_client)

agent = Agent(
    model
)

# 为 agent 添加一个工具函数
@agent.tool
async def get_time():
    import datetime
    return f"Current time is {datetime.datetime.now()}"

async def main():
    response = await agent.run("What time is it?")
    print(response)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

只要这个代码可以运行,那就说明你已经成功为DeepSeek V3添加了函数调用功能,即使他没调用你给的工具函数 😀

其他

解决闪退问题

由于DeepSeek系列模型不支持K-shift,当上下文达到一定长度时,会导致ollama闪退。目前ollama官方还没给出一个比较好的解决方案,这个问题暂时可以通过编辑Modelfile来规避,你只需要在 Modelfile 中添加下面这两行即可:

1
2
PARAMETER num_ctx 24576
PARAMETER num_predict 8192

修改后的完整 Modelfile 如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
FROM deepseek-v3:latest
PARAMETER num_ctx 24576
PARAMETER num_predict 8192
TEMPLATE """{{- if .Messages }}
{{- if or .System .Tools }}
{{- if .System }}
{{ .System }}
{{- end }}
{{- if .Tools }}
{{- end }}
{{- end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 }}
{{- if eq .Role "user" }}<|User|>
{{- if and $.Tools $last }}
Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.
Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
{{ $.Tools }}
{{- end }}
{{ .Content }}
{{- if $last }}<|Assistant|>{{ end }}
{{- else if eq .Role "assistant" }}
<|Assistant|>
{{- if .ToolCalls }}
<|tool▁calls▁begin|>
{{- range .ToolCalls }}
<|tool▁call▁begin|>
{"name": "{{ .Function.Name }}", "parameters": {{ .Function.Arguments }}}
<|tool▁call▁end|>
{{- end }}
<|tool▁calls▁end|>
{{- else }}
{{ .Content }}
{{- if not $last }}<|end▁of▁sentence|>{{- end }}
{{- end }}
{{- else if eq .Role "tool" }}
<|tool▁outputs▁begin|>
<|tool▁output▁begin|>
{{ .Content }}
<|tool▁output▁end|>
<|tool▁outputs▁end|>
{{- if and $last (ne .Role "assistant") }}<|Assistant|>{{- end }}
{{- end }}
{{- end }}
{{- else }}
{{- if .System }}
{{ .System }}
{{- end }}
{{- if .Prompt }}
<|User|>
{{ .Prompt }}
{{- end }}
<|Assistant|>
{{ .Response }}
{{- if .Response }}{{ end }}
{{- end }}"""

然后根据上文给出的命令重新创建模型即可。

DeepSeek R1 也适用吗?

理论上,添加了这个模板之后DeepSeek R1也可以有工具调用的能力,但是由于模型默认输出的<think>xxx</think>标签,不一定和常见的agent框架兼容,所以可能需要一些额外的处理,我也没有测试过,如果你有兴趣,可以试试看。

相关资料


使用 Hugo 构建
主题 StackJimmy 设计