Technical Design: Synapse Custom Agents Feature V2
Part 1: High-Level Concepts
This section is intended for all stakeholders. It explains what we are building and why, without requiring engineering background.
Problem Statement
The Synapse Chat Framework allows users to interact with private Synapse data through AI-powered chatbots. Today (V1), every chatbot is a single monolithic agent loaded with all available tools. This creates two problems:
Difficult to extend: Developers who want to create custom chatbots (e.g., a portal-specific dataset finder) must manually copy hundreds of lines of tool definitions from the production agent. This is error-prone, hard to maintain, and creates version drift.
Performance degradation: As conversations grow longer, the single agent accumulates raw API responses in its memory, causing token throttling and degraded response quality.
What We Are Building
V2 introduces a Specialist Agent architecture that solves both problems:
Concept | Description |
|---|---|
Specialist Agents | Production-grade, narrowly focused AI agents hosted by Synapse. Each specialist owns a specific capability (e.g., "Search" or "Metadata Lookup"). They are maintained by the Synapse Core Team. |
Supervisor Agents | Lightweight custom agents created by developers. A supervisor contains domain-specific instructions and a knowledge base, but delegates heavy lifting to specialists instead of reimplementing tools. |
Specialist Proxy | A single, simple mechanism that allows any custom agent to call a specialist. Replaces the need to copy-paste tool definitions. |
Specialist Card | A discoverable description of each specialist's capabilities, inputs, and outputs. Developers use this to understand what specialists are available and how to invoke them (inspired by the Agent2Agent protocol). |
How It Works (Conceptual)
Imagine a portal developer wants a chatbot that helps researchers find Alzheimer's datasets:
The developer discovers available specialists by calling the listing API, which returns a "calling card" for each specialist describing its capabilities and how to invoke it.
The developer creates a Supervisor Agent with instructions like: "You are an Alzheimer's research assistant. When users ask for datasets, delegate to the Search Specialist."
The developer attaches their portal's knowledge base (RAG) for domain context.
The developer registers the agent with Synapse, declaring which specialists it may use (e.g., Search, Metadata).
At runtime, when a user asks "Find me recent tau protein datasets," the supervisor recognizes this as a search task and delegates to the Search Specialist.
The Search Specialist executes the query securely (respecting the user's permissions), produces a concise summary, and returns it to the supervisor.
The supervisor uses the summary to formulate a user-friendly response.
Key Benefits
Benefit | Who It Helps | How |
|---|---|---|
Dramatically simpler agent development | Custom Agent Developers | One action group replaces 500+ lines of copied tool definitions |
Better conversation quality | End Users | Raw API payloads never enter the main conversation, preventing context overload |
Centralized tool maintenance | Synapse Core Team | Specialists are updated once; all supervisors benefit immediately |
Stronger security | Everyone | Cryptographic identity tokens ensure specialists always respect user permissions |
Full backwards compatibility | Existing Agent Developers | V1 agents continue working without any changes |
Discoverability | Custom Agent Developers | Calling cards describe each specialist's capabilities, making it easy to build against |
Developer Experience: Before vs After
Step | V1 (Today) | V2 (New) |
|---|---|---|
1 | Copy 500+ lines of OpenAPI templates from baseline agent | Call |
2 | Copy multi-paragraph tool instructions | Create a lightweight agent with your custom instructions |
3 | Modify instructions for your domain | Attach your knowledge base (RAG) |
4 | Add your knowledge base | Add ONE action group: |
5 | Deploy via CloudFormation | Deploy and register as SUPERVISOR with declared specialists |
6 | Register with Synapse |
|
Security Model
When a supervisor delegates to a specialist, the system:
Generates a short-lived cryptographic token (JWT) carrying the user's identity and permissions
Validates the supervisor is authorized to call that specific specialist
The specialist verifies the token and enforces the user's access level
Results flow back only as bounded summaries (never raw data payloads)
This ensures that even if a custom supervisor is compromised, it cannot access data the user doesn't have permission to see, nor can it call specialists it hasn't been authorized to use.
Backwards Compatibility
All existing V1 custom agents continue to work without any code changes. The new architecture is purely additive. Developers can adopt V2 incrementally at their own pace.
Part 2: Technical Design
This section is for engineers implementing or reviewing the design. It assumes familiarity with the Synapse codebase architecture (Controller → Manager → DAO) and the existing agent framework.
Architecture Overview
User → Synapse Worker → invoke_agent(custom Bedrock agent)
Custom agent (Haiku + custom instructions):
"I need to search for datasets"
→ return_control: specialist_proxy(specialist_name="SYNAPSE_CORE_SEARCH", query="...")
Worker → SpecialistProxyHandler.handleEvent(event)
→ Validate specialist_name is in registration's delegatedSpecialists
→ Generate ephemeral JWT (DelegationTokenService)
→ Resolve specialist (Spring AI service)
→ Invoke specialist with query + JWT
→ Receive bounded summary from specialist
→ Return summary as handler response (≤4000 chars)
Custom agent receives summary → formulates user-facing response
Worker → return response to userDesign Decisions
D1. Specialist Runtime: Spring AI
Specialists are hosted via Spring AI rather than AWS Bedrock Agent Collaboration. This gives us full control over:
Tool execution and context management
Identity propagation (JWT injection at agent boundaries)
Versioning specialists with our codebase (not separately managed AWS resources)
Tool definitions in Java (not CloudFormation OpenAPI templates)
D2. Bridge Pattern: specialist_proxy ReturnControlHandler
Rather than building a new orchestration engine in the worker, we add a single ReturnControlHandler implementation that intercepts proxy requests and routes them to the Spring AI specialist service. This:
Reuses the existing
ReturnControlHandlerinfrastructure with zero changes to the invoke_agent loopProvides natural context segregation (specialist response is what flows back through return_control)
Keeps custom Bedrock agents simple: one action group, one function
D3. Registration Declares Allowed Specialists
Custom agent registrations include a delegatedSpecialists array. The SpecialistProxyHandler validates at runtime that the requested specialist is in that list. This prevents prompt-injection attacks from tricking an agent into calling unauthorized specialists.
D4. Alias-Based Specialist Resolution (Hybrid)
Specialist agents have stable logical identifiers (e.g., SYNAPSE_CORE_SEARCH). Stack Builder creates per-stack aliases (e.g., prod-593) on specialist resources. Workers derive the correct alias from their own stack identity via StackConfiguration. No re-registration is needed on stack promotion.
D5. Layered Context Segregation
Specialists are instructed to produce concise structured summaries (they know what is relevant)
The handler enforces a hard character cap (
MAX_SPECIALIST_RESPONSE_CHARS = 4000) as a safety net
D6. Deployment Topology Deferred
The SpecialistService interface is designed to work both in-process and over HTTP. We start with in-process invocation and can extract to a separate Spring Boot service later if scaling demands it.
API: Specialist Discovery
Custom agent developers need to discover what specialists are available and understand how to invoke them. Inspired by the Agent2Agent (A2A) protocol, each specialist publishes a calling card that describes its capabilities.
GET /agent/specialists
Lists all available specialists and their calling cards. No authentication required (specialist capabilities are public metadata).
Response: ListSpecialistsResponse
{
"description": "A page of specialist calling cards.",
"properties": {
"page": {
"type": "array",
"items": {
"$ref": "org.sagebionetworks.repo.model.agent.SpecialistCard"
},
"description": "The list of available specialist calling cards."
},
"nextPageToken": {
"type": "string",
"description": "Token for the next page of results. Null if no more pages."
}
}
}SpecialistCard (New Schema)
The calling card for a specialist agent. Provides everything a developer needs to understand a specialist's capabilities, input contract, and expected output.
{
"description": "A specialist agent's calling card. Describes the specialist's capabilities, expected input, and output contract. Used by custom agent developers to understand how to invoke specialists via the specialist_proxy function.",
"properties": {
"specialistType": {
"$ref": "org.sagebionetworks.repo.model.agent.SpecialistType",
"description": "The stable logical identifier used when calling specialist_proxy."
},
"displayName": {
"type": "string",
"description": "Human-readable name for display (e.g., 'Synapse Search Specialist')."
},
"description": {
"type": "string",
"description": "A concise description of what this specialist does and when to use it."
},
"version": {
"type": "string",
"description": "The current version of this specialist (semver)."
},
"lifecycleState": {
"$ref": "org.sagebionetworks.repo.model.agent.SpecialistLifecycleState",
"description": "Current lifecycle state. Only ACTIVE specialists may be invoked."
},
"capabilities": {
"type": "array",
"items": { "type": "string" },
"description": "A list of high-level capabilities this specialist provides (e.g., 'full-text search', 'entity metadata retrieval', 'annotation lookup')."
},
"inputDescription": {
"type": "string",
"description": "Describes what the 'query' parameter should contain when invoking this specialist. Guides the supervisor on how to formulate delegation requests."
},
"outputDescription": {
"type": "string",
"description": "Describes the format and content of the specialist's response. Helps the supervisor interpret results."
},
"exampleInvocations": {
"type": "array",
"items": {
"$ref": "org.sagebionetworks.repo.model.agent.SpecialistExample"
},
"description": "Example query/response pairs demonstrating typical usage."
}
}
}SpecialistExample (New Schema)
{
"description": "An example invocation of a specialist, showing a sample query and the kind of response it produces.",
"properties": {
"query": {
"type": "string",
"description": "An example query string to pass as the 'query' parameter."
},
"responsePreview": {
"type": "string",
"description": "A representative preview of what the specialist returns for this query."
}
}
}API: Extended Registration
AgentRegistrationRequest (Modified Schema)
The existing registration request gains two optional fields for V2 supervisor agents:
{
"description": "Request to register a custom AWS agent with Synapse. Currently, only internal users are authorized to register custom agents.",
"properties": {
"awsAgentId": {
"type": "string",
"description": "The AWS issued agent ID of the agent to be registered."
},
"awsAliasId": {
"type": "string",
"description": "The AWS issued agent alias ID. Optional. If not provided, 'TSTALIASID' will be used."
},
"agentType": {
"$ref": "org.sagebionetworks.repo.model.agent.AgentType",
"description": "The type of agent being registered. Optional. Defaults to CUSTOM if not provided (backwards compatible). Set to SUPERVISOR for V2 multi-agent delegation."
},
"delegatedSpecialists": {
"type": "array",
"items": {
"$ref": "org.sagebionetworks.repo.model.agent.DelegatedSpecialist"
},
"description": "Required when agentType is SUPERVISOR. The list of specialists this supervisor is authorized to invoke. Each specialist must be ACTIVE in the registry."
}
}
}AgentRegistration (Modified Schema)
The registration response is extended to include delegated specialists for SUPERVISOR registrations:
{
"description": "The registration of a custom AWS agent.",
"properties": {
"agentRegistrationId": {
"type": "string",
"description": "The unique ID issued by Synapse when this agent was registered."
},
"awsAgentId": {
"type": "string",
"description": "The AWS issued agent ID of the agent."
},
"awsAliasId": {
"type": "string",
"description": "The AWS issued agent alias ID."
},
"registeredOn": {
"type": "string",
"format": "date-time",
"description": "The date this agent was registered."
},
"type": {
"$ref": "org.sagebionetworks.repo.model.agent.AgentType",
"description": "The agent's type."
},
"delegatedSpecialists": {
"type": "array",
"items": {
"$ref": "org.sagebionetworks.repo.model.agent.DelegatedSpecialist"
},
"description": "Present when type is SUPERVISOR. The specialists this agent is authorized to invoke."
}
}
}API: Supporting Schemas (New)
AgentType (Modified Enum)
{
"description": "The type of a registered agent.",
"name": "AgentType",
"type": "string",
"enum": [
{
"name": "BASELINE",
"description": "The baseline agent designed for general interactions with Synapse."
},
{
"name": "CUSTOM",
"description": "A custom agent designed for a specific use case (V1 pattern)."
},
{
"name": "SUPERVISOR",
"description": "A custom agent that delegates to production specialists via the specialist_proxy mechanism (V2 pattern)."
}
]
}SpecialistType (New Enum)
{
"description": "The logical identifier of a production specialist agent. Used as the 'specialist_name' parameter when invoking specialist_proxy.",
"name": "SpecialistType",
"type": "string",
"enum": [
{
"name": "SYNAPSE_CORE_SEARCH",
"description": "Specialist for searching Synapse entities, datasets, and metadata via full-text and structured queries."
},
{
"name": "SYNAPSE_CORE_METADATA",
"description": "Specialist for retrieving and interpreting entity metadata, annotations, and schema information."
}
]
}SpecialistLifecycleState (New Enum)
{
"description": "The lifecycle state of a specialist agent in the registry.",
"name": "SpecialistLifecycleState",
"type": "string",
"enum": [
{
"name": "ACTIVE",
"description": "The specialist is available for invocation."
},
{
"name": "DEPRECATED",
"description": "The specialist still functions but is scheduled for retirement. Existing supervisors continue to work; new registrations should use a replacement."
},
{
"name": "RETIRED",
"description": "The specialist is no longer available. Invocations will fail with actionable migration guidance."
}
]
}DelegatedSpecialist (New Schema)
{
"description": "Identifies a specialist that a supervisor agent is authorized to invoke via specialist_proxy.",
"properties": {
"specialistType": {
"$ref": "org.sagebionetworks.repo.model.agent.SpecialistType",
"description": "The logical identifier of the specialist."
}
}
}Specialist Proxy: Action Group Contract
Custom agent developers include the following action group in their Bedrock agent definition. This is the only tool they need to delegate to Synapse specialists:
{
"actionGroupName": "specialist_tools",
"description": "Delegates tasks to Synapse production specialist agents.",
"functions": [
{
"name": "specialist_proxy",
"description": "Invoke a Synapse specialist agent to perform a specific task. The specialist will execute the query using the current user's permissions and return a concise summary.",
"parameters": {
"specialist_name": {
"type": "string",
"description": "The SpecialistType identifier (e.g., SYNAPSE_CORE_SEARCH, SYNAPSE_CORE_METADATA). Must match a specialist declared in this agent's registration.",
"required": true
},
"query": {
"type": "string",
"description": "The natural language query or structured request to send to the specialist. Refer to the specialist's calling card (inputDescription) for guidance on formatting.",
"required": true
}
}
}
]
}This action group uses Bedrock's RETURN_CONTROL invocation type, meaning the Synapse worker intercepts the call and handles it server-side via SpecialistProxyHandler.
Sequence Diagram
sequenceDiagram
participant User
participant UI as Synapse Chat UI
participant Worker as Async Chat Worker
participant Bedrock as Custom Bedrock Agent (Supervisor)
participant Handler as SpecialistProxyHandler
participant JWT as DelegationTokenService
participant Specialist as Spring AI Specialist
User->>UI: Submit prompt
UI->>Worker: Async chat job
Worker->>Bedrock: invoke_agent(prompt)
Bedrock->>Worker: return_control: specialist_proxy(SYNAPSE_CORE_SEARCH, query)
Worker->>Handler: handleEvent(specialist_name, query)
Handler->>Handler: Validate specialist is in registration's delegatedSpecialists
Handler->>JWT: generateDelegationToken(user, session)
JWT-->>Handler: Short-lived RS256 JWT (5 min TTL)
Handler->>Specialist: invoke(type, query, jwt, accessLevel)
Specialist->>Specialist: Validate JWT, execute search with user permissions
Specialist-->>Handler: Bounded summary (≤4000 chars)
Handler-->>Worker: Return summary as handler response
Worker->>Bedrock: invoke_agent(summary as return_control result)
Bedrock-->>Worker: Final user-facing response
Worker-->>UI: Stream response
UI-->>User: Render answerSecurity: Ephemeral JWT
When the SpecialistProxyHandler delegates to a specialist, it generates a short-lived JWT:
Claim | Description |
|---|---|
| Synapse user ID of the initiating user |
| Team membership IDs (for access control decisions) |
| The originating agent session (prevents cross-session token reuse) |
| 5-minute expiration (bound to single execution cycle) |
Signed with RS256 using the existing OIDC signing keys. Specialists verify the signature locally (same JVM or shared key material) before executing any operation.
Implementation Components
SpecialistProxyHandler
A ReturnControlHandler implementation (auto-discovered via Spring component scan):
Action group:
specialist_toolsFunction:
specialist_proxy
Responsibilities:
Parse
specialist_nameandqueryfrom the return_control eventResolve the specialist from the registry (must be ACTIVE)
Validate the specialist is in the registration's
delegatedSpecialistslistGenerate ephemeral JWT via
DelegationTokenServiceInvoke the specialist via
SpecialistServiceinterfaceEnforce hard character cap (4000) on response
Emit trace events for UI rendering
Return bounded summary to the agent
SpecialistService (Interface)
public interface SpecialistService {
/**
* Invoke a specialist agent with the given query.
* The specialist validates the JWT, executes with the user's access level,
* and returns a bounded summary.
*/
String invoke(SpecialistType type, String query,
String delegationToken, AgentAccessLevel accessLevel);
}Abstracts deployment topology. Initial implementation is in-process (Spring AI agents in same JVM); can be extracted to a separate service later.
DelegationTokenService
Generates and validates short-lived RS256 JWTs. Reuses the existing JwtBuilder and OIDC RSA key infrastructure already in the codebase.
Spring AI Specialist Module (New Maven Module)
Contains the actual specialist implementations:
Spring AI agent definitions for each SpecialistType
Tool definitions wrapping existing Synapse manager methods (EntityManager, SearchManager, etc.)
Agent instructions tuned for concise structured output
JWT validation logic
Each specialist is stateless per-invocation, has a narrow tool set, and produces bounded summaries.
Backwards Compatibility
Scenario | V2 Behavior |
|---|---|
V1 CUSTOM agent (awsAgentId, no agentType) | Defaults to CUSTOM. No delegatedSpecialists. Existing invoke_agent path unchanged. |
V1 BASELINE agent | Bootstrap registration ID=1 unchanged. Zero impact. |
V1 Grid agent | GridAgentSessionContext resolves to grid agent. Zero impact. |
New V2 SUPERVISOR agent | Registers with agentType=SUPERVISOR + delegatedSpecialists. Uses specialist_proxy action group. |
Migration Safety
All changes are single-stack safe:
New database tables are purely additive
MySQL ENUM extension (adding SUPERVISOR) preserves existing data
No columns removed or renamed on existing tables
V1 registrations are untouched (new fields are optional)
New
MigrationType: AGENT_SPECIALIST_REGISTRYfor the specialist catalog table
Testing Strategy
Layer | Test | Validates |
|---|---|---|
DAO |
| CRUD, lifecycle transitions, unique constraints |
DAO |
| Junction table persistence, lazy population of delegatedSpecialists |
Manager |
| SUPERVISOR registration validation, V1 backwards compat |
Handler |
| Delegation flow, access validation, char cap, rejected specialists |
JWT |
| Token generation, validation, expiry |
Migration |
| New migration type is registered |
Integration |
| Full round-trip: register SUPERVISOR, list specialists, V1 still works |
Open Items (Future Work)
Spring AI specialist implementations -- actual SYNAPSE_CORE_SEARCH and SYNAPSE_CORE_METADATA agent logic (tool definitions, instructions, model selection)
Deployment topology -- in-process vs separate Spring Boot service decision
Specialist versioning -- how specialist versions relate to stack versions and alias management
Rate limiting -- per-user, per-session, per-specialist throttling
Observability -- correlation IDs across supervisor-to-specialist boundary
Additional specialist types -- Entity CRUD, Table Query, File Operations, etc.
Calling card management API -- admin endpoints for creating/updating specialist cards