- eval/run_ragas.py: collect contexts (RetrieverService) + answers (/chat API), evaluate with faithfulness / answer_relevancy / context_recall / context_precision - eval/dataset.jsonl: 5 Korean Q&A pairs for initial evaluation - eval/requirements.txt: ragas==0.2.9, datasets, langchain-google-vertexai - Evaluator LLM priority: OpenAI > Anthropic > local Qwen3 - Runtime shim for ragas 0.2 / langchain-community 0.4+ vertexai incompatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
25 KiB
율봇 개발 로드맵
현재 구현 상태
| 영역 | 현황 |
|---|---|
| LLM | Qwen3-8B-4bit (MLX, Apple Silicon) |
| Agent | LangGraph ReAct + Tool Calling + Thinking 모드 |
| RAG | Qdrant + BAAI/bge-m3 임베딩 + Semantic Chunking (SemanticChunker) + Reranker (BAAI/bge-reranker-v2-m3) |
| Tools | search_documents, web_search, get_current_date, remember_user_info, recall_user_info (5개) |
| Feedback | Gradio 👍/👎 → td_feedback DB 저장 + LangSmith create_feedback() 연동 |
| UI | CLI + Gradio Web UI + 음성 입력(STT)/출력(TTS) |
| Memory | LangGraph MemorySaver (세션 내) + MySQL 대화 저장 + 장기 사용자 프로필 |
| Tracing | LangSmith 트레이싱 |
| Streaming | 비동기 토큰 스트리밍 + 타입별 이벤트 분리 (__meta / __thinking / __status) |
| 사고 과정 UI | 스트리밍 중 현재 줄 실시간 표시 → 완료 후 접기/펼치기 (<details>) |
| History Compact | 대화 20턴 초과 시 오래된 절반을 LLM으로 자동 요약 (CompactService) |
| 나이 계산 | 시스템 프롬프트에 오늘 날짜 주입 + 한국 나이/만 나이 자동 계산 |
버그 수정 현황
✅ 버그 1 — RAG 중복 수집 (수정 완료)
IngestionService._delete_by_source()를 구현해 같은 파일 경로로 저장된 기존 청크를 ingest() 시작 시 삭제한다.
✅ 버그 2 — LangGraph MemorySaver와 MySQL 이력 미연동 (수정 완료)
AgentService.__init__에서 MySQL에 저장된 최근 10턴을 _pending_history로 불러온 뒤, 첫 stream_response() 호출 시 LangGraph 초기 메시지로 주입한다.
✅ 버그 3 — 단일 사용자 전제 (수정 완료)
DB 스키마(td_conversations.user_id, td_user_profile.user_id)는 _migrate_schema로 자동 마이그레이션. AgentService에 user_id 파라미터 추가, 모든 Repository 호출에 전파. Gradio에 사용자 선택 드롭다운(아록/근혜/도율/하율) 추가 및 사용자별 에이전트 캐시 구현.
✅ 버그 4 — 나이 계산 오류 (수정 완료)
LLM이 훈련 데이터 기준 연도로 나이를 계산하는 문제. AgentService.call_model()에서 매 호출 시 시스템 프롬프트 앞에 오늘 날짜: {date.today().isoformat()}를 주입. 프로필에서 생년월일/생년 값을 파싱해 한국 나이(현재연도-출생연도+1)와 만 나이(생일 기준 정확 계산)를 자동 계산해 시스템 프롬프트에 포함.
✅ 버그 6 — TTS가 진행 메시지까지 읽는 문제 (수정 완료)
stream_response()가 [LangGraph → agent: ...], 문서 검색 중... 등 진행 메시지와 실제 답변을 동일한 plain string으로 yield해 TTS가 전부 읽던 문제.
stream_response()yield 타입 분리: 답변 →plain str, 진행/thinking/출처 →{"__meta": str}dict- thinking 토큰은 별도
{"__thinking": str}key 사용 call_model시작 직후writer({"__start": True})emit →{"__status": label}변환으로 LLM 추론 전 즉각 피드백api.py:json.dumps(token)이 dict/str 모두 처리하므로 변경 없음- WebUI
respond():tts_text누적 변수 분리,__meta·__thinking토큰 제외 후 TTS 전달 - Telegram
bot.py:__meta·__thinking토큰 skip
✅ 버그 5 — 사고 과정(thinking) 체크박스 무효 (수정 완료)
ON/OFF와 무관하게 사고 과정이 표시되지 않던 버그.
call_model내부에서get_stream_writer()로 thinking 토큰을 custom 이벤트로 emit → 답변 앞에 먼저 스트리밍- 체크박스 값을 LangGraph configurable →
llm_with_tools.bind(enable_thinking=...)로 모델 레벨까지 전달 (.envENABLE_THINKING설정과 독립) stream_response루프를stream_mode=["messages", "custom"]이중 스트림으로 전환self._think_verbose인스턴스 변수 참조 버그 수정 (_think_verbose로컬 변수 사용)
✅ Phase 4 — Web UI (Gradio)
app.py— Gradio ChatInterface +stream_response()연결- PDF/TXT 파일 업로드 → 인제스트 버튼
- 사고 과정(thinking) 표시 토글
- 대화 초기화 버튼
✅ Phase 5 — 장기 사용자 메모리
- MySQL
td_user_profile테이블 + Tool 2개 등록 remember_user_info(key, value)— 영구 저장 (아이 생년, 재정 목표 등)recall_user_info(key)— 이전 저장 정보 조회UserProfileRepository(services/db/user_profile_repository.py)
✅ Phase 6 — 실시간 웹 검색 Tool
web_search(query)— DuckDuckGo (무료, API 키 불필요)- 최신 금리, 육아 정책, 뉴스 등 실시간 정보 검색 가능
✅ Phase 7 — LangSmith 트레이싱
.env에서LANGCHAIN_TRACING_V2=true+LANGCHAIN_API_KEY설정으로 활성화- Tool Call 실패 원인, RAG 청크 내용, 에이전트 루프 흐름 시각화 가능
✅ Phase 9 — 문서 관리
IngestionService._delete_by_source()— 파일 경로 기반 중복 청크 삭제RetrieverService.list_documents()— Qdrant scroll로 고유 source 목록 반환RetrieverService.delete_document(source)— source 기준 청크 전체 삭제- Gradio "문서 관리" 탭 — 목록 테이블 + 경로 입력 삭제 버튼 + 앱 로드 시 자동 새로고침
✅ Phase 10 — 멀티유저 지원
Bug 3 수정 및 Phase 9 작업과 함께 완전 구현됨.
- DB 마이그레이션:
mysql_service._migrate_schema()가td_conversations,td_user_profile양쪽에user_id컬럼 자동 추가 ConversationRepository:create_conversation(user_id)/get_latest_conversation_id(user_id)— user_id 기반 격리AgentService:user_id파라미터 추가, 모든 프로필·대화 조회에 전파make_memory_tools(profile_repo, user_id): remember/recall 도구가 올바른 사용자 데이터만 접근- Gradio: 사용자 선택 드롭다운(아록/근혜/도율/하율, 기본값 아록) +
_agent_cache사전으로 사용자별 에이전트 분리
✅ Phase 11 — 대화 이력 복원
버그 2와 함께 해결됨. AgentService 초기화 시 MySQL에서 최근 10턴을 _pending_history에 로드 → 첫 메시지와 함께 LangGraph에 주입.
turns = conversation_repository.load_turns_after(self._conv_id, None, limit=10)
# → HumanMessage / AIMessage 변환 후 _pending_history에 저장
✅ Phase 12 — 답변 피드백 & 품질 개선
배경: 에이전트가 잘못된 답변을 해도 피드백 루프가 없어 개선이 어려움.
구현 내용:
- Gradio Chatbot 메시지마다 👍 / 👎 버튼 (
chatbot.like()이벤트) td_feedback테이블에user_id, 질문, 답변, 평점 저장 (FeedbackRepository)AgentService에서 응답마다run_id(UUID)를 LangChain config에 주입 →last_run_idproperty로 노출run_ids_state(gr.State)로 대화 턴별run_id추적- LangSmith
Client().create_feedback()연동 (트레이싱 활성화 시 자동 기록)
난이도: 중간 | 임팩트: 중간 (장기 품질 향상)
✅ Phase 13 — RAG 품질 향상 ★★★ (완료)
배경: 고정 크기 청킹 + 벡터 유사도 검색만으로는 관련 없는 청크가 섞일 수 있음.
✅ Semantic Chunker — 완료
커스텀 _SemanticSplitter를 제거하고 langchain_experimental.SemanticChunker로 교체 (services/rag/ingestion_service.py).
기존에 무시되던 semantic_breakpoint_threshold_type 설정이 이제 실제로 적용된다.
| 기능 | 지원 여부 |
|---|---|
| breakpoint_threshold_type | ✅ percentile / standard_deviation / interquartile / gradient |
| buffer_size | ✅ SEMANTIC_BUFFER_SIZE 환경변수로 설정 |
| min_chunk_size | ✅ (SemanticChunker 기본 지원) |
| HuggingFaceEmbeddings 재사용 | ✅ 기존 임베딩 모델 그대로 사용 |
langchain-experimental 패키지 상태:
langchain-experimentalv0.4.2는 공식 유지보수 종료가 선언됐지만(#87),
SemanticChunker자체는 현재 정상 동작하며 후속 패키지(langchain-text-splitters)로 이전 완료 시 migration 예정.
✅ 미완 1 — Semantic Chunker 기능 완성 (완료)
기존 Qdrant 저장 문서는 재등록해야 새 청킹 방식이 적용됨.
난이도: 중간 | 임팩트: 중간 (답변 정확도 향상)
✅ Phase 14 — 음성 인터페이스
배경: 육아 중에는 손이 자유롭지 않아 타이핑이 어려움.
구현 내용:
openai-whisper(small 모델) — 마이크 녹음 → 한국어 텍스트 변환, 지연 로딩- macOS
say -v Yuna— 에이전트 응답을 음성으로 읽어줌 (aiff 파일 경유) - Gradio "대화" 탭 확장 — 마이크 녹음 + "음성→텍스트 변환" 버튼 + "음성으로 답변 읽기" 체크박스 + TTS 오디오 플레이어
- LLM/Agent 레이어 변경 없음 — 순수 I/O 어댑터로 구현
config.py 추가: whisper_model_size = "small", tts_voice = "Yuna"
난이도: 중간 | 임팩트: 높음 (핵심 사용 시나리오)
✅ Phase 13-B — Reranker ★★☆
배경: 벡터 유사도 검색은 의미적으로 비슷한 청크를 가져오지만, 질문과 실제로 관련 있는 청크를 정확히 가려내지 못하는 경우가 있다. Reranker는 검색 후 순위를 재조정해 LLM에 전달되는 컨텍스트 품질을 높인다.
구현 내용:
services/rag/rerank_service.py—RerankService클래스 (Cross-Encoder 래퍼)RetrieverService.search(): reranker 활성화 시rerank_fetch_k(기본 10)개 후보 검색 → rerank → 상위rag_top_k(기본 3)개 반환tools.pymake_retriever_tool:as_retriever()→search()직접 호출로 변경 (reranker 자동 적용).envRERANKER_ENABLED=true로 활성화, 기본 비활성 (첫 실행 시 모델 다운로드)
| 설정 | 기본값 | 설명 |
|---|---|---|
RERANKER_ENABLED |
false |
true로 설정 시 활성화 |
RERANKER_MODEL_ID |
cross-encoder/mmarco-mMiniLMv2-L12-H384-v1 |
한국어 포함 다국어 모델 (117MB) |
RERANKER_FETCH_K |
10 |
rerank 전 벡터 검색 후보 수 |
난이도: 중간 | 임팩트: 높음 (관련성 낮은 청크 필터링 → 답변 정확도 향상)
✅ Phase 18 — Hybrid Search (BM25 + Vector) ★★☆
배경: 한국어 질문에서 고유명사·전문용어가 포함된 경우 의미 검색(Dense)만으로는 recall이 떨어진다. BM25 키워드 검색과 결합(Hybrid)하면 보완이 가능하다.
구현 내용:
FastEmbedSparse(model_name="Qdrant/bm25")— 언어 무관 BM25 sparse 임베딩 (fastembed패키지)IngestionService:HYBRID_SEARCH_ENABLED=true시 dense + sparse 동시 저장 (RetrievalMode.HYBRID)RetrieverService: hybrid 스토어로 검색 → Qdrant 내장 RRF로 결과 통합; sparse vector 미설정 컬렉션은 dense로 자동 폴백_ensure_collection_schema(): hybrid 전환 시 스키마 불일치 컬렉션 자동 재생성 (기존 문서 재수집 필요).envHYBRID_SEARCH_ENABLED=true로 활성화, 활성화 후 기존 문서 재수집 필요
| 설정 | 기본값 | 설명 |
|---|---|---|
HYBRID_SEARCH_ENABLED |
false |
true로 설정 시 활성화 |
SPARSE_MODEL_ID |
Qdrant/bm25 |
fastembed sparse 모델 (첫 실행 시 자동 다운로드) |
난이도: 중간 | 임팩트: 높음 (키워드 포함 질문 recall 대폭 향상)
✅ Phase 19 — Query Rewriting ★☆☆
배경: 사용자 구어체 질문("아이가 밥을 안 먹어요")은 벡터 검색에 최적화되어 있지 않다. LLM이 검색 전에 질문을 재작성하면 관련 문서 검색 확률이 높아진다.
구현 내용:
- LangGraph 그래프에
query_rewrite노드 추가 —agent → query_rewrite → tools순서 search_documents호출 시에만 작동하는 조건부 라우팅 (route_after_agent): 다른 도구 호출이나 tool 없음 케이스는 그대로 통과- 구어체 → 키워드 중심 쿼리로 변환 + 대명사·지시어를 구체적 명칭으로 해소 (이전 대화 2턴 컨텍스트 활용)
tools_condition제거 → 커스텀route_after_agent함수로 대체- 변환 결과를 custom stream 이벤트로 emit →
RAG_VERBOSE=true시쿼리 최적화: "원본" → "최적화"출력 .envQUERY_REWRITE_ENABLED=true로 활성화
난이도: 하 | 임팩트: 중간 (구어체 질문 검색 품질 향상)
✅ Phase 21 — Telegram Bot ★★☆
배경: Gradio Web UI는 브라우저에서만 사용 가능. 텔레그램으로 이동 중에도 율봇과 대화하고 싶음.
구현 방식: youlbot REST API(Phase 22) 호출 — youlbot-telegram/ 별도 프로젝트로 분리.
youlbot-telegram/
├── bot.py ← Application (python-telegram-bot >= 20.0, async)
│ ├── /start, /reset CommandHandler
│ └── MessageHandler → api_client.chat() → edit_message_text() (타이핑 효과)
├── api_client.py ← httpx 기반 REST API 클라이언트 (chat/reset)
├── .env ← TELEGRAM_BOT_TOKEN, YOULBOT_API_URL, 유저 ID 매핑
└── requirements.txt
구현 내용:
python-telegram-bot>=20.0(asyncio 기반)youlbot-telegram/bot.py— 새 진입점 (python bot.py로 실행)/start— 환영 메시지 + 매핑된 youlbot 사용자 이름 표시/reset—api_client.reset(user_id)호출로 대화 이력 초기화- 일반 메시지 →
api_client.chat()SSE 스트리밍 → 0.6초 간격 실시간 편집 - Telegram numeric ID → youlbot user_id
.env매핑 (USER_아록_TELEGRAM_ID등) - 미등록 사용자에게 Telegram ID 안내 메시지 표시
실행 방법:
cd youlbot-telegram
python bot.py
난이도: 중간 | 임팩트: 높음 (모바일·이동 중 접근)
✅ Phase 22 — REST API (FastAPI) ★★☆
배경: 다른 Python 스크립트나 원격 서버에서 율봇을 호출하려면 HTTP API가 필요하다.
Telegram Bot을 별도 프로젝트로 분리해 이 API를 호출하는 구조로 사용 가능.
구현 내용:
api.py— FastAPI 앱,uvicorn api:app --host 0.0.0.0 --port 8000으로 실행- SSE(
text/event-stream) 스트리밍: 각 라인data: <JSON 토큰>\n\n, 종료data: [DONE]\n\n - Bearer Token 인증 (
.envAPI_TOKEN설정; 빈 값이면 개발 모드 무인증) user_id파라미터로 멀티유저 지원 (기존 DB·메모리 구조 그대로 재사용)
| 엔드포인트 | 설명 |
|---|---|
GET /health |
헬스체크 |
POST /chat |
SSE 스트리밍 대화 (message, user_id, show_thinking) |
POST /reset |
대화 이력 초기화 (user_id) |
POST /ingest |
PDF/TXT 파일 업로드 → 벡터DB 수집 |
GET /documents |
등록 문서 목록 |
DELETE /documents/{source} |
문서 삭제 |
클라이언트 예시 (별도 Telegram 봇 프로젝트):
import httpx, json
API_URL = "http://192.168.10.x:8000"
HEADERS = {"Authorization": "Bearer YOUR_TOKEN"}
async def ask_youlbot(message: str, user_id: str) -> str:
full = ""
async with httpx.AsyncClient(timeout=120) as client:
async with client.stream("POST", f"{API_URL}/chat",
json={"message": message, "user_id": user_id},
headers=HEADERS) as r:
async for line in r.aiter_lines():
if line.startswith("data: ") and line != "data: [DONE]":
full += json.loads(line[6:])
return full
난이도: 중간 | 임팩트: 높음 (확장성·외부 연동)
✅ Phase 23 — WebUI 분리 (youlbot-webui 별도 프로젝트) ★★☆
배경: 현재 app.py(Gradio)는 container.py를 직접 import해 서비스를 사용한다.
REST API(Phase 22)를 완성했으므로, WebUI를 독립 프로젝트로 분리해 API만 호출하도록 변경한다.
분리 후 youlbot은 순수 백엔드(API 서버)로만 동작하며, Telegram Bot과 WebUI가 모두 같은 API를 공유한다.
구현 내용:
① youlbot/api.py 보완
POST /feedback엔드포인트 추가 (FeedbackRepository 노출 + LangSmith 연동)/chatSSE 마지막 이벤트에run_id포함 → 피드백 연결 가능data: {"__done": true, "run_id": "uuid"}
② 신규 프로젝트 youlbot-webui/
youlbot-webui/
├── app.py ← Gradio UI (REST API 호출 방식으로 재작성)
├── api_client.py ← httpx 기반 API 클라이언트 (chat/reset/ingest/documents/feedback)
├── .env ← YOULBOT_API_URL, YOULBOT_API_TOKEN
├── .env.example
└── requirements.txt ← gradio, httpx, python-dotenv, openai-whisper
| 기존 app.py (container 직접 사용) | 변경 후 (API 클라이언트) |
|---|---|
container.ingestion_service() |
api_client.ingest(path) |
agent.stream_response() |
api_client.chat(msg, user_id) |
retriever.list_documents() |
api_client.list_documents() |
feedback_repo.save_feedback() |
api_client.save_feedback(...) |
| STT (Whisper) | 변경 없음 — WebUI 로컬 실행 유지 |
| TTS (macOS say) | 변경 없음 — WebUI 로컬 실행 유지 |
실행 방법:
# 백엔드
cd youlbot && uvicorn api:app --host 0.0.0.0 --port 8000
# WebUI (별도 터미널, 별도 프로젝트)
cd youlbot-webui && python app.py
기존 youlbot/app.py는 레거시 직접 실행 옵션으로 보존.
난이도: 중간 | 임팩트: 높음 (백엔드/프론트엔드 완전 분리, 다중 클라이언트 지원)
✅ Phase 24 — 사고 과정 UI 분리 & 실시간 피드백 ★★☆
배경: 사고 과정(thinking)·진행 로그가 답변과 섞여 출력되고, 10초 동안 아무 피드백 없이 대기하는 UX 문제.
구현 내용:
① 스트리밍 토큰 타입 분리 (youlbot/services/agent/agent_service.py)
- 답변:
yield str(기존 그대로) - 진행 메시지(
[LangGraph → ...],문서 검색 중...등):yield {"__meta": str} - 사고 과정 내용:
yield {"__thinking": str} - LLM 추론 시작 즉시:
writer({"__start": True})→yield {"__status": label}으로 변환
② 사고 과정 전용 박스 (youlbot-webui/app.py)
| 단계 | 표시 방식 | 비고 |
|---|---|---|
| 전송 즉시 | 🤔 질문을 분석하고 있습니다... |
단순 div, LLM 추론 전 즉각 표시 |
| 스트리밍 중 | 🤔 분석 중... + 현재 줄 |
plain <div>, 새 줄 도착 시 이전 줄 교체 |
| 진행 로그 | 🤔 분석 중... + 로그 메시지 |
__meta 토큰 전체를 표시 |
| 완료 | 💭 분석 완료 ▶ |
<details> 로 전환, 클릭 시 전체 내용 펼침 |
- 스트리밍 중
<details>미사용 → 내용 업데이트 시 닫힘 현상 없음 - TTS는 순수 답변 토큰만 읽음 (
__meta·__thinking제외) - 챗봇에는 답변만 표시 (진행 메시지 숨김)
show_thinking체크박스 기본값 ON으로 변경
③ 멀티클라이언트 대응
youlbot-telegram/bot.py:__meta·__thinking토큰 skip → 순수 답변만 스트리밍asyncio.get_event_loop().run_until_complete()→asyncio.run()전체 교체 (AnyIO 워커 스레드 호환)
난이도: 중간 | 임팩트: 높음 (UX 대폭 개선)
✅ Phase 25 — RAG 출처 전용 접기/펼치기 박스
배경: RAG 검색 출처가 사고 과정(thinking)과 같은 __meta 토큰으로 섞여 "💭 분석 완료" 박스 안에 표시되던 문제.
구현 내용:
agent_service.py: 출처를{"__meta": "..."}개별 토큰 대신{"__sources": [{filename, page}, ...]}단일 토큰으로 yieldyoulbot-webui/app.py:_sources_html()헬퍼 추가 —<details>기반 접기/펼치기- chatbot 바로 아래
source_box = gr.HTML()컴포넌트 추가 respond()에서__sources토큰 처리 → 답변 완료 후 "📄 출처 (N개)" 박스 표시
youlbot-telegram/bot.py:__sources토큰 skip 처리 추가
난이도: 하 | 임팩트: 중간 (UX 개선 — 출처와 사고 과정 분리)
✅ Phase 20 — RAG 품질 자동 평가 (RAGAS) ★☆☆
배경: 청킹 전략·검색 파라미터·Reranker 변경 시 답변 품질이 실제로 나아졌는지 수치로 확인할 방법이 없다.
구현 내용:
eval/
├── dataset.jsonl ← 평가용 Q&A 쌍 (질문·정답 — 필요 시 수정)
├── run_ragas.py ← 평가 실행 스크립트
├── requirements.txt ← ragas==0.2.9, datasets, langchain-google-vertexai
└── results/ ← report_YYYYMMDD_HHMMSS.{csv,json} 저장
평가 지표:
| 지표 | 설명 |
|---|---|
faithfulness |
답변이 검색 컨텍스트에 충실한가 (환각 탐지) |
answer_relevancy |
답변이 질문에 얼마나 관련 있는가 |
context_recall |
컨텍스트가 정답에 필요한 정보를 포함하는가 |
context_precision |
검색된 컨텍스트 중 실제 유용한 비율 |
평가 LLM 우선순위: OpenAI GPT-4o-mini > Anthropic Claude Haiku > 로컬 Qwen3
실행 방법:
# API 서버 실행 후
python eval/run_ragas.py
python eval/run_ragas.py --dataset eval/dataset.jsonl --api http://localhost:8000
호환성 처리: ragas 0.2가 langchain-community 0.4+에서 ChatVertexAI 임포트 실패하는 문제를 런타임 shim으로 우회.
난이도: 중간 | 임팩트: 중간 (장기 품질 관리 기반)
Phase 15 — 모델 선택 (Claude API / OpenAI 옵션) ★☆☆
배경: 로컬 MLX 모델은 Apple Silicon 전용. 원격 접속 시나리오나 더 높은 품질이 필요할 때 Claude API/OpenAI를 선택할 수 있으면 유연성 확보.
구현 방식: config.py에 model_provider 추가, container.py에서 provider별 chat_model 분기.
model_provider: str = "mlx" # "mlx" | "claude" | "openai"
난이도: 중간 | 임팩트: 중간
Phase 16 — Docker 컨테이너화 ★☆☆
배경: 현재 로컬 전용. 가족이나 지인도 쓸 수 있도록 서버 배포 가능한 형태로 패키징.
구현 범위:
docker-compose.yml
├── youlbot (Gradio app)
├── qdrant
└── mysql
주의: MLX는 Apple Silicon 전용이라 서버 배포 시 Phase 15(모델 선택)이 선행되어야 함.
난이도: 높음 | 임팩트: 중간
Phase 17 — 멀티모달 이미지 이해 ★☆☆
배경: 이유식 사진 → 재료 분석, 금융 서류 사진 → 내용 해석 등.
제약: Qwen3-14B는 이미지 미지원 → mlx-community/Qwen2.5-VL-7B-Instruct-4bit 교체 필요.
난이도: 높음 | 임팩트: 높음 (장기 과제)
추천 진행 순서
단기 (1~2주) 중기 (1개월) 장기
──────────────────────── ────────────────────── ──────────────────
Phase 20 RAGAS 평가 → Phase 15 (모델선택) → Phase 16 (Docker)
→ Phase 17 (멀티모달)
우선순위 매트릭스
| Phase | 상태 | 난이도 | 임팩트 | 추천 순위 |
|---|---|---|---|---|
| 버그 1 RAG 중복 | ✅ 완료 | — | — | — |
| 버그 2 이력 미연동 | ✅ 완료 | — | — | — |
| 버그 3 단일 사용자 | ✅ 완료 | — | — | — |
| 버그 4 나이 계산 오류 | ✅ 완료 | — | — | — |
| 버그 5 thinking 체크박스 무효 | ✅ 완료 | — | — | — |
| 버그 6 TTS 메타 토큰 혼재 | ✅ 완료 | — | — | — |
| Phase 4 Web UI | ✅ 완료 | — | — | — |
| Phase 5 장기 사용자 메모리 | ✅ 완료 | — | — | — |
| Phase 6 웹 검색 | ✅ 완료 | — | — | — |
| Phase 7 LangSmith 트레이싱 | ✅ 완료 | — | — | — |
| Phase 9 문서 관리 | ✅ 완료 | — | — | — |
| Phase 10 멀티유저 | ✅ 완료 | — | — | — |
| Phase 11 이력 복원 | ✅ 완료 | — | — | — |
| Phase 12 피드백 | ✅ 완료 | — | — | — |
| Phase 13 Semantic Chunker | ✅ 완료 | — | — | — |
| Phase 14 음성 인터페이스 | ✅ 완료 | — | — | — |
| Phase 13-B Reranker | ✅ 완료 | — | — | — |
| Phase 18 Hybrid Search | ✅ 완료 | — | — | — |
| Phase 19 Query Rewriting | ✅ 완료 | — | — | — |
| Phase 21 Telegram Bot | ✅ 완료 | — | — | — |
| Phase 22 REST API | ✅ 완료 | — | — | — |
| Phase 23 WebUI 분리 | ✅ 완료 | — | — | — |
| Phase 24 사고 과정 UI 분리 | ✅ 완료 | — | — | — |
| Phase 25 RAG 출처 전용 박스 | ✅ 완료 | — | — | — |
| Phase 20 RAGAS 평가 | ✅ 완료 | — | — | — |
| Phase 15 모델 선택 | 🔲 미완 | 중간 | 중간 | 4순위 |
| Phase 16 Docker | 🔲 미완 | 높음 | 중간 | 5순위 |
| Phase 17 멀티모달 | 🔲 미완 | 높음 | 높음 | 6순위 |