feat: dynamic subagent loading from SSM
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user