AX Agent 코워크·코드 후속 호환 과제 정리 및 IBM Qwen 도구호출 경로 보강

이번 커밋은 claude-code 기준 후속 과제를 이어서 반영해 Cowork/Code의 도구 선택 강도와 IBM 배포형 vLLM(Qwen) 호환 경로를 정리했다.

핵심 변경 사항:

- IBM 전용 tool body에서 과거 assistant tool_calls 및 role=tool 이력을 OpenAI 형식으로 재전송하지 않고 평탄한 transcript로 직렬화하도록 변경

- Cowork 프롬프트에서 document_review 및 format_convert를 기본 단계처럼 강제하지 않고 file_read/document_read 중심의 가벼운 검증 흐름으로 완화

- unknown/disallowed tool recovery에서 tool_search를 항상 강제하지 않고 alias 후보나 활성 도구 예시로 바로 선택 가능하면 직접 사용하도록 조정

- Code 탐색에서 정의/참조/구현/호출관계 의도는 lsp_code_intel을 더 우선하도록 보강하고 LSP 결과 요약 품질 개선

- 문서 검증 근거는 file_read/document_read면 충분하도록 단순화

문서 반영:

- README.md, docs/DEVELOPMENT.md에 2026-04-09 22:48 (KST) 기준 작업 이력 추가

검증 결과:

- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\

- 경고 0개, 오류 0개
This commit is contained in:
2026-04-09 22:50:27 +09:00
parent 3c6d2f1ce4
commit 1fab215344
8 changed files with 967 additions and 72 deletions

View File

@@ -0,0 +1,435 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AxCopilot.Models;
using AxCopilot.Services;
namespace AxCopilot.Views;
public partial class ChatWindow
{
// ─── 코워크 에이전트 지원 ────────────────────────────────────────────
private string BuildCoworkSystemPrompt()
{
var workFolder = GetCurrentWorkFolder();
var llm = _settings.Settings.Llm;
var sb = new System.Text.StringBuilder();
// ══════════════════════════════════════════════════════════════════
// 도구 호출 절대 규칙을 시스템 프롬프트 맨 첫 줄에 배치 — 최대 강제력
// ══════════════════════════════════════════════════════════════════
sb.AppendLine("## [절대 규칙] 도구 우선 — USE TOOLS WHEN THEY MATERIALLY ADVANCE THE TASK");
sb.AppendLine("이 규칙들은 다른 모든 지시보다 우선합니다. 위반 시 작업이 즉시 실패합니다.");
sb.AppendLine("Treat these as operating guidance, not as a requirement to force unnecessary tool calls.");
sb.AppendLine("");
sb.AppendLine("### [규칙 1] 필요한 도구를 먼저 사용하라 — Tools First When Needed");
sb.AppendLine("모든 응답에는 반드시 하나 이상의 도구 호출이 포함되어야 합니다.");
sb.AppendLine("텍스트만 있고 도구 호출이 없는 응답은 무효이며 즉시 거부됩니다.");
sb.AppendLine("Use tools whenever they materially advance the task. Do not answer with text only when inspection, file creation, editing, or verification is still needed.");
sb.AppendLine("A text-only response is fine once the requested artifact already exists or enough evidence has been gathered.");
sb.AppendLine("출력의 첫 번째 항목은 반드시 도구 호출이어야 합니다. 텍스트로 시작하는 것은 금지입니다.");
sb.AppendLine("When a tool is clearly useful, call it promptly without a long preamble. Do not force an unnecessary tool call if the task is already complete.");
sb.AppendLine("");
sb.AppendLine("### [규칙 2] 말 먼저 하지 마라 — No Verbal Preamble");
sb.AppendLine("절대 금지 문구 — 도구 호출 전에 절대 쓰지 말 것:");
sb.AppendLine(" ✗ '알겠습니다' / '네' / '확인했습니다' / '작업을 시작하겠습니다' / '먼저 ... 하겠습니다'");
sb.AppendLine(" ✗ '다음과 같이 진행하겠습니다' / '분석해보겠습니다' / '살펴보겠습니다'");
sb.AppendLine(" ✗ 'I will ...' / 'Let me ...' / 'Sure!' / 'Of course!' / 'I'll now ...' / 'First, I need to ...'");
sb.AppendLine("올바른 행동: 즉시 도구를 호출하세요. 간단한 설명은 도구 결과 이후에만 허용됩니다.");
sb.AppendLine("CORRECT: call the tool immediately. A brief explanation may follow AFTER the tool result, never before.");
sb.AppendLine("");
sb.AppendLine("### [규칙 3] 한 번에 여러 도구를 호출하라 — Batch Multiple Tools");
sb.AppendLine("독립적인 작업들은 반드시 같은 응답에서 동시에 호출해야 합니다. 순차적으로 하지 마세요.");
sb.AppendLine("Batch independent reads only when it genuinely saves time and keeps the scope narrow.");
sb.AppendLine(" 나쁜 예(BAD): 응답1: folder_map → 응답2: document_read → 응답3: html_create (순차 처리 — 금지)");
sb.AppendLine(" 좋은 예(GOOD): 응답1: folder_map + document_read 동시 호출 → 응답2: html_create (배치 처리)");
sb.AppendLine("");
sb.AppendLine("### [규칙 4] 불확실해도 즉시 도구를 호출하라 — Act on Uncertainty");
sb.AppendLine("어떤 파일을 읽어야 할지 모를 때도 사용자에게 묻지 말고 즉시 folder_map이나 document_read를 호출하세요.");
sb.AppendLine("If the task requires existing files, start with glob or grep, then use document_read or file_read on the best candidates. If the user wants a brand-new document, skip file exploration and create the artifact directly.");
sb.AppendLine("");
sb.AppendLine("### [규칙 5] 완료까지 계속 도구를 호출하라 — Continue Until Complete");
sb.AppendLine("사용자의 요청이 완전히 완료될 때까지 도구 호출을 계속하세요. '작업을 시작했습니다'는 완료가 아닙니다.");
sb.AppendLine("'완료'의 정의: 결과 파일이 실제로 존재하고, 검증되고, 사용자에게 보고된 상태.");
sb.AppendLine("Keep working until the artifact exists or a concrete blocker is reached. Prefer the shortest path to a complete result.");
sb.AppendLine("");
// 소개 및 메타 정보
sb.AppendLine("---");
sb.AppendLine("You are AX Copilot Agent. You can read, write, and edit files using the provided tools.");
sb.AppendLine($"Today's date: {DateTime.Now:yyyy년 M월 d일} ({DateTime.Now:yyyy-MM-dd}, {DateTime.Now:dddd}).");
sb.AppendLine("Available skills: excel_create (.xlsx), docx_create (.docx), csv_create (.csv), markdown_create (.md), html_create (.html), script_create (.bat/.ps1), document_review (품질 검증), format_convert (포맷 변환).");
sb.AppendLine("\nOnly present a step-by-step plan when the user explicitly asks for a plan or when the task is too ambiguous to execute safely.");
sb.AppendLine("For ordinary Cowork requests, proceed directly with the work and focus on producing the real artifact.");
sb.AppendLine("If the user asks for a brand-new report, proposal, analysis, manual, or other document and does not explicitly ask to reference workspace files, do NOT start with glob, grep, document_read, or folder_map.");
sb.AppendLine("In that case, go straight to document_plan when helpful, then immediately call the creation tool such as docx_create, html_create, markdown_create, excel_create, or document_assemble.");
sb.AppendLine("After creating files, summarize what was created and include the actual output path.");
sb.AppendLine("Do not stop after a single step. Continue autonomously until the request is completed or a concrete blocker (permission denial, missing dependency, hard error) is encountered.");
sb.AppendLine("When adapting external references, rewrite names/structure/comments to AX Copilot style. Avoid clone-like outputs.");
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above. Never use placeholder or fictional dates.");
sb.AppendLine("IMPORTANT: For reports, proposals, analyses, and manuals with multiple sections:");
sb.AppendLine(" 1. Decide the document structure internally first.");
sb.AppendLine(" 2. Use document_plan only when it improves the actual output, not as a mandatory user-facing approval step.");
sb.AppendLine(" 3. Then immediately create the real output file with html_create, docx_create, markdown_create, or file_write.");
sb.AppendLine(" 4. Fill every section with real content. Do not stop at an outline or plan only.");
sb.AppendLine(" 5. The task is complete only after the actual document file has been created or updated.");
// 문서 품질 검증 루프
sb.AppendLine("\n## Document Quality Review");
sb.AppendLine("After creating a document, verify it in the lightest way that is sufficient:");
sb.AppendLine("1. Start with file_read or document_read to confirm the generated file exists and covers the requested sections");
sb.AppendLine("2. Use document_review only for large generated documents, conversion-heavy output, or when the user explicitly asks for a quality check");
sb.AppendLine("3. Check logical errors only when relevant: dates, missing sections, broken formatting, or placeholder text");
sb.AppendLine("4. If issues are found, fix them using file_write or file_edit, then re-verify");
sb.AppendLine("5. Report what was checked and whether corrections were needed");
// 문서 포맷 변환 지원
sb.AppendLine("\n## Format Conversion");
sb.AppendLine("When the user requests format conversion (e.g., HTML→Word, Excel→CSV, Markdown→HTML):");
sb.AppendLine("Use format conversion tools only when the user explicitly requests conversion between formats.");
sb.AppendLine("1. Use file_read or document_read to read the source file content");
sb.AppendLine("2. Create a new file in the target format using the appropriate skill (docx_create, html_create, etc.)");
sb.AppendLine("3. Preserve the content structure, formatting, and data as closely as possible");
// 사용자 지정 출력 포맷
var fmt = llm.DefaultOutputFormat;
if (!string.IsNullOrEmpty(fmt) && fmt != "auto")
{
var fmtMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["xlsx"] = "Excel (.xlsx) using excel_create",
["docx"] = "Word (.docx) using docx_create",
["html"] = "HTML (.html) using html_create",
["md"] = "Markdown (.md) using markdown_create",
["csv"] = "CSV (.csv) using csv_create",
};
if (fmtMap.TryGetValue(fmt, out var fmtDesc))
sb.AppendLine($"IMPORTANT: User prefers output format: {fmtDesc}. Use this format unless the user specifies otherwise.");
}
// 디자인 무드 — HTML 문서 생성 시 mood 파라미터로 전달하도록 안내
if (!string.IsNullOrEmpty(_selectedMood) && _selectedMood != "modern")
sb.AppendLine($"When creating HTML documents with html_create, use mood=\"{_selectedMood}\" for the design template.");
else
sb.AppendLine("When creating HTML documents with html_create, you can set 'mood' parameter: modern, professional, creative, minimal, elegant, dark, colorful, corporate, magazine, dashboard.");
if (!string.IsNullOrEmpty(workFolder))
sb.AppendLine($"Current work folder: {workFolder}");
sb.AppendLine($"File permission mode: {llm.FilePermission}");
sb.Append(BuildSubAgentDelegationSection(false));
// 폴더 데이터 활용 지침 (사용자 옵션이 아닌 탭별 자동 정책)
switch (GetAutomaticFolderDataUsage())
{
case "active":
sb.AppendLine("IMPORTANT: Folder Data Usage = ACTIVE. You have 'document_read' and 'folder_map' tools available.");
sb.AppendLine("Use workspace exploration only when the user explicitly asks to reference existing materials, provides a file/path hint, or asks about folder contents.");
sb.AppendLine("For brand-new document creation requests, skip glob/grep/document_read/folder_map and proceed directly to document_plan plus the creation tool.");
sb.AppendLine("[CRITICAL] FILE SELECTION STRATEGY — DO NOT READ ALL FILES:");
sb.AppendLine(" 1. Identify candidate files by filename or topic keywords first.");
sb.AppendLine(" 2. Read ONLY files that clearly match the user's topic. Skip unrelated topics.");
sb.AppendLine(" 3. Maximum 2-3 relevant files for the first pass. Expand only when evidence shows more files are needed.");
sb.AppendLine(" 4. Do NOT read every file 'just in case'. Broad reading without evidence is forbidden.");
sb.AppendLine(" 5. If no files match the topic, proceed WITHOUT reading any workspace files.");
sb.AppendLine("VIOLATION: Reading all files in the folder is FORBIDDEN. It wastes tokens and degrades quality.");
break;
case "passive":
sb.AppendLine("Folder Data Usage = PASSIVE. You have 'document_read' and 'folder_map' tools. " +
"Only read folder documents when the user explicitly asks you to reference or use them.");
break;
default: // "none"
sb.AppendLine("Folder Data Usage = NONE. Do NOT read or reference documents in the work folder unless the user explicitly provides a file path.");
break;
}
// 프리셋 시스템 프롬프트가 있으면 추가
lock (_convLock)
{
if (_currentConversation != null && !string.IsNullOrEmpty(_currentConversation.SystemCommand))
sb.AppendLine("\n" + _currentConversation.SystemCommand);
}
// 프로젝트 문맥 파일 (AGENTS.md) 주입
sb.Append(LoadProjectContext(workFolder));
// 프로젝트 규칙 (.ax/rules/) 자동 주입
sb.Append(BuildProjectRulesSection(workFolder));
// 에이전트 메모리 주입
sb.Append(BuildMemorySection(workFolder));
// 피드백 학습 컨텍스트 주입
sb.Append(BuildFeedbackContext());
return sb.ToString();
}
private string BuildCodeSystemPrompt()
{
var workFolder = GetCurrentWorkFolder();
var llm = _settings.Settings.Llm;
var code = llm.Code;
var sb = new System.Text.StringBuilder();
// ══════════════════════════════════════════════════════════════════
// 도구 호출 절대 규칙을 시스템 프롬프트 맨 첫 줄에 배치 — 최대 강제력
// ══════════════════════════════════════════════════════════════════
sb.AppendLine("## [절대 규칙] 도구 우선 — USE TOOLS WHEN THEY MATERIALLY ADVANCE THE TASK");
sb.AppendLine("이 규칙들은 다른 모든 지시보다 우선합니다. 위반 시 작업이 즉시 실패합니다.");
sb.AppendLine("Treat these as operating guidance, not as a requirement to force unnecessary tool calls.");
sb.AppendLine("");
sb.AppendLine("### [규칙 1] 필요한 도구를 먼저 사용하라 — Tools First When Needed");
sb.AppendLine("모든 응답에는 반드시 하나 이상의 도구 호출이 포함되어야 합니다.");
sb.AppendLine("텍스트만 있고 도구 호출이 없는 응답은 무효이며 즉시 거부됩니다.");
sb.AppendLine("Use tools whenever they materially advance the task. Do not answer with text only when code inspection, editing, diff review, or verification is still needed.");
sb.AppendLine("A text-only response is fine once the requested code change is complete or enough evidence has been gathered.");
sb.AppendLine("출력의 첫 번째 항목은 반드시 도구 호출이어야 합니다. 텍스트로 시작하는 것은 금지입니다.");
sb.AppendLine("When a tool is clearly useful, call it promptly without a long preamble. Do not force an unnecessary tool call if the task is already complete.");
sb.AppendLine("");
sb.AppendLine("### [규칙 2] 말 먼저 하지 마라 — No Verbal Preamble");
sb.AppendLine("절대 금지 문구 — 도구 호출 전에 절대 쓰지 말 것:");
sb.AppendLine(" ✗ '알겠습니다' / '네' / '확인했습니다' / '분석해보겠습니다' / '살펴보겠습니다'");
sb.AppendLine(" ✗ '먼저 폴더 구조를 파악하겠습니다' — folder_map을 즉시 호출하세요, 예고하지 마세요");
sb.AppendLine(" ✗ 'I will ...' / 'Let me ...' / 'Sure!' / 'I'll start by ...' / 'First, I need to ...'");
sb.AppendLine("올바른 행동: 즉시 도구를 호출하세요. 한 문장 설명은 도구 결과 이후에만 허용됩니다.");
sb.AppendLine("CORRECT: call the tool immediately. One-sentence explanation may follow the tool result, never precede it.");
sb.AppendLine("");
sb.AppendLine("### [규칙 3] 모든 독립 작업은 병렬로 호출하라 — Parallelise Everything Possible");
sb.AppendLine("독립적인 읽기/검색 작업들은 반드시 같은 응답에서 동시에 호출해야 합니다. 절대 순차적으로 하지 마세요.");
sb.AppendLine("Batch independent reads/searches only when it genuinely saves time and keeps the scope narrow.");
sb.AppendLine(" 나쁜 예(BAD): R1: folder_map → R2: grep → R3: file_read → R4: file_edit (순차 — 금지)");
sb.AppendLine(" 좋은 예(GOOD): R1: folder_map + grep + file_read 동시 호출 → R2: file_edit + build_run");
sb.AppendLine("모든 독립적인 읽기/검색 작업은 같은 응답에 배치해야 합니다.");
sb.AppendLine("Prefer targeted grep/glob plus a small number of file reads over broad parallel sweeps.");
sb.AppendLine("");
sb.AppendLine("### [규칙 4] 불확실해도 즉시 도구를 호출하라 — Act on Uncertainty, Do Not Ask");
sb.AppendLine("어떤 파일을 읽어야 할지 모를 때: 즉시 glob이나 grep을 호출하세요.");
sb.AppendLine("의존성이 불확실할 때: 즉시 dev_env_detect를 호출하세요.");
sb.AppendLine("If unsure which file, use glob or grep to narrow the target. If unsure about dependencies or tooling, use dev_env_detect.");
sb.AppendLine("'잘 모르겠습니다, 알려주시겠어요?' — 도구 호출로 답을 구할 수 있다면 절대 사용자에게 묻지 마세요.");
sb.AppendLine("Prefer answering uncertainty with a focused tool call instead of asking avoidable clarification questions.");
sb.AppendLine("");
sb.AppendLine("### [규칙 5] 도구로 검증하라, 가정하지 마라 — Verify with Tools, Not Assumptions");
sb.AppendLine("편집 후에는 반드시 file_read로 최종 상태를 확인하세요. 편집이 성공했다고 가정하지 마세요.");
sb.AppendLine("빌드/테스트 후에는 build_run의 실제 출력을 인용하세요. '정상 작동할 것입니다'라고 말하지 마세요.");
sb.AppendLine("After editing, re-open the changed file when confirmation is helpful. Do not assume a risky edit succeeded.");
sb.AppendLine("When you run build/test, cite the actual build_run output instead of assuming success.");
sb.AppendLine("'완료'의 정의: 빌드 통과 + 테스트 통과 + 편집 파일 재확인 + 결과 보고 — 모두 도구 출력으로 증명된 상태.");
sb.AppendLine("'Done' = build passes + tests pass + edited files re-read + result reported — all proven by tool output.");
sb.AppendLine("");
// 소개 및 메타 정보
sb.AppendLine("---");
sb.AppendLine("You are AX Copilot Code Agent — a senior software engineer for enterprise development.");
sb.AppendLine($"Today's date: {DateTime.Now:yyyy년 M월 d일} ({DateTime.Now:yyyy-MM-dd}).");
sb.AppendLine("Available tools: file_read, file_write, file_edit (supports replace_all), glob, grep (supports context_lines, case_sensitive), lsp_code_intel, folder_map, process, dev_env_detect, build_run, git_tool.");
sb.AppendLine("Do not pause after partial progress. Keep executing consecutive steps until completion or a concrete blocker is reached.");
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above.");
sb.AppendLine("\n## Core Workflow");
sb.AppendLine("1. ORIENT: Pick the smallest next step that can answer the request.");
sb.AppendLine(" - If a specific file is already obvious, start with file_read directly.");
sb.AppendLine(" - If you need definitions, references, symbols, implementations, or call hierarchy, prefer lsp_code_intel before broad searching.");
sb.AppendLine(" - Otherwise use grep/glob to find the smallest relevant set of files.");
sb.AppendLine(" - Use folder_map only when the repository structure itself is unclear.");
sb.AppendLine("2. ANALYZE: Read only the files needed to understand the change.");
sb.AppendLine(" - Check impacted callers/references when changing shared code or public behavior. Use lsp_code_intel when semantic lookup will be more precise than text search.");
sb.AppendLine("3. IMPLEMENT: Apply the smallest safe edit. Use file_edit for existing files and file_write for new files.");
sb.AppendLine("4. VERIFY: Run build_run/test_loop when the change affects buildable or testable behavior, or when the user explicitly asks for verification.");
sb.AppendLine(" - Use git_tool(diff) when it helps confirm the final change set or explain what changed.");
sb.AppendLine("5. REPORT: Summarize what changed, what was verified, and any remaining risk.");
sb.AppendLine("\n## Development Environment");
sb.AppendLine("Use dev_env_detect to check installed IDEs, runtimes, and build tools before running commands.");
sb.AppendLine("IMPORTANT: Do NOT attempt to install compilers, IDEs, or build tools. Only use what is already installed.");
// 패키지 저장소 정보
sb.AppendLine("\n## Package Repositories");
if (!string.IsNullOrEmpty(code.NexusBaseUrl))
sb.AppendLine($"Enterprise Nexus: {code.NexusBaseUrl}");
sb.AppendLine($"NuGet (.NET): {code.NugetSource}");
sb.AppendLine($"PyPI/Conda (Python): {code.PypiSource}");
sb.AppendLine($"Maven (Java): {code.MavenSource}");
sb.AppendLine($"npm (JavaScript): {code.NpmSource}");
sb.AppendLine("When adding dependencies, use these repository URLs.");
// IDE 정보
if (!string.IsNullOrEmpty(code.PreferredIdePath))
sb.AppendLine($"\nPreferred IDE: {code.PreferredIdePath}");
// 사용자 선택 개발 언어
if (_selectedLanguage != "auto")
{
var langName = _selectedLanguage switch { "python" => "Python", "java" => "Java", "csharp" => "C# (.NET)", "cpp" => "C/C++", "javascript" => "JavaScript/TypeScript", _ => _selectedLanguage };
sb.AppendLine($"\nIMPORTANT: User selected language: {langName}. Prioritize this language for code analysis and generation.");
}
// 언어별 가이드라인
sb.AppendLine("\n## Language Guidelines");
sb.AppendLine("- C# (.NET): Use dotnet CLI. NuGet for packages. Follow Microsoft naming conventions.");
sb.AppendLine("- Python: Use conda/pip. Follow PEP8. Use type hints. Virtual env preferred.");
sb.AppendLine("- Java: Use Maven/Gradle. Follow Google Java Style Guide.");
sb.AppendLine("- C++: Use CMake for build. Follow C++ Core Guidelines.");
sb.AppendLine("- JavaScript/TypeScript: Use npm/yarn. Follow ESLint rules. Vue3 uses Composition API.");
// 코드 품질 + 안전 수칙
sb.AppendLine("\n## Code Quality & Safety");
sb.AppendLine("- NEVER delete or overwrite files without user confirmation.");
sb.AppendLine("- ALWAYS read a file before editing it. Don't guess contents.");
sb.AppendLine("- Prefer file_edit over file_write for existing files (shows diff).");
sb.AppendLine("- When porting/referencing external code, do not copy verbatim. Rename and re-structure to match AX Copilot conventions.");
sb.AppendLine("- Use grep to find ALL references before renaming/removing anything.");
sb.AppendLine("- After editing, re-open the changed files and nearby callers to verify the final state, not just the patch intent.");
sb.AppendLine("- Treat verification as incomplete unless you can cite build/test or direct file-read evidence.");
sb.AppendLine("- If unsure about a change's impact, ask the user first.");
sb.AppendLine("- For large refactors, do them incrementally with build verification between steps.");
sb.AppendLine("- Use git_tool action='diff' to review your changes before committing.");
sb.AppendLine("\n## Lint & Format");
sb.AppendLine("After code changes, check for available linters:");
sb.AppendLine("- Python: ruff, black, flake8, pylint");
sb.AppendLine("- JavaScript: eslint, prettier");
sb.AppendLine("- C#: dotnet format");
sb.AppendLine("- C++: clang-format");
sb.AppendLine("Run the appropriate linter via process tool if detected by dev_env_detect.");
if (!string.IsNullOrEmpty(workFolder))
sb.AppendLine($"\nCurrent work folder: {workFolder}");
sb.AppendLine($"File permission mode: {llm.FilePermission}");
sb.Append(BuildSubAgentDelegationSection(true));
// 폴더 데이터 활용
sb.AppendLine("\nFolder Data Usage = ACTIVE.");
sb.AppendLine("Prefer targeted file_read, grep/glob, and lsp_code_intel for narrow requests.");
sb.AppendLine("Use folder_map only when structure is unclear, the user explicitly asks for folder contents, or the request is repo-wide.");
sb.AppendLine("Read only files that are relevant to the current question. Avoid broad codebase sweeps without evidence.");
// 프리셋 시스템 프롬프트
lock (_convLock)
{
if (_currentConversation?.SystemCommand is { Length: > 0 } sysCmd)
sb.AppendLine("\n" + sysCmd);
}
// 프로젝트 문맥 파일 (AGENTS.md) 주입
sb.Append(LoadProjectContext(workFolder));
// 프로젝트 규칙 (.ax/rules/) 자동 주입
sb.Append(BuildProjectRulesSection(workFolder));
// 에이전트 메모리 주입
sb.Append(BuildMemorySection(workFolder));
// 피드백 학습 컨텍스트 주입
sb.Append(BuildFeedbackContext());
return sb.ToString();
}
private static string BuildSubAgentDelegationSection(bool codeMode)
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("\n## Sub-Agent Delegation");
sb.AppendLine("Use spawn_agent only when a bounded side investigation can run in parallel without blocking your own next step.");
sb.AppendLine("Good delegation targets: impact analysis, reference/caller search, test-file discovery, diff review, and bug root-cause investigation.");
sb.AppendLine("Do not delegate the final editing decision, the final report, or blocking work that you must inspect immediately yourself.");
sb.AppendLine("When spawning a sub-agent, give a concrete task with the exact question, likely file/module scope, and the output shape you want.");
sb.AppendLine("Expected sub-agent result shape: conclusion, files checked, key evidence, recommended next action, risks/unknowns.");
sb.AppendLine("Use wait_agents only when you are ready to integrate the result. Keep doing useful local work while the sub-agent runs.");
if (codeMode)
{
sb.AppendLine("For code tasks, prefer delegating read-only investigations such as caller mapping, related test discovery, or build/test failure triage.");
sb.AppendLine("For high-impact code changes, consider delegation when it quickly adds evidence, but do not treat it as mandatory.");
}
else
{
sb.AppendLine("For cowork tasks, consider delegation only when fact gathering or source cross-checking clearly speeds up the work.");
}
return sb.ToString();
}
/// <summary>프로젝트 규칙 (.ax/rules/)을 시스템 프롬프트 섹션으로 포맷합니다.</summary>
private string BuildProjectRulesSection(string? workFolder)
{
if (string.IsNullOrEmpty(workFolder)) return "";
if (!_settings.Settings.Llm.EnableProjectRules) return "";
try
{
var rules = Services.Agent.ProjectRulesService.LoadRules(workFolder);
if (rules.Count == 0) return "";
// 컨텍스트별 필터링: Cowork=document, Code=always (기본)
var when = _activeTab == "Code" ? "always" : "always";
var filtered = Services.Agent.ProjectRulesService.FilterRules(rules, when);
return Services.Agent.ProjectRulesService.FormatForSystemPrompt(filtered);
}
catch
{
return "";
}
}
/// <summary>에이전트 메모리를 시스템 프롬프트 섹션으로 포맷합니다.</summary>
private string BuildMemorySection(string? workFolder)
{
if (!_settings.Settings.Llm.EnableAgentMemory) return "";
var app = System.Windows.Application.Current as App;
var memService = app?.MemoryService;
if (memService == null) return "";
// 메모리를 로드 (작업 폴더 변경 시 재로드)
memService.Load(workFolder ?? "");
var all = memService.All;
var layeredDocs = memService.InstructionDocuments;
if (all.Count == 0 && layeredDocs.Count == 0) return "";
var sb = new System.Text.StringBuilder();
sb.AppendLine("\n## 메모리 계층");
sb.AppendLine("다음 메모리는 claude-code와 비슷하게 관리형 → 사용자 → 프로젝트 → 로컬 순서로 조립됩니다.");
sb.AppendLine("현재 작업 디렉토리에 가까운 메모리가 더 높은 우선순위를 가집니다.\n");
const int maxLayeredDocs = 8;
const int maxDocChars = 1800;
foreach (var doc in layeredDocs.Take(maxLayeredDocs))
{
sb.AppendLine($"[{doc.Label}] {doc.Path}");
var text = doc.Content;
if (text.Length > maxDocChars)
text = text[..maxDocChars] + "\n...(생략)";
sb.AppendLine(text);
sb.AppendLine();
}
if (all.Count > 0)
{
sb.AppendLine("## 학습 메모리 (이전 대화에서 학습한 내용)");
sb.AppendLine("아래는 이전 대화에서 학습한 규칙과 선호도입니다. 작업 시 참고하세요.");
sb.AppendLine("새로운 규칙이나 선호도를 발견하면 memory 도구의 save 액션으로 저장하세요.");
sb.AppendLine("사용자가 이전 학습 내용과 다른 지시를 하면 memory 도구의 delete 후 새로 save 하세요.\n");
foreach (var group in all.GroupBy(e => e.Type))
{
var label = group.Key switch
{
"rule" => "프로젝트 규칙",
"preference" => "사용자 선호",
"fact" => "프로젝트 사실",
"correction" => "이전 교정",
_ => group.Key,
};
sb.AppendLine($"[{label}]");
foreach (var e in group.OrderByDescending(e => e.UseCount).Take(15))
sb.AppendLine($"- {e.Content}");
sb.AppendLine();
}
}
return sb.ToString();
}
}