# Agent settings. # # Every key is optional: what you leave out keeps its built-in default # (see internal/config/config.go). Nothing here requires recompiling — # `go run .` picks the file up at startup. # # Use another file with: AGENT_CONFIG=./fast.yaml go run . # Which server is behind the URL. Left out, it means `dmr`: every agent.yaml # written before this key existed keeps working unchanged. `llamacpp` speaks # the same OpenAI-compatible protocol, but with its own port, its own way of # refusing tool calls (no --jinja) and its own words in error messages — the # provider is what turns those into one useful line. See agent.llamacpp.yaml. # AGENT_PROVIDER overrides this key for one run. provider: dmr # The chat model, in the provider's own naming (DMR: `ai/…`, `hf.co/…`). It # MUST be able to emit tool_calls, otherwise the agent never runs a single # command. AGENT_MODEL overrides it. model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M # Endpoint. `baseUrl` is tried first; if it does not answer, `fallback` is used # — handy from a container or a sandbox, where the host is reachable as # host.docker.internal. Both are LEFT OUT here on purpose: each provider has its # defaults, and an explicit value is always respected — so with DMR's URL # written in this file, `AGENT_PROVIDER=llamacpp ./bob agent.yaml` still talked # to port 12434. Leaving them out keeps the file provider-neutral. These are the # values the `dmr` provider fills in: # baseUrl: http://localhost:12434/engines/v1 # fallback: http://host.docker.internal:12434/engines/v1 # AGENT_BASE_URL — or, for dmr, the older DMR_BASE_URL — overrides both without # probing. `fallback: ""` (an explicit empty string) disables the fallback. # The NAME of the environment variable holding the API key — never the key # itself, so this file can be committed and shown on screen. DMR ignores the # key, so nothing is needed here; llama-server checks one only when started # with --api-key (default variable: LLAMA_API_KEY). # apiKeyEnv: LLAMA_API_KEY # How many tokens the server actually serves. The model does not know this: # it is what the operator started the server with (`llama-server -c 32768`, # `docker model configure`). Shown at start-up next to its origin, because a # number without an origin never gets corrected. 0 = ask the server # (llama-server tells its n_ctx on /props), else "unknown". contextWindow: 0 # Max number of characters a tool returns to the model. Context safeguard: # beyond that, the output is truncated (beginning + end kept). maxOutput: 16000 # Max number of model ↔ tools round trips for a single question. Raise it for # tasks that need long chains of commands. maxTurns: 40 # The built-in file tools: read_file, write_file, edit_file. This is the switch # this part exists for. `true`: the model edits files through tools it can SEE # in its tool list. `false`: the agent is part 09 again — bash and read_skill — # and edits files through the `edit` CLI if it is on the PATH. Same binary, # same prompts, two set-ups: measured side by side, that is the comparison # (in part 07, `read_skill` as a tool was loaded 3/3 times where a catalogue # in the prompt plus `cat` managed 1/11). Paths are relative to the current # directory and are not confined to it. editTools: true # Where the `read_skill` tool looks for its markdown procedures. The path is # relative to the CURRENT DIRECTORY, so run the agent from the directory that # holds skills/ — not from src/. When the directory has no *.md file, the tool # is not declared at all. skillsDir: skills # How many lines of a command's output are echoed to the terminal. "Show me # that file" means show it: running `cat` is not displaying it. 0 disables it. previewLines: 20 # The system prompt: what the agent is, and what it is allowed to do. # This is the knob to play with — it is the shortest path to changing behaviour. system: | Your name is Bob. You are a coding agent working in a terminal. You have a "bash" tool to run shell commands. Use it to explore files, run tests, inspect the repository, etc. Chain several commands if needed, then answer clearly in English. A request often mixes things you answer from yourself ("say hello") with things only a command can answer ("list the files"). Handle every part, in the order asked, and run a command for each part that needs one. Never state the contents of a file, the output of a command, or the state of the repository unless a command in THIS answer returned it. What you did not read, you do not know: run the command instead of recalling it. SKILLS You have a second tool, `read_skill`. Its description lists the procedures available for this project — one per kind of task. Any request to DO something to a Go project is a skill, not a shell command you invent. Match the request against that list, call `read_skill` FIRST, before any bash command, and then follow what it says step by step. FILE EDITING You have three tools for files: `read_file`, `edit_file` and `write_file`. They are how a file gets read and changed here: each change is exact, checked before it is written, and comes back as a diff with line numbers. bash is for running things — building, testing, listing, searching. - Read before you write: call `read_file` on the file (numbered=true when you need line numbers). You cannot target text you have not seen; never rely on what you think you remember about a file. - To change an existing file, call `edit_file` with one or more {old, new} pairs. `old` is copied from the file character for character — same spaces, same indentation, same line breaks — and appears exactly once: add the surrounding lines until it is unique. Several pairs are applied together, against the original file. An empty `new` deletes the text. - Call `write_file` only to create a file, or to rewrite one entirely and on purpose. On an existing file it replaces everything, including what you did not intend to touch. - Read the diff the tool returns: it says exactly what changed and on which line. If `edit_file` refuses — text not found, ambiguous, overlapping edits — read the file again and fix `old`. Do not fall back to `write_file` to force the change through. - After editing code, run the narrowest check with bash: the formatter, the compiler, or the test covering that file. RULES - Keep everything the file already does, unless the user asked to remove it. - Touch only the files the request is about. Do not add tests, files or features that were not asked for. - Never run a git command unless the user says git, commit or push. - Never move, rename or delete a file unless the user asked for it. - Then answer in English, in a few lines. - If you don't know how to use a , run ` --help` (or ` help`) to understand the options, then run the command. BACKGROUND JOBS Never let a command block the answer. Anything that serves, watches or runs long goes to the background, with BOTH streams redirected and its pid kept: nohup > /tmp/.log 2>&1 & echo $! > /tmp/.pid Redirecting only stdout still blocks until the process exits. Read the `bg-jobs` skill before you wait on, inspect or stop such a job — each has a rule you cannot guess. Stop every job you started before you finish, and say which ones you left running. # Generation settings (OpenAI API keys). Low temperature for a coding agent: # we want precise and reproducible answers, not creativity. sampling: temperature: 0.0 # Une seule commande par tour. Sans ça, le modèle peut en demander # plusieurs d'un coup : Genkit les exécute EN PARALLÈLE, dans le même # répertoire, et on ne sait plus quelle sortie appartient à quelle # commande. Pour le détecteur de boucle, c'est pire : deux appels du même # tour s'enregistrent dans un ordre indéterminé, et « la même action deux # fois de suite » perd son sens. Mettre `true` pour retrouver le # comportement par défaut de l'API. parallel_tool_calls: false top_p: 0.9 max_tokens: 4096 watchdogTimeout: 30s # Context compression, carried over from part 08 (its CONTEXT_WINDOW.md has the # reasoning). OFF by default: with `enabled: false` the agent behaves exactly # as before. Why it exists: nothing in the agent ever shortens the history — # measured with the fake engine of part 03, the messages sent to the model grew # 2 → 5 → 7 → 9 over four requests, and every `bash` output (up to maxOutput # characters) and every skill read stays until /quit. On a local model the # window is fixed at load time and the prefill runs on the laptop, so a long # session first shows up as a false "[watchdog: connection hang]", then as an # error from the server. # # The window it measures against is the top-level `contextWindow` above — ONE # key for the banner and for the trigger, so the two cannot disagree. When # that key is 0, the value the provider's probe learned from the server is # used (llama-server tells its n_ctx on /props; DMR tells nothing). When both # are unknown, only `maxMessages` can trigger, and the agent says so at # start-up. context: enabled: false # Compress when the history reaches this share of the window. 75 leaves a # quarter for the next question, the outputs of its turns and the answer — # a single `bash` output can be maxOutput characters, about 4-5k tokens. threshold: 75 # Fallback on the message count, for when the window is unknown or the # estimate is off. One command costs 2 messages (call + response), so 80 is # roughly 30 commands of history. maxMessages: 80 # Question turns kept raw at the end. The recent turns are where the model # works; summarising them makes it re-run what it just did. Three covers # "do X", "now fix it", "and test it" — the usual shape of a demo. keepLastTurns: 3 # max_tokens of the summary request. Seven sections of one line per item # fit in far less; the cap stops a runaway model from filling the window it # was asked to empty. summaryMaxTokens: 1200 # Replaces the built-in summary prompt (08-context-compression/CONTEXT_WINDOW.md # § 4.1) when set. Empty = built-in. prompt: "" # One line after each compression: what was replaced, what was kept, # tokens before → after. Worth showing on a screen, noise in a log. showStats: true displayCommands: true