diff --git a/README.md b/README.md index ea0ae57..ee538ee 100644 --- a/README.md +++ b/README.md @@ -1909,3 +1909,11 @@ MIT License - 테스트로 [AgentQueryContextBuilderTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/AgentQueryContextBuilderTests.cs)를 추가했고, [AgentToolResultBudgetTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/AgentToolResultBudgetTests.cs), [CodeLanguageCatalogTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/CodeLanguageCatalogTests.cs)를 확장해 preview 재사용과 fallback 힌트를 회귀 검증했습니다. - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_loop_lang_finish\\ -p:IntermediateOutputPath=obj\\verify_loop_lang_finish\\` 경고 0 / 오류 0 - 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentToolResultBudgetTests|AgentQueryContextBuilderTests|CodeLanguageCatalogTests|ContextCondenserTests" -p:OutputPath=bin\\verify_loop_lang_finish_tests\\ -p:IntermediateOutputPath=obj\\verify_loop_lang_finish_tests\\` 통과 20 + +업데이트: 2026-04-15 09:05 (KST) +- 문서 품질 보정 루프를 한 단계 더 마감했습니다. [DocxSkill.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DocxSkill.cs)는 DOCX 결과에도 `Repair guide:`를 함께 반환하도록 정리했고, Executive Summary/Recommendation/Appendix 판정에 한글 키워드도 함께 반영해 한국어 문서에서 품질 리뷰 누락이 덜 발생하도록 맞췄습니다. +- [ArtifactQualityReviewService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs)는 DOCX 장문 문서에 대해 `cover`, `table of contents`, `template`, `header/footer` 보강 포인트를, XLSX dashboard workbook에 대해 `highlight/action`, `detail navigation`, `trend/variance formula` 보강 포인트를 추가로 잡아냅니다. +- [ArtifactRepairGuideService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs)와 [DeckRepairGuideService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs)는 위 리뷰를 실제 수정 행동으로 바로 연결하는 문장을 더 풍부하게 반환합니다. 이제 문서/워크북/deck 출력에서 “무엇을 더 고치면 되는지”가 더 직접적으로 보입니다. +- 테스트로 [ArtifactQualityReviewServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs), [ArtifactRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs), [DeckRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs), [DocxSkillTemplateFeaturesTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs)를 확장했습니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_doc_repair_finalize\\ -p:IntermediateOutputPath=obj\\verify_doc_repair_finalize\\` 경고 0 / 오류 0 +- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ArtifactQualityReviewServiceTests|ArtifactRepairGuideServiceTests|DeckRepairGuideServiceTests|DocxSkillTemplateFeaturesTests" -p:OutputPath=bin\\verify_doc_repair_finalize_tests\\ -p:IntermediateOutputPath=obj\\verify_doc_repair_finalize_tests\\` 통과 11 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 1f70daf..9b4056b 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -979,3 +979,12 @@ UI ?붿옄???€洹쒕え 由ы뙥?좊쭅 ???꾪뿕 ?묒뾽 ??湲곕줉???덉쟾 - 테스트로 [AgentQueryContextBuilderTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/AgentQueryContextBuilderTests.cs)를 추가했고, [AgentToolResultBudgetTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/AgentToolResultBudgetTests.cs), [CodeLanguageCatalogTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/CodeLanguageCatalogTests.cs)를 확장해 preview 재사용과 fallback 힌트를 회귀 검증했습니다. - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_loop_lang_finish\\ -p:IntermediateOutputPath=obj\\verify_loop_lang_finish\\` 경고 0 / 오류 0 - 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentToolResultBudgetTests|AgentQueryContextBuilderTests|CodeLanguageCatalogTests|ContextCondenserTests" -p:OutputPath=bin\\verify_loop_lang_finish_tests\\ -p:IntermediateOutputPath=obj\\verify_loop_lang_finish_tests\\` 통과 20 + +업데이트: 2026-04-15 09:05 (KST) +- 문서 critic/repair 루프를 추가 정리했습니다. [ArtifactQualityReviewService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs)는 DOCX 장문 문서에서 `cover`, `table of contents`, `template`, `header/footer` 보강 포인트를, XLSX dashboard workbook에서는 `highlight/action`, `detail navigation`, `trend/variance formula` 보강 포인트를 새로 판정합니다. +- [ArtifactRepairGuideService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs)는 위 이슈들을 바로 실행 가능한 보정 가이드로 치환하도록 확장했습니다. HTML/DOCX/XLSX에서 품질 점수만 보여주는 것이 아니라, 어떤 구조를 추가하거나 어떤 데이터를 연결해야 하는지 직접 안내합니다. +- [DeckRepairGuideService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs)는 `appendix/evidence` 부족과 `duplicate headline` 문제를 별도 액션으로 바꿔 deck 마감 가이드를 더 구체화했습니다. +- [DocxSkill.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DocxSkill.cs)는 DOCX 출력 결과에도 `Repair guide:`를 함께 반환하고, Executive Summary/Recommendation/Appendix 섹션 인식에 한글 키워드를 같이 사용하도록 조정했습니다. +- 테스트는 [ArtifactQualityReviewServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs), [ArtifactRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs), [DeckRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs), [DocxSkillTemplateFeaturesTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs)를 확장해 회귀를 고정했습니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_doc_repair_finalize\\ -p:IntermediateOutputPath=obj\\verify_doc_repair_finalize\\` 경고 0 / 오류 0 +- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ArtifactQualityReviewServiceTests|ArtifactRepairGuideServiceTests|DeckRepairGuideServiceTests|DocxSkillTemplateFeaturesTests" -p:OutputPath=bin\\verify_doc_repair_finalize_tests\\ -p:IntermediateOutputPath=obj\\verify_doc_repair_finalize_tests\\` 통과 11 diff --git a/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs b/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs index 082d226..a3e6950 100644 --- a/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs +++ b/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs @@ -38,7 +38,7 @@ public class ArtifactQualityReviewServiceTests { var review = ArtifactQualityReviewService.ReviewStructuredDocument(new StructuredDocumentReviewInput( "Ops Memo", - 2, + 6, 600, 0, 0, @@ -55,6 +55,8 @@ public class ArtifactQualityReviewServiceTests review.Issues.Should().Contain(issue => issue.Message.Contains("Executive Summary", StringComparison.OrdinalIgnoreCase)); review.Issues.Should().Contain(issue => issue.Message.Contains("Recommendation", StringComparison.OrdinalIgnoreCase)); + review.Issues.Should().Contain(issue => issue.Message.Contains("table of contents", StringComparison.OrdinalIgnoreCase)); + review.Issues.Should().Contain(issue => issue.Message.Contains("Template-based styling", StringComparison.OrdinalIgnoreCase)); } [Fact] @@ -80,4 +82,29 @@ public class ArtifactQualityReviewServiceTests review.Issues.Should().Contain(issue => issue.Message.Contains("summary sheet", StringComparison.OrdinalIgnoreCase)); review.Score.Should().BeLessThan(80); } + + [Fact] + public void ReviewWorkbook_ShouldFlagWeakDashboardSupport_WhenDashboardLacksActionsAndFormulas() + { + var review = ArtifactQualityReviewService.ReviewWorkbook(new WorkbookReviewInput( + "Operating Review", + 4, + 3, + 32, + 1, + 0, + 1, + 1, + true, + true, + false, + false, + false, + false, + true)); + + review.Issues.Should().Contain(issue => issue.Message.Contains("highlights or actions", StringComparison.OrdinalIgnoreCase)); + review.Issues.Should().Contain(issue => issue.Message.Contains("supporting detail sheets", StringComparison.OrdinalIgnoreCase)); + review.Issues.Should().Contain(issue => issue.Message.Contains("trend or variance formulas", StringComparison.OrdinalIgnoreCase)); + } } diff --git a/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs b/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs index d92b7e5..0dea5c3 100644 --- a/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs +++ b/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs @@ -41,4 +41,24 @@ public class ArtifactRepairGuideServiceTests guide.Should().Contain("decision summary"); guide.Should().Contain("comparison or roadmap"); } + + [Fact] + public void BuildGuide_ForDocumentIssues_ShouldReturnTemplateAndNavigationActions() + { + var review = new ArtifactQualityReport( + "docx", + 66, + ["Includes cover page"], + [ + new ArtifactReviewIssue("Long document could benefit from a table of contents.", ArtifactReviewSeverity.Info), + new ArtifactReviewIssue("Template-based styling could improve consistency for a longer document.", ArtifactReviewSeverity.Info), + new ArtifactReviewIssue("Header or footer metadata is limited for a business document.", ArtifactReviewSeverity.Info) + ]); + + var guide = ArtifactRepairGuideService.BuildGuide(review); + + guide.Should().Contain("table of contents"); + guide.Should().Contain("template"); + guide.Should().Contain("header and footer"); + } } diff --git a/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs b/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs index ea3cf6d..3bee15b 100644 --- a/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs +++ b/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs @@ -35,4 +35,22 @@ public class DeckRepairGuideServiceTests guide.Should().Contain("Reduce text density"); guide.Should().Contain("evidence"); } + + [Fact] + public void BuildGuide_ShouldSurfaceAppendixAndHeadlineActions() + { + var report = new DeckQualityReport( + 58, + [], + [ + new DeckReviewIssue("Appendix or evidence slide is limited.", DeckReviewSeverity.Info), + new DeckReviewIssue("Found 2 duplicate headline(s).", DeckReviewSeverity.Warning) + ], + []); + + var guide = DeckRepairGuideService.BuildGuide(report); + + guide.Should().Contain("appendix"); + guide.Should().Contain("distinct message"); + } } diff --git a/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs b/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs index 880ec14..3b676e6 100644 --- a/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs +++ b/src/AxCopilot.Tests/Services/DocxSkillTemplateFeaturesTests.cs @@ -53,6 +53,7 @@ public class DocxSkillTemplateFeaturesTests var result = await tool.ExecuteAsync(args, context, CancellationToken.None); result.Success.Should().BeTrue(); + result.Output.Should().Contain("Repair guide:"); var outputPath = Path.Combine(workDir, "executive-brief.docx"); File.Exists(outputPath).Should().BeTrue(); diff --git a/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs b/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs index 5d088d8..51fa52a 100644 --- a/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs +++ b/src/AxCopilot/Services/Agent/ArtifactQualityReviewService.cs @@ -188,6 +188,14 @@ public static class ArtifactQualityReviewService issues.Add(new("Body content may be too short for an executive document.", ArtifactReviewSeverity.Warning)); if (input.SectionCount < 4) issues.Add(new("Major section count is low for a business document.", ArtifactReviewSeverity.Warning)); + if (input.SectionCount >= 5 && !input.HasCoverPage) + issues.Add(new("Client-facing document could benefit from a cover page.", ArtifactReviewSeverity.Info)); + if (input.SectionCount >= 5 && !input.HasTableOfContents) + issues.Add(new("Long document could benefit from a table of contents.", ArtifactReviewSeverity.Info)); + if (input.SectionCount >= 6 && !input.HasTemplate) + issues.Add(new("Template-based styling could improve consistency for a longer document.", ArtifactReviewSeverity.Info)); + if (input.SectionCount >= 4 && !input.HasHeaderFooter) + issues.Add(new("Header or footer metadata is limited for a business document.", ArtifactReviewSeverity.Info)); if (!input.HasExecutiveSummarySection) issues.Add(new("Executive Summary section is missing.", ArtifactReviewSeverity.Warning)); if (!input.HasRecommendationSection) @@ -232,6 +240,12 @@ public static class ArtifactQualityReviewService issues.Add(new("Summary sheet could better surface KPIs, decisions, or highlights.", ArtifactReviewSeverity.Info)); if (input.HasSummarySheet && input.DetailSheetCount >= 2 && !input.HasDashboardSheet) issues.Add(new("Workbook could benefit from a dashboard sheet to summarize multi-sheet trends.", ArtifactReviewSeverity.Info)); + if (input.HasDashboardSheet && !input.HasHighlightSection && !input.HasActionSection) + issues.Add(new("Dashboard sheet could better call out highlights or actions.", ArtifactReviewSeverity.Info)); + if (input.HasDashboardSheet && input.DetailSheetCount >= 2 && input.HyperlinkCount == 0) + issues.Add(new("Dashboard sheet should link to supporting detail sheets.", ArtifactReviewSeverity.Info)); + if (input.HasDashboardSheet && input.DetailSheetCount >= 2 && input.FormulaCount < 3) + issues.Add(new("Dashboard sheet would benefit from more calculated trend or variance formulas.", ArtifactReviewSeverity.Info)); return BuildReport("xlsx", strengths, issues); } diff --git a/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs b/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs index 484fa0f..85f248f 100644 --- a/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs +++ b/src/AxCopilot/Services/Agent/ArtifactRepairGuideService.cs @@ -38,6 +38,8 @@ public static class ArtifactRepairGuideService return "Add a decision summary and evidence cards near the recommendation section"; if (message.Contains("cover page", StringComparison.OrdinalIgnoreCase)) return "Add a cover page for print-ready or board-facing reports"; + if (message.Contains("table of contents", StringComparison.OrdinalIgnoreCase)) + return "Add a table of contents for longer print-ready reports"; if (message.Contains("comparison or roadmap", StringComparison.OrdinalIgnoreCase)) return "Add comparison or roadmap blocks to make options and sequencing explicit"; if (message.Contains("supporting paragraphs", StringComparison.OrdinalIgnoreCase)) @@ -55,6 +57,12 @@ public static class ArtifactRepairGuideService return "Add a dashboard sheet with KPI tiles, trends, and links to detail sheets"; if (message.Contains("Summary sheet could better surface", StringComparison.OrdinalIgnoreCase)) return "Promote KPIs, decisions, and highlights into the summary or dashboard sheet"; + if (message.Contains("Dashboard sheet could better call out highlights or actions", StringComparison.OrdinalIgnoreCase)) + return "Add highlight and action callout blocks to the dashboard sheet"; + if (message.Contains("Dashboard sheet should link", StringComparison.OrdinalIgnoreCase)) + return "Add hyperlinks from the dashboard to each supporting detail sheet"; + if (message.Contains("trend or variance formulas", StringComparison.OrdinalIgnoreCase)) + return "Add trend, variance, or rollup formulas so the dashboard summarizes detail sheets"; if (message.Contains("data validation", StringComparison.OrdinalIgnoreCase)) return "Add data validation rules for editable status, owner, or category columns"; if (message.Contains("Conditional formatting", StringComparison.OrdinalIgnoreCase)) @@ -68,6 +76,14 @@ public static class ArtifactRepairGuideService private static string? BuildDocumentAction(string message) { + if (message.Contains("cover page", StringComparison.OrdinalIgnoreCase)) + return "Add a cover page with subtitle, owner, and review context for the document"; + if (message.Contains("table of contents", StringComparison.OrdinalIgnoreCase)) + return "Add a table of contents so longer readers can navigate the document quickly"; + if (message.Contains("Template-based styling", StringComparison.OrdinalIgnoreCase)) + return "Apply a document template or style map to keep headings and body copy consistent"; + if (message.Contains("Header or footer", StringComparison.OrdinalIgnoreCase)) + return "Add header and footer metadata such as document name, confidentiality, or page numbers"; if (message.Contains("Executive Summary", StringComparison.OrdinalIgnoreCase)) return "Add an Executive Summary section at the start of the document"; if (message.Contains("Recommendation", StringComparison.OrdinalIgnoreCase)) diff --git a/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs b/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs index b26e571..340919c 100644 --- a/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs +++ b/src/AxCopilot/Services/Agent/DeckRepairGuideService.cs @@ -18,6 +18,10 @@ public static class DeckRepairGuideService => "Add a clear recommendation or decision request slide near the end of the deck", var message when message.Contains("roadmap", StringComparison.OrdinalIgnoreCase) => "Add a roadmap slide with phases, owners, and timing", + var message when message.Contains("Appendix or evidence slide", StringComparison.OrdinalIgnoreCase) + => "Add appendix or evidence slides with supporting data, assumptions, or source tables", + var message when message.Contains("duplicate headline", StringComparison.OrdinalIgnoreCase) + => "Rewrite repeated headlines so each slide lands a distinct message", var message when message.Contains("headline is too long", StringComparison.OrdinalIgnoreCase) => "Tighten slide headlines to one clear message sentence", var message when message.Contains("content density is high", StringComparison.OrdinalIgnoreCase) diff --git a/src/AxCopilot/Services/Agent/DocxSkill.cs b/src/AxCopilot/Services/Agent/DocxSkill.cs index 823efd6..f869139 100644 --- a/src/AxCopilot/Services/Agent/DocxSkill.cs +++ b/src/AxCopilot/Services/Agent/DocxSkill.cs @@ -333,12 +333,13 @@ public class DocxSkill : IAgentTool useToc, templateApplied, headerText != null || footerText != null || showPageNumbers, - headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "executive summary", "summary")), - headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "recommendation", "proposal", "next step", "action")), - headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "appendix", "reference", "supplement")) + headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "executive summary", "summary", "요약", "핵심 요약")), + headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "recommendation", "proposal", "next step", "action", "권고", "제안", "실행")), + headings.Any(h => ArtifactQualityReviewService.ContainsBusinessKeyword(h, "appendix", "reference", "supplement", "부록", "참고")) )); parts.Add(review.ToToolSummary()); + parts.Add(ArtifactRepairGuideService.BuildGuide(review)); return ToolResult.Ok( $"Word 문서 생성 완료: {fullPath}\n{string.Join(", ", parts)}", fullPath);