From 6836fd25b5fc441c8e09fb8fc86740456b261e00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Peer=20Sch=C3=BCtt?=
<20603780+peerschuett@users.noreply.github.com>
Date: Fri, 20 Mar 2026 14:57:25 +0100
Subject: [PATCH] First working prototype for PromptOptimizer assistant
---
.../Assistants/AssistantBase.razor | 12 +
.../Assistants/AssistantBase.razor.cs | 4 +
.../AssistantPromptOptimizer.razor | 106 ++++
.../AssistantPromptOptimizer.razor.cs | 490 ++++++++++++++++++
.../PromptOptimizationResult.cs | 33 ++
.../PromptOptimizer/prompting_guideline.md | 85 +++
.../Dialogs/PromptingGuidelineDialog.razor | 26 +
.../Dialogs/PromptingGuidelineDialog.razor.cs | 22 +
app/MindWork AI Studio/Pages/Assistants.razor | 2 +
app/MindWork AI Studio/Routes.razor.cs | 1 +
.../Settings/ConfigurableAssistant.cs | 1 +
.../Tools/AssistantVisibilityExtensions.cs | 1 +
app/MindWork AI Studio/Tools/Components.cs | 3 +-
.../Tools/ComponentsExtensions.cs | 4 +-
app/MindWork AI Studio/Tools/Event.cs | 1 +
15 files changed, 789 insertions(+), 2 deletions(-)
create mode 100644 app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor
create mode 100644 app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs
create mode 100644 app/MindWork AI Studio/Assistants/PromptOptimizer/PromptOptimizationResult.cs
create mode 100644 app/MindWork AI Studio/Assistants/PromptOptimizer/prompting_guideline.md
create mode 100644 app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor
create mode 100644 app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor.cs
diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor b/app/MindWork AI Studio/Assistants/AssistantBase.razor
index 3268612d..4760b622 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor
@@ -8,6 +8,13 @@
@this.Title
+
+
+
+ @if (this.HeaderActions is not null)
+ {
+ @this.HeaderActions
+ }
@if (this.HasSettingsPanel)
{
@@ -71,6 +78,11 @@
}
}
}
+
+ @if (this.ShowResult && this.AfterResultContent is not null)
+ {
+ @this.AfterResultContent
+ }
diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
index 632722ab..1b1503b6 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
@@ -81,6 +81,10 @@ public abstract partial class AssistantBase : AssistantLowerBase wher
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
+ private protected virtual RenderFragment? HeaderActions => null;
+
+ private protected virtual RenderFragment? AfterResultContent => null;
+
protected virtual IReadOnlyList FooterButtons => [];
protected virtual bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel);
diff --git a/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor
new file mode 100644
index 00000000..615266e0
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor
@@ -0,0 +1,106 @@
+@attribute [Route(Routes.ASSISTANT_PROMPT_OPTIMIZER)]
+@inherits AssistantBaseCore
+
+
+
+
+@if (!this.useCustomPromptGuide)
+{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+
+
+ @T("View default prompt guide")
+
+
+
+ @T("Use custom prompt guide")
+
+
+ @if (this.useCustomPromptGuide)
+ {
+
+ }
+
+
+
+
+ @T("View")
+
+
+
+
+
+
+
+
+
diff --git a/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs
new file mode 100644
index 00000000..8b8b0b2f
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs
@@ -0,0 +1,490 @@
+using System.Text.Json;
+using System.Text.RegularExpressions;
+
+using AIStudio.Chat;
+using AIStudio.Dialogs;
+using AIStudio.Dialogs.Settings;
+using Microsoft.AspNetCore.Components;
+
+#if !DEBUG
+using System.Reflection;
+using Microsoft.Extensions.FileProviders;
+#endif
+
+namespace AIStudio.Assistants.PromptOptimizer;
+
+public partial class AssistantPromptOptimizer : AssistantBaseCore
+{
+ private static readonly Regex JSON_CODE_FENCE_REGEX = new(
+ pattern: """```(?:json)?\s*(?\{[\s\S]*\})\s*```""",
+ options: RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ private static readonly JsonSerializerOptions JSON_OPTIONS = new()
+ {
+ PropertyNameCaseInsensitive = true,
+ };
+
+ [Inject]
+ private IDialogService DialogService { get; init; } = null!;
+
+ protected override Tools.Components Component => Tools.Components.PROMPT_OPTIMIZER_ASSISTANT;
+
+ protected override string Title => T("Prompt Optimizer");
+
+ protected override string Description => T("Optimize a prompt using your prompt guideline and get targeted recommendations for future versions.");
+
+ protected override string SystemPrompt =>
+ """
+ You are an expert prompt optimization assistant.
+ You optimize user prompts while preserving the original intent.
+ You must return valid JSON only and no extra markdown or commentary.
+ """;
+
+ protected override bool AllowProfiles => false;
+
+ protected override bool ShowDedicatedProgress => true;
+
+ protected override bool ShowEntireChatThread => true;
+
+ protected override Func Result2Copy => () => this.optimizedPrompt;
+
+ protected override IReadOnlyList FooterButtons =>
+ [
+ new SendToButton
+ {
+ Self = Tools.Components.PROMPT_OPTIMIZER_ASSISTANT,
+ UseResultingContentBlockData = false,
+ GetText = () => string.IsNullOrWhiteSpace(this.optimizedPrompt) ? this.inputPrompt : this.optimizedPrompt,
+ },
+ ];
+
+ protected override string SubmitText => T("Optimize prompt");
+
+ protected override Func SubmitAction => this.OptimizePromptAsync;
+
+ protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
+ {
+ SystemPrompt = SystemPrompts.DEFAULT,
+ };
+
+ protected override void ResetForm()
+ {
+ this.inputPrompt = string.Empty;
+ this.selectedTargetLanguage = CommonLanguages.AS_IS;
+ this.customTargetLanguage = string.Empty;
+ this.importantAspects = string.Empty;
+ this.useCustomPromptGuide = false;
+ this.customPromptGuideFiles.Clear();
+ this.currentCustomPromptGuidePath = string.Empty;
+ this.customPromptingGuidelineContent = string.Empty;
+ this.ResetGuidelineSummaryToDefault();
+ this.ResetOutput();
+ }
+
+ protected override bool MightPreselectValues() => false;
+
+ protected override async Task OnInitializedAsync()
+ {
+ this.ResetGuidelineSummaryToDefault();
+
+ var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages(Event.SEND_TO_PROMPT_OPTIMIZER_ASSISTANT).FirstOrDefault();
+ if (deferredContent is not null)
+ this.inputPrompt = deferredContent;
+
+ await base.OnInitializedAsync();
+ }
+
+ private string inputPrompt = string.Empty;
+ private CommonLanguages selectedTargetLanguage;
+ private string customTargetLanguage = string.Empty;
+ private string importantAspects = string.Empty;
+ private bool useCustomPromptGuide;
+ private HashSet customPromptGuideFiles = [];
+ private string currentCustomPromptGuidePath = string.Empty;
+ private string customPromptingGuidelineContent = string.Empty;
+ private bool isLoadingCustomPromptGuide;
+
+ private string optimizedPrompt = string.Empty;
+ private string recClarityDirectness = string.Empty;
+ private string recExamplesContext = string.Empty;
+ private string recSequentialSteps = string.Empty;
+ private string recStructureMarkers = string.Empty;
+ private string recRoleDefinition = string.Empty;
+ private string recLanguageChoice = string.Empty;
+
+ private bool HasOptimizationResult => !string.IsNullOrWhiteSpace(this.optimizedPrompt);
+ private bool CanPreviewCustomPromptGuide => this.useCustomPromptGuide && this.customPromptGuideFiles.Count > 0;
+ private string CustomPromptGuideFileName => this.customPromptGuideFiles.Count switch
+ {
+ 0 => T("No file selected"),
+ _ => this.customPromptGuideFiles.First().FileName
+ };
+
+ private string? ValidateInputPrompt(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text))
+ return T("Please provide a prompt or prompt description.");
+
+ return null;
+ }
+
+ private string? ValidateCustomLanguage(string language)
+ {
+ if (this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
+ return T("Please provide a custom language.");
+
+ return null;
+ }
+
+ private string SystemPromptLanguage()
+ {
+ var language = this.selectedTargetLanguage switch
+ {
+ CommonLanguages.AS_IS => "the source language of the input prompt",
+ CommonLanguages.OTHER => this.customTargetLanguage,
+ _ => this.selectedTargetLanguage.Name(),
+ };
+
+ if (string.IsNullOrWhiteSpace(language))
+ return "the source language of the input prompt";
+
+ return language;
+ }
+
+ private async Task OptimizePromptAsync()
+ {
+ await this.form!.Validate();
+ if (!this.inputIsValid)
+ return;
+
+ this.ClearInputIssues();
+ this.ResetOutput();
+
+ var promptingGuideline = await this.GetPromptingGuidelineForOptimizationAsync();
+ if (string.IsNullOrWhiteSpace(promptingGuideline))
+ {
+ if (this.useCustomPromptGuide)
+ this.AddInputIssue(T("Please attach and load a valid custom prompt guide file."));
+ else
+ this.AddInputIssue(T("The prompting guideline file could not be loaded. Please verify 'prompting_guideline.md' in Assistants/PromptOptimizer."));
+ return;
+ }
+
+ this.CreateChatThread();
+ var requestTime = this.AddUserRequest(this.BuildOptimizationRequest(promptingGuideline), hideContentFromUser: true);
+ var aiResponse = await this.AddAIResponseAsync(requestTime, hideContentFromUser: true);
+
+ if (!TryParseOptimizationResult(aiResponse, out var parsedResult))
+ {
+ this.optimizedPrompt = aiResponse.Trim();
+ this.recClarityDirectness = T("Add clearer goals and explicit quality expectations.");
+ this.recExamplesContext = T("Add short examples and background context for your specific use case.");
+ this.recSequentialSteps = T("Break the task into numbered steps if order matters.");
+ this.recStructureMarkers = T("Use headings or markers to separate context, task, and constraints.");
+ this.recRoleDefinition = T("Define a role for the model to focus output style and expertise.");
+ this.recLanguageChoice = T("Use English for complex prompts and explicitly request response language if needed.");
+ this.AddInputIssue(T("The model response was not in the expected JSON format. The raw response is shown as optimized prompt."));
+ this.AddVisibleOptimizedPromptBlock();
+ return;
+ }
+
+ this.ApplyOptimizationResult(parsedResult);
+ this.AddVisibleOptimizedPromptBlock();
+ }
+
+ private string BuildOptimizationRequest(string promptingGuideline)
+ {
+ return
+ $$"""
+ # Prompting Guideline
+
+ {{promptingGuideline}}
+
+
+ # Task
+ Optimize the user's prompt according to the prompting guideline.
+ Preserve the original intent.
+ Ensure the optimized prompt is in {{this.SystemPromptLanguage()}}.
+ {{this.PromptImportantAspects()}}
+
+ # User Input Prompt
+
+ {{this.inputPrompt}}
+
+
+ # Output Requirements
+ Return valid JSON only (no markdown code fence, no additional text), using exactly this schema:
+ {
+ "optimized_prompt": "string",
+ "recommendations": {
+ "clarity_and_directness": "string",
+ "examples_and_context": "string",
+ "sequential_steps": "string",
+ "structure_with_markers": "string",
+ "role_definition": "string",
+ "language_choice": "string"
+ }
+ }
+
+ # Recommendation style
+ Keep each recommendation concise and actionable. Mention what to improve in a future prompt version.
+ """;
+ }
+
+ private string PromptImportantAspects()
+ {
+ if (string.IsNullOrWhiteSpace(this.importantAspects))
+ return string.Empty;
+
+ return
+ $"""
+ Additional emphasis for the optimization:
+
+ {this.importantAspects}
+
+ """;
+ }
+
+ private static bool TryParseOptimizationResult(string rawResponse, out PromptOptimizationResult parsedResult)
+ {
+ parsedResult = new();
+
+ if (TryDeserialize(rawResponse, out parsedResult))
+ return true;
+
+ var codeFenceMatch = JSON_CODE_FENCE_REGEX.Match(rawResponse);
+ if (codeFenceMatch.Success)
+ {
+ var codeFenceJson = codeFenceMatch.Groups["json"].Value;
+ if (TryDeserialize(codeFenceJson, out parsedResult))
+ return true;
+ }
+
+ var firstBrace = rawResponse.IndexOf('{');
+ var lastBrace = rawResponse.LastIndexOf('}');
+ if (firstBrace >= 0 && lastBrace > firstBrace)
+ {
+ var objectText = rawResponse[firstBrace..(lastBrace + 1)];
+ if (TryDeserialize(objectText, out parsedResult))
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool TryDeserialize(string json, out PromptOptimizationResult parsedResult)
+ {
+ parsedResult = new();
+
+ if (string.IsNullOrWhiteSpace(json))
+ return false;
+
+ try
+ {
+ var probe = JsonSerializer.Deserialize(json, JSON_OPTIONS);
+ if (probe is null || string.IsNullOrWhiteSpace(probe.OptimizedPrompt))
+ return false;
+
+ probe.Recommendations ??= new PromptOptimizationRecommendations();
+ parsedResult = probe;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private void ApplyOptimizationResult(PromptOptimizationResult optimizationResult)
+ {
+ this.optimizedPrompt = optimizationResult.OptimizedPrompt.Trim();
+ this.recClarityDirectness = this.EmptyFallback(optimizationResult.Recommendations.ClarityAndDirectness);
+ this.recExamplesContext = this.EmptyFallback(optimizationResult.Recommendations.ExamplesAndContext);
+ this.recSequentialSteps = this.EmptyFallback(optimizationResult.Recommendations.SequentialSteps);
+ this.recStructureMarkers = this.EmptyFallback(optimizationResult.Recommendations.StructureWithMarkers);
+ this.recRoleDefinition = this.EmptyFallback(optimizationResult.Recommendations.RoleDefinition);
+ this.recLanguageChoice = this.EmptyFallback(optimizationResult.Recommendations.LanguageChoice);
+ }
+
+ private string EmptyFallback(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text))
+ return T("No further recommendation in this area.");
+
+ return text.Trim();
+ }
+
+ private void ResetOutput()
+ {
+ this.optimizedPrompt = string.Empty;
+ }
+
+ private void ResetGuidelineSummaryToDefault()
+ {
+ this.recClarityDirectness = T("Use clear, explicit instructions and directly state quality expectations.");
+ this.recExamplesContext = T("Include short examples and context that explain the purpose behind requirements.");
+ this.recSequentialSteps = T("Prefer numbered steps when task order matters.");
+ this.recStructureMarkers = T("Separate context, task, constraints, and output format with headings or markers.");
+ this.recRoleDefinition = T("Assign a role to shape tone, expertise, and focus.");
+ this.recLanguageChoice = T("For complex tasks, write prompts in English and request response language explicitly if needed.");
+ }
+
+ private void AddVisibleOptimizedPromptBlock()
+ {
+ if (string.IsNullOrWhiteSpace(this.optimizedPrompt))
+ return;
+
+ if (this.chatThread is null)
+ return;
+
+ var visibleResponseContent = new ContentText
+ {
+ Text = this.optimizedPrompt,
+ };
+
+ this.chatThread.Blocks.Add(new ContentBlock
+ {
+ Time = DateTimeOffset.Now,
+ ContentType = ContentType.TEXT,
+ Role = ChatRole.AI,
+ HideFromUser = false,
+ Content = visibleResponseContent,
+ });
+ }
+
+ private static async Task ReadPromptingGuidelineAsync()
+ {
+#if DEBUG
+ var guidelinePath = Path.Join(Environment.CurrentDirectory, "Assistants", "PromptOptimizer", "prompting_guideline.md");
+ return File.Exists(guidelinePath)
+ ? await File.ReadAllTextAsync(guidelinePath)
+ : string.Empty;
+#else
+ var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Assistants/PromptOptimizer");
+ var file = resourceFileProvider.GetFileInfo("prompting_guideline.md");
+ if (!file.Exists)
+ return string.Empty;
+
+ await using var fileStream = file.CreateReadStream();
+ using var reader = new StreamReader(fileStream);
+ return await reader.ReadToEndAsync();
+#endif
+ }
+
+ private async Task GetPromptingGuidelineForOptimizationAsync()
+ {
+ if (!this.useCustomPromptGuide)
+ return await ReadPromptingGuidelineAsync();
+
+ if (this.customPromptGuideFiles.Count == 0)
+ return string.Empty;
+
+ if (!string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent))
+ return this.customPromptingGuidelineContent;
+
+ var fileAttachment = this.customPromptGuideFiles.First();
+ await this.LoadCustomPromptGuidelineContentAsync(fileAttachment);
+ return this.customPromptingGuidelineContent;
+ }
+
+ private async Task SetUseCustomPromptGuide(bool useCustom)
+ {
+ this.useCustomPromptGuide = useCustom;
+ if (!useCustom)
+ return;
+
+ if (this.customPromptGuideFiles.Count == 0)
+ return;
+
+ var fileAttachment = this.customPromptGuideFiles.First();
+ if (string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent))
+ await this.LoadCustomPromptGuidelineContentAsync(fileAttachment);
+ }
+
+ private async Task OnCustomPromptGuideFilesChanged(HashSet files)
+ {
+ if (files.Count == 0)
+ {
+ this.customPromptGuideFiles.Clear();
+ this.currentCustomPromptGuidePath = string.Empty;
+ this.customPromptingGuidelineContent = string.Empty;
+ return;
+ }
+
+ // Keep only a single file and prefer the newly attached one over the previous selection.
+ var selected = files.FirstOrDefault(file => !string.Equals(file.FilePath, this.currentCustomPromptGuidePath, StringComparison.OrdinalIgnoreCase))
+ ?? files.First();
+
+ this.customPromptGuideFiles = [ selected ];
+ this.currentCustomPromptGuidePath = selected.FilePath;
+
+ if (files.Count > 1)
+ this.Snackbar.Add(T("Replaced the previously selected custom prompt guide file."), Severity.Info);
+
+ await this.LoadCustomPromptGuidelineContentAsync(selected);
+ }
+
+ private async Task LoadCustomPromptGuidelineContentAsync(FileAttachment fileAttachment)
+ {
+ if (!fileAttachment.Exists)
+ {
+ this.customPromptingGuidelineContent = string.Empty;
+ this.Snackbar.Add(T("The selected custom prompt guide file could not be found."), Severity.Warning);
+ return;
+ }
+
+ try
+ {
+ this.isLoadingCustomPromptGuide = true;
+ this.customPromptingGuidelineContent = await UserFile.LoadFileData(fileAttachment.FilePath, this.RustService, this.DialogService);
+ if (string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent))
+ this.Snackbar.Add(T("The custom prompt guide file is empty or could not be read."), Severity.Warning);
+ }
+ catch
+ {
+ this.customPromptingGuidelineContent = string.Empty;
+ this.Snackbar.Add(T("Failed to load custom prompt guide content."), Severity.Error);
+ }
+ finally
+ {
+ this.isLoadingCustomPromptGuide = false;
+ this.StateHasChanged();
+ }
+ }
+
+ private async Task OpenPromptingGuidelineDialog()
+ {
+ var promptingGuideline = await ReadPromptingGuidelineAsync();
+ if (string.IsNullOrWhiteSpace(promptingGuideline))
+ {
+ this.Snackbar.Add(T("The prompting guideline file could not be loaded."), Severity.Warning);
+ return;
+ }
+
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.GuidelineMarkdown, promptingGuideline }
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync(T("Prompting Guideline"), dialogParameters, AIStudio.Dialogs.DialogOptions.FULLSCREEN);
+ await dialogReference.Result;
+ }
+
+ private async Task OpenCustomPromptGuideDialog()
+ {
+ if (this.customPromptGuideFiles.Count == 0)
+ return;
+
+ var fileAttachment = this.customPromptGuideFiles.First();
+ if (string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent) && !this.isLoadingCustomPromptGuide)
+ await this.LoadCustomPromptGuidelineContentAsync(fileAttachment);
+
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.Document, fileAttachment },
+ { x => x.FileContent, this.customPromptingGuidelineContent },
+ };
+
+ await this.DialogService.ShowAsync(T("Custom Prompt Guide Preview"), dialogParameters, AIStudio.Dialogs.DialogOptions.FULLSCREEN);
+ }
+}
diff --git a/app/MindWork AI Studio/Assistants/PromptOptimizer/PromptOptimizationResult.cs b/app/MindWork AI Studio/Assistants/PromptOptimizer/PromptOptimizationResult.cs
new file mode 100644
index 00000000..88a78374
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/PromptOptimizer/PromptOptimizationResult.cs
@@ -0,0 +1,33 @@
+using System.Text.Json.Serialization;
+
+namespace AIStudio.Assistants.PromptOptimizer;
+
+public sealed class PromptOptimizationResult
+{
+ [JsonPropertyName("optimized_prompt")]
+ public string OptimizedPrompt { get; set; } = string.Empty;
+
+ [JsonPropertyName("recommendations")]
+ public PromptOptimizationRecommendations Recommendations { get; set; } = new();
+}
+
+public sealed class PromptOptimizationRecommendations
+{
+ [JsonPropertyName("clarity_and_directness")]
+ public string ClarityAndDirectness { get; set; } = string.Empty;
+
+ [JsonPropertyName("examples_and_context")]
+ public string ExamplesAndContext { get; set; } = string.Empty;
+
+ [JsonPropertyName("sequential_steps")]
+ public string SequentialSteps { get; set; } = string.Empty;
+
+ [JsonPropertyName("structure_with_markers")]
+ public string StructureWithMarkers { get; set; } = string.Empty;
+
+ [JsonPropertyName("role_definition")]
+ public string RoleDefinition { get; set; } = string.Empty;
+
+ [JsonPropertyName("language_choice")]
+ public string LanguageChoice { get; set; } = string.Empty;
+}
diff --git a/app/MindWork AI Studio/Assistants/PromptOptimizer/prompting_guideline.md b/app/MindWork AI Studio/Assistants/PromptOptimizer/prompting_guideline.md
new file mode 100644
index 00000000..e505ee72
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/PromptOptimizer/prompting_guideline.md
@@ -0,0 +1,85 @@
+# 1 – Be Clear and Direct
+
+LLMs respond best to clear, explicit instructions. Being specific about your desired output improves results. If you want high-quality work, ask for it directly rather than expecting the model to guess.
+
+Think of the LLM as a skilled new employee: They do not know your specific workflows yet. The more precisely you explain what you want, the better the result.
+
+**Golden Rule:** If a colleague would be confused by your prompt without extra context, the LLM will be too.
+
+**Less Effective:**
+```text
+Create an analytics dashboard
+```
+
+**More Effective:**
+```text
+Create an analytics dashboard. Include relevant features and interactions. Go beyond the basics to create a fully-featured implementation.
+```
+
+# 2 – Add Examples and Context to Improve Performance
+
+Providing examples, context, or the reason behind your instructions helps the model understand your goals.
+
+**Less Effective:**
+```text
+NEVER use ellipses
+```
+
+**More Effective:**
+```text
+Your response will be read aloud by a text-to-speech engine, so never use ellipses since the engine will not know how to pronounce them.
+```
+
+The model can generalize from the explanation.
+
+# 3 – Use Sequential Steps
+
+When the order of tasks matters, provide instructions as a numbered list.
+
+**Example:**
+```text
+1. Analyze the provided text for key themes.
+2. Extract the top 5 most frequent terms.
+3. Format the output as a table with columns: Term, Frequency, Context.
+```
+
+# 4 – Structure Prompts with Markers
+
+Headings (e.g., `#` or `###`) or quotation marks (`"""`) help the model parse complex prompts, especially when mixing instructions, context, and data.
+
+**Less Effective:**
+```text
+{text input here}
+
+Summarize the text above as a bullet point list of the most important points.
+```
+
+**More Effective:**
+```text
+# Text:
+"""{text input here}"""
+
+# Task:
+Summarize the text above as a bullet point list of the most important points.
+```
+
+# 5 – Give the LLM a Role
+
+Setting a role in your prompt focuses the LLM's behavior and tone. Even a single sentence makes a difference.
+
+**Example:**
+```text
+You are a helpful coding assistant specializing in Python.
+```
+```text
+You are a senior marketing expert with 10 years of experience in the aerospace industry.
+```
+
+# 6 – Prompt Language
+
+LLMs are primarily trained on English text. They generally perform best with prompts written in **English**, especially for complex tasks.
+
+* **Recommendation:** Write your prompts in English.
+* **If needed:** You can ask the LLM to respond in your native language (e.g., "Answer in German").
+* **Note:** This is especially important for smaller models, which may have limited multilingual capabilities.
+
diff --git a/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor b/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor
new file mode 100644
index 00000000..db50e32b
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor
@@ -0,0 +1,26 @@
+@inherits MSGComponentBase
+
+
+
+
+ @T("The full prompting guideline used by the Prompt Optimizer.")
+
+
+
+
+
+
+
+
+
+
+ @T("Close")
+
+
+
diff --git a/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor.cs
new file mode 100644
index 00000000..f8672cd9
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/PromptingGuidelineDialog.razor.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Components;
+using AIStudio.Components;
+
+namespace AIStudio.Dialogs;
+
+public partial class PromptingGuidelineDialog : MSGComponentBase
+{
+ [CascadingParameter]
+ private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter]
+ public string GuidelineMarkdown { get; set; } = string.Empty;
+
+ private void Close() => this.MudDialog.Cancel();
+
+ private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
+
+ private MudMarkdownStyling MarkdownStyling => new()
+ {
+ CodeBlock = { Theme = this.CodeColorPalette },
+ };
+}
diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor
index 8e4896dc..3a05b19c 100644
--- a/app/MindWork AI Studio/Pages/Assistants.razor
+++ b/app/MindWork AI Studio/Pages/Assistants.razor
@@ -15,6 +15,7 @@
(Components.TRANSLATION_ASSISTANT, PreviewFeatures.NONE),
(Components.GRAMMAR_SPELLING_ASSISTANT, PreviewFeatures.NONE),
(Components.REWRITE_ASSISTANT, PreviewFeatures.NONE),
+ (Components.PROMPT_OPTIMIZER_ASSISTANT, PreviewFeatures.NONE),
(Components.SYNONYMS_ASSISTANT, PreviewFeatures.NONE)
))
{
@@ -26,6 +27,7 @@
+
}
diff --git a/app/MindWork AI Studio/Routes.razor.cs b/app/MindWork AI Studio/Routes.razor.cs
index 92ff3067..594170ac 100644
--- a/app/MindWork AI Studio/Routes.razor.cs
+++ b/app/MindWork AI Studio/Routes.razor.cs
@@ -14,6 +14,7 @@ public sealed partial class Routes
// ReSharper disable InconsistentNaming
public const string ASSISTANT_TRANSLATION = "/assistant/translation";
public const string ASSISTANT_REWRITE = "/assistant/rewrite-improve";
+ public const string ASSISTANT_PROMPT_OPTIMIZER = "/assistant/prompt-optimizer";
public const string ASSISTANT_ICON_FINDER = "/assistant/icons";
public const string ASSISTANT_GRAMMAR_SPELLING = "/assistant/grammar-spelling";
public const string ASSISTANT_SUMMARIZER = "/assistant/summarizer";
diff --git a/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs b/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs
index f1076d9e..3b9bb625 100644
--- a/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs
+++ b/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs
@@ -11,6 +11,7 @@ public enum ConfigurableAssistant
GRAMMAR_SPELLING_ASSISTANT,
ICON_FINDER_ASSISTANT,
REWRITE_ASSISTANT,
+ PROMPT_OPTIMIZER_ASSISTANT,
TRANSLATION_ASSISTANT,
AGENDA_ASSISTANT,
CODING_ASSISTANT,
diff --git a/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs b/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs
index cb4e7fdc..8ab8eb88 100644
--- a/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs
+++ b/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs
@@ -47,6 +47,7 @@ public static class AssistantVisibilityExtensions
Components.GRAMMAR_SPELLING_ASSISTANT => ConfigurableAssistant.GRAMMAR_SPELLING_ASSISTANT,
Components.ICON_FINDER_ASSISTANT => ConfigurableAssistant.ICON_FINDER_ASSISTANT,
Components.REWRITE_ASSISTANT => ConfigurableAssistant.REWRITE_ASSISTANT,
+ Components.PROMPT_OPTIMIZER_ASSISTANT => ConfigurableAssistant.PROMPT_OPTIMIZER_ASSISTANT,
Components.TRANSLATION_ASSISTANT => ConfigurableAssistant.TRANSLATION_ASSISTANT,
Components.AGENDA_ASSISTANT => ConfigurableAssistant.AGENDA_ASSISTANT,
Components.CODING_ASSISTANT => ConfigurableAssistant.CODING_ASSISTANT,
diff --git a/app/MindWork AI Studio/Tools/Components.cs b/app/MindWork AI Studio/Tools/Components.cs
index 02718736..4b8f47f3 100644
--- a/app/MindWork AI Studio/Tools/Components.cs
+++ b/app/MindWork AI Studio/Tools/Components.cs
@@ -7,6 +7,7 @@ public enum Components
GRAMMAR_SPELLING_ASSISTANT,
ICON_FINDER_ASSISTANT,
REWRITE_ASSISTANT,
+ PROMPT_OPTIMIZER_ASSISTANT,
TRANSLATION_ASSISTANT,
AGENDA_ASSISTANT,
CODING_ASSISTANT,
@@ -32,4 +33,4 @@ public enum Components
AGENT_TEXT_CONTENT_CLEANER,
AGENT_DATA_SOURCE_SELECTION,
AGENT_RETRIEVAL_CONTEXT_VALIDATION,
-}
\ No newline at end of file
+}
diff --git a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
index 70f06380..dfbae19f 100644
--- a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
+++ b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
@@ -35,6 +35,7 @@ public static class ComponentsExtensions
Components.ICON_FINDER_ASSISTANT => TB("Icon Finder Assistant"),
Components.TRANSLATION_ASSISTANT => TB("Translation Assistant"),
Components.REWRITE_ASSISTANT => TB("Rewrite Assistant"),
+ Components.PROMPT_OPTIMIZER_ASSISTANT => TB("Prompt Optimizer Assistant"),
Components.AGENDA_ASSISTANT => TB("Agenda Assistant"),
Components.CODING_ASSISTANT => TB("Coding Assistant"),
Components.EMAIL_ASSISTANT => TB("E-Mail Assistant"),
@@ -57,6 +58,7 @@ public static class ComponentsExtensions
Components.AGENDA_ASSISTANT => new(Event.SEND_TO_AGENDA_ASSISTANT, Routes.ASSISTANT_AGENDA),
Components.CODING_ASSISTANT => new(Event.SEND_TO_CODING_ASSISTANT, Routes.ASSISTANT_CODING),
Components.REWRITE_ASSISTANT => new(Event.SEND_TO_REWRITE_ASSISTANT, Routes.ASSISTANT_REWRITE),
+ Components.PROMPT_OPTIMIZER_ASSISTANT => new(Event.SEND_TO_PROMPT_OPTIMIZER_ASSISTANT, Routes.ASSISTANT_PROMPT_OPTIMIZER),
Components.EMAIL_ASSISTANT => new(Event.SEND_TO_EMAIL_ASSISTANT, Routes.ASSISTANT_EMAIL),
Components.TRANSLATION_ASSISTANT => new(Event.SEND_TO_TRANSLATION_ASSISTANT, Routes.ASSISTANT_TRANSLATION),
Components.ICON_FINDER_ASSISTANT => new(Event.SEND_TO_ICON_FINDER_ASSISTANT, Routes.ASSISTANT_ICON_FINDER),
@@ -167,4 +169,4 @@ public static class ComponentsExtensions
_ => ChatTemplate.NO_CHAT_TEMPLATE,
};
-}
\ No newline at end of file
+}
diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs
index 6e899a79..a9a4f223 100644
--- a/app/MindWork AI Studio/Tools/Event.cs
+++ b/app/MindWork AI Studio/Tools/Event.cs
@@ -45,6 +45,7 @@ public enum Event
SEND_TO_GRAMMAR_SPELLING_ASSISTANT,
SEND_TO_ICON_FINDER_ASSISTANT,
SEND_TO_REWRITE_ASSISTANT,
+ SEND_TO_PROMPT_OPTIMIZER_ASSISTANT,
SEND_TO_TRANSLATION_ASSISTANT,
SEND_TO_AGENDA_ASSISTANT,
SEND_TO_CODING_ASSISTANT,