Files
youlbot/docs/01-plan/features/phase17-multimodal.plan.md
T
shinalok 68f741af72 Phase 17: Multimodal image understanding via analyze_image tool
Dual-model approach (C): Qwen3-8B handles conversation, Qwen2.5-VL-7B
analyzes images on demand via analyze_image LangChain tool.

- services/model/mlx_vision_model.py: MlxVisionModel (mlx-vlm wrapper, lazy load)
- services/agent/tools.py: make_vision_tool(vision_model, image_path)
- agent_service.py: stream_response(image_path=None), dynamic tool binding
  via config["image_path"] — thread-safe per-request rebinding
- container.py: vision_model Singleton provider
- config.py: vision_enabled, vision_model_id, vision_max_tokens
- api.py: image_base64 in ChatRequest, decode to temp file, cleanup after stream

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:52:10 +09:00

6.6 KiB

template, version, feature, date, author, project, status
template version feature date author project status
plan 1.3 phase17-multimodal 2026-06-02 sal youlbot Draft

phase17-multimodal Planning Document

Summary: analyze_image 도구 방식으로 이미지 이해 기능을 추가한다. Qwen3-8B가 대화를 유지하고, 이미지 첨부 시 Qwen2.5-VL-7B를 도구로 호출해 설명을 얻은 뒤 답변한다.

Project: youlbot Author: sal Date: 2026-06-02 Status: Draft


Executive Summary

Perspective Content
Problem 이유식 사진·금융 서류 등 이미지를 텍스트로만 처리하는 현재 한계
Solution Qwen2.5-VL-7B를 analyze_image LangChain 도구로 래핑, Qwen3-8B가 필요 시 자동 호출
Function/UX Effect 채팅창에 이미지 첨부 → 자동 분석 → 육아·금융 상담으로 자연스럽게 연결
Core Value 텍스트 추론 품질(Qwen3-8B)을 유지하면서 이미지 이해 기능 추가

Context Anchor

Key Value
WHY 손이 자유롭지 않은 육아 상황에서 사진 한 장으로 재료 분석·서류 해석이 가능해야 함
WHO 아록(주 사용자) — 이유식 사진, 건강보험 서류, 접종 기록지 등 촬영 후 질문
RISK 16GB 메모리에서 두 모델 동시 로드 시 OOM 가능 → Vision 모델 lazy load로 완화
SUCCESS 이미지 첨부 → analyze_image 도구 자동 호출 → 설명이 대화 히스토리에 남아 후속 질문 가능
SCOPE 이미지 분석 + 채팅 연동. 동영상·실시간 캡처는 제외

1. Overview

1.1 Purpose

사진을 첨부하면 analyze_image 도구가 Qwen2.5-VL-7B를 호출해 이미지 설명을 생성하고, Qwen3-8B가 그 설명을 컨텍스트로 삼아 육아·금융 상담 답변을 제공한다.

1.2 모델 분담

모델 역할 메모리
Qwen3-8B-4bit 대화·추론·도구 결정 (항상 로드) ~5GB
Qwen2.5-VL-7B-Instruct-4bit 이미지 분석 (lazy load) ~5GB
합계 ~10GB / 16GB 사용 가능

2. Scope

2.1 In Scope

  • mlx-vlm 패키지로 Vision 모델 로드 및 추론
  • analyze_image(image_path, prompt) LangChain 도구 구현
  • AgentService: 요청에 이미지 있을 때 도구 동적 주입
  • API(/chat): 이미지 파일 업로드 지원 (multipart form)
  • WebUI: 채팅 입력창에 이미지 첨부 버튼 추가
  • Telegram: 사진 메시지 수신 → 이미지 다운로드 → API 전달

2.2 Out of Scope

  • 동영상 분석
  • 이미지 생성(text-to-image)
  • 실시간 카메라 입력

3. Architecture — C방식 (analyze_image 도구)

사용자
  │  텍스트 + 이미지(선택)
  ▼
API /chat  (multipart form)
  │  image → /tmp/youlbot_img_xxx.jpg 저장
  │  image_path → AgentService.stream_response(message, image_path=...)
  ▼
AgentService
  │  image_path 있을 때: analyze_image 도구를 tools 목록에 동적 추가
  │  image_path를 도구 클로저로 바인딩
  ▼
LangGraph ReAct
  │  Qwen3-8B가 이미지 관련 질문 감지 → analyze_image() 자동 호출
  ▼
analyze_image 도구
  │  mlx_vision_model.analyze(image_path, prompt)
  ▼
MlxVisionModel (Qwen2.5-VL-7B, lazy load)
  │  이미지 설명 텍스트 반환
  ▼
LangGraph
  │  설명이 ToolMessage로 대화 히스토리에 저장
  ▼
Qwen3-8B  →  최종 답변 생성

핵심 특성:

  • Vision 모델은 처음 analyze_image 호출 시 로드 (이후 캐시)
  • 이미지 설명이 대화 히스토리에 남아 후속 질문("그 재료로 이유식 만들어줘") 가능
  • 이미지 없는 메시지는 기존과 완전히 동일하게 동작

4. 변경 파일 목록

신규 생성

파일 설명
services/model/mlx_vision_model.py MlxVisionModel 클래스 (mlx-vlm 래퍼, lazy load)

수정

파일 변경 내용
config.py vision_enabled: bool, vision_model_id: str 추가
container.py vision_model Singleton 프로바이더 추가
services/agent/tools.py make_vision_tool(vision_model, image_path) 추가
services/agent/agent_service.py stream_response(image_path=None) 파라미터 추가, 도구 동적 주입
api.py /chat → multipart form으로 변경, 이미지 temp 저장
youlbot-webui/api_client.py chat(image_path=None) 파라미터 추가, multipart 전송
youlbot-webui/app.py 채팅 입력 영역에 이미지 업로드 컴포넌트 추가

5. 주요 구현 세부사항

5.1 MlxVisionModel

class MlxVisionModel:
    def __init__(self, model_id: str): ...
    
    def analyze(self, image_path: str, prompt: str = "이 이미지를 한국어로 자세히 설명해줘.") -> str:
        # 첫 호출 시 lazy load
        # mlx_vlm.generate() 호출
        # 한국어 설명 반환

5.2 make_vision_tool

def make_vision_tool(vision_model, image_path: str):
    @tool
    def analyze_image(prompt: str = "이 이미지를 설명해줘") -> str:
        """현재 첨부된 이미지를 분석한다."""
        return vision_model.analyze(image_path, prompt)
    return analyze_image

5.3 API /chat 변경

  • JSON Body → multipart/form-data
  • 필드: message, user_id, show_thinking, image (optional file)
  • 이미지를 /tmp/youlbot_img_{uuid}.{ext}에 저장 후 agent에 전달
  • 응답 완료 후 temp 파일 삭제

5.4 WebUI 변경

  • gr.Image(type="filepath", ...) 컴포넌트 채팅 입력 영역에 추가
  • 이미지 첨부 시 api_client.chat()에 image_path 전달
  • 전송 후 이미지 초기화

6. 환경 설정

# .env 추가
VISION_ENABLED=true
VISION_MODEL_ID=mlx-community/Qwen2.5-VL-7B-Instruct-4bit
# 패키지 설치
pip install mlx-vlm

7. 위험 요소 및 대응

위험 대응
16GB에서 두 모델 동시 OOM Vision 모델 lazy load + 미사용 시 unload 옵션 제공
mlx-vlm API 변경 가능성 MlxVisionModel로 캡슐화해 교체 용이하게
Telegram 이미지 전달 복잡성 Phase 17-B로 분리, 우선 WebUI만 구현
이미지 temp 파일 누적 응답 완료 후 즉시 삭제

8. 성공 기준

  • 이미지 첨부 시 analyze_image 도구가 자동 호출되어 설명 생성
  • "이 사진에서 뭐가 보여?" 후속 질문이 히스토리 기반으로 동작
  • 이미지 없는 일반 질문은 기존과 동일하게 Qwen3-8B로 처리
  • 16GB 환경에서 OOM 없이 동작