2025-02-17 16:51:26 +01:00
using AIStudio.Agents ;
using AIStudio.Chat ;
using AIStudio.Components ;
using AIStudio.Provider ;
using AIStudio.Settings ;
2025-05-04 14:59:30 +02:00
using AIStudio.Tools.PluginSystem ;
2025-02-17 16:51:26 +01:00
namespace AIStudio.Tools.RAG.DataSourceSelectionProcesses ;
public class AgenticSrcSelWithDynHeur : IDataSourceSelectionProcess
{
2025-09-25 19:47:18 +02:00
private static readonly ILogger < AgenticSrcSelWithDynHeur > LOGGER = Program . LOGGER_FACTORY . CreateLogger < AgenticSrcSelWithDynHeur >();
2025-05-04 14:59:30 +02:00
private static string TB ( string fallbackEN ) => I18N . I . T ( fallbackEN , typeof ( AgenticSrcSelWithDynHeur ). Namespace , nameof ( AgenticSrcSelWithDynHeur ));
2025-02-17 16:51:26 +01:00
#region Implementation of IDataSourceSelectionProcess
/// <inheritdoc />
public string TechnicalName => "AgenticSrcSelWithDynHeur" ;
/// <inheritdoc />
2025-05-04 14:59:30 +02:00
public string UIName => TB ( "Automatic AI data source selection with heuristik source reduction" );
2025-02-17 16:51:26 +01:00
/// <inheritdoc />
2025-05-04 14:59:30 +02:00
public string Description => TB ( "Automatically selects the appropriate data sources based on the last prompt. Applies a heuristic reduction at the end to reduce the number of data sources." );
2025-02-17 16:51:26 +01:00
/// <inheritdoc />
2025-09-25 19:47:18 +02:00
public async Task < DataSelectionResult > SelectDataSourcesAsync ( IProvider provider , IContent lastUserPrompt , ChatThread chatThread , AllowedSelectedDataSources dataSources , CancellationToken token = default )
2025-02-17 16:51:26 +01:00
{
var proceedWithRAG = true ;
IReadOnlyList < IDataSource > selectedDataSources = [];
IReadOnlyList < DataSourceAgentSelected > finalAISelection = [];
// Get the agent for the data source selection:
var selectionAgent = Program . SERVICE_PROVIDER . GetService < AgentDataSourceSelection >()!;
2026-09-09 18:43:37 +02:00
var allowedDataSources = dataSources . AllowedDataSources . ToDictionary ( ds => ds . Id , StringComparer . Ordinal );
2025-02-17 16:51:26 +01:00
try
{
// Let the AI agent do its work:
2025-09-25 19:47:18 +02:00
var aiSelectedDataSources = await selectionAgent . PerformSelectionAsync ( provider , lastUserPrompt , chatThread , dataSources , token );
2025-02-17 16:51:26 +01:00
// Check if the AI selected any data sources:
if ( aiSelectedDataSources . Count is 0 )
{
2025-09-25 19:47:18 +02:00
LOGGER . LogWarning ( "The AI did not select any data sources. The RAG process is skipped." );
2025-02-17 16:51:26 +01:00
proceedWithRAG = false ;
return new ( proceedWithRAG , selectedDataSources );
}
// Log the selected data sources:
var selectedDataSourceInfo = aiSelectedDataSources . Select ( ds => $"[Id={ds.Id}, reason={ds.Reason}, confidence={ds.Confidence}]" ). Aggregate (( a , b ) => $"'{a}', '{b}'" );
2025-09-25 19:47:18 +02:00
LOGGER . LogInformation ( $"The AI selected the data sources automatically. {aiSelectedDataSources.Count} data source(s) are selected: {selectedDataSourceInfo}." );
2025-02-17 16:51:26 +01:00
//
// Check how many data sources were hallucinated by the AI:
//
var totalAISelectedDataSources = aiSelectedDataSources . Count ;
// Filter out the data sources that are not available:
2026-09-09 18:43:37 +02:00
aiSelectedDataSources = aiSelectedDataSources . Where ( x => allowedDataSources . ContainsKey ( x . Id )). ToList ();
2025-02-17 16:51:26 +01:00
// Store the real AI-selected data sources:
2026-09-09 18:43:37 +02:00
finalAISelection = aiSelectedDataSources . Select ( x => new DataSourceAgentSelected { DataSource = allowedDataSources [ x . Id ], AIDecision = x , Selected = false }). ToList ();
2025-02-17 16:51:26 +01:00
var numHallucinatedSources = totalAISelectedDataSources - aiSelectedDataSources . Count ;
if ( numHallucinatedSources > 0 )
2026-09-09 18:43:37 +02:00
LOGGER . LogWarning ( $"The AI selected {numHallucinatedSources} unavailable data source(s). We ignore them." );
2025-02-17 16:51:26 +01:00
if ( aiSelectedDataSources . Count > 3 )
{
2025-02-22 20:51:06 +01:00
// We have more than 3 data sources. Let's filter by confidence:
var targetWindow = aiSelectedDataSources . DetermineTargetWindow ( TargetWindowStrategy . A_FEW_GOOD_ONES );
var threshold = aiSelectedDataSources . GetConfidenceThreshold ( targetWindow );
2025-02-17 16:51:26 +01:00
//
// Filter the data sources by the threshold:
//
aiSelectedDataSources = aiSelectedDataSources . Where ( x => x . Confidence >= threshold ). ToList ();
foreach ( var dataSource in finalAISelection )
if ( aiSelectedDataSources . Any ( x => x . Id == dataSource . DataSource . Id ))
dataSource . Selected = true ;
2025-09-25 19:47:18 +02:00
LOGGER . LogInformation ( $"The AI selected {aiSelectedDataSources.Count} data source(s) with a confidence of at least {threshold}." );
2025-02-17 16:51:26 +01:00
// Transform the final data sources to the actual data sources:
2026-09-09 18:43:37 +02:00
selectedDataSources = aiSelectedDataSources . Select ( x => allowedDataSources [ x . Id ]). ToList ();
2025-02-17 16:51:26 +01:00
return new ( proceedWithRAG , selectedDataSources );
}
//
// Case: we have max. 3 data sources. We take all of them:
//
// Transform the selected data sources to the actual data sources:
2026-09-09 18:43:37 +02:00
selectedDataSources = aiSelectedDataSources . Select ( x => allowedDataSources [ x . Id ]). ToList ();
2025-02-17 16:51:26 +01:00
// Mark the data sources as selected:
foreach ( var dataSource in finalAISelection )
dataSource . Selected = true ;
return new ( proceedWithRAG , selectedDataSources );
}
finally
{
// Send the selected data sources to the data source selection component.
// Then, the user can see which data sources were selected by the AI.
await MessageBus . INSTANCE . SendMessage ( null , Event . RAG_AUTO_DATA_SOURCES_SELECTED , finalAISelection );
chatThread . AISelectedDataSources = finalAISelection ;
}
}
#endregion
}