第二章:原始请求

大多数教程会告诉你运行 pip install anthropic。但我们不会这样做。

SDK 隐藏了真相。它们增加了抽象层,使“Hello World“变得容易,却让“Error 400“的调试变成噩梦。当你学习 SDK 时,你只是在学习 SDK。但当你学习原始 HTTP 调用时,你学习的是所有 SDK 底层的协议。

我们将只使用 requests 库向 Claude 发送消息。Claude 是 Anthropic 的旗舰 LLM——是最擅长编程任务的模型之一。

获取 API 密钥

要与 Claude 对话,你需要一个 API 密钥。这是一串字符,就像你的信用卡一样重要。

  1. 访问 Anthropic 控制台。1
  2. 注册并添加支付方式(最低充值 5 美元)。
  3. 创建一个新的 API 密钥并将其命名为 nanocode
  4. 复制该密钥(以 sk-ant-... 开头)。
An icon of a warning1

警告: 要像对待密码一样保护这个密钥。任何拥有它的人都可以消费你的账户余额。

密钥保管(.env)

我们需要一个安全的地方来存储这个密钥。我们决不能直接将密钥放在代码中。

在项目根目录创建一个名为 .env 的文件:

1 touch .env

打开并粘贴您的密钥:

1 ANTHROPIC_API_KEY=sk-ant-api03-...

我们在第 1 章安装 python-dotenv 就是为了这个目的——它读取 .env 并将值加载到 os.environ 中。

请求的解析

要与 LLM 对话,我们需要发送 HTTP POST 请求到:

https://api.anthropic.com/v1/messages

这个请求需要三个要素:请求头中的身份验证信息(你的 API 密钥)、请求体中的配置信息(使用哪个模型,多少令牌),以及消息本身。

请求头

Anthropic 需要三个请求头:

请求头 用途
x-api-key 你的密钥 身份验证
anthropic-version 2023-06-01 API 版本
content-type application/json 格式

负载

“Messages API” 需要一个消息字典列表:

1 "messages": [
2     {"role": "user", "content": "Hello, world!"}
3 ]

每条消息都有一个 role(可以是 "user""assistant")和 content(文本内容)。

代码

创建一个名为 test_api.py 的文件。这是一个“冒烟测试“,用于证明我们的连接是否正常工作。我们稍后会删除它。

上下文: 我们正在编写线性过程式代码。不使用函数,不使用类。我们想要直接看到底层实现。

代码:

 1 import os
 2 import requests
 3 import json
 4 from dotenv import load_dotenv
 5 
 6 # 1. Load the vault
 7 load_dotenv()
 8 api_key = os.getenv("ANTHROPIC_API_KEY")
 9 
10 # Basic check so we don't crash with a confusing "NoneType" error later
11 if not api_key:
12     print("Error: ANTHROPIC_API_KEY not found in .env")
13     exit(1)
14 
15 # 2. Define the target
16 url = "https://api.anthropic.com/v1/messages"
17 
18 # 3. Authenticate
19 headers = {
20     "x-api-key": api_key,
21     "anthropic-version": "2023-06-01",
22     "content-type": "application/json"
23 }
24 
25 # 4. Construct the payload
26 payload = {
27     "model": "claude-sonnet-4-6",
28     "max_tokens": 4096,
29     "messages": [
30         {"role": "user", "content": "Hello, are you ready to code?"}
31     ]
32 }
33 
34 # 5. Fire! (No safety net)
35 print("📡 Sending request to Claude...")
36 response = requests.post(url, headers=headers, json=payload, timeout=120)
37 
38 # 6. Inspect the raw result
39 print(f"Status: {response.status_code}")
40 
41 if response.status_code == 200:
42     # Success: Print the beautiful JSON
43     print("Response:")
44     print(json.dumps(response.json(), indent=2))
45 else:
46     # Failure: Print the ugly raw text so we can debug
47     print("Error:", response.text)

代码详解:

  • 第 7 行: load_dotenv() 定位并加载 .env 文件中的变量到 os.environ
  • 第 8 行: 我们获取 API 密钥。切勿将其硬编码。
  • 第 11-13 行: 基础健全性检查。如果没有这个检查,缺失的密钥会在请求头字典中导致令人困惑的 NoneType 错误。
  • 第 21 行: anthropic-version 请求头是必需的。如果省略,API 会拒绝请求。
  • 第 27 行: claude-sonnet-4-6 指定我们要使用的模型。
  • 第 28 行: max_tokens 是必需的。它限制响应长度并防止成本失控。
  • 第 36 行: 我们发送请求时设置 2 分钟超时。这里没有使用 try/except——如果网络断开,就让 Python 崩溃。你需要看到具体在哪里失败。
  • 第 41-47 行: 检查状态码。200 表示成功(格式化打印 JSON)。其他任何状态码都会打印原始错误文本以便调试。

运行程序

1 python test_api.py

如果一切正常,你应该会看到:

Status: 200
Response:
{
  "id": "msg_01...",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Hello! Yes, I'm ready to code..."
    }
  ],
  "stop_reason": "end_turn",
  "usage": {
    "input_tokens": 15,
    "output_tokens": 81
  }
}

故障排除

错误 原因 解决方法
401 Unauthorized API 密钥错误 检查 .env 是否正确加载。打印 os.environ.get("ANTHROPIC_API_KEY") 进行验证。
400 Bad Request JSON 格式错误 是否忘记设置 max_tokensmessages 是否为列表?
429 Rate Limit 请求过多或额度用尽 等待一段时间,或为账户充值。

清理工作

我们已经证实可以与 brain 进行通信。删除 test_api.py——我们不再需要它了。

An icon of a info-circle1

补充说明: 这个 test_api.py 是一次性代码——仅用于进行一次性的冒烟测试。真正的自动化测试(使用 FakeBrain 和 pytest)将在第 3 章介绍。在验证 API 连接正常工作后,你应该始终删除这个文件。

An icon of a info-circle1

补充说明: 要监控你的支出,请查看 Anthropic Console 中的使用量标签页。截至 2026 年初,使用 Claude Sonnet 进行 20-30 次交互的典型编程会话费用在 0.10-0.50 美元之间。响应 JSON 底部的 usage 字段显示确切的令牌计数——你可以通过程序记录这些数据来追踪成本。

总结

这就是原始的 API 调用:包括请求头、JSON 负载和响应解析。在你和底层之间没有任何抽象。当出现问题时(这种情况一定会发生),你可以准确知道是哪一层出了问题,因为只有一层。

一个问题是:Claude 会完全失忆。每个请求都是一个全新的开始。我们将通过在每次交互时重放整个对话历史来模拟记忆功能。


  1. https://console.anthropic.com/settings/keys↩︎