Skip to content

Configuration

Complete example: configs/config.example.json. ${VAR} placeholders are replaced from the environment when loaded (e.g. for tokens).

"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 }
]
FieldMeaning
nameunique name (appears in logs, CLI, API)
urlbase URL of the Ollama server
max_inflightmax. concurrent requests to this server (default 2 — small servers can’t handle much parallelism)
priorityat 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.

FieldDefaultMeaning
health_interval_sec15ping interval
ping_timeout_sec3timeout per ping
fail_threshold3this many consecutive failures → server is considered down
recover_threshold2this many consecutive successes → server active again
tags_refresh_every_n_pings4how often the model list is refreshed
request_timeout_sec900timeout per chat request — deliberately generous: local models on weak hardware legitimately need several minutes
"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, default applies.
  • Category names are freely chosen — with two special roles: router (the classification model, pick something small and fast!) and summarize (also used for history compaction).
  • options are Ollama options (temperature, num_ctx, …) sent along with every call of this category.
  • Model names without a tag match any tag: qwen3.5 finds qwen3.5:9b.

How is the category determined? In this order:

  1. explicitly by the caller (--category, API field, job definition)
  2. free heuristics (code blocks → code, “summarize”/“tl;dr” → summarize)
  3. the router model classifies the input (JSON-schema-enforced response, temperature 0)
  4. fallback: default_category
FieldDefaultMeaning
max_iterations10max. tool rounds per request
max_tools_per_call8max. tools offered to the model per call
max_tool_result_chars4000tool results are truncated to this length
history_budget_ratio0.8once the context window is filled to this ratio, the history is summarized
system_promptbase system prompt (keep it short!)
"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_url can 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 at max_page_chars; the tool tells the model how to continue reading with page=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": { "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": {
"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.addr: address of the daemon (default 127.0.0.1:2330); api.auth_token: if set, every API request needs Authorization: Bearer <token> (see HTTP API reference).
  • log.level: debug | info | warn | error; log.format: text | json.