A mini code agent with Docker Agent - Part 3: `think` + `memory` + `permissions`
Our agent can now explore (01) and work with files (02). This new step, which brings three additions, will make it "more thoughtful", give it a memory, and above all make it safer.
think: a reasoning scratchpad for an instruct model (one with no native thinking).memory: a persisted memory, re-read from one session to the next.permissions: a guardrail that decides which actions the agent may actually run.
ā Careful: once again, guardrails buy you a certain amount of safety, but it will always be safer to "run" your agents in sandboxed, secured environments.
think: giving an instruct model room to reason
toolsets:
- type: think
This toolset has no options. The tool offers a thinking space: the agent "thinks out loud, quietly" there (planning, breaking the problem down) without any of it reaching the user.
It is the "on-demand" counterpart of the thinking model we saw in step 1b:
| thinking model | instruct model + think tool | |
|---|---|---|
| Reasoning | native, always present | added by a tool, on demand |
| Cost | thinking tokens on every turn | only when the agent calls think |
The docker-agent documentation is explicit: the think tool is meant for models without native reasoning. If you already use the thinking variant, it is redundant. We use it here because this step's model is the instruct one.
Now let's add a persisted memory to our agent.
memory: remembering across sessions
toolsets:
- type: memory
path: "./memory.db"
The memory toolset gives the agent a persistent key/value store. The model calls add_memory(...) to retain a durable fact (a preference, a project convention), and get_memories() to recall them ā including on a later run, since everything is written to disk.
Where is the file written? The relative
pathis resolved against the config file's folder, not the current directory. So./memory.dbhere creates03-think-memory-permissions/memory.db(plus amemory.lock). These runtime artifacts are deliberately ignored by Git (.gitignore).Small model, small reminder: a model this size does not always call
memoryspontaneously. A clear instruction ("usememoryto remember durable facts") noticeably increases how often it does.
memory vs todo: todo is a working memory, ephemeral, specific to one task (the steps in progress); memory is a long-term memory, persisted, which outlives the session.
Let's move on to the permissions part ā important for security, but once again it does not replace a sandboxed environment, because you will not necessarily think of every possibility.
permissions: scoping which actions are allowed
--safety/--yolo set a global approval posture. The permissions block, on the other hand, defines precise rules, per tool-call pattern:
permissions:
deny:
- "shell:cmd=rm *"
- "shell:cmd=sudo *"
- "shell:cmd=git push --force*"
- "shell:cmd=git reset --hard*"
allow:
- "shell:cmd=ls *"
- "shell:cmd=cat *"
- "shell:cmd=git status*"
# ...
Evaluation order: deny -> allow -> ask -> safety mode.
- A
denypattern is refused always: even under--yolo. That is the crucial point: the blocklist cannot be short-circuited by autonomous mode. - An
allowpattern is auto-approved (no question asked). - Anything matching no pattern "falls back" to the session's safety mode (
strict/balanced/autonomous).
The shell:cmd=<glob> syntax filters on the requested command. This lets you build an agent that explores freely (ls, cat, git status, grep...) but can never run rm, sudo or a git push --force, whatever the mode.
Two complementary guardrails:
filesystem/allow_listscopes where the agent may read/write;permissionsscopes which commands it may run.
Our complete agent.yaml for this lesson:
agents:
root:
model: mellum
description: A careful code agent that thinks before acting, remembers, and only runs safe commands.
instruction: |
You are a careful code agent.
- Before a non-trivial task, use the `think` tool to lay out your plan.
- Use `memory` to remember durable facts about the user and the project
(preferences, conventions), and to recall them later.
- Use `shell` to explore the project.
toolsets:
- type: shell
- type: think
- type: memory
path: "./memory.db"
permissions:
deny:
- "shell:cmd=rm *"
- "shell:cmd=sudo *"
- "shell:cmd=git push --force*"
- "shell:cmd=git reset --hard*"
allow:
- "shell:cmd=ls *"
- "shell:cmd=cat *"
- "shell:cmd=find *"
- "shell:cmd=grep *"
- "shell:cmd=head *"
- "shell:cmd=tail *"
- "shell:cmd=wc *"
- "shell:cmd=pwd"
- "shell:cmd=git status*"
- "shell:cmd=git log*"
- "shell:cmd=git diff*"
models:
mellum:
provider: dmr
model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M
base_url: http://host.docker.internal:12434/engines/v1
# base_url: http://localhost:12434/engines/v1
š You'll find all the configuration files over here: 03-think-memory-permissions
Running it
We move into the lesson folder.
cd 03-think-memory-permissions
# TUI
docker-agent run localhost.agent.yaml
# Or TUI with sbx (sandbox):
sbx run docker-agent --kit . -- run agent.yaml
Testing it
permissions: (this one is better tested inside a sandbox š)
Try deleting all files with rm -rf

think: forcing a plan before acting
First think with the think tool about the best way to count the files in this folder, then do it.

memory: storing a durable fact, then recalling it
Remember that my BlueSky handle is k33gorg and that I am a Solutions Architect at Docker
Type the command to store the fact:

Approve the memory tool call:

Then restart the agent and ask it to recall your job:

Then type the command to recall the fact:
My bluesky handle is k33gorg, what is my job?

Key takeaways
thinkadds on-demand reasoning to an instruct model; pointless with a thinking model.memorypersists facts across sessions (resolved relative to the config file); distinct from the ephemeraltodo.permissions(deny -> allow -> ask) scopes actions finely, and adenysurvives--yolo.allow_list(where) andpermissions(what) combine into a genuinely bounded agent.
In the next blog post, we'll see how to use RAG to give our agent external knowledge (documentation, source code, etc.) and turn it into a Rust expert.
Written by

No comments yet. Be the first to comment!