Configured Agents and Registry¶
As powerful as single agents can be, due to a variety of limitations like context window length, chances of hallucination when large number of tools are exposed to the model etc., it is better to have agents that specialize in doing specific tasks. A common pattern that works out well in a variety of scenarios is to have a high level agent that does the planning and/or orchestration and delegates specific tasks to specialized agents.
To allow developers to implement this pattern easily, Sentinel AI provides a way to configure and manage multiple agents
through a centralized registry. This allows for easy access and management of different agents that can perform various
tasks. The registry is implemented as an AgentExtension
and can be added to any top-level agent implementation.
Nomenclature¶
Let us understand a few fundamental constructs before we dive into the implementation details.
ConfiguredAgent
: This is an abstraction that represents an agent with a specific configuration.AgentCapability
: Capabilities define what an agent gets access to. For example, HTTP tools, MCP tools, local/custom tools etc.AgentConfiguration
: This represents the configuration details of an agent, including its name, description, input, output spec, capabilities, capabilities and other settings.AgentConfigurationSource
: The agent configuration source is responsible for providing the configuration details for an agent during the build process. A default in-memory source is provided, but you can implement your own source based off more permanent storage.ConfiguredAgentFactory
: This factory is responsible for creating instances of configured agents based on the provided configuration.AgentRegistry
: The agent registry is a centralized repository that holds all the configured agents. It allows for easy retrieval and management of agents and exposes required tools to a Sentinel AI agent to get details about agents and to invoke them to get work done.
All tools from different sources are registered globally with the ConfiguredAgentFactory
and individual agents
get access to only those tools that they are configured to use. The factory is passed to the AgentRegistry
extension.
All configured agents are listed from the AgentConfigurationSource
and created on the fly as and when needed. A
summarized list of all configured agents is injected to the prompt sent to the model being used by the top-level
agent. The top-level agent can then invoke any of the configured agents as tools to get work done.
Dependencies¶
To use the agent registry and configured agents, you need to include the following dependencies in your project:
<dependency>
<groupId>com.phonepe.sentinel-ai</groupId>
<artifactId>sentinel-ai-configured-agents</artifactId>
</dependency>
Creating an Agent Factory¶
Use the builder on the ConfiguredAgentFactory
to create an agent factory.
final var agentFactory = ConfiguredAgentFactory.builder()
.httpToolboxFactory(...) // Optional, if you want to provide HTTP capabilities
.mcpToolboxFactory(...) // Optional, if you want to provide MCP capabilities
.customToolBox(...) // Optional, if you want to provide custom/local capabilities
.build();
Agent Configuration Source¶
In order for Sentinel AI to be able to build agents on the fly, it needs to read agent configurations from an
AgentConfigurationSource
. You can use InMemoryAgentConfigurationSource
for testing or simple use cases where existing configurations can be
populated at runtime or loaded from somewhere more permanent.
final var agentSource = new InMemoryAgentConfigurationSource();
Agent Factory¶
ConfiguredAgentFactory
is used to create instances of configured agents on the fly. It needs to be provided with
different toolboxes that agents can use based on their configured capabilities. ToolBoxes that can be completely
configure from outside, for example HTTP ToolBox
and MCP ToolBox
are created on the fly using ToolBox Factories.
final var agentFactory = ConfiguredAgentFactory.builder()
.httpToolboxFactory(...) // Optional, if you want to provide HTTP capabilities
.mcpToolboxFactory(...) // Optional, if you want to provide MCP capabilities
.customToolBox(...) // Optional, if you want to provide custom/local capabilities
.build();
Agent Registry¶
The AgentRegistry
extension is used to manage and provide access to configured agents. It needs to be provided with
an AgentConfigurationSource
to read agent configurations and an ConfiguredAgentFactory
to create instances of configured
agents.
final var agentRegistry = AgentRegistry.builder()
.agentConfigurationSource(agentSource) //Mandatory
.configuredAgentFactory(agentFactory::createAgent) //Mandatory
.build();
AgentRegistry
extension can then be added to the top level Agent
implementation.
final var topAgent = PlannerAgent.builder()
.setup(setup)
.extension(registry)
.build();
// Interact with the planner agent as usual
final var response = topAgent.executeAsync(AgentInput.<String>builder()
.request("Summarize the story of War and Peace")
.build())
.join();
The AgentRegistry
extension automatically injects a summarized list of all configured agents to the prompt sent to
the model being used by the top-level agent. It also exposes tools for the planner to get details like I/O schema of these
agents and to invoke them to get work done.
Sample planner agent implementation
The following is the trivial implementation of a planner agent that uses the agent registry extension to manage and invoke Configured Agents.
public class PlannerAgent extends Agent<String, String, PlannerAgent> {
@Builder
public PlannerAgent(
@NonNull AgentSetup setup,
@Singular List<AgentExtension<String, String, PlannerAgent>> extensions) {
super(String.class,
"""
Your role is to perform complex tasks as specified by the user. You can achieve this by using
the other agents.
""",
setup,
extensions,
Map.of());
}
@Override
public String name() {
return "planner-agent";
}
}
Configuring Agents¶
Agents can be configured using the AgentConfiguration
class. Such agents need to be registered with the AgentRegistry
by calling the configureAgent
method. The registry in-turn invokes the AgentConfigurationSource::save
method to
persist the configuration and the ConfiguredAgentFactory
to create an instance of the agent on the fly as and when
needed.
// Create agent configuration
final var summarizerAgentConfig = AgentConfiguration.builder()
.agentName("Summarizer Agent") //(1)!
.description("Summarizes input text") //(2)!
.prompt("Provide a 140 character summary for the provided input text") //(3)!
.inputSchema(JsonUtils.schemaForPrimitive(String.class, "rawTextInput", mapper)) //(4)!
.outputSchema(JsonUtils.schema(SummarizerAgentOutput.class)) //(5)!
.capability(AgentCapabilities.remoteHttpCalls(Map.of("weatherserver", //(6)!
Set.of("get_weather_for_location"))))
.capability(AgentCapabilities.mcpCalls(Map.of("mcp", Set.of("add"))))
.build();
// Register agent configuration with the registry
agentRegistry.configureAgent(summarizerAgentConfig);
- Name of the agent (Mandatory)
- Description of the agent (Mandatory)
- Prompt to be used by the agent (Mandatory)
- Input schema for the agent. (Optional, defaults to a simple string input schema)
- Output schema for the agent (Optional, defaults to a simple string output schema)
- Capabilities of the agent (Optional, defaults to no capabilities). See section on Tools for Configured Agents for more details.
Schema generation
You can use the JsonUtils.schemaForPrimitive
and JsonUtils.schema
utility functions to generate schema in the
required format for primitive types/string and classes/records respectively.
Agent Configuration Parameters¶
The following parameters can be configured using the AgentConfiguration
builder.
Builder Parameter | Mandatory | Description |
---|---|---|
agentName | Yes | Name of the agent to be configured. |
description | Yes | Detailed description of the agent. |
prompt | Yes | System prompt to be used for the agent. |
inputSchema | No | Input schema for the agent. Defaults to String if missing. |
outputSchema | No | Output schema for the agent. Defaults to String if missing. |
capabilities | No | Extra capabilities of the agent. |
Loading Agent configurations from file¶
Agent Configurations can be leaded into the registry directly into registry from serialized JSON using the following functions:
loadAgentsFromContent(byte[] content)
- Load agent configurations from serialized JSON content.loadAgentsFromFile(final String agentConfig)
- Load agent configurations from a file containing serialized JSON
Both methods return the list of agents that were loaded into the registry.
// Load agent configurations from file
final var agents = agentRegistry.loadAgentsFromFile("path/to/agent_config.json");
Agent Configuration File Format¶
Sample file format can be found below:
[
{
"agentName": "All-in-One Agent", //(1)!
"description": "Agent with HTTP, MCP, and Custom Tool capabilities.", //(2)!
"prompt": "Use all available tools to answer the query.", //(3)!
"inputSchema": { //(4)!
"type": "object",
"additionalProperties": false,
"required": [
"location"
],
"properties": {
"location": {
"description": "Location to know weather for",
"type": "string"
}
}
},
"outputSchema": { //(5)!
"type": "object",
"properties": {
"condition": {
"type": "string"
},
"temperature": {
"type": "string"
}
},
"required": [
"condition",
"temperature"
],
"additionalProperties": false
},
"capabilities": [ //(6)!
{
"type": "REMOTE_HTTP_CALLS", //(7)!
"selectedTools": { //(8)!
"weatherserver": [ //(9)!
"get_weather_for_location" //(10)!
]
}
},
{
"type": "MCP", //(11)!
"selectedTools": { //(12)!
"mcp": [ //(13)!
"add", //(14)!
"subtract"
]
}
},
{
"type": "CUSTOM_TOOLS", //(15)!
"selectedTools": [//(16)!
"getWeather",
"doSomethingCustom"
]
}
]
},
... //more agents
]
- Name of the agent (Mandatory)
- Description of the agent (Mandatory)
- Prompt to be used by the agent (Mandatory)
- Input schema for the agent. (Optional, defaults to a simple string input schema)
- Output schema for the agent (Optional, defaults to a simple string output schema)
- Capabilities of the agent (Optional, defaults to no capabilities).
- Fixed for Remote HTTP calls.
- HTTP tools exposed to this agent.
- Name of HTTP upstream as specified in HTTPToolboxFactory configuration.
- Set of tools selected from this upstream.
- Fixed for MCP calls.
- MCP tools exposed to this agent.
- Name of MCP upstream as specified when configuring MCP servers in MCPToolBoxFactory.
- Set of tools selected from this upstream.
- Fixed for Custom/local tools.
- Set of custom/local tools exposed to this agent as explained in Using Custom Tools.
Tools for Configured Agents¶
Configured agents can use tools from different sources. Sentinel AI provides built-in support for the following tool sources:
- HTTP ToolBox: Tools that make remote HTTP calls to upstream services. Check the section on HTTP ToolBox for more details.
- MCP ToolBox: Tools that make calls to MCP servers. Check the section on MCP ToolBox for more details.
- Custom ToolBox: Tools that are implemented in local code and registered with the agent factory.
flowchart TD
USER["User/Client"]
TOP_AGENT["Planner/Orchestrator Agent"]
subgraph ToolSources["Tool Sources"]
HTTP[HTTP Tool Box Factory]
MCP[MCP Tool Box Factory]
CUSTOM[Custom Tool Box]
end
AGENT_FACTORY["Agent Factory"]
REGISTRY["Agent Registry (Extension)"]
subgraph Agents["Configured Agents"]
AGENT1[ConfiguredAgent A]
AGENT2[ConfiguredAgent B]
end
USER -- Requests Work --> TOP_AGENT
TOP_AGENT -- Calls agents using tool calls --> REGISTRY --> AGENT_FACTORY -- Creates on the fly --> Agents
ToolSources -- Globally Registered Tools --> AGENT_FACTORY
Agents -- Uses Tools From --> ToolSources
TOP_AGENT -.-> Agents
When using Configured Agents and Agent Registry, tools from remote HTTP sources, MCP servers and local code are globally
registered to the AgentRegistry itself. Once registered, such tools can be used by individual agents as configured using
AgentCapability
when configuring it. This prevents pollution of the prompts sent to models being used individual agents.
Making remote HTTP calls¶
As we have seen in the section on HTTP ToolBox, Sentinel AI provides a flexible
way to configure and expose HTTP API calls to upstream services as parameterized tools that agents can use.
The HttpToolboxFactory
is used to create instances of HttpToolbox
on the fly as and when needed.
//Create HTTP Tool Source
final var toolSource = InMemoryHttpToolSource.builder() //Create tool source
.mapper(MAPPER)
.build();
//Bulk Load tools from YAML file
HttpToolReaders.loadToolsFromYAML(Paths.get("http-tools.yaml"), toolSource);
//Register custom HTTP tools to toolSource
toolSource.registerTool("my-service", ...);
//Add tools to toolSource any other way you want
//Create OkHttpClient instance to make the actual HTTP calls
final var okHttpClient = new OkHttpClient.Builder()
... //Custom configurations
.build();
//Create and pass the HttpToolboxFactory to ConfiguredAgentFactory
final var agentFactory = ConfiguredAgentFactory.builder()
.httpToolboxFactory(HttpToolboxFactory.builder() //All params mandatory
.toolConfigSource(toolSource)
.okHttpClient(okHttpClient)
.objectMapper(objectMapper)
.upstreamResolver(UpstreamResolver.direct())
.build())
.build();
OkHttp Client Provider
The HttpToolboxFactory
class provides another builder httpClientProvidingBuilder
which lets you configure a
provider function that resolves to a specific instance of OkHttpClient depending on the upstream name.
HttpToolboxFactory.httpClientProvidingBuilder()
.okHttpClientProvider(upstream -> myClientRegistery.getClient(upstream)) //or so on
...
.build()
Once configured, tools from this factory can be used by configured agents by using the
AgentCapabilities.remoteHttpCalls
capability.
final var weatherAgentConfig = AgentConfiguration.builder()
//Standard config
...
//Add http tool call capabilites
.capability(AgentCapabilities.remoteHttpCalls(
Map.of("userservice", //Upstream name
Set.of("get_user_name", "get_location_from_user"), //Set of tools from this upstream
"weatherserver", //Upstream name
Set.of("get_weather_for_location") //Set of tools from upstream
)))
.build();
Using tools from MCP servers¶
Similar to HttpToolboxFactory
, the McpToolboxFactory
is used to create instances of McpToolbox
on the fly as and
when needed. check the section on MCP ToolBox for more details on how to use
MCP servers with Sentinel AI.
// Load MCP json config from file
final var mcpToolboxFactory = MCPToolBoxFactory.builder()
.objectMapper(MAPPER)
.build();
// (Optionally) Load MCP server details from mcp JSON file
mcpToolboxFactory.loadFromFile("/path/to/mcp_config.json");
// (Optionally) Register clients for specific upstreams programmatically
mcpToolboxFactory.registerMcpClient("my-upstream", mcpClient);
// Create agent factory and pass the MCP toolbox factory
final var agentFactory = ConfiguredAgentFactory.builder()
.mcpToolboxFactory(mcpToolboxFactory)
.build();
Custom MCP Client resolution
You can also provide a custom MCP client resolver function to the McpToolboxFactory
builder that can resolve to a
specific instance of
McpClient
depending on the upstream name. This is useful when you have multiple MCP clients to connect to different
MCP servers and for some reason the registry won't work for you.
MCPToolBoxFactory.builder()
.mcpClientProvider(upstream -> myMcpClientRegistery.getClient(upstream)) //or so on
...
.build()
Once configured, tools from this factory can be used by configured agents by using the AgentCapabilities.mcpCalls
capability.
final var mathAgentConfig = AgentConfiguration.builder()
//Standard config, http tool boc factory etc
...
//Add mcp tool call capabilites
.capability(AgentCapabilities.mcpCalls(Map.of(
"metricservice", // Upstream/MCP server name as specified in MCP JSON config or during client registration
Set.of("load_timeseries"), // Set of tools from this MCP server
"mathservice", // Upstream/MCP server name
Set.of("multiply", "divide") // Set of tools from this MCP server
)));
.build();
Using Custom Tools¶
Custom tools are those that are implemented in local code and registered with the ConfiguredAgentFactory
. Custom tools
can be used to perform tasks that are specific to your application or domain and cannot be easily achieved using remote
HTTP calls or MCP calls. Custom tools can be anything from simple utility functions to complex business logic
implementations. Custom tools are registered globally with the ConfiguredAgentFactory
and individual agents get access to
only those tools that they are configured to use using the AgentCapabilities.customToolCalls
capability.
//Define tools as usual
@Tool("Provides the weather for a location")
public String getWeather(@JsonPropertyDescription("Name of the city to get weather for") final String city) {
return """
{
"location" : "Bangalore",
"temperature" : "33 centigrade",
"condition" : "sunny"
}
""";
}
// Create a global custom tool box and register custom tools
final var globalCustomToolBox = CustomToolBox.builder()
.name("custom")
.build();
// Register tools
globalCustomToolBox.registerToolsFromObject(this);
// Register the custom tool box with the agent factory
final var agentFactory = ConfiguredAgentFactory.builder()
.customToolBox(globalCustomToolBox)
.build();
Registering tools of various types
The CustomToolBox
class provides multiple ways to register tools.
Once the tools are registered globally with the ConfiguredAgentFactory
, they can be used by configured agents by using
the AgentCapabilities.customToolCalls
capability.
// Create agent configured factory and register custom tools
final var agentConfig = AgentConfiguration.builder()
//Standard config, http tool boc factory etc
...
//Add custom tool call capabilites
.capability(AgentCapabilities.customToolCalls(Set.of("getWeather", "getName")))
.build();