Skip to content

The Three Enriches

AI Butler’s YAML config isn’t a flat bag of knobs. It’s organized into three tiers with progressive disclosure: simple toggles at the top, power-user structure in the middle, developer tunables at the bottom. This pattern is called the Three Enriches.

Every configurable parameter belongs to exactly one tier. This is what makes the setup wizard, web UI, and AI-assisted configuration possible without drowning new users in choices.

TierAudienceExposed in
SettingsEveryoneSetup wizard, web form, natural language
ConfigurationsPower usersWeb form (advanced), YAML, natural language
OptionsDevelopersYAML only

“Everyday knobs.” The setup wizard shows only these. They’re simple values like timezone, enabled channels, persona name, cost strategy.

settings:
language: en
timezone: Europe/Madrid
persona_name: "Butler"
active_channels:
- webchat
- telegram
model: claude-sonnet-4-6
agent_mode: swarm
cost:
strategy: balanced # frugal | balanced | quality
monthly_budget: 25 # USD
offline_mode: false
telemetry_enabled: false

“Power-user structure.” Used by people who know what they want. Organized by subsystem — models, channels, embeddings, MCP, IoT, plugins, etc.

configurations:
models:
primary: claude-sonnet-4-6
fallback: claude-haiku-4-5
channels:
telegram:
enabled: true
typing_indicators: true
voice_response: auto
slack:
enabled: true
typing_indicators: true
voice_response: text
web:
port: 3377
bind_address: 0.0.0.0
max_upload_size_mb: 25
embedding:
provider: ollama # openai | ollama | openai_compat | "" (auto-detect)
model: nomic-embed-text
mcp:
servers:
- name: filesystem
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/me"]
iot:
adapter: stub # stub (default); a homeassistant adapter is on the roadmap
plugins:
auto_enable: false
plugin_dir: /data/plugins

“Developer tunables.” Fine-grained internals — database timeouts, model sampling parameters, plugin sandbox limits. The average user never touches these.

options:
models:
temperature: 0.7
max_tokens: 8192
request_timeout: 120s
retry_count: 3
database:
busy_timeout: 5000 # ms
plugins:
max_plugins: 20
max_memory_mb: 64
exec_timeout: 30s
transaction:
per_transaction_limit: 5 # max USD per single transaction
daily_limit: 20 # max USD per day

The same question has to be answered at multiple levels of detail. Take “which model should I use?”:

  • Settings: model: claude-sonnet-4-6 — pick one model, done
  • Configurations: primary: claude-sonnet-4-6, fallback: claude-haiku-4-5 — specific models, fallback strategy
  • Options: temperature: 0.7, max_tokens: 8192, request_timeout: 120s — sampling and request tuning

Each tier is sufficient on its own. A user who only sets settings.model gets sensible defaults for everything else. A developer who needs to tune options.models.temperature still inherits the model choice from settings.model.

At startup, values are resolved in this order:

  1. Built-in defaults
  2. Your config file — ~/.aibutler/config.yaml (override the path with the AIBUTLER_CONFIG environment variable; there is no --config flag)
  3. Cross-tier resolution: settings.model, when set, overrides configurations.models.primary

Later sources win. This means you can leave configurations.models.primary unset and let settings.model drive the whole model stack.

Plugins declare their own parameters using the same three tiers in their plugin.toml manifest. Each parameter has a type, a default, and a description:

name = "weather-plugin"
version = "1.0.0"
[settings.default_location]
type = "string"
default = "Madrid"
description = "City used when no location is given"
[configurations.cache_ttl_minutes]
type = "int"
default = 15
description = "How long to cache weather responses"
[options.http_timeout_seconds]
type = "int"
default = 10
description = "HTTP request timeout"

The plugin management UI shows the right parameters at the right disclosure level — end users see default_location, developers see http_timeout_seconds.