기술의 발전은 어떤 갈증으로부터 시작된다.
“이런 걸 할 수 있었으면 좋겠는데, 지금 방식으로는 불편하다.”
이 문제의식이 새로운 기술을 만든다.
RAG 기술이 강력해지면서 개발자들은 다음과 같은 고민을 하게 되었다:
이 갈증을 해결하기 위해 LangGraph가 등장했다.
기존 RAG 구조는 다음과 같이 단방향 파이프라인이다:
질문 → 문서 로더 → 텍스트 분할 → 임베딩 생성 → 벡터 저장소
문서 검색 → LLM 호출 → 답변 생성 → 종료
문제가 되는 이유:
질문:
“생성형 AI 가우스를 만든 회사의 2023년 매출액은?”
기존 RAG에서는 이런 오류를 자동으로 제어하거나 재수정하기 어렵다.
| 항목 | 설명 |
| 단방향 구조 | 한 번 실행되면 되돌릴 수 없음 |
| 고정 설정 | 동적 변경 불가 |
| 한 번에 성공 필요 | 하나라도 실패하면 전체 실패 |
| 복잡성 증가 | 체인이 길어지고 관리 어려움 |
| 디버깅 어려움 | 문제 지점 추적 어려움 |
기존 RAG 사고방식:
“체인을 잘 설계해서 한 번에 성공하게 하자.”
LangGraph 사고방식:
“모든 단계를 독립 노드로 쪼개고, 실행 중 필요에 따라 분기하고 재실행하자.”
State는 노드 간 데이터를 전달하는 택배 상자와 같다.
A 노드 → 상태 저장 → B 노드
B 노드는 상태를 읽고 다음 작업을 수행한다.
from typing import TypedDict, Annotated, List
from langgraph.graph.message import add_messages
class GraphState(TypedDict):
question: str
context: str
answer: str
score: str
messages: Annotated[List, add_messages]
List 값을 새로 덮어쓰지 않고 자동으로 이어 붙여준다.
def retrieve(state):
docs = retriever.get(state["question"])
return {"context": docs}
Node 내부는 파이썬 코드, API 호출, DB 조회 등 어떤 작업이든 가능하다.
LangChain을 반드시 쓸 필요도 없다.
graph.add_edge("A", "B")
def route(state):
if state["score"] == "GOOD":
return "generate"
return "rewrite"
graph.add_conditional_edges(
"grade",
route,
{
"generate": "generate",
"rewrite": "rewrite"
}
)
질문 → Retrieval → Generation → 출력
질문
→ 검색
→ 평가
→ (부족함) → 질문 재작성 → 다시 검색
→ 생성
→ 환각 검사
→ (부족함) → 웹 검색
→ 재생성
→ 최종 출력
평가가 BAD면 다음 단계:
이 모든 과정이 자동 순환된다.
동적 라우팅의 핵심.
중간에 사람이 승인 여부를 판단하도록 구성할 수 있음.
각 단계의 상태를 저장 → 특정 시점으로 돌아가 재실행(Replaying) 가능.
| 항목 | LangChain | LangGraph |
| 목적 | LLM 통합 | 워크플로우 제어 |
| 구조 | 선형 체인 | 그래프 |
| 조건부 분기 | 어려움 | 매우 쉬움 |
| 순환 구조 | 거의 불가능 | 기본 지원 |
| 상태 관리 | 단순 메모리 | TypedDict 기반 |
| 복잡 RAG | 제한적 | 최적화됨 |
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
class SimpleRAG:
def __init__(self):
self.llm = OpenAI()
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
retriever=self.retriever,
chain_type="stuff"
)
def ask(self, question):
return self.qa_chain.run(question)
from langgraph.graph import StateGraph, END
from typing import TypedDict
class RAGState(TypedDict):
question: str
documents: str
answer: str
score: str
class AdaptiveRAG:
def __init__(self):
self.graph = StateGraph(RAGState)
self._build_graph()
def _build_graph(self):
self.graph.add_node("retrieve", self.retrieve)
self.graph.add_node("grade", self.grade)
self.graph.add_node("generate", self.generate)
self.graph.add_node("rewrite", self.rewrite)
self.graph.add_edge("retrieve", "grade")
self.graph.add_conditional_edges(
"grade",
self.should_continue,
{
"generate": "generate",
"rewrite": "rewrite"
}
)
self.graph.add_edge("rewrite", "retrieve")
self.graph.add_edge("generate", END)
self.graph.set_entry_point("retrieve")
self.compiled = self.graph.compile()
def should_continue(self, state):
if state["score"] == "GOOD":
return "generate"
return "rewrite"
| 영상처리 - 에지 검출 (1) | 2024.12.05 |
|---|---|
| 영상처리 - 모폴로지 및 다해상도-기하변환 및 컬러처리 (0) | 2024.12.05 |
| 영상처리 (0) | 2024.10.17 |
| 인공지능 - 기계 학습 (1) | 2024.06.09 |
| 인공지능 - 퍼지논리와 불확실성 (0) | 2024.06.07 |