之前在业务迭代里尝试大模型 Agent 落地时,经常卡在三个问题上:云端 API 费用太贵、工具调用框架配置复杂、本地模型效果和成本难以平衡。网上资料要么只讲推理部署,要么只讲函数调用,很少有一篇文章把“本地开源模型 + 云端大模型调度”这一套组合打通。本文以“千问 3.8”这类社区习惯称呼的 Qwen 系列开源模型为例,完整拆解一套本地部署超智能 Agent 的闭环方案,配合 DeepSeek Harness 作为意图识别与工具调度层,在已有硬件的前提下,可以做到不直接购买商业 API 套餐就能跑起一个可用的 Agent 系统。新手能跟着一步步完成环境搭建,有基础的开发者可以直接跳到架构设计和核心代码部分复用。
在开始之前先明确一点:截至目前,社区里传的“千问 3.8”并不是一个有明确定义的官方版本号,通常指的是 Qwen(通义千问)系列当前可用的开源模型,例如 Qwen2.5、QwQ-32B 等。本文讲的是这套“本地部署 + 云端调度”的通用思路,模型名称和版本不影响整体架构,你只需要把实际拉取的模型名称替换到代码里即可。
1. 背景与核心概念
1.1 本地部署开源模型解决什么问题
大模型 Agent 并不是某个单一模型在工作,它通常包含三层能力:理解用户意图、调用外部工具、生成最终回复。如果全部依赖云端 API,每轮对话和每次工具调用都会产生 token 费用,高频使用场景下成本会快速累积;如果全部依赖本地小模型,则会出现意图识别不准确、工具调用格式不稳定、长文本理解能力弱等问题。
本地部署开源模型的核心价值在于:推理过程可控、数据不出内网、没有按次计费的订阅成本。配合 DeepSeek Harness 这类调度层方案,可以让本地模型负责“生成和表达”,让云端大模型负责“理解和决策”,两者各取所长。
这里要先解释两个易混淆的概念:
- 本地部署:指把开源模型权重下载到你自己的机器上,通过推理框架加载,你的数据在本地完成计算,不需要上传到第三方服务。
- DeepSeek Harness:并不是 DeepSeek 官方发布的一个固定软件包,而是社区对“以 DeepSeek 大模型能力为调度器/harness,驱动本地模型完成复杂任务”这一架构模式的统称。实现方式可以是一个 Python 编写的 Agent 框架、一个函数调用解析器,也可以是一套评测与回环机制。
本文的统一理解方式:本地模型是“工人”,DeepSeek 在线模型是“调度员”,Harness 是连接两者的“通信管道”。
1.2 Agent 为什么需要 Harness
单独部署一个大模型只能做对话,不是 Agent。Agent 至少要能自主判断何时需要查资料、何时需要执行计算、何时需要调用外部 API。Qwen 系列开源模型虽然支持 function calling 格式,但在复杂指令下,小模型的工具调用成功率明显低于云端大模型。
Harness 在这里承担三个职责:
- 意图路由:判断用户输入属于闲聊、知识问答、工具调用还是代码执行。
- 参数抽取:从用户自然语言中提取工具需要的结构化参数。
- 结果校验:把工具返回的结果组织成模型可理解的上下文,再交给本地模型生成最终回复。
这种拆分的最大好处是省钱。意图路由和参数抽取属于高密度逻辑任务,token 消耗远低于让云端模型做完整的多轮对话生成,因此即使调用 DeepSeek API,在免费额度内通常也能覆盖日常开发和测试需求。
1.3 适用场景与局限性
这套方案最适合以下场景:
- 个人开发者想在已有电脑上搭建私人 Agent。
- 企业内部希望在数据不出内网的前提下试用 Agent 能力。
- 学生在学习大模型应用开发,希望用最低成本跑通整个链路。
但也有明显的局限:
- 本地模型参数量受限于显存和内存,生成质量和云端旗舰模型仍有差距。
- DeepSeek API 作为调度层仍然属于云端调用,如果涉及机密数据,不应把用户输入直接发给云端。
- “不花一分钱”的前提是你已经有可用的硬件设备和网络环境,本质上是把成本从订阅费转移到了硬件和电力上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
在开始操作之前,先把环境逐个确认好。下面的版本以当前常见稳定版本为例,不同时间下载时请以官方仓库最新发布为准,不要盲目锁定版本号。
2.1 硬件要求
本地部署 Qwen 系列开源模型的硬件门槛取决于你选择的模型参数量。给一个参考区间:
| 模型规模 | 量化方式 | 最低内存建议 | 适合场景 |
|---|---|---|---|
| 0.5B ~ 1.5B | Q4_K_M | 4GB 内存 | 简单对话与测试 |
| 3B ~ 4B | Q4_K_M | 8GB 内存 | 工具调用开发调试 |
| 7B ~ 8B | Q4_K_M | 16GB 内存 | 生产级 Agent 核心推理 |
| 14B+ | Q4_K_M | 32GB 内存 / 10GB 以上显存 | 高质量生成与复杂任务 |
如果只有 CPU 没有显卡,依然可以运行小参数模型,只是推理速度会明显变慢。本文示例以 3B ~ 8B 量化模型为主,普通 16GB 内存办公电脑即可运行。
2.2 软件环境建议
- 操作系统:Windows 10/11、Ubuntu 20.04+、macOS 均可,本文命令以 Ubuntu 和 Windows 双平台说明。
- Python:3.10 或 3.11,用于编写 Agent 调度逻辑。
- Ollama:用于本地模型加载与 OpenAI 兼容 API 暴露。
- Git:用于拉取示例项目。
- 代码编辑器:VS Code 或任意你熟悉的 IDE。
- DeepSeek API Key:在 DeepSeek 开放平台注册获取,新用户通常会有一定额度的免费体验,具体以官网实时政策为准。
2.3 说明
以下所有命令都需要在终端中执行。Windows 用户建议使用 PowerShell 或 Windows Terminal,Linux/macOS 用户使用 Bash。版本信息在写文章时是准确的,随着生态更新可能变化,遇到兼容性问题优先查看官方文档。
3. 架构设计:本地模型与 DeepSeek Harness 如何协作
先来看整体数据流,理解之后后面的代码才不突兀。整个 Agent 系统由四部分组成:
- 用户输入层:接收用户自然语言提问。
- DeepSeek 调度层(Harness):判断意图并抽取参数。
- 本地 Qwen 推理层:负责生成回复、补全信息、执行本地逻辑。
- 工具执行层:提供计算器、文件查询、API 调用等能力。
完整的一次请求流程如下:
- 第 1 步:用户输入“帮我计算一下 (23*45) + 789 = ?”
- 第 2 步:本地 Qwen 先对输入做初判,如果只是闲聊,直接生成回复,不调用任何工具。
- 第 3 步:如果判断需要工具,把原始输入发给 DeepSeek Harness,由云端模型输出 JSON 格式的工具调用指令,例如工具名 calculator 和参数表达式。
- 第 4 步:Python 调度代码解析 JSON,执行对应工具函数。
- 第 5 步:工具返回结果后,连同原始问题和结果一起交给本地 Qwen,生成自然语言回复。
为什么要这样设计而不是让本地模型直接输出工具调用?因为小模型直接输出 JSON 时经常出现格式错误、参数缺少引号、数组多一个逗号等问题。DeepSeek 这类云端模型在指令遵循能力上更强,输出的 JSON 结构更稳定。两种模型互补,成本却能控制住。
3.1 本地模型与云端模型的职责边界
| 职责 | 由谁负责 | 原因 |
|---|---|---|
| 闲聊与开放生成 | 本地 Qwen | 免费、隐私、响应快 |
| 意图识别 | DeepSeek Harness | 准确率高,支持复杂指令 |
| 参数抽取 | DeepSeek Harness | JSON 格式稳定 |
| 工具执行 | Python 函数 | 确定性逻辑,不该交给模型 |
| 最终润色回复 | 本地 Qwen | 控制成本,体验流畅 |
3.2 为什么选择 OpenAI 兼容协议
Ollama 默认提供 OpenAI 兼容的 /v1/chat/completions 接口,这意味着你可以用 OpenAI SDK 的方式调用本地模型,代码层面完全不需要区分“本地模型”和“云端模型”的请求格式。DeepSeek API 同样兼容 OpenAI 协议,因此两套调用可以封装在同一个客户端里,只是 base_url 和 api_key 不同。
代码里建议把 base_url 统一配置为环境变量,方便随时切换。
4. 实战:本地部署 Qwen 开源模型
4.1 安装 Ollama
Ollama 是目前最简单的本地模型运行工具之一,它封装了模型下载、量化、推理服务启动等流程。
Linux / macOS 执行:
bash复制curl -fsSL https://ollama.com/install.sh | sh
Windows 用户直接访问 Ollama 官网下载安装包,安装后命令行里输入 ollama -v 验证。
4.2 拉取 Qwen 开源模型
以 Qwen 系列中适合普通电脑配置的 3B 模型为例:
bash复制ollama pull qwen3:4b
如果你使用的是 Qwen2.5 系列,可以执行:
bash复制ollama pull qwen2.5:7b
这里不要纠结具体哪一个模型“最好”。模型更新迭代很快,ollama pull 命令支持通过标签指定版本,你可以先拉一个 3B 级别的模型验证流程,再根据自己的硬件条件升级到更大参数版本。
下载完成后启动本地服务:
bash复制ollama serve
正常情况下服务会监听在 http://localhost:11434。
4.3 调用本地模型验证
用 Python 请求本地模型的 chat 接口:
python复制# 文件路径:test_local_qwen.py
import requests
import json
url = "http://localhost:11434/v1/chat/completions"
payload = {
"model": "qwen3:4b",
"messages": [
{"role": "user", "content": "用一句话介绍你自己"}
],
"temperature": 0.7
}
resp = requests.post(url, json=payload)
data = resp.json()
print(data["choices"][0]["message"]["content"])
如果控制台打印出了一句自然语言回复,说明本地模型已经可以正常工作。
4.4 确认工具调用能力
这里有一个常见的认知误区:本地模型“支持 function calling”不代表开箱即用。在 Ollama 中,部分 Qwen 系列模型需要通过特定的 prompt 格式来触发工具调用,不同版本的兼容性也有差异。
为了不把希望全部寄托在本地模型格式稳定性上,架构中把工具决策交给 DeepSeek Harness,本地模型只做最终回复生成。这样即使本地模型不输出规整 JSON,整个 Agent 流程依然能跑通。
5. 实战:搭建 DeepSeek Harness 调度层
5.1 申请 DeepSeek API Key
登录 DeepSeek 开放平台,在控制台创建 API Key。创建之后把 Key 保存到环境变量,不要直接写在代码里。
Linux / macOS 临时设置:
bash复制export DEEPSEEK_API_KEY="sk-xxxxxxxx"
Windows PowerShell 临时设置:
powershell复制$env:DEEPSEEK_API_KEY="sk-xxxxxxxx"
需要说明的是,DeepSeek API 的计费政策会随运营策略变化,新用户是否赠送免费额度、赠送多少,以官方最新说明为准。本文演示的代码在额度内即可完成学习和测试。
5.2 编写 Agent 核心框架
下面创建完整的 Agent 项目。建议目录结构如下:
code复制deepseek-agent/
├── main.py
├── tools.py
├── harness.py
├── config.py
└── requirements.txt
创建设置文件 config.py:
python复制# 文件路径:config.py
import os
# 本地 Qwen 模型配置
LOCAL_MODEL_NAME = "qwen3:4b"
LOCAL_BASE_URL = "http://localhost:11434/v1"
# DeepSeek 调度层配置
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
DEEPSEEK_BASE_URL = "https://api.deepseek.com/v1"
DEEPSEEK_MODEL = "deepseek-chat"
创建工具模块 tools.py:
python复制# 文件路径:tools.py
import math
import datetime
def calculator(expression: str) -> str:
"""安全计算数学表达式,只允许数字和基础运算符。"""
allowed = set("0123456789+-*/(). ")
for ch in expression:
if ch not in allowed:
raise ValueError(f"非法字符: {ch}")
# 避免直接使用 eval,这里用 ast 模块做更安全的解析
import ast
try:
tree = ast.parse(expression, mode="eval")
result = eval(compile(tree, "<string>", "eval"), {"__builtins__": {}}, {})
return str(result)
except Exception as e:
return f"计算失败: {e}"
def get_current_time() -> str:
"""返回当前时间。"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
TOOLS = {
"calculator": {
"description": "计算数学表达式,参数为字符串形式的算术表达式",
"function": calculator
},
"get_current_time": {
"description": "获取当前日期和时间",
"function": get_current_time
}
}
创建 Harness 调度模块 harness.py:
python复制# 文件路径:harness.py
import json
import requests
from config import DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL
SYSTEM_PROMPT = """
你是一个工具调用调度器。你需要根据用户输入判断是否调用工具。
如果需要调用工具,只输出 JSON,不要输出任何其他文字。
JSON 格式必须如下:
{"tool": "工具名", "params": {"参数名": "参数值"}}
可用工具:
{available_tools}
如果不需要调用工具,输出: {"tool": "none", "params": {}}
""".strip()
def parse_user_intent(user_input: str) -> dict:
"""调用 DeepSeek 模型解析用户意图,返回工具调用指令。"""
url = f"{DEEPSEEK_BASE_URL}/chat/completions"
headers = {
"Authorization": f"Bearer {DEEPSEEK_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": DEEPSEEK_MODEL,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT.format(
available_tools=json.dumps({k: v["description"] for k, v in TOOLS.items()}, ensure_ascii=False)
)},
{"role": "user", "content": user_input}
],
"temperature": 0,
"max_tokens": 200
}
resp = requests.post(url, headers=headers, json=payload, timeout=30)
resp.raise_for_status()
content = resp.json()["choices"][0]["message"]["content"].strip()
# 去掉可能的 ```json 包裹
if content.startswith("```"):
content = content.strip("`")
if content.startswith("json"):
content = content[4:]
return json.loads(content)
创建主程序 main.py:
python复制# 文件路径:main.py
import json
from config import LOCAL_MODEL_NAME, LOCAL_BASE_URL
from harness import parse_user_intent
from tools import TOOLS
import requests
def call_local_qwen(messages):
"""调用本地 Qwen 模型生成最终回复。"""
url = f"{LOCAL_BASE_URL}/chat/completions"
payload = {
"model": LOCAL_MODEL_NAME,
"messages": messages,
"temperature": 0.7
}
resp = requests.post(url, json=payload, timeout=60)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
def run_agent(user_input: str):
# 第一步:交给 DeepSeek 调度器判断是否需要调用工具
intent = parse_user_intent(user_input)
tool_name = intent.get("tool", "none")
params = intent.get("params", {})
if tool_name in TOOLS:
try:
result = TOOLS[tool_name]["function"](**params)
# 第二步:把工具结果交给本地模型组织语言
messages = [
{"role": "system", "content": "你是一个乐于助人的助手,请根据工具返回结果自然作答。"},
{"role": "user", "content": f"用户问题:{user_input}\n工具返回:{result}"}
]
response = call_local_qwen(messages)
return response
except Exception as e:
return f"工具执行出错:{e}"
# 不需要工具,直接交给本地模型回答
messages = [
{"role": "system", "content": "你是一个乐于助人的助手。"},
{"role": "user", "content": user_input}
]
response = call_local_qwen(messages)
return response
if __name__ == "__main__":
while True:
user_input = input("请输入你的问题(输入 exit 退出):")
if user_input.lower() == "exit":
break
answer = run_agent(user_input)
print("Agent 回复:", answer)
再补上依赖文件:
code复制# 文件路径:requirements.txt
requests>=2.31.0
5.3 工具注册与扩展
上面代码中 TOOLS 字典是整个工具的注册表。以后想新增一个天气查询工具,只需要写一个普通函数,然后在 TOOLS 里注册描述信息即可,调度层完全不需要改动。
例如增加一个新工具:
python复制def get_ip_info(ip: str) -> str:
# 这里仅演示思路,实际调用第三方 IP 查询 API
return f"IP {ip} 的归属地查询功能待接入"
TOOLS["get_ip_info"] = {
"description": "查询 IP 地址归属地,参数为 IP 字符串",
"function": get_ip_info
}
注意,任何涉及外部网络请求的工具,在真实项目中都要考虑接口是否公开免费、返回数据是否含隐私、调用频率是否有限制。
6. 完整运行示例与结果验证
6.1 启动本地模型服务
先确保 Ollama 服务在运行,并确认模型已经拉取成功:
bash复制ollama list
输出中应该能看到 qwen3:4b 或你拉取的其他模型。
6.2 运行 Agent
在项目目录中执行:
bash复制python main.py
输入以下测试用例:
code复制用户输入:帮我算一下 23*45+789 等于多少?
预期流程:
- DeepSeek 调度器返回
{"tool": "calculator", "params": {"expression": "23*45+789"}}。 - Python 执行 calculator 函数,得到
1824。 - 本地 Qwen 生成回复:“根据计算,23*45+789 的结果是 1824。”
再测试一个不需要调用工具的问答:
code复制用户输入:你好,你今天感觉怎么样?
预期流程:DeepSeek 判断不需要工具,返回 {"tool": "none"},本地 Qwen 直接生成回复。
6.3 结果说明
从这个测试中可以直观看到,真正消耗成本的部分只有第 1 步的意图解析请求,而占回复篇幅最多的本地模型生成过程是完全免费的。在免费额度内,这个架构可以把 Agent 系统跑起来,确实可以做到零订阅费用。
7. 常见问题与排查思路
在本地部署和 Agent 联调过程中,最容易遇到的问题集中在以下几个场景。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| Ollama 启动失败 | 端口被占用 | 执行 ollama serve 前检查 11434 端口是否被占用 |
| 拉取模型速度慢 | 网络波动 | 更换镜像源或等待重试 |
| 本地模型回复乱码 | 模型名称写错 | 执行 ollama list 确认准确名称 |
| 本地模型响应超时 | 首次加载模型较慢 | 增加 Python 请求 timeout 到 60 秒以上 |
| DeepSeek API 返回 401 | API Key 为空或错误 | 检查环境变量 DEEPSEEK_API_KEY 是否设置 |
| JSON 解析报错 | DeepSeek 偶尔输出多余解释文字 | 在后处理中增加异常捕获并重试一次 |
| 工具结果和问题不匹配 | 参数抽取错误 | 在 SYSTEM_PROMPT 中补充更严格的格式说明 |
| 本地回答的是英文 | prompt 中没指定语言 | 在系统提示中加入“请用中文回复” |
7.1 排查顺序建议
当 Agent 没有给出预期回复时,按下面顺序排查:
- 先确认本地 Qwen 单独调用是否正常。直接用 Python 请求本地接口,排除推理层问题。
- 再确认 DeepSeek 调度层输出是否合法。打印
intent变量,检查 JSON 结构。 - 再确认工具函数入参是否合理。独立调用工具函数,排除计算逻辑问题。
- 最后检查最终 prompt 组装是否混乱,确认本地模型是否收到了正确的上下文。
大多数问题出在第 2 步,因为云端模型在极端输入下仍可能输出非标准 JSON。解决办法很简单:在 harness.py 的 parse_user_intent 中加入重试机制,最多重试两次,丢弃异常输出。
8. 最佳实践与工程建议
8.1 不要把密钥写在代码里
上面示例中 DeepSeek API Key 通过环境变量读取,这是必须养成的习惯。如果你把密钥硬编码到 config.py 中,一旦项目上传到公开仓库,密钥就会泄露,可能导致账号被盗用或产生额外费用。
如果项目需要多人协作,推荐使用 .env 文件配合 python-dotenv 管理环境变量,同时把 .env 加入 .gitignore。
8.2 为本地模型设计更完整的 Prompt
本地模型的回答质量高度依赖 Prompt 质量。建议在系统提示中写明:
- 身份设定。
- 回答语言。
- 回答风格。
- 工具结果的使用方式。
例如把系统提示改为:
code复制你是一个严谨的私人助理。请只根据工具返回的事实回答问题。如果工具返回的结果缺失或异常,请直接说明无法回答,不要编造数据。所有回复使用中文。
8.3 对工具函数做安全边界控制
在上面的 calculator 函数中,用白名单过滤了表达式字符,并清除了内置函数。这是一个重要的安全习惯。实际生产环境如果允许模型调用任意 Python 函数,可能带来严重安全风险。
再强调一次安全边界:
- 不接受任意路径参数,除非你明确允许访问某个目录。
- 不直接执行 shell 命令。
- 不把文件内容任意发送给云端 API。
- 外部 API 调用要限制请求频率。
8.4 日志与可观测性
在生产环境中,任何 Agent 系统都必须记录完整交互日志。建议至少记录以下内容:
- 用户输入原文。
- DeepSeek 返回的工具调用 JSON。
- 工具函数执行耗时与结果。
- 本地模型最终回复。
- 每轮请求的总耗时。
- 调用 DeepSeek 的 token 数。
这样即使将来发生了“Agent 说出奇怪内容”的事故,也能回溯是哪一层的输出导致的。
8.5 成本控制策略
虽然标题是“不花一分钱”,但真实使用中还是要关注成本。建议做三个控制:
- 在 Harness 中为 DeepSeek 请求设置
max_tokens,避免极端情况下输出超长内容。 - 如果某个工具调用的结果已经足够明确,可以跳过本地模型,直接返回工具结果。
- 对用户输入做长度限制,超长输入直接截断,避免 token 浪费。
9. 超智能 Agent 的进阶方向
跑通基础闭环之后,这套架构已经具备继续扩展的能力。下一步可以沿着下面几个方向深入:
9.1 接入 RAG 本地知识库
把工具层扩展为文档检索工具,将本地 PDF、Markdown 文件切分后存入向量数据库。用户提问时先检索相关片段,再交给本地 Qwen 生成回答。这个方向不需要额外购买服务,完全本地运行,是当前性价比最高的知识库方案。
9.2 引入多工具组合
当前的 Harness 每轮只输出一个工具调用。进阶版可以设计为输出一个工具调用列表,让 Agent 依次执行多个操作。例如用户问“对比这两份文档的差异”,Agent 需要先读取文档 A,再读取文档 B,最后汇总结果。这种多步工具链需要 Harness 支持递归调用或计划生成,复杂度会高一些,但功能实用性也更强。
9.3 用 DeepSeek 做评测器
除了做调度,DeepSeek 还能扮演评测者角色。让本地模型生成多轮回复,再由 DeepSeek 打分排序,把最优结果输出给用户。这种方式可以显著提升最终回答质量,同时因为评测属于高频短请求,成本依旧可控。
9.4 服务化部署
当开发调试完成后,可以把 main.py 封装为一个 FastAPI 应用,对外暴露 HTTP 接口,让网页端或其他业务系统统一调用。本文代码中的 run_agent 函数已经天然适配这种改造,只需要在外部包一层 API 路由即可。
10. 总结与学习路线
本文完整实现了“本地 Qwen 开源模型 + DeepSeek 调度层”的 Agent 闭环方案。你学会了安装 Ollama、拉取 Qwen 系列开源模型、编写工具注册表、调用 DeepSeek API 做意图解析、用本地模型生成最终回复,也掌握了这套架构中涉及的安全边界、日志记录和成本控制要点。
这个方案最大的价值不是“免费”两个字,而是为你提供了一种模块化的 Agent 设计思路:生成能力可以是本地模型,决策能力可以交给云端大模型,工具能力完全由自己控制。三层解耦之后,未来无论本地模型更新成什么版本、云端 API 换成什么服务商,你的核心代码都不需要推翻重写。
如果条件允许,建议你亲手把示例项目跑一遍,然后把工具列表扩展成自己的常用工具,再慢慢加入 RAG、评测器和多轮记忆能力。动手之后你会直观感受到,大模型 Agent 开发的门槛并没有想象中那么高,关键还是先把最小闭环跑通。
