Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions python/01-learn/01-first-agent/01-first-agent.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"| Agent structure | Single agent |\n",
"| Model provider | Amazon Bedrock |\n",
"| Model | Anthropic Claude Sonnet 4.5 |\n",
"| Native tools used | calculator |\n",
"| Custom tools created | weather, websearch |\n",
"| Native tools used | none |\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This row is now correct — and it makes the surrounding prose self-contradictory, since there genuinely is no built-in tool left in the notebook. Three spots in this file and two in its README still promise one:

  • :32 — "* Add built-in and custom tools"
  • :442 — "you built your first Strands agent, added built-in and custom tools…"
  • :11 — "…from a simple agent to one that uses built-in and custom tools"
  • README.md:49 — "- Adding built-in and custom tools"
  • README.md:3 — same framing in the opening paragraph

Dropping "built-in and" in each covers it. README.md:13 and :44 are already fixed, so this is the last of that thread.

"| Custom tools created | calculator, weather, websearch |\n",
"| Strands features | Agent, @tool decorator, BedrockModel |"
]
},
Expand Down Expand Up @@ -59,7 +59,7 @@
"source": [
"# Install Strands using pip\n",
"\n",
"!pip install strands-agents strands-agents-tools"
"!pip install strands-agents"
]
},
{
Expand Down Expand Up @@ -113,7 +113,7 @@
"source": [
"### Add tools to the agent\n",
"\n",
"The [strands-agents-tools](https://github.com/strands-agents/tools) repository provides some built-in tools that you can import. You can also create custom tools using the `@tool` decorator. The function's typed arguments and docstring are not just documentation. Strands turns them into the tool's input schema, which is what the model reads to decide when and how to call the tool. We can create agents with built-in and custom tools. For instance, adding the built-in tool of a calculator and a custom tool for getting the weather, you get the following architecture:\n",
"You create custom tools using the `@tool` decorator. The [strands-agents-tools](https://github.com/strands-agents/tools) repository also provides pre-built tools that you can import. The function's typed arguments and docstring are not just documentation. Strands turns them into the tool's input schema, which is what the model reads to decide when and how to call the tool. We can create agents with built-in and custom tools. For instance, defining a small calculator tool and a custom tool for getting the weather, you get the following architecture:\n",
"<div style=\"text-align:center\">\n",
" <img src=\"images/agent_with_tools.png\" width=\"75%\" />\n",
"</div>\n",
Expand All @@ -128,8 +128,29 @@
"metadata": {},
"outputs": [],
"source": [
"import operator\n",
"\n",
"from strands import Agent, tool\n",
"from strands_tools import calculator # Import the calculator tool\n",
"\n",
"_OPS = {\n",
" \"+\": operator.add,\n",
" \"-\": operator.sub,\n",
" \"*\": operator.mul,\n",
" \"/\": operator.truediv,\n",
" \"**\": operator.pow,\n",
"}\n",
"\n",
"\n",
"@tool\n",
"def calculator(a: float, b: float, op: str) -> float:\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Non-blocking, but this is the copy every newcomer reads first, so it's the one worth getting right. op is a stringly-typed enum whose valid set lives only in the docstring prose — so it isn't in the schema the model sees, and an unsupported op becomes a bare KeyError.

I generated both specs against 1.50.2. Same function, same docstring, only the annotation differs:

op: str (as shipped) op: Literal["+","-","*","/","**"]
properties.op {"description": "One of \"+\"…", "type": "string"} {…, "enum": ["+","-","*","/","**"], "type": "string"}
model sees on op="^" Error: KeyError - '^' Input should be '+', '-', '*', '/' or '**' [type=literal_error, input_value='^']

Both are recoverable errors — nothing crashes — so this is about whether the model is told the valid set up front instead of discovering it by failing a call.

Three things make it more than taste here: the SDK solves this exact shape this way (strands/vended_tools/file_editor/file_editor.py:60command: Literal["view","create","str_replace","insert"], prose and Literal); the 02-custom-tools guide hand-writes "enum": ["circle","rectangle"] for a constrained string; and :116 of this very notebook teaches "A tool's typed arguments and docstring become the schema the model reads" — then the next cell ships a parameter whose valid set is deliberately outside the type.

Suggested change
"def calculator(a: float, b: float, op: str) -> float:\n",
"def calculator(a: float, b: float, op: Literal[\"+\", \"-\", \"*\", \"/\", \"**\"]) -> float:\n",

Needs from typing import Literal in the same cell. If you take it, it wants a sweep — the tool body is byte-identical in 12 files (a80187b5ec83), and the copies drifting apart would be worse than leaving all twelve as-is.

(For what it's worth on the bigger question: I think the two-operand redesign is the right call and teaches better than the AST walker did — 30 lines about ast dispatch in the notebook where a reader is learning what a tool is was the wrong lesson. Also confirmed -> float is pure documentation: the generated schema is byte-identical with -> float, no annotation, or a deliberately wrong one.)

" \"\"\"Apply an arithmetic operator to two numbers.\n",
"\n",
" Args:\n",
" a: Left operand.\n",
" b: Right operand.\n",
" op: One of \"+\", \"-\", \"*\", \"/\", \"**\".\n",
" \"\"\"\n",
" return _OPS[op](a, b)\n",
"\n",
"# Create a custom tool\n",
"@tool\n",
Expand Down Expand Up @@ -169,7 +190,7 @@
"outputs": [],
"source": [
"# Alternatively, you can invoke the tool directly like so:\n",
"agent.tool.calculator(expression=\"sin(x)\", mode=\"derive\", wrt=\"x\", order=2)"
"agent.tool.calculator(a=144, b=0.5, op=\"**\")"
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions python/01-learn/01-first-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This tutorial walks you through building your first Strands agent. You start wit
|------------------------|----------------------------------------------------------|
| **Strands Features** | `Agent`, `@tool` decorator, `BedrockModel` |
| **Agent Pattern** | Single agent |
| **Tools** | Built-in (`calculator`) and custom (`@tool`) |
| **Tools** | Custom tools via the `@tool` decorator |
| **Model** | Claude Sonnet 4.5 on Amazon Bedrock (`us.anthropic.claude-sonnet-4-5-20250929-v1:0`) |

## Key Concepts
Expand Down Expand Up @@ -41,7 +41,7 @@ The command-line script runs the RecipeBot interactively:

1. **Install dependencies:**
```bash
pip install strands-agents strands-agents-tools
pip install strands-agents
```

2. **Run the notebook:** open [`01-first-agent.ipynb`](./01-first-agent.ipynb) and run the cells in order. It covers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"| Agent structure | Single agent |\n",
"| Model provider | Amazon Bedrock |\n",
"| Model | Anthropic Claude Sonnet 4.5 |\n",
"| Native tools used | current_time, calculator |\n",
"| Custom tools created | create_appointment, list_appointments, update_appointment |\n",
"| Native tools used | none |\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Same as 01-first-agent: this row is right now, and :526 still says "provide it with built-in and custom tools" a few cells down, which no longer describes the notebook — it defines both tools itself. Worth dropping "built-in and" there too.

The heading fix landed well, by the way — "Define the tools our agent needs" reads correctly, and current_time being defined here and wired into tools=[...] closes the prompt/tool mismatch I flagged last round.

For context one level up: python/01-learn/02-tools-and-mcp/README.md:11,17 still says "Built-in tools: Ready-made tools from the strands-agents-tools package, such as calculator and current_time", which now contradicts both sub-notebooks. Outside this file, so I'm not asking for it here — flagging in case you want it in the same sweep.

"| Custom tools created | calculator, current_time, create_appointment, list_appointments, update_appointment |\n",
"| Strands features | @tool decorator, TOOL_SPEC, BedrockModel extended thinking |\n"
]
},
Expand Down Expand Up @@ -541,7 +541,7 @@
"outputs": [],
"source": [
"system_prompt = \"\"\"You are a helpful personal assistant that specializes in managing my appointments and calendar. \n",
"You have access to appointment management tools, a calculator, and can check the current time to help me organize my schedule effectively. \n",
"You have access to appointment management tools, a calculator, and a clock to help me organize my schedule effectively. \n",
"Always provide the appointment id so that I can update it if required\"\"\""
]
},
Expand Down Expand Up @@ -592,9 +592,9 @@
}
},
"source": [
"#### Import built-in tools\n",
"#### Define the tools our agent needs\n",
"\n",
"The next step to build our agent is to import our Strands Agents built-in tools. Strands Agents provides a set of commonly used built-in tools in the optional package `strands-agents-tools`, which you import as `strands_tools`. You have tools for RAG, memory, file operations, code interpretation and others available in this repo. For our example we will use the `current_time` tool to provide our agent with the information about the current time and the `calculator` tool to do some math"
"The next step is to define the remaining tools our agent needs. Strands Agents also offers an optional package of pre-built tools, `strands-agents-tools`, with tools for RAG, memory, file operations, code interpretation and more. For this example we keep everything self-contained and define two small tools of our own: a `calculator` to do some math and a `current_time` tool so the agent can resolve relative dates like \"tomorrow\"."
]
},
{
Expand All @@ -608,7 +608,36 @@
},
"outputs": [],
"source": [
"from strands_tools import calculator, current_time"
"import operator\n",
"from datetime import datetime, timezone\n",
"\n",
"from strands import tool\n",
"\n",
"_OPS = {\n",
" \"+\": operator.add,\n",
" \"-\": operator.sub,\n",
" \"*\": operator.mul,\n",
" \"/\": operator.truediv,\n",
" \"**\": operator.pow,\n",
"}\n",
"\n",
"\n",
"@tool\n",
"def calculator(a: float, b: float, op: str) -> float:\n",
" \"\"\"Apply an arithmetic operator to two numbers.\n",
"\n",
" Args:\n",
" a: Left operand.\n",
" b: Right operand.\n",
" op: One of \"+\", \"-\", \"*\", \"/\", \"**\".\n",
" \"\"\"\n",
" return _OPS[op](a, b)\n",
"\n",
"\n",
"@tool\n",
"def current_time() -> str:\n",
" \"\"\"Get the current UTC date and time in ISO 8601 format.\"\"\"\n",
" return datetime.now(timezone.utc).isoformat()"
]
},
{
Expand Down Expand Up @@ -640,8 +669,8 @@
" model=model,\n",
" system_prompt=system_prompt,\n",
" tools=[\n",
" current_time,\n",
" calculator,\n",
" current_time,\n",
" create_appointment,\n",
" list_appointments,\n",
" update_appointment,\n",
Expand Down Expand Up @@ -949,18 +978,18 @@
},
"outputs": [],
"source": [
"current_time_result = agent.tool.current_time()\n",
"print(\"Current Time direct tool call result:\")\n",
"print(current_time_result)\n",
"current_time_direct_tool_messages = agent.messages[-4:]\n",
"print(\"Current Time direct tool call messages:\")\n",
"print(current_time_direct_tool_messages)\n",
"calculator_result = agent.tool.calculator(a=2, b=2, op=\"+\")\n",
"print(\"Calculator direct tool call result:\")\n",
"print(calculator_result)\n",
"calculator_direct_tool_messages = agent.messages[-4:]\n",
"print(\"Calculator direct tool call messages:\")\n",
"print(calculator_direct_tool_messages)\n",
"\n",
"agent.record_direct_tool_call = False # Set the record_direct_tool_call to False\n",
"agent.tool.list_appointments()\n",
"after_disable_record_messages = agent.messages[-4:]\n",
"print(\"After disabling record direct tool call messages, history should not have changed:\")\n",
"print(current_time_direct_tool_messages == after_disable_record_messages)"
"print(calculator_direct_tool_messages == after_disable_record_messages)"
]
},
{
Expand Down Expand Up @@ -1061,16 +1090,16 @@
"outputs": [],
"source": [
"thinking_system_prompt = \"\"\"You are a helpful personal assistant that specializes in managing my appointments and calendar. \n",
"You have access to appointment management tools, a calculator, and can check the current time to help me organize my schedule effectively. \n",
"You have access to appointment management tools, a calculator, and a clock to help me organize my schedule effectively. \n",
"You think through your problem, step by step, to come up with an answer.\n",
"Always provide the appointment id so that I can update it if required\"\"\"\n",
"\n",
"thinking_agent = Agent(\n",
" model=thinking_model,\n",
" system_prompt=thinking_system_prompt,\n",
" tools=[\n",
" current_time,\n",
" calculator,\n",
" current_time,\n",
" create_appointment,\n",
" list_appointments,\n",
" update_appointment,\n",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
strands-agents
strands-agents-tools
strands-agents
1 change: 0 additions & 1 deletion python/01-learn/04-streaming/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
fastapi
uv
strands-agents
strands-agents-tools
uvicorn
pydantic
httpx
42 changes: 37 additions & 5 deletions python/01-learn/04-streaming/streaming.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"|--------------------|---------------------------------------------------|\n",
"|Feature used |async iterators, callback handlers |\n",
"|Agent Structure |single agent architecture |\n",
"|Native tools used |calculator |\n",
"|Custom tools created|Weather forecast |\n",
"|Native tools used |none |\n",
"|Custom tools created|calculator, Weather forecast |\n",
"\n",
"</div>"
]
Expand Down Expand Up @@ -87,7 +87,39 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "import asyncio\n\nimport httpx\nimport uvicorn\nfrom fastapi import FastAPI\nfrom fastapi.responses import StreamingResponse\nfrom pydantic import BaseModel\nfrom strands import Agent, tool\nfrom strands_tools import calculator"
"source": [
"import operator\n",
"\n",
"import asyncio\n",
"\n",
"import httpx\n",
"import uvicorn\n",
"from fastapi import FastAPI\n",
"from fastapi.responses import StreamingResponse\n",
"from pydantic import BaseModel\n",
"from strands import Agent, tool\n",
"\n",
"_OPS = {\n",
" \"+\": operator.add,\n",
" \"-\": operator.sub,\n",
" \"*\": operator.mul,\n",
" \"/\": operator.truediv,\n",
" \"**\": operator.pow,\n",
"}\n",
"\n",
"\n",
"@tool\n",
"def calculator(a: float, b: float, op: str) -> float:\n",
" \"\"\"Apply an arithmetic operator to two numbers.\n",
"\n",
" Args:\n",
" a: Left operand.\n",
" b: Right operand.\n",
" op: One of \"+\", \"-\", \"*\", \"/\", \"**\".\n",
" \"\"\"\n",
" return _OPS[op](a, b)\n",
"\n"
]
},
{
"cell_type": "markdown",
Expand All @@ -107,7 +139,7 @@
"source": [
"### Creating and invoking agent with stream_async\n",
"\n",
"Let's now create our agent with a built-in calculator tool and no `callback_handler`. We will use the `stream_async` method to iterate over the streamed agent events"
"Let's now create our agent with the calculator tool defined above and no `callback_handler`. We will use the `stream_async` method to iterate over the streamed agent events"
]
},
{
Expand Down Expand Up @@ -378,4 +410,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
Loading