v1.14.0
Major Features
Structured Output via Agentic Loop
Agents can now validate responses against predefined schemas using JSON Schema or Pydantic models. Validation occurs at response generation time with configurable retry behavior for non-conforming outputs.
agent = Agent()
result = agent(
"John Smith is a 30 year-old software engineer",
structured_output_model=PersonInfo
)
# Access the structured output from the result
person_info: PersonInfo = result.structured_output
See more in the docs for Structured Output.
Interrupting Agents
Interrupts now provide first-class support for Human-in-the-loop patterns in Strands. They can be raised in Hooks or directly in tool definitions. Related, MCP elicitation has been exposed on the MCPClient.
import json
from typing import Any
from strands import Agent, tool
from strands.hooks import BeforeToolCallEvent, HookProvider, HookRegistry
from my_project import delete_files, inspect_files
class ApprovalHook(HookProvider):
def __init__(self, app_name: str) -> None:
self.app_name = app_name
def register_hooks(self, registry: HookRegistry, **kwargs: Any) -> None:
registry.add_callback(BeforeToolCallEvent, self.approve)
def approve(self, event: BeforeToolCallEvent) -> None:
if event.tool_use["name"] != "delete_files":
return
approval = event.interrupt(f"{self.app_name}-approval", reason={"paths": event.tool_use["input"]["paths"]})
if approval.lower() != "y":
event.cancel_tool = "User denied permission to delete files"
agent = Agent(
hooks=[ApprovalHook("myapp")],
system_prompt="You delete files older than 5 days",
tools=[delete_files, inspect_files],
)
paths = ["a/b/c.txt", "d/e/f.txt"]
result = agent(f"paths=<{paths}>")
while True:
if result.stop_reason != "interrupt":
break
responses = []
for interrupt in result.interrupts:
if interrupt.name == "myapp-approval":
user_input = input(f"Do you want to delete {interrupt.reason["paths"]} (y/N): ")
responses.append({
"interruptResponse": {
"interruptId": interrupt.id,
"response": user_input
}
})
result = agent(responses)Managed MCP Connections
We've introduced MCP Connections via ToolProviders, an experimental interface that addresses the requirement to use context managers with MCP tools. The Agent now manages connection lifecycles automatically, enabling simpler syntax:
agent = Agent(tools=[stdio_mcp_client])
agent("do something")
While this feature is experimental, we aim to mark it as stable soon and welcome user testing of this and other new features.
Agent Config
Users can now define and create agents using configuration files or dictionaries:
{
"model": "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
"prompt": "You are a coding assistant. Help users write, debug, and improve their code. You have access to file operations and can execute shell commands when needed.",
"tools": ["strands_tools.file_read", "strands_tools.editor", "strands_tools.shell"]
}
All Changes
- models - litellm - start and stop reasoning by @pgrayy in #947
- feat: add experimental AgentConfig with comprehensive tool management by @mr-lee in #935
- fix(telemetry): make strands agent invoke_agent span as INTERNAL spanKind by @poshinchen in #1055
- feat: add multiagent hooks, add serialize & deserialize function to multiagent base & agent result by @JackYPCOnline in #1070
- feat: Add Structured Output as part of the agent loop by @afarntrog in #943
- integ tests - interrupts - remove asyncio marker by @pgrayy in #1045
- interrupt - docstring - fix formatting by @pgrayy in #1074
- ci: add pr size labeler by @dbschmigelski in #1082
- fix: Don't bail out if there are no tool_uses by @zastrowm in #1087
- feat(mcp): add experimental agent managed connection via ToolProvider by @dbschmigelski in #895
- fix (bug): retry on varying Bedrock throttlingexception cases by @mehtarac in #1096
- feat: skip model invocation when latest message contains ToolUse by @Unshure in #1068
- direct tool call - interrupt not allowed by @pgrayy in #1097
- mcp elicitation by @pgrayy in #1094
- fix(litellm): enhance structured output handling by @Arindam200 in #1021
- Transform invalid tool usages on sending, not on initial detection by @zastrowm in #1091
New Contributors
- @mr-lee made their first contribution in #935
- @Arindam200 made their first contribution in #1021
Full Changelog: v1.13.0...v1.14.0