Add grounded unified search answers and live verification

This commit is contained in:
master
2026-03-07 03:55:51 +02:00
parent 2ff0e1f86b
commit edb947d602
19 changed files with 1180 additions and 32 deletions

View File

@@ -379,6 +379,18 @@ public static class UnifiedSearchEndpoints
CurrentRoute = string.IsNullOrWhiteSpace(ambient.CurrentRoute) ? null : ambient.CurrentRoute.Trim(),
SessionId = string.IsNullOrWhiteSpace(ambient.SessionId) ? null : ambient.SessionId.Trim(),
ResetSession = ambient.ResetSession,
LastAction = ambient.LastAction is null || string.IsNullOrWhiteSpace(ambient.LastAction.Action)
? null
: new AmbientAction
{
Action = ambient.LastAction.Action.Trim(),
Source = string.IsNullOrWhiteSpace(ambient.LastAction.Source) ? null : ambient.LastAction.Source.Trim(),
QueryHint = string.IsNullOrWhiteSpace(ambient.LastAction.QueryHint) ? null : ambient.LastAction.QueryHint.Trim(),
Domain = string.IsNullOrWhiteSpace(ambient.LastAction.Domain) ? null : ambient.LastAction.Domain.Trim().ToLowerInvariant(),
EntityKey = string.IsNullOrWhiteSpace(ambient.LastAction.EntityKey) ? null : ambient.LastAction.EntityKey.Trim(),
Route = string.IsNullOrWhiteSpace(ambient.LastAction.Route) ? null : ambient.LastAction.Route.Trim(),
OccurredAt = ambient.LastAction.OccurredAt
},
VisibleEntityKeys = ambient.VisibleEntityKeys is { Count: > 0 }
? ambient.VisibleEntityKeys
.Where(static value => !string.IsNullOrWhiteSpace(value))
@@ -462,6 +474,31 @@ public static class UnifiedSearchEndpoints
}).ToArray();
}
UnifiedSearchApiContextAnswer? contextAnswer = null;
if (response.ContextAnswer is not null)
{
contextAnswer = new UnifiedSearchApiContextAnswer
{
Status = response.ContextAnswer.Status,
Code = response.ContextAnswer.Code,
Summary = response.ContextAnswer.Summary,
Reason = response.ContextAnswer.Reason,
Evidence = response.ContextAnswer.Evidence,
Citations = response.ContextAnswer.Citations?.Select(static citation => new UnifiedSearchApiContextAnswerCitation
{
EntityKey = citation.EntityKey,
Title = citation.Title,
Domain = citation.Domain,
Route = citation.Route
}).ToArray(),
Questions = response.ContextAnswer.Questions?.Select(static question => new UnifiedSearchApiContextAnswerQuestion
{
Query = question.Query,
Kind = question.Kind
}).ToArray()
};
}
return new UnifiedSearchApiResponse
{
Query = response.Query,
@@ -470,6 +507,7 @@ public static class UnifiedSearchEndpoints
Synthesis = synthesis,
Suggestions = suggestions,
Refinements = refinements,
ContextAnswer = contextAnswer,
Diagnostics = new UnifiedSearchApiDiagnostics
{
FtsMatches = response.Diagnostics.FtsMatches,
@@ -696,6 +734,25 @@ public sealed record UnifiedSearchApiAmbientContext
public string? SessionId { get; init; }
public bool ResetSession { get; init; }
public UnifiedSearchApiAmbientAction? LastAction { get; init; }
}
public sealed record UnifiedSearchApiAmbientAction
{
public string Action { get; init; } = string.Empty;
public string? Source { get; init; }
public string? QueryHint { get; init; }
public string? Domain { get; init; }
public string? EntityKey { get; init; }
public string? Route { get; init; }
public DateTimeOffset? OccurredAt { get; init; }
}
public sealed record UnifiedSearchApiFilter
@@ -751,6 +808,8 @@ public sealed record UnifiedSearchApiResponse
public IReadOnlyList<UnifiedSearchApiRefinement>? Refinements { get; init; }
public UnifiedSearchApiContextAnswer? ContextAnswer { get; init; }
public UnifiedSearchApiDiagnostics Diagnostics { get; init; } = new();
public IReadOnlyList<UnifiedSearchApiFederationDiagnostic>? Federation { get; init; }
@@ -839,6 +898,41 @@ public sealed record UnifiedSearchApiRefinement
public string Source { get; init; } = string.Empty;
}
public sealed record UnifiedSearchApiContextAnswer
{
public string Status { get; init; } = string.Empty;
public string Code { get; init; } = string.Empty;
public string Summary { get; init; } = string.Empty;
public string Reason { get; init; } = string.Empty;
public string Evidence { get; init; } = string.Empty;
public IReadOnlyList<UnifiedSearchApiContextAnswerCitation>? Citations { get; init; }
public IReadOnlyList<UnifiedSearchApiContextAnswerQuestion>? Questions { get; init; }
}
public sealed record UnifiedSearchApiContextAnswerCitation
{
public string EntityKey { get; init; } = string.Empty;
public string Title { get; init; } = string.Empty;
public string Domain { get; init; } = string.Empty;
public string? Route { get; init; }
}
public sealed record UnifiedSearchApiContextAnswerQuestion
{
public string Query { get; init; } = string.Empty;
public string Kind { get; init; } = "follow_up";
}
public sealed record UnifiedSearchApiDiagnostics
{
public int FtsMatches { get; init; }

View File

@@ -16,3 +16,5 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| SPRINT_20260224_003-LOC-202 | DONE | `SPRINT_20260224_003_AdvisoryAI_translation_rollout_remaining_phases.md`: phase-3.4 AdvisoryAI slice completed (remote bundle wiring, localized validation keys in search/unified-search endpoints, `en-US`+`de-DE` service bundles, and de-DE integration coverage). |
| SPRINT_20260224_G1-G10 | DONE | Search improvement sprints G1G10 implemented. New endpoints: `SearchAnalyticsEndpoints.cs` (history, events, popularity), `SearchFeedbackEndpoints.cs` (feedback, quality alerts, metrics). Extended: `UnifiedSearchEndpoints.cs` (suggestions, refinements, previews, diagnostics.activeEncoder). Extended: `KnowledgeSearchEndpoints.cs` (activeEncoder in diagnostics). See `docs/modules/advisory-ai/knowledge-search.md` for full testing guide. |
| AI-SELF-001 | DONE | Unified search endpoint contract now exposes backend contextual answer fields for self-serve search. |
| AI-SELF-006 | DONE | Endpoint readiness now includes a proven local rebuilt-corpus verification lane in addition to stubbed integration tests. |