mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-29 11:51:37 +00:00
First working prototype for PromptOptimizer assistant
This commit is contained in:
parent
dac0b74145
commit
6836fd25b5
@ -8,6 +8,13 @@
|
||||
<MudText Typo="Typo.h3">
|
||||
@this.Title
|
||||
</MudText>
|
||||
|
||||
<MudSpacer/>
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
<div id="@AFTER_RESULT_DIV_ID" class="mt-3">
|
||||
</div>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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>
|
||||
@ -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 },
|
||||
};
|
||||
}
|
||||
@ -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>
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -11,6 +11,7 @@ public enum ConfigurableAssistant
|
||||
GRAMMAR_SPELLING_ASSISTANT,
|
||||
ICON_FINDER_ASSISTANT,
|
||||
REWRITE_ASSISTANT,
|
||||
PROMPT_OPTIMIZER_ASSISTANT,
|
||||
TRANSLATION_ASSISTANT,
|
||||
AGENDA_ASSISTANT,
|
||||
CODING_ASSISTANT,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user