📌 内容摘要

  • 使用 Anthropic 官方 Node.js SDK,支持 JavaScript 和 TypeScript,5行代码完成第一次调用。
  • 覆盖7个核心场景:基础调用、System Prompt、多轮对话、流式输出、并发请求、Express 集成、错误处理。
  • 所有示例提供 TypeScript 和 JavaScript 双版本,附完整注释。
  • 文末附 Express 流式 API 接口完整实现,可直接用于构建聊天后端。

一、环境准备

Node.js 版本要求

Anthropic Node.js SDK 要求 Node.js 18 及以上版本(推荐 20 LTS)。

node --version   # 确认版本 ≥ 18

安装 SDK

# npm
npm install @anthropic-ai/sdk

# pnpm
pnpm add @anthropic-ai/sdk

# yarn
yarn add @anthropic-ai/sdk

TypeScript 项目额外配置

npm install -D typescript @types/node ts-node

# tsconfig.json 关键配置
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true
  }
}

配置 API Key

# .env 文件
ANTHROPIC_API_KEY=sk-ant-api03-你的key

# 安装 dotenv
npm install dotenv

二、基础调用

TypeScript 版本

import Anthropic from "@anthropic-ai/sdk";
import "dotenv/config";

const client = new Anthropic();
// 默认自动读取环境变量 ANTHROPIC_API_KEY
// 也可以显式传入:new Anthropic({ apiKey: "sk-ant-..." })

async function main() {
  const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [
      { role: "user", content: "你好,请用一句话自我介绍" }
    ],
  });

  // 获取回复文本
  const text = message.content[0];
  if (text.type === "text") {
    console.log(text.text);
  }
}

main();

JavaScript 版本(ESM)

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "你好,请用一句话自我介绍" }
  ],
});

console.log(message.content[0].text);

JavaScript 版本(CommonJS)

const Anthropic = require("@anthropic-ai/sdk").default;

const client = new Anthropic();

async function main() {
  const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [
      { role: "user", content: "你好,请用一句话自我介绍" }
    ],
  });

  console.log(message.content[0].text);
}

main();

响应对象说明

console.log(message.id);                     // 请求唯一 ID
console.log(message.model);                  // 实际使用的模型
console.log(message.role);                   // 始终是 "assistant"
console.log(message.content[0].text);        // 回复文本
console.log(message.stop_reason);            // "end_turn" | "max_tokens"
console.log(message.usage.input_tokens);     // 输入消耗 token 数
console.log(message.usage.output_tokens);    // 输出消耗 token 数

三、System Prompt 设置

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 2048,
  system: `你是一名资深 Node.js 工程师,专注于性能优化和最佳实践。
回答规则:
- 优先给出可运行的代码示例
- 代码使用 TypeScript,加上类型注解
- 指出潜在的内存泄漏或性能问题
- 回答简洁,直接切入要点`,
  messages: [
    { role: "user", content: "如何正确关闭 Node.js 应用并释放资源?" }
  ],
});

console.log(message.content[0].text);

四、多轮对话

Claude API 是无状态的,每次请求需传入完整对话历史:

import Anthropic from "@anthropic-ai/sdk";

type Message = { role: "user" | "assistant"; content: string };

class ChatSession {
  private client: Anthropic;
  private history: Message[] = [];
  private system: string;

  constructor(system = "") {
    this.client = new Anthropic();
    this.system = system;
  }

  async chat(userInput: string): Promise {
    // 追加用户消息
    this.history.push({ role: "user", content: userInput });

    const response = await this.client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 2048,
      system: this.system,
      messages: this.history,
    });

    const assistantReply =
      response.content[0].type === "text"
        ? response.content[0].text
        : "";

    // 追加 AI 回复
    this.history.push({ role: "assistant", content: assistantReply });

    return assistantReply;
  }

  getHistory(): Message[] {
    return [...this.history];
  }

  clear(): void {
    this.history = [];
  }
}

// 使用示例
const session = new ChatSession(
  "你是一个友善的助手,记住用户说过的所有信息。"
);

console.log(await session.chat("我叫小明,是一名 Node.js 开发者"));
console.log(await session.chat("我主要做什么方向的开发?"));
console.log(await session.chat("给我推荐一个适合我的开源项目"));

五、流式输出(Streaming)

基础流式输出

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// 方式一:stream 辅助方法(推荐)
const stream = await client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 2048,
  messages: [
    { role: "user", content: "写一篇300字关于秋天的散文" }
  ],
});

// 实时打印每个文字片段
for await (const chunk of stream) {
  if (
    chunk.type === "content_block_delta" &&
    chunk.delta.type === "text_delta"
  ) {
    process.stdout.write(chunk.delta.text);
  }
}

console.log(); // 换行

// 获取完整响应统计
const finalMessage = await stream.finalMessage();
console.log(`\n输入 tokens: ${finalMessage.usage.input_tokens}`);
console.log(`输出 tokens: ${finalMessage.usage.output_tokens}`);

使用事件监听器

const stream = client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "写一首短诗" }],
});

stream
  .on("text", (text) => {
    process.stdout.write(text);
  })
  .on("message", (message) => {
    console.log("\n[完成]", message.stop_reason);
  })
  .on("error", (error) => {
    console.error("流式输出错误:", error);
  });

await stream.finalMessage();

六、Express 集成:构建聊天 API

这是最常见的生产场景——把 Claude 包装成 HTTP 接口供前端调用:

npm install express cors
npm install -D @types/express @types/cors
import express from "express";
import cors from "cors";
import Anthropic from "@anthropic-ai/sdk";

const app = express();
const client = new Anthropic();

app.use(cors());
app.use(express.json());

type ChatMessage = { role: "user" | "assistant"; content: string };

// ── 普通接口(等待完整响应)──────────────────────
app.post("/api/chat", async (req, res) => {
  const { messages, system } = req.body as {
    messages: ChatMessage[];
    system?: string;
  };

  try {
    const response = await client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 2048,
      system: system ?? "你是一个有帮助的 AI 助手。",
      messages,
    });

    res.json({
      content: response.content[0].type === "text"
        ? response.content[0].text
        : "",
      usage: response.usage,
    });
  } catch (error) {
    if (error instanceof Anthropic.APIError) {
      res.status(error.status).json({ error: error.message });
    } else {
      res.status(500).json({ error: "Internal server error" });
    }
  }
});

// ── 流式接口(SSE,逐字返回)────────────────────
app.post("/api/chat/stream", async (req, res) => {
  const { messages, system } = req.body as {
    messages: ChatMessage[];
    system?: string;
  };

  // 设置 SSE 响应头
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  try {
    const stream = client.messages.stream({
      model: "claude-sonnet-4-6",
      max_tokens: 2048,
      system: system ?? "你是一个有帮助的 AI 助手。",
      messages,
    });

    // 逐块发送给前端
    stream.on("text", (text) => {
      res.write(`data: ${JSON.stringify({ text })}\n\n`);
    });

    const final = await stream.finalMessage();

    // 发送结束信号和用量统计
    res.write(
      `data: ${JSON.stringify({
        done: true,
        usage: final.usage,
      })}\n\n`
    );
    res.end();
  } catch (error) {
    res.write(`data: ${JSON.stringify({ error: "Stream failed" })}\n\n`);
    res.end();
  }
});

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

前端调用流式接口(示例)

async function streamChat(userMessage) {
  const response = await fetch("/api/chat/stream", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      messages: [{ role: "user", content: userMessage }],
    }),
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const data = JSON.parse(line.slice(6));
        if (data.text) {
          // 实时追加到页面
          document.getElementById("output").textContent += data.text;
        }
        if (data.done) {
          console.log("完成,用量:", data.usage);
        }
      }
    }
  }
}

七、并发请求

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// 控制并发数量,避免触发速率限制
async function batchProcess(
  prompts: string[],
  concurrency = 5
): Promise {
  const results: string[] = new Array(prompts.length);

  // 分批处理
  for (let i = 0; i < prompts.length; i += concurrency) {
    const batch = prompts.slice(i, i + concurrency);

    const batchResults = await Promise.all(
      batch.map(async (prompt, idx) => {
        const response = await client.messages.create({
          model: "claude-haiku-4-5-20251001", // 批量任务用 Haiku 省钱
          max_tokens: 512,
          messages: [{ role: "user", content: prompt }],
        });
        return {
          index: i + idx,
          text:
            response.content[0].type === "text"
              ? response.content[0].text
              : "",
        };
      })
    );

    for (const { index, text } of batchResults) {
      results[index] = text;
    }

    // 批次间稍作延迟,避免频率限制
    if (i + concurrency < prompts.length) {
      await new Promise((r) => setTimeout(r, 200));
    }
  }

  return results;
}

// 使用示例
const questions = [
  "Python 和 JavaScript 的主要区别是什么?",
  "什么是 REST API?",
  "解释一下 async/await",
  "什么是 Docker?",
  "Git rebase 和 merge 的区别?",
];

const answers = await batchProcess(questions, 3);
answers.forEach((a, i) => {
  console.log(`Q${i + 1}: ${questions[i]}`);
  console.log(`A: ${a.slice(0, 80)}...\n`);
});

八、错误处理

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

async function callWithRetry(
  messages: Anthropic.MessageParam[],
  maxRetries = 3
): Promise {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await client.messages.create({
        model: "claude-sonnet-4-6",
        max_tokens: 1024,
        messages,
      });

      return response.content[0].type === "text"
        ? response.content[0].text
        : "";

    } catch (error) {
      if (error instanceof Anthropic.AuthenticationError) {
        // API Key 错误,不重试
        throw new Error("API Key 无效,请检查环境变量 ANTHROPIC_API_KEY");
      }

      if (error instanceof Anthropic.RateLimitError) {
        // 频率限制,指数退避后重试
        const waitMs = Math.pow(2, attempt) * 1000;
        console.warn(`频率限制,${waitMs / 1000}秒后重试(第${attempt + 1}次)`);
        await new Promise((r) => setTimeout(r, waitMs));
        continue;
      }

      if (error instanceof Anthropic.APIStatusError) {
        if (error.status === 529) {
          // 服务器过载
          const waitMs = Math.pow(2, attempt) * 1000;
          console.warn(`服务过载,${waitMs / 1000}秒后重试`);
          await new Promise((r) => setTimeout(r, waitMs));
          continue;
        }
        // 其他 API 错误
        throw new Error(`API 错误 ${error.status}: ${error.message}`);
      }

      if (error instanceof Anthropic.APIConnectionError) {
        if (attempt < maxRetries - 1) {
          console.warn("网络连接失败,重试中...");
          await new Promise((r) => setTimeout(r, 1000));
          continue;
        }
        throw new Error("网络连接持续失败,请检查网络环境");
      }

      throw error;
    }
  }

  throw new Error(`重试 ${maxRetries} 次后仍然失败`);
}

常见问题

Q:ES module 还是 CommonJS?
SDK 同时支持两种模式。新项目推荐使用 ESM(import 语法),在 package.json 中设置 "type": "module"。老项目使用 CommonJS 的 require 也完全兼容。

Q:Node.js 里如何使用顶层 await?
ESM 模块(.mjspackage.json 设置 "type": "module")原生支持顶层 await。CommonJS 需要包在 async function main() 里再调用。

Q:流式输出和普通输出的费用一样吗?
完全一样。流式输出只是改变了内容传输方式,不影响 token 计费。输入 token 数 × 输入单价 + 输出 token 数 × 输出单价,与是否流式无关。

Q:如何在 Vercel / Cloudflare Workers 等 Edge 环境使用?
SDK 支持 Edge Runtime。在 Vercel Edge Functions 中直接 import 即可;Cloudflare Workers 需要使用 fetch-based 传输,SDK 会自动检测环境并切换。注意 Edge 环境有执行时间限制,流式输出比等待完整响应更适合这类场景。

总结

Node.js 调用 Claude API 的核心是掌握三个场景:普通请求、流式输出、多轮对话——本文的代码示例都可以直接复制运行。实际项目中最常用的是 Express + 流式 SSE 接口这个组合,把它封装好之后,前端无论是 React 还是 Vue 都能很方便地接入。生产环境一定加上错误处理和重试机制,避免因临时网络问题导致服务中断。