From a181e585436eddc90c83eaf02e4a2323c1f42891 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Peer=20Sch=C3=BCtt?=
<20603780+peerschuett@users.noreply.github.com>
Date: Wed, 10 Jun 2026 11:29:23 +0200
Subject: [PATCH] Remodelled how tool policy instructions are saved and added
to the llm call.
---
.../Assistants/I18N/allTexts.lua | 3 ++
app/MindWork AI Studio/Chat/ChatThread.cs | 26 +---------
.../Provider/Anthropic/ProviderAnthropic.cs | 8 ++--
.../Provider/BaseProvider.cs | 28 +++++++----
.../Provider/OpenAI/ProviderOpenAI.cs | 38 ++++++++-------
.../Tools/ToolCallingSystem/ToolDefinition.cs | 2 +
.../Tools/ToolCallingSystem/ToolExecutor.cs | 48 +++++++++++--------
.../ToolCallingSystem/ToolSelectionRules.cs | 19 ++++++++
.../tool_definitions/read_web_page.json | 1 +
.../wwwroot/tool_definitions/web_search.json | 3 +-
documentation/Tools.md | 11 +++--
11 files changed, 106 insertions(+), 81 deletions(-)
diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
index f4cd7c9b..454f6791 100644
--- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
+++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
@@ -6724,6 +6724,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::WRITER::T3948127789"] = "Suggestion"
-- Your stage directions
UI_TEXT_CONTENT["AISTUDIO::PAGES::WRITER::T779923726"] = "Your stage directions"
+-- The tool calling request failed with status code {0}. See the logs for details.
+UI_TEXT_CONTENT["AISTUDIO::PROVIDER::ANTHROPIC::PROVIDERANTHROPIC::T3117779001"] = "The tool calling request failed with status code {0}. See the logs for details."
+
-- We tried to communicate with the LLM provider '{0}' (type={1}). The server might be down or having issues. The provider message is: '{2}'
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1000247110"] = "We tried to communicate with the LLM provider '{0}' (type={1}). The server might be down or having issues. The provider message is: '{2}'"
diff --git a/app/MindWork AI Studio/Chat/ChatThread.cs b/app/MindWork AI Studio/Chat/ChatThread.cs
index 2ba1d7d5..3fe4f3da 100644
--- a/app/MindWork AI Studio/Chat/ChatThread.cs
+++ b/app/MindWork AI Studio/Chat/ChatThread.cs
@@ -101,7 +101,7 @@ public sealed record ChatThread
///
/// The settings manager instance to use.
/// The prepared system prompt.
- public string PrepareSystemPrompt(SettingsManager settingsManager)
+ public string PrepareSystemPrompt(SettingsManager settingsManager, IEnumerable? runnableToolDefinitions = null)
{
//
// Use the information from the chat template, if provided. Otherwise, use the default system prompt
@@ -195,7 +195,7 @@ public sealed record ChatThread
LOGGER.LogInformation(logMessage);
- var toolPolicy = this.BuildToolPolicyPrompt();
+ var toolPolicy = ToolSelectionRules.BuildToolPolicyPrompt(runnableToolDefinitions ?? []);
if (!string.IsNullOrWhiteSpace(toolPolicy))
{
systemPromptText = $"""
@@ -225,28 +225,6 @@ public sealed record ChatThread
""";
}
- private string BuildToolPolicyPrompt()
- {
- var normalizedToolIds = ToolSelectionRules.NormalizeSelection(this.RuntimeSelectedToolIds);
- var hasWebSearch = normalizedToolIds.Contains(ToolSelectionRules.WEB_SEARCH_TOOL_ID);
- var hasReadWebPage = normalizedToolIds.Contains(ToolSelectionRules.READ_WEB_PAGE_TOOL_ID);
-
- if (hasWebSearch && hasReadWebPage)
- return """
- Tool usage policy for web search:
- - Use the `web_search`-tool to discover relevant candidate URLs.
- - Do not answer substantive web questions from search snippets alone when `read_web_page` is available.
- - Search snippets alone are only sufficient for simple link-finding or very high-level orientation.
- - After `web_search`, use the `read_web_page`-tool on at least one relevant result before answering questions that require facts, summaries, comparisons, current information, or other page-level details.
- - Prefer answering from the extracted page content when it is available.
- - Summarize tool results in natural language.
- - Treat `read_web_page` results as working material for synthesis, not as final answer text.
- - Add a sources-section to the end of your answer, where you link the sources that you used.
- """;
-
- return string.Empty;
- }
-
///
/// Removes a content block from this chat thread.
///
diff --git a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
index 15e8b365..d68193df 100644
--- a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
+++ b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
@@ -94,10 +94,11 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, n
if (toolExecutor is not null && runnableTools.Count > 0)
{
+ var systemPrompt = chatThread.PrepareSystemPrompt(settingsManager, runnableTools.Select(x => x.Definition));
await foreach (var content in this.StreamWithLocalTools(
chatModel,
messages,
- chatThread.PrepareSystemPrompt(settingsManager),
+ systemPrompt,
maxTokens,
apiParameters,
runnableTools,
@@ -171,7 +172,6 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, n
.ToList();
var internalMessages = new List();
var toolCallCount = 0;
- const int MAX_TOOL_CALLS = 30;
while (true)
{
@@ -242,9 +242,9 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, n
foreach (var toolUse in toolUses)
{
toolCallCount++;
- if (toolCallCount > MAX_TOOL_CALLS)
+ if (toolCallCount > ToolSelectionRules.MAX_TOOL_CALLS)
{
- var limitMessage = $"Tool calling stopped because the maximum of {MAX_TOOL_CALLS} tool calls was reached.";
+ var limitMessage = ToolSelectionRules.GetMaxToolCallsLimitMessage();
currentAssistantContent?.ToolInvocations.Add(new ToolInvocationTrace
{
Order = toolCallCount,
diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs
index 79776dc9..124b4b10 100644
--- a/app/MindWork AI Studio/Provider/BaseProvider.cs
+++ b/app/MindWork AI Studio/Provider/BaseProvider.cs
@@ -989,13 +989,6 @@ public abstract class BaseProvider : IProvider, ISecretId
if(!requestedSecret.Success && !isTryingSecret)
yield break;
- // Prepare the system prompt:
- var systemPrompt = new TextMessage
- {
- Role = systemPromptRole,
- Content = chatThread.PrepareSystemPrompt(settingsManager),
- };
-
// Parse the API parameters:
var apiParameters = this.ParseAdditionalApiParameters();
@@ -1004,6 +997,7 @@ public abstract class BaseProvider : IProvider, ISecretId
var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText;
currentAssistantContent?.ToolInvocations.Clear();
+ TextMessage systemPrompt;
if (toolRegistry is not null && toolExecutor is not null)
{
var runnableTools = await toolRegistry.GetRunnableToolsAsync(
@@ -1013,6 +1007,12 @@ public abstract class BaseProvider : IProvider, ISecretId
this.Provider.GetConfidence(settingsManager).Level,
settingsManager.IsToolSelectionVisible(chatThread.RuntimeComponent));
+ systemPrompt = new TextMessage
+ {
+ Role = systemPromptRole,
+ Content = chatThread.PrepareSystemPrompt(settingsManager, runnableTools.Select(x => x.Definition)),
+ };
+
if (runnableTools.Count > 0)
{
var providerTools = runnableTools.Select(x => ProviderToolAdapters.ToChatCompletionTool(x.Definition)).ToList();
@@ -1062,13 +1062,12 @@ public abstract class BaseProvider : IProvider, ISecretId
ToolCalls = responseMessage.ToolCalls,
});
- var maxToolCalls = 30;
foreach (var toolCall in responseMessage.ToolCalls)
{
toolCallCount++;
- if (toolCallCount > maxToolCalls)
+ if (toolCallCount > ToolSelectionRules.MAX_TOOL_CALLS)
{
- var limitMessage = $"Tool calling stopped because the maximum of {maxToolCalls} tool calls was reached.";
+ var limitMessage = ToolSelectionRules.GetMaxToolCallsLimitMessage();
currentAssistantContent.ToolInvocations.Add(new ToolInvocationTrace
{
Order = toolCallCount,
@@ -1106,6 +1105,15 @@ public abstract class BaseProvider : IProvider, ISecretId
await currentAssistantContent.StreamingEvent();
}
}
+
+ }
+ else
+ {
+ systemPrompt = new TextMessage
+ {
+ Role = systemPromptRole,
+ Content = chatThread.PrepareSystemPrompt(settingsManager),
+ };
}
// Prepare the provider HTTP chat request:
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
index 242e8ec1..de764e2a 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
@@ -168,11 +168,27 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
yield break;
}
- // Prepare the system prompt:
+ var toolRegistry = Program.SERVICE_PROVIDER.GetService();
+ var toolExecutor = Program.SERVICE_PROVIDER.GetService();
+ var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText;
+ currentAssistantContent?.ToolInvocations.Clear();
+
+ IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)> runnableTools = toolRegistry is null
+ ? []
+ : await toolRegistry.GetRunnableToolsAsync(
+ chatThread.RuntimeComponent,
+ chatThread.RuntimeSelectedToolIds,
+ modelCapabilities,
+ providerConfidence,
+ settingsManager.IsToolSelectionVisible(chatThread.RuntimeComponent));
+
+ var toolAwareDefinitions = toolExecutor is null
+ ? Enumerable.Empty()
+ : runnableTools.Select(x => x.Definition);
var systemPrompt = new TextMessage
{
Role = systemPromptRole,
- Content = chatThread.PrepareSystemPrompt(settingsManager),
+ Content = chatThread.PrepareSystemPrompt(settingsManager, toolAwareDefinitions),
};
// Build the list of messages:
@@ -200,20 +216,6 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
var baseInput = new List