EP01. "Claude API 基础入门:从请求到多轮对话"
🔒 登录后可标记已读- 一个 API 请求从你的 App 出发,要走「你的 server → Anthropic API → 模型处理 → 回到 server → 回到 client」这五步,中间每一步都可能是出错/需要优化的地方
- API key 一定要放在自己的 server 端,前端代码里绝对不能出现——泄漏了就等于把额度免费送人
- Claude 本身不存对话记录,每次请求都是独立的,要自己在代码里维护 messages 列表、每次把完整历史一起传过去,Claude 才「记得」上一句说了什么
temperature、systemprompt、streaming、assistant prefill 这几个参数/技巧,决定了回答的语气、可预测性和输出格式,是接下来所有课程内容的地基- 前置知识:会写 Python,有 Anthropic 账号(还没有的话,这篇笔记第二节就是申请步骤)
重点内容
请求的五步流程
一次典型的 API 调用会经过 5 个阶段:
- 你的 App 发请求给你自己的 server
- 你的 server 再转发请求给 Anthropic API
- 模型处理(下面「Claude 怎么处理你的输入」细讲)
- Anthropic API 把结果传回你的 server
- 你的 server 再把结果传回 client
为什么一定要经过自己的 server
📌 三个理由:
- API 请求需要一把密钥(API key)做身份验证
- 如果把 key 直接写进前端代码,任何用户打开开发者工具都能看到、拿去用
- key 泄漏后,别人能拿着它无限量发请求,账单算在你头上
正确做法:API key 只存在自己的 server(用环境变量/密钥管理服务),前端只跟自己的 server 对话,server 再代为呼叫 Anthropic API。
申请 API Key
- 打开 https://platform.claude.com/ ,登入 Anthropic 账号
- 右上角点 Get API Keys
- 页面右上角再点 Create Key
- 选
Defaultworkspace,给这把 key 取个名字(方便日后区分是哪个项目在用),例如 "Anthropic Course" - 弹窗会显示 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=["```"])
原理:
- User message 说明要生成什么
- 预先塞一条 assistant message
`json,让 Claude 以为自己已经开了一个 markdown 代码块 - Claude 接着只会写 JSON 内容本身
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
官方文档:
- Building with the Claude API(Claude Academy 课程)— https://academy.claude.com/courses/building-with-the-claude-api