using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows; using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; namespace AxCopilot.Services.Agent; public class DocumentPlannerTool : IAgentTool { private class SectionPlan { public string Id { get; set; } = ""; public string Heading { get; set; } = ""; public int Level { get; set; } = 1; public int TargetWords { get; set; } = 300; public List KeyPoints { get; set; } = new List(); } private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public string Name => "document_plan"; public string Description => "Create a structured document outline/plan and optionally generate the document file immediately. Use this BEFORE generating long documents (3+ pages). When multi-pass mode is OFF, this tool directly creates and saves a document file with section headings and key points. When multi-pass mode is ON, returns a plan for section-by-section writing via document_assemble."; public ToolParameterSchema Parameters { get { ToolParameterSchema toolParameterSchema = new ToolParameterSchema(); Dictionary obj = new Dictionary { ["topic"] = new ToolProperty { Type = "string", Description = "Document topic or full user request describing what the document should cover." } }; ToolProperty obj2 = new ToolProperty { Type = "string", Description = "Type of document: report, proposal, analysis, manual, minutes, presentation, guide. Default: report" }; int num = 7; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "report"; span[1] = "proposal"; span[2] = "analysis"; span[3] = "manual"; span[4] = "minutes"; span[5] = "presentation"; span[6] = "guide"; obj2.Enum = list; obj["document_type"] = obj2; obj["target_pages"] = new ToolProperty { Type = "integer", Description = "Target number of pages (1 page ≈ 500 words). Default: 5" }; ToolProperty obj3 = new ToolProperty { Type = "string", Description = "Output document format: html, docx, markdown. Default: html" }; num = 3; List list2 = new List(num); CollectionsMarshal.SetCount(list2, num); Span span2 = CollectionsMarshal.AsSpan(list2); span2[0] = "html"; span2[1] = "docx"; span2[2] = "markdown"; obj3.Enum = list2; obj["format"] = obj3; obj["sections_hint"] = new ToolProperty { Type = "string", Description = "Optional hint for desired sections/structure (e.g. '서론, 현황분석, 문제점, 개선방안, 결론')" }; obj["reference_summary"] = new ToolProperty { Type = "string", Description = "Optional summary of reference data already read via file_read (to incorporate into plan)" }; toolParameterSchema.Properties = obj; num = 1; List list3 = new List(num); CollectionsMarshal.SetCount(list3, num); CollectionsMarshal.AsSpan(list3)[0] = "topic"; toolParameterSchema.Required = list3; return toolParameterSchema; } } private static bool IsMultiPassEnabled() { return ((!(Application.Current is App app)) ? ((bool?)null) : app.SettingsService?.Settings.Llm.EnableMultiPassDocument) == true; } private static string GetFolderDataUsage() { return ((!(Application.Current is App app)) ? null : app.SettingsService?.Settings.Llm.FolderDataUsage) ?? "none"; } public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct) { string text = args.GetProperty("topic").GetString() ?? ""; JsonElement value; string docType = (args.TryGetProperty("document_type", out value) ? (value.GetString() ?? "report") : "report"); JsonElement value2; int num = (args.TryGetProperty("target_pages", out value2) ? value2.GetInt32() : 5); JsonElement value3; string format = (args.TryGetProperty("format", out value3) ? (value3.GetString() ?? "html") : "html"); JsonElement value4; string hint = (args.TryGetProperty("sections_hint", out value4) ? (value4.GetString() ?? "") : ""); JsonElement value5; string refSummary = (args.TryGetProperty("reference_summary", out value5) ? (value5.GetString() ?? "") : ""); if (string.IsNullOrWhiteSpace(text)) { return Task.FromResult(ToolResult.Fail("topic이 비어있습니다.")); } if (num < 1) { num = 1; } if (num > 50) { num = 50; } bool flag = IsMultiPassEnabled(); string folderDataUsage = GetFolderDataUsage(); int num2 = (flag ? ((int)Math.Ceiling((double)num * 1.5)) : num); int totalWords = num2 * 500; List sections = BuildSections(docType, num2, hint, refSummary); DistributeWordCount(sections, totalWords); if ((folderDataUsage == "active" || folderDataUsage == "passive") ? true : false) { return ExecuteSinglePassWithData(text, docType, format, num2, totalWords, sections, folderDataUsage, refSummary); } return ExecuteWithAssembleInstructions(text, docType, format, num2, totalWords, sections, flag); } private Task ExecuteWithAssembleInstructions(string topic, string docType, string format, int targetPages, int totalWords, List sections, bool highQuality) { string text = SanitizeFileName(topic); if (1 == 0) { } string text2 = ((format == "docx") ? ".docx" : ((!(format == "markdown")) ? ".html" : ".md")); if (1 == 0) { } string text3 = text2; string fileName = text + text3; string label = (highQuality ? "[고품질]" : "[표준]"); if (!(format == "markdown")) { if (format == "docx") { return ExecuteWithDocxScaffold(topic, fileName, sections, targetPages, totalWords, label); } return ExecuteWithHtmlScaffold(topic, fileName, sections, targetPages, totalWords, label); } return ExecuteWithMarkdownScaffold(topic, fileName, sections, targetPages, totalWords, label); } private Task ExecuteWithHtmlScaffold(string topic, string fileName, List sections, int targetPages, int totalWords, string label) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler; foreach (SectionPlan section in sections) { string value = SuggestHtmlElements(section.KeyPoints); stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(section.Heading)); handler.AppendLiteral("

"); stringBuilder3.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(17, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder5.AppendLine(ref handler); stringBuilder.AppendLine(); } StringBuilder stringBuilder6 = new StringBuilder(); stringBuilder2 = stringBuilder6; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2); handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 "); handler.AppendFormatted(label); handler.AppendLiteral(" ("); handler.AppendFormatted(sections.Count); handler.AppendLiteral("개 섹션, "); handler.AppendFormatted(targetPages); handler.AppendLiteral("페이지/"); handler.AppendFormatted(totalWords); handler.AppendLiteral("단어)"); stringBuilder7.AppendLine(ref handler); stringBuilder6.AppendLine(); stringBuilder6.AppendLine("## 즉시 실행: html_create 호출"); stringBuilder2 = stringBuilder6; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2); handler.AppendLiteral("path: \""); handler.AppendFormatted(fileName); handler.AppendLiteral("\""); stringBuilder8.AppendLine(ref handler); stringBuilder2 = stringBuilder6; StringBuilder stringBuilder9 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("title: \""); handler.AppendFormatted(topic); handler.AppendLiteral("\""); stringBuilder9.AppendLine(ref handler); stringBuilder6.AppendLine("toc: true, numbered: true, mood: \"professional\""); stringBuilder2 = stringBuilder6; StringBuilder stringBuilder10 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(50, 1, stringBuilder2); handler.AppendLiteral("cover: {\"title\": \""); handler.AppendFormatted(topic); handler.AppendLiteral("\", \"author\": \"AX Copilot Agent\"}"); stringBuilder10.AppendLine(ref handler); stringBuilder6.AppendLine(); stringBuilder6.AppendLine("body에 아래 섹션 구조를 기반으로 각 섹션의 내용과 시각화를 자유롭게 작성하세요:"); stringBuilder6.AppendLine("(주석의 '활용 가능 요소'는 참고용이며, 내용에 맞게 다른 요소를 써도 됩니다)"); stringBuilder6.AppendLine(); stringBuilder6.AppendLine("--- body 시작 ---"); stringBuilder6.Append(stringBuilder); stringBuilder6.AppendLine("--- body 끝 ---"); stringBuilder6.AppendLine(); stringBuilder6.AppendLine("⚠ html_create를 지금 즉시 호출하세요. 모든 섹션에 충분한 실제 내용을 작성하세요."); return Task.FromResult(ToolResult.Ok(stringBuilder6.ToString())); } private static string SuggestHtmlElements(List keyPoints) { string text = string.Join(" ", keyPoints).ToLowerInvariant(); List list = new List(); if (ContainsAny(text, "매출", "실적", "통계", "수치", "비교", "현황", "지표", "점유율", "비율", "순위", "성과")) { list.Add("table(데이터 정리)"); list.Add("div.chart-bar(수치 시각화)"); } if (ContainsAny(text, "강점", "약점", "기회", "위협", "swot", "리스크", "문제점", "장점", "단점")) { list.Add("div.grid-2(항목 대비)"); list.Add("callout-warning/callout-tip"); } if (ContainsAny(text, "전략", "제안", "방안", "계획", "방향", "로드맵", "목표")) { list.Add("callout-info(핵심 전략)"); list.Add("span.badge-blue/green(구분 배지)"); } if (ContainsAny(text, "일정", "단계", "절차", "프로세스", "과정", "순서", "연혁", "역사")) { list.Add("div.timeline(단계/일정)"); } if (ContainsAny(text, "달성", "진행", "완료", "목표 대비", "진척")) { list.Add("div.progress(달성률)"); } if (ContainsAny(text, "종류", "유형", "분류", "구성", "항목", "요소", "구분")) { list.Add("div.grid-3.card(항목 카드)"); } if (ContainsAny(text, "핵심", "요약", "결론", "시사점", "포인트")) { list.Add("blockquote(핵심 메시지)"); } list.Add("p/ul/ol(일반 서술)"); return string.Join(" | ", list.Distinct().Take(4)); } private static bool ContainsAny(string text, params string[] keywords) { return keywords.Any((string k) => text.Contains(k)); } private Task ExecuteWithDocxScaffold(string topic, string fileName, List sections, int targetPages, int totalWords, string label) { var sections2 = sections.Select((SectionPlan s) => new { heading = s.Heading, content = $"[{s.Heading} 내용을 {s.TargetWords}단어 이상으로 작성. 핵심 항목: {string.Join(", ", s.KeyPoints)}]", level = s.Level }); string value = JsonSerializer.Serialize(new { path = fileName, title = topic, format = "docx", cover_subtitle = "AX Copilot Agent", sections = sections2 }, _jsonOptions); StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2); handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 "); handler.AppendFormatted(label); handler.AppendLiteral(" ("); handler.AppendFormatted(sections.Count); handler.AppendLiteral("개 섹션, "); handler.AppendFormatted(targetPages); handler.AppendLiteral("페이지/"); handler.AppendFormatted(totalWords); handler.AppendLiteral("단어)"); stringBuilder2.AppendLine(ref handler); stringBuilder.AppendLine("아래 document_assemble 파라미터에서 각 sections[].content의 [내용...] 부분을 실제 내용으로 채워서 즉시 호출하세요."); stringBuilder.AppendLine(); stringBuilder.AppendLine("## 즉시 실행: document_assemble 호출 파라미터"); stringBuilder.AppendLine(value); stringBuilder.AppendLine("⚠ 주의: 설명하지 말고 document_assemble 도구를 지금 즉시 호출하세요."); return Task.FromResult(ToolResult.Ok(stringBuilder.ToString())); } private Task ExecuteWithMarkdownScaffold(string topic, string fileName, List sections, int targetPages, int totalWords, string label) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2); handler.AppendLiteral("# "); handler.AppendFormatted(topic); stringBuilder3.AppendLine(ref handler); stringBuilder.AppendLine(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2); handler.AppendLiteral("*작성일: "); handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd"); handler.AppendLiteral("*"); stringBuilder4.AppendLine(ref handler); stringBuilder.AppendLine(); foreach (SectionPlan section in sections) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral("## "); handler.AppendFormatted(section.Heading); stringBuilder5.AppendLine(ref handler); stringBuilder.AppendLine(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(21, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder6.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(24, 2, stringBuilder2); handler.AppendLiteral("["); handler.AppendFormatted(section.Heading); handler.AppendLiteral(" 내용을 "); handler.AppendFormatted(section.TargetWords); handler.AppendLiteral("단어 이상으로 상세히 작성하세요]"); stringBuilder7.AppendLine(ref handler); stringBuilder.AppendLine(); } StringBuilder stringBuilder8 = new StringBuilder(); stringBuilder2 = stringBuilder8; StringBuilder stringBuilder9 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(30, 4, stringBuilder2); handler.AppendLiteral("\ud83d\udccb 문서 개요 생성 완료 "); handler.AppendFormatted(label); handler.AppendLiteral(" ("); handler.AppendFormatted(sections.Count); handler.AppendLiteral("개 섹션, "); handler.AppendFormatted(targetPages); handler.AppendLiteral("페이지/"); handler.AppendFormatted(totalWords); handler.AppendLiteral("단어)"); stringBuilder9.AppendLine(ref handler); stringBuilder8.AppendLine("아래 file_write 파라미터에서 각 섹션의 [내용...] 부분을 실제 내용으로 채워서 즉시 호출하세요."); stringBuilder8.AppendLine(); stringBuilder8.AppendLine("## 즉시 실행: file_write 호출"); stringBuilder2 = stringBuilder8; StringBuilder stringBuilder10 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(10, 1, stringBuilder2); handler.AppendLiteral("- path: \""); handler.AppendFormatted(fileName); handler.AppendLiteral("\""); stringBuilder10.AppendLine(ref handler); stringBuilder8.AppendLine("- content (각 [내용...] 부분을 실제 내용으로 교체):"); stringBuilder8.AppendLine(stringBuilder.ToString()); stringBuilder8.AppendLine("⚠ 주의: 설명하지 말고 file_write 도구를 지금 즉시 호출하세요. 모든 섹션에 실제 내용을 작성하세요."); return Task.FromResult(ToolResult.Ok(stringBuilder8.ToString())); } private Task ExecuteSinglePassWithData(string topic, string docType, string format, int targetPages, int totalWords, List sections, string folderDataUsage, string refSummary) { if (1 == 0) { } string text = ((format == "docx") ? ".docx" : ((!(format == "markdown")) ? ".html" : ".md")); if (1 == 0) { } string text2 = text; if (1 == 0) { } text = ((format == "docx") ? "docx_create" : ((!(format == "markdown")) ? "html_create" : "file_write")); if (1 == 0) { } string value = text; string text3 = SanitizeFileName(topic); string text4 = text3 + text2; bool flag = !string.IsNullOrWhiteSpace(refSummary); var value2 = new { topic = topic, document_type = docType, format = format, target_pages = targetPages, estimated_total_words = totalWords, suggested_file_name = text4, sections = sections, reference_data = (flag ? refSummary : null), instructions = new { mode = "single-pass-with-data", folder_data_usage = folderDataUsage, step1 = (flag ? "Reference data is already provided above. Skip to step 3." : "Use folder_map to scan the work folder, then use document_read to read RELEVANT files only."), step2 = (flag ? "(skipped)" : "Summarize the key findings from the folder documents relevant to the topic."), step3 = "Write the COMPLETE document content covering ALL sections above, incorporating folder data.", step4 = $"Save the document using {value} with the path '{text4}'. " + "Write ALL sections in a SINGLE tool call. Do NOT split into multiple calls.", note = "Minimize LLM calls. Read data → write complete document → save. Maximum 3 iterations." } }; string value3 = JsonSerializer.Serialize(value2, _jsonOptions); string value4 = (flag ? "참조 데이터가 이미 제공되었습니다. 바로 문서를 작성하세요." : ("폴더 데이터 활용 모드(" + folderDataUsage + ")가 활성화되어 있습니다. 먼저 관련 데이터를 읽은 후 문서를 작성하세요.")); return Task.FromResult(ToolResult.Ok($"\ud83d\udccb 문서 개요가 생성되었습니다 [싱글패스+데이터활용] ({sections.Count}개 섹션, 목표 {targetPages}페이지/{totalWords}단어).\n{value4}\n작성 완료 후 {value}로 '{text4}' 파일에 저장하세요.\n\n{value3}")); } private static void GenerateHtml(string path, string title, string docType, List sections) { string css = TemplateService.GetCss("professional"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(Escape(title)); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine("
"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(title)); handler.AppendLiteral("

"); stringBuilder4.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(47, 3, stringBuilder2); handler.AppendLiteral("
문서 유형: "); handler.AppendFormatted(Escape(GetDocTypeLabel(docType))); handler.AppendLiteral(" | 작성일: "); handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd"); handler.AppendLiteral(" | 섹션: "); handler.AppendFormatted(sections.Count); handler.AppendLiteral("개
"); stringBuilder5.AppendLine(ref handler); if (sections.Count > 1) { stringBuilder.AppendLine("
"); stringBuilder.AppendLine("

\ud83d\udccb 목차

"); stringBuilder.AppendLine(""); stringBuilder.AppendLine("
"); } for (int j = 0; j < sections.Count; j++) { SectionPlan sectionPlan = sections[j]; stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(19, 2, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(sectionPlan.Heading)); handler.AppendLiteral("

"); stringBuilder7.AppendLine(ref handler); stringBuilder.AppendLine("
"); foreach (string keyPoint in sectionPlan.KeyPoints) { stringBuilder.AppendLine("
"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(19, 1, stringBuilder2); handler.AppendLiteral("▸ "); handler.AppendFormatted(Escape(keyPoint)); handler.AppendLiteral(""); stringBuilder8.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder9 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(40, 2, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(keyPoint)); handler.AppendLiteral("에 대한 상세 내용을 여기에 작성합니다. (목표: 약 "); handler.AppendFormatted(sectionPlan.TargetWords / Math.Max(1, sectionPlan.KeyPoints.Count)); handler.AppendLiteral("단어)

"); stringBuilder9.AppendLine(ref handler); stringBuilder.AppendLine("
"); } stringBuilder.AppendLine("
"); } stringBuilder.AppendLine("
"); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); File.WriteAllText(path, stringBuilder.ToString(), Encoding.UTF8); } private static void GenerateDocx(string path, string title, List sections) { using WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document); MainDocumentPart mainDocumentPart = wordprocessingDocument.AddMainDocumentPart(); mainDocumentPart.Document = new Document(); Body body = mainDocumentPart.Document.AppendChild(new Body()); AddDocxParagraph(body, title, bold: true, "48"); AddDocxParagraph(body, $"작성일: {DateTime.Now:yyyy-MM-dd}", bold: false, "20", "888888"); AddDocxParagraph(body, ""); foreach (SectionPlan section in sections) { AddDocxParagraph(body, section.Heading, bold: true, "32", "2B579A"); foreach (string keyPoint in section.KeyPoints) { AddDocxParagraph(body, "▸ " + keyPoint, bold: true); AddDocxParagraph(body, keyPoint + "에 대한 상세 내용을 여기에 작성합니다."); } AddDocxParagraph(body, ""); } } private static void AddDocxParagraph(Body body, string text, bool bold = false, string fontSize = "22", string? color = null) { Paragraph paragraph = new Paragraph(); Run run = new Run(); RunProperties runProperties = new RunProperties { FontSize = new FontSize { Val = fontSize } }; if (bold) { runProperties.Bold = new Bold(); } if (color != null) { runProperties.Color = new Color { Val = color }; } run.AppendChild(runProperties); run.AppendChild(new Text(text) { Space = SpaceProcessingModeValues.Preserve }); paragraph.AppendChild(run); body.AppendChild(paragraph); } private static void GenerateMarkdown(string path, string title, List sections) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2); handler.AppendLiteral("# "); handler.AppendFormatted(title); stringBuilder3.AppendLine(ref handler); stringBuilder.AppendLine(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2); handler.AppendLiteral("*작성일: "); handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd"); handler.AppendLiteral("*"); stringBuilder4.AppendLine(ref handler); stringBuilder.AppendLine(); if (sections.Count > 1) { stringBuilder.AppendLine("## 목차"); stringBuilder.AppendLine(); foreach (SectionPlan section in sections) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(7, 2, stringBuilder2); handler.AppendLiteral("- ["); handler.AppendFormatted(section.Heading); handler.AppendLiteral("](#"); handler.AppendFormatted(section.Heading.Replace(" ", "-").ToLowerInvariant()); handler.AppendLiteral(")"); stringBuilder5.AppendLine(ref handler); } stringBuilder.AppendLine(); stringBuilder.AppendLine("---"); stringBuilder.AppendLine(); } foreach (SectionPlan section2 in sections) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2); handler.AppendLiteral("## "); handler.AppendFormatted(section2.Heading); stringBuilder6.AppendLine(ref handler); stringBuilder.AppendLine(); foreach (string keyPoint in section2.KeyPoints) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder2); handler.AppendLiteral("### ▸ "); handler.AppendFormatted(keyPoint); stringBuilder7.AppendLine(ref handler); stringBuilder.AppendLine(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendFormatted(keyPoint); handler.AppendLiteral("에 대한 상세 내용을 여기에 작성합니다."); stringBuilder8.AppendLine(ref handler); stringBuilder.AppendLine(); } } File.WriteAllText(path, stringBuilder.ToString(), Encoding.UTF8); } private static List BuildSections(string docType, int pages, string hint, string refSummary) { if (!string.IsNullOrWhiteSpace(hint)) { string[] array = hint.Split(new char[4] { ',', '/', '→', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); List list = new List(); for (int i = 0; i < array.Length; i++) { SectionPlan obj = new SectionPlan { Id = $"sec-{i + 1}", Heading = $"{i + 1}. {array[i].TrimStart("0123456789. ".ToCharArray())}", Level = 1 }; int num = 1; List list2 = new List(num); CollectionsMarshal.SetCount(list2, num); CollectionsMarshal.AsSpan(list2)[0] = array[i] + " 관련 핵심 내용을 상세히 작성"; obj.KeyPoints = list2; list.Add(obj); } return list; } if (1 == 0) { } List result; switch (docType) { case "proposal": { List list29 = new List(); SectionPlan obj24 = new SectionPlan { Id = "sec-1", Heading = "1. 개요", Level = 1 }; int num = 3; List list30 = new List(num); CollectionsMarshal.SetCount(list30, num); Span span23 = CollectionsMarshal.AsSpan(list30); span23[0] = "배경"; span23[1] = "목적"; span23[2] = "범위"; obj24.KeyPoints = list30; list29.Add(obj24); SectionPlan obj25 = new SectionPlan { Id = "sec-2", Heading = "2. 현황 분석", Level = 1 }; num = 2; List list31 = new List(num); CollectionsMarshal.SetCount(list31, num); Span span24 = CollectionsMarshal.AsSpan(list31); span24[0] = "현재 상황"; span24[1] = "문제점 식별"; obj25.KeyPoints = list31; list29.Add(obj25); SectionPlan obj26 = new SectionPlan { Id = "sec-3", Heading = "3. 제안 내용", Level = 1 }; num = 3; List list32 = new List(num); CollectionsMarshal.SetCount(list32, num); Span span25 = CollectionsMarshal.AsSpan(list32); span25[0] = "핵심 제안"; span25[1] = "기대 효과"; span25[2] = "실행 방안"; obj26.KeyPoints = list32; list29.Add(obj26); SectionPlan obj27 = new SectionPlan { Id = "sec-4", Heading = "4. 추진 일정", Level = 1 }; num = 2; List list33 = new List(num); CollectionsMarshal.SetCount(list33, num); Span span26 = CollectionsMarshal.AsSpan(list33); span26[0] = "단계별 일정"; span26[1] = "마일스톤"; obj27.KeyPoints = list33; list29.Add(obj27); SectionPlan obj28 = new SectionPlan { Id = "sec-5", Heading = "5. 소요 자원", Level = 1 }; num = 3; List list34 = new List(num); CollectionsMarshal.SetCount(list34, num); Span span27 = CollectionsMarshal.AsSpan(list34); span27[0] = "인력"; span27[1] = "예산"; span27[2] = "장비"; obj28.KeyPoints = list34; list29.Add(obj28); SectionPlan obj29 = new SectionPlan { Id = "sec-6", Heading = "6. 기대 효과 및 결론", Level = 1 }; num = 3; List list35 = new List(num); CollectionsMarshal.SetCount(list35, num); Span span28 = CollectionsMarshal.AsSpan(list35); span28[0] = "정량적 효과"; span28[1] = "정성적 효과"; span28[2] = "결론"; obj29.KeyPoints = list35; list29.Add(obj29); result = list29; break; } case "analysis": { List list22 = new List(); SectionPlan obj18 = new SectionPlan { Id = "sec-1", Heading = "1. 분석 개요", Level = 1 }; int num = 3; List list23 = new List(num); CollectionsMarshal.SetCount(list23, num); Span span17 = CollectionsMarshal.AsSpan(list23); span17[0] = "분석 목적"; span17[1] = "분석 범위"; span17[2] = "방법론"; obj18.KeyPoints = list23; list22.Add(obj18); SectionPlan obj19 = new SectionPlan { Id = "sec-2", Heading = "2. 데이터 현황", Level = 1 }; num = 3; List list24 = new List(num); CollectionsMarshal.SetCount(list24, num); Span span18 = CollectionsMarshal.AsSpan(list24); span18[0] = "데이터 출처"; span18[1] = "기본 통계"; span18[2] = "데이터 품질"; obj19.KeyPoints = list24; list22.Add(obj19); SectionPlan obj20 = new SectionPlan { Id = "sec-3", Heading = "3. 정량 분석", Level = 1 }; num = 3; List list25 = new List(num); CollectionsMarshal.SetCount(list25, num); Span span19 = CollectionsMarshal.AsSpan(list25); span19[0] = "수치 분석"; span19[1] = "추세"; span19[2] = "비교"; obj20.KeyPoints = list25; list22.Add(obj20); SectionPlan obj21 = new SectionPlan { Id = "sec-4", Heading = "4. 정성 분석", Level = 1 }; num = 3; List list26 = new List(num); CollectionsMarshal.SetCount(list26, num); Span span20 = CollectionsMarshal.AsSpan(list26); span20[0] = "패턴"; span20[1] = "인사이트"; span20[2] = "이상치"; obj21.KeyPoints = list26; list22.Add(obj21); SectionPlan obj22 = new SectionPlan { Id = "sec-5", Heading = "5. 종합 해석", Level = 1 }; num = 2; List list27 = new List(num); CollectionsMarshal.SetCount(list27, num); Span span21 = CollectionsMarshal.AsSpan(list27); span21[0] = "핵심 발견"; span21[1] = "시사점"; obj22.KeyPoints = list27; list22.Add(obj22); SectionPlan obj23 = new SectionPlan { Id = "sec-6", Heading = "6. 결론 및 권장사항", Level = 1 }; num = 3; List list28 = new List(num); CollectionsMarshal.SetCount(list28, num); Span span22 = CollectionsMarshal.AsSpan(list28); span22[0] = "결론"; span22[1] = "조치 방안"; span22[2] = "추가 분석 필요 항목"; obj23.KeyPoints = list28; list22.Add(obj23); result = list22; break; } case "manual": case "guide": { List list16 = new List(); SectionPlan obj13 = new SectionPlan { Id = "sec-1", Heading = "1. 소개", Level = 1 }; int num = 3; List list17 = new List(num); CollectionsMarshal.SetCount(list17, num); Span span12 = CollectionsMarshal.AsSpan(list17); span12[0] = "목적"; span12[1] = "대상 독자"; span12[2] = "사전 요구사항"; obj13.KeyPoints = list17; list16.Add(obj13); SectionPlan obj14 = new SectionPlan { Id = "sec-2", Heading = "2. 시작하기", Level = 1 }; num = 3; List list18 = new List(num); CollectionsMarshal.SetCount(list18, num); Span span13 = CollectionsMarshal.AsSpan(list18); span13[0] = "설치"; span13[1] = "초기 설정"; span13[2] = "기본 사용법"; obj14.KeyPoints = list18; list16.Add(obj14); SectionPlan obj15 = new SectionPlan { Id = "sec-3", Heading = "3. 주요 기능", Level = 1 }; num = 2; List list19 = new List(num); CollectionsMarshal.SetCount(list19, num); Span span14 = CollectionsMarshal.AsSpan(list19); span14[0] = "기능별 상세 설명"; span14[1] = "사용 예시"; obj15.KeyPoints = list19; list16.Add(obj15); SectionPlan obj16 = new SectionPlan { Id = "sec-4", Heading = "4. 고급 기능", Level = 1 }; num = 3; List list20 = new List(num); CollectionsMarshal.SetCount(list20, num); Span span15 = CollectionsMarshal.AsSpan(list20); span15[0] = "고급 설정"; span15[1] = "커스터마이징"; span15[2] = "자동화"; obj16.KeyPoints = list20; list16.Add(obj16); SectionPlan obj17 = new SectionPlan { Id = "sec-5", Heading = "5. 문제 해결", Level = 1 }; num = 3; List list21 = new List(num); CollectionsMarshal.SetCount(list21, num); Span span16 = CollectionsMarshal.AsSpan(list21); span16[0] = "자주 묻는 질문"; span16[1] = "에러 대응"; span16[2] = "지원 정보"; obj17.KeyPoints = list21; list16.Add(obj17); result = list16; break; } case "minutes": { List list10 = new List(); SectionPlan obj8 = new SectionPlan { Id = "sec-1", Heading = "1. 회의 정보", Level = 1 }; int num = 3; List list11 = new List(num); CollectionsMarshal.SetCount(list11, num); Span span7 = CollectionsMarshal.AsSpan(list11); span7[0] = "일시"; span7[1] = "참석자"; span7[2] = "장소"; obj8.KeyPoints = list11; list10.Add(obj8); SectionPlan obj9 = new SectionPlan { Id = "sec-2", Heading = "2. 안건 및 논의", Level = 1 }; num = 2; List list12 = new List(num); CollectionsMarshal.SetCount(list12, num); Span span8 = CollectionsMarshal.AsSpan(list12); span8[0] = "안건별 논의 내용"; span8[1] = "주요 의견"; obj9.KeyPoints = list12; list10.Add(obj9); SectionPlan obj10 = new SectionPlan { Id = "sec-3", Heading = "3. 결정 사항", Level = 1 }; num = 2; List list13 = new List(num); CollectionsMarshal.SetCount(list13, num); Span span9 = CollectionsMarshal.AsSpan(list13); span9[0] = "합의 내용"; span9[1] = "변경 사항"; obj10.KeyPoints = list13; list10.Add(obj10); SectionPlan obj11 = new SectionPlan { Id = "sec-4", Heading = "4. 액션 아이템", Level = 1 }; num = 3; List list14 = new List(num); CollectionsMarshal.SetCount(list14, num); Span span10 = CollectionsMarshal.AsSpan(list14); span10[0] = "담당자"; span10[1] = "기한"; span10[2] = "세부 내용"; obj11.KeyPoints = list14; list10.Add(obj11); SectionPlan obj12 = new SectionPlan { Id = "sec-5", Heading = "5. 다음 회의", Level = 1 }; num = 2; List list15 = new List(num); CollectionsMarshal.SetCount(list15, num); Span span11 = CollectionsMarshal.AsSpan(list15); span11[0] = "예정일"; span11[1] = "주요 안건"; obj12.KeyPoints = list15; list10.Add(obj12); result = list10; break; } default: { List list3 = new List(); SectionPlan obj2 = new SectionPlan { Id = "sec-1", Heading = "1. 개요", Level = 1 }; int num = 3; List list4 = new List(num); CollectionsMarshal.SetCount(list4, num); Span span = CollectionsMarshal.AsSpan(list4); span[0] = "배경"; span[1] = "목적"; span[2] = "범위"; obj2.KeyPoints = list4; list3.Add(obj2); SectionPlan obj3 = new SectionPlan { Id = "sec-2", Heading = "2. 현황", Level = 1 }; num = 2; List list5 = new List(num); CollectionsMarshal.SetCount(list5, num); Span span2 = CollectionsMarshal.AsSpan(list5); span2[0] = "현재 상태"; span2[1] = "주요 지표"; obj3.KeyPoints = list5; list3.Add(obj3); SectionPlan obj4 = new SectionPlan { Id = "sec-3", Heading = "3. 분석", Level = 1 }; num = 3; List list6 = new List(num); CollectionsMarshal.SetCount(list6, num); Span span3 = CollectionsMarshal.AsSpan(list6); span3[0] = "데이터 분석"; span3[1] = "비교"; span3[2] = "추세"; obj4.KeyPoints = list6; list3.Add(obj4); SectionPlan obj5 = new SectionPlan { Id = "sec-4", Heading = "4. 주요 발견", Level = 1 }; num = 3; List list7 = new List(num); CollectionsMarshal.SetCount(list7, num); Span span4 = CollectionsMarshal.AsSpan(list7); span4[0] = "핵심 인사이트"; span4[1] = "문제점"; span4[2] = "기회"; obj5.KeyPoints = list7; list3.Add(obj5); SectionPlan obj6 = new SectionPlan { Id = "sec-5", Heading = "5. 제안", Level = 1 }; num = 2; List list8 = new List(num); CollectionsMarshal.SetCount(list8, num); Span span5 = CollectionsMarshal.AsSpan(list8); span5[0] = "개선 방안"; span5[1] = "실행 계획"; obj6.KeyPoints = list8; list3.Add(obj6); SectionPlan obj7 = new SectionPlan { Id = "sec-6", Heading = "6. 결론", Level = 1 }; num = 3; List list9 = new List(num); CollectionsMarshal.SetCount(list9, num); Span span6 = CollectionsMarshal.AsSpan(list9); span6[0] = "요약"; span6[1] = "기대 효과"; span6[2] = "향후 과제"; obj7.KeyPoints = list9; list3.Add(obj7); result = list3; break; } } if (1 == 0) { } return result; } private static void DistributeWordCount(List sections, int totalWords) { if (sections.Count != 0) { double[] array = new double[sections.Count]; for (int i = 0; i < sections.Count; i++) { array[i] = ((i == 0 || i == sections.Count - 1) ? 0.7 : 1.2); } double num = array.Sum(); for (int j = 0; j < sections.Count; j++) { sections[j].TargetWords = Math.Max(100, (int)((double)totalWords * array[j] / num)); } } } private static string SanitizeFileName(string name) { string text = ((name.Length > 60) ? name.Substring(0, 60) : name); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } return text.Trim().TrimEnd('.'); } private static string GetDocTypeLabel(string docType) { if (1 == 0) { } string result; switch (docType) { case "proposal": result = "제안서"; break; case "analysis": result = "분석 보고서"; break; case "manual": case "guide": result = "매뉴얼/가이드"; break; case "minutes": result = "회의록"; break; case "presentation": result = "프레젠테이션"; break; default: result = "보고서"; break; } if (1 == 0) { } return result; } private static string Escape(string text) { return text.Replace("&", "&").Replace("<", "<").Replace(">", ">"); } }