Configuration
LLLM uses two configuration formats: lllm.toml declares the package structure and resource locations; YAML files define the agents. Together they let an entire agentic system be wired together without hardcoding paths in Python.
lllm.toml — Package Manifest
Place lllm.toml at the project root. LLLM finds it automatically by searching upward from the working directory, or you can point to it explicitly via $LLLM_CONFIG.
[package]
name = "my_system"
version = "0.1.0"
[prompts]
paths = ["prompts/"]
[tools]
paths = ["tools/"]
[proxies]
paths = ["proxies/"]
[configs]
paths = ["configs/"]
[tactics]
paths = ["tactics/"]
[dependencies]
packages = ["./packages/shared_lib"]
Sections
| Section | Purpose | Default path |
|---|---|---|
[package] |
Name, version, description. All resources are namespaced under name. |
— |
[prompts] |
Folders scanned for Prompt objects and .md files. |
prompts/ |
[tools] |
Folders scanned for Function objects created by @tool; colocated data files are also registered. |
tools/ |
[proxies] |
Folders scanned for BaseProxy subclasses. |
proxies/ |
[configs] |
Folders scanned for YAML agent config files (loaded lazily). | configs/ |
[tactics] |
Folders scanned for Tactic subclasses. |
tactics/ |
[dependencies] |
Other packages to load. Each loads into its own namespace. | — |
Custom sections (e.g. [assets], [schemas]) are also supported for packaging arbitrary files.
Environment variables
| Variable | Purpose |
|---|---|
LLLM_CONFIG |
Absolute path to a lllm.toml or folder; overrides auto-detection. |
Multiple environments
For the full package system — namespacing, resource URLs, dependency aliasing, re-exporting, custom sections — see Package System.
Agent YAML Config
Agent config files live in the configs/ folder and describe how agents are constructed. Files are loaded lazily — only read when load_config() or resolve_config() is called.
Structure
base: ... # optional — inherit from another config (no .yaml suffix)
global: # defaults merged into every agent in this file
model_name: gpt-4o
api_type: completion
model_args:
temperature: 0.1
max_exception_retry: 3
max_interrupt_steps: 5
max_llm_recall: 3
tools:
- shared_pkg.tools:search
- shared_pkg.tactics:code_review
- shared_pkg.proxies:market_data
agent_configs:
- name: analyzer
system_prompt_path: analytica/analyzer_system
model_args:
max_completion_tokens: 20000
- name: synthesizer
model_name: gpt-4o-mini # overrides global
system_prompt: "You are a synthesizer."
model_args:
temperature: 0.3 # overrides global
Per-Agent Fields
| Field | Required | Description |
|---|---|---|
name |
Yes | Agent identifier — must match an entry in the tactic's agent_group. |
model_name |
Yes (or from global) | Model identifier (e.g. gpt-4o, claude-opus-4-6). |
system_prompt |
One of | Inline system prompt string. Creates a Prompt object directly. |
system_prompt_path |
these two | Path to a registered prompt (e.g. analytica/system or my_pkg:research/system). |
api_type |
No | "completion" (default) or "response" for OpenAI Responses API. |
model_args |
No | Model parameters (temperature, max_tokens, etc.). Merged with global. |
max_exception_retry |
No | Max retries on parse/validation errors. Default: 3. |
max_interrupt_steps |
No | Max consecutive tool-call interrupts. Default: 5. |
max_llm_recall |
No | Max retries on LLM API errors. Default: 0. |
tools |
No | Regular tool, tactic tool, or proxy refs available to the agent on every prompt turn. |
extra_settings |
No | Reserved for advanced usage. |
Any unrecognized keys are treated as additional model_args.
Global Merge
The global section provides defaults for all agents. Per-agent values override them. For model_args, the dicts are merged (not replaced) — so you can set temperature globally and override only max_tokens per agent.
global:
model_name: gpt-4o
model_args:
temperature: 0.1
agent_configs:
- name: creative_writer
model_args:
temperature: 0.9 # overrides global
max_tokens: 4000 # added on top of global model_args
system_prompt: "You are a creative writer."
Result: creative_writer gets model_name: gpt-4o, model_args: {temperature: 0.9, max_tokens: 4000}.
For list fields such as tools and skills, a per-agent list replaces the global list. This lets one agent opt into a different capability surface without inheriting every global tool.
Agent Tools
tools attaches package resources to an agent through config:
global:
model_name: gpt-4o
tools:
- shared_pkg.tools:search
- shared_pkg.tactics:code_review
- shared_pkg.tactics:research#summarize
- shared_pkg.proxies:market_data
agent_configs:
- name: analyst
system_prompt_path: system/analyst
- name: writer
system_prompt_path: system/writer
tools: [] # replaces global tools; writer gets none
Supported entries:
shared_pkg.tools:search— a regularFunctioncreated with@tool.shared_pkg.tactics:code_review— the default exposed tactic tool.shared_pkg.tactics:research#summarize— a named tactic tool method.shared_pkg.proxies:market_data— a proxy resource; this enables the proxy programming surface (query_api_docplusrun_pythonwhenexec_env: interpreter).
This is the config-level form of the decoupled tool style. The config declares the tool URLs that belong to the agent's global surface; the implementation is resolved from package resources. Regular @tool functions and tactic tools become direct prompt tools. Proxy refs enable a programming surface rather than one direct function schema, because the agent discovers endpoint docs and calls CALL_API from the sandbox.
Full URLs are recommended for shared packages. Bare names and package shorthand work for regular tools and tactic tools, resolving relative to the agent's system-prompt package before falling back to the runtime default namespace. Proxy entries should use an explicit proxy URL such as pkg.proxies:name, because proxies change the whole prompt/tool surface rather than becoming one direct function schema.
To avoid cyclic definitions, config stores only resource references. Regular tool and tactic refs are left as strings when the agent is built and are resolved only when the prompt is about to run. Prompt Function declarations bind to same-package @tool implementations by exact key. Proxy refs are the exception because the agent must inject the proxy directory and execution tools before the first model call. Keep tool modules declarative at import time: define @tool functions and BaseProxy classes there, but do not build agents or tactics at module scope. If a tool needs to call an agent, construct or load that agent inside the tool function body.
Inheritance via base
Configs can inherit from other configs using the base key. Inheritance is recursive and uses deep merge — dict values merge, lists and scalars replace.
# base.yaml
global:
model_name: gpt-4o
model_args:
temperature: 0.1
agent_configs:
- name: analyzer
system_prompt_path: research/system
# experiments/ablation.yaml
base: base # inherits from base.yaml
global:
model_args:
max_tokens: 500 # added to base's model_args
agent_configs: # replaces base's list entirely (lists replace, dicts merge)
- name: analyzer
system_prompt_path: research/system_no_cot
# experiments/full.yaml
base: experiments/ablation # chain: full → ablation → base
global:
model_name: o4-mini-2025-04-16 # overrides all the way up
from lllm import resolve_config
config = resolve_config("experiments/full")
# config["global"]["model_name"] == "o4-mini-2025-04-16"
# config["global"]["model_args"]["temperature"] == 0.1 (from base)
# config["global"]["model_args"]["max_tokens"] == 500 (from ablation)
Circular inheritance is detected and raises ValueError.
Recommended layout
configs/
├── default.yaml # main config
├── experiments/
│ ├── fast.yaml # base: default → swap to mini models
│ └── ablation.yaml # base: default → swap prompts
└── vendor/ # pinned dependency configs
└── A.yaml # base: "A:default" + overrides
Managing Dependency Configs
When your package depends on other packages, keep the config model simple: each package is responsible for configuring its own dependencies. Your config should only declare your own agents, referencing prompts from dependencies by their namespaced path.
Assembly Config Pattern
# configs/default.yaml
global:
model_name: gpt-4o
model_args:
temperature: 0.1
agent_configs:
- name: orchestrator
system_prompt_path: my_pkg:orchestrator/system
- name: analyzer
system_prompt_path: A:analysis/system # prompt from dependency A
model_args:
max_completion_tokens: 20000
- name: searcher
system_prompt_path: B:search/system # prompt from dependency B
model_name: gpt-4o-mini
Prompt references point into dependencies (that's fine — importing a function). But configuration decisions (model, temperature, retries) are owned by you.
Config Vendoring for Sub-Tactics
When you compose a dependency tactic as a sub-tactic, use vendor_config to pull its config and apply your overrides:
from lllm import vendor_config, get_default_runtime
cfg = vendor_config("A:default", {
"global": {
"model_name": "gpt-4o",
"model_args": {"temperature": 0.05},
},
})
runtime = get_default_runtime()
runtime.register_config("vendor/A", cfg, namespace="my_pkg.configs")
Or equivalently, as a YAML file:
vendor_config resolves the full base chain, applies your overrides via deep merge, and returns a standalone config with no base key.
from lllm import vendor_config
cfg = vendor_config("A:default") # resolve only
cfg = vendor_config("A:default", {"global": {"model_name": "gpt-4o-mini"}}) # with overrides
import yaml
with open("configs/vendor/A.yaml", "w") as f:
yaml.dump(cfg, f) # save to disk
Loading Configs in Python
from lllm import load_config, resolve_config
# Direct load — no inheritance resolution
cfg = load_config("default")
cfg = load_config("my_pkg:experiments/fast")
# With full base chain resolution (recommended)
cfg = resolve_config("experiments/full")
# build_tactic handles everything automatically
from lllm import build_tactic
tactic = build_tactic(resolve_config("default"), ckpt_dir="./runs")