Configuration
Complete example: configs/config.example.json. ${VAR} placeholders are replaced from the environment when loaded (e.g. for tokens).
endpoints — the Ollama servers
Section titled “endpoints — the Ollama servers”"endpoints": [ { "name": "gpu-server", "url": "http://192.168.6.80:11436", "max_inflight": 2, "priority": 0 }, { "name": "cpu-server", "url": "http://192.168.6.80:11434", "max_inflight": 2, "priority": 1 }]| Field | Meaning |
|---|---|
name | unique name (appears in logs, CLI, API) |
url | base URL of the Ollama server |
max_inflight | max. concurrent requests to this server (default 2 — small servers can’t handle much parallelism) |
priority | at equal load, the smaller number wins (e.g. GPU before CPU) |
The pool pings each server regularly (/api/version), fetches the model list (/api/tags), and sends requests only to healthy servers that have the required model. If a server fails, the others take over automatically; when it comes back, it is reintegrated. Running requests with a transport error are automatically retried on the next server.
pool — health-check behavior
Section titled “pool — health-check behavior”| Field | Default | Meaning |
|---|---|---|
health_interval_sec | 15 | ping interval |
ping_timeout_sec | 3 | timeout per ping |
fail_threshold | 3 | this many consecutive failures → server is considered down |
recover_threshold | 2 | this many consecutive successes → server active again |
tags_refresh_every_n_pings | 4 | how often the model list is refreshed |
request_timeout_sec | 900 | timeout per chat request — deliberately generous: local models on weak hardware legitimately need several minutes |
models — which model for which task
Section titled “models — which model for which task”"models": { "default": "qwen3.5:9b", "default_category": "chat", "tasks": { "router": { "models": ["mistral:latest"], "options": { "temperature": 0 } }, "chat": { "models": ["qwen3.5:9b", "llama3.1:8b"] }, "code": { "models": ["qwen3-coder:latest", "qwen3.5:9b"] }, "summarize": { "models": ["mistral:latest"] } }}- Each task category has a preference list; the first model that resides on a healthy server wins. If none is available,
defaultapplies. - Category names are freely chosen — with two special roles:
router(the classification model, pick something small and fast!) andsummarize(also used for history compaction). optionsare Ollama options (temperature,num_ctx, …) sent along with every call of this category.- Model names without a tag match any tag:
qwen3.5findsqwen3.5:9b.
How is the category determined? In this order:
- explicitly by the caller (
--category, API field, job definition) - free heuristics (code blocks →
code, “summarize”/“tl;dr” →summarize) - the
routermodel classifies the input (JSON-schema-enforced response, temperature 0) - fallback:
default_category
agent — the tool loop
Section titled “agent — the tool loop”| Field | Default | Meaning |
|---|---|---|
max_iterations | 10 | max. tool rounds per request |
max_tools_per_call | 8 | max. tools offered to the model per call |
max_tool_result_chars | 4000 | tool results are truncated to this length |
history_budget_ratio | 0.8 | once the context window is filled to this ratio, the history is summarized |
system_prompt | … | base system prompt (keep it short!) |
web — built-in internet tools
Section titled “web — built-in internet tools”"web": { "enabled": true, "search_url": "", "max_page_chars": 6000, "max_results": 5, "timeout_sec": 30 }The agent gets two tools, both with a compression layer for small models:
web_search: web search (default: DuckDuckGo HTML, no API key needed;search_urlcan point to your own SearXNG instance). Returns numbered results with title, URL, snippet.web_fetch: fetches a page and returns it filtered — scripts, styles, navigation, footer, and boilerplate are removed, leaving the readable main content in compact Markdown form (headings, paragraphs, lists, tables, code). Modes:text(default),links(numbered link list),meta(title/description/size). Long pages are paginated atmax_page_chars; the tool tells the model how to continue reading withpage=2. Non-HTML content (JSON APIs, text files) is passed through directly.
No JavaScript rendering — for pages that strictly require a real browser, a browser MCP server (e.g. Playwright) can later be attached under mcp_servers; for analysis and research the built-in layer is sufficient and stays small enough for 4B–26B models.
skills — directories with skill folders
Section titled “skills — directories with skill folders”"skills": { "dirs": ["./skills"], "auto_load_on_trigger": true, "allow_scripts": false }auto_load_on_trigger automatically loads a skill when one of its triggers appears in the prompt. allow_scripts allows skill scripts to be executed (caution: executes code). Details and an example skill: Extending the platform.
mcp_servers — connecting external tools via MCP
Section titled “mcp_servers — connecting external tools via MCP”"mcp_servers": [ { "name": "files", "transport": "stdio", "command": "mcp-filesystem-server", "args": ["/path/to/project"], "tool_tags": ["tool_calling", "code"] }, { "name": "web", "transport": "http", "url": "http://127.0.0.1:9090/mcp", "headers": { "Authorization": "Bearer ${WEB_MCP_TOKEN}" } }]stdio starts the server as a subprocess, http connects to a running Streamable HTTP MCP server. tool_tags filters which task categories the server’s tools are offered in at all — without specifying it, they are available everywhere. ${WEB_MCP_TOKEN} is expanded from the environment when loaded, so the token never ends up in plain text in the file. More on this: Extending the platform.
scheduler — recurring agent tasks
Section titled “scheduler — recurring agent tasks”"scheduler": { "enabled": true, "state_dir": "~/.ollama-agent/state", "jobs": [ { "id": "daily-digest", "schedule": "0 7 * * *", "prompt": "Fasse die wichtigsten Ereignisse der letzten 24 Stunden zusammen.", "category": "summarize", "overlap": "skip", "enabled": true } ]}schedule understands standard cron (5 fields) as well as @hourly, @daily, @every 10m, etc. overlap: "skip" prevents a still-running job from being started again at the next scheduled time. Jobs created at runtime via API (POST /v1/jobs) only run while the daemon is running — they are not written to this file. Details: Scheduled jobs.
api, log
Section titled “api, log”api.addr: address of the daemon (default127.0.0.1:2330);api.auth_token: if set, every API request needsAuthorization: Bearer <token>(see HTTP API reference).log.level:debug|info|warn|error;log.format:text|json.