课程目标 #
了解消息 API 格式
使用并了解模型响应对象
构建一个简单的多轮聊天机器人
基本设置 #
我们将首先导入所需的包并初始化客户端对象。有关如何获取 API 密钥并正确存储它的详细信息,请参阅上一个教程。
from dotenv import load_dotenv
from anthropic import Anthropic
#load environment variable
load_dotenv()
#automatically looks for an "ANTHROPIC_API_KEY" environment variable
client = Anthropic()
消息格式 #
正如我们在上一课中看到的,我们可以使用 client.messages.create() 向 Claude 发送消息并得到响应:
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=1000,
messages=[
{"role": "user", "content": "What flavors are used in Dr. Pepper?"}
]
)
print(response)
Message(id='msg_013wVsHLHRjuDM2WgvVJ8RNm', content=[ContentBlock(text='The exact flavor formula for Dr Pepper is a closely guarded trade secret, but here are some of the main flavors that are believed to be used:\n\n- Cherry - This is one of the most prominent flavors in Dr Pepper. The cherry flavor comes from the use of a type of cherry extract.\n\n- Prune - Dr Pepper contains a prune-like flavor which contributes to its unique profile.\n\n- Vanilla - Vanilla is another key component that helps round out the flavor.\n\n- Spices - Various spices like cinnamon, prune, and other aromatics are believed to be part of the blend.\n\n- Citrus - Flavors like orange, lemon, and prune add some citrus notes.\n\nThe exact combination of these and other secret ingredients is what gives Dr Pepper its signature taste that differentiates it from other cola or soda flavors. The complex blend of sweet, spicy, and tart notes is part of what makes Dr Pepper a unique and iconic soft drink flavor.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=18, output_tokens=225))
Let's take a closer look at this bit:
messages=[
{"role": "user", "content": "What flavors are used in Dr. Pepper?"}
]
messages 参数是与 Claude API 交互的关键部分。它允许您提供对话历史记录和上下文,以便 Claude 生成相关响应。
messages 参数需要一个消息字典列表,其中每个字典代表对话中的一条消息。每个消息字典应具有以下键:
role:表示消息发送者角色的字符串。它可以是“用户”(对于用户发送的消息)或“助手”(对于 Claude 发送的消息)。
content:表示消息实际内容的字符串或内容字典列表。如果提供了字符串,它将被视为单个文本内容块。如果提供了内容字典列表,则每个字典应具有“类型”(例如“文本”或“图像”)和相应的内容。目前,我们将内容保留为单个字符串。
以下是包含单个用户消息的消息列表示例:
messages = [
{“role”: “user”, “content”: “你好,Claude!你今天好吗?”}
]
以下是包含多个消息(代表对话)的示例:
messages = [
{“role”: “user”, “content”: “你好,Claude!你今天好吗?”},
{“role”: “assistant”, “content”: “你好!我很好,谢谢。我今天能为你做些什么?”},
{“role”: “user”, “content”: “你能告诉我一个关于雪貂的有趣事实吗?”},
{“role”: “assistant”, “content”: “当然可以!你知道兴奋的雪貂会发出一种称为‘dooking’的咯咯声吗?”},
]
请记住,消息总是在用户消息和助手消息之间交替。
消息格式允许我们以对话的形式构造对 Claude 的 API 调用,从而实现上下文保存:消息格式允许维护整个对话历史记录,包括用户和助手消息。这确保 Claude 在生成响应时可以访问对话的完整上下文,从而产生更连贯和相关的输出。
注意:许多用例不需要对话历史记录,提供仅包含一条消息的消息列表也没有错!
测验 #
每条消息中两个必需的键是什么?
a) “发件人”和“文本”
b) “角色”和“内容”
c) “用户”和“助手”
d) “输入”和“输出”
查看测验答案
正确答案是 b。每条消息都应该有“角色”和“内容”
检查消息响应 #
接下来,让我们看看从 Claude 那里得到的响应的形状。
让我们要求 Claude 做一些简单的事情:
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=1000,
messages=[
{"role": "user", "content": "Translate hello to French. Respond with a single word"}
]
)
现在让我们检查一下收到的响应的内容:
response
Message(id='msg_01SuDqJSTJaRpkDmHGrbfxCt', content=[ContentBlock(text='Bonjour.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=19, output_tokens=8))
我们返回一个包含一些属性的 Message 对象。以下是示例:
Message(id=’msg_01Mq5gDnUmDESukTgwPV8xtG’, content=[TextBlock(text=’Bonjour.’, type=’text’)], model=’claude-3-haiku-20240307′, role=’assistant’, stop_reason=’end_turn’, stop_sequence=None, type=’message’, usage=Usage(input_tokens=19, output_tokens=8))
最重要的信息是 content 属性:它包含模型为我们生成的实际内容。这是一个内容块列表,每个内容块都有一个决定其形状的类型。
为了访问模型响应的实际文本内容,我们需要执行以下操作:
print(response.content[0].text)
Bonjour。
除了内容之外,Message 对象还包含一些其他信息:
id – 唯一对象标识符
type – 对象类型,始终为“消息”
role – 生成消息的对话角色。始终为“助手”。
model – 处理请求并生成响应的模型
stop_reason – 模型停止生成的原因。稍后我们将详细了解这一点。
stop_sequence – 我们将很快详细了解这一点。
usage – 有关计费和速率限制使用的信息。包含以下信息:
input_tokens – 使用的输入令牌数量。
output_tokens – 使用的输出令牌数量。
知道我们可以访问这些信息很重要,但如果你只记住一件事,那就是:内容包含实际的模型生成的内容
练习 #
编写一个名为translate的函数,该函数需要两个参数:
一个单词
一种语言
当您调用translate函数时,它应该返回要求Claude将单词翻译成语言的结果。例如:
translate("hello", "Spanish")
# 'The word "hello" translated into Spanish is: Hola'
translate("chicken", "Italian")
# 'The Italian word for "chicken" is: pollo'
Bonus points if you can write a prompt so that Claude only responds with the translated word and no preamble, like this:
translate("chicken", "Italian")
# 'pollo'
View exercise solution
Here's one possible solution:
def translate(word, language):
response = client.messages.create(
model="claude-3-opus-20240229",
max_tokens=1000,
messages=[
{"role": "user", "content": f"Translate the word {word} into {language}. Only respond with the translated word, nothing else"}
]
)
return response.content[0].text
消息列表错误 #
错误 #1:以助理消息开头 #
刚开始时,使用消息列表时很容易犯错误。消息列表必须以用户消息开头。以下代码会产生错误,因为消息列表以助理消息开头:
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=1000,
messages=[
{"role": "assistant", "content": "Hello there!"}
]
)
print(response.content[0].text)
BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages: first message must use the "user" role'}}
错误 #2:不正确地交替发送消息 #
消息必须在用户和助手之间交替发送,如果我们不遵循此规则,就会出现错误:
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=1000,
messages=[
{"role": "user", "content": "Hey there!"},
{"role": "assistant", "content": "Hi there!"},
{"role": "assistant", "content": "How can I help you??"}
]
)
print(response.content[0].text)
BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages: roles must alternate between "user" and "assistant", but found multiple "assistant" roles in a row'}}
消息列表用例 #
将单词放入 Claude 的嘴里 #
获得非常具体的输出的另一种常见策略是“将单词放入 Claude 的嘴里”。我们不仅可以向 Claude 提供用户消息,还可以提供 Claude 在生成输出时将使用的助手消息。
使用 Anthropic 的 API 时,您不仅限于用户消息。如果您提供助手消息,Claude 将从最后一个助手令牌继续对话。请记住,我们必须从用户消息开始。
假设我想让 Claude 给我写一首俳句,第一行开头是“平静的山间空气”。我可以提供以下对话历史记录:
messages=[
{“role”: “user”, “content”: f”生成一首美丽的俳句”},
{“role”: “assistant”, “content”: “平静的山间空气”}
]
我们告诉 Claude,我们希望它生成一首俳句,并且我们将俳句的第一行放到 Claude 的嘴里
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=500,
messages=[
{"role": "user", "content": f"Generate a beautiful haiku"},
{"role": "assistant", "content": "calming mountain air"}
]
)
print(response.content[0].text)
dancing sunlight on still waters,
nature's gentle grace.
print("calming mountain air" + response.content[0].text)
calming mountain air,
dancing sunlight on still waters,
nature's gentle grace.
少量提示 #
最有用的提示策略之一称为“少量提示”,它涉及为模型提供少量示例。这些示例有助于指导 Claude 生成的输出。消息对话历史记录是一种向 Claude 提供示例的简单方法。
例如,假设我们想使用 Claude 分析推文中的情绪。我们可以先简单地要求 Claude“请分析这条推文中的情绪:”,然后看看我们得到什么样的输出:
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=500,
messages=[
{"role": "user", "content": f"Analyze the sentiment in this tweet: Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
]
)
print(response.content[0].text)
The sentiment in this tweet is overwhelmingly positive. The user expresses their enjoyment of the new spicy pickles from @PickleCo, using enthusiastic language and emojis to convey their delight.
Positive indicators:
1. "My taste buds are doing a happy dance!" - This phrase indicates that the user is extremely pleased with the taste of the pickles, to the point of eliciting a joyful physical response.
2. Emojis - The use of the hot pepper 🌶️ and cucumber 🥒 emojis further emphasizes the user's excitement about the spicy pickles.
3. Hashtags - The inclusion of #pickleslove and #spicyfood hashtags suggests that the user has a strong affinity for pickles and spicy food, and the new product aligns perfectly with their preferences.
4. Exclamation mark - The exclamation mark at the end of the first sentence adds emphasis to the user's positive experience.
Overall, the tweet conveys a strong sense of satisfaction, excitement, and enjoyment related to trying the new spicy pickles from @PickleCo.
我第一次运行上述代码时,Claude 生成了这个长响应:
这是一个很棒的回复,但它可能比我们需要从 Claude 那里得到的信息要多得多,特别是如果我们试图自动对大量推文进行情绪分析的话。
我们可能更希望 Claude 用标准化的输出格式来回复,比如一个单词(POSITIVE、NEUTRAL、NEGATIVE)或一个数值(1、0、-1)。为了便于阅读和简单起见,让我们让 Claude 用“POSITIVE”或“NEGATIVE”来回复。一种方法是通过少量提示。我们可以为 Claude 提供一个对话历史记录,准确显示我们希望它如何回应:
messages=[
{"role": "user", "content": "Unpopular opinion: Pickles are disgusting. Don't @ me"},
{"role": "assistant", "content": "NEGATIVE"},
{"role": "user", "content": "I think my love for pickles might be getting out of hand. I just bought a pickle-shaped pool float"},
{"role": "assistant", "content": "POSITIVE"},
{"role": "user", "content": "Seriously why would anyone ever eat a pickle? Those things are nasty!"},
{"role": "assistant", "content": "NEGATIVE"},
{"role": "user", "content": "Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
]
response = client.messages.create(
model="claude-3-haiku-20240307",
max_tokens=500,
messages=[
{"role": "user", "content": "Unpopular opinion: Pickles are disgusting. Don't @ me"},
{"role": "assistant", "content": "NEGATIVE"},
{"role": "user", "content": "I think my love for pickles might be getting out of hand. I just bought a pickle-shaped pool float"},
{"role": "assistant", "content": "POSITIVE"},
{"role": "user", "content": "Seriously why would anyone ever eat a pickle? Those things are nasty!"},
{"role": "assistant", "content": "NEGATIVE"},
{"role": "user", "content": "Just tried the new spicy pickles from @PickleCo, and my taste buds are doing a happy dance! 🌶️🥒 #pickleslove #spicyfood"},
]
)
print(response.content[0].text)
POSITIVE
练习 #
您的任务:构建一个聊天机器人 #
构建一个简单的多轮命令行聊天机器人脚本。消息格式适合构建基于聊天的应用程序。要使用 Claude 构建聊天机器人,非常简单:
- 1.保留一个列表来存储对话历史记录
- 2.使用 input() 向用户询问消息并将用户输入添加到消息列表
- 3.将消息历史记录发送给 Claude
- 4.打印出 Claude 对用户的回复
- 5.将 Claude 的助手回复添加到历史记录中
- 6.返回步骤 2 并重复!(使用循环并为用户提供退出方式!)
查看练习解决方案
conversation_history = []
while True:
user_input = input("User: ")
if user_input.lower() == "quit":
print("Conversation ended.")
break
conversation_history.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-3-haiku-20240307",
messages=conversation_history,
max_tokens=500
)
assistant_response = response.content[0].text
print(f"Assistant: {assistant_response}")
conversation_history.append({"role": "assistant", "content": assistant_response})