提示 Claude 获取“JSON 模式”
Claude 没有正式的“JSON 模式”并进行约束采样。但别担心——你仍然可以从 Claude 获取可靠的 JSON!本指南将向你展示如何操作。
首先,让我们看看 Claude 的默认行为。
%pip install anthropic
from anthropic import Anthropic
import json
import re
from pprint import pprint
client = Anthropic()
MODEL_NAME = "claude-3-opus-20240229"
message = client.messages.create(
model=MODEL_NAME,
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Give me a JSON dict with names of famous athletes & their sports."
},
]
).content[0].text
print(message)
Here is a JSON dictionary with names of famous athletes and their respective sports:
{
"athletes": [
{
"name": "Usain Bolt",
"sport": "Track and Field"
},
{
"name": "Michael Phelps",
"sport": "Swimming"
},
{
"name": "Serena Williams",
"sport": "Tennis"
},
{
"name": "LeBron James",
"sport": "Basketball"
},
{
"name": "Lionel Messi",
"sport": "Soccer"
},
{
"name": "Simone Biles",
"sport": "Gymnastics"
},
{
"name": "Tom Brady",
"sport": "American Football"
},
{
"name": "Muhammad Ali",
"sport": "Boxing"
},
{
"name": "Nadia Comaneci",
"sport": "Gymnastics"
},
{
"name": "Michael Jordan",
"sport": "Basketball"
},
{
"name": "Pelé",
"sport": "Soccer"
},
{
"name": "Roger Federer",
"sport": "Tennis"
}
]
}
Claude 遵循了指示并输出了一个不错的字典,我们可以用代码提取它:
def extract_json(response):
json_start = response.index("{")
json_end = response.rfind("}")
return json.loads(response[json_start:json_end + 1])
extract_json(message)
{'athletes': [{'name': 'Usain Bolt', 'sport': 'Track and Field'},
{'name': 'Michael Phelps', 'sport': 'Swimming'},
{'name': 'Serena Williams', 'sport': 'Tennis'},
{'name': 'LeBron James', 'sport': 'Basketball'},
{'name': 'Lionel Messi', 'sport': 'Soccer'},
{'name': 'Simone Biles', 'sport': 'Gymnastics'},
{'name': 'Tom Brady', 'sport': 'American Football'},
{'name': 'Muhammad Ali', 'sport': 'Boxing'},
{'name': 'Nadia Comaneci', 'sport': 'Gymnastics'},
{'name': 'Michael Jordan', 'sport': 'Basketball'},
{'name': 'Pelé', 'sport': 'Soccer'},
{'name': 'Roger Federer', 'sport': 'Tennis'}]}
但是,如果我们希望 Claude 跳过前导语,直接输出 JSON 怎么办?一种简单的方法是预填充 Claude 的响应,并包含一个“{”字符。
message = client.messages.create(
model=MODEL_NAME,
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Give me a JSON dict with names of famous athletes & their sports."
},
{
"role": "assistant",
"content": "Here is the JSON requested:\n{"
}
]
).content[0].text
print(message)
"athletes":[
{
"name":"Michael Jordan",
"sport":"Basketball"
},
{
"name":"Babe Ruth",
"sport":"Baseball"
},
{
"name":"Muhammad Ali",
"sport":"Boxing"
},
{
"name":"Serena Williams",
"sport":"Tennis"
},
{
"name":"Wayne Gretzky",
"sport":"Hockey"
},
{
"name":"Michael Phelps",
"sport":"Swimming"
},
{
"name":"Usain Bolt",
"sport":"Track and Field"
},
{
"name":"Mia Hamm",
"sport":"Soccer"
},
{
"name":"Michael Schumacher",
"sport":"Formula 1 Racing"
},
{
"name":"Simone Biles",
"sport":"Gymnastics"
}
]
}
现在我们只需要加上我们预填充的“{”字符,就可以提取 JSON 了。
output_json = json.loads("{" + message[:message.rfind("}") + 1])
output_json
{'athletes': [{'name': 'Michael Jordan', 'sport': 'Basketball'},
{'name': 'Babe Ruth', 'sport': 'Baseball'},
{'name': 'Muhammad Ali', 'sport': 'Boxing'},
{'name': 'Serena Williams', 'sport': 'Tennis'},
{'name': 'Wayne Gretzky', 'sport': 'Hockey'},
{'name': 'Michael Phelps', 'sport': 'Swimming'},
{'name': 'Usain Bolt', 'sport': 'Track and Field'},
{'name': 'Mia Hamm', 'sport': 'Soccer'},
{'name': 'Michael Schumacher', 'sport': 'Formula 1 Racing'},
{'name': 'Simone Biles', 'sport': 'Gymnastics'}]}
对于包含多个 JSON 输出的长而复杂的提示,字符串搜索“{”和“}”可能无法奏效,你可以让 Claude 将每个 JSON 项输出到指定的标签中,以便将来提取。
message = client.messages.create(
model=MODEL_NAME,
max_tokens=1024,
messages=[
{
"role": "user",
"content": """Give me a JSON dict with the names of 5 famous athletes & their sports.
Put this dictionary in <athlete_sports> tags.
Then, for each athlete, output an additional JSON dictionary. In each of these additional dictionaries:
- Include two keys: the athlete's first name and last name.
- For the values, list three words that start with the same letter as that name.
Put each of these additional dictionaries in separate <athlete_name> tags."""
},
{
"role": "assistant",
"content": "Here is the JSON requested:"
}
],
).content[0].text
print(message)
<athlete_sports>
{
"Michael Jordan": "Basketball",
"Serena Williams": "Tennis",
"Lionel Messi": "Soccer",
"Usain Bolt": "Track and Field",
"Michael Phelps": "Swimming"
}
</athlete_sports>
<athlete_name>
{
"first": ["Magnificent", "Motivating", "Memorable"],
"last": ["Joyful", "Jumping", "Jocular"]
}
</athlete_name>
<athlete_name>
{
"first": ["Skillful", "Strong", "Superstar"],
"last": ["Winning", "Willful", "Wise"]
}
</athlete_name>
<athlete_name>
{
"first": ["Legendary", "Lively", "Leaping"],
"last": ["Magical", "Marvelous", "Masterful"]
}
</athlete_name>
<athlete_name>
{
"first": ["Unbeatable", "Unbelievable", "Unstoppable"],
"last": ["Brave", "Bold", "Brilliant"]
}
</athlete_name>
<athlete_name>
{
"first": ["Marvelous", "Methodical", "Medalist"],
"last": ["Powerful", "Persevering", "Precise"]
}
</athlete_name>
现在,我们可以使用提取正则表达式来获取所有字典。
import re
def extract_between_tags(tag: str, string: str, strip: bool = False) -> list[str]:
ext_list = re.findall(f"<{tag}>(.+?)</{tag}>", string, re.DOTALL)
if strip:
ext_list = [e.strip() for e in ext_list]
return ext_list
athlete_sports_dict = json.loads(extract_between_tags("athlete_sports", message)[0])
athlete_name_dicts = [
json.loads(d)
for d in extract_between_tags("athlete_name", message)
]
pprint(athlete_sports_dict)
{'Lionel Messi': 'Soccer',
'Michael Jordan': 'Basketball',
'Michael Phelps': 'Swimming',
'Serena Williams': 'Tennis',
'Usain Bolt': 'Track and Field'}
pprint(athlete_name_dicts, width=1)
[{'first': ['Magnificent',
'Motivating',
'Memorable'],
'last': ['Joyful',
'Jumping',
'Jocular']},
{'first': ['Skillful',
'Strong',
'Superstar'],
'last': ['Winning',
'Willful',
'Wise']},
{'first': ['Legendary',
'Lively',
'Leaping'],
'last': ['Magical',
'Marvelous',
'Masterful']},
{'first': ['Unbeatable',
'Unbelievable',
'Unstoppable'],
'last': ['Brave',
'Bold',
'Brilliant']},
{'first': ['Marvelous',
'Methodical',
'Medalist'],
'last': ['Powerful',
'Persevering',
'Precise']}]
总结一下:
- 你可以使用字符串解析来提取“
json”和“
”之间的文本以获取 JSON。 - 你可以通过部分助手消息删除前导语。 (但是,这会消除让 Claude 进行“思维链”以在开始输出 JSON 之前提高智能性的可能性。)
- 你可以使用停止序列来删除 JSON 之后出现的文本。
- 你可以指示 Claude 以 XML 标签输出 JSON,以便在更复杂的提示中轻松收集。