AI TOOLS

EP01. "Claude API 基础入门:从请求到多轮对话"

首页 › AI 工具 › Claude · Developer and Platform · Claude API · EP01
约 21 分钟· #EP01#Claude#Claude API
🔒 登录后可标记已读
  • 一个 API 请求从你的 App 出发,要走「你的 server → Anthropic API → 模型处理 → 回到 server → 回到 client」这五步,中间每一步都可能是出错/需要优化的地方
  • API key 一定要放在自己的 server 端,前端代码里绝对不能出现——泄漏了就等于把额度免费送人
  • Claude 本身不存对话记录,每次请求都是独立的,要自己在代码里维护 messages 列表、每次把完整历史一起传过去,Claude 才「记得」上一句说了什么
  • temperature、system prompt、streaming、assistant prefill 这几个参数/技巧,决定了回答的语气、可预测性和输出格式,是接下来所有课程内容的地基
  • 前置知识:会写 Python,有 Anthropic 账号(还没有的话,这篇笔记第二节就是申请步骤)

重点内容


请求的五步流程

一次典型的 API 调用会经过 5 个阶段:

  1. 你的 App 发请求给你自己的 server
  2. 你的 server 再转发请求给 Anthropic API
  3. 模型处理(下面「Claude 怎么处理你的输入」细讲)
  4. Anthropic API 把结果传回你的 server
  5. 你的 server 再把结果传回 client

为什么一定要经过自己的 server

📌 三个理由:

  • API 请求需要一把密钥(API key)做身份验证
  • 如果把 key 直接写进前端代码,任何用户打开开发者工具都能看到、拿去用
  • key 泄漏后,别人能拿着它无限量发请求,账单算在你头上

正确做法:API key 只存在自己的 server(用环境变量/密钥管理服务),前端只跟自己的 server 对话,server 再代为呼叫 Anthropic API。


申请 API Key

  1. 打开 https://platform.claude.com/ ,登入 Anthropic 账号
  2. 右上角点 Get API Keys
  3. 页面右上角再点 Create Key
  4. 选 Default workspace,给这把 key 取个名字(方便日后区分是哪个项目在用),例如 "Anthropic Course"
  5. 弹窗会显示 key 本身——只会显示这一次,一定要马上复制存好

💡 如果不小心把弹窗关掉了、没复制到,唯一的办法是删掉这把 key、重新生成一把新的。


环境搭建与第一个请求

%pip install anthropic python-dotenv

.env 文件放 key(不要 commit 进 git):

ANTHROPIC_API_KEY="your-api-key-here"

初始化 client:

from dotenv import load_dotenv
load_dotenv()

from anthropic import Anthropic

client = Anthropic()
model = "claude-sonnet-4-5"

client.messages.create() 三个必填参数:

参数作用
model要用哪个 Claude 模型,例如 "claude-sonnet-4-5"
max_tokens回答长度的安全上限(不是「目标长度」,是硬顶),Claude 生成到这个数量就会被切断
messages对话历史列表,每条消息有 role("user" 或 "assistant")和 content 两个字段
message = client.messages.create(
    model=model,
    max_tokens=1000,
    messages=[
        {
            "role": "user",
            "content": "What is quantum computing? Answer in one sentence"
        }
    ]
)

拿生成的文字:

message.content[0].text

Claude 怎么处理你的输入(四个阶段)

  • Tokenization(分词):把输入文字切成更小的 token(可能是一个词、半个词、空格或符号)
  • Embedding(向量化):每个 token 转成一串数字,这串数字带着这个词可能的多种含义
  • Contextualization(语境化):Claude 根据上下文,把每个 token 的向量往「这句话里它实际的意思」修正
  • Generation(生成):模型对「下一个词可能是什么」算出一串概率,再用「概率 + 一定的随机性」选出实际输出的词,不是每次都无脑选概率最高的那个

生成会在以下任一情况停止:

  • 达到 max_tokens 上限
  • 遇到自然的结尾(end-of-sequence token)
  • 撞到你自己设定的 stop sequence

API 回应包含什么

一次回应会带回三样东西:

  • Message:生成的文字内容
  • Usage:这次请求用掉的 input/output token 数量
  • Stop Reason:为什么停止生成(达到上限?自然结束?撞到 stop sequence?)

理解这三样东西,才能设计合理的 max_tokens、正确处理各种 stop reason、系统性地排查问题。


多轮对话:自己维护 message 历史

📌 Claude 不会帮你存对话记录。每次请求都是全新的、互不相关的——如果用户接着问「再写一句」,Claude 完全不知道上一句在讲什么,除非你把完整历史一起传过去。

解法:自己在代码里维护一份完整的 messages 列表,每次请求都把它整包传过去。

def add_user_message(messages, text):
    user_message = {"role": "user", "content": text}
    messages.append(user_message)

def add_assistant_message(messages, text):
    assistant_message = {"role": "assistant", "content": text}
    messages.append(assistant_message)

def chat(messages):
    message = client.messages.create(
        model=model,
        max_tokens=1000,
        messages=messages,
    )
    return message.content[0].text

完整流程示范:

messages = []
add_user_message(messages, "Define quantum computing in one sentence")
answer = chat(messages)
add_assistant_message(messages, answer)
add_user_message(messages, "Write another sentence")
final_answer = chat(messages)

System Prompt:定调子

System prompt 用来定 Claude 的语气、风格、角色设定。举例:数学家教场景,没有 system prompt 时 Claude 会直接给完整解答;配上合适的 system prompt,Claude 会像真的家教那样一步步引导、给提示,而不是直接报答案。

system_prompt = """
You are a patient math tutor.
Do not directly answer a student's questions.
Guide them to a solution step by step.
"""

client.messages.create(
    model=model,
    messages=messages,
    max_tokens=1000,
    system=system_prompt
)

⚠️ API 不接受 system=None——要用条件判断,System prompt 没给就整个不传这个参数:

def chat(messages, system=None):
    params = {
        "model": model,
        "max_tokens": 1000,
        "messages": messages,
    }

    if system:
        params["system"] = system

    message = client.messages.create(**params)
    return message.content[0].text

Temperature:可预测 vs 有创意

temperature 是 0 到 1 之间的小数,直接影响「选下一个词」时各个候选的概率分布。

区间效果适合用在
0.0 – 0.3结果稳定、可预测,几乎每次都选概率最高的词事实性回答、写代码、数据抽取、内容审核
0.4 – 0.7平衡摘要、教育类内容、解题、有一定限制的创意写作
0.8 – 1.0输出更多变、更有创意头脑风暴、创意写作、营销文案、讲笑话
def chat(messages, system=None, temperature=1.0):
    params = {
        "model": model,
        "max_tokens": 1000,
        "messages": messages,
        "temperature": temperature
    }

    if system:
        params["system"] = system

    message = client.messages.create(**params)
    return message.content[0].text

💡 temperature 不保证每次输出都不一样——它只是改变了「拿到不同输出」的概率。


Response Streaming:边生成边显示

一般回答要 10-30 秒才能生成完,用户只能盯着 loading 转圈。Streaming 让文字一块一块地即时显示,不用等整段生成完才有反应。

Stream 事件类型:

事件含义
MessageStart新消息开始
ContentBlockStart一个内容块(文字/工具调用等)开始
ContentBlockDelta实际的文字片段(要显示给用户看的就是这个)
ContentBlockStop内容块结束
MessageDelta消息层级的更新(比如 stop reason)
MessageStop整条消息结束

原始事件流:

messages = []
add_user_message(messages, "Write a 1 sentence description of a fake database")

stream = client.messages.create(
    model=model,
    max_tokens=1000,
    messages=messages,
    stream=True
)

for event in stream:
    print(event)

只要文字,用简化写法(自动过滤掉非文字内容):

with client.messages.stream(
    model=model,
    max_tokens=1000,
    messages=messages
) as stream:
    for text in stream.text_stream:
        print(text, end="")

既要即时显示、又要最后存进数据库,两者可以一起拿:

with client.messages.stream(
    model=model,
    max_tokens=1000,
    messages=messages
) as stream:
    for text in stream.text_stream:
        # 把每个片段传给前端
        pass

    # 生成结束后拿完整的 message,存进数据库
    final_message = stream.get_final_message()

Structured Data:只要 JSON,不要废话

Claude 默认回答会带解释文字和 markdown 格式,用户没办法直接复制整段拿去用——得自己手动挑出 JSON 那一块。

解法:assistant prefill + stop sequence 组合拳

messages = []

add_user_message(messages, "Generate a very short event bridge rule as json")
add_assistant_message(messages, "```json")

text = chat(messages, stop_sequences=["```"])

原理:

  1. User message 说明要生成什么
  2. 预先塞一条 assistant message `json ,让 Claude 以为自己已经开了一个 markdown 代码块
  3. Claude 接着只会写 JSON 内容本身
  4. stop_sequences=["`"] 让生成在 Claude 想写收尾的 ` 时就直接截断

拿到的就是干净的输出,不带任何解释文字:

{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["running"]
  }
}

后续直接 parse:

import json

clean_json = json.loads(text.strip())

💡 这招不只能用在 JSON——Python 代码、bullet list、CSV,任何「只要内容、不要解释」的格式都适用,把 prefill 换成对应的开头符号(比如 `python )就行。

常见错误

  • ❌ 把 API key 直接写进前端代码——一定要经过自己的 server,key 只存在后端
  • ❌ 以为 Claude 会「记得」上一轮对话——它完全无状态,不自己维护并传完整的 messages 列表,后续问题 Claude 根本接不上
  • ❌ system 参数传 None——API 会报错,要用 if system: 判断,没给就整个不传这个 key
  • ❌ 以为调高 max_tokens 就是叫 Claude「写长一点」——它只是安全上限,不是长度目标
  • ❌ API key 弹窗关掉忘记复制——没有「再看一次」这回事,只能删掉重开一把新的

Sources

官方文档:

  1. Building with the Claude API(Claude Academy 课程)— https://academy.claude.com/courses/building-with-the-claude-api