misc/how_to_enable_json_mode.ipynb (532 lines of code) (raw):
{
"cells": [
{
"cell_type": "markdown",
"id": "20de56a8",
"metadata": {},
"source": [
"# Prompting Claude for \"JSON Mode\""
]
},
{
"cell_type": "markdown",
"id": "8e7fe136",
"metadata": {},
"source": [
"Claude doesn't have a formal \"JSON Mode\" with constrained sampling. But not to worry -- you can still get reliable JSON from Claude! This recipe will show you how."
]
},
{
"cell_type": "markdown",
"id": "e5e1a230",
"metadata": {},
"source": [
"First, let's look at Claude's default behavior."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0c07a2c1",
"metadata": {},
"outputs": [],
"source": [
"%pip install anthropic"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "fc39114b",
"metadata": {},
"outputs": [],
"source": [
"from anthropic import Anthropic\n",
"import json\n",
"import re\n",
"from pprint import pprint"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1b991340",
"metadata": {},
"outputs": [],
"source": [
"client = Anthropic()\n",
"MODEL_NAME = \"claude-3-opus-20240229\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "c28ca1ad",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Here is a JSON dictionary with names of famous athletes and their respective sports:\n",
"\n",
"{\n",
" \"athletes\": [\n",
" {\n",
" \"name\": \"Usain Bolt\",\n",
" \"sport\": \"Track and Field\"\n",
" },\n",
" {\n",
" \"name\": \"Michael Phelps\",\n",
" \"sport\": \"Swimming\"\n",
" },\n",
" {\n",
" \"name\": \"Serena Williams\",\n",
" \"sport\": \"Tennis\"\n",
" },\n",
" {\n",
" \"name\": \"LeBron James\",\n",
" \"sport\": \"Basketball\"\n",
" },\n",
" {\n",
" \"name\": \"Lionel Messi\",\n",
" \"sport\": \"Soccer\"\n",
" },\n",
" {\n",
" \"name\": \"Simone Biles\",\n",
" \"sport\": \"Gymnastics\"\n",
" },\n",
" {\n",
" \"name\": \"Tom Brady\",\n",
" \"sport\": \"American Football\"\n",
" },\n",
" {\n",
" \"name\": \"Muhammad Ali\",\n",
" \"sport\": \"Boxing\"\n",
" },\n",
" {\n",
" \"name\": \"Nadia Comaneci\",\n",
" \"sport\": \"Gymnastics\"\n",
" },\n",
" {\n",
" \"name\": \"Michael Jordan\",\n",
" \"sport\": \"Basketball\"\n",
" },\n",
" {\n",
" \"name\": \"Pelé\",\n",
" \"sport\": \"Soccer\"\n",
" },\n",
" {\n",
" \"name\": \"Roger Federer\",\n",
" \"sport\": \"Tennis\"\n",
" }\n",
" ]\n",
"}\n"
]
}
],
"source": [
"message = client.messages.create(\n",
" model=MODEL_NAME,\n",
" max_tokens=1024,\n",
" messages=[\n",
" {\n",
" \"role\": \"user\", \n",
" \"content\": \"Give me a JSON dict with names of famous athletes & their sports.\"\n",
" },\n",
" ]\n",
").content[0].text\n",
"print(message)"
]
},
{
"cell_type": "markdown",
"id": "0a63ad7e",
"metadata": {},
"source": [
"Claude followed instructions and outputted a nice dictionary, which we can extract with code:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "08626553",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'athletes': [{'name': 'Usain Bolt', 'sport': 'Track and Field'},\n",
" {'name': 'Michael Phelps', 'sport': 'Swimming'},\n",
" {'name': 'Serena Williams', 'sport': 'Tennis'},\n",
" {'name': 'LeBron James', 'sport': 'Basketball'},\n",
" {'name': 'Lionel Messi', 'sport': 'Soccer'},\n",
" {'name': 'Simone Biles', 'sport': 'Gymnastics'},\n",
" {'name': 'Tom Brady', 'sport': 'American Football'},\n",
" {'name': 'Muhammad Ali', 'sport': 'Boxing'},\n",
" {'name': 'Nadia Comaneci', 'sport': 'Gymnastics'},\n",
" {'name': 'Michael Jordan', 'sport': 'Basketball'},\n",
" {'name': 'Pelé', 'sport': 'Soccer'},\n",
" {'name': 'Roger Federer', 'sport': 'Tennis'}]}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_json(response):\n",
" json_start = response.index(\"{\")\n",
" json_end = response.rfind(\"}\")\n",
" return json.loads(response[json_start:json_end + 1])\n",
"extract_json(message)"
]
},
{
"cell_type": "markdown",
"id": "39275885",
"metadata": {},
"source": [
"But what if we want Claude to skip the preamble and go straight to the JSON? One simple way is to prefill Claude's response and include a \"{\" character."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "155e088a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" \"athletes\":[\n",
" {\n",
" \"name\":\"Michael Jordan\",\n",
" \"sport\":\"Basketball\"\n",
" },\n",
" {\n",
" \"name\":\"Babe Ruth\",\n",
" \"sport\":\"Baseball\"\n",
" },\n",
" {\n",
" \"name\":\"Muhammad Ali\",\n",
" \"sport\":\"Boxing\"\n",
" },\n",
" {\n",
" \"name\":\"Serena Williams\",\n",
" \"sport\":\"Tennis\"\n",
" },\n",
" {\n",
" \"name\":\"Wayne Gretzky\",\n",
" \"sport\":\"Hockey\"\n",
" },\n",
" {\n",
" \"name\":\"Michael Phelps\",\n",
" \"sport\":\"Swimming\"\n",
" },\n",
" {\n",
" \"name\":\"Usain Bolt\",\n",
" \"sport\":\"Track and Field\"\n",
" },\n",
" {\n",
" \"name\":\"Mia Hamm\",\n",
" \"sport\":\"Soccer\"\n",
" },\n",
" {\n",
" \"name\":\"Michael Schumacher\",\n",
" \"sport\":\"Formula 1 Racing\"\n",
" },\n",
" {\n",
" \"name\":\"Simone Biles\",\n",
" \"sport\":\"Gymnastics\"\n",
" }\n",
" ]\n",
"}\n"
]
}
],
"source": [
"message = client.messages.create(\n",
" model=MODEL_NAME,\n",
" max_tokens=1024,\n",
" messages=[\n",
" {\n",
" \"role\": \"user\", \n",
" \"content\": \"Give me a JSON dict with names of famous athletes & their sports.\"\n",
" },\n",
" {\n",
" \"role\": \"assistant\",\n",
" \"content\": \"Here is the JSON requested:\\n{\"\n",
" }\n",
" ]\n",
").content[0].text\n",
"print(message)"
]
},
{
"cell_type": "markdown",
"id": "64e94c65",
"metadata": {},
"source": [
"Now all we have to do is add back the \"{\" that we prefilled and we can extract the JSON."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "6c066ac6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'athletes': [{'name': 'Michael Jordan', 'sport': 'Basketball'},\n",
" {'name': 'Babe Ruth', 'sport': 'Baseball'},\n",
" {'name': 'Muhammad Ali', 'sport': 'Boxing'},\n",
" {'name': 'Serena Williams', 'sport': 'Tennis'},\n",
" {'name': 'Wayne Gretzky', 'sport': 'Hockey'},\n",
" {'name': 'Michael Phelps', 'sport': 'Swimming'},\n",
" {'name': 'Usain Bolt', 'sport': 'Track and Field'},\n",
" {'name': 'Mia Hamm', 'sport': 'Soccer'},\n",
" {'name': 'Michael Schumacher', 'sport': 'Formula 1 Racing'},\n",
" {'name': 'Simone Biles', 'sport': 'Gymnastics'}]}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"output_json = json.loads(\"{\" + message[:message.rfind(\"}\") + 1])\n",
"output_json"
]
},
{
"cell_type": "markdown",
"id": "cd4492fd",
"metadata": {},
"source": [
"For very long and complicated prompts, which contain multiple JSON outputs so that a string search for \"{\" and \"}\" don't do the trick, you can also have Claude output each JSON item in specified tags for future extraction."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "443ad932",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" \n",
"\n",
"<athlete_sports>\n",
"{\n",
" \"Michael Jordan\": \"Basketball\",\n",
" \"Serena Williams\": \"Tennis\",\n",
" \"Lionel Messi\": \"Soccer\", \n",
" \"Usain Bolt\": \"Track and Field\",\n",
" \"Michael Phelps\": \"Swimming\"\n",
"}\n",
"</athlete_sports>\n",
"\n",
"<athlete_name>\n",
"{\n",
" \"first\": [\"Magnificent\", \"Motivating\", \"Memorable\"],\n",
" \"last\": [\"Joyful\", \"Jumping\", \"Jocular\"]\n",
"}\n",
"</athlete_name>\n",
"\n",
"<athlete_name>\n",
"{\n",
" \"first\": [\"Skillful\", \"Strong\", \"Superstar\"],\n",
" \"last\": [\"Winning\", \"Willful\", \"Wise\"]\n",
"}\n",
"</athlete_name>\n",
"\n",
"<athlete_name>\n",
"{\n",
" \"first\": [\"Legendary\", \"Lively\", \"Leaping\"],\n",
" \"last\": [\"Magical\", \"Marvelous\", \"Masterful\"] \n",
"}\n",
"</athlete_name>\n",
"\n",
"<athlete_name>\n",
"{\n",
" \"first\": [\"Unbeatable\", \"Unbelievable\", \"Unstoppable\"],\n",
" \"last\": [\"Brave\", \"Bold\", \"Brilliant\"]\n",
"}\n",
"</athlete_name>\n",
"\n",
"<athlete_name>\n",
"{\n",
" \"first\": [\"Marvelous\", \"Methodical\", \"Medalist\"],\n",
" \"last\": [\"Powerful\", \"Persevering\", \"Precise\"]\n",
"}\n",
"</athlete_name>\n"
]
}
],
"source": [
"message = client.messages.create(\n",
" model=MODEL_NAME,\n",
" max_tokens=1024,\n",
" messages=[\n",
" {\n",
" \"role\": \"user\", \n",
" \"content\": \"\"\"Give me a JSON dict with the names of 5 famous athletes & their sports.\n",
"Put this dictionary in <athlete_sports> tags. \n",
"\n",
"Then, for each athlete, output an additional JSON dictionary. In each of these additional dictionaries:\n",
"- Include two keys: the athlete's first name and last name.\n",
"- For the values, list three words that start with the same letter as that name.\n",
"Put each of these additional dictionaries in separate <athlete_name> tags.\"\"\"\n",
" },\n",
" {\n",
" \"role\": \"assistant\",\n",
" \"content\": \"Here is the JSON requested:\"\n",
" }\n",
" ],\n",
").content[0].text\n",
"print(message)"
]
},
{
"cell_type": "markdown",
"id": "74043369",
"metadata": {},
"source": [
"Now, we can use an extraction regex to get all the dictionaries."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "bd847a70",
"metadata": {},
"outputs": [],
"source": [
"import re\n",
"def extract_between_tags(tag: str, string: str, strip: bool = False) -> list[str]:\n",
" ext_list = re.findall(f\"<{tag}>(.+?)</{tag}>\", string, re.DOTALL)\n",
" if strip:\n",
" ext_list = [e.strip() for e in ext_list]\n",
" return ext_list\n",
"\n",
"athlete_sports_dict = json.loads(extract_between_tags(\"athlete_sports\", message)[0])\n",
"athlete_name_dicts = [\n",
" json.loads(d)\n",
" for d in extract_between_tags(\"athlete_name\", message)\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "eb61ee06",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'Lionel Messi': 'Soccer',\n",
" 'Michael Jordan': 'Basketball',\n",
" 'Michael Phelps': 'Swimming',\n",
" 'Serena Williams': 'Tennis',\n",
" 'Usain Bolt': 'Track and Field'}\n"
]
}
],
"source": [
"pprint(athlete_sports_dict)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "57dade0f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[{'first': ['Magnificent',\n",
" 'Motivating',\n",
" 'Memorable'],\n",
" 'last': ['Joyful',\n",
" 'Jumping',\n",
" 'Jocular']},\n",
" {'first': ['Skillful',\n",
" 'Strong',\n",
" 'Superstar'],\n",
" 'last': ['Winning',\n",
" 'Willful',\n",
" 'Wise']},\n",
" {'first': ['Legendary',\n",
" 'Lively',\n",
" 'Leaping'],\n",
" 'last': ['Magical',\n",
" 'Marvelous',\n",
" 'Masterful']},\n",
" {'first': ['Unbeatable',\n",
" 'Unbelievable',\n",
" 'Unstoppable'],\n",
" 'last': ['Brave',\n",
" 'Bold',\n",
" 'Brilliant']},\n",
" {'first': ['Marvelous',\n",
" 'Methodical',\n",
" 'Medalist'],\n",
" 'last': ['Powerful',\n",
" 'Persevering',\n",
" 'Precise']}]\n"
]
}
],
"source": [
"pprint(athlete_name_dicts, width=1)"
]
},
{
"cell_type": "markdown",
"id": "5e5854c0",
"metadata": {},
"source": [
"So to recap:\n",
"\n",
"- You can use string parsing to extract the text between \"```json\" and \"```\" to get the JSON.\n",
"- You can remove preambles *before* the JSON via a partial Assistant message. (However, this removes the possibility of having Claude do \"Chain of Thought\" for increased intelligence before beginning to output the JSON.)\n",
"- You can get rid of text that comes *after* the JSON by using a stop sequence.\n",
"- You can instruct Claude to output JSON in XML tags to make it easy to collect afterward for more complex prompts."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}