← 博客

DeepSeek V4 API Python:流式响应的最少代码示例

DeepSeek V4的即插即用Python代码。聊天补全、代码生成和流式响应处理。

By Dora 3 min read
DeepSeek V4 API Python:流式响应的最少代码示例

大家好!我是Dora。一切始于一个小烦恼:我不停地在项目之间复制相同的聊天补全样板代码,像换标签一样替换基础URL和模型名称。这不算难事,只是那种给你一天增添摩擦的活儿。我看到DeepSeek出现的次数足够多,引起了我的好奇,所以在2026年1月下旬花了几个早晨,把他们的”V4” API接入我的Python技术栈,看看实际用起来感觉如何。

在 WaveSpeedAI 上即刻可用 — 按 token 透明计费,OpenAI 兼容端点。 DeepSeek V3.2 API → · DeepSeek R1 API → · 打开 Playground →

我不是在追基准测试。我想知道的是:客户端会不会碍事,能不能可靠地进行流式传输,错误是否以容易理解的方式抛出?以下是我尝试的内容、绊住我的地方,以及默默运行良好的部分。开始吧!

环境设置

依赖项

我在macOS上用Python 3.11保持了简单的设置。用标准库也能完成,但三个小包让生活更轻松:

  • requests(简单直接的HTTP:大多数场景够用了)
  • httpx(异步请求和表现良好的超时控制)
  • python-dotenv(这样我就不用到处粘贴密钥了) 如果你计划用Server-Sent Events进行流式传输,可以用requests自己解析行(我就是这么做的),或者引入一个辅助库比如sseclient-py。我坚持用requests,活动部件更少。

安装

pip install requests httpx python-dotenv

我还为每个项目创建了一个最小的虚拟环境。这是老生常谈的建议,但三个月后重新回来时它能帮你避免依赖漂移的痛苦。

API密钥配置

我将密钥存储在环境变量中。没什么花哨的:

# .env
DEEPSEEK_API_KEY=your_key_here

然后在Python中:

from dotenv import load_dotenv
import os

load_dotenv()

API_KEY = os.getenv("DEEPSEEK_API_KEY")

if not API_KEY:
    raise RuntimeError("Missing DEEPSEEK_API_KEY")

设置中的两个小提示:

  • 基础URL和模型名称比你想象的更新更频繁。我在每次运行前都会查看官方DeepSeek API文档来确认路径和可用模型。
  • 我保持超时设置明确。这是个好习惯,一旦遇到速率限制或网络波动就会见效。

基本聊天请求

如果你在别处用过聊天补全,这个思路会很熟悉。DeepSeek提供了一个带有 messages=[{"role": "...", "content": "..."}] 的聊天端点。这很方便,因为我不需要重新构造我的提示词。 以下是我用requests发出的最简请求。模型名称因账户和地区而异,在我的测试中看到了deepseek-chat和deepseek-reasoner这样的引用。如果你的文档提到了”V4”模型字符串,就用那个。否则,选择你控制台中列出的最接近的通用聊天模型。

import os
import requests

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

payload = {
    "model": "deepseek-chat", # check docs/console for the exact model
    "messages": [
        {"role": "system", "content": "You are a concise assistant."},
        {"role": "user", "content": "Give me two bullet points on the value of clear commit messages."}
    ],
    "temperature": 0.3,
    "max_tokens": 200
}

resp = requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json=payload,
    timeout=30
)

resp.raise_for_status()

data = resp.json()
content = data["choices"][0]["message"]["content"]
print(content)

实践笔记

  • 第一次运行波澜不惊(这令人安心)。结构与我预期的一致,迁移一个小型提示词库很快就完成了。
  • 我保持低温度以获得可重复的答案。这听起来很明显,但排查问题时我还是会忘。
  • 如果你需要确定性运行,也可以固定top_p和seed(如果API支持的话)。当文档没有说明时,我默认假设是非确定性的。

如果你在比较不同供应商,这里的优势是低摩擦。缺点是差异隐藏在细节中——错误响应体、token计数和流式传输格式。这些细节决定了你的集成是感觉稳固还是恼人。

代码生成示例

我不会让模型写完整的模块,那会变成一个清理工程。但对于小的辅助函数,比如”解析这个时间戳格式”或”用占位符起草SQL”,还挺方便的。 我使用了一个狭窄的提示词、一个明确的约定和较小的输出限制。这让模型不会跑偏,也让diff容易审查。

import requests, os

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

messages = [
    {"role": "system", "content": (
        "You generate small, safe Python helpers. "
        "Return only code inside one block."
    )},
    {"role": "user", "content": (
        "Write a Python function `parse_yyyymmdd` that takes a string like '2026-01-31' "
        "and returns a datetime.date. If invalid, return None. No external deps."
    )}
]

resp = requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "model": "deepseek-chat", # or your V4-capable model
        "messages": messages,
        "temperature": 0,
        "max_tokens": 250
    },
    timeout=30
)

resp.raise_for_status()
code = resp.json()["choices"][0]["message"]["content"]
print(code)

实践中有帮助的是:

  • 我总是告诉它只返回代码。如果跳过这步,我会收到不需要的总结语句。
  • 温度设为0减少了琐碎的修改。
  • 我无论如何都会通读一遍逻辑。在我的运行中,它处理了ValueError,但我仍然为空白字符添加了额外的测试。现在多花两分钟,以后能省下几个小时的意外。

第一次用并没有节省时间。写了三四个小辅助函数之后,我注意到它减少了脑力消耗:更少的标签页切换,更少的”strptime的确切代码怎么写来着?“时刻。对我来说这就够了。

流式响应

对于任何可能变长的提示词,我都喜欢用流式传输。它让我在答案偏离时可以提前中止,也让长响应不那么令人焦虑。 在我的测试中,DeepSeek的流式传输使用了常见的模式:设置 stream=true 并读取数据行直到 [DONE]。我不需要特殊的客户端,requests 配合 iter_lines 就够了。

import os, json, requests

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

payload = {
    "model": "deepseek-chat",
    "messages": [
        {"role": "system", "content": "Be brief."},
        {"role": "user", "content": "Summarize this: Streaming keeps the UI responsive and lets me stop early."}
    ],
    "stream": True,
    "temperature": 0.2,
}

with requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json=payload,
    stream=True,
    timeout=60
) as r:
    r.raise_for_status()
    for line in r.iter_lines(decode_unicode=True):
        if not line:
            continue
        if line.startswith("data: "):
            chunk = line[len("data: "):]
            if chunk == "[DONE]":
                break
            try:
                obj = json.loads(chunk)
                delta = obj["choices"][0]["delta"].get("content", "")
                if delta:
                    print(delta, end="", flush=True)
            except json.JSONDecodeError:
                # I keep a small log when this happens: usually network blips
                pass

print()

两个我喜欢的小行为:

  • 早期token到达很快(干净连接上一两秒钟)。不算科学测量,但当我把它接入CLI工具时足以感觉流畅。
  • [DONE] 标记可靠地出现。这听起来微不足道,直到它不出现为止——缺失的终止符会让UI挂起。

如果你需要流式传输到Web应用,我建议在中间放一个薄的服务端层来规范化事件。多一个步骤,但能让你的前端保持简洁。

Server-Sent Events

在底层,你实际上是在读取Server-Sent Events。如果你更喜欢用辅助库,sseclient-py 可以用,但只要你防范了不完整的行和超时,自己处理也完全没问题。DeepSeek API文档上关于流式传输的页面足以让你顺利运行起来。

错误处理

我遇到的大多数错误都是可预见的:缺少密钥、错误的模型名称,或者当我限制网络模拟旅途中的Wi-Fi时出现超时。 一个我反复使用的小模式:

import httpx, time, os

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

RETRIABLE = {408, 409, 425, 429, 500, 502, 503, 504}

async def chat_once(client, messages):
    resp = await client.post(
        BASE_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "model": "deepseek-chat",
            "messages": messages,
            "temperature": 0.2,
            "max_tokens": 300,
        },
        timeout=30,
    )
    if resp.status_code == 401:
        raise RuntimeError("Unauthorized. Check DEEPSEEK_API_KEY and account access.")
    if resp.status_code == 404:
        raise RuntimeError("Endpoint or model not found. Confirm model name in console/docs.")
    if resp.status_code in RETRIABLE:
        raise RuntimeError(f"Retryable status: {resp.status_code}")
    resp.raise_for_status()
    return resp.json()

async def chat_with_retries(messages, attempts=4):
    backoff = 0.5
    async with httpx.AsyncClient() as client:
        for i in range(attempts):
            try:
                return await chat_once(client, messages)
            except RuntimeError as e:
                msg = str(e)
                if "Retryable status" in msg and i < attempts - 1:
                    time.sleep(backoff)
                    backoff *= 2
                    continue
                raise

一些实际注意事项:

  • 速率限制:并行测试时我收到了429。指数退避有帮助,但我还加了小幅抖动(随机50-150ms)来避免惊群效应。
  • 超时卫生:我为快速检查设置了较短的连接/读取超时(5-10秒),为大型提示词设置了较长的超时。超时不该全都默认30秒:那会掩盖问题。
  • 错误响应体:当请求失败时,JSON响应体包含一条我可以记录到日志的消息。我仍然把它包装在自己的异常中,这样我能控制到达UI的内容。

如果你的代码库已经采用OpenAI风格的架构,这就很好处理:相同的消息格式,略有不同的细节。关键是对模型名称要严格,并在非2xx响应时记录完整的响应体,这样你就不用猜了。 从文档角度来说,我依赖官方DeepSeek API文档来获取参数名称和流式传输格式。每当供应商使用熟悉的端点时,很容易假设是一样的。我已经学会了先查文档,少在客户端之间复制粘贴。

谁可能会喜欢这个

  • 如果你已经有一个Python聊天补全的封装,迁移路径很平缓。
  • 如果你关心流式传输和简单的重试,它的行为是可预测的。
  • 如果你需要非常具体的功能(函数调用模式、推理token或批量作业),你会想仔细阅读文档,并用一个小任务先做原型验证再正式投入。

我没有尝试在这里编排长的、多步骤的AI代理。我专注于小的、日常使用的提示词——那种能减少摩擦的类型。这正是DeepSeek V4 API配合Python感觉足够稳定、值得保留的地方。