Skip to content

Custom Classifiers

Any type that conforms to Classifier can be passed to the Orchestrator in place of LLMClassifier. This is the right choice when you need deterministic routing (keyword rules, regex, external service calls, ML models) without an LLM round-trip.

See the Classifiers overview for how ClassifierResult drives the dispatch.

public protocol Classifier: Sendable {
func classify(
_ input: String,
history: [ConversationMessage],
agents: [any AgentProtocol]
) async throws -> ClassifierResult
}

Your implementation receives:

ParameterTypeNotes
inputStringThe user’s current message.
history[ConversationMessage]Merged, [agentId]-prefixed conversation from chat storage.
agents[any AgentProtocol]The full set of agents registered with the Orchestrator.

It must return a ClassifierResult:

public struct ClassifierResult: Sendable {
public let selectedAgent: (any AgentProtocol)?
public let confidence: Double
public init(selectedAgent: (any AgentProtocol)?, confidence: Double)
}

A simple classifier that matches agent names against words in the user’s input:

struct KeywordClassifier: Classifier {
func classify(
_ input: String,
history: [ConversationMessage],
agents: [any AgentProtocol]
) async throws -> ClassifierResult {
let lower = input.lowercased()
let match = agents.first { agent in
lower.contains(agent.name.lowercased())
}
return ClassifierResult(
selectedAgent: match,
confidence: match != nil ? 1.0 : 0.0
)
}
}

Pass it to the Orchestrator exactly like the built-in one:

let orchestrator = Orchestrator(classifier: KeywordClassifier(), storage: storage)
  • Return ClassifierResult(selectedAgent: nil, confidence: 0.0) when no agent matches; the Orchestrator falls back to the first registered agent.
  • confidence is captured for tracing only and does not affect dispatch. Use it to record routing certainty without building threshold logic into the Orchestrator.
  • classify is async throws, so you can call remote services, run Core ML inference, or await any other async work.
  • Because Classifier inherits Sendable, all stored state must be Sendable-safe (value types or actors).