Skip to content
Draft
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
266 changes: 266 additions & 0 deletions build_output.log

Large diffs are not rendered by default.

180 changes: 180 additions & 0 deletions docs/user-guide/deploy/operating-agents-in-production.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,186 @@ except Exception as e:
handle_agent_error(e)
```

### Execution Limits

Production agents should have clear execution boundaries to prevent runaway operations, control costs, and ensure predictable resource consumption. Without proper limits, agents can enter infinite loops, consume excessive tokens, or run indefinitely—all of which impact reliability and cost in production environments.

#### Agent Loop Iteration Limits

Limit the number of model calls per agent invocation using hooks. This prevents agents from getting stuck in reasoning loops or making excessive API calls:

=== "Python"

```python
from strands import Agent
from strands.hooks import HookProvider, HookRegistry, BeforeModelCallEvent

class LimitModelCalls(HookProvider):
"""Limit the number of model calls per invocation."""

def __init__(self, max_calls: int = 10):
self.max_calls = max_calls
self.call_count = 0

def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeModelCallEvent, self.check_limit)

def check_limit(self, event: BeforeModelCallEvent) -> None:
self.call_count += 1
if self.call_count > self.max_calls:
raise RuntimeError(f"Maximum model calls ({self.max_calls}) exceeded")
Copy link
Contributor

Choose a reason for hiding this comment

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

Issue: LimitModelCalls doesn't reset call_count between invocations.

If the same agent/hook instance is reused, the count will persist and may incorrectly limit subsequent invocations. The LimitToolCounts example correctly handles this by resetting on BeforeInvocationEvent.

Suggestion: Add a reset mechanism:

def register_hooks(self, registry: HookRegistry) -> None:
    registry.add_callback(BeforeInvocationEvent, self.reset_count)
    registry.add_callback(BeforeModelCallEvent, self.check_limit)

def reset_count(self, event: BeforeInvocationEvent) -> None:
    self.call_count = 0


agent = Agent(hooks=[LimitModelCalls(max_calls=15)])
```

{{ ts_not_supported_code("Hook-based model call limiting is not yet available in TypeScript SDK") }}

See [Hooks](../../user-guide/concepts/agents/hooks.md) for more information on implementing custom hooks.

#### Tool Invocation Limits

Limit how many times specific tools can be called to prevent excessive external API usage, rate limiting issues, or runaway tool execution:

=== "Python"

```python
from strands.hooks import HookProvider, HookRegistry, BeforeToolCallEvent, BeforeInvocationEvent

class LimitToolCounts(HookProvider):
"""Limit tool invocations per agent request."""

def __init__(self, max_tool_counts: dict[str, int]):
self.max_tool_counts = max_tool_counts
self.tool_counts = {}

def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeInvocationEvent, self.reset_counts)
registry.add_callback(BeforeToolCallEvent, self.check_tool_limit)

def reset_counts(self, event: BeforeInvocationEvent) -> None:
self.tool_counts = {}

def check_tool_limit(self, event: BeforeToolCallEvent) -> None:
tool_name = event.tool_use["name"]
count = self.tool_counts.get(tool_name, 0) + 1
self.tool_counts[tool_name] = count

if max_count := self.max_tool_counts.get(tool_name):
if count > max_count:
event.cancel_tool = f"Tool '{tool_name}' limit exceeded"

# Limit expensive API calls
agent = Agent(
tools=[search_api, database_query],
hooks=[LimitToolCounts({"search_api": 5, "database_query": 10})]
)
```

{{ ts_not_supported_code("Hook-based tool limiting is not yet available in TypeScript SDK") }}

See the [Limit Tool Counts](../../user-guide/concepts/agents/hooks.md#limit-tool-counts) cookbook example for a complete implementation.

#### Token Consumption Budgets

Control token usage at the model level by configuring `max_tokens` to limit response lengths. This helps manage costs and ensures the model doesn't generate excessively long responses:

=== "Python"

```python
from strands import Agent
from strands.models import BedrockModel

# Configure model with token limits
model = BedrockModel(
model_id="us.amazon.nova-premier-v1:0",
max_tokens=2000 # Limit response tokens
)

agent = Agent(model=model)
```

{{ ts_not_supported_code() }}

When the model's response exceeds the configured token limit, the agent loop terminates with a `max_tokens` stop reason. See [Agent Loop - Stop Reasons](../../user-guide/concepts/agents/agent-loop.md#stop-reasons) for details on handling this condition.

#### Execution Timeouts

Implement wall-clock time limits to ensure agent invocations complete within acceptable timeframes. This can be achieved through hook-based approaches or external timeout wrappers:

=== "Python"

```python
import asyncio
from strands import Agent

async def invoke_with_timeout(agent: Agent, prompt: str, timeout_seconds: float):
"""Execute agent with a timeout."""
try:
return await asyncio.wait_for(
asyncio.to_thread(agent, prompt),
timeout=timeout_seconds
)
except asyncio.TimeoutError:
raise RuntimeError(f"Agent execution exceeded {timeout_seconds}s timeout")

agent = Agent()

# 60 second timeout
result = await invoke_with_timeout(agent, "Analyze this data", timeout_seconds=60.0)
```

{{ ts_not_supported_code() }}

#### Multi-Agent Safety Mechanisms

When using multi-agent patterns, leverage the built-in safety mechanisms to prevent runaway orchestration:

**Swarm Pattern**

Swarms include configurable limits for handoffs, iterations, and timeouts:

=== "Python"

```python
from strands.multiagent import Swarm

swarm = Swarm(
[agent1, agent2, agent3],
max_handoffs=20, # Limit agent-to-agent transfers
max_iterations=20, # Cap total execution iterations
execution_timeout=900.0, # 15 minute total timeout
node_timeout=300.0 # 5 minute per-agent timeout
)
```

{{ ts_not_supported_code("Swarm pattern is not yet available in TypeScript SDK") }}

See [Swarm - Safety Mechanisms](../../user-guide/concepts/multi-agent/swarm.md#safety-mechanisms) for details.

**Graph Pattern**

Graphs support execution limits especially important for cyclic workflows:

=== "Python"

```python
from strands.multiagent import GraphBuilder

builder = GraphBuilder()
# ... add nodes and edges ...

# Set execution limits for production
builder.set_max_node_executions(10) # Limit total node executions
builder.set_execution_timeout(300) # 5 minute total timeout
builder.set_node_timeout(60) # 1 minute per-node timeout

graph = builder.build()
```

{{ ts_not_supported_code("Graph pattern is not yet available in TypeScript SDK") }}

See [Graph - GraphBuilder](../../user-guide/concepts/multi-agent/graph.md#3-graphbuilder) for configuration options.

## Deployment Patterns

Strands agents can be deployed using various options from serverless to dedicated server machines.
Expand Down
12 changes: 12 additions & 0 deletions npm_install.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

added 246 packages, and audited 247 packages in 28s

38 packages are looking for funding
run `npm fund` for details

2 high severity vulnerabilities

To address all issues (including breaking changes), run:
npm audit fix --force

Run `npm audit` for details.
111 changes: 111 additions & 0 deletions pip_deps.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
Collecting click<8.3.0
Using cached click-8.2.1-py3-none-any.whl.metadata (2.5 kB)
Requirement already satisfied: mike~=2.1.3 in ./.venv/lib/python3.13/site-packages (2.1.3)
Requirement already satisfied: mkdocs~=1.6.1 in ./.venv/lib/python3.13/site-packages (1.6.1)
Collecting mkdocs-macros-plugin~=1.3.7
Using cached mkdocs_macros_plugin-1.3.9-py3-none-any.whl.metadata (8.1 kB)
Collecting mkdocs-material~=9.6.12
Using cached mkdocs_material-9.6.23-py3-none-any.whl.metadata (19 kB)
Collecting mkdocstrings-python~=1.16.10
Using cached mkdocstrings_python-1.16.12-py3-none-any.whl.metadata (5.6 kB)
Collecting mkdocs-llmstxt~=0.2.0
Using cached mkdocs_llmstxt-0.2.0-py3-none-any.whl.metadata (5.4 kB)
Requirement already satisfied: pymdown-extensions>=10.16.1 in ./.venv/lib/python3.13/site-packages (10.21)
Requirement already satisfied: importlib-metadata in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (8.7.1)
Requirement already satisfied: importlib-resources in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (6.5.2)
Requirement already satisfied: jinja2>=2.7 in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (3.1.6)
Requirement already satisfied: pyparsing>=3.0 in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (3.3.2)
Requirement already satisfied: pyyaml>=5.1 in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (6.0.3)
Requirement already satisfied: pyyaml-env-tag in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (1.1)
Requirement already satisfied: verspec in ./.venv/lib/python3.13/site-packages (from mike~=2.1.3) (0.1.0)
Requirement already satisfied: ghp-import>=1.0 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (2.1.0)
Requirement already satisfied: markdown>=3.3.6 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (3.10.2)
Requirement already satisfied: markupsafe>=2.0.1 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (3.0.3)
Requirement already satisfied: mergedeep>=1.3.4 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (1.3.4)
Requirement already satisfied: mkdocs-get-deps>=0.2.0 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (0.2.0)
Requirement already satisfied: packaging>=20.5 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (26.0)
Requirement already satisfied: pathspec>=0.11.1 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (1.0.4)
Requirement already satisfied: watchdog>=2.0 in ./.venv/lib/python3.13/site-packages (from mkdocs~=1.6.1) (6.0.0)
Requirement already satisfied: hjson in ./.venv/lib/python3.13/site-packages (from mkdocs-macros-plugin~=1.3.7) (3.1.0)
Requirement already satisfied: python-dateutil in ./.venv/lib/python3.13/site-packages (from mkdocs-macros-plugin~=1.3.7) (2.9.0.post0)
Requirement already satisfied: super-collections in ./.venv/lib/python3.13/site-packages (from mkdocs-macros-plugin~=1.3.7) (0.6.2)
Requirement already satisfied: termcolor in ./.venv/lib/python3.13/site-packages (from mkdocs-macros-plugin~=1.3.7) (3.3.0)
Requirement already satisfied: babel~=2.10 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (2.18.0)
Collecting backrefs~=5.7.post1 (from mkdocs-material~=9.6.12)
Using cached backrefs-5.9-py313-none-any.whl.metadata (3.2 kB)
Requirement already satisfied: colorama~=0.4 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (0.4.6)
Requirement already satisfied: mkdocs-material-extensions~=1.3 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (1.3.1)
Requirement already satisfied: paginate~=0.5 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (0.5.7)
Requirement already satisfied: pygments~=2.16 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (2.19.2)
Requirement already satisfied: requests~=2.26 in ./.venv/lib/python3.13/site-packages (from mkdocs-material~=9.6.12) (2.32.5)
Collecting mkdocstrings>=0.28.3 (from mkdocstrings-python~=1.16.10)
Using cached mkdocstrings-1.0.3-py3-none-any.whl.metadata (15 kB)
Collecting mkdocs-autorefs>=1.4 (from mkdocstrings-python~=1.16.10)
Using cached mkdocs_autorefs-1.4.4-py3-none-any.whl.metadata (14 kB)
Collecting griffe>=1.6.2 (from mkdocstrings-python~=1.16.10)
Using cached griffe-2.0.0-py3-none-any.whl.metadata (12 kB)
Collecting beautifulsoup4>=4.12 (from mkdocs-llmstxt~=0.2.0)
Using cached beautifulsoup4-4.14.3-py3-none-any.whl.metadata (3.8 kB)
Collecting markdownify>=0.14 (from mkdocs-llmstxt~=0.2.0)
Using cached markdownify-1.2.2-py3-none-any.whl.metadata (9.9 kB)
Collecting mdformat>=0.7.21 (from mkdocs-llmstxt~=0.2.0)
Using cached mdformat-1.0.0-py3-none-any.whl.metadata (9.6 kB)
Requirement already satisfied: charset_normalizer<4,>=2 in ./.venv/lib/python3.13/site-packages (from requests~=2.26->mkdocs-material~=9.6.12) (3.4.4)
Requirement already satisfied: idna<4,>=2.5 in ./.venv/lib/python3.13/site-packages (from requests~=2.26->mkdocs-material~=9.6.12) (3.11)
Requirement already satisfied: urllib3<3,>=1.21.1 in ./.venv/lib/python3.13/site-packages (from requests~=2.26->mkdocs-material~=9.6.12) (2.6.3)
Requirement already satisfied: certifi>=2017.4.17 in ./.venv/lib/python3.13/site-packages (from requests~=2.26->mkdocs-material~=9.6.12) (2026.1.4)
Collecting soupsieve>=1.6.1 (from beautifulsoup4>=4.12->mkdocs-llmstxt~=0.2.0)
Using cached soupsieve-2.8.3-py3-none-any.whl.metadata (4.6 kB)
Collecting typing-extensions>=4.0.0 (from beautifulsoup4>=4.12->mkdocs-llmstxt~=0.2.0)
Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
Collecting griffecli==2.0.0 (from griffe>=1.6.2->mkdocstrings-python~=1.16.10)
Using cached griffecli-2.0.0-py3-none-any.whl.metadata (1.2 kB)
Collecting griffelib==2.0.0 (from griffe>=1.6.2->mkdocstrings-python~=1.16.10)
Using cached griffelib-2.0.0-py3-none-any.whl.metadata (1.3 kB)
Requirement already satisfied: six<2,>=1.15 in ./.venv/lib/python3.13/site-packages (from markdownify>=0.14->mkdocs-llmstxt~=0.2.0) (1.17.0)
Collecting markdown-it-py<5,>=1 (from mdformat>=0.7.21->mkdocs-llmstxt~=0.2.0)
Using cached markdown_it_py-4.0.0-py3-none-any.whl.metadata (7.3 kB)
Collecting mdurl~=0.1 (from markdown-it-py<5,>=1->mdformat>=0.7.21->mkdocs-llmstxt~=0.2.0)
Using cached mdurl-0.1.2-py3-none-any.whl.metadata (1.6 kB)
Requirement already satisfied: platformdirs>=2.2.0 in ./.venv/lib/python3.13/site-packages (from mkdocs-get-deps>=0.2.0->mkdocs~=1.6.1) (4.9.2)
Requirement already satisfied: zipp>=3.20 in ./.venv/lib/python3.13/site-packages (from importlib-metadata->mike~=2.1.3) (3.23.0)
Using cached click-8.2.1-py3-none-any.whl (102 kB)
Using cached mkdocs_macros_plugin-1.3.9-py3-none-any.whl (38 kB)
Using cached mkdocs_material-9.6.23-py3-none-any.whl (9.2 MB)
Using cached mkdocstrings_python-1.16.12-py3-none-any.whl (124 kB)
Using cached mkdocs_llmstxt-0.2.0-py3-none-any.whl (23 kB)
Using cached backrefs-5.9-py313-none-any.whl (399 kB)
Using cached beautifulsoup4-4.14.3-py3-none-any.whl (107 kB)
Using cached griffe-2.0.0-py3-none-any.whl (5.2 kB)
Using cached griffecli-2.0.0-py3-none-any.whl (9.3 kB)
Using cached griffelib-2.0.0-py3-none-any.whl (142 kB)
Using cached markdownify-1.2.2-py3-none-any.whl (15 kB)
Using cached mdformat-1.0.0-py3-none-any.whl (53 kB)
Using cached markdown_it_py-4.0.0-py3-none-any.whl (87 kB)
Using cached mdurl-0.1.2-py3-none-any.whl (10.0 kB)
Using cached mkdocs_autorefs-1.4.4-py3-none-any.whl (25 kB)
Using cached mkdocstrings-1.0.3-py3-none-any.whl (35 kB)
Using cached soupsieve-2.8.3-py3-none-any.whl (37 kB)
Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB)
Installing collected packages: typing-extensions, soupsieve, mdurl, griffelib, click, backrefs, markdown-it-py, griffecli, beautifulsoup4, mdformat, markdownify, griffe, mkdocs-material, mkdocs-macros-plugin, mkdocs-llmstxt, mkdocs-autorefs, mkdocstrings, mkdocstrings-python
Attempting uninstall: click
Found existing installation: click 8.3.1
Uninstalling click-8.3.1:
Successfully uninstalled click-8.3.1
Attempting uninstall: backrefs
Found existing installation: backrefs 6.2
Uninstalling backrefs-6.2:
Successfully uninstalled backrefs-6.2
Attempting uninstall: mkdocs-material
Found existing installation: mkdocs-material 9.7.2
Uninstalling mkdocs-material-9.7.2:
Successfully uninstalled mkdocs-material-9.7.2
Attempting uninstall: mkdocs-macros-plugin
Found existing installation: mkdocs-macros-plugin 1.5.0
Uninstalling mkdocs-macros-plugin-1.5.0:
Successfully uninstalled mkdocs-macros-plugin-1.5.0

Successfully installed backrefs-5.9 beautifulsoup4-4.14.3 click-8.2.1 griffe-2.0.0 griffecli-2.0.0 griffelib-2.0.0 markdown-it-py-4.0.0 markdownify-1.2.2 mdformat-1.0.0 mdurl-0.1.2 mkdocs-autorefs-1.4.4 mkdocs-llmstxt-0.2.0 mkdocs-macros-plugin-1.3.9 mkdocs-material-9.6.23 mkdocstrings-1.0.3 mkdocstrings-python-1.16.12 soupsieve-2.8.3 typing-extensions-4.15.0

[notice] A new release of pip is available: 25.3 -> 26.0.1
[notice] To update, run: pip install --upgrade pip
Loading