Compare commits

...

2 Commits

Author SHA1 Message Date
Vincent Koc
9803ae90b2 docs(agents): align lean message help 2026-05-30 02:42:45 +02:00
Vincent Koc
34cddc295d fix(agents): keep message tool for lean source replies 2026-05-30 02:40:30 +02:00
9 changed files with 86 additions and 6 deletions

View File

@@ -30,7 +30,7 @@ Treat them differently from normal config:
## Local model lean mode
`agents.defaults.experimental.localModelLean: true` is a pressure-release valve for weaker local-model setups. When it is on, OpenClaw drops three default tools — `browser`, `cron`, and `message` from the agent's tool surface for every turn. Nothing else changes. Use `agents.list[].experimental.localModelLean` to enable or disable the same behavior for one configured agent.
`agents.defaults.experimental.localModelLean: true` is a pressure-release valve for weaker local-model setups. When it is on, OpenClaw drops the `browser` and `cron` tools from the agent's tool surface, and it also drops `message` unless the current turn requires message-tool-only visible replies. Nothing else changes. Use `agents.list[].experimental.localModelLean` to enable or disable the same behavior for one configured agent.
### Why these three tools
@@ -40,7 +40,7 @@ These three tools have the largest descriptions and the most parameter shapes in
- The model picking the right tool vs. emitting malformed tool calls because there are too many similar-looking schemas.
- The Chat Completions adapter staying inside the server's structured-output limits vs. tripping a 400 on tool-call payload size.
Removing them does not silently rewire OpenClaw — it just makes the tool list shorter. The model still has `read`, `write`, `edit`, `exec`, `apply_patch`, web search/fetch (when configured), memory, and session/agent tools available.
Removing them does not silently rewire OpenClaw — it just makes the tool list shorter. The model still has `read`, `write`, `edit`, `exec`, `apply_patch`, web search/fetch (when configured), memory, and session/agent tools available. When the source reply delivery contract is `message_tool_only`, lean mode keeps `message` so visible replies can still be delivered.
### When to turn it on

View File

@@ -315,7 +315,7 @@ If the model loads cleanly but full agent turns misbehave, work top-down — con
openclaw infer model run --gateway --model <provider/model> --prompt "Reply with exactly: pong" --json
```
3. **Try lean mode.** If both probes pass but real agent turns fail with malformed tool calls or oversized prompts, enable `agents.defaults.experimental.localModelLean: true`. It drops the three heaviest default tools (`browser`, `cron`, `message`) so the prompt shape is smaller and less brittle. See [Experimental Features → Local model lean mode](/concepts/experimental-features#local-model-lean-mode) for the full explanation, when to use it, and how to confirm it is on.
3. **Try lean mode.** If both probes pass but real agent turns fail with malformed tool calls or oversized prompts, enable `agents.defaults.experimental.localModelLean: true`. It drops the heaviest default tools (`browser`, `cron`, and usually `message`) so the prompt shape is smaller and less brittle. Lean mode keeps `message` when the current source reply contract requires message-tool delivery. See [Experimental Features → Local model lean mode](/concepts/experimental-features#local-model-lean-mode) for the full explanation, when to use it, and how to confirm it is on.
4. **Disable tools entirely as a last resort.** If lean mode is not enough, set `models.providers.<provider>.models[].compat.supportsTools: false` for that model entry. The agent will then operate without tool calls on that model.

View File

@@ -679,7 +679,7 @@ Use these as starting points and replace model IDs with the exact names from `ol
```
Use `compat.supportsTools: false` only when the model or server reliably fails on tool schemas. It trades agent capability for stability.
`localModelLean` removes the browser, cron, and message tools from the agent surface, but it does not change Ollama's runtime context or thinking mode. Pair it with explicit `params.num_ctx` and `params.thinking: false` for small Qwen-style thinking models that loop or spend their response budget on hidden reasoning.
`localModelLean` removes the browser and cron tools from the agent surface, and it usually removes message unless the current source reply contract requires message-tool delivery. It does not change Ollama's runtime context or thinking mode. Pair it with explicit `params.num_ctx` and `params.thinking: false` for small Qwen-style thinking models that loop or spend their response budget on hidden reasoning.
</Accordion>
</AccordionGroup>

View File

@@ -164,6 +164,35 @@ describe("applyModelProviderToolPolicy", () => {
expect(toolNames(filtered)).toEqual(["read", "exec"]);
});
it("keeps message in lean local-model mode when source replies require the message tool", () => {
const filtered = testing.applyModelProviderToolPolicy(
[
{ name: "read" },
{ name: "browser" },
{ name: "cron" },
{ name: "message" },
{ name: "exec" },
] as unknown as AnyAgentTool[],
{
config: {
agents: {
defaults: {
experimental: {
localModelLean: true,
},
},
},
},
modelProvider: "openai",
modelApi: "openai-responses",
modelId: "gpt-5.4",
sourceReplyDeliveryMode: "message_tool_only",
},
);
expect(toolNames(filtered)).toEqual(["read", "message", "exec"]);
});
it("drops heavyweight tools when lean local-model mode is enabled for the current agent", () => {
const filtered = testing.applyModelProviderToolPolicy(
[

View File

@@ -245,6 +245,7 @@ function applyModelProviderToolPolicy(
agentDir?: string;
modelCompat?: ModelCompatConfig;
suppressManagedWebSearch?: boolean;
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
},
): AnyAgentTool[] {
tools = filterLocalModelLeanTools({
@@ -252,6 +253,7 @@ function applyModelProviderToolPolicy(
config: params?.config,
agentId: params?.agentId,
sessionKey: params?.sessionKey,
sourceReplyDeliveryMode: params?.sourceReplyDeliveryMode,
});
if (
@@ -1063,6 +1065,7 @@ export function createOpenClawCodingTools(options?: {
agentDir: options?.agentDir,
modelCompat: options?.modelCompat,
suppressManagedWebSearch: options?.suppressManagedWebSearch,
sourceReplyDeliveryMode: options?.sourceReplyDeliveryMode,
});
options?.recordToolPrepStage?.("model-provider-policy");
// Sender identity is carried for command/channel-action auth; tool visibility

View File

@@ -1438,6 +1438,7 @@ export async function runEmbeddedAttempt(
tools: [...tools, ...normalizedBundledTools],
config: params.config,
agentId: sessionAgentId,
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
});
const uncompactedToolSchemaProjection = filterRuntimeCompatibleTools(
projectedUncompactedEffectiveTools,
@@ -1509,6 +1510,7 @@ export async function runEmbeddedAttempt(
tools: toolSearch.tools,
config: params.config,
agentId: sessionAgentId,
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
});
const toolSearchSchemaProjection = filterRuntimeCompatibleTools(projectedToolSearchTools);
logRuntimeToolSchemaQuarantine({

View File

@@ -32,6 +32,46 @@ describe("local model lean tool filtering", () => {
).toEqual(["read", "exec"]);
});
it("keeps message when source replies require message-tool delivery", () => {
const cfg: OpenClawConfig = {
agents: {
defaults: {
experimental: {
localModelLean: true,
},
},
},
};
expect(
filterLocalModelLeanTools({
tools: tools(["read", "browser", "cron", "message", "exec"]),
config: cfg,
sourceReplyDeliveryMode: "message_tool_only",
}).map((tool) => tool.name),
).toEqual(["read", "message", "exec"]);
});
it("drops message when source replies use automatic delivery", () => {
const cfg: OpenClawConfig = {
agents: {
defaults: {
experimental: {
localModelLean: true,
},
},
},
};
expect(
filterLocalModelLeanTools({
tools: tools(["read", "browser", "cron", "message", "exec"]),
config: cfg,
sourceReplyDeliveryMode: "automatic",
}).map((tool) => tool.name),
).toEqual(["read", "exec"]);
});
it("lets an agent opt out of an inherited global lean setting", () => {
const cfg: OpenClawConfig = {
agents: {

View File

@@ -1,3 +1,4 @@
import type { SourceReplyDeliveryMode } from "../auto-reply/get-reply-options.types.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.js";
import { resolveAgentConfig, resolveDefaultAgentId } from "./agent-scope-config.js";
@@ -43,9 +44,14 @@ export function filterLocalModelLeanTools(params: {
config?: OpenClawConfig;
agentId?: string;
sessionKey?: string;
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
}): AnyAgentTool[] {
if (!isLocalModelLeanEnabled(params)) {
return params.tools;
}
return params.tools.filter((tool) => !LOCAL_MODEL_LEAN_DENY_TOOL_NAMES.has(tool.name));
return params.tools.filter(
(tool) =>
(tool.name === "message" && params.sourceReplyDeliveryMode === "message_tool_only") ||
!LOCAL_MODEL_LEAN_DENY_TOOL_NAMES.has(tool.name),
);
}

View File

@@ -1127,7 +1127,7 @@ export const FIELD_HELP: Record<string, string> = {
"agents.defaults.experimental":
"Experimental agent-default flags. Keep these off unless you are intentionally testing a preview surface.",
"agents.defaults.experimental.localModelLean":
"Experimental local-model prompt trim. When enabled, OpenClaw drops heavyweight default tools like browser, cron, and message for weaker or smaller local-model backends.",
"Experimental local-model prompt trim. When enabled, OpenClaw drops heavyweight default tools like browser, cron, and message for weaker or smaller local-model backends, but keeps message when source replies require message-tool delivery.",
"agents.defaults.bootstrapPromptTruncationWarning":
'Inject agent-visible warning text when bootstrap files are truncated: "off", "once", or "always" (default).',
"agents.defaults.startupContext":