GoClaw runs one or more agents in one process. Each agent is an entry in agents.list with its own workspace, model and memory, and channel messages reach an agent through routing. With no agents.list, there is one agent, main. Every agent processes messages with an LLM provider and tool calling.
flowchart TD
MSG["User Message"] --> RUNTIME["<b>Agent Runtime</b><br/>System Prompt + History + Memory"]
RUNTIME --> LLM["<b>LLM Provider</b><br/>Gemini / Claude / OpenAI / DeepSeek"]
LLM -->|Tool Calls| POOL["<b>WorkerPool</b><br/>Parallel execution ยท Bounded concurrency"]
POOL -->|Tool Results| LLM
LLM -->|Streamed Tokens| RES["Final Response"]
RES --> CH["Channel Output"]
The event-driven agent loop (ProcessStream) is shared across all LLM providers:
tools.rag.top_k, default 12) and the agent's most-used tools (tools.rag.usage_prior, default 6). The model can add a tool the selection left out with search_toolsWorkerPooltools.max_tool_iterations, default 20)EventTokenChunk to the clientThe Gemini provider uses a custom loop (native genai SDK) that natively streams content, but follows the same logical flow.
| Provider | Config Key | SDK |
|---|---|---|
| OpenAI | openai |
github.com/openai/openai-go/v3 (official) |
| Claude (Anthropic) | claude |
github.com/anthropics/anthropic-sdk-go (official) |
| Gemini | gemini |
google.golang.org/genai (native) |
| Gemini Legacy (deprecated) | gemini-legacy |
served as gemini |
| DeepSeek | deepseek |
github.com/openai/openai-go/v3, DeepSeek's API (DeepSeek publishes no Go SDK) |
| Ollama | ollama |
github.com/ollama/ollama/api (official) |
The provider is chosen at startup from ai.provider. An agent's agents.list[].model overrides it for that agent, either as a provider name (claude) or as provider/model (claude/claude-sonnet-4-5-20250929). With neither set, the provider is openai.
Every provider makes a model call again when it fails because the provider is overloaded (twice, after about 2 and 6 seconds) or on the network (once, after half a second); quota, billing and key errors are not retried (Provider Fallback).
When the provider still fails before the reply has used a tool, the request moves to the next provider/model of ai.fallback, or of the agent's own agents.list[].fallback, which replaces it. A provider that keeps failing is put in cooldown and skipped while others remain. Model Failover explains how.
The system prompt is assembled in this order (GetStaticSystemPromptFor in agent.go):
IDENTITY.md and SOUL.md โ Identity, persona, tone, boundaries. A subagent gets its profile here insteadAGENTS.md โ Operating instructions, rulesinstructions/<name>.instructions.md that apply to this run and name no toolsTOOLS.md โ Tool usage notes and conventions (narrowed to the step's tools in a SOP step)search_tools); when you cannot get it: say which of three it is, in a sentence or two (no tool can do it; a tool exists but needs an administrator, was not offered here or was blocked by a rule; or the tool failed or found nothing), and never present other data as the answer; and GoClaw documentation: goclaw_docs for how GoClaw is configured and how it works, not for data a webhook or a system sent or holdssubagents.allow_agentsA missing file is skipped. Memory.md, personal and global memory, agent notes and lessons are not part of this prompt: what holds for the session goes into the session context that follows it, and what was found for the message (the notes, lessons and personal memory it matches, and the current time) follows the person's message, after the history, so the providers' prompt caches keep the conversation from turn to turn (Memory).
The following files are read-only except to an admin and to cron and SOP runs that act for no one in particular:
Memory.mdSOUL.mdTOOLS.mdAGENTS.mdIDENTITY.mdUSER.mdagents/*.agent.md) and instructions (instructions/*.instructions.md)The file_write and file_edit tools refuse to write them for other callers.
The default agent's workspace is ~/.goclaw/workspace/. Every other agent's is ~/.goclaw/agents/<id>/workspace/. agents.list[].workspace sets another path for either.
The workspace is the agent's working directory for all file operations. Path traversal outside the workspace is blocked.
| File | Purpose |
|---|---|
SOUL.md |
Agent persona and boundaries |
AGENTS.md |
Operating rules and guidelines |
TOOLS.md |
Tool documentation and conventions |
Memory.md |
Long-term memory (shared across sessions) |
A new workspace is seeded with these four files from defaults built into the binary. In the default agent's workspace, TOOLS.md and AGENTS.md are written again from those defaults at every start (AGENTS.md from the tools.mode profile), so edits to those two files there do not last; SOUL.md and Memory.md are seeded only when missing. Rules of your own belong in an instruction (instructions/<name>.instructions.md, on the Web UI's Instructions page) instead.
Memory.md is the agent's curated long-term memory; it goes into the session context of every request. Everything the agent learns at run time is stored as records in the database, not as files:
memory_write saves a record in one of four scopes: personal (one user's own context, never written or loaded in cron and SOP runs), agent_notes (org-wide knowledge: admins, cron and SOP runs, and whoever memory.write_access lists), global and agent_config (admins, and whoever memory.write_access lists; never SOP, cron or webhook runs)memory_search searches memory by meaning and keywords; memory_get reads a full entryThere is no canvas tool the model calls directly (canvas_show exists in canvas.go but is not registered). Files reach the canvas from tools that produce them:
browser (its screenshot action) saves the PNG and shows it to Web UI clients as a canvas messagediagram_render renders a Mermaid diagram to PNGfile_write with attach: true attaches the file it wroteThese files are collected for the request and sent as attachments with the answer, on every channel. Screenshots and diagrams are stored in ~/.goclaw/canvas/.