본문 바로가기
AI Codes/LangChain & MCP & Agent

부서와 직급을 고려한 RAG 그리고 평가

by 아르카눔 2025. 5. 2.

부서별, 직급별로 열람 가능한 문서의 등급이 다른 경우를 가정한다.

부서마다, 직급마다 서로 retrieve할 수 있는 vector db를 설정하고 이에 따라서 RAG를 수행한다.

이때 LangGraph를 사용해서 검색이 필요한지 아닌지부터 결정한다.

그리고 Hallucination이나 context relevancy, 그리고 response relevancy도 함께 측정해서 RAG 자체의 성능도 평가한다.

 

실습할 겸 Gemini API와 OpenAI의 API 둘 다 사용해보았다. 

 

Github에 주피터 노트북 형태로 올렸다. (링크)

 

기본적인 골격인 테디노트의 랭체인와 랭그래프를 참고했다. (링크)

 

 

원래는 RAGAS로 context relevancy, response relevancy, faithfulness, groundedness를 측정했다.

 

하지만 주피터노트북으로 그냥 실행하면 정상적으로 작동하는데 LangGraph과 섞으면,

 

traces root 관련해서 에러가 발생했다. 이를 해결하려다가 실패해서, 결국 RAGAS의 프롬프트를 참고해서 직접 evaluators를 만들어서 각각의 지표를 측정하는 함수를 직접 작성했다. 

 

 

 

문제의 구체적인 설정은 다음과 같다.

 

권한별 서로 다른 벡터 DB 이용  

회사에 마케팅, 재무, 영업 부서가 있다고 가정한다.  

각 부서별로 사원과 임원은 다음의 문서 그룹만을 볼 수 있다.  

부서별 열람 가능 부서   

 

마케팅: 마케팅  
영업: 영업  
재무: 마케팅, 영업, 재무  
임원 : 임원 전용 문서  

임원급의 경우 모든 부서의 문서와 임원급 전용 문서를 볼 수 있다.  


문서의 등급   


그리고 각 문서들은 임원급, 부장급, 사원급으로 나뉘어서 관리된다.  
문서의 등급은 각각 A, B, C이며, 각 직급별로 다음의 문서 등급을 볼 수 있다.  

사원급: C  
부장급: B, C  
임원급: A, B, C  


영업, 마케팅, 재무 모두 A, B, C의 세가지 문서 등급이 있다.  

임원 전용 문서의 경우 등급이 A다. 

이렇게 직급별, 부서별로 볼 수 있는 문서의 종류와 등급을 고려한 RAG를 구성한다.  

 

 

LangGraph 

 

 

위 그래프의 흐름은 다음과 같다. 

 

1. set_user에서 사용자의 직급과 부서를 파악한다.

2. messages에서 question을 가져온다. 

3. rag_decide에서 RAG를 수행할지 아닐지 결정한다.

4.1. rag_decide에서 False라서 RAG를 수행하지 않으면 바로 vanilla_generate로 이동한다.

4.2. rag_decide에서 True라면 우선 retrieve를 실행한다.

5. relevance_check를 통해서 context relevancy를 체크한다.

결과는 0, 1, 2로 나오는데 0은 관계가 아예 없어서 제외한다.

6. relevance_check 이후 contexts의 리스트가 비어있지 않다면 retrieved augmented generate를 수행한다.

7. hallucination을 평가한다. 

8.1. hallucination을 평가해서 grounded가 아니라면 쿼리를 다시 작성한다.

8.2. grounded라면 faithfulness 평가로 넘어간다.

9. response_relevancy를 이용해서 답변인 response과 유저의 질문 question 사이의 연관성을 체크한다.

10. final_answer에서 meassages로 [('user', question), ('asisstant', answer)]을 반환한다. 

 

결과 예시 

질문은 "내년도 예산안에 대해서 알려줘"로 통일하고,

직급과 부서에 따라서 어떻게 다른지 비교한다. 

이를 위해서 app.invoke 대신 app.stream으로 중간 과정을 모두 살펴본다. 

 

1. executive

 

임원급 

user_data = {"id": 123, 
            "name": "김철수", 
            "email": "chulsoo.kim@example.com", 
            "department": "executive", 
            "rank": "executive"}


# 실행 (예시)
inputs = {
    "question": "내년도 예산안에 대해서 알려줘"
}

config_checkpointer = {"configurable": {"thread_id": "1"}}

for output in app.stream(inputs, config_checkpointer):
    for key, value in output.items():
        print(f"Output from node '{key}': {value}")

 

중간 과정을 포함한 출력 결과는 아래와 같다. 

 

더보기

Output from node 'set_user': {'user': {'id': 123, 'name': '김철수', 'email': 'chulsoo.kim@example.com', 'department': 'executive', 'rank': 'executive'}}
Output from node 'preprocess_data': {'question': '내년도 예산안에 대해서 알려줘'}
----- RAG Decision -----
content='[Retrieval]' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-b31e651b-73f8-4cb5-92dd-f4448933fdb1-0' usage_metadata={'input_tokens': 49, 'output_tokens': 4, 'total_tokens': 53, 'input_token_details': {'cache_read': 0}}
<class 'langchain_core.messages.ai.AIMessage'>
['[Retrieval]']
Output from node 'rag_decide': {'rag_decision': ['[Retrieval]']}
----- Retrieving Process -----
query of Retrieve: 내년도 예산안에 대해서 알려줘
vesctorestore path: faiss_index_marketing_A
Dept. marketing  
 retrieved contexts: [(Document(id='a4a2f1ed-e5da-4cd5-baf7-fa2d25860a21', metadata={'id': '4de6844e-c367-4f77-8849-4f88aa7b9a4d', 'department': 'marketing', 'rank': 'A'}, page_content='[마케팅 - A] 새로운 광고 캠페인 전략 보고서: \n                  이번 분기 새롭게 추진할 광고 캠페인 전략 보고서에서는 \n                  핵심 타겟 고객층을 20대 후반에서 30대 초반의 디지털 네이티브로 설정하고, \n                  이들의 온라인 활동 패턴을 분석하여 최적의 광고 채널 및 시간대를 제안합니다. \n                  주요 전략으로는 인스타그램, 유튜브 등 비주얼 중심 플랫폼에서의 브랜드 인지도\n                   강화, 숏폼 비디오 콘텐츠를 활용한 흥미 유발, 그리고 관련 분야의 영향력 있는 \n                  인플루언서와의 협업을 통한 신뢰도 구축을 포함합니다. 캠페인의 성공적인 진행을 \n                  위해 구체적인 예산 배분 계획, 단계별 실행 일정, 그리고 도달수, 전환율, ROI 등 \n                  측정 가능한 핵심 성과 지표(KPI)를 명확하게 제시합니다. 또한, 경쟁사 캠페인 분석\n                  을 통해 차별화된 전략 포인트를 강조하고, 잠재적인 위험 요소 및 대응 방안을 함께 제시합니다.\n                  '), 0.5353105)]
vesctorestore path: faiss_index_marketing_B
Dept. marketing  
 retrieved contexts: [(Document(id='46deb5d5-9719-44da-aa33-d5b3ecceb394', metadata={'id': '8341ab59-98a8-4e23-bb48-f90cece2144a', 'department': 'marketing', 'rank': 'B'}, page_content='[마케팅 - B] 이번 달 소셜 미디어 성과 분석: \n                  지난 한 달간 회사의 공식 소셜 미디어 채널(인스타그램, 페이스북, 트위터) \n                  운영 성과를 종합적으로 분석한 보고서입니다. 총 도달수는 지난달 대비 15% 증가한 \n                  120만 명을 기록했으며, 게시물에 대한 좋아요, 댓글, 공유 등의 참여율은 8% 상승한\n                  5.2%로 나타났습니다. 특히, 특정 신제품 홍보 게시물의 높은 참여율을 심층 분석하여 \n                  향후 콘텐츠 제작 방향에 대한 시사점을 도출하고, 각 플랫폼별 팔로워 증감 추이, 인기\n                  콘텐츠 유형, 그리고 사용자 반응 분석 결과를 구체적인 데이터와 그래프를 통해 시각\n                  적으로 제시합니다. 또한, 소셜 미디어 광고 집행 효율성을 분석하여 예산 배분 최적화\n                  방안을 제안하고, 경쟁사 소셜 미디어 활동 분석 결과를 비교하여 개선점을 도출합니다.\n                  '), 0.51677287)]
vesctorestore path: faiss_index_marketing_C
Dept. marketing  
 retrieved contexts: [(Document(id='7a841e05-e63f-4e3a-934d-b265053c4fb0', metadata={'id': '091e8dcb-0698-4f51-b9f5-8943f035ccd9', 'department': 'marketing', 'rank': 'C'}, page_content='\n                  [마케팅 - C] 마케팅 부서 주간 회의록: \n                  금주 마케팅 부서 정기 회의에서는 다음 주 주요 캠페인 론칭 일정 확정, 신규 콘텐츠 \n                  아이디어 브레인스토밍, 그리고 지난주 진행되었던 온라인 이벤트 결과 보고가 주요 \n                  안건으로 논의되었습니다. 각 팀별(디지털 마케팅팀, 콘텐츠 제작팀, 브랜드 전략팀) \n                  진행 상황 및 애로사항을 공유하고, 캠페인 협업 방안, 콘텐츠 제작 일정 조율, \n                  그리고 긴급하게 처리해야 할 의사 결정 사항들을 명확하게 기록합니다. \n                  또한, 회의에서 논의된 추가 아이디어 및 결정 사항에 대한 담당자 및 마감일을 \n                  명시하여 향후 진행 상황을 추적 관리할 수 있도록 합니다.\n                  '), 0.5002698)]
vesctorestore path: faiss_index_sales_A
Dept. sales  
 retrieved contexts: [(Document(id='6f9d2a8b-d8c2-4820-84f8-da11abc728f7', metadata={'id': '53aaa4b9-18af-472c-b838-3a010f37b9b8', 'department': 'sales', 'rank': 'A'}, page_content='[영업 - A] 대규모 고객 계약 조건: \n              핵심 고객사인 ABC Corp.과의 대규모 제품 공급 계약에 대한 최종 세부 조건이 명시되어 있습니다.\n                총 계약 금액은 50억 원이며, 제품 인도 조건은 계약 후 3개월 이내 분할 배송,\n                지불 조건은 제품 인도 완료 후 30일 이내 전액 지급입니다. 특별 할인 조건으로 총 구매 금액의\n                3% 할인이 적용되며, 계약 불이행 시의 책임 소재 및 손해 배상 규정 또한 명확하게 기술되어 있습니다.\n              법무팀의 최종 검토를 완료한 계약서 원본과 함께, 계약 관련 배경 설명 및 예상되는 기대 효과 분석 자료가 첨부되어 있습니다.\n              '), 0.53989947)]
vesctorestore path: faiss_index_sales_B
Dept. sales  
 retrieved contexts: [(Document(id='1429278d-eded-41f1-94f2-874f313afb3e', metadata={'id': 'e37ad490-1c7c-47e8-b13d-6614d1043f4d', 'department': 'sales', 'rank': 'B'}, page_content='[영업 - B] 지난 분기 판매 실적 보고서:\n                지난 3분기(7월-9월)의 전체 판매 실적을 분석한 보고서로, 주력 제품인 XYZ 모델의 판매량은\n                5,000대를 기록하여 목표 대비 95% 달성률을 보였습니다. 지역별 판매 현황 분석 결과,\n                수도권 지역의 판매량이 전체의 60%를 차지했으며, 지방 시장 확대를 위한 전략 수립의 필요성을 제시합니다.\n                또한, 판매 채널별(온라인, 오프라인, B2B) 실적 분석을 통해 각 채널의 강점과 약점을 파악하고,\n                판매 부진 제품에 대한 원인 분석 및 개선 방안을 모색합니다. 다음 분기 판매 목표 설정 및 전략 수립을 위한 기초 자료로 활용될 예정입니다.\n                '), 0.5434583)]
vesctorestore path: faiss_index_sales_C
Dept. sales  
 retrieved contexts: [(Document(id='6aea5642-7296-4abc-8471-4950dfa7e7f4', metadata={'id': '46cf51a3-772a-4c10-aea6-c4b3e0c393e8', 'department': 'sales', 'rank': 'C'}, page_content='[영업 - C] 신규 영업 사원 교육 자료:\n                새롭게 입사한 영업 사원들을 위한 필수 교육 자료로, 회사의 주요 제품 라인업 소개,\n                핵심 영업 프로세스(잠재 고객 발굴, 제안서 작성, 계약 체결, 고객 관리) 안내,\n                효과적인 고객 응대 및 커뮤니케이션 방법, 그리고 영업 활동 관련 내부 규정 및 정책 등을 상세하게 설명합니다.\n                각 교육 내용에 대한 이해도를 높이기 위해 실제 영업 사례 연구 및 역할극 연습 세션에 대한 안내도 포함되어 있으며,\n                교육 수료 후 평가 기준 및 향후 멘토링 프로그램에 대한 정보도 제공합니다.\n                '), 0.5002489)]
vesctorestore path: faiss_index_finance_A
Dept. finance  
 retrieved contexts: [(Document(id='110afa30-8f22-4d49-983d-cdb58fa4c1c5', metadata={'id': '5b5a49ca-b3f2-4e75-b959-6b84ba82ee34', 'department': 'finance', 'rank': 'A'}, page_content='[재무 - A] 내년도 예산안:\n                2026년도 회사의 전체 예산 계획을 담고 있는 최종 검토 문서입니다. \n                각 사업 부서별(마케팅, 영업, R&D, 생산) 예산 요청 내역, 예상 매출액\n                 및 영업 이익, 투자 계획(신규 설비 투자, 연구 개발 투자 등), \n                그리고 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측을 포함합니다. \n                특히, 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획을 \n                상세하게 기술하고 있으며, 예산 집행 통제 및 평가 기준을 명확히 제시하여 \n                효율적인 재정 관리를 목표로 합니다. 경영진의 최종 승인을 거쳐 확정될 예정입니다.\n                '), 0.55739176)]
vesctorestore path: faiss_index_finance_B
Dept. finance  
 retrieved contexts: [(Document(id='edc94c2a-b3dd-49bc-8011-7379cf52a5f3', metadata={'id': 'c49e9e4f-24b0-48b3-a495-dc557fb2e5f3', 'department': 'finance', 'rank': 'B'}, page_content='[재무 - B] 월별 재무 보고서:\n                2025년 4월의 회사 재무 상태를 종합적으로 보여주는 보고서입니다.\n                손익계산서에는 매출액, 매출원가, 판매비와관리비, 영업이익 등이 명시되어 있으며,\n                전월 대비 증감률 및 주요 변동 요인을 분석합니다. 대차대조표에는 자산, 부채,\n                자본 항목별 잔액과 변동 내역을 상세하게 기록하고, 유동비율, 부채비율 등 주요\n                재무 비율을 산출하여 회사의 단기 및 장기 재정 건전성을 평가합니다.\n                현금흐름표에는 영업활동, 투자활동, 재무활동으로 인한 현금 유입 및 유출 내역을\n                보여주어 회사의 현금 관리 능력을 파악할 수 있도록 합니다.\n                '), 0.5482129)]
vesctorestore path: faiss_index_finance_C
Dept. finance  
 retrieved contexts: [(Document(id='10e2088d-75cf-42b6-aa68-a9414a1ea900', metadata={'id': 'd5aa1457-588f-4c3e-937a-b5c0d8e65b8a', 'department': 'finance', 'rank': 'C'}, page_content='[재무 - C] 경비 처리 규정:\n                회사의 모든 임직원이 따라야 하는 경비 처리 절차 및 관련 규정을\n                명확하게 명시한 문서의 최신 개정본입니다. 출장비(교통비, 숙박비, 식비) 지급 기준\n                및 증빙 서류 안내, 회의비 사용 범위 및 승인 절차, 접대비 한도 및 관련 세금 처리 방법\n                등 각 항목별 처리 기준을 구체적으로 설명합니다. 또한, 전자 경비 처리 시스템 사용\n                방법 및 주의사항, 부정 경비 처리 시의 징계 규정, 그리고 관련 문의 부서 및 담당자 연락처를\n                포함하여 경비 처리 과정에서의 혼란을 최소화하고 투명성을 확보하고자 합니다.\n                '), 0.5074447)]
vesctorestore path: faiss_index_executive_A
Dept. executive  
 retrieved contexts: [(Document(id='4881b101-dc6b-44e0-b7bd-5f5c5a1c62f4', metadata={'id': '71654e7c-75b1-41d6-b295-a241465b80c8', 'department': 'executive', 'rank': 'A'}, page_content='[executive - A] 조직 개편안:\n                    급변하는 시장 환경에 대한 신속한 대응력을 높이고, 부서 간 협업 강화 및 의사 결정 효율성을 극대화하기 위한 조직 구조 개편안입니다.\n                    기존 기능 중심의 조직 구조를 시장 및 고객 중심 구조로 전환하고, 핵심 사업 부문의 독립성을 강화하며,\n                    신규 사업 추진을 위한 전담 조직 신설을 포함합니다.\n                    각 부서의 역할과 책임 재정의, 직급 체계 단순화, 그리고 새로운 커뮤니케이션 및 협업 프로세스 도입 방안을 상세하게 설명하며,\n                    조직 개편에 따른 예상 효과 및 잠재적인 위험 요소, 그리고 단계별 실행 계획을 제시합니다.\n                    '), 0.51343596)]
Output from node 'retrieve': {'context': ['[마케팅 - A] 새로운 광고 캠페인 전략 보고서: \n                  이번 분기 새롭게 추진할 광고 캠페인 전략 보고서에서는 \n                  핵심 타겟 고객층을 20대 후반에서 30대 초반의 디지털 네이티브로 설정하고, \n                  이들의 온라인 활동 패턴을 분석하여 최적의 광고 채널 및 시간대를 제안합니다. \n                  주요 전략으로는 인스타그램, 유튜브 등 비주얼 중심 플랫폼에서의 브랜드 인지도\n                   강화, 숏폼 비디오 콘텐츠를 활용한 흥미 유발, 그리고 관련 분야의 영향력 있는 \n                  인플루언서와의 협업을 통한 신뢰도 구축을 포함합니다. 캠페인의 성공적인 진행을 \n                  위해 구체적인 예산 배분 계획, 단계별 실행 일정, 그리고 도달수, 전환율, ROI 등 \n                  측정 가능한 핵심 성과 지표(KPI)를 명확하게 제시합니다. 또한, 경쟁사 캠페인 분석\n                  을 통해 차별화된 전략 포인트를 강조하고, 잠재적인 위험 요소 및 대응 방안을 함께 제시합니다.', '[마케팅 - B] 이번 달 소셜 미디어 성과 분석: \n                  지난 한 달간 회사의 공식 소셜 미디어 채널(인스타그램, 페이스북, 트위터) \n                  운영 성과를 종합적으로 분석한 보고서입니다. 총 도달수는 지난달 대비 15% 증가한 \n                  120만 명을 기록했으며, 게시물에 대한 좋아요, 댓글, 공유 등의 참여율은 8% 상승한\n                  5.2%로 나타났습니다. 특히, 특정 신제품 홍보 게시물의 높은 참여율을 심층 분석하여 \n                  향후 콘텐츠 제작 방향에 대한 시사점을 도출하고, 각 플랫폼별 팔로워 증감 추이, 인기\n                  콘텐츠 유형, 그리고 사용자 반응 분석 결과를 구체적인 데이터와 그래프를 통해 시각\n                  적으로 제시합니다. 또한, 소셜 미디어 광고 집행 효율성을 분석하여 예산 배분 최적화\n                  방안을 제안하고, 경쟁사 소셜 미디어 활동 분석 결과를 비교하여 개선점을 도출합니다.', '[마케팅 - C] 마케팅 부서 주간 회의록: \n                  금주 마케팅 부서 정기 회의에서는 다음 주 주요 캠페인 론칭 일정 확정, 신규 콘텐츠 \n                  아이디어 브레인스토밍, 그리고 지난주 진행되었던 온라인 이벤트 결과 보고가 주요 \n                  안건으로 논의되었습니다. 각 팀별(디지털 마케팅팀, 콘텐츠 제작팀, 브랜드 전략팀) \n                  진행 상황 및 애로사항을 공유하고, 캠페인 협업 방안, 콘텐츠 제작 일정 조율, \n                  그리고 긴급하게 처리해야 할 의사 결정 사항들을 명확하게 기록합니다. \n                  또한, 회의에서 논의된 추가 아이디어 및 결정 사항에 대한 담당자 및 마감일을 \n                  명시하여 향후 진행 상황을 추적 관리할 수 있도록 합니다.', '[영업 - A] 대규모 고객 계약 조건: \n              핵심 고객사인 ABC Corp.과의 대규모 제품 공급 계약에 대한 최종 세부 조건이 명시되어 있습니다.\n                총 계약 금액은 50억 원이며, 제품 인도 조건은 계약 후 3개월 이내 분할 배송,\n                지불 조건은 제품 인도 완료 후 30일 이내 전액 지급입니다. 특별 할인 조건으로 총 구매 금액의\n                3% 할인이 적용되며, 계약 불이행 시의 책임 소재 및 손해 배상 규정 또한 명확하게 기술되어 있습니다.\n              법무팀의 최종 검토를 완료한 계약서 원본과 함께, 계약 관련 배경 설명 및 예상되는 기대 효과 분석 자료가 첨부되어 있습니다.', '[영업 - B] 지난 분기 판매 실적 보고서:\n                지난 3분기(7월-9월)의 전체 판매 실적을 분석한 보고서로, 주력 제품인 XYZ 모델의 판매량은\n                5,000대를 기록하여 목표 대비 95% 달성률을 보였습니다. 지역별 판매 현황 분석 결과,\n                수도권 지역의 판매량이 전체의 60%를 차지했으며, 지방 시장 확대를 위한 전략 수립의 필요성을 제시합니다.\n                또한, 판매 채널별(온라인, 오프라인, B2B) 실적 분석을 통해 각 채널의 강점과 약점을 파악하고,\n                판매 부진 제품에 대한 원인 분석 및 개선 방안을 모색합니다. 다음 분기 판매 목표 설정 및 전략 수립을 위한 기초 자료로 활용될 예정입니다.', '[영업 - C] 신규 영업 사원 교육 자료:\n                새롭게 입사한 영업 사원들을 위한 필수 교육 자료로, 회사의 주요 제품 라인업 소개,\n                핵심 영업 프로세스(잠재 고객 발굴, 제안서 작성, 계약 체결, 고객 관리) 안내,\n                효과적인 고객 응대 및 커뮤니케이션 방법, 그리고 영업 활동 관련 내부 규정 및 정책 등을 상세하게 설명합니다.\n                각 교육 내용에 대한 이해도를 높이기 위해 실제 영업 사례 연구 및 역할극 연습 세션에 대한 안내도 포함되어 있으며,\n                교육 수료 후 평가 기준 및 향후 멘토링 프로그램에 대한 정보도 제공합니다.', '[재무 - A] 내년도 예산안:\n                2026년도 회사의 전체 예산 계획을 담고 있는 최종 검토 문서입니다. \n                각 사업 부서별(마케팅, 영업, R&D, 생산) 예산 요청 내역, 예상 매출액\n                 및 영업 이익, 투자 계획(신규 설비 투자, 연구 개발 투자 등), \n                그리고 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측을 포함합니다. \n                특히, 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획을 \n                상세하게 기술하고 있으며, 예산 집행 통제 및 평가 기준을 명확히 제시하여 \n                효율적인 재정 관리를 목표로 합니다. 경영진의 최종 승인을 거쳐 확정될 예정입니다.', '[재무 - B] 월별 재무 보고서:\n                2025년 4월의 회사 재무 상태를 종합적으로 보여주는 보고서입니다.\n                손익계산서에는 매출액, 매출원가, 판매비와관리비, 영업이익 등이 명시되어 있으며,\n                전월 대비 증감률 및 주요 변동 요인을 분석합니다. 대차대조표에는 자산, 부채,\n                자본 항목별 잔액과 변동 내역을 상세하게 기록하고, 유동비율, 부채비율 등 주요\n                재무 비율을 산출하여 회사의 단기 및 장기 재정 건전성을 평가합니다.\n                현금흐름표에는 영업활동, 투자활동, 재무활동으로 인한 현금 유입 및 유출 내역을\n                보여주어 회사의 현금 관리 능력을 파악할 수 있도록 합니다.', '[재무 - C] 경비 처리 규정:\n                회사의 모든 임직원이 따라야 하는 경비 처리 절차 및 관련 규정을\n                명확하게 명시한 문서의 최신 개정본입니다. 출장비(교통비, 숙박비, 식비) 지급 기준\n                및 증빙 서류 안내, 회의비 사용 범위 및 승인 절차, 접대비 한도 및 관련 세금 처리 방법\n                등 각 항목별 처리 기준을 구체적으로 설명합니다. 또한, 전자 경비 처리 시스템 사용\n                방법 및 주의사항, 부정 경비 처리 시의 징계 규정, 그리고 관련 문의 부서 및 담당자 연락처를\n                포함하여 경비 처리 과정에서의 혼란을 최소화하고 투명성을 확보하고자 합니다.', '[executive - A] 조직 개편안:\n                    급변하는 시장 환경에 대한 신속한 대응력을 높이고, 부서 간 협업 강화 및 의사 결정 효율성을 극대화하기 위한 조직 구조 개편안입니다.\n                    기존 기능 중심의 조직 구조를 시장 및 고객 중심 구조로 전환하고, 핵심 사업 부문의 독립성을 강화하며,\n                    신규 사업 추진을 위한 전담 조직 신설을 포함합니다.\n                    각 부서의 역할과 책임 재정의, 직급 체계 단순화, 그리고 새로운 커뮤니케이션 및 협업 프로세스 도입 방안을 상세하게 설명하며,\n                    조직 개편에 따른 예상 효과 및 잠재적인 위험 요소, 그리고 단계별 실행 계획을 제시합니다.']}
------ Context Relevance Check ------
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 381, 'total_tokens': 383, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRuIItCE2gJiBzZwCHHvDgRDhdG', 'finish_reason': 'stop', 'logprobs': None} id='run-135752e8-c10e-42fd-8c89-d17d0725c6ef-0' usage_metadata={'input_tokens': 381, 'output_tokens': 2, 'total_tokens': 383, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='1' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 451, 'total_tokens': 453, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRwfCJLNUiirC3tmq9f2sEc3SOR', 'finish_reason': 'stop', 'logprobs': None} id='run-d3371d11-cac9-4b9a-8c13-9eddfa7dfd80-0' usage_metadata={'input_tokens': 451, 'output_tokens': 2, 'total_tokens': 453, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 1
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 437, 'total_tokens': 439, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRx4peoADeKXtuVcv6kS0tZzfFy', 'finish_reason': 'stop', 'logprobs': None} id='run-5302f489-f92b-4d06-9f23-3072a1bf93b6-0' usage_metadata={'input_tokens': 437, 'output_tokens': 2, 'total_tokens': 439, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 381, 'total_tokens': 383, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRyl61avLmzfJ7tTta2TJKZyc5u', 'finish_reason': 'stop', 'logprobs': None} id='run-82c4188a-a8d5-47a4-80b6-35e3a9fe4ad1-0' usage_metadata={'input_tokens': 381, 'output_tokens': 2, 'total_tokens': 383, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 372, 'total_tokens': 374, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRypba9BVFyB2tHwWK9B4fAXIPO', 'finish_reason': 'stop', 'logprobs': None} id='run-bb350793-aefe-4df9-9e73-d7fca5e7a7a2-0' usage_metadata={'input_tokens': 372, 'output_tokens': 2, 'total_tokens': 374, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 386, 'total_tokens': 388, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdRzjRbTL1Ef37adU594hKEje9kf', 'finish_reason': 'stop', 'logprobs': None} id='run-8c0999fb-96af-470a-8ce9-898fadb53387-0' usage_metadata={'input_tokens': 386, 'output_tokens': 2, 'total_tokens': 388, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 351, 'total_tokens': 353, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdS0YzzvY6wumWHVY0l4aT1XaEke', 'finish_reason': 'stop', 'logprobs': None} id='run-d308c104-834c-4699-b97e-fb275afb56a1-0' usage_metadata={'input_tokens': 351, 'output_tokens': 2, 'total_tokens': 353, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='2' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 386, 'total_tokens': 388, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdS00dl8H6ARLQz7qRb0NIoLJTWK', 'finish_reason': 'stop', 'logprobs': None} id='run-2553256e-5bfc-4c50-83c5-0249b4020bd2-0' usage_metadata={'input_tokens': 386, 'output_tokens': 2, 'total_tokens': 388, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 2
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 404, 'total_tokens': 406, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdS1cCUFmrIiwZwJG1JOkabRCdRT', 'finish_reason': 'stop', 'logprobs': None} id='run-54dce96e-7427-413b-b35e-1e04838a5495-0' usage_metadata={'input_tokens': 404, 'output_tokens': 2, 'total_tokens': 406, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 375, 'total_tokens': 377, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdS1LcSKWP3TBRcimUYSyv8fBpJs', 'finish_reason': 'stop', 'logprobs': None} id='run-9a43bd9e-dc31-40a0-bab8-b1de9beed603-0' usage_metadata={'input_tokens': 375, 'output_tokens': 2, 'total_tokens': 377, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 361, 'total_tokens': 363, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdS26vpC7sfDNn7F0uLcg8sKDQKp', 'finish_reason': 'stop', 'logprobs': None} id='run-6f46add6-c328-44bb-b423-d15302ea2eda-0' usage_metadata={'input_tokens': 361, 'output_tokens': 2, 'total_tokens': 363, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
relevance_scores: [1, 2]
2 retrieved contexts:
sample - [마케팅 - A] 새로운 광고 캠페인
----- Is Context Empty -----
filtered context length: 2
Output from node 'relevance_check': {'relevance_scores': [1, 2], 'filtered_context': ['[마케팅 - A] 새로운 광고 캠페인 전략 보고서: \n                  이번 분기 새롭게 추진할 광고 캠페인 전략 보고서에서는 \n                  핵심 타겟 고객층을 20대 후반에서 30대 초반의 디지털 네이티브로 설정하고, \n                  이들의 온라인 활동 패턴을 분석하여 최적의 광고 채널 및 시간대를 제안합니다. \n                  주요 전략으로는 인스타그램, 유튜브 등 비주얼 중심 플랫폼에서의 브랜드 인지도\n                   강화, 숏폼 비디오 콘텐츠를 활용한 흥미 유발, 그리고 관련 분야의 영향력 있는 \n                  인플루언서와의 협업을 통한 신뢰도 구축을 포함합니다. 캠페인의 성공적인 진행을 \n                  위해 구체적인 예산 배분 계획, 단계별 실행 일정, 그리고 도달수, 전환율, ROI 등 \n                  측정 가능한 핵심 성과 지표(KPI)를 명확하게 제시합니다. 또한, 경쟁사 캠페인 분석\n                  을 통해 차별화된 전략 포인트를 강조하고, 잠재적인 위험 요소 및 대응 방안을 함께 제시합니다.', '[재무 - A] 내년도 예산안:\n                2026년도 회사의 전체 예산 계획을 담고 있는 최종 검토 문서입니다. \n                각 사업 부서별(마케팅, 영업, R&D, 생산) 예산 요청 내역, 예상 매출액\n                 및 영업 이익, 투자 계획(신규 설비 투자, 연구 개발 투자 등), \n                그리고 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측을 포함합니다. \n                특히, 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획을 \n                상세하게 기술하고 있으며, 예산 집행 통제 및 평가 기준을 명확히 제시하여 \n                효율적인 재정 관리를 목표로 합니다. 경영진의 최종 승인을 거쳐 확정될 예정입니다.']}
----- Retreival Agumented Generate -----
Output from node 'retrieved_generate': {'answer': '제공된 문서에 따르면, "[재무 - A] 내년도 예산안"은 2026년도 회사 전체 예산 계획을 담은 최종 검토 문서입니다.  이 문서에는 각 사업 부서(마케팅, 영업, R&D, 생산)의 예산 요청 내역, 예상 매출액 및 영업 이익, 투자 계획(신규 설비 투자, 연구 개발 투자 등), 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측이 포함되어 있습니다.  특히 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획이 상세히 기술되어 있으며, 예산 집행 통제 및 평가 기준을 명확히 제시하여 효율적인 재정 관리를 목표로 합니다.  현재는 경영진의 최종 승인을 기다리고 있습니다.'}
------ Groundedness (Hallucination) Check ------
groundedness_score: content='2' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-06ab7205-dc0f-490b-b39f-5a5a641a0675-0' usage_metadata={'input_tokens': 922, 'output_tokens': 2, 'total_tokens': 924, 'input_token_details': {'cache_read': 0}}
groundedness_score: 2
Output from node 'hallucination_check': {'groundedness_score': 2}
----- Faithfulness Check -----
statement: 제공된 문서가 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-3f476f1d-710b-4df2-a811-2b54d5473149-0' usage_metadata={'input_tokens': 668, 'output_tokens': 2, 'total_tokens': 670, 'input_token_details': {'cache_read': 0}}
statement: “[재무 - A] 내년도 예산안”이라는 문서가 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-5cff5558-d2f4-44f1-ad57-19c2f62cdcd0-0' usage_metadata={'input_tokens': 681, 'output_tokens': 2, 'total_tokens': 683, 'input_token_details': {'cache_read': 0}}
statement: “[재무 - A] 내년도 예산안”은 2026년도 회사 전체 예산 계획을 담고 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-e6818d43-28f3-4b60-9d68-97098f793b4a-0' usage_metadata={'input_tokens': 694, 'output_tokens': 2, 'total_tokens': 696, 'input_token_details': {'cache_read': 0}}
statement: “[재무 - A] 내년도 예산안”은 최종 검토 문서이다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-b6999d00-f893-4439-8d24-1d22620f136c-0' usage_metadata={'input_tokens': 683, 'output_tokens': 2, 'total_tokens': 685, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 각 사업 부서의 예산 요청 내역이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-0f5fe8a8-f9e4-4e62-92ea-c6bc0d8dc1f7-0' usage_metadata={'input_tokens': 680, 'output_tokens': 2, 'total_tokens': 682, 'input_token_details': {'cache_read': 0}}
statement: 마케팅 부서의 예산 요청 내역이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-7918c4dd-f05c-44d0-ad26-8c7801e3d9dc-0' usage_metadata={'input_tokens': 678, 'output_tokens': 2, 'total_tokens': 680, 'input_token_details': {'cache_read': 0}}
statement: 영업 부서의 예산 요청 내역이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-88e7c6e7-4648-450e-b17d-53e9d2f0219b-0' usage_metadata={'input_tokens': 677, 'output_tokens': 2, 'total_tokens': 679, 'input_token_details': {'cache_read': 0}}
statement: R&D 부서의 예산 요청 내역이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-238cac45-d4f6-45e8-926d-3c9636643a01-0' usage_metadata={'input_tokens': 678, 'output_tokens': 2, 'total_tokens': 680, 'input_token_details': {'cache_read': 0}}
statement: 생산 부서의 예산 요청 내역이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-95e0b700-c9c9-4be0-86a9-733fdf1eff8f-0' usage_metadata={'input_tokens': 677, 'output_tokens': 2, 'total_tokens': 679, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 예상 매출액이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-3ca2b790-5c61-4739-8d44-f10ffe9fcf19-0' usage_metadata={'input_tokens': 675, 'output_tokens': 2, 'total_tokens': 677, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 예상 영업 이익이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-cc3105db-4b97-4ea4-870a-434769079af3-0' usage_metadata={'input_tokens': 676, 'output_tokens': 2, 'total_tokens': 678, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 투자 계획이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-f4278ac4-e691-435f-8a98-7e3b31a2171e-0' usage_metadata={'input_tokens': 673, 'output_tokens': 2, 'total_tokens': 675, 'input_token_details': {'cache_read': 0}}
statement: 신규 설비 투자 계획이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-fdfea920-5ca0-4631-a41b-4b81fd9d15a5-0' usage_metadata={'input_tokens': 674, 'output_tokens': 2, 'total_tokens': 676, 'input_token_details': {'cache_read': 0}}
statement: 연구 개발 투자 계획이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-f3a26d23-f865-49c1-a373-4bb2a86f5868-0' usage_metadata={'input_tokens': 672, 'output_tokens': 2, 'total_tokens': 674, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 거시 경제 전망을 기반으로 한 재무 예측이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-def27a52-504b-4584-b6f4-7445128cbfe8-0' usage_metadata={'input_tokens': 685, 'output_tokens': 2, 'total_tokens': 687, 'input_token_details': {'cache_read': 0}}
statement: 이 문서에는 시장 상황 분석을 기반으로 한 재무 예측이 포함되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-50274b8b-f9be-4222-9529-9e90fbfb6462-0' usage_metadata={'input_tokens': 684, 'output_tokens': 2, 'total_tokens': 686, 'input_token_details': {'cache_read': 0}}
statement: 신규 사업 진출에 따른 추가 예산 확보 방안이 상세히 기술되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-26336548-f37f-490b-ac25-184795d8ff54-0' usage_metadata={'input_tokens': 684, 'output_tokens': 2, 'total_tokens': 686, 'input_token_details': {'cache_read': 0}}
statement: 비상 자금 운용 계획이 상세히 기술되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-c460655e-d596-489d-8e6e-da4013786790-0' usage_metadata={'input_tokens': 676, 'output_tokens': 2, 'total_tokens': 678, 'input_token_details': {'cache_read': 0}}
statement: 예산 집행 통제 기준이 명확히 제시되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-64f85edb-6923-4e56-bfe7-5cf121e60091-0' usage_metadata={'input_tokens': 679, 'output_tokens': 2, 'total_tokens': 681, 'input_token_details': {'cache_read': 0}}
statement: 예산 평가 기준이 명확히 제시되어 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-8d9e4f98-da87-43f9-8566-50c90a6452e2-0' usage_metadata={'input_tokens': 677, 'output_tokens': 2, 'total_tokens': 679, 'input_token_details': {'cache_read': 0}}
statement: 효율적인 재정 관리를 목표로 한다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-623fe77c-df5c-447c-b18e-bf8cb3a8a99d-0' usage_metadata={'input_tokens': 674, 'output_tokens': 2, 'total_tokens': 676, 'input_token_details': {'cache_read': 0}}
statement: 현재는 경영진의 최종 승인을 기다리고 있다.
content='1' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-4f6484ef-ddf9-4ffd-82fb-99cfbcc06e5c-0' usage_metadata={'input_tokens': 678, 'output_tokens': 2, 'total_tokens': 680, 'input_token_details': {'cache_read': 0}}
Output from node 'faithulness_check': {'faithfulness_score': 1.0}
------ Response Relevancy ------
response_relevancy_score: [0.83492866]
response_relevancy_score: 0.8349286588528286
Output from node 'response_relevancy_check': {'response_relevancy_score': 0.8349286588528286}
------ LLM Messages ------
Output from node 'final_answer': {'messages': [('user', '내년도 예산안에 대해서 알려줘'), 'assistant', '제공된 문서에 따르면, "[재무 - A] 내년도 예산안"은 2026년도 회사 전체 예산 계획을 담은 최종 검토 문서입니다.  이 문서에는 각 사업 부서(마케팅, 영업, R&D, 생산)의 예산 요청 내역, 예상 매출액 및 영업 이익, 투자 계획(신규 설비 투자, 연구 개발 투자 등), 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측이 포함되어 있습니다.  특히 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획이 상세히 기술되어 있으며, 예산 집행 통제 및 평가 기준을 명확히 제시하여 효율적인 재정 관리를 목표로 합니다.  현재는 경영진의 최종 승인을 기다리고 있습니다.']}

 

최종 결과

 

output['final_answer']

>> {'messages': [('user', '내년도 예산안에 대해서 알려줘'),
  'assistant',
  '제공된 문서에 따르면, "[재무 - A] 내년도 예산안"은 2026년도 회사 전체 예산 계획을 담은 최종 검토 문서입니다.  
  이 문서에는 각 사업 부서(마케팅, 영업, R&D, 생산)의 예산 요청 내역, 예상 매출액 및 영업 이익, 
  투자 계획(신규 설비 투자, 연구 개발 투자 등), 거시 경제 전망 및 시장 상황 분석을 기반으로 한 재무 예측이 포함되어 있습니다.  
  특히 신규 사업 진출에 따른 추가 예산 확보 방안 및 비상 자금 운용 계획이 상세히 기술되어 있으며, 
  예산 집행 통제 및 평가 기준을 명확히 제시하여 효율적인 재정 관리를 목표로 합니다.  
  현재는 경영진의 최종 승인을 기다리고 있습니다.']}

 

 

사전에 설정한 [재무 - A] 내년도 예산안 문서의 내용을 정확하게 반환함을 알 수 있다. 

 

 

 

 

2. marketing - staff

 

마케팅 부서의 사원

user_data = {"id": 123, 
            "name": "김철수", 
            "email": "chulsoo.kim@example.com", 
            "department": "marketing", 
            "rank": "staff"}


# 실행 (예시)
inputs = {
    "question": "내년도 예산안에 대해서 알려줘"
}

config_checkpointer = {"configurable": {"thread_id": "2"}}

for output in app.stream(inputs, config_checkpointer):
    for key, value in output.items():
        print(f"Output from node '{key}': {value}")

 

중간 과정을 포함한 출력 결과는 아래와 같다. 

 

더보기

Output from node 'set_user': {'user': {'id': 123, 'name': '김철수', 'email': 'chulsoo.kim@example.com', 'department': 'marketing', 'rank': 'staff'}}
Output from node 'preprocess_data': {'question': '내년도 예산안에 대해서 알려줘'}
----- RAG Decision -----
content='[Retrieval]' additional_kwargs={} response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []} id='run-d80713de-2ffb-4c5f-8a6e-76b2c373cb8a-0' usage_metadata={'input_tokens': 49, 'output_tokens': 4, 'total_tokens': 53, 'input_token_details': {'cache_read': 0}}
<class 'langchain_core.messages.ai.AIMessage'>
['[Retrieval]']
Output from node 'rag_decide': {'rag_decision': ['[Retrieval]']}
----- Retrieving Process -----
query of Retrieve: 내년도 예산안에 대해서 알려줘
vesctorestore path: faiss_index_marketing_C
Dept. marketing  
 retrieved contexts: [(Document(id='7a841e05-e63f-4e3a-934d-b265053c4fb0', metadata={'id': '091e8dcb-0698-4f51-b9f5-8943f035ccd9', 'department': 'marketing', 'rank': 'C'}, page_content='\n                  [마케팅 - C] 마케팅 부서 주간 회의록: \n                  금주 마케팅 부서 정기 회의에서는 다음 주 주요 캠페인 론칭 일정 확정, 신규 콘텐츠 \n                  아이디어 브레인스토밍, 그리고 지난주 진행되었던 온라인 이벤트 결과 보고가 주요 \n                  안건으로 논의되었습니다. 각 팀별(디지털 마케팅팀, 콘텐츠 제작팀, 브랜드 전략팀) \n                  진행 상황 및 애로사항을 공유하고, 캠페인 협업 방안, 콘텐츠 제작 일정 조율, \n                  그리고 긴급하게 처리해야 할 의사 결정 사항들을 명확하게 기록합니다. \n                  또한, 회의에서 논의된 추가 아이디어 및 결정 사항에 대한 담당자 및 마감일을 \n                  명시하여 향후 진행 상황을 추적 관리할 수 있도록 합니다.\n                  '), 0.5002698)]
Output from node 'retrieve': {'context': ['[마케팅 - C] 마케팅 부서 주간 회의록: \n                  금주 마케팅 부서 정기 회의에서는 다음 주 주요 캠페인 론칭 일정 확정, 신규 콘텐츠 \n                  아이디어 브레인스토밍, 그리고 지난주 진행되었던 온라인 이벤트 결과 보고가 주요 \n                  안건으로 논의되었습니다. 각 팀별(디지털 마케팅팀, 콘텐츠 제작팀, 브랜드 전략팀) \n                  진행 상황 및 애로사항을 공유하고, 캠페인 협업 방안, 콘텐츠 제작 일정 조율, \n                  그리고 긴급하게 처리해야 할 의사 결정 사항들을 명확하게 기록합니다. \n                  또한, 회의에서 논의된 추가 아이디어 및 결정 사항에 대한 담당자 및 마감일을 \n                  명시하여 향후 진행 상황을 추적 관리할 수 있도록 합니다.']}
------ Context Relevance Check ------
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 381, 'total_tokens': 383, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdTznvyElSotu0gRHVmDgyphJ0Mq', 'finish_reason': 'stop', 'logprobs': None} id='run-0345f365-b111-4bf2-a631-987003770f35-0' usage_metadata={'input_tokens': 381, 'output_tokens': 2, 'total_tokens': 383, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
context_relevance: content='0' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 381, 'total_tokens': 383, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'id': 'chatcmpl-BSdU0Sl6IqMkHdU01w0pdfer65WKq', 'finish_reason': 'stop', 'logprobs': None} id='run-7fb5a908-9f53-4d5a-b7f4-11af27623889-0' usage_metadata={'input_tokens': 381, 'output_tokens': 2, 'total_tokens': 383, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
relevance_score: 0
relevance_scores: []
0 retrieved contexts:
----- Is Context Empty -----
filtered context length: 0
Output from node 'relevance_check': {'relevance_scores': [], 'filtered_context': []}
----- Vanilla Generate -----
Output from node 'vanilla_generate': {'answer': '내년도 예산안에 대한 정보를 드리기 위해서는 어떤 부분에 대해 궁금하신지 구체적으로 알려주셔야 합니다.  회사, 부서, 혹은 특정 프로젝트에 대한 예산안 정보를 원하시는지,  예산안의 전체적인 규모, 주요 투자 분야, 예산 편성 기준 등에 대한 정보를 원하시는지 명확히 해주시면 더욱 정확한 답변을 드릴 수 있습니다.\n\n예를 들어, 다음과 같은 정보를 제공해주시면 도움이 됩니다:\n\n* **회사/부서:** 어떤 회사 또는 부서의 예산안에 관심이 있으십니까?\n* **예산 항목:**  어떤 항목의 예산에 대해 알고 싶으십니까? (예: 마케팅, 연구개발, 인건비 등)\n* **구체적인 질문:** 예산안과 관련하여 궁금한 점이 있다면 구체적으로 질문해주세요. (예: 예산 증감률, 예산 배정 기준, 예산 집행 계획 등)\n\n제공해주시는 정보에 따라,  관련 자료를 찾아보거나 담당 부서에 문의하여 정확한 정보를 제공해 드리겠습니다.'}
------ Response Relevancy ------
response_relevancy_score: [0.84903055]
response_relevancy_score: 0.849030552780449
Output from node 'response_relevancy_check': {'response_relevancy_score': 0.849030552780449}
------ LLM Messages ------
Output from node 'final_answer': {'messages': [('user', '내년도 예산안에 대해서 알려줘'), 'assistant', '내년도 예산안에 대한 정보를 드리기 위해서는 어떤 부분에 대해 궁금하신지 구체적으로 알려주셔야 합니다.  회사, 부서, 혹은 특정 프로젝트에 대한 예산안 정보를 원하시는지,  예산안의 전체적인 규모, 주요 투자 분야, 예산 편성 기준 등에 대한 정보를 원하시는지 명확히 해주시면 더욱 정확한 답변을 드릴 수 있습니다.\n\n예를 들어, 다음과 같은 정보를 제공해주시면 도움이 됩니다:\n\n* **회사/부서:** 어떤 회사 또는 부서의 예산안에 관심이 있으십니까?\n* **예산 항목:**  어떤 항목의 예산에 대해 알고 싶으십니까? (예: 마케팅, 연구개발, 인건비 등)\n* **구체적인 질문:** 예산안과 관련하여 궁금한 점이 있다면 구체적으로 질문해주세요. (예: 예산 증감률, 예산 배정 기준, 예산 집행 계획 등)\n\n제공해주시는 정보에 따라,  관련 자료를 찾아보거나 담당 부서에 문의하여 정확한 정보를 제공해 드리겠습니다.']}

 

최종 결과는 아래와 같다. 

 

output['final_answer']
>> 
{'messages': [('user', '내년도 예산안에 대해서 알려줘'),
  'assistant',
  '내년도 예산안에 대한 정보를 드리기 위해서는 어떤 부분에 대해 궁금하신지 구체적으로 알려주셔야 합니다.
  회사, 부서, 혹은 특정 프로젝트에 대한 예산안 정보를 원하시는지,  
  예산안의 전체적인 규모, 주요 투자 분야, 예산 편성 기준 등에 대한 정보를 원하시는지 명확히 해주시면 
  더욱 정확한 답변을 드릴 수 있습니다.\n\n예를 들어, 다음과 같은 정보를 제공해주시면 도움이 됩니다:
  \n\n* **회사/부서:** 어떤 회사 또는 부서의 예산안에 관심이 있으십니까?\n* 
  **예산 항목:**  어떤 항목의 예산에 대해 알고 싶으십니까? (예: 마케팅, 연구개발, 인건비 등)\n* 
  **구체적인 질문:** 예산안과 관련하여 궁금한 점이 있다면 구체적으로 질문해주세요. 
  (예: 예산 증감률, 예산 배정 기준, 예산 집행 계획 등)\n\n제공해주시는 정보에 따라,  
  관련 자료를 찾아보거나 담당 부서에 문의하여 정확한 정보를 제공해 드리겠습니다.']}

 

부서와 권한에 맞게 재무 부서나 임원급의 문서를 참조해서 답변하지 않았다.

 

 

LangGraph의 configurable의 thread_id 

 

config_checkpointer = {"configurable": {"thread_id": "2"}}에서 정의하는 thread_id는 매우 중요하다.

 

유저의 현재 세션 ID기 때문에 유저 마다, 그리고 세션마다 다르게 지정해주어야 한다.

 

 

GraphState와 업데이트 

 

질문 재작성을 예로 들어서 아래 예시를 보자.

 

def rewrite_query(state: GraphState) -> GraphState:
    print('----- Rewrite Question -----')
    # Query Transform: 쿼리 재작성
    new_question = question_rewriter(state['question'])
    #state['question'] = new_question
    return GraphState(question=new_question)

 

위 내용을 보면 state에 직접적으로 state['question']=new_question을 한 다음에 return state의 형식으로 직접 반환하는게 아니라,

 

GraphState(question=new_question)로 넘겨주면 알아서 LangGraph가 state를 누적해서 업데이트 해준다.

 

이 때문에 아래처럼 operator.add로 값이 추가 되거나, add_mesages로 메시지가 누적되는 작업이 가능하다. 

 

from typing import Annotated, List
from typing_extensions import TypedDict

from langchain_core.documents import Document
from langgraph.graph.message import add_messages
import operator

# State 정의
class GraphState(TypedDict):
    context: Annotated[List[str], operator.add]
    filtered_context: Annotated[List[str], operator.add]
    answer: Annotated[str, "answer"]
    question: Annotated[str, "question"]
    slq_query: Annotated[str, "SQL Query"]
    relevance_scores: Annotated[List[int], operator.add]
    rag_decision: Annotated[List[str], '[Retrieval] or [No Retrieval]']
    is_rag: Annotated[bool, 'True means do RAG']
    is_contexts_empty: Annotated[bool, 'True means empty contexts']
    is_hallucinated: Annotated[bool, 'Hallucination check']
    groundedness_score: Annotated[int, 'groundedness check']
    is_grounded: Annotated[bool, 'groundedness check']
    response_relevancy_score: Annotated[float, 'answer relevancy score']
    faithfulness_score: Annotated[float, 'answer faithfulness score']
    messages: Annotated[list, add_messages]
    user: User

 

 

operator.add나 add_mesages의 누적이 중요한 이유는 직접 랭그래프를 만들어보고 실행해보니 멀티 턴 대화에서 적극적으로 활용 가능하기 때문으로 추측하고 있다.

 

 

Future Work

 

기본적으로 싱글 턴 대화 기반이라서 멀티턴의 경우 다음의 보완이 필요하다.

 

1. 이전 대화의 맥락 저장 → chat_history 변수 도입 

2. 이전 대화의 맥락을 뒤에서 사용할지 여부 결정

→ 이전 대화 내용이 너무 길어지면 요약할지도 결정 필요

→ 사용자 이전 질문 / 이전 retrieved context / 이전 response가 현재 사용자의 질문에서 유효한 정보인가 판단 필요 

 

 

 

 

References:

https://langchain-ai.github.io/langgraph/concepts/persistence/

https://wikidocs.net/261601

https://wikidocs.net/book/14314