Docs
Advanced Subsystems
Builder Pattern

Builder Pattern

⚠️

This is a v2 design — not yet implemented. See Architecture v2 for context.

The problem

AgentRuntime requires several components — core, provider, registry, policy, memory, hooks. The current constructor has many positional parameters:


// Hard to read, easy to get wrong
let runtime = AgentRuntime::new(
Box::new(core), Box::new(provider), registry,
Box::new(policy), Some(Box::new(memory)), Box::new(hooks), config,
);

AgentRuntimeBuilder

Non-generic fluent builder with sensible defaults:


pub struct AgentRuntimeBuilder {
core: Option<Box<dyn AgentCore>>,
provider: Option<Box<dyn LlmProvider>>,
registry: Option<PluginRegistry>,
policy: Option<Box<dyn RuntimePolicy>>,
memory: Option<Box<dyn MemoryPlugin>>,
hooks: Option<Box<dyn AgentHooks>>,
config: Option<AgentConfig>,
}
impl AgentRuntimeBuilder {
pub fn new() -> Self;
pub fn core(mut self, core: impl AgentCore + 'static) -> Self; // required
pub fn provider(mut self, provider: impl LlmProvider + 'static) -> Self; // required
pub fn registry(mut self, registry: PluginRegistry) -> Self; // optional
pub fn policy(mut self, policy: impl RuntimePolicy + 'static) -> Self;
pub fn memory(mut self, memory: impl MemoryPlugin + 'static) -> Self;
pub fn hooks(mut self, hooks: impl AgentHooks + 'static) -> Self;
pub fn config(mut self, config: AgentConfig) -> Self;
pub fn build(self) -> Result<AgentRuntime, BuildError>; // sync, not async
}

Usage examples


// Minimal — just core + provider
let runtime = AgentRuntime::builder()
.core(ReActCore::default())
.provider(OpenAiProvider::new(url, key))
.build()?;
// Full configuration
let runtime = AgentRuntime::builder()
.core(ReActCore::new(system_prompt))
.provider(OpenAiProvider::new(url, key))
.registry(registry)
.policy(DefaultPolicy::standard())
.memory(CompositeMemory::new()
.add("conversation", ConversationMemory::new(thread_id))
.add("working", WorkingMemory::new()))
.hooks(CompositeHooks::new()
.add(LoggingHooks::new(log::Level::Info))
.add(RateLimitHooks::new(10)))
.build()?;
// Jan Desktop embedding
let runtime = AgentRuntime::builder()
.core(ReActCore::new(system_prompt))
.provider(OpenAiProvider::new("http://localhost:6767/v1", key))
.registry(PluginRegistry::new().with_tools(BuiltInTools::all()))
.memory(ConversationMemory::new(thread_id))
.build()?;

Defaults

ComponentDefaultBehavior
registryPluginRegistry::default()Empty — no tools
policyHostPolicyNo restrictions
memoryNullMemoryNo persistence
hooksNoOpHooksNo hooks — zero overhead
configAgentConfig::default()Default token limits

Build-time validation

build() validates required components:


pub fn build(self) -> Result<AgentRuntime, BuildError> {
let core = self.core.ok_or(BuildError::Missing("core"))?;
let provider = self.provider.ok_or(BuildError::Missing("provider"))?;
// optional components use defaults
Ok(AgentRuntime { core, provider, ... })
}
pub enum BuildError {
Missing(&'static str),
}

Design decisions

DecisionRationale
No genericsAll trait objects — AgentRuntimeBuilder has no type parameters
Sync buildNo actors or async spawning — build() is synchronous
Two required fieldsCore + provider are the minimum for a functional agent
impl Trait parametersBuilder auto-boxes — callers pass concrete types, not Box<dyn ...>