Keep context-activated tools out of every tool selection

This commit is contained in:
Thorsten Sommer committed 2026-09-24 14:32:43 +02:00
1 parent a9825520f4
commit df257714f2
5 files changed
+52 -1

No files matched your search

@@ -0,0 +1,23 @@
namespace AIStudio.Tools.ToolCallingSystem;
/// <summary>
/// How a tool comes to be offered to a model.
/// </summary>
public enum ToolActivation
{
/// <summary>
/// Offered when it was selected: by the user, a chat template, a policy, or an assistant.
/// </summary>
SELECTION,
/// <summary>
/// Offered whenever the chat calls for it, without anybody selecting it.
/// </summary>
/// <remarks>
/// For a tool whose use is already decided somewhere else. Semantic Search is such a tool: the
/// user picks the data sources of a chat, and a second switch for searching them would only be
/// a way to contradict the first one. Such a tool never appears in a selection, and it decides
/// on each request whether it has anything to offer.
/// </remarks>
CONTEXT,
}
@@ -12,6 +12,11 @@ public sealed class ToolDefinition
public ToolVisibilityDefinition VisibleIn { get; init; } = new();
/// <summary>
/// Whether the tool waits to be selected, or offers itself whenever the chat calls for it.
/// </summary>
public ToolActivation Activation { get; init; } = ToolActivation.SELECTION;
public ToolSettingsSchema SettingsSchema { get; init; } = new();
public string SystemPromptInstructions { get; init; } = string.Empty;
@@ -269,9 +269,19 @@ public sealed class ToolRegistry
return filtered;
}
/// <summary>
/// The tools somebody can select in this component.
/// </summary>
/// <remarks>
/// Every selection in the app is built from this list: the one below the message field, the
/// defaults, the templates, and the tools the AI picks for a new assistant. A tool which offers
/// itself from the context of a chat is left out, because selecting it would change nothing.
/// The tool list of the app settings asks for all definitions instead, so an organization can
/// still switch such a tool off or set the trust it requires.
/// </remarks>
public async Task<IReadOnlyList<ToolCatalogItem>> GetCatalogAsync(Components component)
{
var definitions = this.GetDefinitionsForComponent(component);
var definitions = this.GetDefinitionsForComponent(component).Where(x => x.Activation is ToolActivation.SELECTION);
return await this.GetCatalogAsync(definitions);
}
@@ -9,6 +9,7 @@ public static class ToolSelectionRules
public const string WEB_SEARCH_TOOL_ID = "web_search";
public const string READ_WEB_PAGE_TOOL_ID = "read_web_page";
public const string SEARCH_CONFLUENCE_TOOL_ID = "search_confluence";
public const string SEMANTIC_SEARCH_TOOL_ID = "semantic_search";
/// <summary>
/// Turns a set of selected tool IDs into the set which actually runs.
@@ -19,6 +20,10 @@ public static class ToolSelectionRules
/// ToolRegistry still drops it when it is switched off or the provider's confidence is too
/// low, and Read Web Page reaches a wiki on a private or VPN address only when its host is
/// allowed there.<br/><br/>
/// It also removes the tools nobody selects. Semantic Search offers itself whenever the data
/// sources of a chat call for it, see ToolActivation.CONTEXT; kept in a selection, it would
/// appear on the security card of a plugin and in its audit without the selection having any
/// say in whether it runs.<br/><br/>
/// Every place which shows or stores a selection normalizes it, the tool selection fields
/// included. That way a chat, a template, a policy, or an assistant plugin shows the tools
/// which will actually run, and the audit of a plugin judges exactly those.
@@ -29,6 +34,7 @@ public static class ToolSelectionRules
if (normalized.Contains(SEARCH_CONFLUENCE_TOOL_ID))
normalized.Add(READ_WEB_PAGE_TOOL_ID);
normalized.Remove(SEMANTIC_SEARCH_TOOL_ID);
return normalized;
}