Add LLM review request candidates

This commit is contained in:
2026-06-09 14:34:57 +09:00
parent b8c7f9efdb
commit c4891823ca
6 changed files with 146 additions and 3 deletions

View File

@@ -29,4 +29,26 @@ namespace TornadoAce_CJOnStyle.Domain
{
}
}
public sealed record LlmReviewField(
string CueSheetField,
string SceneObjectName,
string ExpectedText,
string ActualText,
double MatchRate,
string Severity);
public sealed record LlmReviewRequest(
string RequestId,
string CueSheetId,
string ProgramTitle,
string SceneName,
string PromptKey,
string Policy,
string RecommendedPriority,
IReadOnlyList<LlmReviewField> Fields)
{
public string AuditSummary =>
$"{RequestId} / {CueSheetId} / {RecommendedPriority} / {Fields.Count} field(s)";
}
}

View File

@@ -93,6 +93,42 @@ namespace TornadoAce_CJOnStyle.Services
return new PocInspectionSummary(results, passedCount, reviewCount, averageMatchRate);
}
public IReadOnlyList<LlmReviewRequest> BuildLlmReviewRequests(IEnumerable<InspectionResult> inspectionResults)
{
return inspectionResults
.Select(result =>
{
var failedFields = result.FieldResults
.Where(field => field.MatchRate < 99.5)
.Select(field => new LlmReviewField(
field.CueSheetField,
field.SceneObjectName,
field.ExpectedText,
field.ActualText,
field.MatchRate,
field.Severity))
.ToArray();
if (failedFields.Length == 0)
{
return null;
}
var priority = failedFields.Any(field => field.Severity == "High") ? "High" : "Medium";
return new LlmReviewRequest(
$"LLM-{result.CueSheetId[^3..]}",
result.CueSheetId,
result.ProgramTitle,
result.SceneName,
"cj-onstyle-cg-inspection-v1",
"사내 LLM 우선, 외부 LLM은 정책 승인 후 사용",
priority,
failedFields);
})
.OfType<LlmReviewRequest>()
.ToArray();
}
private InspectionResult Inspect(CueSheetRecord cueSheet, SceneDataSnapshot snapshot, SceneTemplateDefinition template)
{
var expectedObjects = BuildVisibleTextObjects(cueSheet, template);

View File

@@ -50,6 +50,7 @@ namespace TornadoAce_CJOnStyle.ViewModels
LoadScheduleMatches();
LoadInspectionQueue(initialInspectionSummary.Results);
LoadInspectionDetails(initialInspectionSummary.Results);
LoadLlmReviewRequests(initialInspectionSummary.Results);
}
private string buildPhase = string.Empty;
@@ -139,6 +140,8 @@ namespace TornadoAce_CJOnStyle.ViewModels
public ObservableCollection<InspectionDetailItemViewModel> InspectionDetails { get; } = [];
public ObservableCollection<LlmReviewRequestViewModel> LlmReviewRequests { get; } = [];
public string LoginOperatorName
{
get => loginOperatorName;
@@ -222,6 +225,7 @@ namespace TornadoAce_CJOnStyle.ViewModels
StatusCards[2].Detail = $"확인 {summary.ReviewCount}건";
LoadInspectionQueue(summary.Results);
LoadInspectionDetails(summary.Results);
LoadLlmReviewRequests(summary.Results);
}
private void LoadStatusCards()
@@ -342,6 +346,23 @@ namespace TornadoAce_CJOnStyle.ViewModels
}
}
private void LoadLlmReviewRequests(IEnumerable<InspectionResult> results)
{
LlmReviewRequests.Clear();
foreach (var request in automationEngine.BuildLlmReviewRequests(results))
{
LlmReviewRequests.Add(new LlmReviewRequestViewModel(
request.RequestId,
request.ProgramTitle,
request.SceneName,
request.RecommendedPriority,
request.AuditSummary,
request.PromptKey,
SeverityBrush(request.RecommendedPriority)));
}
}
private void ApplySceneSnapshot(SceneDataSnapshot scene)
{
SceneSnapshotLabel = $"{scene.SceneName} / {scene.VisibleTextObjects.Count} text objects";

View File

@@ -99,4 +99,13 @@ namespace TornadoAce_CJOnStyle.ViewModels
string Severity,
string Decision,
SolidColorBrush SeverityBrush);
public sealed record LlmReviewRequestViewModel(
string RequestId,
string ProgramTitle,
string SceneName,
string Priority,
string PayloadSummary,
string PromptKey,
SolidColorBrush PriorityBrush);
}

View File

@@ -664,6 +664,62 @@
Text="{x:Bind ViewModel.LlmPolicyDetail, Mode=OneWay}" />
</StackPanel>
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="LLM 후보 요청" />
<ItemsControl ItemsSource="{x:Bind ViewModel.LlmReviewRequests, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:LlmReviewRequestViewModel">
<Border
Margin="0,0,0,10"
Padding="10"
Background="{StaticResource PanelAltBrush}"
CornerRadius="8">
<StackPanel Spacing="6">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Spacing="2">
<TextBlock
FontFamily="Cascadia Mono"
FontSize="12"
Foreground="{StaticResource TextMutedBrush}"
Text="{x:Bind RequestId}" />
<TextBlock
FontFamily="Bahnschrift SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"
Text="{x:Bind ProgramTitle}" />
</StackPanel>
<Border
Grid.Column="1"
Padding="8,3"
Background="{x:Bind PriorityBrush}"
CornerRadius="8">
<TextBlock
FontFamily="Bahnschrift SemiBold"
FontSize="12"
Foreground="#071013"
Text="{x:Bind Priority}" />
</Border>
</Grid>
<TextBlock
Style="{StaticResource MonoTextStyle}"
Text="{x:Bind SceneName}"
TextTrimming="CharacterEllipsis" />
<TextBlock
Style="{StaticResource LabelTextStyle}"
Text="{x:Bind PromptKey}"
TextTrimming="CharacterEllipsis" />
<TextBlock
Style="{StaticResource BodyTextStyle}"
Text="{x:Bind PayloadSummary}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Border
Height="1"
Background="{StaticResource PanelStrokeBrush}" />

View File

@@ -19,7 +19,7 @@
| RFP-04 | Tornado Scene API | Scene visible Text/Image 스냅샷 샘플 구현 | `SceneDataSnapshot.ToPreviewJson` |
| RFP-05 | 큐시트 데이터와 Tornado Scene 연동 관리자 기능 | JSON 매핑 프로파일과 검증 UI 구현 | `FieldMappings`, `MappingValidations`, `ValidateMappingProfile` |
| RFP-06 | 자동 검수 1단계 Text Diffing | 필드별 기대값/실제값/일치율 비교 구현 | `PocAutomationEngine.InspectWorkspace`, `InspectionFieldResult` |
| RFP-07 | 자동 검수 2단계 LLM 중요도 피드백 | LLM 검토 후보 분류와 제작자 상세 UI 구현 | 검수 결과 `Severity`, `Decision`, `InspectionDetails` |
| RFP-07 | 자동 검수 2단계 LLM 중요도 피드백 | LLM 요청 후보 payload와 제작자 상세 UI 구현 | `LlmReviewRequest`, `BuildLlmReviewRequests`, `LlmReviewRequests` |
| RFP-08 | 제작자가 결과를 볼 수 있는 GUI | WinUI 3 운영 콘솔 초안 구현 | `MainPage.xaml` |
## 초기 PoC 완료 기준
@@ -36,5 +36,4 @@
- 관리자용 매핑 프로파일 편집/저장 권한 정책을 정한다.
- Tornado2/Tornado3 어댑터를 동일 인터페이스로 유지하고, 버전별 구현체를 분리한다.
- Scene 내부 비노출 객체를 구분할 메타데이터 규칙을 정의한다.
- 1차 검수 실패 사유를 필드 단위로 누적하고 LLM 요청 후보로 전달한다.
- LLM 요청/응답 스키마와 감사 로그 보관 정책을 별도 모델로 둔다.
- LLM 응답 스키마와 감사 로그 보관 정책을 실제 사내 정책에 맞춘다.