Add per-agent reasoning effort

Opus 4.5+ and Sonnet 4.6+ support a new "effort" knob that controls how
much budget the model spends on thinking and tool calls. Plumb it through
end to end so each agent can pick its own level.

Storage & shape:
- New TEXT column agents.effort (default '') added via TryAddColumn.
- AgentRecord.Effort + INSERT/UPDATE in DatabaseService.Agents.cs.
- ChatContext.Effort + SubAgentContext.Effort so the value travels with the
  request alongside model and tool settings.

Agent editor:
- Effort dropdown above git-commit behavior in the agent form (low/medium/
  high/xhigh/max plus empty = "use default"). Always visible; on OpenAI
  providers the value is sent but silently ignored.
- agent-page.js load/save and AgentEndpoint create/update wired up.

Anthropic API path:
- StreamChat and RunNonStreamingToolLoop add output_config: { effort } when
  effort is set and not "high" – empty/null/"high" means "omit and let the
  API pick its default".
- Sub-agents (preparer/review/finalizer) get the parameter through
  RunAgentChat so each step uses its own agent's effort.

Anthropic CLI path:
- ClaudeCliService.ChatStreamingAsync takes an effort parameter and exports
  CLAUDE_CODE_EFFORT_LEVEL on the spawned process. Env-var beats the
  --effort flag because it also covers "max" and "ultracode".
- StreamChatViaCli, ChatNonStreamingViaCli, RunSubAgentViaCli, and the
  ChatEndpoint anthropic branches all hand the value through.

ARCHITECTURE.md / ARCHITECTURE_QUICK.md updated with the new field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.7 committed 2026-05-31 21:33:32 +02:00
1 parent 88fb3c9867
commit 3f913f14b5
15 files changed
+70 -17

No files matched your search

+16 -7
View File
@@ -77,7 +77,7 @@ public partial class AnthropicService
preparer.Workspace, preparer.UseMemory, preparer.WebSearchVersion,
preparer.ToolChoice, preparerMessages, chainId, sessionId, apiToken, usage,
0, "preparer", preparer.Agent?.Name ?? "Vorbereiter-Agent",
cancellationToken, cancellationToken);
cancellationToken, cancellationToken, preparer.Effort);
}
var result = new PreparerResult { IterationCount = iterations, ResponseText = text };
@@ -185,7 +185,7 @@ public partial class AnthropicService
review.Workspace, review.UseMemory, review.WebSearchVersion,
review.ToolChoice, reviewMessages, chainId, sessionId, apiToken, usage,
baseIterations, "review", review.Agent?.Name ?? "Review-Agent",
workCt, sseCt);
workCt, sseCt, review.Effort);
}
var result = new ReviewResult { ReviewIterationCount = iterations };
@@ -256,7 +256,7 @@ public partial class AnthropicService
finalizer.Workspace, finalizer.UseMemory, finalizer.WebSearchVersion,
finalizer.ToolChoice, finalizerMessages, chainId, sessionId, apiToken, usage,
baseIterations, "finalizer", finalizer.Agent?.Name ?? "Finalizer-Agent",
workCt, sseCt);
workCt, sseCt, finalizer.Effort);
}
await SseHelper.TryWriteSse(response, "finalizer_done", new
@@ -446,7 +446,8 @@ public partial class AnthropicService
int baseIteration,
int maxIterations,
CancellationToken ct,
NonStreamingLoopCallbacks cb = null)
NonStreamingLoopCallbacks cb = null,
string effort = null)
{
cb ??= new NonStreamingLoopCallbacks();
int iterations = 0;
@@ -465,6 +466,10 @@ public partial class AnthropicService
["messages"] = messages
};
// Reasoning-Effort (Opus 4.5+, Sonnet 4.6+). "" / "high" / null = Default weglassen.
if (!string.IsNullOrEmpty(effort) && effort != "high")
payload["output_config"] = new { effort };
if (systemPrompt is List<object> spBlocks && spBlocks.Count > 0)
{
// Cache-Breakpoint auf den letzten System-Prompt-Block (1h TTL).
@@ -640,7 +645,8 @@ public partial class AnthropicService
string agentType,
string agentName,
CancellationToken workCt,
CancellationToken sseCt)
CancellationToken sseCt,
string effort = null)
{
return RunNonStreamingToolLoop(
modelId, systemPrompt,
@@ -657,7 +663,8 @@ public partial class AnthropicService
SseResponse = response, SseCt = sseCt,
SsePrefix = agentType, AgentName = agentName,
Tag = agentType
});
},
effort);
}
/// <summary>
@@ -722,6 +729,7 @@ public partial class AnthropicService
systemPrompt: systemText.ToString(),
workspace: sub.Workspace,
hasWorkspace: !string.IsNullOrEmpty(sub.Workspace),
effort: sub.Effort,
cancellationToken: workCt
);
@@ -785,7 +793,8 @@ public partial class AnthropicService
CancellationToken = ct, VectorSearch = _vectorSearch, EmbeddingBg = _embeddingBg,
Db = _db, ChatService = _chatService, AnthropicService = this, OpenAiService = _openAiService
})
});
},
ctx.Effort);
var finalStopReason = text.Length > 0 ? "end_turn" : "error";
await FinalizeChain(chain, usage, iterations, finalStopReason, sw);