mm init: where a generated agent's settings come from — explanation
What is this about?
mm init (or mm --init) writes a commented agent.yaml from a few answers: the model server, the model, the usage (developer, translator, docs…), the machine, and whether to ask before each change. No model runs while the wizard runs. Every value in the file comes from one of four sources, and each value says which one in its comment. This page explains what those sources are, how they were put together, and why the wizard trusts them in that order. The steps are in Create an agent with mm init and the flags in the command line reference.
The four sources, most specific first
The code is in internal/agentinit. Decide (decide.go) applies the sources in a fixed order of trust: the server's facts about this model, then the family table, then the usage profile, then the machine row. Each source only fills in what the sources before it do not know.
1. What the model server says about the model (facts.go)
The server knows the exact model that is loaded, so it is the most specific source there is. The wizard asks it before choosing anything else:
| Provider | Asked | What it gives |
|---|---|---|
llmman, ollama |
/api/tags, then /api/show |
the model list, the family the server reports (details.family), the native context window, the sampling written in the GGUF (temperature, top_p, top_k, min_p), the chat template |
llamacpp |
/props |
the window and the chat template |
dmr |
/models |
the model list only |
openai |
/models (with the key) |
the model list only |
From the chat template, templateThinking reads how the model's reasoning is switched on or off. It finds enable_thinking and its default (on or off), or reasoning_effort and the values the template accepts. A model whose template knows neither gets no reasoning switch.
These rules were not written for mm init. They are a Go port of skills/create-agent/model_facts.py, the script the agent-creating agent (agent.creator.yaml) already used to read a model from llmman: the same endpoints, the same way of reading the template.
When the server gives the GGUF's own sampling, those numbers replace the family table's. The GGUF is the most specific source, because it was written for that very file. When the server says nothing beyond the model's name (DMR, the generic openai provider, an unknown model), the file says so in its closing notes, and the family table decides.
2. The family table (presets.yaml)
The family is guessed from the model id: the first family whose match words all appear in the lower-cased name wins. qwen-coder (qwen + coder) therefore comes before qwen, and other matches anything. The server's family is used when the name does not tell. A family gives:
- the sampling for normal use (
general) and with reasoning on (thinking); - a
precisetemperature, for agents that use tools without reasoning; - the reasoning switch;
- sometimes a note, for example "a guard model, not a general agent".
Every number has a source line, and the table was filled from two kinds of source only:
- Model cards and published files. The Qwen cards (thinking 0.6 / 0.95 / 20, non-thinking 0.7 / 0.8 / 20), the Gemma 4 GGUF sampling, Llama 3.1 8B Instruct's
generation_config.json(0.6 / 0.9, read from a mirror because Meta's repository needs a login), the Mistral Small 3.1 card ("a relatively low temperature, such as temperature=0.15"), and the OpenAI API reference for hosted models. - This repository's measured demos. For each family, the demos that run the model were searched for the sampling they use (
demos/*/agent*.yaml). For example,demos/qwen-style(Qwen3-Coder-Next),demos/coding-helpers(Mellum2 12B Thinking, MiniCPM5 2B),demos/team(Ornith 1.5 9B),demos/guard(Granite Guardian),demos/codex-style(gpt-oss 20B) anddemos/llmman(Jan-nano, Lucy). Qwen'sprecise0.0 comes from runs with Qwen3.5 4B, reasoning off (demos/docs-search,memory-search,plans).
When nothing is published, other uses the rule of the skills/create-agent skill: 0.7 / 0.9, and 0.2 for a code agent that does not reason.
3. The usage profile (presets.yaml, usages)
The usage says what the agent is for, which the model cannot tell. A profile sets:
- which tool switches are on (a translator has no tool at all; a developer has bash, the file tools and the plan tool);
- whether to reason when the model can;
- whether to use the family's precise temperature;
maxTurns;- the default for asking before each change (
yolo); - a starting system prompt.
Some usages switch on one feature each: docs turns on the documentation search, json writes a structuredOutput example (which forbids every tool), and coordinator hands the questions to other agents. The prompts are in English, by the user's choice in ticket 97. They repeat rules the demos' prompts share, for example "Never state the output of a command you did not run in this answer".
4. The machine row (presets.yaml, machines)
The timeouts and the size of tool outputs depend on the machine, not on the model. The three rows small, medium and large (firstTokenTimeout, watchdogTimeout, bashTimeout, maxOutput) are copied from the "Summary: typical profiles" table of the settings guide, written for ticket 98.
The rules the code adds
A few values follow from the answers rather than from a table. Each rule writes its reason above the value it sets:
- Reasoning on means a temperature of at least 0.6 and
max_tokens: 16384. A thinking model loops at a low temperature, and its thoughts need room besides the tool call. With reasoning off, the limit is 4096. - The precise temperature applies only without reasoning. It is used when the usage asks for it (developer, reviewer, docs, json…).
- With bash on,
watchdogTimeoutis aligned onbashTimeout, as section 3 of the settings guide says. Otherwise a long command would look like a stalled model. parallel_tool_calls: falsefor any agent with tools. Otherwise small models run a check before the change it checks has landed.contextWindow: 0andcontext.threshold: 75. mm asks the server for the window it actually serves. The proposal planned a threshold computed from the model's native window. It was dropped while writing the code: Ollama often serves 4k–8k, so a percentage of the native window would compress almost at once.skillsDir: no-skills. A new agent starts without skills, and the comment says how to add some.
Every number says how it was obtained
Since ticket 121, each number about the model carries a tag in its comment. The file's header explains the tags it uses:
| Tag | Meaning | After a model or quantization change |
|---|---|---|
[server] |
read from the model server when the file was written | run mm init again |
[measured] |
a demo of this repository ran with it | measure again |
[card] |
copied from the model card, the GGUF or the API reference, not measured here | read the card again |
[rule] |
a rule of thumb, nothing specific to the model | check it still fits |
[guide] |
the settings guide's row for the machine | — |
A comment above model: lists what to redo when the model changes. The comments make the file teach as it configures, the way the demos' agent.yaml files are written: a reader sees which numbers were measured on a real run and which were only copied.
The file is checked before the wizard says it is done
The wizard never overwrites an existing file. After writing, it loads the file with mm's own config.Load, so a file mm would refuse is reported at once. With --check, it also runs the same mm on the new file with one question (Say hello in one line.), to prove the model answers.
Why it is designed this way
A generator with no model. Ticket 97 asked for an agent.yaml from the command line, by model family and usage. The proposal written for the ticket first listed what already existed: agent.creator.yaml with its create-agent skill, model_facts.py, the settings guide, config.samples/ and the demos' tested settings. Instead of a new source of knowledge, mm init puts those pieces together in deterministic Go code. It is instant and works before any agent exists.
A server first, a table second. A table can only know a family, while the server knows the very file that is loaded: its window, its GGUF sampling, its template. The table covers the servers that say nothing, such as Docker Model Runner.
Sourced values only. The user decided that the family table would use model cards and measured demos. A number with no source does not go in. A family with neither falls back to other, and the file says that its sampling is only a starting point.
One table. presets.yaml is embedded in the binary. Its header says it is meant to be shared with the Claude skill of ticket 111 (a skill built from the settings guide). That ticket is still open, and the mm-agent-creator skill does not read the file today.
Rejected alternatives
- Generating the file with a model (
agent.creator.yaml). Kept for the unusual cases, but not the default: it needs a 27B model loaded and minutes per agent. - A threshold computed from the native window. Rejected, see above: it would compress almost at once on the windows Ollama serves.
- Skills, MCP servers, hooks and
mmConfigsin the wizard. Left out of the first version. The file's closing comment points to the how-to guides instead. - Prompts in the user's language. Rejected by the user's choice: the generated prompts are in English.
Known limits
- Live checks were run against llmman only (Qwen3.5 4B, gemma-4-E4B). Ollama, Docker Model Runner and llama-server were tested against fake servers recorded in the tests. The OpenAI API itself was not tried; its constraints come from its reference.
- A
[card]number was copied, not measured in this repository. - The families are matched on words in the model id: a model renamed by its publisher may land in
other.
How it relates to the rest
- Providers: the five servers the wizard can ask, and why each has its own entry.
- Thinking models: what the reasoning switch the wizard sets does at run time.
- Context compression: what
context.thresholdtriggers. - Structured output: why the
jsonusage has no tools. - Asking another agent: what the
coordinatorusage relies on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
|