简介:《DeepSeek使用指南》面向具备一定编程背景的技术人员、产品经理及希望提升客户服务自动化水平的企业管理者,系统梳理大语言模型助手在网页端、API 集成与移动设备上的使用路径。资源以 1 个 docx 文档交付,压缩包约 16KB,篇幅精炼、结构清晰,便于按网页对话、接口调用和第三方客户端配置等模块快速检索。内容覆盖官网访问、注册登录与对话发起,API Key 创建、OpenAI SDK 安装、DeepSeek API 端点调用及响应解析,也涉及借助 Chatbox 添加自定义提供方、选择 OpenAI API 兼容模式、设置上下文消息数量与温度值、验证部署并查看 tokens 用量等关键环节。已有 5986 人学习,适合初次接触 DeepSeek 的读者建立从网页对话到接口调用的完整操作框架,理解参数配置与客户端接入背后的机制,也可为技术支持、产品选型与商业创新提供可复用的实践参考。
1. 从“能对话”到“能接入”:三种形态的边界先划清
多数人第一次接触大模型是从对话框开始的:敲一句话,等几秒,回一段话,觉得够用了,就停在网页端。真要把这套能力接入自己的系统里,问题立刻变形——怎么鉴权、超时设多少、输出要不要流式、多轮上下文是自己维护还是交给服务端。DeepSeek 这份使用指南的价值,恰恰在于它把网页端、API 集成和移动端三条路径摆在一起讲,而这三条路径用的是同一套模型能力,工程约束却完全不同。写过 Python、要给现有业务加 AI 能力的开发者,需要先把 API 这条线跑通;只想快速上手、不写代码的人,网页端和移动端客户端就够;负责给团队做接入方案选型的人,则要同时看清三者的成本、可控性和失败模式。网页端是基线,API 是生产通道,移动端是终端形态,这个顺序决定了后面每一步该验证什么。
2. 网页端与 API 的分工:先有基线,再谈接入
2.1 网页端该用来干什么,很多人用反了
网页端的正确用法是建立一个“人眼可判”的基线,重点不在尝鲜。具体做法是:把你准备写进 system prompt 的那段角色设定和格式要求,原封不动贴进对话框,连续跑三到五次,观察输出格式的漂移程度。如果五次里有两次格式跑偏、多出解释性废话,说明提示词约束不够硬,这时候直接上代码,你根本分不清是模型不稳定还是代码传参有问题。
我会在网页端固定做三件事:第一,确认模型对这类任务的基本理解水平,比如摘要任务会不会自行加标题;第二,摸清输出长度的分布,用来反推max_tokens该给多少;第三,试出对格式最敏感的指令措辞,比如“只输出 JSON,不要任何解释”和“输出必须是合法 JSON”哪个更稳。这三件事在网页端做,成本几乎为零。
网页端的天花板也很明确:没有并发、没有可编程重试、无法批量、拿不到原始响应结构、接不进业务流程。所以它的定位是验证,不是承载。一旦你在网页端把提示词跑到满意的稳定度,就该把它原样搬到 API 里,用同一套 messages 结构复现。
2.2 API 接入的三个核心要素
API 这条线的第一道门槛不在代码,在参数。DeepSeek 的接口走的是 OpenAI 兼容协议,这意味着请求体结构、字段名、响应格式都按同一套规范来,你只需要改两个地方:base_url换掉,model换成对应的模型名。听起来简单,但第一次接入翻车的人八成栽在下面这三个值上。
| 参数 | 取值 | 说明 |
|---|---|---|
base_url | https://api.deepseek.com | SDK 会在此基础上拼/chat/completions,不要自己再加路径 |
api_key | 开放平台创建后生成 | 只在创建时完整展示一次,关掉页面就看不到了,必须立刻存进环境变量 |
model | deepseek-chat | 模型名写错通常不会报错,而是直接落到默认模型,输出风格不对还找不到原因 |
鉴权链路是标准的 Bearer 方式,SDK 会自动把 key 塞进请求头。所以 key 泄露的路径通常不是被中间人截获,而是被你自己硬编码进代码、提交到仓库、再被打包发出去。常见做法是全程读环境变量,代码里不出现任何明文 key。
2.3 一段能直接跑通的最小脚本
先别急着封装,先用最短的代码确认链路通不通。下面这段是整个接入的地基,后面所有花活都是在它上面加东西。
import os from openai import OpenAI # key 从环境变量读,代码里永远不出现明文 client = OpenAI( api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com", timeout=30.0, # 单次请求超时,单位秒 max_retries=2, # SDK 内置重试,只对连接类错误生效 ) response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": "你是一个严谨的技术助手,回答控制在三句话以内"}, {"role": "user", "content": "用一句话解释什么是 base_url"}, ], stream=False, # 先跑非流式,方便看完整结构 temperature=0.3, max_tokens=256, ) # 响应对象是结构化的,正文在 choices[0].message.content print(response.choices[0].message.content) # finish_reason 能告诉你这次是被截断还是正常结束 print(response.choices[0].finish_reason) # usage 是做成本核对的关键,非流式才有完整数值 print(response.usage)代码后要看的不是那一行 print,而是两个容易被忽略的字段。finish_reason的值如果是length,说明输出被max_tokens截断了,这时候你看到的“回答不完整”不是模型笨,是你把额度设小了。usage里的 prompt_tokens 和 completion_tokens 分别对应输入和输出计费,第一次跑通一定要记下来,后面做成本估算全靠这个基准值。
参数上,timeout和max_retries放在客户端初始化阶段比放在单次请求里更实用,因为绝大多数超时是网络抖动而非模型慢。temperature在 0.2 到 0.5 之间适合做结构化输出和抽取任务,超过 0.8 之后格式稳定性会明显下降,不要在被解析的字段上冒险。
3. SDK 调用细节:兼容层、流式与上下文管理
3.1 为什么 OpenAI 的 SDK 能直接打 DeepSeek
这不是巧合,是协议层面的兼容。SDK 本身只做两件事:把 Python 对象序列化成 HTTP 请求体,再把响应反序列化成对象。只要服务端的请求体结构和响应结构遵循同一套约定,SDK 就完全无所谓对面是谁。这也解释了为什么你只需要改base_url和model,业务代码一行都不用动。
但兼容是有边界的。协议一致不代表参数全集一致,OpenAI 侧的一些专有字段,比如特定格式的结构化输出约束、某些工具调用细节,在兼容实现上支持程度不一样。血泪经验是:不要假设你在别处能用的参数在这里一定生效,尤其是那些“传了不报错但也不起作用”的参数——它们最坑,因为没有任何报错提示你它被忽略了。判断方法是拿同一个请求传和不传该参数各跑几次,对比输出分布有没有变化,没变化就是没生效。
另一个隐含成本在响应结构上。不同实现对usage字段的填充时机不同,流式模式下往往拿不到完整的用量数据。如果你的系统要按调用链记账,这一点必须在设计阶段就定下来:要么放弃流式改批量,要么在流式结束后单独做一次用量查询。
3.2 流式输出:把首字延迟压下去
非流式的问题不是慢,是“用户不知道你在干活”。生成三百字可能要几秒,这段时间界面毫无反馈,体验上等同于卡死。流式解决的就是这个感知问题,它把一次完整响应拆成若干增量块,边生成边推。
stream = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "列出三条 API 调用注意事项"}], stream=True, temperature=0.4, ) collected = [] for chunk in stream: # 首块通常只带 role,delta.content 为 None,必须判空 if not chunk.choices: continue delta = chunk.choices[0].delta piece = delta.content if piece: collected.append(piece) print(piece, end="", flush=True) # flush 不加,终端会攒着一起吐 full_text = "".join(collected)这段代码有三个必须理解的点。第一,chunk.choices可能是空列表,某些实现会在流的收尾阶段推一个只带 usage 的块,不做判空会直接 IndexError。第二,delta.content可能是None而不是空字符串,用if piece判空比if piece != ""安全。第三,flush=True不是可选项,不写的话终端会缓冲,你看到的还是“一次性全出来”,白折腾。
流式还有一个副作用要提前想清楚:一旦开始推送,你就没法在中途反悔重来。如果输出格式必须严格合法(比如要被下游 JSON 解析),流式下你只能先拼完整再校验,失败了重试,用户会看到内容闪一下再变。这种场景我一般退回非流式,宁可多等两秒,也不给用户看抖动。
3.3 多轮上下文的维护责任在自己手里
API 是无状态的,这是很多人第一次做多轮对话时最容易误解的地方。服务端不记得你上一句说了什么,所谓“记得”全靠你每次把历史消息重新发一遍。
history = [{"role": "system", "content": "你是客服助手,只回答订单相关问题"}] def ask(user_input: str) -> str: history.append({"role": "user", "content": user_input}) resp = client.chat.completions.create( model="deepseek-chat", messages=history, temperature=0.3, max_tokens=512, ) answer = resp.choices[0].message.content # 必须把助手回复也塞回历史,否则下一轮模型看不到自己说过什么 history.append({"role": "assistant", "content": answer}) return answer def trim(keep_rounds: int = 6): # 保留 system + 最近 N 轮,防止历史无限膨胀 global history sys_msg = history[0] rest = history[1:] history = [sys_msg] + rest[-keep_rounds * 2:]trim这个函数看着不起眼,却是长期运行的系统里最容易出事的地方。历史越长,每次请求的输入 token 越多,成本和延迟是线性涨的。一轮对话涨得不明显,几百轮之后单次请求的输入可能上万 token,费用翻几十倍,延迟也拖到不可接受。我一般按业务定一个轮数上限,超过就裁掉最早的几轮,只保留 system 和最近的往来。裁掉的内容如果确实重要,就在裁之前做一次摘要,把摘要塞进 system 里,比直接丢弃温和。
3.4 参数怎么设才有意义
参数没有“最优值”,只有“匹配场景的值”。下面这张表是我在不同任务类型上反复调过之后留下的经验区间。
| 参数 | 抽取/格式化任务 | 创作/头脑风暴 | 说明 |
|---|---|---|---|
temperature | 0.1 ~ 0.3 | 0.7 ~ 1.0 | 越低越稳,高值换多样性 |
max_tokens | 按输出上限 + 20% 余量 | 放宽到 1024 以上 | 设太小会触发 length 截断 |
timeout | 20 ~ 30 秒 | 60 秒 | 长输出必须给足时间 |
max_retries | 2 | 1 | 重试会放大费用,非幂等场景慎用 |
有一点值得单独说:max_retries不是越多越好。SDK 的重试只针对连接失败和超时,不针对业务错误,所以它救不了参数写错的问题。而在写操作场景里,重试可能导致重复提交,这类调用我通常直接关掉重试,改为在业务层做幂等。
4. 移动端落地:官方 App 与第三方客户端的取舍
4.1 先判断你到底需要哪一种
路径选择上先看官方有没有提供现成客户端。如果有,直接装官方版本是最省事的,登录方式和网页端打通,功能也最全。但官方客户端通常不让你改 system prompt、不支持多模型切换、也没法把对话导出成结构化数据。这类需求就得走第三方客户端这条路。
第三方客户端的共同前提是:你得先有一个能用的 API Key,配置进去之后所有对话都走你自己的账户额度。这一点必须在动手前说清楚,很多人配完之后以为捡了个免费客户端,聊了两天才发现余额在掉。它省的是开发时间,不是模型费用。
选客户端时优先看两个特征:是否声明兼容 OpenAI 协议,以及是否允许自定义 API 地址。前者决定能不能接,后者决定接得干不干净。只支持固定服务商的客户端,即使界面再好看也直接跳过。
4.2 自定义提供方的配置项逐项拆解
配置过程本身不难,难在每一项填错都不一定报错。下表是这套配置里真正会影响到结果的字段。
| 配置项 | 填法 | 填错的后果 |
|---|---|---|
| API 模式 | 选 OpenAI API 兼容 | 选成别的模式,请求体结构不对,直接 400 |
| 名称 | 随便起,仅本地展示 | 无影响 |
| API 地址/路径 | 填https://api.deepseek.com | 多写或漏写/v1、多一个斜杠,404 或 401 |
| API Key | 粘贴创建好的 key | 带空格或换行,401 且报错文案很模糊 |
| 模型名 | deepseek-chat | 填错可能不报错,静默回落到其他模型 |
| 上下文消息数量 | 6 ~ 10 轮起步 | 设太大,长对话后变慢变贵 |
| 温度值 | 0.3 ~ 0.7 | 太高时客户端界面里回答会显得飘 |
操作顺序是:打开设置,找到“添加自定义提供方”,模式选兼容项,把 API 地址和 key 填进去,再选模型名,最后调上下文轮数和温度,保存。这里有个细节:部分客户端把地址和路径拆成两个输入框,此时地址填域名,路径填/v1或留空,具体看客户端提示。判断标准只有一个——保存后能不能正常对话,别去猜文档。
4.3 怎么确认真的配好了
“问它你是谁”是最不可靠的验证方式。几乎所有模型在被问到时都会自称 AI 助手,你没法从这个回答判断请求到底发到了哪个服务端。
更硬的验证有三招。第一,在客户端里设一条特征明显的 system prompt,比如“每次回答开头必须加【测试】两个字”,如果回答带了这两个字,说明 system 确实透传下去了。第二,去开放平台的用量页面看请求数有没有 +1,这是最直接的证据,能证明请求确实走的是你的账户。第三,问一个只能从特定文档里得到答案的问题,看回答是否符合该模型的表述习惯。
如果对话能通但答非所问,先查模型名,再查是不是客户端在你不知情的情况下切回了默认提供方。这类问题在界面上通常没有任何提示,只能靠上面三招反推。
5. 避坑与排查:五条踩出来的经验
5.1 401 报错,八成是 key 里混进了不可见字符
现象:配置完立刻 401,key 是从平台复制过来的,看起来完全一致。
原因:从网页复制 key 时很容易把行尾换行或前后空格带进去,这些字符在输入框里不可见,但会被原样拼进请求头,服务端判定签名不匹配。
解决:粘贴后手动全选看有没有多余空白;或者干脆用代码读环境变量,绕开手抄环节。判断是不是这个原因,最快的办法是把 key 打印长度,和平台显示的长度对一下,多一个字符就是它。
5.2 404 或路径错误,问题出在 base_url 的尾巴上
现象:同一个 key 在别处能用,这里报 404 或提示接口不存在。
原因:base_url的语义是“域名根”,SDK 会自己拼/chat/completions。有人习惯性在后面补/v1或补完整路径,结果拼出/v1/chat/completions这种不存在的地址。反过来,某些客户端要求你填带/v1的地址,两者规则不同。
解决:先确认你用的是 SDK 还是客户端,SDK 只填域名,客户端按它自己的输入框提示填。拿不准就把两种写法各试一次,看哪个不通,比翻文档快。
5.3 流式输出中文被截断或出现乱码
现象:流式下偶尔出现半个汉字,或者末尾几个字丢失。
原因:两种情况。一是编码问题,输出端没按 UTF-8 处理,多字节字符在分块边界被切开;二是你在收到结束标记前就退出了循环,最后一块没处理。
解决:确认输出端统一 UTF-8;循环里不要用异常或超时提前 break,让它自然结束;拼接时用列表收集再 join,不要用字符串+=逐次拼,后者在长输出下性能差且容易出边界问题。
5.4 聊到后面又慢又贵,历史没做裁剪
现象:刚上线时响应很快,用了一两周之后单次对话延迟明显上升,账单也在涨。
原因:messages数组只增不减,每轮都把全部历史重新发一遍,输入 token 随轮数线性增长。
解决:按业务定轮数上限,超出就裁掉最早的对话;重要信息在裁剪前做摘要放进 system。监控上可以定期打印usage.prompt_tokens的分布,一旦均值持续爬升,就是该加裁剪了。
5.5 客户端能连上但回答风格不对
现象:移动端配置完成后能正常对话,但回答明显不像预期模型,质量也不稳定。
原因:模型名填错或留空,客户端静默用了默认模型;或者配置里同时存在多个提供方,实际走的是另一个。
解决:把模型名显式填成正确值,保存后重启客户端;再按第 4 章那三招验证一次,重点看用量页面的请求数有没有增加,请求数不动说明根本没走你的配置。
6. 把调用封装成可复用小模块,以及怎么核对用量
跑通单次调用只是起点,真要放进项目里,得有统一的出口。我一般会写一个薄封装,把客户端初始化、重试策略、用量累计都收在一处,业务侧只传 messages 和参数。
import os, time, logging from openai import OpenAI log = logging.getLogger(__name__) class ChatClient: def __init__(self): self.client = OpenAI( api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com", timeout=30.0, max_retries=2, ) # 累计用量,用于和平台账单交叉核对 self.used_prompt_tokens = 0 self.used_completion_tokens = 0 def chat(self, messages, temperature=0.3, max_tokens=512): start = time.time() resp = self.client.chat.completions.create( model="deepseek-chat", messages=messages, temperature=temperature, max_tokens=max_tokens, ) cost = time.time() - start # usage 可能为空,取值前必须判空 if resp.usage: self.used_prompt_tokens += resp.usage.prompt_tokens self.used_completion_tokens += resp.usage.completion_tokens log.info("latency=%.2fs finish=%s", cost, resp.choices[0].finish_reason) return resp.choices[0].message.content这段封装真正有用的不是类结构,是那两个累计字段和那一行日志。累计字段的作用是做交叉核对:本地统计的 token 数和平台用量页面的数字如果长期对不上,说明有调用绕过了这个出口,这在多人协作的项目里几乎一定会发生,越早发现越好。日志里记finish_reason则是为了抓截断,比事后翻用户投诉快得多。
验证这套东西有没有生效,我有个固定的自检流程:造三条不同长度的输入,分别跑到正常结束和被截断两种情况,看本地累计值是否随请求数递增、finish_reason是否与预期一致、延迟是否落在合理区间。三步都过,才算这个模块可以交付。
还有个容易被忽略的点是并发下的累计精度。上面这种简单加法在多线程里会丢数,生产环境至少加个锁,或者改用原子计数。我第一次做这块时没在意,压测跑了一晚上,本地统计比平台少了将近一成,排查了半天才发现是计数竞争,跟模型一点关系没有。从那以后我每接一个新模型,都强制先跑一遍三件套:key 走环境变量校验、最小非流式请求、流式断连测试。这三步花不了十分钟,但能挡掉后面九成的低级故障。希望帮到你。
本文还有配套的精品资源,点击获取