Guilds¶
A Guild is a logical grouping of agents in Rustic AI. It acts as a collaborative environment, managing the lifecycle, execution, and coordination of its member agents. Guilds provide a shared context, including common dependencies, message routing infrastructure, and state management facilities.
Purpose¶
- Organize Agents: Group agents into functional units for specific tasks or workflows.
- Lifecycle Management: Control the registration, launching, execution, and removal of agents.
- Coordinated Execution: Define how agents run, leveraging different Execution Engines.
- Message Orchestration: Define sophisticated Messaging flows between agents using routes.
- Shared Resources: Provide shared Dependencies and State for member agents.
Guild Specification (GuildSpec
)¶
A GuildSpec
is a declarative blueprint that defines the structure and behavior of a Guild. It's typically defined in a YAML or JSON file, or constructed programmatically using the GuildBuilder
.
Key fields in a GuildSpec
:
id
(str): A unique identifier for the Guild. Defaults to ashortuuid
.name
(str): A human-readable name for the Guild (1-64 characters).description
(str): A detailed description of the Guild's purpose.properties
(Dict[str, Any]): A dictionary for guild-specific configurations. This often includes:execution_engine
(str): The fully qualified class name of the Execution Engine to use (e.g.,"rustic_ai.core.guild.execution.sync.sync_exec_engine.SyncExecutionEngine"
).messaging
(Dict): Configuration for the Messaging system, includingbackend_module
,backend_class
, andbackend_config
.client_type
(str): The defaultClient
class for agents.client_properties
(Dict): Default properties for agent clients.
agents
(List[AgentSpec]): A list of Agent Specifications (AgentSpec
) that define the agents belonging to this guild.dependency_map
(Dict[str, DependencySpec]): A mapping of dependency keys to Dependency Specifications (DependencySpec
) available to all agents in the guild. These can be overridden at the agent level.routes
(RoutingSlip): Defines the message routing rules within the guild. This is a powerful mechanism for orchestrating agent interactions.
Example GuildSpec
(Conceptual JSON)¶
{
"id": "research-guild-01",
"name": "Research Guild",
"description": "A guild to research topics using multiple agents.",
"properties": {
"execution_engine": "rustic_ai.core.guild.execution.sync.sync_exec_engine.SyncExecutionEngine",
"messaging": {
"backend_module": "rustic_ai.core.messaging.backend",
"backend_class": "InMemoryMessagingBackend",
"backend_config": {}
}
},
"agents": [
// ... AgentSpec definitions here ... (see Agents section below)
],
"dependency_map": {
"database": {
"class_name": "my_package.resolvers.DatabaseResolver",
"properties": {
"connection_string": "postgresql://user:pass@host/db"
}
},
"api_client": {
"class_name": "my_package.resolvers.ApiClientResolver",
"properties": {
"api_key": "secret-key",
"base_url": "https://api.example.com"
}
}
},
"routes": {
// ... RoutingSlip definition here ... (see Routes section below)
}
}
Agents within a Guild¶
The agents
field in GuildSpec
is a list of AgentSpec
objects. Each AgentSpec
defines an individual agent within the guild. Key AgentSpec
fields include:
id
(str): Unique ID for the agent.name
(str): Human-readable name.description
(str): Agent's purpose.class_name
(str): Fully qualified class name of the agent (e.g.,"rustic_ai.agents.llm.litellm.litellm_agent.LiteLLMAgent"
).additional_topics
(List[str]): Specific message topics the agent subscribes to.properties
(Dict[str, Any]): Agent-specific configuration (e.g., LLM model, API keys).listen_to_default_topic
(bool): Whether the agent listens to the guild's default topic.dependency_map
(Dict[str, DependencySpec]): Agent-specific dependencies, which can override guild-level ones.
Refer to the Agents documentation for a complete overview of AgentSpec
.
Example Agent in a Guild:¶
// Inside GuildSpec.agents list
{
"id": "research_manager",
"name": "Research Manager",
"description": "A manager for the research process",
"class_name": "rustic_ai.agents.laira.research_manager.ResearchManager",
"properties": {
"llm_model": "gpt-4o"
},
"listen_to_default_topic": true
}
Guild-Level Dependencies (dependency_map
)¶
The dependency_map
in GuildSpec
allows you to define dependencies that are available to all agents within the guild. This is useful for sharing resources like database connections, API clients, or configuration objects.
- Each key in the map is a name by which agents can request the dependency.
- The value is a
DependencySpec
that defines how to resolve and instantiate the dependency. - Agent-specific
dependency_map
entries can override guild-level ones with the same key.
See Dependencies for more details on how dependency injection works.
Message Routing (routes
)¶
The routes
field in GuildSpec
defines a RoutingSlip
. A RoutingSlip is a list of RoutingRule
objects (often called "steps") that dictate how messages are processed and forwarded within the guild. This enables complex workflows and orchestrations.
RoutingSlip
Structure:¶
A RoutingSlip
primarily contains a list of steps
, where each step is a RoutingRule
.
// GuildSpec.routes
"routes": {
"steps": [
// ... RoutingRule definitions here ...
]
}
RoutingRule
(Step) Structure:¶
Each RoutingRule
defines how a message, upon matching certain criteria, should be transformed and sent to a destination. Key fields include:
agent
(AgentTag) oragent_type
(str):agent
: Specifies a particular agent instance by itsid
orname
(e.g.,{"name": "echo_agent_1"}
). This rule applies to messages produced by this specific agent.agent_type
: Specifies an agent class (e.g.,"rustic_ai.agents.utils.user_proxy_agent.UserProxyAgent"
). This rule applies to messages produced by any agent of this type.
method_name
(str, optional): The name of the method on the source agent that produced the message. If specified, the rule only applies to messages from this method.origin_filter
(Dict, optional): Further filters messages based on their origin:origin_sender
(AgentTag): The original sender of the message.origin_topic
(str): The topic the original message was on.origin_message_format
(str): The Pydantic model type of the original message.
message_format
(str, optional): The Pydantic model type (fully qualified name) of the incoming message payload this rule should match (e.g.,"rustic_ai.ui_protocol.types.TextFormat"
).transformer
(Dict, optional): Defines how to transform the incoming message payload before sending it to the destination.expression
(str): A JSONata expression or a custom transformation logic to apply to the incoming message payload.output_format
(str): The Pydantic model type (fully qualified name) of the transformed payload (e.g.,"rustic_ai.agents.laira.research_manager.UserQuery"
). Example:{"expression": "{\"query\": text}", "output_format": "rustic_ai.agents.laira.research_manager.UserQuery"}
destination
(Dict, optional): Specifies where the (transformed) message should be sent. Ifnull
, the message is typically sent back to the original sender or follows routing slip logic from the incoming message.topics
(str | List[str]): The topic(s) to publish the message to (e.g.,"echo_topic"
,"user_message_broadcast"
).recipient_list
(List[AgentTag]): A list of specific agents to send the message to.priority
(int, optional): Message priority.
route_times
(int): How many times this rule can be applied.1
means it applies once.-1
means it can apply indefinitely for messages matching its criteria.mark_forwarded
(bool, default:false
): Iftrue
, marks the message as forwarded, which can influence subsequent routing decisions.
Example Route Step:¶
This rule takes a TextFormat
message from a UserProxyAgent
, transforms its text
field into a UserQuery
's query
field, and sends it to the default_topic
.
// Inside RoutingSlip.steps list
{
"agent_type": "rustic_ai.agents.utils.user_proxy_agent.UserProxyAgent",
"method_name": "unwrap_and_forward_message", // From UserProxyAgent
"message_format": "rustic_ai.ui_protocol.types.TextFormat",
"transformer": {
"expression": "{\"query\": text}",
"output_format": "rustic_ai.agents.laira.research_manager.UserQuery"
},
"destination": {
"topics": "default_topic"
},
"route_times": 1
}
Routing slips enable powerful patterns: - Sequential Processing: Message passed from agent A -> B -> C. - Fan-out/Fan-in: One message triggers multiple agents, results are aggregated. - Content-Based Routing: Message content dictates the next recipient. - Transformations: Messages are reshaped between agents to match differing interfaces.
Guild Lifecycle & Management¶
Managing the lifecycle of a Guild involves defining its structure, instantiating it, launching its agents, facilitating their interactions, and eventually shutting it down. Rustic AI provides different mechanisms for bringing a Guild to life, suited for development versus production environments.
- Definition (
GuildSpec
): The blueprint for a Guild is itsGuildSpec
. This can be defined in a YAML/JSON file or constructed programmatically usingGuildBuilder
andAgentBuilder
.
```python # Basic programmatic GuildSpec construction from rustic_ai.core.guild.builders import GuildBuilder, AgentBuilder, RouteBuilder from rustic_ai.core.guild.dsl import GuildSpec, AgentSpec, RoutingSlip, RoutingRule from rustic_ai.core.messaging.core.message import AgentTag # Assuming EchoAgent and UserProxyAgent are defined/imported # from rustic_ai.core.agents.testutils import EchoAgent # from rustic_ai.core.agents.utils import UserProxyAgent
# Define an AgentSpec
echo_agent_spec = AgentBuilder(EchoAgent) \
.set_name("echo_agent_1") \
.set_description("Echoes input") \
.add_additional_topic("echo_topic") \
.listen_to_default_topic(False) \
.build_spec()
# Define a Route
route1 = RouteBuilder(from_agent=UserProxyAgent) \
.from_method("unwrap_and_forward_message") \
.on_message_format("rustic_ai.ui_protocol.types.TextFormat") \
.set_destination_topics("echo_topic") \
.set_route_times(1) \
.build()
route2 = RouteBuilder(from_agent=AgentTag(name="echo_agent_1")) \
.set_destination_topics("user_message_broadcast") \
.set_route_times(1) \
.build()
routing_slip = RoutingSlip(steps=[route1, route2])
# Build the GuildSpec
guild_builder = GuildBuilder(guild_name="MyDevGuild", guild_description="For local testing.") \
.set_execution_engine("rustic_ai.core.guild.execution.sync.sync_exec_engine.SyncExecutionEngine") \
.set_messaging(
backend_module="rustic_ai.core.messaging.backend",
backend_class="InMemoryMessagingBackend",
backend_config={}
) \
.add_agent_spec(echo_agent_spec) \
.set_routes(routing_slip)
guild_spec = guild_builder.build_spec()
```
- Instantiation and Launching Agents:
There are two main approaches to instantiate a Guild
from a GuildSpec
and launch its agents, provided by the GuildBuilder
:
* **`guild_builder.launch()` - For Development & Testing:**
* **How it works:** This method is a convenient way to quickly bring up an entire Guild and its agents in the current environment.
1. It first creates a `Guild` instance from the `GuildSpec` (a "shallow" guild without agents running).
2. Then, it iterates through all `AgentSpec`s defined within the `GuildSpec` and calls `guild.launch_agent(agent_spec)` for each. The `guild.launch_agent()` method handles registering the agent with the guild and then uses the Guild's configured `ExecutionEngine` to start the agent.
* **Outcome:** A fully operational `Guild` with all its agents running.
* **Use Case:** Best suited for local development, running examples, and automated tests where immediate, self-contained execution is desired.
```python
# Assuming guild_builder is configured as above
# development_guild = guild_builder.launch(org_id="myawesomeorgid")
# print(f"Guild '{development_guild.name}' launched with {development_guild.get_agent_count()} agent(s).")
# # ... interact with the guild ...
# development_guild.shutdown()
```
* **`guild_builder.bootstrap(metastore_database_url: str, org_id: str = "myawesomeorgid")` - For Production:**
* **How it works:** This method is designed for more robust, production-like deployments.
1. It also starts by creating a "shallow" `Guild` instance from the `GuildSpec`.
2. However, instead of directly launching the agents from the `GuildSpec`, it instantiates and launches a special system agent called `GuildManagerAgent`.
3. This `GuildManagerAgent` is configured with the original `GuildSpec` and the `metastore_database_url`.
4. The `GuildManagerAgent` then takes on the responsibility of persisting the `GuildSpec` to the metastore and managing the lifecycle (launching, monitoring, stopping) of the user-defined agents based on this persisted specification.
* **Outcome:** A `Guild` instance where the primary user-defined agents are managed by the `GuildManagerAgent`, facilitating persistence, and potentially more advanced lifecycle operations suitable for production (like distributed execution, depending on the execution engine).
* **Use Case:** Recommended for production or staging environments where Guilds need to be persistent, managed centrally, and potentially operate in a distributed manner.
```python
# Assuming guild_builder is configured as above
# METASTORE_URL = "sqlite:///./rusticai_metastore.db" # Example
# production_guild = guild_builder.bootstrap(metastore_database_url=METASTORE_URL, org_id="myawesomeorgid")
# print(f"Guild '{production_guild.name}' bootstrapped. GuildManagerAgent is running.")
# # Agents defined in guild_spec will be launched by GuildManagerAgent
# # ... (Guild operates, likely in a separate process or managed environment) ...
# # Shutdown would typically be managed by the environment or the GuildManagerAgent's signals
```
**Important Note on `guild.launch_agent()`:** While `guild.launch_agent(agent_spec)` is the underlying method that starts an individual agent, you typically **do not call it directly** when setting up a guild from a specification. Instead, rely on `guild_builder.launch()` for development or `guild_builder.bootstrap()` for production scenarios, as these methods handle the overall setup process correctly.
-
Interaction: Once launched, agents within the Guild communicate via messages, with their interactions orchestrated by the Guild's defined routes and messaging system.
-
Agent Management (Runtime): After initial launch, you might interact with the guild or its agents:
- Via API endpoints if the Guild is managed by a service.
- Programmatically in some scenarios (less common for bootstrapped guilds directly):
guild.remove_agent(agent_id)
: Stops and removes an agent from the guild's tracking and execution engine.guild.get_agent(agent_id)
: Retrieves anAgentSpec
by its ID.guild.list_agents()
: Lists allAgentSpec
s currently registered with the guild instance.guild.list_all_running_agents()
: ListsAgentSpec
s of agents the execution engine reports as running for this guild.
-
Shutdown:
- For guilds started with
guild_builder.launch()
: Callguild.shutdown()
. This will attempt to gracefully stop all agents and release resources associated with the guild and its execution engine. - For guilds started with
guild_builder.bootstrap()
: Shutdown is typically managed by the environment hosting theGuildManagerAgent
or through signals sent to it. TheGuildManagerAgent
would then handle the graceful shutdown of the agents it manages.
- For guilds started with
Guild Object API¶
Once a Guild
object is instantiated (e.g., via GuildBuilder().launch(org_id="myawesomeorgid")
or GuildBuilder().load()
), it provides several methods for interaction and introspection:
guild.id
,guild.name
,guild.description
: Access basic properties.guild.register_agent(agent_spec: AgentSpec)
: Registers an agent spec with the guild. Does not run the agent.guild.launch_agent(agent_spec: AgentSpec, execution_engine: Optional[ExecutionEngine] = None)
: (As noted, primarily for internal/system use) Registers and runs an agent using the specified or guild's default execution engine.guild.remove_agent(agent_id: str)
: Stops the agent in the execution engine and removes it from the guild's internal tracking.guild.get_agent(agent_id: str) -> Optional[AgentSpec]
: Retrieves a registered agent's specification by ID.guild.get_agent_by_name(name: str) -> Optional[AgentSpec]
: Retrieves a registered agent's specification by name.guild.list_agents() -> List[AgentSpec]
: Returns a list of all registered (not necessarily running) agent specifications.guild.list_all_running_agents() -> List[AgentSpec]
: Queries the execution engine for agents it considers currently running for this guild.guild.get_agent_count() -> int
: Returns the number of registered agents.guild.to_spec() -> GuildSpec
: Converts the current state of the guild instance (including its registered agents, routes, etc.) back into aGuildSpec
object.guild.shutdown()
: Initiates the shutdown process for the guild and its associated execution engine(s), attempting to stop all managed agents.
Execution Engines¶
Guilds use execution engines to manage how agents are run. Supported modes include synchronous, multithreaded, and more (see Execution). The execution engine is typically specified in the GuildSpec.properties
.
Observability & Remote Management¶
Large deployments expose a Guild Control Plane (HTTP/REST or gRPC) that allows operators to:
Endpoint | Verb | Description |
---|---|---|
/agents |
GET | List running agents |
/agents |
POST | Register + launch a new agent |
/agents/{id} |
DELETE | Stop and remove an agent |
/state |
GET | Dump guild-level state snapshot |
/metrics |
GET | Prometheus scrape endpoint |
These endpoints are backed by the same APIs shown earlier (register_agent
, launch_agent
, etc.) and secured via JWT bearer tokens.
Monitoring¶
Guilds emit the following metrics:
* guild_agents_running
– Gauge labelled by guild_id
and agent_type
.
* guild_messages_total
– Counter for messages processed.
* guild_errors_total
– Counter for processing errors.
Integrate with Grafana dashboards for a helicopter view of the system.
Advanced Topics¶
- Dynamic Guilds: Guilds can be created, modified, and managed at runtime, especially when using a
GuildManagerAgent
. - State Propagation: Guild-level state changes can be broadcast or made available to agents.
- Cross-Guild Communication: While guilds provide encapsulation, advanced scenarios might involve routing messages between guilds.
See the Agents, Messaging, Execution, and Dependencies sections for more details on related components.