LG U+ Why Not SW CAMP 7기 18주차 회고

gayoniee·2025년 9월 15일

회고

목록 보기
18/25

Liked

  • 캐싱 전 구동 → 인메모리/Redis

    • InMemoryCache로 같은 질문 반복 시 0s → 첫 실행은 오래걸림
    • Redis 연결 테스트까지 했고 LangChain의 RedisCache로 LLM 응답 캐싱도 동작!
  • 프롬프트 템플릿 → ChatPromptTemplate

    • PromptTemplate로 변수만 바꿔 프롬프트 재사용
    • ChatPromptTemplate로 system/human/ai 구조화
  • Output Parser

    • JsonOutputParser + 지시문을 프롬프트에 → 파싱 실패 없이 JSON 획득
  • LCEL 파이프라인

    • prompt | llm으로 실행
  • 임베딩 비교 실험

    • OpenAI, Upstage, Ollama까지 코사인 유사도 비교.
    • king–왕에서 모델별 유사도 차이
  • Pinecone

    • 3차원 인덱스 생성→업서트→필터 쿼리까지 확인
  • 위키 100개 샘플: datasets로 위키 로드 → RecursiveCharacterTextSplitter(400/20)로 쪼개기 → text-embedding-3-small(1536차원)으로 임베딩 → Pinecone 업서트(배치 20 + sleep) → query()로 바로 검색까지

  • 리트리벌 비교:

    • wiki_index.query(..., include_metadata=True)
    • PineconeVectorStore(..., text_key='full_text').similarity_search()
  • 세금 문서 RAG: Docx2txtLoadersplit(1500/200)Chroma.persist로 로컬 저장 → ChatOpenAI로 컨텍스트 기반 답변 생성

  • 질의 정규화(정규식/치환) → 바로 성과

    • 연봉 5천만원을 과세표준 5,000만원으로 바꾸니 세율 표(제55조)가 정확히 걸렸고 624만원(= 84만 + 3,600만×15%)을 안정적으로 뽑아냈다.
    • 9월 급여 미지을 12월 31일 원천징수 시기로 유도하니 제135조 특례가 바로 리트리브되어 12/31에 지급 간주 → 원천징수라는 핵심 규정이 깔끔히 나왔다.
  • 사전 기반 용어 표준화가 유효

    • 직장인→거주자, 월급→근로소득, 세금 떼다→원천징수 같은 치환이 검색 분산을 줄이고 법 조문 제목/본문 키워드와 정합성을 높여줌.
  • LCEL 체이닝 구조가 읽기 쉬움

    • dictionary_chain → qa_chain 흐름이 명확하고 나중에 모듈별로 성능 비교하기 좋아 보였다.
  • LangSmith 평가 프레임 설계

    • 실행은 안 했지만 정확도/도움됨/할루시네이션 3축으로 평가 루프를 준비해둔 점이 좋았다.
  • 소믈리에 페르소나

    • 시스템 프롬프트가 입력 부족 시 묻기 → 기본 출력 포맷까지 포함해서 재사용성이 높다. 이미지가 들어와도 안정적으로 제안이 나오는 것 확인.

Learned

  • 캐시의 종류
    • 정확 일치 캐시: 프롬프트가 글자 하나까지 똑같아야 저장된 응답을 씀. → 서울 프렌치 레스토랑 추천해줘와 서울에서 프렌치 레스토랑 추천해줘는 달라서 다른 응답 생성
    • 시맨틱 캐시: 프롬프트를 임베딩으로 바꾸고 코사인 유사도로 비교해서 비슷하면 같은 응답을 씀. → 표현만 다르게 해도 같은 뜻이면 응답 재사용 가능.
    • 코드에서 InMemoryCache → 속도 빠름, 종료 시 데이터 사라짐. RedisCache → 여러 서버 공유, 껐다 켜도 데이터 유지.
  • 프롬프트 템플릿
    • 변수를 넣는 자리를 미리 틀로 만들어서 재사용 가능.
    • PromptTemplate → 서울에서 유명한 프렌치 레스토랑을 알려줘 같은 문장을 변수만 바꿔 자동 생성.
    • ChatPromptTemplate → system / human / ai 역할을 나눠서 대화 프롬프트를 안정적으로 만들 수 있음.
  • Output Parser
    • LLM 답변을 JSON 같은 구조로 뽑아내는 도구.
    • JsonOutputParser + Pydantic 스키마로 내가 원하는 필드(sender_name, title, content)만 딱딱 뽑을 수 있었음.
    • 포인트: parser.get_format_instructions()를 프롬프트에 넣으니 LLM이 정해진 JSON 구조로 잘 출력해줌.
  • LCEL 체인
    • prompt | llm 처럼 파이프라인으로 연결
    • 여러 단계를 차례대로 묶을 땐 SequentialChain처럼 사용할 수 있지만 LCEL이 더 직관적이고 가볍다.
  • 차원 일치의 중요성
    • 인덱스마다 차원 다름: quickstart(1024), wiki(1536).
    • 임베딩 모델이 뱉는 벡터 차원과 인덱스 차원이 정확히 맞아야 함(안 맞으면 바로 에러).
  • 텍스트 스플릿 전략
    • RecursiveCharacterTextSplitter(chunk_size=400, overlap=20)로 위키 텍스트를 쪼개고 35KB 초과는 스킵.
    • 위키: 400/20 → 짧아서 정확히 맞는 문단을 잘 끌어옴.
    • 세금 문서: 1500/200 → 길어서 문맥은 잘 유지되는데, 가끔 필요 없는 내용이 같이 따라옴.
  • RAG 파이프라인
    1. Retrieval: 벡터DB에서 k개 뽑기
    2. Augmentation: 컨텍스트 묶기
    3. Generation: LLM에 컨텍스트만 참고, 조건 프롬프트로 답변
    • 세금 문서(docx2txt → split → Chroma.persist)로 질문 → 관련 문단 → 근거 기반 답변
  • 질의 끝에 누진공제를 적용해 계산을 붙이는 것만으로도 모델이 제55조 근처로 강하게 수렴. 원천징수, 시기, 간이세액표 같은 단어도 리트리버를 올바른 조문으로!
  • RagBot 패턴 retrieve_docs()invoke_llm()에서 문맥을 system에 고정해 주면 헛소리가 줄어든다. 다만 발췌 범위/길이 관리가 중요(너무 길면 분산)
  • LLM 판단 주의 LangSmith LLM 평가자는 프롬프트 편향을 타니 샘플 다양성과 정답 스키마를 갖춘 데이터셋이 필요. 그래야 점수가 의미 있게 나온다.

Lacked

프롬프트 표준화가 왜 중요한지 감은 오지만 막상 적용은..

  • 프로젝트나 실제는 질문이 매번 다르게 들어올 텐데… 그걸 어떻게 표준화하지? 라는 고민..
  • 코드로는 단순히 {city}, {topic}처럼 바꾸면 되지만 실제 데이터에서는 훨씬 복잡할 것 같다.

시간 관계상 생각보다 굉장히 빨라서 따로 리뷰하는 시간을 꼭 가져야한다…

  • 메타데이터에 full_text를 통째로 넣음: 임베딩은 chunk로 했는데 메타데이터 텍스트 키를 full_text로 두니 각 청크가 전부 원문 전체를 달고 다니는 구조가 됨.
  • 세금 RAG 근거 표기 부족: 답변은 나왔지만 어느 조항/문단에서 왔는지를 제목/페이지/문단ID로 알아보기가 어려움

Longed for

  • 프롬프트 포맷 고정하기
    • 같은 질문이면 같은 형식으로 들어가게 만들어보기
    • 시스템 프롬프트 + 사용자 입력 + 버전 태그로 캐시 키를 일정하게 유지해보기
  • TTL / 로그 붙이기
    • 자주 바뀌는 것
    • 로그: hit/miss, sim_score, latency, tokens, cost 저장해서 캐시 성능을 숫자로 확인!
  • 질의 강화
    • 위키: 주기율표 78번 → 원소 78 백금, 화학적 성질/용도처럼 키워드 보강
    • 세금: 연봉 5천만 원 소득세 → 근로소득 간이세액/과세표준/세액공제 키워드 자동 추가.
  • RAG 근거 표준화
    • 세금 답변에 출처: [문서명] 섹션/문단ID 템플릿 고정.
    • 위키도 title | url | chunk_id | score를 함께 보여주기.
  • 메타데이터 스키마 확정 + 재업서트
    • 필드: country, variety, price, points, winery, title, region_1
    • filter={"price":{"$lte":30000}}
  • 필터+검색 결합 쿼리
    • 가벼운 바디 & 산도 높은 화이트, 2–4만, 프랑스/이탈리아
    • similarity_search(query, k, filter={...}) 패턴 확보.

0개의 댓글