week14,15: ESG Agent 개발 프로젝트 회고록

yoon·2025년 12월 23일

HDC LABS NOVA 1기

목록 보기
7/7
post-thumbnail

1. 프로젝트 소개

  • 기간
    2025.12.02 ~ 12.11 <(주말제외) 8일>

  • 목적
    ESG 도메인에 특정한 AI AGENT를 만드는 것이 목적.

  • 배경
    상반기에 제조업에서 유럽 규제 관련 ESG 업무 인턴을 했던 나로서는 복잡한 ESG 규제들과 관련 대응 보고서를 작성하는게 만만치 않은 일이었다. 대기업은 회계법인의 컨설팅을 받거나 ESG 담당 팀이 마련되어 있어 관련 대응에 그나마 용이할 수 있겠지만, 계열사나 협력사 등은 정보를 찾는 일 부터 관련 법규에 대응하는 일을 사람이 손수 서치해야하는게 쉽지 않다.

이런 배경과 함께 Agent 개발이라는 2차 프로젝트 목적성에 아주 부합한 ESG Agent 만들기라는 주제가 빠르게 확정되었다.

  • Target
    ESG 규제는 도메인(ex. 자동차, 건설사 등)에 따라 주목해서 봐야할 규제가 다르기 때문에 초기 프로젝트에서는 도메인의 범위를 건설사로 특정하기로 했다. (아무래도 우리의 멘토링 기업이 HDC 랩스, 즉 건설사이기 때문)

2. 기능

ESG 목적 달성을 위해 해야할 일들은 크게 4가지로 생각해서 이 기능별로 tool을 만들기로 정했다.

1. 정책 요약/비교 [Policy Tool]

  • K-ESG, SASB, GRI 등 어려운 정책 용어들을 설명해주고 자동 비교
  • 건설사 맞춤 지침서 생성

2. 리스크 진단 [Risk Tool]

  • 프로젝트별 ESG 리스크 자동 평가
  • 현장 체크리스트 생성

3. 보고서 자동 작성 [Report Tool]

  • K-ESG 61개 항목 기반 제출 보고서 자동생성 후 PDF/DOCX 다운로드

4. 규제 모니터링 [Regulation Tool]

  • 환경부/고용부 규제 변경 감지
  • 주간 요약 리포트

3. 기술 스택

Backend: FastAPI
AI: LangChain + LangGraph + GPT-4-mini
 - Embedding: bge-m3
 - Parsing: PyPDF, PyMuPDF, Tesseract
Vector DB: ChromaDB
Frontend: React

사실 2차 프로젝트는 10일짜리 단기 프로젝트여서 프로젝트의 목적에 맞게 AI agent 구축에만 신경을 쓸까하다가, 이 프로젝트를 단기 프로젝트로만 끝내고 싶지 않아서 backend와 frontend도 커버해보기로 했다.
멋사를 통해서 frontend로 프로젝트는 몇 번 해봐서 오랜만에 하더라도 프론트는 바이브 코딩으로도 충분했는데 백엔드는 아예 무지해서 로직을 이해하고 수정하는데 시간이 오래 걸렸다.😯


4. 개발 과정

  • 8일 개발 일정

[AI 기능]

1. 문서 정제 및 청킹

PyMuPDF+조건부 OCR로 텍스트 확보 → 표지·목차·헤더 제거 → source_file, source_type, year, page, ocr 등 메타데이터 포함한 chunk 생성.
- 조건부 OCR? : 기업에서 발행한 ESG 보고서 등은 그림 및 표 자료가 많아서 PyPDF 등으로는 파싱이 잘 안 됨 → 이럴 경우 OCR 이용

┌────────────────────────────────────────┐
│  🔵 1단계: PDF 로드 & 파싱           
├────────────────────────────────────────┤
│  - PDF 파일 읽기                     │
│  - 텍스트 추출                       │   
│  - 테이블/이미지 처리 (선택)           │
└────────────┬───────────────────────────┘
             ↓
┌────────────────────────────────────────┐
│  🔵 2단계: 청킹 (Chunking)          
├────────────────────────────────────────┤
│  - 문서를 작은 조각으로 분할          
│  - 메타데이터 추가                    │
│    (회사명, 연도, 페이지, 섹션 등)     
│  - 청크 크기: 500-1000 토큰           │
└────────────┬───────────────────────────┘ 

2. 임베딩·VectorDB 구축

BAAI/bge-m3 임베딩으로 chunk 벡터화 → Chroma에 collection_name 지정해 저장.

vector_db 디렉터리(vector_db/esg_all)에는 ESG 보고서·규제 문서·협력사 자료를 임베딩해 놓은 Chroma 컬렉션이 저장된다.

3. Retriever 구축

  • fetch_k·top_k·MMR 값 결정해 다양성 있는 후보 확보.
  • 메타데이터 필터(기업, 연도, 영역 등)를 사전 적용해 검색 범위를 줄임.
    • 이미 VectorDB에 저장된 메타데이터를 검색 시 필터로 쓰임 ⇒ chunk마다 Document(..., metadata=...)를 만들어 Chroma에 넣음
  • LLM 기반 query rewriting으로 도메인 용어를 보강한 검색어 생성.
  • 필요 시 cross-encoder reranker(Cohere/bge-reranker)로 top-N 재정렬.
  • post-filter 단계에서 도메인 규칙(메타데이터, 키워드 등)으로 최종 chunk를 정제.

[Retriever Logic]

  1. retriever/retriever_pipeline.py는 이미 VectorDB에 저장돼 있는 메타데이터를 검색 시 필터로 쓰도록 구성.
  2. vector_db/esg_all.py에서 chunk마다 Document(..., metadata=...)를 만들어 Chroma에 넣었기 때문에, 저장 단계에서 포함된 필드(source_file, source_type, page, ocr, 앞으로 추가할 company, year, country 등)가 VectorDB에 그대로 남는다.
    리트리버는 metadata_filter에 {"source_type": "companies", "company": "DL건설"} 같은 조건을 전달해서 “이미 저장돼 있는 메타데이터”를 기준으로 검색 범위를 좁히는 것!

만약, 필드를 더 쓰고 싶으면 ingestion 단계에서 metadata.update(...)에 추가하고 VectorDB를 다시 생성하면 됨

[ 각 TOOL 별 RAG 체인]
각 툴(policy_tool.py, regulation_tool.py, risk/…, supplier_eval.py 등)이 동일한 벡터 DB를 직접 로드하지만, retriever 설정·프롬프트·LLM 파라미터는 서로 다름.

모듈벡터 DBRetriever / LLM 특성
policy_tool.pyChroma(persist_directory="vector_db/esg_all")vector_db/esg_all.py 등으로 PDF를 chunk → BGE 임베딩 후 저장
② 사용자 질의를 동일 모델로 벡터화해 retriever.get_relevant_documents(k=5)로 상위 문단을 가져옴
③ “정책 요약/비교/평가” 프롬프트에 [관련 근거] 블록 형태로 삽입해 LLM(GPT-4o mini) 을 호출하는 정석 RAG 체인
regulation_tool.pyChroma(collection_name="esg_regulations", persist_directory="vector_db/all_esg")Selenium + Tavily 로 최신 문서를 수집·저장 후, 필요할 때만 RAG로 규제 요약 수행
동일한 BGE 임베딩을 사용하지만 크롤러 히스토리, 검색 범위, 스케줄링 로직이 다름
risk / supplier_eval템플릿 기반 점수 / 보고서RAG 의존도는 낮음, 그러나 필요 시 다문서 증거를 vector DB에서 가져와 항목별 근거를 생성

4. LangChain/LangGraph 통합

위 Retriever를 그래프/체인 노드로 넣어 LLM이 항상 필터링된 chunk를 받도록 구성.

  • src/workflows/custom_graph.py: policy → regulation → risk → report 노드를 StateGraph로 구성.
  • run_custom_agent가 LangGraph를 호출해 종합 결과를 만드는 구조.

[데이터 흐름]

  1. 사용자 업로드 → /api/upload → Redis context에 파일 목록 반영.
    [ Redis 기반 context 관리]
  2. 특정 에이전트 요청(/api/agent/*) → LangChain 툴 실행 → 결과를 Redis context에 저장.
  3. /api/chat/stream → 자동으로 custom 에이전트 실행 → 정책/규제/리스크/보고서를 컨텍스트에 추가 → LLM 프롬프트 생성 → 응답/스트리밍.

5. 확장성

  • 협력사 종류별 찾은 내용 분야별로 주요기능에 연결 <도메인 지식 더 명확히 하기>
  • 로그인 서버 디비
  • 채팅창이랑 보고서창이랑 동적으로 이동시킬 수 있도록 전환
  • 보고서/체크리스트 templete 제작
  • 프론트 - 파일 업로드 속도 개선
    • 문제상황
      : 백엔드 업로드 엔드포인트가 파일을 받은 뒤 즉시 임베딩을 계산하면서 오래 막혀 버림
    • 해결방안
      1. 더 가벼운 임베딩 모델을 사용(sentence-transformers/all-MiniLM-L6-v2 or BAAI/bge-small-en) -> CPU에서 빠르게 동작하는 모델로 교체
      2. 파일 업로드 시 즉시 벡터화하지 않고 백그라운드 작업으로 넘기거나, 업로드에선 파일 메타데이터만 저장하고 임베딩은 나중에 트리거하도록 비동기 처리
  • 단일chroma → 멀티벡터리트리버 체인지

6. 마무리하며

🤩잘한 점

우선 Notion을 사용하여 매일 진행상황을 꼼꼼히 문서화한 것이 프로젝트 진행을 원활히 하는데 큰 도움이 되었다. 매일 아침 저녁으로 팀원마다 계획과 진행상황 점검을 하며 회의록을 작성하다보니, 나무만 보는 게 아니라 숲을 보며 프로젝트를 진행할 수 있어서 흐름이 끊기지 않아 좋았다. 3차 프로젝트를 시작하기 전까지 텀이 있을텐데, 노션을 보며 바로 상황을 파악하고 바로 진행할 수 있을 것 같다.

또한 깃허브의 중요성을 강조하여 팀원들 모두가 깃허브로 코드를 공유하며 협업할 수 있도록 이끈 점이 매우 뿌듯하다. 팀원 모두가 깃허브에 익숙한 것은 아니라서 사용하는데 어려움이 있었고 오류를 해결하는데 시간이 많이 걸렸지만, 그 과정에서 나 역시 깃허브에 더 익숙해졌고 각자 코드의 진행상황을 바로 공유할 수 있어서 좋았다.

😑아쉬운 점

도메인 특화적인 Agent였기 때문에 짧은 시간 내에 도메인 지식을 습득하고 정보와 문서를 찾아오는게 쉽지 않았다. 하루 안에 도메인 정보를 찾아오고 개발을 시작하려다 보니 그에 따라 성능이 낮아진 게 아쉬웠다. 3차 프로젝트에서는 도메인 지식을 확실히 하고 전처리나 모델의 성능들도 하나씩 테스트해가며 우리 프로젝트에 잘 맞는 모델과 기능들을 찾았으면 좋겠다.

👩🏻‍🎓배운 점

LangGraph, LangChain을 이용하면 RAG Agent를 만드는 게 그렇게 어렵지 않다고 오만했는데, 이론과 실전은 다르다는 것을 깨달았다. 문서를 파싱하여 청킹하는 것부터 깔끔하게 하는 게 쉽지 않았고, 문서를 찾아올 때도 정해진 로직을 따라서 하기만 하면 잘 찾아올 줄 알았는데 아니었다.. 넣고 싶었던 기능이 많다보니 tool 별로 retriever 설정도 다르게 해야했고, 단일 retirever를 쓰다보니 정확도도 낮았다.
그러나 여러 시행착오를 겪으며 디벨롭해나가야 할 부분이나 해결방안들이 현재로서는 명확하게 보여서 발전 가능성이 보인다. 그래도 RAG를 직접 사용해봤다는 경험에서 더 나은 성능을 내는 방법을 깨달은 것 같다. (전처리나 도메인 지식이나 이 프로젝트에 맞는 모델들은 써보면서 알 수 있다는 점 등등..)
짧은 기간이라서 부족했던 부분이 많지만 3차 프로젝트를 진행하면서 더 높은 완성도를 가진, 내가 원했던 그런 ESG Agent를 만들어 낼 수 있으면 좋겠다!!!

0개의 댓글