Skip to main content
Set default values for all Agent parameters using a configuration file. When you pass True to a feature, it uses your configured defaults.
The user sets defaults in .praisonai/config.toml; explicit Agent parameters still override the file. Config files are deep-merged across the whole precedence chain — your global ~/.praisonai/config.* supplies defaults, and every project config down to your current directory overrides only the keys it sets.

How It Works

PraisonAI discovers every config file from your global home down to your current directory, then deep-merges them so unset keys fall through and the nearest project file wins for keys it sets. Precedence (highest to lowest):
  1. Explicit parameters in code
  2. Environment variables
  3. Nearest project config (deepest ancestor of cwd)
  4. Ancestor project configs (root → parent-of-cwd)
  5. User global (~/.praisonai/config.*)
  6. Built-in defaults

Nearest Project Config

.praisonai/config.toml nearest to cwd — wins for keys it sets

Ancestor Configs

Any .praisonai/config.* in a parent directory — merged underneath

User Global

~/.praisonai/config.toml — lowest, supplies defaults for all

Environment Override

PRAISONAI_* env vars beat every config file value

Quick Start

1

Create Config File

Create .praisonai/config.toml in your project:
2

Use in Code

PraisonAI now validates field names automatically — see Automatic Field Validation for typo detection and suggestions.

Config File Locations

Every config file found from your home directory down to cwd is deep-merged. Unset keys fall through to a lower layer; the nearest project file wins for the keys it sets. Discovery order (lowest → highest precedence): Within a single directory, the first matching filename supplies that layer, in this order:

Deep Merge Semantics

Config files merge recursively — a project override for one key keeps every other key from the layers below. Merge inputs are never mutated, so loading is safe to repeat and caching still works via clear_config_cache().
Lists replace, they never concatenate:
Single-file fast path: when only one config file exists (global-only or project-only), the resolved dict is byte-identical to parsing that file alone — 100% backward compatible with the pre-merge behaviour.

Ancestor Discovery

Project configs are discovered by walking up from Path.cwd() through every parent to the filesystem root. Each directory contributes its own .praisonai/config.* (or root-level praisonai.*), and the nearest one to cwd wins for the keys it sets. The global ~/.praisonai/config.* always sits underneath. Running praisonai run … from ~/work/monorepo/apps/frontend/:
All three are deep-merged. A repo-root config sets defaults for every package; a package-level config overrides just the keys it names (e.g. model); the global user config still supplies everything neither one sets.

Small Model for Cheap Internal Calls

small_model routes internal, non-user-facing LLM calls — session-title generation, context compaction/summarisation, and LLM guardrail validation — to a cheaper model than your primary model.
When unset, behaviour is byte-identical to today: internal calls fall back to gpt-4o-mini. Resolution precedence (highest first):
An explicit llm_model= at the call site (e.g. generate_title(..., llm_model="gpt-4o")) always wins over small_model.
The CLI/JSON-schema namespace agent.small_model is honoured too — both defaults.small_model and agent.small_model read the same config file.

Full Configuration Reference

Since PraisonAI PR #4020, these [defaults] keys were removed because they were silently ignored: api_key, context, hooks, templates. If your config file uses them, validate_config() now fails loudly with Unknown key '<name>' — remove them or move them to the correct location:
  • api_key — pass via environment variable (OPENAI_API_KEY, etc.), not the config file
  • context — use Agent(context=...) per agent
  • hooks — see Hooks
  • templates — see Templates (per agent)
Both formats are discovered — .praisonai/config.toml and .praisonai/config.yaml (plus .yml) share the same search walk. A per-plugin <name>: { ... } block also implicitly opts that plugin in unless it sets enabled: false.
When [plugins] enabled = true (or PRAISONAI_PLUGINS=true), constructing any Agent(...) calls plugins.maybe_enable_from_config() internally — you don’t need to call plugins.enable() yourself. Per-plugin option maps are delivered automatically to each plugin’s on_config(options) hook. See Plugins → Configure from config.yaml for the full per-plugin surface.

Cheap auxiliary model for internal calls

small_model sets a cheap/fast model for PraisonAI’s internal LLM calls — session-title generation, context compaction/summarisation, and LLM guardrail validation — independent of your agent’s primary model. Resolution order for get_small_model(primary_model, fallback):
  1. defaults.small_model (or agent.small_model CLI namespace) — if set.
  2. primary_model — the running agent’s model, if any.
  3. defaults.model (or agent.model) — if set.
  4. fallback — preserves today’s hardcoded gpt-4o-mini when nothing is configured.

Env-var fallback (new in PR #4812)

Call sites outside the config-file loader (memory quality scoring, context compaction/summarisation, lite helpers, workflow routing, task callbacks, learn-manager extraction) resolve their auxiliary model through a parallel env-var ladder. This is what lets you run fully locally without having to set defaults.small_model in a config file. Why a dedicated variable? A local setup usually wants a smaller model for internal helper calls than for the agent itself. Pointing OPENAI_MODEL_NAME at a 70B local model should not make every internal summarisation call use it:
Empty or whitespace-only env values are ignored, so OPENAI_MODEL_NAME=" " does not override the default. The env-var resolver covers 8 modules: memory/memory.py, memory/learn/manager.py, context/compressor.py, context/optimizer.py, workflows/workflows.py, lite/__init__.py, task/task.py, and session/title.py. Deliberately not routed — cost tables (utils/cost_utils.py, llm/_cost.py, where the name selects a price, not a model), docstring examples, and public dataclass field defaults. Setting PRAISONAI_AUXILIARY_MODEL does not change pricing behaviour. Session-title generation is the one call site that combines both ladders: config-file defaults.small_model still wins over env vars there (its fallback is now PRAISONAI_AUXILIARY_MODELOPENAI_MODEL_NAME"gpt-4o-mini" instead of a hardcoded literal).

Usage Examples

Set a config-wide guardrail so every Agent gets it unless it passes its own.
Precedence: an explicit guardrails= on the Agent still wins; None (the default) now resolves through defaults.guardrails; False still opts out entirely.
Use a cheap/fast model for internal work (session titles, compaction/summarisation, LLM guardrails) while keeping a powerful primary model for the agent itself.
Single-provider setups work the same way:
Explicit Agent(...) kwargs still override the config file, same as model.
small_model also routes LLM-based guardrail validation. A guardrail expressed as a natural-language string uses the auxiliary model automatically — no code change on the agent.
An explicit llm_instance passed to GuardrailConfig (with its own api_key, base_url, or client) always wins — the reroute only applies when the guardrail would otherwise fall back to the bare primary model-name string.
Postgres and Redis are not memory backends — pass a live store with db(database_url=...) (Postgres) or db(state_url=...) (Redis). Set durable defaults (like learn) in the config file, then attach the store in code.

Programmatic Access

get_config() and get_default() return the merged view across global, ancestor, and project files. set_plugin_enabled(name, enabled) writes to the highest-precedence existing file (the one that actually wins the merge) — or creates a project-local .praisonai/config.yaml when none exist — so CLI writes always land in the same file the runtime reads. It seeds from the target file only (not the merged view), so writes round-trip cleanly without persisting inherited keys.

Auxiliary / Small Model Resolution

small_model routes PraisonAI’s internal LLM calls — session-title generation, context compaction/summarisation, and LLM guardrail validation — to a cheap, fast, or local model without changing your agent’s primary model.
Set small_model to a local model to keep background calls off third-party APIs:
The resolver picks the first available source top to bottom: Precedence: explicit llm_model > defaults.small_model > primary_model > defaults.model > built-in gpt-4o-mini. Resolve the auxiliary model programmatically:
The CLI/schema namespace agent.small_model reaches the same resolver, so both [defaults] and [agent] in the same config file work.
small_model is fully additive. When it is unset and no primary model is available, the resolver returns gpt-4o-mini — reproducing the previous hardcoded behaviour exactly.

What small_model drives

The resolved small_model is used for cheap, non-user-facing LLM calls:
  • Session titles — generating short titles for conversations
  • Context summarisation / compaction — the ContextCompactor LLM-summarize path (Agent._create_llm_summarize_fn)
  • LLM guardrail validationAgent(guardrail=...) and Task(guardrail=...) when the guardrail is a natural-language string (no explicit LLM instance passed)
An explicit LLM instance or per-call model override always wins over small_model. Leaving small_model unset resolves to the primary model — single-provider setups (Anthropic-only, Ollama, on-prem) make zero unexpected third-party calls.

Config Validation

Config files are validated automatically. Invalid keys trigger helpful error messages with suggestions.

Override Precedence

Explicit parameters always override config defaults:
The three config-file layers (nearest → ancestor → global) are deep-merged, not first-wins: a key set only in the global file still applies when no project file overrides it.

Sample Config File

Best Practices

Put values every project should share (model, memory backend, output preset) in .praisonai/config.toml, and let explicit Agent(...) parameters override them where a specific agent needs something different. Explicit parameters always win over the file, so the config is a floor, not a cage.
Configuration resolves as Explicit Agent parameter > Environment variable > Nearest project config > Ancestor configs > User global > Built-in default. The config-file layers are deep-merged, so when a setting seems ignored, check for a higher-precedence source (an env var like PRAISONAI_PLUGINS=true, an explicit kwarg, or a nearer project file) before editing a lower layer.
Put reusable defaults — model, small_model, plugin options, telemetry — in ~/.praisonai/config.yaml, and keep only project-specific overrides in .praisonai/config.yaml. Deep-merge folds them together, so the project file stays tiny.
set_plugin_enabled() (and the CLI plugin toggles) write to the highest-precedence existing config file — the one that actually wins the merge — or create .praisonai/config.yaml when none exist. This avoids editing a lower-precedence global whose change would be silently overridden on reload.
Commit .praisonai/config.toml so every teammate and CI run starts from the same defaults. Keep secrets (API keys) in environment variables, not the config file, so the file stays safe to share.
Most feature defaults ship disabled (enabled = false) for a reason — turning on memory, knowledge, or reflection globally adds latency and cost to every agent. Enable them in the config only when the whole project needs them; otherwise flip them on per agent.
Run agents and YAML configs directly from your terminal.
See every field available in YAML agent and task configuration.

Plugins

Auto-enable plugins from [plugins] on Agent init.

Tool Discovery

How Agent resolves tool names at runtime.