from dotenv import load_dotenv
from embedding import openai_embedding_function as embedding_function
from langgraph.graph import StateGraph, END
from langchain.chains.query_constructor.base import AttributeInfo
load_dotenv()
from node.retrieve_node import (
plan_retrieval_node, # 사용자의 질문을 분석해 어떤 방식으로 검색할지 계획
execute_retrieval_node, # 계획에 따라 Chroma 벡터스토어에서 관련 문서를 임베딩 기반으로 검색
grade_and_filter_node, # grade_and_filter_node
rewrite_query_node, # rewrite_query_node
)
from node.decide_generate import decide_to_generate_or_rewrite, generate_node
from node.graph_state import GraphState
# --- 0. 환경 설정 (기존과 동일) ---
persist_directory= "chroma_db"
# 1. 메타데이터 필드에 대한 정보를 정의합니다.
metadata_field_info = [
AttributeInfo(
name="party",
description="문서를 발표한 정당의 이름. (예: '더불어민주당', '국민의힘')",
type="string",
),
AttributeInfo(
name="score",
description="문서의 정치적 편향 점수. -1은 진보, 1은 보수.",
type="float",
),
AttributeInfo(
name="date",
description="문서가 발표된 날짜. 'YYYY-MM-DD' 형식의 문자열.",
type="string",
),
]
# --- 3. 그래프 구성 및 연결 ---
graph_builder = StateGraph(GraphState) # 각 노드를 순차적으로 연결함
graph_builder.add_node("plan_retrieval", plan_retrieval_node)
graph_builder.add_node("execute_retrieval", execute_retrieval_node)
graph_builder.add_node("grade_and_filter", grade_and_filter_node)
graph_builder.add_node("rewrite_query", rewrite_query_node)
graph_builder.add_node("generate", generate_node)
graph_builder.set_entry_point("plan_retrieval")
graph_builder.add_edge("plan_retrieval", "execute_retrieval")
graph_builder.add_edge("execute_retrieval", "grade_and_filter")
graph_builder.add_conditional_edges(
"grade_and_filter",
decide_to_generate_or_rewrite,
{"rewrite": "rewrite_query", "generate": "generate", "end": END}
)
graph_builder.add_edge("rewrite_query", "execute_retrieval") # 루프 생성!
app = graph_builder.compile()
# verbose=True로 설정하면, LLM이 생성한 쿼리와 필터를 출력해줍니다.
# query='대통령 평가'
# filter=Expr(op='and', args=[Expr(op='eq', args=['score', -1.0]), Expr(op='gte', args=['date', '2024-01-01'])])
# --- 4. 메인 실행 루프 ---
def main():
"""메인 애플리케이션 루프"""
print("\n--- LangGraph RAG 애플리케이션 시작 ---")
print("질문을 입력하세요. 종료하려면 'exit' 또는 'quit'을 입력하세요.")
while True:
question = input("\n질문: ")
if question.lower() in ["exit", "quit"]:
print("애플리케이션을 종료합니다.")
break
# 그래프 실행
inputs = {"question": question}
final_state = app.invoke(inputs)
# 결과 출력
if not final_state.get("generation"):
print("\n[AI 답변]\n죄송하지만, 제공된 정보만으로는 답변하기 어렵습니다.")
else:
print("\n[AI 답변]")
print(final_state["generation"])
# 근거 문서 출력
if final_state.get("documents"):
print("\n--- 검색된 근거 문서 ---")
for i, doc in enumerate(final_state["documents"]):
print(f"\n--- 근거 문서 {i+1} ---")
print(f"내용: {doc.page_content}")
print(f"출처 정당: {doc.metadata.get('party', 'N/A')}")
print(f"정치 편향: {doc.metadata.get('score', 'N/A')}")
print(f"작성 날짜: {doc.metadata.get('date', 'N/A')}")
print("-" * 30)
if __name__ == "__main__":
main()
각 노드는 node 폴더에 정의되어 있습니다.
사용자가 "2024년 이후 민주당의 대통령 평가 논평은?"이라고 입력
→ "대통령 평가"라는 키워드와, "더불어민주당", "2024-01-01 이후"라는 필터 조건을 추출
→ Chroma에서 임베딩 검색 + 메타데이터 필터로 문서 추출
→ 문서가 충분하면 다음 단계, 부족하면 쿼리 리라이트로 분기
→ LLM이 더 효과적인 검색 쿼리로 변환, 다시 검색
→ 최종적으로 근거 문서와 함께 답변 생성
→ 답변 + 근거 문서(정당, 점수, 날짜 등) 출력
이 파이프라인은 단순 검색이 아니라,
본 후기는 [한글과컴퓨터x한국생산성본부x스나이퍼팩토리] 한컴 AI 아카데미 (B-log) 리뷰로 작성 되었습니다.
#한컴AI아카데미 #AI개발자 #AI개발자교육 #한글과컴퓨터 #한국생산성본부 #스나이퍼팩토리 #부트캠프 #AI전문가양성 #개발자교육 #개발자취업