feat: dynamic subagent loading from SSM

This commit is contained in:
daniel
2026-05-15 15:19:08 -05:00
parent 85efb082f7
commit 05fee423f2

View File

@@ -68,6 +68,7 @@ from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemory
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
# code_interpreter removed — causes [Errno 98] port 8080 conflict on warm container re-init
from tools.code_interpreter import run_code
from strands_tools import http_request, file_read
app = BedrockAgentCoreApp()
@@ -87,6 +88,43 @@ except Exception as _e:
print(traceback.format_exc())
# ── Subagent loading ──────────────────────────────────────────────────────
TOOL_PRESETS = {
"aws": lambda: [MCPClient(lambda: aws_iam_streamablehttp_client(config.AWS_MCP_URL, aws_service="aws-mcp"))],
"coding": lambda: [MCPClient(lambda: aws_iam_streamablehttp_client(config.AWS_MCP_URL, aws_service="aws-mcp")), run_code],
"documents": lambda: [http_request, file_read],
}
def _load_subagents(ssm_client) -> list:
"""Load subagent definitions from SSM and return as tools."""
import json
try:
resp = ssm_client.get_parameter(Name='/agent-claw/subagents')
defs = json.loads(resp['Parameter']['Value'])
except Exception as e:
print(f'[main] Failed to load subagents from SSM: {type(e).__name__}: {e}')
return []
tools = []
for cfg in defs:
preset = cfg.get('tools', '')
if preset not in TOOL_PRESETS:
print(f'[main] Unknown tool preset "{preset}" for subagent "{cfg.get("name")}", skipping')
continue
try:
sub = Agent(
model=BedrockModel(model_id=cfg['model_id'], region_name='us-east-1'),
system_prompt=cfg['system_prompt'],
tools=TOOL_PRESETS[preset](),
)
tools.append(sub.as_tool(name=cfg['name'], description=cfg['description']))
except Exception as e:
print(f'[main] Failed to build subagent "{cfg.get("name")}": {type(e).__name__}: {e}')
print(f'[main] Loaded {len(tools)} subagent(s)')
return tools
# ── Tool definitions ──────────────────────────────────────────────────────
# NOTE: send_message tool removed — delivery handled by agent-runner streaming consumer
@@ -497,6 +535,7 @@ async def main(payload: dict, context):
_time_str = _now.strftime('%A, %B %d, %Y %I:%M %p %Z')
system_prompt = system_prompt + f'\n\nCurrent date/time: {_time_str}'
system_prompt += 'AWS tools available: call_aws (any AWS API via AWS MCP Server), aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service. Use call_aws directly for AWS API calls — do NOT say you lack AWS access.'
system_prompt += '\n\nSubagents available — use them aggressively to save cost and improve quality:\n- aws_agent: all AWS infrastructure, cost, resource, IAM, CloudWatch queries\n- coding_agent: code writing, builds, deployments, CodeBuild/AppRunner/ECR\n- document_agent: summarize URLs, extract data from documents, process long text\nDefault to delegating; only answer directly for simple conversational responses or tasks that don\'t fit a subagent.'
print(f'[main] System prompt time injection: {_time_str}')
# Model: claude-sonnet-4-6 via cross-region inference
@@ -519,7 +558,11 @@ async def main(payload: dict, context):
mcp_connections = services.get('mcp_connections', [])
mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id)
all_tools = base_tools + _aws_mcp_tools + mcp_clients
# Load subagents from SSM
ssm = boto3.client('ssm', region_name='us-east-1')
subagent_tools = _load_subagents(ssm)
all_tools = base_tools + _aws_mcp_tools + mcp_clients + subagent_tools
agent = Agent(
model=model,