사고력 답변구조 개발 명세 (FRD)¶
요지¶
v0.2 대화형 사고 유도를 어떻게 구현하는지, "왜·어떻게 동작하는가" 수준에서 정리한다. 엔티티 필드나 SQL, 서비스 클래스명 같은 코드 세부는 자주 바뀌므로 코드 옆 spec 문서(mvp-back:docs/frd/사고력-답변구조/spec·mvp-front:docs/frd/사고력-답변구조/spec)에 두기로 했다(ADR-0004·ADR-0005). 엔티티 자체의 단일 기준은 qna 도메인이다.
상위 PRD¶
기획(대화형 코칭, progression+회귀, 정답 회피 가드)은 prd v0.2이며, v0.1을 뒤집은 변경은 ADR-0009에 있다. 기획이 바뀌면 이 문서도 갱신한다.
기술 접근 · 아키텍처¶
처리 흐름¶
ai-coaching/start: StudentContext 조회 → 시스템 프롬프트 구성(맞춤 설정 반영, Skip 시 일반 힌트) → 세션 생성(READY) → 첫 힌트 생성. 이후 message마다 progression 단계 판단(개념→접근→풀이 힌트→답 직전, 회귀 가능) + 정답 직접 노출 금지 가드. 종료 조건: 학생 종료 / 문제 복귀 유도 / 답 선택 이동 / 이탈 / 세션 만료. attempt당 세션 1개.
트리거·연계¶
- challenge 풀이 중 막힘 →
ChallengeProblemAttempt.qnaContextIdlazy 생성 → 이 답변 구조 위임. 1차 MVP 핵심 채널은 챌린지(챌린지식-풀이). - 선생님 조회는 동일 스터디룸 연결 학생 한정(타 스터디룸 조회 불가) — 권한 경계는 studyroom 도메인.
StudentContext는 학생 1:1. - 이벤트 로그는 코칭의 전 구간에 심는다: 진입(ai_cta_click, ai_setting_modal_view), 설정(ai_setting_submit·skip·edit), 코칭(ai_session_start, ai_message_send, ai_progression_step, ai_regression), 종료(ai_finish, guide_to_problem), 결과(answer_submit_after_ai, feedback_submit), 선생님(teacher_ai_log_view).
API · 데이터 모델¶
데이터 모델 (v0.2)¶
단계형(v0.1)에서 대화형으로 바뀌면서 Q&A 도메인을 확장해야 했다. 핵심은 단계 인덱스(hintLevel)를 대화 turn 인덱스(turnIndex)로 갈아끼우고, 만족도·토큰 사용량 같은 새 엔티티를 더한 것이다. 기존 데이터를 깨지 않으려고 마이그레이션은 모두 추가 전용으로 짰다(Flyway V40~V44).
| 엔티티 | v0.2 처리 |
|---|---|
QnaMessage |
hintLevel deprecated + turnIndex 신규 (multi-turn 인덱스) |
QnaContext |
ai_status (NONE/IN_PROGRESS/RESOLVED) — AI 기록 정렬용 |
StudentContext |
strengths·weaknesses deprecated + difficulties 신규 |
AiSatisfactionRating |
신규 — qnaContextId, score 1-5, comment |
AiPromptTemplate |
hint.level1~5 키 deprecated + conversation.system·conversation.turn 신규 시드 |
AiTokenUsage |
호출당 model·prompt/completion tokens·cost 기록 |
API (동작 계약)¶
API 명세가 두 곳에 흩어져 있다 — qna 도메인의 path가 단일 기준이고, AI 코치 v2 FDD는 같은 동작을 "코칭 세션" 관점으로 다르게 적었다. 구현할 때 헷갈리지 않도록, 두 명세가 가리키는 같은 동작을 아래 한 표로 맞춰 둔다.
| 동작 | 위키 path | FDD v2 path | 요청 핵심 | 응답 핵심 |
|---|---|---|---|---|
| 맞춤 설정 저장/수정 | GET·PUT /qna/me/context |
POST /student-context |
learningStage·learningGoal·difficultArea·customText | StudentContext (Skip 시 생성 안 함) |
| 코칭 시작 | (ai-turn 최초) | POST /ai-coaching/start |
challengeAttemptId | {sessionId, status: READY} + 첫 힌트 |
| 대화 진행 | POST /qna/{id}/ai-turn |
POST /ai-coaching/message |
content(≤1,000자) | {reply, status} |
| 종료 | (status 전환) | PATCH /ai-coaching/:id/finish |
— | {status: FINISHED} → 문제 복귀·답 선택 유도 |
| 만족도 | POST /qna/{id}/ai-rating |
(challenge-feedback) | score 1-5, comment(opt) | 저장 |
| 선생님 조회 | GET /teacher/.../ai-log |
GET /teacher/student-ai-log/:studentId |
— | 전체 로그(동일 스터디룸 학생 한정) |
- 레이트 리밋: 대화 메시지 1분당 10회, 길이 1,000자.
- Deprecated:
POST /api/v1/qna/{id}/ai-hint(단계형 v0.1, 12개월 후방 호환).
의존성 · 영향 범위¶
질문 도메인(qna, 엔티티 기준)과 챌린지(challenge, 막힘 트리거), 권한 경계(studyroom)에 걸쳐 있다. 같은 테이블을 Q&A와 공유하므로, 스키마를 바꾸면 양쪽 모두에 영향이 간다.
테스트 계획¶
공통 전략은 qa-playbook을 따른다. 이 기능에서 가장 중요한 건 정답 회피 가드다 — "답 알려주세요"를 아무리 반복해도 정답 번호나 값이 새어 나가면 안 된다. 마찬가지로 "모르겠어요"를 반복하면 같은 설명을 되풀이하지 않고 개념 단계로 회귀하는지, 레이트 리밋(1분 10회·1,000자)이 잘 막는지, 선생님 AI 로그 조회가 같은 스터디룸 학생으로만 제한되는지를 본다.
예외 처리도 미리 정해 뒀다. 맞춤 설정을 Skip하면 일반 힌트로, 학생이 응답을 안 하면 WAITING으로 대기, AI에 오류가 나면 "잠시 문제가 발생했어요", 로그인이 만료되면 재로그인 후 세션 복구(선택)로 흐른다. 모든 LLM 호출은 토큰 사용량(AiTokenUsage)과 마스킹된 요청/응답 로그로 남긴다. RAG 개인화나 자동 약점 분석, 정답 직접 제공, 드로잉 풀이 해석 등은 이번 MVP에서 뺐고, LLM provider 선정과 프롬프트 저장 위치는 별도 ADR로 정한다.
작업 분해¶
완료 추적은 GitHub 이슈에서 한다. 방향을 바꾼 결정은 ADR-0009(단계형 → 대화형), FRD 폴더 구조와 versioning은 ADR-0005에 있다.