diff --git a/docs/user-guide/concepts/agents/hooks.md b/docs/user-guide/concepts/agents/hooks.md index 7b8f1748..5045a283 100644 --- a/docs/user-guide/concepts/agents/hooks.md +++ b/docs/user-guide/concepts/agents/hooks.md @@ -121,6 +121,56 @@ Some events come in pairs, such as Before/After events. The After event callback ## Advanced Usage +### Accessing Invocation State in Hooks + +Hook events that involve tool execution include access to `invocation_state`, which provides configuration and context data passed through the agent invocation. This is particularly useful for: + +1. **Custom Objects**: Access database client objects, connection pools, or other Python objects +2. **Request Context**: Access session IDs, user information, settings, or request-specific data +3. **Multi-Agent Shared State**: In multi-agent patterns, access state shared across all agents - see [Shared State Across Multi-Agent Patterns](../multi-agent/multi-agent-patterns.md#shared-state-across-multi-agent-patterns) +4. **Custom Parameters**: Pass any additional data that hooks might need + +```python +from strands.hooks import BeforeToolCallEvent +import logging + +def log_with_context(event: BeforeToolCallEvent) -> None: + """Log tool invocations with context from invocation state.""" + # Access invocation state from the event + user_id = event.invocation_state.get("user_id", "unknown") + session_id = event.invocation_state.get("session_id") + + # Access non-JSON serializable objects like database connections + db_connection = event.invocation_state.get("database_connection") + logger_instance = event.invocation_state.get("custom_logger") + + # Use custom logger if provided, otherwise use default + logger = logger_instance if logger_instance else logging.getLogger(__name__) + + logger.info( + f"User {user_id} in session {session_id} " + f"invoking tool: {event.tool_use['name']} " + f"with DB connection: {db_connection is not None}" + ) + +# Register the hook +agent = Agent(tools=[my_tool]) +agent.hooks.add_callback(BeforeToolCallEvent, log_with_context) + +# Execute with context including non-serializable objects +import sqlite3 +custom_logger = logging.getLogger("custom") +db_conn = sqlite3.connect(":memory:") + +result = agent( + "Process the data", + user_id="user123", + session_id="sess456", + database_connection=db_conn, # Non-JSON serializable object + custom_logger=custom_logger # Non-JSON serializable object +) +``` + ### Fixed Tool Arguments Enforce specific arguments for tools, ensuring they always use particular values regardless of what the agent specifies: diff --git a/docs/user-guide/concepts/multi-agent/graph.md b/docs/user-guide/concepts/multi-agent/graph.md index 5c92ffba..709dd1df 100644 --- a/docs/user-guide/concepts/multi-agent/graph.md +++ b/docs/user-guide/concepts/multi-agent/graph.md @@ -319,6 +319,12 @@ From [another_node_id]: - [Agent name]: [Result text] ``` +## Shared State + +Graphs support passing shared state to all agents through the `invocation_state` parameter. This enables sharing context and configuration across agents without exposing it to the LLM. + +For detailed information about shared state, including examples and best practices, see [Shared State Across Multi-Agent Patterns](./multi-agent-patterns.md#shared-state-across-multi-agent-patterns). + ## Graphs as a Tool Agents can dynamically create and orchestrate graphs by using the `graph` tool available in the [Strands tools package](../tools/community-tools-package.md). diff --git a/docs/user-guide/concepts/multi-agent/multi-agent-patterns.md b/docs/user-guide/concepts/multi-agent/multi-agent-patterns.md index 061fca93..f53b875c 100644 --- a/docs/user-guide/concepts/multi-agent/multi-agent-patterns.md +++ b/docs/user-guide/concepts/multi-agent/multi-agent-patterns.md @@ -72,6 +72,61 @@ Some Examples: - Automated Data Pipelines: A fixed set of tasks to extract, analyze, and report on data, where independent analysis steps can run in parallel. - Standard Business Processes: Onboarding a new employee by creating accounts, assigning training, and sending a welcome email, all triggered by a single agent action. +## Shared State Across Multi-Agent Patterns + +Both Graph and Swarm patterns support passing shared state to all agents through the `invocation_state` parameter. This enables sharing context and configuration across agents without exposing it to the LLM. + +### How Shared State Works + +The `invocation_state` is automatically propagated to: + +- All agents in the pattern via their `**kwargs` +- Tools via `ToolContext` when using `@tool(context=True)` - see [Python Tools](../tools/python-tools.md#accessing-invocation-state-in-tools) +- Tool-related hooks (BeforeToolCallEvent, AfterToolCallEvent) - see [Hooks](../agents/hooks.md#accessing-invocation-state-in-hooks) + +### Example Usage + +```python +# Same invocation_state works for both patterns +shared_state = { + "user_id": "user123", + "session_id": "sess456", + "debug_mode": True, + "database_connection": db_connection_object +} + +# Execute with Graph +result = graph( + "Analyze customer data", + invocation_state=shared_state +) + +# Execute with Swarm (same shared_state) +result = swarm( + "Analyze customer data", + invocation_state=shared_state +) +``` + +### Accessing Shared State in Tools + +```python +from strands import tool, ToolContext + +@tool(context=True) +def query_data(query: str, tool_context: ToolContext) -> str: + user_id = tool_context.invocation_state.get("user_id") + debug_mode = tool_context.invocation_state.get("debug_mode", False) + # Use context for personalized queries... +``` + +### Important Distinctions + +- **Shared State**: Configuration and objects passed via `invocation_state`, not visible in prompts +- **Pattern-Specific Data Flow**: Each pattern has its own mechanisms for passing data that the LLM should reason about including shared context for swarms and agent inputs for graphs + +Use `invocation_state` for context and configuration that shouldn't appear in prompts, while using each pattern's specific data flow mechanisms for data the LLM should reason about. + ## Conclusion This guide has explored the three primary multi-agent patterns in Strands: Graph, Swarm, and Workflow. Each pattern serves distinct use cases based on how execution paths are determined and controlled. When choosing between patterns, consider your problem's complexity, the need for deterministic vs. emergent behavior, and whether you require cycles, parallel execution, or specific error handling approaches. diff --git a/docs/user-guide/concepts/multi-agent/swarm.md b/docs/user-guide/concepts/multi-agent/swarm.md index a5376c03..b62c261b 100644 --- a/docs/user-guide/concepts/multi-agent/swarm.md +++ b/docs/user-guide/concepts/multi-agent/swarm.md @@ -166,6 +166,12 @@ Agent name: security_specialist. Agent description: Focuses on secure coding pra You have access to swarm coordination tools if you need help from other agents. ``` +## Shared State + +Swarms support passing shared state to all agents through the `invocation_state` parameter. This enables sharing context and configuration across agents without exposing them to the LLM, keeping them separate from the shared context used for collaboration. + +For detailed information about shared state, including examples and best practices, see [Shared State Across Multi-Agent Patterns](./multi-agent-patterns.md#shared-state-across-multi-agent-patterns). + ## Asynchronous Execution You can also execute a Swarm asynchronously by calling the [`invoke_async`](../../../api-reference/multiagent.md#strands.multiagent.swarm.Swarm.invoke_async) function: diff --git a/docs/user-guide/concepts/tools/python-tools.md b/docs/user-guide/concepts/tools/python-tools.md index c8796580..72e9b872 100644 --- a/docs/user-guide/concepts/tools/python-tools.md +++ b/docs/user-guide/concepts/tools/python-tools.md @@ -172,6 +172,50 @@ agent("What is the tool use id?") agent("What is the invocation state?", custom_data="You're the best agent ;)") ``` +#### Accessing Invocation State in Tools + +The `invocation_state` attribute in `ToolContext` provides access to data passed through the agent invocation. This is particularly useful for: + +1. **Request Context**: Access session IDs, user information, or request-specific data +2. **Multi-Agent Shared State**: In [Graph](../multi-agent/graph.md) and [Swarm](../multi-agent/swarm.md) patterns, access state shared across all agents +3. **Per-Invocation Overrides**: Override behavior or settings for specific requests + +```python +from strands import tool, Agent, ToolContext +import requests + +@tool(context=True) +def api_call(query: str, tool_context: ToolContext) -> dict: + """Make an API call with user context. + + Args: + query: The search query to send to the API + tool_context: Context containing user information + """ + user_id = tool_context.invocation_state.get("user_id") + + response = requests.get( + "https://api.example.com/search", + headers={"X-User-ID": user_id}, + params={"q": query} + ) + + return response.json() + +agent = Agent(tools=[api_call]) +result = agent("Get my profile data", user_id="user123") +``` + +##### Invocation State Compared To Other Approaches + +It's important to understand how invocation state compares to other approaches that impact tool execution: + +- **Tool Parameters**: Use for data that the LLM should reason about and provide based on the user's request. Examples include search queries, file paths, calculation inputs, or any data the agent needs to determine from context. + +- **Invocation State**: Use for context and configuration that should not appear in prompts but affects tool behavior. Best suited for parameters that can change between agent invocations. Examples include user IDs for personalization, session IDs, or user flags. + +- **[Class-based tools](#class-based-tools)**: Use for configuration that doesn't change between requests and requires initialization. Examples include API keys, database connection strings, service endpoints, or shared resources that need setup. + ### Tool Streaming Async tools can yield intermediate results to provide real-time progress updates. Each yielded value becomes a [streaming event](../streaming/overview.md), with the final value serving as the tool's return result: