AgentCapabilities & AgentRequirements
The capability system enables agent discovery, matching, and routing based on required features and tags. It consists of two main types:
- AgentCapabilities - Describes what an agent can do
- AgentRequirements - Describes what is needed for a task
Location: mofa-kernel/src/agent/capabilities.rs
AgentCapabilities
Type Definition
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentCapabilities {
pub tags: HashSet<String>,
pub input_types: HashSet<InputType>,
pub output_types: HashSet<OutputType>,
pub max_context_length: Option<usize>,
pub reasoning_strategies: Vec<ReasoningStrategy>,
pub supports_streaming: bool,
pub supports_conversation: bool,
pub supports_tools: bool,
pub supports_coordination: bool,
pub custom: HashMap<String, serde_json::Value>,
}
Fields
tags
HashSet<String>
default:"{}"
Capability tags like "llm", "coding", "research" for discovery and matching
input_types
HashSet<InputType>
default:"{}"
Supported input types (Text, Image, Audio, Video, Structured, Binary)
output_types
HashSet<OutputType>
default:"{}"
Supported output types (Text, Json, Stream, Binary, Multimodal)
Maximum context length for LLM-based agents (in tokens)
reasoning_strategies
Vec<ReasoningStrategy>
default:"[]"
Supported reasoning strategies (Direct, ReAct, ChainOfThought, etc.)
Whether streaming output is supported
Whether multi-turn conversation is supported
Whether tool calling is supported
Whether multi-agent coordination is supported
custom
HashMap<String, serde_json::Value>
default:"{}"
Custom capability flags for domain-specific features
Constructor Methods
impl AgentCapabilities {
pub fn new() -> Self;
pub fn builder() -> AgentCapabilitiesBuilder;
}
Query Methods
has_tag
fn(&self, tag: &str) -> bool
Checks if a specific tag is presentif caps.has_tag("llm") {
println!("This is an LLM agent");
}
supports_input
fn(&self, input_type: &InputType) -> bool
Checks if a specific input type is supportedif caps.supports_input(&InputType::Image) {
println!("Agent can process images");
}
supports_output
fn(&self, output_type: &OutputType) -> bool
Checks if a specific output type is supported
matches
fn(&self, requirements: &AgentRequirements) -> bool
Checks if capabilities match the given requirementsif caps.matches(&requirements) {
println!("Agent meets requirements");
}
match_score
fn(&self, requirements: &AgentRequirements) -> f64
Calculates match score (0.0 - 1.0+) for ranking agentslet score = caps.match_score(&requirements);
println!("Match score: {:.2}", score);
AgentCapabilitiesBuilder
Builder Methods
tag
fn(self, tag: impl Into<String>) -> Self
Adds a capability taglet builder = AgentCapabilitiesBuilder::new()
.tag("llm")
.tag("coding");
tags
fn(self, tags: impl IntoIterator<Item = impl Into<String>>) -> Self
Adds multiple tags at oncelet builder = AgentCapabilitiesBuilder::new()
.tags(vec!["llm", "research", "analysis"]);
input_type
fn(self, input_type: InputType) -> Self
Adds a supported input type
output_type
fn(self, output_type: OutputType) -> Self
Adds a supported output type
max_context_length
fn(self, length: usize) -> Self
Sets maximum context length
reasoning_strategy
fn(self, strategy: ReasoningStrategy) -> Self
Adds a supported reasoning strategy
supports_streaming
fn(self, supports: bool) -> Self
Sets streaming support flag
supports_conversation
fn(self, supports: bool) -> Self
Sets conversation support flag
supports_tools
fn(self, supports: bool) -> Self
Sets tool support flag
supports_coordination
fn(self, supports: bool) -> Self
Sets coordination support flag
custom
fn(self, key: impl Into<String>, value: serde_json::Value) -> Self
Adds a custom capability
build
fn(self) -> AgentCapabilities
Builds the capabilities object
Example Usage
use mofa_sdk::kernel::{AgentCapabilities, InputType, OutputType, ReasoningStrategy};
let caps = AgentCapabilities::builder()
.tag("llm")
.tag("coding")
.tag("assistant")
.input_type(InputType::Text)
.output_type(OutputType::Text)
.output_type(OutputType::Json)
.max_context_length(128000)
.reasoning_strategy(ReasoningStrategy::ReAct { max_iterations: 10 })
.supports_streaming(true)
.supports_conversation(true)
.supports_tools(true)
.custom("model", json!("gpt-4o"))
.build();
AgentRequirements
Type Definition
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentRequirements {
pub required_tags: HashSet<String>,
pub preferred_tags: HashSet<String>,
pub input_types: HashSet<InputType>,
pub output_types: HashSet<OutputType>,
pub requires_streaming: bool,
pub requires_tools: bool,
pub requires_conversation: bool,
pub requires_coordination: bool,
}
Fields
required_tags
HashSet<String>
default:"{}"
Tags that the agent MUST have (hard requirement)
preferred_tags
HashSet<String>
default:"{}"
Tags that are nice to have (used for ranking)
input_types
HashSet<InputType>
default:"{}"
Required input types the agent must support
output_types
HashSet<OutputType>
default:"{}"
Required output types the agent must support
Whether streaming output is required
Whether tool support is required
Whether multi-turn conversation is required
Whether multi-agent coordination is required
Constructor Methods
impl AgentRequirements {
pub fn new() -> Self;
pub fn builder() -> AgentRequirementsBuilder;
}
Query Methods
matches
fn(&self, capabilities: &AgentCapabilities) -> bool
Checks if given capabilities meet these requirements
score
fn(&self, capabilities: &AgentCapabilities) -> f32
Calculates match score for ranking
AgentRequirementsBuilder
Builder Methods
require_tag
fn(self, tag: impl Into<String>) -> Self
Adds a required tag (hard requirement)
prefer_tag
fn(self, tag: impl Into<String>) -> Self
Adds a preferred tag (for ranking)
require_input
fn(self, input_type: InputType) -> Self
Requires a specific input type
require_output
fn(self, output_type: OutputType) -> Self
Requires a specific output type
Requires streaming support
Requires conversation support
Requires coordination support
build
fn(self) -> AgentRequirements
Builds the requirements object
Example Usage
use mofa_sdk::kernel::{AgentRequirements, InputType, OutputType};
let requirements = AgentRequirements::builder()
.require_tag("llm") // Must have
.prefer_tag("coding") // Nice to have
.prefer_tag("research")
.require_input(InputType::Text)
.require_output(OutputType::Text)
.require_tools()
.require_streaming()
.build();
ReasoningStrategy
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub enum ReasoningStrategy {
#[default]
Direct,
ReAct { max_iterations: usize },
ChainOfThought,
TreeOfThought { branching_factor: usize },
Custom(String),
}
Variants
Direct LLM reasoning without structured approach
ReAct
{ max_iterations: usize }
ReAct-style Thought-Action-Observation loopReasoningStrategy::ReAct { max_iterations: 10 }
Chain of Thought reasoning (step-by-step)
TreeOfThought
{ branching_factor: usize }
Tree of Thought exploration with branchingReasoningStrategy::TreeOfThought { branching_factor: 3 }
Custom reasoning modeReasoningStrategy::Custom("hybrid-reasoning".to_string())
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum InputType {
Text,
Image,
Audio,
Video,
Structured(String),
Binary,
}
OutputType
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum OutputType {
Text,
Json,
StructuredJson,
Stream,
Binary,
Multimodal,
}
Complete Example
Defining Agent Capabilities
use mofa_sdk::kernel::{
AgentCapabilities, InputType, OutputType, ReasoningStrategy
};
use serde_json::json;
// LLM coding assistant
let coding_agent_caps = AgentCapabilities::builder()
.tags(vec!["llm", "coding", "rust"])
.input_type(InputType::Text)
.output_type(OutputType::Text)
.output_type(OutputType::Json)
.max_context_length(128000)
.reasoning_strategy(ReasoningStrategy::ChainOfThought)
.supports_streaming(true)
.supports_conversation(true)
.supports_tools(true)
.custom("programming_languages", json!(["rust", "python", "go"]))
.build();
// Research assistant
let research_agent_caps = AgentCapabilities::builder()
.tags(vec!["llm", "research", "web-search"])
.input_type(InputType::Text)
.output_type(OutputType::Text)
.reasoning_strategy(ReasoningStrategy::ReAct { max_iterations: 15 })
.supports_tools(true)
.supports_conversation(true)
.custom("search_engines", json!(["google", "bing"]))
.build();
Matching Agents to Requirements
use mofa_sdk::kernel::{AgentRequirements, InputType};
let requirements = AgentRequirements::builder()
.require_tag("llm")
.require_tag("coding")
.prefer_tag("rust")
.require_input(InputType::Text)
.require_tools()
.build();
// Check if agent matches
if coding_agent_caps.matches(&requirements) {
let score = coding_agent_caps.match_score(&requirements);
println!("Coding agent matches! Score: {:.2}", score);
}
if research_agent_caps.matches(&requirements) {
let score = research_agent_caps.match_score(&requirements);
println!("Research agent matches! Score: {:.2}", score);
} else {
println!("Research agent doesn't match requirements");
}
Agent Registry with Capability Matching
use mofa_sdk::kernel::{AgentCapabilities, AgentRequirements};
use std::collections::HashMap;
struct AgentRegistry {
agents: HashMap<String, AgentCapabilities>,
}
impl AgentRegistry {
fn find_best_match(&self, requirements: &AgentRequirements) -> Option<String> {
self.agents
.iter()
.filter(|(_, caps)| caps.matches(requirements))
.max_by(|(_, caps_a), (_, caps_b)| {
caps_a.match_score(requirements)
.partial_cmp(&caps_b.match_score(requirements))
.unwrap()
})
.map(|(id, _)| id.clone())
}
}
// Usage
let mut registry = AgentRegistry { agents: HashMap::new() };
registry.agents.insert("coding-agent".to_string(), coding_agent_caps);
registry.agents.insert("research-agent".to_string(), research_agent_caps);
if let Some(agent_id) = registry.find_best_match(&requirements) {
println!("Best match: {}", agent_id);
}
See Also