过去半年,我把 DeepSeek 从一个“偶尔问两句”的模型,逐步调教成了手头几个项目里真正的 AI coding agent 主力。这个过程踩了不少坑,也踩通了 API、本地部署、harness 编排、编辑器接入这些链路。最近总有人问我:DeepSeek 到底能不能当 coding agent 用?和 Codex、Claude Code 这些怎么接?为什么我跑一轮就报 tool calls 相关错误?本地部署到底要什么显卡?
这篇文章就把我这段时间的实操经验梳理一遍。内容围绕 DeepSeek 原生 AI coding agent 这个主题,覆盖接入路线的选择、API 调用细节、工具调用时序、vLLM 本地部署、VS Code / Codex / Claude Code 接入、多智能体 harness 编排,以及高频报错的排查。不管你是刚接触 API 调用,还是已经在折腾本地部署的老手,应该都能找到对得上号的部分。
1. 从聊天到干活:DeepSeek 怎么当上 coding agent
1.1 聊天接口和 coding agent 接口差在哪
很多人第一次用 DeepSeek,是在网页聊天窗口里打字。聊天模式的核心是“一问一答”,你发消息,它回消息,上下文由前端帮你维护,你基本不需要关心消息怎么组织。但 coding agent 不一样。
所谓 AI coding agent,核心变化是模型需要主动、连续地完成任务:读文件、跑命令、看报错、改代码、再跑测试,直到得到合格结果。这中间模型要反复调用工具,每次工具返回的结果都要回到对话上下文里。也就是说,你需要的不是一次回答,而是一个能自己“干活并验收”的执行循环。
这个循环落到技术上,就是 messages 关系和 tool_calls 的时序处理。聊天网页把这些都藏起来了,而你自己搭 agent 的时候,这些细节全部暴露在你面前。这也是为什么很多人直接把网页聊天里的会话当成“agent”用,体验差距会很大。
1.2 DeepSeek 凭什么适合做这件事
DeepSeek 能在 coding agent 生态里被反复提起,核心原因有三个。
第一,API 兼容 OpenAI 格式。这意味着大量现成的工具链——从 OpenAI SDK 到各类开源 agent 框架——都能通过改 base_url 的方式直接接上 DeepSeek。你用过的所谓“Codex 接入 DeepSeek”“Continue 接入 DeepSeek”,本质都是这条兼容路径。
第二,价格确实低。对于每天要跑几百轮工具调用的 agent 场景,按 token 计费的成本会被快速放大。DeepSeek 的定价在同类模型里属于非常能打的那一档,这也让它成了很多个人开发者和中小团队搭建 agent 工作流的首选。
第三,开源权重配合本地部署能力。数据敏感的项目、断网环境、需要私有化交付的场景,DeepSeek 都给了你一条“自己拉起模型服务”的退路。这一点后面专门讲 vLLM 部署时会详细展开。
另外还有一点容易被忽略:DeepSeek 官方和社区会不定期放出智能体训练与工具调用相关的新方法。每次这类消息出来,harness、插件、接入教程就会更新一轮,整个生态的迭代速度非常快,这也是我敢把主力工作流押在它身上的原因。
2. 三条接入路线怎么选
2.1 官方 API:大多数人的第一站
如果是个人项目、小团队、或者想快速验证 DeepSeek 做 coding agent 是否可行,我的建议很直接:先走官方 API。理由有几个:
- 接入成本最低。只要你拿到 API Key,改造现有 OpenAI 兼容代码只需要换 base_url 和模型名,半小时内能跑通。
- 不需要自己维护显卡和推理服务,稳定性由平台兜底。
- 模型版本更新及时,官方升级模型,你这边几乎无感切换。
尤其对刚开始折腾 agent 的人来说,官方 API 能帮你把“模型本身的问题”和“部署环境的问题”分开。等你在这条路上跑顺了,再决定要不要本地化,思路会清晰很多。
当然,官方 API 也有它的限制:隐私数据要过外部接口、长时间多轮任务成本会累积、某些特定时段的负载波动可能影响响应速度。这些不是致命问题,但你要心里有数。
2.2 本地部署:需要数据闭环时的兜底
当你开始碰企业项目、内部代码库、或者有合规要求的数据时,API 这条路就走不通了。这时候本地部署是必须考虑的方案。
本地部署的本质,是你自己拿 GPU 把模型跑起来,提供一个 OpenAI 兼容的接口,让上层 agent 框架根本感觉不到后端换了。常见的部署方式有 vLLM、llama.cpp、SGLang 等,后面我会用 vLLM 举例完整走一遍。
本地部署的代价也很现实:你需要一块显存足够大的显卡,需要花时间配置量化、上下文长度、并发参数,遇到问题要自己排查。地道的说法是,本地部署不是省钱方案,而是“数据主权”方案。如果你只是因为觉得 API 贵想本地部署,建议先把账算清楚。
2.3 聚合平台:成本与便利的折中
除了官方 API 和完全本地化,还有一个中间选择:通过聚合平台使用 DeepSeek。比如硅基流动这类国内平台就提供了多种开源模型的统一接口,DeepSeek 系列也在其中。
聚合平台的好处是,一个 Key 能访问多个模型,方便你做模型对比和切换。比如同一套 agent 逻辑,今天用 DeepSeek,明天想试试别的模型,只需要改模型名。对做验证、做中间层开发的团队来说很顺手。
但聚合平台也有需要注意的地方:不同平台实现的 OpenAI 兼容程度不完全一致,有的对工具调用支持得比较粗糙,有的返回格式存在细微差异。选平台之前,一定要先在同一个 agent 场景下做一轮工具调用的回归测试,别等接了业务再发现兼容性问题。
3. DeepSeek API 调用与工具调用细节
3.1 拿到 Key 后先做一次最小验证
不管你是要用官方 API,还是测试本地 vLLM,我都建议先写一个最小调用脚本,把链路通一遍再往上叠功能。最小验证的代码很简单,用 OpenAI SDK 改 base_url 就能跑:
from openai import OpenAI client = OpenAI( api_key="sk-你的Key", base_url="https://api.deepseek.com" ) resp = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": "你是一个 Python 编码助手,回答时只给代码。"}, {"role": "user", "content": "写一个判断字符串是否为回文的函数。"} ], temperature=0.2, stream=True ) for chunk in resp: delta = chunk.choices[0].delta if delta and delta.content: print(delta.content, end="")这一小段能验证几件事:Key 是否有效、网络链路是否通、模型名是否写对、流式输出是否正常。很多“接入失败”的问题,80% 以上在这一步就能暴露出来。
注意一个细节:DeepSeek 的模型名是deepseek-chat和deepseek-reasoner这类官方名称。有些工具默认填的是“deepseek-v3”之类的社区叫法,接入时最好以官方文档列出的模型名为准,不然会直接报 model not found。
3.2 工具调用(tool calling)的时序与失败原因
Coding agent 区别于聊天机器人的关键,就是工具调用。模型判断“我需要执行命令”“我需要读取文件”时,会返回一个 tool_calls 请求,而不是普通文本。你的 agent 框架拿到这个请求,执行工具,再把结果作为一条消息放回 messages,然后继续调用模型。这个过程是循环,不是一次请求。
下面这段代码演示了如何把工具定义传给 DeepSeek:
from openai import OpenAI client = OpenAI(api_key="sk-你的Key", base_url="https://api.deepseek.com") tools = [ { "type": "function", "function": { "name": "run_shell", "description": "在项目目录执行一条 shell 命令,返回标准输出与错误信息", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "要执行的命令"} }, "required": ["command"] } } } ] messages = [ {"role": "user", "content": "运行项目里的测试,告诉我哪里失败了。"} ] resp = client.chat.completions.create( model="deepseek-chat", messages=messages, tools=tools, tool_choice="auto" ) # 这里大概率会拿到 tool_calls,而不是直接的回答文本 print(resp.choices[0].message.tool_calls)拿到 tool_calls 后,正确的做法是:执行工具,把结果以role="tool"的消息追加进 messages,然后用新的 messages 列表再次请求模型。很多人在这一步出问题:要么不执行工具就继续下一轮对话,要么把 tool 结果放在错误的角色里,要么没有追加原始请求而是重新开始。这些都会导致模型“失忆”或行为错乱。
提示:编码 agent 的工具调用是强时序的。你不能把“等待用户确认再返回工具结果”的逻辑塞进一个自动执行循环里,否则就会碰到下面要讲的 “tool calls need immediate results” 一类错误。
另外,tool_choice也有讲究。默认的auto让模型自己决定要不要调用工具,适合大多数场景。但如果你明确知道这一步必须执行某个工具,也可以显式指定tool_choice的值,不过这需要你对当前任务有足够强的判断,一般不建议新手过度约束。
3.3 上下文窗口与 Token 估算
DeepSeek 的上下文窗口足够长,但“足够长”不等于“随便造”。在 agent 场景里,每一轮工具调用都会把工具结果写回上下文,几十轮下来,上下文消耗是很快的。
我习惯在搭建 agent 时先做一轮 token 估算:系统提示占多少、初始任务占多少、每轮工具调用的平均输入输出是多少、最大轮数是多少。把这些数据算完,你就知道你跑的 agent 链路是否安全处于模型的上下文窗口内。
如果你用的是流式输出,需要关注 usage 字段里的prompt_tokens和completion_tokens,把每一轮的累计值打日志。我见过不少线上 agent 项目,第一件事不是调算法,而是先把这个 token 监控做起来。原因很简单:你连自己每次任务消耗多少 token 都不知道,就没法谈成本优化。
3.4 官方定价与预算控制
DeepSeek 的定价逻辑是典型的“输入便宜、输出贵、缓存命中更便宜”。在实际 coding agent 场景里,系统提示和工具定义是高度重复的,这部分如果能命中缓存,成本会明显下降。
做预算控制时,我的经验是分三块看:
- 固定开销:系统提示 + 工具定义 + 每轮轮次的 prompt 开销。
- 动态开销:模型输出的代码、分析文本,这部分按 completion 计费。
- 重试开销:agent 执行失败后的重试次数,往往是被低估的成本黑洞。
如果你的 agent 每轮失败概率偏高,导致平均每个任务多跑 5-10 轮,再便宜的模型也会把钱烧穿。所以“省钱”的重点反而在提高工具调用的成功率上:给出更清晰的执行规范、更细粒度的任务拆分、更完备的错误反馈格式。
4. 本地部署 DeepSeek:vLLM 方案实录
4.1 先算好显存再选模型
本地部署第一步不是装环境,而是算显存。很多人上来就下载最大的模型,结果显卡带不动,白白浪费时间。
一个粗算经验:fp16 精度下,模型大小约等于参数量的两倍字节数。一个 14B 模型,fp16 权重约 28GB,再加上 KV Cache 和中间激活,实际占用会更高。如果你只有一张 24GB 显存的卡,跑 14B 模型就要谨慎,通常需要量化或者限制上下文长度。
量化方案里,AWQ、GPTQ、GGUF 都比较常见。AWQ 和 GPTQ 适合 GPU 推理,GGUF 配合 llama.cpp 场景更多。我的建议是:vLLM 场景优先考虑 AWQ 量化版本,能用较小的显存跑出不错的速度和稳定性。
4.2 vLLM 拉起 OpenAI 兼容服务
vLLM 的优势在于吞吐高、兼容性好,而且它自带 OpenAI 兼容的 API Server。下面是一个典型启动命令:
python -m vllm.entrypoints.openai.api_server \ --model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \ --served-model-name deepseek-local \ --port 8000 \ --max-model-len 32768 \ --gpu-memory-utilization 0.9几个参数值得说明:
--served-model-name deepseek-local:这是对外暴露的模型名。客户端调用时 model 字段必须填这个名字,而不是原始模型名。--max-model-len:控制最大上下文长度。设太大会导致显存申请失败,设太小会导致长任务截断。--gpu-memory-utilization:设置显存利用率。给推理进程留一点余量,避免和其他程序冲突。
服务跑起来后,你可以直接用前面那个最小验证脚本,把 base_url 改成http://localhost:8000/v1,模型名改成deepseek-local,测试链路是否畅通。链路通了,上层工具完全无感知,这就是本地部署最关键的“兼容层”思维。
4.3 17B 量级模型适合做什么
很多人问 DeepSeek 17B 这种量级的模型能干什么。不瞒你说,这种中等参数规模的模型,在 agent 场景里非常合适,只要你别把它当全知全能的云端大模型用。
以我自己的经验,17B 左右的本地模型适合做三类事:
- 高频小任务:代码补全、函数生成、格式化、改写、注释生成。这类任务单轮就能完成,不需要太长上下文。
- 多 agent 系统中的子 agent:把复杂任务拆成多个子任务,每个子 agent 只负责一个窄领域,17B 完全够用。
- 数据敏感环境的内部辅助:不能出内网的代码问答和脚本生成,本地模型是底线方案。
如果你期望一个 17B 模型能处理涉及几十个文件、几十轮工具调用的复杂重构,那大概率会碰壁。正确姿势是:本地小模型做执行层和子任务,云上大模型做规划和复杂推理,两者组合成一个多智能体系统。
5. 接进常用开发工具的一线实测
5.1 VS Code:Continue 插件的快速配置
VS Code 里接 DeepSeek,我试过几条路,最省事的是 Continue 插件。你不需要额外装一堆东西,只需要在 Continue 的配置里追加一个模型源。
如果你用的是 Continue 自带的 DeepSeek provider,配置大概长这样:
{ "models": [ { "title": "DeepSeek Chat", "provider": "deepseek", "model": "deepseek-chat", "apiKey": "sk-你的Key" } ] }如果你本地用 vLLM 起了一个兼容服务,就把 provider 换成 openai,并指到本地地址:
{ "models": [ { "title": "DeepSeek Local", "provider": "openai", "model": "deepseek-local", "apiBase": "http://localhost:8000/v1", "apiKey": "EMPTY" } ] }实测下来,Continue 的自动补全、对话、代码编辑这些功能都能正常工作。有一个小坑:自动补全对延迟特别敏感,如果用本地小模型,显存不够就会卡,建议给补全单独配一个小模型,对话用大模型。
5.2 Codex CLI 接入 DeepSeek
“Codex 接入 DeepSeek”是社区里讨论很多的方向。Codex 类的命令行工具原本面向特定模型,但因为它遵循 OpenAI 兼容协议,很多实现允许你改模型提供商配置。
在 Codex 的配置文件里,把 provider 指向 DeepSeek 的 base_url,把模型名改成deepseek-chat,并填上你的 DeepSeek API Key,基本就能跑通。需要注意,Codex 的“自动执行”模式对工具调用链路的依赖非常强,所以请先确认你用的 DeepSeek 版本对 function calling 的支持稳定。
我用 Codex 类工具接 DeepSeek 的真实感受是:它在“生成计划 → 执行命令 → 读取结果 → 修正计划”这个循环上表现不错,但遇到复杂项目时,token 消耗会比想象中快。所以建议给 Codex 单独配一个较短的 system prompt,把事情描述清楚,不要让它自由发挥太多。
5.3 Claude Code 组合玩法与注意点
Claude Code 原本是另一个模型的专属工具,但社区里已经有很多人尝试把 DeepSeek 通过兼容层接进去,这也是“claude code deepseek 4.1”这类关键词的来源。本质上,它和 Codex 接入 DeepSeek 的思路一样,都是把客户端的模型端点替换掉。
这种跨模型接入,最大的风险在于系统提示和工具定义是为原模型调优的。不同模型对工具描述的敏感度不同,DeepSeek 对某些长 system prompt 的理解方式可能和原模型有差异。我的实测建议是:不要直接套用默认配置,把 system prompt 里针对原模型的特征描述删掉,换成更适合 DeepSeek 的简洁指令。
另外,这类组合方式的稳定性通常取决于兼容层的实现质量。如果你只是临时玩玩,问题不大;如果要做正式项目,建议彻底测试一轮完整流程再上。
5.4 企业微信机器人接入思路
“企业微信接入 DeepSeek”也是被问得很多的需求。常见场景是这样的:团队在企微群里发需求,机器人调用 DeepSeek 回答,或者把需求转成代码任务。
最轻量的方式是用企业微信自定义机器人 webhook。后端监听群里被 @ 的消息,把文本转发给 DeepSeek API,拿到结果 POST 回 webhook 地址。一个最小的发送端大概是这样的:
import requests webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的Key" def send_to_wecom(text): requests.post(webhook, json={ "msgtype": "text", "text": {"content": text} })这种方案的优点是简单,缺点是只能主动推送,没法做多轮对话。如果要做真正的企微内多轮 agent,就需要接入企微应用的消息回调,处理用户上下文。两个方案我都试过,前者适合做告警和消息通知,后者适合做正经的智能助手。后端服务要保证接口响应在企微允许的超时范围内,所以建议把 DeepSeek 调用做成异步任务,先回“正在处理”,再异步推送结果。
6. 多智能体编排:deepseek harness 这类工具的执念
6.1 harness 解决什么问题
当你不满足于单个 agent 跑一条流水线,而想把“规划”“写代码”“跑测试”“查文档”拆成多个角色协同工作时,就会碰到新的问题:谁来调度这些角色?谁维护它们之间的上下文?谁决定一个子任务算完成?
社区里出现的一类 deepseek harness 项目,就是干这个的。用个通俗类比,它就是“导演+场记”,负责安排各个 agent 上场、传递消息、判断什么时候切换角色、什么时候收工。它解决的正是多智能体编排的混乱问题。
这类系统通常以配置文件和 API 为核心。你定义若干 agent,每个 agent 有自己的 system prompt 和可用工具,harness 负责把它们组装成可执行的工作流。
6.2 Skill 机制:把高频操作固化成能力
多智能体系统里,最容易出现的性能浪费是:每个子 agent 每次运行时都要重新理解“怎么做”。
解决方式是 Skill 机制。简单说,就是把高频操作(比如“跑测试”“修复 lint 错误”“生成提交信息”)事先写成结构化提示或脚本,挂到 agent 能看到的目录里。需要时,agent 直接调用对应 skill,而不是靠现场发挥。
我在实际项目里,会把每个项目的 skill 沉淀成固定文件,包含项目路径、构建命令、测试命令、代码风格约定等。这样换个新 agent 后端,或者模型版本升级,工作流也能快速恢复。DeepSeek 对指令的理解能力很强,只要你把 skill 写得足够结构化,它的表现会非常稳。
6.3 接上 Playwright:让 agent 自己操作网页
编码 agent 不只能操作终端和文件,还能操作浏览器。这就要说到 harness + Playwright 的组合。
接上 Playwright 后,agent 拥有了“打开页面、点击按钮、读取页面内容、截图、检查 console 报错”的能力。这对调试前端问题特别有用:让 agent 自己复现路径、点击操作、对比渲染结果,最后把报错信息和代码修改方案一起给你。
我的建议是,浏览器操作在 agent 体系里尽量做得“窄”。不要指望 agent 自发完成一长串复杂页面操作,而是把每一步操作封装成明确动作:点击某个选择器、读取某个元素的文本、截图保存。这样即使碰到底层 DOM 变化,排错也有方向。
6.4 版本更新与回退:v0.1.5-rc.2 的教训
用任何 harness 类工具,一个绕不开的话题就是版本。这类工具迭代极快,可能昨天的配置今天就不兼容了。我真实遇到过的情况是:升级完 harness,多智能体对话直接挂掉,日志里全是重试和超时。后来回退到之前的 v0.1.5-rc.2 版本,才恢复稳定。
血的教训是,在生产环境里不要追新版本,尤其是 rc 版本。我现在的习惯是:每个项目固定锁定 harness 版本,升级前先在测试环境跑一遍完整工作流;需要回退时,用包管理器的历史版本号直接定位,而不是靠记忆恢复配置。
这里也提醒一句:配置文件和版本强相关。回退版本后,旧的配置文件也一并回退,因为新版生成的项目配置在旧版本上大概率不兼容。
7. 高频报错与排查清单
7.1 “messages tool calls need immediate results”
这个错误名看着很专业,其实核心意思很简单:模型返回了工具调用请求,但你所在的执行环境没有立刻执行工具并返回结果,导致流程卡住。常见诱发原因有三个:
- 工具执行被异步化了,结果返回太慢,消息顺序错乱。
- 一个请求里包含多个 tool_calls,但调用方只处理了第一个,其余被搁置。
- 把 pending 状态的 tool_calls 直接丢回到下一轮多轮对话里,上下文对不上。
排查思路很直接:打开调用日志,看模型返回的 tool_calls 列表,确认是否全部被处理;再确认工具结果是否以正确角色和顺序追加进了 messages。
注意:遇到 “本轮运行失败” 时,第一件事不是改 prompt,而是检查工具执行段。十次里有八次,问题都出在一轮循环里 tool_calls 没有完整闭环。
7.2 “request extension preparation failed”
这个报错更多出现在本地部署或长上下文场景里,意思是“请求扩展预处理失败”。它往往发生在消息历史很长、你试图扩展上下文窗口,但服务端预处理时发现参数超界或格式不一致。
常见的修复方向:
- 检查
--max-model-len是否小于当前消息的实际 token 总量。 - 检查 messages 里是否有异常的空 role 或者 content 字段。
- 减少历史轮数,把早期工具结果压缩成摘要后再保留。
这类错误的排查,我强烈建议先看服务端日志而不是客户端报错。客户端报错往往只给一个笼统提示,服务端日志里才会有真正的参数细节。
7.3 对话达到上限之后的延续方案
官方网页版对话有轮次或长度上限,这是很多人的痛点。尤其是写长文档、长代码时,聊到一半被截断,体验很割裂。
我的方案一般是组合拳:
- 先用导出功能把当前对话导出,保留关键上下文。
- 整理出一份“要点摘要”,包括当前目标、已完成步骤、剩余问题。
- 新开一个对话,把摘要和核心约束注入为初始 system prompt,继续往下走。
如果你走 API 路线,就不存在这个限制,你可以自己控制上下文的替换和压缩。这也是我建议认真尝试 API 的原因:它本质上把“对话续命”的能力交到了你手里。
7.4 导出对话、整理沉淀
DeepSeek 支持导出对话记录,很多人没在意这个功能,但对 agent 工作流来说,导出的两条用途非常实际:
一是复盘。导出的 Markdown 文件可以直接搜索,查找某次任务是怎么拆解的、哪一步出了问题、哪个 prompt 效果最好。
二是做“记忆注入”。把导出记录里的有效结论、项目约束、代码风格,整理成一个精简文档,丢给新对话作为上下文。这样一来,即使官方对话达到上限,你也等于把之前的成果无缝转移到了新会话。
8. 我沉淀下来的几条习惯
最后分享几条我在实战中反复体会到的经验,不成体系,但很管用。
第一,Agent 项目里最重要的不是你选了哪个模型,而是你有没有把“工具调用闭环”这件事做好。很多失败场景,换更强的模型也救不回来,因为问题出在消息时序和上下文管理上。
第二,不要把多个工具塞在一个 agent 里。我现在的习惯是拆:一个 agent 只负责一种工具类型,代码执行、文件操作、浏览器操作分给不同角色,再由调度层决定谁上场。这让每个 agent 的 prompt 可以写得更专注,DeepSeek 的表现也更稳定。
第三,任何 harness 项目都要锁版本。能用 lock 文件锁住就锁住,能用固定 tag 就固定 tag。不要信任“向后兼容”这四个字,在快速迭代的工具生态里,这基本是一句空话。
第四,先把 Cost 监控做好,再谈优化。我至少见过三个项目,因为没统计 token 消耗,跑了一个月才发现成本是预期的三倍。接 API 的第一天就把 usage 日志打出来,这事真的不亏。
最后再分享一个小技巧:本地部署和云端 API 可以同时用。让本地小模型处理格式化、补全、简单问答这类高频操作,让云端大模型处理复杂推理和规划。这样既控制成本,又保住了复杂任务的下限。这个混合架构,是我目前最推荐的 DeepSeek coding agent 落地姿势。