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:
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -155,6 +155,27 @@ public class PptxSkill : IAgentTool
|
||||
Description = "Slide aspect ratio. Default: widescreen (16:9)",
|
||||
Enum = ["widescreen", "standard"]
|
||||
},
|
||||
["audience"] = new()
|
||||
{
|
||||
Type = "string",
|
||||
Description = "Target audience such as executives, steering committee, PMO, sales leadership, or team leads.",
|
||||
},
|
||||
["objective"] = new()
|
||||
{
|
||||
Type = "string",
|
||||
Description = "Presentation objective such as strategy recommendation, board update, PMO steering, operating model proposal, or KPI review.",
|
||||
},
|
||||
["decision_ask"] = new()
|
||||
{
|
||||
Type = "string",
|
||||
Description = "The exact decision or approval being requested from the audience.",
|
||||
},
|
||||
["storyline"] = new()
|
||||
{
|
||||
Type = "array",
|
||||
Description = "Optional storyline or ordered section hints.",
|
||||
Items = new() { Type = "string" }
|
||||
},
|
||||
},
|
||||
Required = ["slides"]
|
||||
};
|
||||
@@ -506,9 +527,15 @@ public class PptxSkill : IAgentTool
|
||||
if (safe.Length > 60) safe = safe[..60].TrimEnd();
|
||||
path = (string.IsNullOrWhiteSpace(safe) ? "presentation" : safe) + ".pptx";
|
||||
}
|
||||
var theme = args.SafeTryGetProperty("theme", out var th) ? th.SafeGetString() ?? "basic100" : "basic100";
|
||||
var aspect = args.SafeTryGetProperty("aspect", out var asp) ? asp.SafeGetString() ?? "widescreen" : "widescreen";
|
||||
var hasExplicitTheme = args.SafeTryGetProperty("theme", out var th) &&
|
||||
!string.IsNullOrWhiteSpace(th.SafeGetString());
|
||||
var theme = hasExplicitTheme ? th.SafeGetString() ?? "basic100" : "basic100";
|
||||
var aspect = args.SafeTryGetProperty("aspect", out var asp) ? asp.SafeGetString() ?? "widescreen" : "widescreen";
|
||||
var cloneAll = args.SafeTryGetProperty("clone_slides", out var cloneEl) && cloneEl.ValueKind == JsonValueKind.True;
|
||||
var hasThemeFile = args.SafeTryGetProperty("theme_file", out var themeFileEl) &&
|
||||
!string.IsNullOrWhiteSpace(themeFileEl.SafeGetString());
|
||||
var hasExplicitTemplate = args.SafeTryGetProperty("template", out var templateArgEl) &&
|
||||
!string.IsNullOrWhiteSpace(templateArgEl.SafeGetString());
|
||||
|
||||
// 전체 복제용 텍스트 교체 맵
|
||||
Dictionary<string, string>? globalReplacements = null;
|
||||
@@ -526,6 +553,20 @@ public class PptxSkill : IAgentTool
|
||||
if (!hasSlidesArray && !cloneAll)
|
||||
return ToolResult.Fail("slides 배열이 필요합니다.");
|
||||
|
||||
using var preparedDeck = hasSlidesArray
|
||||
? DeckPlanningService.Prepare(args, slidesEl, presTitle)
|
||||
: null;
|
||||
|
||||
if (preparedDeck != null)
|
||||
{
|
||||
slidesEl = preparedDeck.Slides;
|
||||
if (!hasExplicitTheme && !hasExplicitTemplate && !hasThemeFile &&
|
||||
!string.IsNullOrWhiteSpace(preparedDeck.SuggestedTheme))
|
||||
{
|
||||
theme = preparedDeck.SuggestedTheme!;
|
||||
}
|
||||
}
|
||||
|
||||
var fullPath = FileReadTool.ResolvePath(path, context.WorkFolder);
|
||||
if (context.ActiveTab == "Cowork") fullPath = AgentContext.EnsureTimestampedPath(fullPath);
|
||||
if (!fullPath.EndsWith(".pptx", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -923,9 +964,23 @@ public class PptxSkill : IAgentTool
|
||||
: templatePptxPath != null
|
||||
? $"theme_file:{cloneInfo_srcLabel} (master cloned{(cloneAll ? " + slides cloned" : "")})"
|
||||
: theme;
|
||||
return ToolResult.Ok(
|
||||
$"✅ PPTX 생성 완료: {fullPath} ({slideCount_final}슬라이드, {themeLabel}, {(isWide ? "16:9" : "4:3")})",
|
||||
fullPath);
|
||||
var deckReview = hasSlidesArray
|
||||
? DeckQualityReviewService.ReviewDeck(
|
||||
presTitle,
|
||||
slidesEl,
|
||||
!string.IsNullOrWhiteSpace(templateName) || templatePptxPath != null,
|
||||
preparedDeck?.AutoRepairCount ?? 0,
|
||||
preparedDeck?.Storyline)
|
||||
: null;
|
||||
var outputParts = new List<string>
|
||||
{
|
||||
$"PPTX created: {fullPath} ({slideCount_final} slides, {themeLabel}, {(isWide ? "16:9" : "4:3")})"
|
||||
};
|
||||
if (preparedDeck != null)
|
||||
outputParts.Add(preparedDeck.ToToolSummary());
|
||||
if (deckReview != null)
|
||||
outputParts.Add(deckReview.ToToolSummary());
|
||||
return ToolResult.Ok(string.Join("\n", outputParts), fullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user