Skip to content

게임 서비스센터 Agentic AI 시스템 (YearDream_AgenticAI)

LangGraph 1.x 기반의 Human-In-The-Loop (HITL) 멀티 에이전트 워크플로우와 FastAPI 비동기 SSE 스트리밍 서버를 구축하여, 게임 서비스센터 운영(유저/버그 신고, 인게임 RAG Q&A)을 자동화한 에이전틱 AI 프로젝트입니다.


1. Project Overview (프로젝트 개요)

  • 기간: 2026.07.16 ~ 2026.07.21 (이어드림스쿨 프로젝트)
  • 역할: Agentic AI 워크플로우 설계, 백엔드 비동기 스트리밍 API 개발, LlamaIndex RAG 시스템 구축, 프론트엔드 연동 지원 (백엔드 & Agentic Architecture 중심)
  • 핵심 기술 스택:
  • Agentic AI & RAG: LangGraph 1.x, LangChain, LlamaIndex, OpenAI API, HuggingFace Embeddings
  • Backend & Streaming: FastAPI, Uvicorn, SSE (Server-Sent Events), Pydantic, Python 3.12, uv
  • DevOps & Frontend: Docker, Docker Compose, React (Chat Streaming UI)
  • Github: https://github.com/dhnam0502/YearDream_AgenticAI

🌐 Human-In-The-Loop (HITL) 워크플로우 아키텍처

사용자 문의 유형(신고, 버그, 인게임 Q&A)을 분류하고, 유저/버그 신고 시 연속 대화가 필요한 경우 LangGraph Checkpoint 및 Interrupt를 이용해 대화 흐름을 제어하는 비동기 스트리밍 구조입니다.

sequenceDiagram
    autonumber
    actor User as 사용자 (React App)
    participant API as FastAPI (Workflow Service)
    participant Graph as LangGraph StateGraph
    participant Agent as Coordinator / Agents
    participant Tool as LlamaIndex RAG / Storage
    participant Checkpoint as MemorySaver (Thread Checkpoint)

    User->>API: 챗 메시지 송신 (Thread ID)
    API->>Graph: astream_events 실행 (StateGraph)
    Graph->>Agent: 의도 분류 및 에이전트 라우팅

    alt 인게임/프로모션 문의 (RAG)
        Agent->>Tool: LlamaIndex Vector Index 검색
        Tool-->>Agent: 관련 문서 반환
        Agent-->>Graph: 답변 생성
    else 유저/버그 신고 (HITL 시나리오)
        Agent->>Checkpoint: 현재 대화 State 및 Intent 저장
        Agent-->>Graph: 추가 정보 요청 (신고 이유 / 대상)
        Graph->>[interrupt]: 대화 일시 중단 (Interrupt)
        API-->>User: 추가 질의 반환 (SSE/JSONL Stream)

        Note over User, Graph: 사용자가 추가 정보를 입력하고 Resume
        User->>API: 신고 상세 정보 답변
        API->>Graph: Thread Resume (State 업데이트)
        Graph->>Tool: Storage Engine에 리포트 저장
    end

    Graph-->>API: 스트리밍 이벤트 전달
    API-->>User: 실시간 응답 출력 (JSONL SSE Stream)

2. Repository Structure (레포지토리 구조)

백엔드 워크플로우 및 RAG 검색 엔진, 프론트엔드 연동 호환을 전담하여 구성한 구조와 핵심 기여 모듈(★)입니다.

YearDream_AgenticAI/
├── docker-compose-dev.yml      # ★ Docker Compose 개발 환경 (Backend, Frontend, Timezone)
├── README.md                   # 프로젝트 기획 및 에이전트 구조 문서
├── backend/                    # ★ 백엔드 및 Agentic AI 파이프라인. LangManus 레포지토리를 기반으로 함.
│   ├── Dockerfile              # ★ 백엔드 컨테이너화 설정
│   ├── pyproject.toml          # ★ uv 기반 의존성 및 Pyrefly 설정
│   ├── uv.lock                 # 의존성 락 파일
│   ├── main.py                 # ★ CLI 및 비동기 워크플로우 실행 테스트 엔트리포인트
│   ├── server.py               # Uvicorn 서버 실행 스크립트
│   ├── data/                   # 테스트 및 문서 데이터 (documents/, CSV 등)
│   └── src/
│       ├── api/
│       │   └── app.py          # ★ FastAPI 라우터 & SSE / JSONL 스트리밍 엔드포인트
│       ├── service/
│       │   └── workflow_service.py # ★ LangGraph 비동기 서비스 & Thread 관리
│       ├── workflow.py         # ★ LangGraph Workflow 인스턴스 및 실행 로직 (invoke v2)
│       ├── graph/
│       │   ├── builder.py      # ★ StateGraph 노드 및 엣지 연결
│       │   ├── nodes.py        # ★ Coordinator, Report, RAG 처리 노드
│       │   └── types.py        # ★ AgentState 및 Pydantic 스키마 정의
│       ├── agents/
│       │   ├── agents.py       # ★ LLM 래퍼 및 에이전트 생성기
│       │   └── llm.py          # LangChain ChatOpenAI 초기화
│       ├── tools/
│       │   ├── retrieve_tool.py          # ★ LlamaIndex Vector Index Persist & RAG 툴
│       │   └── resolve_target_user_tool.py# ★ 신고 대상 유저 검증 툴
│       ├── utils/
│       │   ├── report_saver.py           # 리포트 저장 인터페이스
│       │   └── storage_engine.py         # ★ CSV/DB 영속화 Storage Engine
│       └── prompts/            # ★ 에이전트별 시스템 프롬프트 (coordinator, user_report 등)
└── frontend/                   # 프론트엔드 (React)
    └── src/
        └── components/
            └── Chat.js         # ★ SSE/JSONL 스트리밍 응답 파싱 및 React Chat 연동

3. Key Contributions & Code Highlights (핵심 기여 및 코드 하이라이트)

① LangGraph 1.x 기반 StateGraph & Human-In-The-Loop (HITL) 설계

  • 도전 과제: 신고 문의(유저 신고, 버그 신고) 시 유저로부터 신고 대상, 이유 등의 정보를 단계적으로 전달받아야 하며, 대화 도중 세션이 유지되고 필요시 대화가 일시 중단(Interrupt)된 후 재개(Resume)될 수 있는 구조가 필요함.
  • 해결 방안: LangGraph 1.x의 MemorySaver 체크포인터와 interrupt 구문을 활용하여 Thread 기반 인터럽트 및 재개 시스템을 구현. 턴제 제한(Turn limits)과 멱등한 리포트 키(Idempotent key) 생성을 통해 안정적인 대화 상호작용 달성.
  • 코드 하이라이트 (backend/src/graph/nodes.py / builder.py):
    # Interrupt 및 HITL 시나리오 처리 노드 예시
    async def user_report_node(state: AgentState) -> dict:
        messages = state.get("messages", [])
        report_data = state.get("report_data", {})
    
        # 필수 정보(신고 대상 유저, 사유) 검증
        if not report_data.get("target_user") or not report_data.get("reason"):
            # 유저에게 추가 질문 후 대화 일시 중단 (Human-In-The-Loop)
            return {
                "messages": [AIMessage(content="신고하실 유저의 닉네임과 신고 사유를 말씀해 주세요.")],
                "next_action": "wait_user_input"
            }
    
        # 유저 검증 및 Storage Engine 저장
        save_result = storage_engine.save_user_report(report_data)
        return {
            "messages": [AIMessage(content=f"신고 접수가 완료되었습니다. (접수 번호: {save_result['report_id']})")],
            "is_completed": True
        }
    

② FastAPI & SSE / JSONL 비동기 이벤트 스트리밍 엔드포인트 구축

  • 도전 과제: 에이전트의 사고 과정, RAG 검색 결과, 답변 생성이 비동기로 일어날 때, 클라이언트에 실시간으로 이벤트를 전달(Streaming)하면서도 LangGraph 1.x의 astream_events 스키마 변경에 대응해야 함.
  • 해결 방안: workflow_service.pyapp.py에서 FastAPI StreamingResponse를 적용하고 JSONL/SSE 포맷으로 스트리밍 이벤트를 생성. LangGraph invoke v2 표준에 맞춰 이벤트 파싱 파이프라인을 구축.
  • 코드 하이라이트 (backend/src/service/workflow_service.py):
    async def stream_workflow_events(thread_id: str, message: str):
        config = {"configurable": {"thread_id": thread_id}}
        inputs = {"messages": [HumanMessage(content=message)]}
    
        # LangGraph astream_events (v2) 비동기 이벤트 스트리밍
        async for event in workflow.astream_events(inputs, config=config, version="v2"):
            kind = event["event"]
            if kind == "on_chat_model_stream":
                content = event["data"]["chunk"].content
                if content:
                    yield f"{json.dumps({'type': 'token', 'content': content})}\n"
            elif kind == "on_tool_start":
                yield f"{json.dumps({'type': 'tool_start', 'tool': event['name']})}\n"
            elif kind == "on_tool_end":
                yield f"{json.dumps({'type': 'tool_end', 'output': str(event['data'].get('output'))})}\n"
    

③ LlamaIndex 기반 RAG Vector Store 영속화 및 Target User 검증 툴 구현

  • 도전 과제: 인게임 안내/프로모션 문서를 신속하게 검색하고, 유저 신고 시 존재하지 않는 대상 유저에 대한 허위 신고를 방지하는 검증 시스템이 필요함.
  • 해결 방안: LlamaIndex를 이용해 문서를 청크화 및 인덱싱하고 디렉토리에 영속화(StorageContext.from_defaults(persist_dir=...))하여 서버 재시작 시 재인덱싱 오버헤드를 제거함. 또한 유저 DB/CSV와 연동되는 resolve_target_user_tool 및 커스텀 StorageEngine을 개발하여 신고 데이터의 신뢰성을 확보.
  • 코드 하이라이트 (backend/src/tools/retrieve_tool.py):
    def get_or_create_vector_index(data_dir: str, persist_dir: str):
        if os.path.exists(persist_dir) and os.listdir(persist_dir):
            storage_context = StorageContext.from_defaults(persist_dir=persist_dir)
            index = load_index_from_storage(storage_context)
        else:
            documents = SimpleDirectoryReader(data_dir).load_data()
            index = VectorStoreIndex.from_documents(documents)
            index.storage_context.persist(persist_dir=persist_dir)
        return index
    

④ 풀스택 연동 지원 & Docker / uv 개발 환경 구축

  • 도전 과제: 백엔드의 SSE 스트리밍 데이터 구조 변경에 따라 React 프론트엔드(Chat.js)와의 통신 깨짐 현상 및 로컬/컨테이너 환경 간 타임존/CORS 불일치 발생.
  • 해결 방안: React Chat.js 컴포넌트의 비동기 스트림 파서 코드를 백엔드 JSONL 규격에 맞춰 직접 수정하고, docker-compose-dev.yml에 CORS 헤더 설정 및 타임존 환경변수(TZ=Asia/Seoul)를 통합 적용하여 전천후 개발 환경을 정립.

4. Troubleshooting Stories (트러블슈팅 경험)

🚨 [LangGraph 1.x Migration] LangGraph 버전 상향에 따른 ReAct Agent 생성 및 StateGraph 구축 패러다임 변화

  • 문제 상황: 기존 LangChain/LangGraph 레거시 방식(create_react_agent)으로 에이전트 구성은 deprecated됨. 버전을 높여 마이그레이션 할 필요가 있음.
  • 원인 분석:
  • LangGraph가 1.x 버전으로 올라가며 ReAct 에이전트 생성 체계와 노드 간 State 전달 방식이 대대적으로 개편됨.
  • 기존에는 AgentExecutor나 레거시 create_react_agent가 프롬프트와 툴 바인딩을 암묵적으로 처리했으나, 1.x 환경에서는 StateGraph 내에서 state_schema, middleware, response_format을 명시적으로 제어하거나 ToolNode 기반 구조로 정립해야 했음.
  • 프롬프트 제어를 위한 Middleware 구조와 Pydantic 기반 Structured Output 연동 방식이 변경됨에 따라 기존 에이전트 초기화 및 노드 정의 로직이 동작하지 않았음.
  • 해결 방안:
  • LangGraph 1.x 사양에 맞춰 create_agent 초기화 함수를 재설계하고, state_schema=State 및 커스텀 middleware=[make_prompt_template(...)] 구성을 적용하여 프롬프트 주입과 상태 관리를 체계화함.
  • Pydantic 스키마(UserReportIntentCheck) 기반 response_format을 지정하여 에이전트의 구조화된 출력(Structured Output) 안정성을 확보.
  • builder.py에서 StateGraph의 노드 간 흐름과 조건부 엣지를 명시적으로 배치하고, 툴 실행 결과를 AgentState로 올바르게 업데이트하도록 파이프라인을 정립하여 ReAct 루프를 안정적으로 작동시킴.