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
Chapter 26Example: Planning with Our Assistant › The Planning Phase
Reading section 3 of 9 · The Planning Phase · 2 minSection 3 of 9 · 2 min

Book contents

Section 3 of 9 · 2 min read

Example: Planning with Our Assistant › The Planning PhaseOne section · Sections in this chapter3 / 9 ↓

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"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 18
      7 user_request = """
      8 Book a meeting next week with Alice to discuss the Q4 marketing campaign.
      9 Send her a summary of what we accomplished in Q3 before the meeting.
     10 """
     12 planning_prompt = f"""You are a personal assistant. Break down this request into a numbered list of specific steps:
     13 
     14 Request: {user_request}
     15 
     16 List each step as a clear action. Consider what information you need and what order makes sense."""
---> 18 response = client.messages.create(
     19     model="claude-sonnet-4-5",
     20     max_tokens=1024,
     21     messages=[{"role": "user", "content": planning_prompt}],
     22 )
     24 print(response.content[0].text)

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_utils/_utils.py:282, in required_args.<locals>.inner.<locals>.wrapper(*args, **kwargs)
    280             msg = f"Missing required argument: {quote(missing[0])}"
    281     raise TypeError(msg)
--> 282 return func(*args, **kwargs)

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/resources/messages/messages.py:996, in Messages.create(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)
    989 if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled":
    990     warnings.warn(
    991         f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking",
    992         UserWarning,
    993         stacklevel=3,
    994     )
--> 996 return self._post(
    997     "/v1/messages",
    998     body=maybe_transform(
    999         {
   1000             "max_tokens": max_tokens,
   1001             "messages": messages,
   1002             "model": model,
   1003             "cache_control": cache_control,
   1004             "container": container,
   1005             "inference_geo": inference_geo,
   1006             "metadata": metadata,
   1007             "output_config": output_config,
   1008             "service_tier": service_tier,
   1009             "stop_sequences": stop_sequences,
   1010             "stream": stream,
   1011             "system": system,
   1012             "temperature": temperature,
   1013             "thinking": thinking,
   1014             "tool_choice": tool_choice,
   1015             "tools": tools,
   1016             "top_k": top_k,
   1017             "top_p": top_p,
   1018         },
   1019         message_create_params.MessageCreateParamsStreaming
   1020         if stream
   1021         else message_create_params.MessageCreateParamsNonStreaming,
   1022     ),
   1023     options=make_request_options(
   1024         extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
   1025     ),
   1026     cast_to=Message,
   1027     stream=stream or False,
   1028     stream_cls=Stream[RawMessageStreamEvent],
   1029 )

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:1364, in SyncAPIClient.post(self, path, cast_to, body, content, options, files, stream, stream_cls)
   1355     warnings.warn(
   1356         "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
   1357         "Please pass raw bytes via the `content` parameter instead.",
   1358         DeprecationWarning,
   1359         stacklevel=2,
   1360     )
   1361 opts = FinalRequestOptions.construct(
   1362     method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options
   1363 )
-> 1364 return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:1058, in SyncAPIClient.request(self, cast_to, options, stream, stream_cls)
   1055 options = self._prepare_options(options)
   1057 remaining_retries = max_retries - retries_taken
-> 1058 request = self._build_request(options, retries_taken=retries_taken)
   1059 self._prepare_request(request)
   1061 kwargs: HttpxSendArgs = {}

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:521, in BaseClient._build_request(self, options, retries_taken)
    518     else:
    519         raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`")
--> 521 headers = self._build_headers(options, retries_taken=retries_taken)
    522 params = _merge_mappings(self.default_query, options.params)
    523 content_type = headers.get("Content-Type")

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_base_client.py:451, in BaseClient._build_headers(self, options, retries_taken)
    441 custom_headers = options.headers or {}
    442 headers_dict = _merge_mappings(
    443     {
    444         "x-stainless-timeout": str(options.timeout.read)
   (...)    449     custom_headers,
    450 )
--> 451 self._validate_headers(headers_dict, custom_headers)
    453 # headers are case-insensitive while dictionaries are not.
    454 headers = httpx.Headers(headers_dict)

File /private/tmp/mb-ai-style-scanner/.venv/lib/python3.11/site-packages/anthropic/_client.py:196, in Anthropic._validate_headers(self, headers, custom_headers)
    193 if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit):
    194     return
--> 196 raise TypeError(
    197     '"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"'
    198 )

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"

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.