PPT 생성 고도화 3차를 반영하고 deck planning·quality gate를 추가
- DeckPlanningService와 DeckQualityReviewService를 추가해 deck brief 정규화, consulting storyline 보강, 누락된 Executive Summary/Recommendation/Roadmap/Appendix 자동 보강, deck-level 품질 점수와 경고 계산을 지원합니다. - PptxSkill에 audience/objective/decision_ask/storyline 파라미터를 추가하고, issue_tree/before_after/decision_matrix/risk_heatmap/benefit_waterfall/operating_model/appendix_evidence 레이아웃을 네이티브 슬라이드 타입으로 정규화한 뒤 planning summary와 quality summary를 함께 반환하도록 보강했습니다. - pptx-creator 및 strategy-deck/board-update/pmo-steering/sales-review-deck/operating-model-deck 번들 스킬을 추가·정리하고, DeckPlanningServiceTests/DeckQualityReviewServiceTests/PptxSkillAutoRepairTests로 회귀 검증을 보강했습니다. - README.md와 docs/DEVELOPMENT.md에 2026-04-14 21:50, 22:00 (KST) 기준 변경 이력과 검증 결과를 반영했습니다. - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_ppt_phase3\ -p:IntermediateOutputPath=obj\verify_ppt_phase3\ (경고 0 / 오류 0) - 검증: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter 'DeckPlanningServiceTests|DeckQualityReviewServiceTests|PptxSkillAutoRepairTests|PptxSkillConsultingDeckTests' -p:OutputPath=bin\verify_ppt_phase3_tests\ -p:IntermediateOutputPath=obj\verify_ppt_phase3_tests\ (통과 5)
This commit is contained in:
54
src/AxCopilot.Tests/Services/DeckPlanningServiceTests.cs
Normal file
54
src/AxCopilot.Tests/Services/DeckPlanningServiceTests.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class DeckPlanningServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Prepare_ShouldInjectConsultingStructure_WhenKeySlidesAreMissing()
|
||||
{
|
||||
using var args = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"title": "Growth Plan",
|
||||
"objective": "growth strategy recommendation",
|
||||
"decision_ask": "approve phase-1 funding",
|
||||
"slides": [
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "Current State",
|
||||
"body": [
|
||||
"Top-line growth has slowed in the enterprise segment.",
|
||||
"Retention is declining in two priority cohorts.",
|
||||
"Manual CRM operations drive avoidable cost."
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "comparison",
|
||||
"title": "Options",
|
||||
"options": [
|
||||
{ "name": "Option A", "pros": "Fast", "cons": "Shallow", "verdict": "Fastest" },
|
||||
{ "name": "Option B", "pros": "Balanced", "cons": "Requires alignment", "verdict": "Recommended" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""");
|
||||
|
||||
using var result = DeckPlanningService.Prepare(args.RootElement, args.RootElement.GetProperty("slides"), "Growth Plan");
|
||||
var slides = result.Slides.EnumerateArray().ToList();
|
||||
var layouts = slides.Select(slide => slide.GetProperty("layout").GetString()).ToList();
|
||||
var titles = slides.Select(slide => slide.GetProperty("title").GetString()).ToList();
|
||||
|
||||
layouts.Should().Contain("title");
|
||||
layouts.Should().Contain("executive_summary");
|
||||
layouts.Should().Contain("recommendation");
|
||||
layouts.Should().Contain("roadmap");
|
||||
titles.Should().Contain(title => title != null && title.Contains("Appendix", StringComparison.OrdinalIgnoreCase));
|
||||
result.AutoRepairCount.Should().BeGreaterThan(0);
|
||||
result.Storyline.Should().NotBeEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class DeckQualityReviewServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReviewDeck_ShouldFlagMissingExecutiveSummaryAndRecommendation()
|
||||
{
|
||||
using var slides = JsonDocument.Parse(
|
||||
"""
|
||||
[
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "Current State",
|
||||
"body": "Point 1\nPoint 2\nPoint 3\nPoint 4\nPoint 5\nPoint 6\nPoint 7"
|
||||
},
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "Findings",
|
||||
"body": "Observation A\nObservation B\nObservation C"
|
||||
}
|
||||
]
|
||||
""");
|
||||
|
||||
var review = DeckQualityReviewService.ReviewDeck("Weak Deck", slides.RootElement, hasTemplate: false, autoRepairCount: 0);
|
||||
|
||||
review.Score.Should().BeLessThan(75);
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("Executive Summary"));
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("Recommendation"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReviewDeck_ShouldRewardBalancedConsultingDeck()
|
||||
{
|
||||
using var slides = JsonDocument.Parse(
|
||||
"""
|
||||
[
|
||||
{ "layout": "title", "title": "Growth Strategy" },
|
||||
{ "layout": "executive_summary", "title": "Executive Summary", "headline": "Headline", "summary_points": ["A","B","C"], "recommendation": "Do B" },
|
||||
{ "layout": "comparison", "title": "Options", "headline": "Compare", "options": [{ "name": "A", "pros": "Fast", "cons": "Shallow", "verdict": "Fastest" }, { "name": "B", "pros": "Balanced", "cons": "Needs alignment", "verdict": "Recommended" }] },
|
||||
{ "layout": "roadmap", "title": "Roadmap", "headline": "Execute in three phases" },
|
||||
{ "layout": "recommendation", "title": "Recommendation", "recommendation": "Approve phase-1", "summary_points": ["Reason 1", "Reason 2"] },
|
||||
{ "layout": "table", "title": "Appendix & Evidence", "headers": ["Evidence","Detail"], "rows": [["Metric","Value"]] }
|
||||
]
|
||||
""");
|
||||
|
||||
var review = DeckQualityReviewService.ReviewDeck("Strong Deck", slides.RootElement, hasTemplate: true, autoRepairCount: 2, storyline: ["Executive Summary", "Options", "Roadmap"]);
|
||||
|
||||
review.Score.Should().BeGreaterThan(80);
|
||||
review.Strengths.Should().Contain(strength => strength.Contains("Executive Summary"));
|
||||
review.Issues.Should().NotContain(issue => issue.Severity == DeckReviewSeverity.Critical);
|
||||
}
|
||||
}
|
||||
79
src/AxCopilot.Tests/Services/PptxSkillAutoRepairTests.cs
Normal file
79
src/AxCopilot.Tests/Services/PptxSkillAutoRepairTests.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Services.Agent;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class PptxSkillAutoRepairTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_ShouldAugmentDeckAndReturnQualitySummary()
|
||||
{
|
||||
var workDir = Path.Combine(Path.GetTempPath(), "ax-pptx-repair-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(workDir);
|
||||
|
||||
try
|
||||
{
|
||||
var context = new AgentContext
|
||||
{
|
||||
WorkFolder = workDir,
|
||||
Permission = "Auto",
|
||||
OperationMode = "external",
|
||||
};
|
||||
|
||||
var tool = new PptxSkill();
|
||||
var args = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"path": "auto-repair-deck.pptx",
|
||||
"title": "Operating Improvement",
|
||||
"objective": "operating model proposal",
|
||||
"decision_ask": "approve phase-1 launch",
|
||||
"slides": [
|
||||
{
|
||||
"layout": "before_after",
|
||||
"title": "Current vs Future",
|
||||
"before": ["Manual coordination", "Slow approvals", "Fragmented KPI tracking"],
|
||||
"after": ["Standard workflow", "Faster approvals", "Single KPI view"]
|
||||
},
|
||||
{
|
||||
"layout": "benefit_waterfall",
|
||||
"title": "Benefits",
|
||||
"benefits": [
|
||||
{ "label": "Cost", "value": 8 },
|
||||
{ "label": "Speed", "value": 12 },
|
||||
{ "label": "Quality", "value": 6 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").RootElement;
|
||||
|
||||
var result = await tool.ExecuteAsync(args, context, CancellationToken.None);
|
||||
var outputPath = Path.Combine(workDir, "auto-repair-deck.pptx");
|
||||
|
||||
result.Success.Should().BeTrue();
|
||||
result.Output.Should().Contain("PPT quality");
|
||||
result.Output.Should().Contain("Storyline:");
|
||||
File.Exists(outputPath).Should().BeTrue();
|
||||
|
||||
using var doc = PresentationDocument.Open(outputPath, false);
|
||||
doc.PresentationPart!.Presentation.SlideIdList!.Count().Should().BeGreaterThanOrEqualTo(6);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(workDir))
|
||||
Directory.Delete(workDir, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user