mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-10 23:47:21 +00:00
Fixed and improved the system prompt of chats started from assistants (#1172)
This commit is contained in:
1 parent
bbf4dc2231
commit
31862f5ecd
50 files changed
+1112
-107
No files matched your search
@@ -21,6 +21,13 @@ and whoever runs it: loading, starting, checking an import, and the assistant bu
|
||||
- `PluginModuleLoadingTests` checks both sides: what plugins must load, and what they must not reach. Run
|
||||
it after every change to the runtime, the module loader, or the opened libraries.
|
||||
|
||||
## Assistant plugins
|
||||
|
||||
Every prompt text an assistant plugin brings, its `SystemPrompt` as well as its `CustomChatSystemPrompt`, goes
|
||||
through `PluginAssistants.BuildSecureSystemPrompt` and into the security audit of `AssistantAuditAgent`. A new
|
||||
field which reaches a model as a prompt needs both, and its documentation in `Plugins/assistants/README.md` and in
|
||||
the template `Plugins/assistants/plugin.lua`: the Assistant Builder reads both files as its source of truth.
|
||||
|
||||
## Configuration plugins
|
||||
|
||||
Plugins can configure:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Immutable;
|
||||
using AIStudio.Assistants;
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
|
||||
using Lua;
|
||||
@@ -46,6 +47,23 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
/// trusted enough to receive, stays out of reach either way.
|
||||
/// </remarks>
|
||||
public IReadOnlyList<string>? AssistantToolIds { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Which system prompt a chat gets which continues the conversation of this assistant.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The plugin chooses it in ChatSystemPrompt, like every assistant of AI Studio chooses it for itself.
|
||||
/// Without that key, the chat gets the default prompt of AI Studio. A prompt the plugin writes for the
|
||||
/// chat gets the same security preamble and postamble as its system prompt.
|
||||
/// </remarks>
|
||||
public ChatSystemPromptChoice ChatSystemPrompt { get; private set; } = ChatSystemPromptChoice.AI_STUDIO_DEFAULT;
|
||||
|
||||
/// <summary>
|
||||
/// The prompt the plugin wrote for a chat which continues the conversation of this assistant, as the
|
||||
/// plugin states it; empty unless it chose CUSTOM. The security audit reads it.
|
||||
/// </summary>
|
||||
public string RawCustomChatSystemPrompt { get; private set; } = string.Empty;
|
||||
|
||||
public bool HasEmbeddedProfileSelection { get; private set; }
|
||||
public bool HasCustomPromptBuilder => this.buildPromptFunction is not null;
|
||||
public bool IsAssistantBuilderGenerated { get; private set; }
|
||||
@@ -85,6 +103,8 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
this.SubmitText = string.Empty;
|
||||
this.AllowProfiles = true;
|
||||
this.AssistantToolIds = null;
|
||||
this.ChatSystemPrompt = ChatSystemPromptChoice.AI_STUDIO_DEFAULT;
|
||||
this.RawCustomChatSystemPrompt = string.Empty;
|
||||
this.HasEmbeddedProfileSelection = false;
|
||||
this.IsAssistantBuilderGenerated = false;
|
||||
this.HasDeploymentManagementMetadata = false;
|
||||
@@ -140,7 +160,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
if (!assistantTable.TryGetValue("SubmitText", out var assistantSubmitTextValue) ||
|
||||
!assistantSubmitTextValue.TryRead<string>(out var assistantSubmitText))
|
||||
{
|
||||
message = TB("The ASSISTANT table does not contain a valid system prompt.");
|
||||
message = TB("The provided ASSISTANT lua table does not contain a valid submit text.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -154,6 +174,9 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
if (!TryReadOptionalToolIds(assistantTable, out var assistantToolIds, out message))
|
||||
return false;
|
||||
|
||||
if (!TryReadChatSystemPrompt(assistantTable, out var chatSystemPrompt, out var rawCustomChatSystemPrompt, out message))
|
||||
return false;
|
||||
|
||||
if (assistantTable.TryGetValue("BuildPrompt", out var buildPromptValue))
|
||||
{
|
||||
if (buildPromptValue.TryRead<LuaFunction>(out var buildPrompt))
|
||||
@@ -169,6 +192,8 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
this.SubmitText = assistantSubmitText;
|
||||
this.AllowProfiles = assistantAllowProfiles;
|
||||
this.AssistantToolIds = assistantToolIds;
|
||||
this.ChatSystemPrompt = chatSystemPrompt;
|
||||
this.RawCustomChatSystemPrompt = rawCustomChatSystemPrompt;
|
||||
|
||||
// Ensure that the UI table exists nested in the ASSISTANT table and is a valid Lua table:
|
||||
if (!assistantTable.TryGetValue("UI", out var uiVal) || !uiVal.TryRead<LuaTable>(out var uiTable))
|
||||
@@ -374,6 +399,65 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads which system prompt a chat gets which continues the conversation of the assistant.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Optional: without ChatSystemPrompt, the chat gets the default prompt of AI Studio. A prompt of its own
|
||||
/// goes into CustomChatSystemPrompt, and only together with CUSTOM. That prompt next to another choice,
|
||||
/// or CUSTOM without it, is a mistake which the author would otherwise only notice in the chat, so the
|
||||
/// plugin does not load. The names are compared exactly, apart from case: a number is no name, even
|
||||
/// though Enum.TryParse would take it for one.
|
||||
/// </remarks>
|
||||
/// <param name="assistantTable">The ASSISTANT table of the plugin.</param>
|
||||
/// <param name="choice">The choice of the plugin; the default prompt of AI Studio when it makes none.</param>
|
||||
/// <param name="rawCustomPrompt">The prompt the plugin wrote for the chat, as it states it; empty unless it chose CUSTOM.</param>
|
||||
/// <param name="message">Why the table could not be read; empty otherwise.</param>
|
||||
/// <returns>True when the table states a valid choice or none at all.</returns>
|
||||
internal static bool TryReadChatSystemPrompt(LuaTable assistantTable, out ChatSystemPromptChoice choice, out string rawCustomPrompt, out string message)
|
||||
{
|
||||
choice = ChatSystemPromptChoice.AI_STUDIO_DEFAULT;
|
||||
rawCustomPrompt = string.Empty;
|
||||
message = string.Empty;
|
||||
|
||||
var source = ChatSystemPromptSource.AI_STUDIO_DEFAULT;
|
||||
if (assistantTable.TryGetValue("ChatSystemPrompt", out var sourceValue))
|
||||
{
|
||||
var sourceName = sourceValue.TryRead<string>(out var sourceText) ? Enum.GetNames<ChatSystemPromptSource>().FirstOrDefault(name => string.Equals(name, sourceText.Trim(), StringComparison.OrdinalIgnoreCase)) : null;
|
||||
if (sourceName is null)
|
||||
{
|
||||
message = TB("The ASSISTANT table contains an invalid ChatSystemPrompt value. Expected 'AI_STUDIO_DEFAULT', 'ASSISTANT', or 'CUSTOM'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
source = Enum.Parse<ChatSystemPromptSource>(sourceName);
|
||||
}
|
||||
|
||||
var hasCustomPrompt = assistantTable.TryGetValue("CustomChatSystemPrompt", out var customPromptValue);
|
||||
if (source is not ChatSystemPromptSource.CUSTOM)
|
||||
{
|
||||
if (hasCustomPrompt)
|
||||
{
|
||||
message = TB("The ASSISTANT table contains a CustomChatSystemPrompt, but its ChatSystemPrompt is not 'CUSTOM', so no chat would use that prompt.");
|
||||
return false;
|
||||
}
|
||||
|
||||
choice = source is ChatSystemPromptSource.ASSISTANT ? ChatSystemPromptChoice.ASSISTANT : ChatSystemPromptChoice.AI_STUDIO_DEFAULT;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hasCustomPrompt || !customPromptValue.TryRead<string>(out var customPrompt) || string.IsNullOrWhiteSpace(customPrompt))
|
||||
{
|
||||
message = TB("The ASSISTANT table contains the ChatSystemPrompt 'CUSTOM' but no valid CustomChatSystemPrompt.");
|
||||
return false;
|
||||
}
|
||||
|
||||
rawCustomPrompt = customPrompt.Trim();
|
||||
var securePrompt = BuildSecureSystemPrompt(rawCustomPrompt);
|
||||
choice = ChatSystemPromptChoice.Custom(() => securePrompt);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<string?> TryBuildPromptAsync(LuaTable input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (this.buildPromptFunction is null)
|
||||
|
||||
Reference in new issue
Block a user