2026-04-15

在 Node.js 中使用 DeepSeek V4:TypeScript、流式、tool use

DeepSeek V4 兼容 OpenAI 协议,所以在 Node.js 里直接用 openai npm 包就能接入。本教程用 TypeScript 走完从安装、第一次调用、流式、tool use 到 Next.js 路由的全链路,并给出实战中的 tokens 成本控制建议。

1. 安装与环境配置

openai 官方包完全支持通过 baseURL 指向 DeepSeek。API Key 放在 .env,不要提交到 Git,更不要写进前端包里。

npm install openai zod
echo "DEEPSEEK_API_KEY=sk-..." >> .env.local

2. TypeScript 的第一次调用

实例化客户端只需要一次,整个应用复用。baseURL 指向 DeepSeek endpoint,model 用 deepseek-chat。

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function ask(question: string): Promise<string> {
  const res = await client.chat.completions.create({
    model: "deepseek-chat",
    messages: [
      { role: "system", content: "You are a concise technical assistant." },
      { role: "user", content: question },
    ],
    temperature: 0.2,
  });
  return res.choices[0].message.content ?? "";
}

3. Next.js route handler 流式转发

前端要看到 tokens 边生成边显示,最简单的做法是在 Next.js route handler 里把 OpenAI SDK 的 async iterable 转成 ReadableStream,直接返回给浏览器。

// app/api/chat/route.ts
import OpenAI from "openai";

export const runtime = "nodejs";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = await client.chat.completions.create({
    model: "deepseek-chat",
    messages,
    stream: true,
  });

  const encoder = new TextEncoder();
  const body = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const delta = chunk.choices[0]?.delta?.content ?? "";
        if (delta) controller.enqueue(encoder.encode(delta));
      }
      controller.close();
    },
  });

  return new Response(body, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

4. 用 Zod 校验 function calling 参数

V4 的 tool use 足够稳定,可以跑真实 agent。但模型偶尔会在参数里出错,所以每个工具都应该先用 Zod schema 校验再执行,别信任 JSON.parse 出来的对象。

import { z } from "zod";

const GetWeatherArgs = z.object({ city: z.string().min(1) });

const tools = [{
  type: "function" as const,
  function: {
    name: "get_weather",
    description: "Get the current weather for a city",
    parameters: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
}];

const res = await client.chat.completions.create({
  model: "deepseek-chat",
  messages: [{ role: "user", content: "Weather in Tokyo?" }],
  tools,
});

const call = res.choices[0].message.tool_calls?.[0];
if (call?.function.name === "get_weather") {
  const args = GetWeatherArgs.parse(JSON.parse(call.function.arguments));
  // … execute tool and feed result back as a { role: "tool" } message …
}

5. Node.js 场景下的成本控制

输入与输出 tokens 分别计费,输出大约是输入的 2 倍。system prompt 和历史都是输入,长对话如果不摘要,费用会迅速失控。

实战建议:给 maxTokens 设上限、对旧对话做摘要、retrieval chunk 做缓存。流量大的团队建议在 /pricing 走官方折扣 Key,进一步压低单价。

FAQ

能直接在浏览器里调用吗?

不能。API Key 不能暴露到前端,所有调用都走 Node.js server / serverless / Next.js route handler 代理。

Bun、Deno、Cloudflare Workers 支持吗?

支持。openai SDK 提供 ESM 构建,运行时只要有 fetch 就能用。长连接场景优先选 Node.js。

能配合 LangChain、LlamaIndex、Vercel AI SDK 吗?

可以。凡是支持 OpenAI 兼容 baseURL 的库都能指向 DeepSeek。

怎么买更便宜的 API Key?

/pricing 有官方 DeepSeek 折扣 Key,同接口、同 context window、更低单价。

如果你已经在跑 Node.js 或 Next.js,把 OpenAI 换成 DeepSeek V4 只是两行改动:baseURL 和 model。剩下的 prompt、工具、重试逻辑都能原样复用,tokens 费用直接打到 1/5。