Configuring a model for Agent mode
From nothing to a finished agent run: which model the agent uses, and how you tell it.
There is no settings screen for this, deliberately. The model, the provider and any key are environment variables read at server startup, which is how everything else in this product is configured. It keeps the choice with whoever operates the server rather than turning it into a permission to grant, revoke and audit per user.
Thirty models are supported — every one of them clears all six agent surfaces five consecutive times. They are listed with their measured durations in the index.
A local model, through Ollama
Section titled “A local model, through Ollama”Twenty-nine of the thirty run locally, with no key and no traffic leaving the machine.
# macOSbrew install ollamaollama serve # leave running
ollama pull qwen3:4b # 2.5 GB, and it clears every surfaceThen in .env.local:
LLM_PROVIDER=ollamaLLM_MODEL=qwen3:4bLLM_API_URL=http://localhost:11434/v1LLM_API_URL must end in /v1: that is Ollama’s OpenAI-compatible endpoint, which is what the
agent runtime posts to. The native /api/chat endpoint is not used.
No API key is needed, and nothing reaches a hosted provider on this path — what a run sends and
where is in ../AGENT_DATA_FLOW.md.
A hosted model
Section titled “A hosted model”One of the thirty is hosted, and it is configured the same way with one line more:
LLM_PROVIDER=geminiLLM_MODEL=gemini-3.5-flash-liteLLM_API_KEY=<your key>The key is read server-side only. It is never sent to the browser and never written to a run’s ledger, so a key configured here does not travel with the artefacts a run produces.
Restart the server after editing the file: these are read at startup, so an edit under a running server changes nothing until it restarts.
Checking a model this list does not cover
Section titled “Checking a model this list does not cover”Agent mode needs tool calling, and a model that cannot call a tool produces fluent prose and answers nothing. Ten seconds tells you which you have:
curl -s http://localhost:11434/api/chat -d '{ "model": "qwen3:4b", "messages": [{"role": "user", "content": "Check this database health."}], "tools": [{"type": "function", "function": { "name": "inspect_operations", "description": "Read live database operational state.", "parameters": {"type": "object", "properties": {"kind": {"type": "string", "enum": ["health"]}}, "required": ["kind"]}}}], "stream": false}' | grep -o '"tool_calls".\{0,120\}'A usable model answers with a tool_calls array naming inspect_operations. One that cannot
answers in prose.
That is not a refusal by itself: the application drops such a model to a prose tool protocol rather than turning it away, and decides per endpoint rather than from a list of names. What it does refuse is a model whose tool calling is established as absent AND which cannot be driven in prose either.
Measuring one properly — six surfaces, five runs each — is
testing-your-own.md.
Run one
Section titled “Run one”Open Agent mode, pick a connection, and ask a question that needs the database. The embedded SQLite sample is there by default.
A finished run leaves a report whose claims cite what it read. On the surfaces that answer with a result, it leaves the result beside the report.
When it does not work
Section titled “When it does not work”| What you see | Where to look |
|---|---|
| A report with an empty answer pane | the model reported without presenting; the ledger’s verdict names it no-answer |
| A run that ends having recorded nothing | no-report — the model narrated its findings instead of filing them |
model-timeout |
one turn exceeded 90 seconds. Check the power mode first: pmset -g | grep powermode |
| Runs that pass alone and fail in a batch | sustained load throttles the machine; a measured sweep of nine models slowed five of one model’s six surfaces by half again |
Every run writes its ledger to .workflow-data, and that is the authority on what a run did.