First working prototype for PromptOptimizer assistant

This commit is contained in:
Peer Schütt 2026-03-20 14:57:25 +01:00
parent dac0b74145
commit 6836fd25b5
15 changed files with 789 additions and 2 deletions

View File

@ -9,6 +9,13 @@
@this.Title
</MudText>
<MudSpacer/>
@if (this.HeaderActions is not null)
{
@this.HeaderActions
}
@if (this.HasSettingsPanel)
{
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettingsDialog())"/>
@ -72,6 +79,11 @@
}
}
@if (this.ShowResult && this.AfterResultContent is not null)
{
@this.AfterResultContent
}
<div id="@AFTER_RESULT_DIV_ID" class="mt-3">
</div>
</ChildContent>

View File

@ -81,6 +81,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
private protected virtual RenderFragment? HeaderActions => null;
private protected virtual RenderFragment? AfterResultContent => null;
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
protected virtual bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel);

View File

@ -0,0 +1,106 @@
@attribute [Route(Routes.ASSISTANT_PROMPT_OPTIMIZER)]
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.NoSettingsPanel>
<MudTextField T="string"
@bind-Text="@this.inputPrompt"
Validation="@this.ValidateInputPrompt"
AdornmentIcon="@Icons.Material.Filled.AutoFixHigh"
Adornment="Adornment.Start"
Label="@T("Prompt or prompt description")"
Variant="Variant.Outlined"
Lines="8"
AutoGrow="@true"
MaxLines="20"
Class="mb-3"
UserAttributes="@USER_INPUT_ATTRIBUTES"/>
@if (!this.useCustomPromptGuide)
{
<MudGrid Class="mb-3">
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recClarityDirectness" Label="@T("Be clear and direct")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recExamplesContext" Label="@T("Add examples and context")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recSequentialSteps" Label="@T("Use sequential steps")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recStructureMarkers" Label="@T("Structure with markers")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recRoleDefinition" Label="@T("Give the model a role")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
<MudItem xs="12" sm="6" md="4">
<MudTextField T="string" Value="@this.recLanguageChoice" Label="@T("Choose prompt language deliberately")" ReadOnly="true" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" />
</MudItem>
</MudGrid>
}
<MudStack Row="true" AlignItems="AlignItems.Center" Wrap="Wrap.Wrap" StretchItems="StretchItems.None" Class="mb-3">
<MudButton Variant="Variant.Outlined"
StartIcon="@Icons.Material.Filled.MenuBook"
OnClick="@(async () => await this.OpenPromptingGuidelineDialog())">
@T("View default prompt guide")
</MudButton>
<MudSwitch T="bool" Value="@this.useCustomPromptGuide" ValueChanged="@this.SetUseCustomPromptGuide" Color="Color.Primary" Class="mx-1">
@T("Use custom prompt guide")
</MudSwitch>
@if (this.useCustomPromptGuide)
{
<AttachDocuments Name="Custom Prompt Guide"
Layer="@DropLayers.ASSISTANTS"
@bind-DocumentPaths="@this.customPromptGuideFiles"
OnChange="@this.OnCustomPromptGuideFilesChanged"
CatchAllDocuments="false"
UseSmallForm="true"
ValidateMediaFileTypes="false"
Provider="@this.providerSettings"/>
}
<MudTextField T="string"
Text="@this.CustomPromptGuideFileName"
Label="@T("Custom guide file")"
ReadOnly="true"
Disabled="@(!this.useCustomPromptGuide)"
Variant="Variant.Outlined"
Class="mx-2"
Style="min-width: 18rem;"/>
<MudButton Variant="Variant.Outlined"
StartIcon="@Icons.Material.Filled.Visibility"
Disabled="@(!this.CanPreviewCustomPromptGuide)"
OnClick="@(async () => await this.OpenCustomPromptGuideDialog())">
@T("View")
</MudButton>
</MudStack>
<EnumSelection T="CommonLanguages"
NameFunc="@(language => language.NameSelectingOptional())"
@bind-Value="@this.selectedTargetLanguage"
Icon="@Icons.Material.Filled.Translate"
Label="@T("Language for the optimized prompt")"
AllowOther="@true"
OtherValue="CommonLanguages.OTHER"
@bind-OtherInput="@this.customTargetLanguage"
ValidateOther="@this.ValidateCustomLanguage"
LabelOther="@T("Custom language")"/>
<MudTextField T="string"
AutoGrow="true"
Lines="2"
@bind-Text="@this.importantAspects"
Class="mb-3"
Label="@T("(Optional) Important Aspects for the prompt")"
HelperText="@T("(Optional) Specify aspects the optimizer should emphasize in the resulting prompt, such as role precision, step ordering, output structure, or constraints.")"
ShrinkLabel="true"
Variant="Variant.Outlined"
AdornmentIcon="@Icons.Material.Filled.List"
Adornment="Adornment.Start"/>
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>

View File

@ -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<NoSettingsPanel>
{
private static readonly Regex JSON_CODE_FENCE_REGEX = new(
pattern: """```(?:json)?\s*(?<json>\{[\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<string> Result2Copy => () => this.optimizedPrompt;
protected override IReadOnlyList<IButtonData> 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<Task> 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<string>(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<FileAttachment> 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
<GUIDELINE>
{{promptingGuideline}}
</GUIDELINE>
# 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
<USER_PROMPT>
{{this.inputPrompt}}
</USER_PROMPT>
# 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:
<IMPORTANT_ASPECTS>
{this.importantAspects}
</IMPORTANT_ASPECTS>
""";
}
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<PromptOptimizationResult>(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<string> 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<string> 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<FileAttachment> 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<PromptingGuidelineDialog>
{
{ x => x.GuidelineMarkdown, promptingGuideline }
};
var dialogReference = await this.DialogService.ShowAsync<PromptingGuidelineDialog>(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<DocumentCheckDialog>
{
{ x => x.Document, fileAttachment },
{ x => x.FileContent, this.customPromptingGuidelineContent },
};
await this.DialogService.ShowAsync<DocumentCheckDialog>(T("Custom Prompt Guide Preview"), dialogParameters, AIStudio.Dialogs.DialogOptions.FULLSCREEN);
}
}

View File

@ -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;
}

View File

@ -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.

View File

@ -0,0 +1,26 @@
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("The full prompting guideline used by the Prompt Optimizer.")
</MudJustifiedText>
<MudField
Variant="Variant.Outlined"
AdornmentIcon="@Icons.Material.Filled.MenuBook"
Adornment="Adornment.Start"
Label="@T("Prompting Guideline")"
FullWidth="true"
Class="ma-2 pe-4">
<div style="max-height: 62vh; overflow-y: auto;">
<MudMarkdown Value="@this.GuidelineMarkdown" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
</div>
</MudField>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Close" Variant="Variant.Filled" Color="Color.Primary">
@T("Close")
</MudButton>
</DialogActions>
</MudDialog>

View File

@ -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 },
};
}

View File

@ -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 @@
<AssistantBlock TSettings="SettingsDialogTranslation" Component="Components.TRANSLATION_ASSISTANT" Name="@T("Translation")" Description="@T("Translate text into another language.")" Icon="@Icons.Material.Filled.Translate" Link="@Routes.ASSISTANT_TRANSLATION"/>
<AssistantBlock TSettings="SettingsDialogGrammarSpelling" Component="Components.GRAMMAR_SPELLING_ASSISTANT" Name="@T("Grammar & Spelling")" Description="@T("Check grammar and spelling of a given text.")" Icon="@Icons.Material.Filled.Edit" Link="@Routes.ASSISTANT_GRAMMAR_SPELLING"/>
<AssistantBlock TSettings="SettingsDialogRewrite" Component="Components.REWRITE_ASSISTANT" Name="@T("Rewrite & Improve")" Description="@T("Rewrite and improve a given text for a chosen style.")" Icon="@Icons.Material.Filled.Edit" Link="@Routes.ASSISTANT_REWRITE"/>
<AssistantBlock TSettings="NoSettingsPanel" Component="Components.PROMPT_OPTIMIZER_ASSISTANT" Name="@T("Prompt Optimizer")" Description="@T("Optimize a prompt using a guideline and receive targeted recommendations.")" Icon="@Icons.Material.Filled.AutoFixHigh" Link="@Routes.ASSISTANT_PROMPT_OPTIMIZER"/>
<AssistantBlock TSettings="SettingsDialogSynonyms" Component="Components.SYNONYMS_ASSISTANT" Name="@T("Synonyms")" Description="@T("Find synonyms for a given word or phrase.")" Icon="@Icons.Material.Filled.Spellcheck" Link="@Routes.ASSISTANT_SYNONYMS"/>
</MudStack>
}

View File

@ -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";

View File

@ -11,6 +11,7 @@ public enum ConfigurableAssistant
GRAMMAR_SPELLING_ASSISTANT,
ICON_FINDER_ASSISTANT,
REWRITE_ASSISTANT,
PROMPT_OPTIMIZER_ASSISTANT,
TRANSLATION_ASSISTANT,
AGENDA_ASSISTANT,
CODING_ASSISTANT,

View File

@ -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,

View File

@ -7,6 +7,7 @@ public enum Components
GRAMMAR_SPELLING_ASSISTANT,
ICON_FINDER_ASSISTANT,
REWRITE_ASSISTANT,
PROMPT_OPTIMIZER_ASSISTANT,
TRANSLATION_ASSISTANT,
AGENDA_ASSISTANT,
CODING_ASSISTANT,

View File

@ -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),

View File

@ -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,