package antigravity import ( "bytes" "context" "fmt" "os/exec" "strings" ) // StudyElement represents the extracted study content from a question. type StudyElement struct { QuestionID int `json:"questionId"` Source string `json:"source"` Category string `json:"category"` Content string `json:"content"` // Markdown content containing formulas, charts, etc. } // GeneratedQuestion matches the structure to be saved in QuizQuestions DB. type GeneratedQuestion struct { QuestionText string `json:"questionText"` Choice1 string `json:"choice1"` Choice2 string `json:"choice2"` Choice3 string `json:"choice3"` Choice4 string `json:"choice4"` CorrectAnswer int `json:"correctAnswer"` CorrectAnswerStr string `json:"correctAnswerStr"` Explanation string `json:"explanation"` Difficulty string `json:"difficulty"` Subject string `json:"subject"` } // CallAgy runs the agy CLI with the given prompt. func CallAgy(ctx context.Context, prompt string) (string, error) { cmd := exec.CommandContext(ctx, "agy", "--dangerously-skip-permissions", "--print", prompt) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { return "", fmt.Errorf("agy error: %v, stderr: %s", err, stderr.String()) } return strings.TrimSpace(stdout.String()), nil } // ExtractStudyElement requests the AI to generate a comprehensive study sheet/note for a specific quiz question. func ExtractStudyElement(ctx context.Context, questionText, choice1, choice2, choice3, choice4, correctAnswer, explanation string) (string, error) { prompt := fmt.Sprintf("아래 기출문제의 문제, 보기, 정답, 해설을 철저히 분석하여 핵심 개념을 담은 \"학습요소(개념 정리)\"를 작성해 주세요.\n"+ "이 학습요소는 사용자가 이 문제를 완벽하게 이해하고 관련된 이론적 배경까지 학습할 수 있도록 돕는 학습 노트입니다.\n\n"+ "[기출문제]\n"+ "문제: %s\n"+ "보기 1: %s\n"+ "보기 2: %s\n"+ "보기 3: %s\n"+ "보기 4: %s\n"+ "정답: %s\n"+ "해설: %s\n\n"+ "[작성 지침]\n"+ "1. **매우 중요**: 문서의 가장 첫 번째 줄은 반드시 핵심 학습개념/키워드를 설명하는 제목으로 시작해 주세요. (예: `# AES 암호화 알고리즘` 또는 `# OSI 7계층과 캡슐화`)\n"+ " - 첫 줄 외에 다른 서두(예: \"네, 아래는 개념 정리입니다\" 등)는 일절 포함하지 마십시오.\n"+ "2. 단순 정답 해설을 넘어, 문제에 포함된 핵심 개념의 정의, 특징, 작동 방식, 장단점을 일목요연하게 정리해 주세요. (표, 리스트 적극 활용)\n"+ "3. **KaTeX 문법 규칙**: 수식 작성 시 파싱 오류를 철저히 방지하세요. 괄호 매칭을 완벽히 지키고, 수식 내부에 한글을 포함해야 하는 경우 반드시 `\\text{한글}` 형식을 사용하세요. 인라인 수식은 `$수식$`, 디스플레이 수식은 `$$수식$$`로만 작성하세요.\n"+ "4. **Mermaid 문법 규칙**: Mermaid 버전 10.9.6 문법을 엄격히 지켜 `syntax error in text mermaid` 에러를 예방하세요.\n"+ " - `subgraph` 이름(타이틀)에는 공백이나 한글, 괄호가 들어가므로 **반드시** 쌍따옴표로 감싸서 선언하세요. (예: `subgraph \"정상 스택 (Normal)\"`)\n"+ " - 화살표 연결선의 텍스트 라벨 `|설명|` 안에는 괄호 `()`, 슬래시 `/`, 백슬래시 `\\` 등의 특수문자를 절대 쓰지 마세요. (예: `-->|SMTP 프로토콜 포트 25|` 로 표현)\n"+ " - 노드 내부에 한글, 공백, 기호, 괄호가 포함되는 경우 **반드시** 쌍따옴표로 감싸서 선언하세요 (예: `A[\"AES 암호화\"] --> B[\"키 생성\"]`).\n"+ " - 연결 화살표 기호는 오직 `-->` 또는 `-.->` 만 사용해야 하며, `->` 나 `- >` 처럼 깨진 형식을 쓰지 마세요.\n"+ " - 서브그래프(subgraph)를 열었다면 반드시 `end`로 닫아 마감하세요.\n"+ " - 백슬래시(`\\`)는 이스케이프 오류 방지를 위해 두 번(`\\\\`) 겹쳐 적으세요.\n"+ "5. 다른 불필요한 설명은 제외하고, 오직 첫 줄 개념명(# ...)으로 시작하는 마크다운 본문만 반환해 주세요.", questionText, choice1, choice2, choice3, choice4, correctAnswer, explanation) return prompt, nil } // GenerateDescriptiveQuestion generates a new descriptive/practical exam question based on given reference questions. func GenerateDescriptiveQuestion(ctx context.Context, referenceExams []string) (*GeneratedQuestion, error) { // We will create the prompt text in quiz_service.go using callAIEngine. // This function is kept for backwards compatibility but we will format the prompt inside quiz_service to leverage both API and agy engines. return nil, fmt.Errorf("use the prompt constructor and callAIEngine helper instead") }