Example: Planning with Our Assistant › The Planning Phase
Michael BrenndoerferJuly 23, 20252 min read
AI Agent HandbookAll chapters Part 9 of 15Planning - Agents with a Plan
Example: Planning with Our Assistant › The Planning PhaseOne section · Sections in this chapter3 / 9 ↓
Sections in this chapter9
When our assistant receives this request, it doesn't immediately start booking meetings or sending emails. Instead, it enters a planning phase where it breaks down the goal into manageable subtasks.
Here's how the assistant might decompose this request:
In[3]:
Code## Using Claude Sonnet 4.5 for its superior planning capabilities
import os
from anthropic import Anthropic
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
user_request = """
Book a meeting next week with Alice to discuss the Q4 marketing campaign.
Send her a summary of what we accomplished in Q3 before the meeting.
"""
planning_prompt = f"""You are a personal assistant. Break down this request into a numbered list of specific steps:
Request: {user_request}
List each step as a clear action. Consider what information you need and what order makes sense."""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": planning_prompt}],
)
print(response.content[0].text)Out[3]:
TypeError: "Could not resolve authentication method. Expected either api_key or auth_token to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"
[31m---------------------------------------------------------------------------[39m
[31mTypeError[39m Traceback (most recent call last)
[36mCell[39m[36m [39m[32mIn[3][39m[32m, line 18[39m
[32m 7[39m user_request = [33m"""[39m
[32m 8[39m [33mBook a meeting next week with Alice to discuss the Q4 marketing campaign.[39m
[32m 9[39m [33mSend her a summary of what we accomplished in Q3 before the meeting.[39m
[32m 10[39m [33m"""[39m
[32m 12[39m planning_prompt = [33mf[39m[33m"""[39m[33mYou are a personal assistant. Break down this request into a numbered list of specific steps:[39m
[32m 13[39m
[32m 14[39m [33mRequest: [39m[38;5;132;01m{[39;00muser_request[38;5;132;01m}[39;00m
[32m 15[39m
[32m 16[39m [33mList each step as a clear action. Consider what information you need and what order makes sense.[39m[33m"""[39m
[32m---> [39m[32m18[39m response = [43mclient[49m[43m.[49m[43mmessages[49m[43m.[49m[43mcreate[49m[43m([49m
[32m 19[39m [43m [49m[43mmodel[49m[43m=[49m[33;43m"[39;49m[33;43mclaude-sonnet-4-5[39;49m[33;43m"[39;49m[43m,[49m
[32m 20[39m [43m [49m[43mmax_tokens[49m[43m=[49m[32;43m1024[39;49m[43m,[49m
[32m 21[39m [43m [49m[43mmessages[49m[43m=[49m[43m[[49m[43m{[49m[33;43m"[39;49m[33;43mrole[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[33;43m"[39;49m[33;43muser[39;49m[33;43m"[39;49m[43m,[49m[43m [49m[33;43m"[39;49m[33;43mcontent[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mplanning_prompt[49m[43m}[49m[43m][49m[43m,[49m
[32m 22[39m [43m)[49m
[32m 24[39m [38;5;28mprint[39m(response.content[[32m0[39m].text)
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_utils/_utils.py:282[39m, in [36mrequired_args.<locals>.inner.<locals>.wrapper[39m[34m(*args, **kwargs)[39m
[32m 280[39m msg = [33mf[39m[33m"[39m[33mMissing required argument: [39m[38;5;132;01m{[39;00mquote(missing[[32m0[39m])[38;5;132;01m}[39;00m[33m"[39m
[32m 281[39m [38;5;28;01mraise[39;00m [38;5;167;01mTypeError[39;00m(msg)
[32m--> [39m[32m282[39m [38;5;28;01mreturn[39;00m [43mfunc[49m[43m([49m[43m*[49m[43margs[49m[43m,[49m[43m [49m[43m*[49m[43m*[49m[43mkwargs[49m[43m)[49m
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/resources/messages/messages.py:996[39m, in [36mMessages.create[39m[34m(self, max_tokens, messages, model, cache_control, container, inference_geo, metadata, output_config, service_tier, stop_sequences, stream, system, temperature, thinking, tool_choice, tools, top_k, top_p, extra_headers, extra_query, extra_body, timeout)[39m
[32m 989[39m [38;5;28;01mif[39;00m model [38;5;129;01min[39;00m MODELS_TO_WARN_WITH_THINKING_ENABLED [38;5;129;01mand[39;00m thinking [38;5;129;01mand[39;00m thinking[[33m"[39m[33mtype[39m[33m"[39m] == [33m"[39m[33menabled[39m[33m"[39m:
[32m 990[39m warnings.warn(
[32m 991[39m [33mf[39m[33m"[39m[33mUsing Claude with [39m[38;5;132;01m{[39;00mmodel[38;5;132;01m}[39;00m[33m and [39m[33m'[39m[33mthinking.type=enabled[39m[33m'[39m[33m is deprecated. Use [39m[33m'[39m[33mthinking.type=adaptive[39m[33m'[39m[33m instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking[39m[33m"[39m,
[32m 992[39m [38;5;167;01mUserWarning[39;00m,
[32m 993[39m stacklevel=[32m3[39m,
[32m 994[39m )
[32m--> [39m[32m996[39m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[43m.[49m[43m_post[49m[43m([49m
[32m 997[39m [43m [49m[33;43m"[39;49m[33;43m/v1/messages[39;49m[33;43m"[39;49m[43m,[49m
[32m 998[39m [43m [49m[43mbody[49m[43m=[49m[43mmaybe_transform[49m[43m([49m
[32m 999[39m [43m [49m[43m{[49m
[32m 1000[39m [43m [49m[33;43m"[39;49m[33;43mmax_tokens[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mmax_tokens[49m[43m,[49m
[32m 1001[39m [43m [49m[33;43m"[39;49m[33;43mmessages[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mmessages[49m[43m,[49m
[32m 1002[39m [43m [49m[33;43m"[39;49m[33;43mmodel[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mmodel[49m[43m,[49m
[32m 1003[39m [43m [49m[33;43m"[39;49m[33;43mcache_control[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mcache_control[49m[43m,[49m
[32m 1004[39m [43m [49m[33;43m"[39;49m[33;43mcontainer[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mcontainer[49m[43m,[49m
[32m 1005[39m [43m [49m[33;43m"[39;49m[33;43minference_geo[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43minference_geo[49m[43m,[49m
[32m 1006[39m [43m [49m[33;43m"[39;49m[33;43mmetadata[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mmetadata[49m[43m,[49m
[32m 1007[39m [43m [49m[33;43m"[39;49m[33;43moutput_config[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43moutput_config[49m[43m,[49m
[32m 1008[39m [43m [49m[33;43m"[39;49m[33;43mservice_tier[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mservice_tier[49m[43m,[49m
[32m 1009[39m [43m [49m[33;43m"[39;49m[33;43mstop_sequences[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mstop_sequences[49m[43m,[49m
[32m 1010[39m [43m [49m[33;43m"[39;49m[33;43mstream[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mstream[49m[43m,[49m
[32m 1011[39m [43m [49m[33;43m"[39;49m[33;43msystem[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43msystem[49m[43m,[49m
[32m 1012[39m [43m [49m[33;43m"[39;49m[33;43mtemperature[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mtemperature[49m[43m,[49m
[32m 1013[39m [43m [49m[33;43m"[39;49m[33;43mthinking[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mthinking[49m[43m,[49m
[32m 1014[39m [43m [49m[33;43m"[39;49m[33;43mtool_choice[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mtool_choice[49m[43m,[49m
[32m 1015[39m [43m [49m[33;43m"[39;49m[33;43mtools[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mtools[49m[43m,[49m
[32m 1016[39m [43m [49m[33;43m"[39;49m[33;43mtop_k[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mtop_k[49m[43m,[49m
[32m 1017[39m [43m [49m[33;43m"[39;49m[33;43mtop_p[39;49m[33;43m"[39;49m[43m:[49m[43m [49m[43mtop_p[49m[43m,[49m
[32m 1018[39m [43m [49m[43m}[49m[43m,[49m
[32m 1019[39m [43m [49m[43mmessage_create_params[49m[43m.[49m[43mMessageCreateParamsStreaming[49m
[32m 1020[39m [43m [49m[38;5;28;43;01mif[39;49;00m[43m [49m[43mstream[49m
[32m 1021[39m [43m [49m[38;5;28;43;01melse[39;49;00m[43m [49m[43mmessage_create_params[49m[43m.[49m[43mMessageCreateParamsNonStreaming[49m[43m,[49m
[32m 1022[39m [43m [49m[43m)[49m[43m,[49m
[32m 1023[39m [43m [49m[43moptions[49m[43m=[49m[43mmake_request_options[49m[43m([49m
[32m 1024[39m [43m [49m[43mextra_headers[49m[43m=[49m[43mextra_headers[49m[43m,[49m[43m [49m[43mextra_query[49m[43m=[49m[43mextra_query[49m[43m,[49m[43m [49m[43mextra_body[49m[43m=[49m[43mextra_body[49m[43m,[49m[43m [49m[43mtimeout[49m[43m=[49m[43mtimeout[49m
[32m 1025[39m [43m [49m[43m)[49m[43m,[49m
[32m 1026[39m [43m [49m[43mcast_to[49m[43m=[49m[43mMessage[49m[43m,[49m
[32m 1027[39m [43m [49m[43mstream[49m[43m=[49m[43mstream[49m[43m [49m[38;5;129;43;01mor[39;49;00m[43m [49m[38;5;28;43;01mFalse[39;49;00m[43m,[49m
[32m 1028[39m [43m [49m[43mstream_cls[49m[43m=[49m[43mStream[49m[43m[[49m[43mRawMessageStreamEvent[49m[43m][49m[43m,[49m
[32m 1029[39m [43m[49m[43m)[49m
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:1364[39m, in [36mSyncAPIClient.post[39m[34m(self, path, cast_to, body, content, options, files, stream, stream_cls)[39m
[32m 1355[39m warnings.warn(
[32m 1356[39m [33m"[39m[33mPassing raw bytes as `body` is deprecated and will be removed in a future version. [39m[33m"[39m
[32m 1357[39m [33m"[39m[33mPlease pass raw bytes via the `content` parameter instead.[39m[33m"[39m,
[32m 1358[39m [38;5;167;01mDeprecationWarning[39;00m,
[32m 1359[39m stacklevel=[32m2[39m,
[32m 1360[39m )
[32m 1361[39m opts = FinalRequestOptions.construct(
[32m 1362[39m method=[33m"[39m[33mpost[39m[33m"[39m, url=path, json_data=body, content=content, files=to_httpx_files(files), **options
[32m 1363[39m )
[32m-> [39m[32m1364[39m [38;5;28;01mreturn[39;00m cast(ResponseT, [38;5;28;43mself[39;49m[43m.[49m[43mrequest[49m[43m([49m[43mcast_to[49m[43m,[49m[43m [49m[43mopts[49m[43m,[49m[43m [49m[43mstream[49m[43m=[49m[43mstream[49m[43m,[49m[43m [49m[43mstream_cls[49m[43m=[49m[43mstream_cls[49m[43m)[49m)
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:1058[39m, in [36mSyncAPIClient.request[39m[34m(self, cast_to, options, stream, stream_cls)[39m
[32m 1055[39m options = [38;5;28mself[39m._prepare_options(options)
[32m 1057[39m remaining_retries = max_retries - retries_taken
[32m-> [39m[32m1058[39m request = [38;5;28;43mself[39;49m[43m.[49m[43m_build_request[49m[43m([49m[43moptions[49m[43m,[49m[43m [49m[43mretries_taken[49m[43m=[49m[43mretries_taken[49m[43m)[49m
[32m 1059[39m [38;5;28mself[39m._prepare_request(request)
[32m 1061[39m kwargs: HttpxSendArgs = {}
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:521[39m, in [36mBaseClient._build_request[39m[34m(self, options, retries_taken)[39m
[32m 518[39m [38;5;28;01melse[39;00m:
[32m 519[39m [38;5;28;01mraise[39;00m [38;5;167;01mRuntimeError[39;00m([33mf[39m[33m"[39m[33mUnexpected JSON data type, [39m[38;5;132;01m{[39;00m[38;5;28mtype[39m(json_data)[38;5;132;01m}[39;00m[33m, cannot merge with `extra_body`[39m[33m"[39m)
[32m--> [39m[32m521[39m headers = [38;5;28;43mself[39;49m[43m.[49m[43m_build_headers[49m[43m([49m[43moptions[49m[43m,[49m[43m [49m[43mretries_taken[49m[43m=[49m[43mretries_taken[49m[43m)[49m
[32m 522[39m params = _merge_mappings([38;5;28mself[39m.default_query, options.params)
[32m 523[39m content_type = headers.get([33m"[39m[33mContent-Type[39m[33m"[39m)
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:451[39m, in [36mBaseClient._build_headers[39m[34m(self, options, retries_taken)[39m
[32m 441[39m custom_headers = options.headers [38;5;129;01mor[39;00m {}
[32m 442[39m headers_dict = _merge_mappings(
[32m 443[39m {
[32m 444[39m [33m"[39m[33mx-stainless-timeout[39m[33m"[39m: [38;5;28mstr[39m(options.timeout.read)
[32m (...)[39m[32m 449[39m custom_headers,
[32m 450[39m )
[32m--> [39m[32m451[39m [38;5;28;43mself[39;49m[43m.[49m[43m_validate_headers[49m[43m([49m[43mheaders_dict[49m[43m,[49m[43m [49m[43mcustom_headers[49m[43m)[49m
[32m 453[39m [38;5;66;03m# headers are case-insensitive while dictionaries are not.[39;00m
[32m 454[39m headers = httpx.Headers(headers_dict)
[36mFile [39m[32m/private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_client.py:196[39m, in [36mAnthropic._validate_headers[39m[34m(self, headers, custom_headers)[39m
[32m 193[39m [38;5;28;01mif[39;00m headers.get([33m"[39m[33mAuthorization[39m[33m"[39m) [38;5;129;01mor[39;00m [38;5;28misinstance[39m(custom_headers.get([33m"[39m[33mAuthorization[39m[33m"[39m), Omit):
[32m 194[39m [38;5;28;01mreturn[39;00m
[32m--> [39m[32m196[39m [38;5;28;01mraise[39;00m [38;5;167;01mTypeError[39;00m(
[32m 197[39m [33m'[39m[33m"[39m[33mCould not resolve authentication method. Expected either api_key or auth_token to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted[39m[33m"[39m[33m'[39m
[32m 198[39m )
[31mTypeError[39m: "Could not resolve authentication method. Expected either api_key or auth_token to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"The assistant generates this plan:
To complete this request, I'll need to:
1. Check Alice's calendar availability for next week
2. Check your calendar for the same period
3. Identify overlapping time slots
4. Retrieve your Q3 marketing notes and accomplishments
5. Create a concise summary of Q3 work
6. Send the summary email to Alice
7. Schedule the meeting once a time is confirmed
8. Provide you with confirmation of both actions
I notice there are dependencies here. I should send the summary before
scheduling the meeting so Alice has context when she receives the invite.
I also need to find availability before I can schedule anything.Notice how the assistant has identified dependencies on its own. You can't schedule a meeting before checking availability, and you should send the summary before the meeting gets scheduled so Alice has context when she accepts. This kind of dependency awareness is what makes planning valuable.