mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 11:33:47 +00:00
Added a prompt injection detection (#857)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
d3163badd9
commit
902a01a4d0
55 files changed
+5598
-70
No files matched your search
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Tools.Security;
|
||||
|
||||
namespace AIStudio.Tools.RAG;
|
||||
|
||||
@@ -13,86 +14,117 @@ public static class IRetrievalContextExtensions
|
||||
sb ??= new StringBuilder();
|
||||
var index = 0;
|
||||
|
||||
//
|
||||
// One report for the whole retrieval run: a query may pull in dozens of contexts, and
|
||||
// the user wants to know that something was filtered, not to acknowledge it per context.
|
||||
//
|
||||
var guardService = Program.SERVICE_PROVIDER.GetRequiredService<PromptInjectionGuardService>();
|
||||
await using var reportingScope = guardService.BeginAction();
|
||||
|
||||
foreach(var retrievalContext in retrievalContexts)
|
||||
{
|
||||
index++;
|
||||
await retrievalContext.AsMarkdown(sb, index, retrievalContexts.Count, token);
|
||||
}
|
||||
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static async Task<string> AsMarkdown(this IRetrievalContext retrievalContext, StringBuilder? sb = null, int index = -1, int numTotalRetrievalContexts = -1, CancellationToken token = default)
|
||||
{
|
||||
sb ??= new StringBuilder();
|
||||
var contextBuilder = new StringBuilder();
|
||||
switch (index)
|
||||
{
|
||||
case > 0 when numTotalRetrievalContexts is -1:
|
||||
sb.AppendLine($"# Retrieval context {index}");
|
||||
contextBuilder.AppendLine($"# Retrieval context {index}");
|
||||
break;
|
||||
|
||||
case > 0 when numTotalRetrievalContexts > 0:
|
||||
sb.AppendLine($"# Retrieval context {index} of {numTotalRetrievalContexts}");
|
||||
contextBuilder.AppendLine($"# Retrieval context {index} of {numTotalRetrievalContexts}");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.AppendLine("# Retrieval context");
|
||||
contextBuilder.AppendLine("# Retrieval context");
|
||||
break;
|
||||
}
|
||||
|
||||
sb.AppendLine($"Data source name: {retrievalContext.DataSourceName}");
|
||||
sb.AppendLine($"Content category: {retrievalContext.Category}");
|
||||
sb.AppendLine($"Content type: {retrievalContext.Type}");
|
||||
sb.AppendLine($"Content path: {retrievalContext.Path}");
|
||||
contextBuilder.AppendLine($"Data source name: {retrievalContext.DataSourceName}");
|
||||
contextBuilder.AppendLine($"Content category: {retrievalContext.Category}");
|
||||
contextBuilder.AppendLine($"Content type: {retrievalContext.Type}");
|
||||
contextBuilder.AppendLine($"Content path: {retrievalContext.Path}");
|
||||
|
||||
if(retrievalContext.Links.Count > 0)
|
||||
{
|
||||
sb.AppendLine("Additional links:");
|
||||
contextBuilder.AppendLine("Additional links:");
|
||||
foreach(var link in retrievalContext.Links)
|
||||
sb.AppendLine($"- {link}");
|
||||
contextBuilder.AppendLine($"- {link}");
|
||||
}
|
||||
|
||||
|
||||
var guardService = Program.SERVICE_PROVIDER.GetRequiredService<PromptInjectionGuardService>();
|
||||
var source = PromptInjectionSource.RetrievalContext(retrievalContext.DataSourceName, retrievalContext.Path);
|
||||
|
||||
switch(retrievalContext)
|
||||
{
|
||||
case RetrievalTextContext textContext:
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Matched text content:");
|
||||
sb.AppendLine("````");
|
||||
sb.AppendLine(textContext.MatchedText);
|
||||
sb.AppendLine("````");
|
||||
|
||||
contextBuilder.AppendLine();
|
||||
contextBuilder.AppendLine("Matched text content:");
|
||||
contextBuilder.AppendLine("````");
|
||||
contextBuilder.AppendLine(textContext.MatchedText);
|
||||
contextBuilder.AppendLine("````");
|
||||
|
||||
if(textContext.SurroundingContent.Count > 0)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Surrounding text content:");
|
||||
contextBuilder.AppendLine();
|
||||
contextBuilder.AppendLine("Surrounding text content:");
|
||||
foreach(var surrounding in textContext.SurroundingContent)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("````");
|
||||
sb.AppendLine(surrounding);
|
||||
sb.AppendLine("````");
|
||||
contextBuilder.AppendLine();
|
||||
contextBuilder.AppendLine("````");
|
||||
contextBuilder.AppendLine(surrounding);
|
||||
contextBuilder.AppendLine("````");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
await FilterWhatWeHaveSoFar();
|
||||
break;
|
||||
|
||||
|
||||
case RetrievalImageContext imageContext:
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Matched image content as base64-encoded data:");
|
||||
sb.AppendLine("````");
|
||||
sb.AppendLine(await imageContext.TryAsBase64(token) is (success: true, { } base64Image)
|
||||
? base64Image
|
||||
//
|
||||
// Filtering happens before the image is appended, and only covers the text
|
||||
// around it. Base64 image data is not prose, and running it through the filter
|
||||
// would have it treated as one enormous encoded carrier.
|
||||
//
|
||||
await FilterWhatWeHaveSoFar();
|
||||
contextBuilder.AppendLine();
|
||||
contextBuilder.AppendLine("Matched image content as base64-encoded data:");
|
||||
contextBuilder.AppendLine("````");
|
||||
contextBuilder.AppendLine(await imageContext.TryAsBase64(token) is (success: true, { } base64Image)
|
||||
? base64Image
|
||||
: string.Empty);
|
||||
sb.AppendLine("````");
|
||||
contextBuilder.AppendLine("````");
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
await FilterWhatWeHaveSoFar();
|
||||
LOGGER.LogWarning($"The retrieval content type '{retrievalContext.Type}' of data source '{retrievalContext.DataSourceName}' at location '{retrievalContext.Path}' is not supported yet.");
|
||||
break;
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
contextBuilder.AppendLine();
|
||||
sb.Append(contextBuilder);
|
||||
return sb.ToString();
|
||||
|
||||
//
|
||||
// Replaces what has been built so far with its filtered version. A data source is as
|
||||
// untrusted as any other external content: it may serve text written to steer the model
|
||||
// rather than to answer the query.
|
||||
//
|
||||
async Task FilterWhatWeHaveSoFar()
|
||||
{
|
||||
var sanitized = await guardService.SanitizeAsync(contextBuilder.ToString(), source);
|
||||
contextBuilder.Clear();
|
||||
contextBuilder.Append(sanitized);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user