ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이코에코(Eco²) Agent 포트폴리오 최신화 리포트
    이력서 2026. 1. 18. 05:48

    https://mangowhoiscloud.github.io/resume/eco2-portfolio.html

    Date: 2026-01-18
    Scope: eco2-portfolio.html - Agent 섹션
    Author: Claude Code, mangowhoiscloud
    Status: Ready


    1. Executive Summary

    eco2-portfolio.html의 Recycling Agent 섹션이 약 2주간 outdated 상태입니다. 블로그 최신 글(2026.1.16-1.18), 코드베이스(apps/chat_worker), docs/reports 문서를 분석하여 아래 핵심 변경사항을 식별했습니다.

    핵심 변경 요약

    영역 현재 (포트폴리오) 최신 (코드베이스)
    Intent 분류 4분류 9분류
    라우팅 순차 실행 Send API 병렬
    메모리 계층 미언급 3-Tier (Redis + PostgreSQL)
    토큰 스트리밍 기본 Token v2 (복구 가능)
    컨텍스트 압축 고정 3K 동적 (OpenCode 스타일)

    2. 블로그 포스팅, 코드베이스, 내부 문서 분석 (2026.1.16-1.18)

    카테고리: 이코에코(Eco²)/Agent

    날짜 제목 URL 핵심 내용
    1.18 Code Review: Chat Queuing & Event Bus #210 RabbitMQ + Redis Streams 분리, A안 패턴
    1.17 Agent #24: Multi-Agent Image Generation #207 캐릭터 참조 이미지 생성, OpenAI Responses API
    1.17 Agent #23: Observability - LangSmith + Prometheus #203 파이프라인 모니터링, 부하테스트 메트릭
    1.16 Agent #21: 동적 컨텍스트 압축 #200 OpenCode 스타일, PRUNE_PROTECT 40K
    1.16 Agent #20: Chat Worker Production Ready #198 고부하 대응, Event-First 설계
    1.16 Agent #19: LangGraph Send API 기반 동적 라우팅 #196 Multi-Intent 병렬 처리, Enrichment Rules
    1.16 Agent #18: 외부 API 연동 #195 행정안전부, 기상청, KECO API
    1.16 Agent #17: Image Generation #194 OpenAI Responses API 네이티브 생성

    3. Chat_id / Job_id 기반 데이터 저장 구조

    3.1 식별자 체계

    ┌─────────────────────────────────────────────────────────────┐
    │                    ID 체계 및 관계                           │
    ├─────────────────────────────────────────────────────────────┤
    │                                                              │
    │  chat_id (UUID)                                              │
    │  ├─ 생성: chat-api (세션 생성 시)                            │
    │  ├─ 역할: 대화 세션 식별                                     │
    │  ├─ 라이프사이클: 영구 (사용자 삭제까지)                     │
    │  └─ 저장: PostgreSQL (chat.conversations PK)                │
    │           │                                                  │
    │           │ = session_id = thread_id (LangGraph)            │
    │           │                                                  │
    │           ▼                                                  │
    │  job_id (UUID)                                               │
    │  ├─ 생성: chat-api (메시지 전송 시)                          │
    │  ├─ 역할: 단일 요청/응답 추적                                │
    │  ├─ 라이프사이클: 요청 완료까지 (TTL 1-24시간)              │
    │  └─ 저장: Redis (chat:state, chat:tokens, chat:events)      │
    │           PostgreSQL (chat.messages.job_id FK nullable)      │
    │                                                              │
    └─────────────────────────────────────────────────────────────┘

    3.2 PostgreSQL 스키마 (영구 저장)

    -- chat.conversations (세션)
    CREATE TABLE chat.conversations (
        id              UUID PRIMARY KEY,           -- chat_id
        user_id         UUID NOT NULL,              -- FK → users
        title           TEXT,
        preview         TEXT,                       -- 마지막 메시지 미리보기
        message_count   INTEGER DEFAULT 0,
        last_message_at TIMESTAMP WITH TIME ZONE,
        is_deleted      BOOLEAN DEFAULT FALSE,      -- soft delete
        created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
        updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
    );
    
    -- chat.messages (메시지 히스토리)
    CREATE TABLE chat.messages (
        id          UUID PRIMARY KEY,
        chat_id     UUID NOT NULL,                  -- FK → conversations
        role        TEXT NOT NULL,                  -- 'user' | 'assistant'
        content     TEXT NOT NULL,
        intent      TEXT,                           -- 분류된 intent
        metadata    JSONB,                          -- 추가 메타데이터
        job_id      UUID,                           -- 요청 추적 ID (nullable)
        created_at  TIMESTAMP WITH TIME ZONE DEFAULT NOW()
    );
    
    -- langgraph_checkpoints (LangGraph 상태)
    -- thread_id = session_id = chat_id

    3.3 Redis 키 체계 (실시간 + 캐시)

    ┌─────────────────────────────────────────────────────────────┐
    │                    Redis 키 체계                             │
    ├─────────────────────────────────────────────────────────────┤
    │                                                              │
    │  [이벤트 스트림] - Shard 기반 분산                            │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ chat:events:{shard}           # 0,1,2,3 (4 shards)      │ │
    │  │ ├─ 라우팅: hash(job_id) % 4                             │ │
    │  │ ├─ Consumer Group: "eventrouter"                        │ │
    │  │ ├─ MAXLEN: 10,000                                       │ │
    │  │ └─ 데이터: {job_id, stage, status, progress, ts, ...}   │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    │  [상태 KV] - job_id 기반                                     │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ chat:state:{job_id}           # 진행 상태                │ │
    │  │ ├─ TTL: 3600 (1시간)                                    │ │
    │  │ ├─ 업데이트: Event Router (Lua Script)                  │ │
    │  │ └─ 용도: SSE 재연결 시 현재 상태 조회                   │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    │  [Token v2 스트리밍] - job_id 기반 (복구 지원)               │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ chat:tokens:{job_id}          # Token Stream            │ │
    │  │ ├─ TTL: 3600 (1시간)                                    │ │
    │  │ ├─ 데이터: {seq, content, node, ts}                     │ │
    │  │ └─ 용도: 재연결 시 토큰 catch-up                        │ │
    │  │                                                          │ │
    │  │ chat:token_state:{job_id}     # Token State (스냅샷)    │ │
    │  │ ├─ TTL: 3600 (1시간)                                    │ │
    │  │ ├─ 저장 주기: 10 토큰마다                               │ │
    │  │ └─ 데이터: {last_seq, accumulated, completed}           │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    │  [체크포인트 캐시] - thread_id(=chat_id) 기반                │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ chat:checkpoint:cache:{thread_id}                       │ │
    │  │ ├─ TTL: 86400 (24시간)                                  │ │
    │  │ ├─ L1 캐시: Hot session ~1ms                            │ │
    │  │ └─ L2 fallback: PostgreSQL (langgraph_checkpoints)      │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    │  [Pub/Sub 채널] - job_id 기반                                │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ sse:events:{job_id}           # SSE Gateway 구독        │ │
    │  │ └─ Event Router → PUBLISH → SSE Gateway → Browser       │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    │  [발행 마커] - Idempotency                                   │
    │  ┌─────────────────────────────────────────────────────────┐ │
    │  │ chat:published:{message_id}   # 중복 발행 방지          │ │
    │  │ └─ TTL: 7200 (2시간)                                    │ │
    │  └─────────────────────────────────────────────────────────┘ │
    │                                                              │
    └─────────────────────────────────────────────────────────────┘

    3.4 데이터 플로우

    ┌─────────────────────────────────────────────────────────────────────────┐
    │                         Chat 요청 → 응답 플로우                           │
    ├─────────────────────────────────────────────────────────────────────────┤
    │                                                                          │
    │  ① 요청 생성 (chat-api)                                                  │
    │     POST /chat/{chat_id}/messages                                        │
    │     ├─ job_id = uuid4()                                                 │
    │     ├─ Message INSERT → PostgreSQL (chat.messages)                      │
    │     └─ RabbitMQ PUBLISH → chat_tasks exchange                           │
    │                                    │                                     │
    │                                    ▼                                     │
    │  ② 파이프라인 실행 (chat-worker)                                         │
    │     LangGraph Pipeline (job_id 추적)                                     │
    │     ├─ Intent → Router → Subagents (병렬) → Aggregator → Answer         │
    │     │                                                                    │
    │     ├─ 진행 이벤트 발행                                                  │
    │     │   └─ XADD chat:events:{shard} {job_id, stage, progress}           │
    │     │                                                                    │
    │     ├─ 토큰 스트리밍 (Token v2)                                          │
    │     │   ├─ XADD chat:tokens:{job_id} {seq, content, node}               │
    │     │   └─ SETEX chat:token_state:{job_id} (10토큰마다)                 │
    │     │                                                                    │
    │     └─ 체크포인트 저장 (멀티턴 대화)                                     │
    │         └─ PostgreSQL + Redis 캐시 (thread_id = chat_id)                │
    │                                    │                                     │
    │                                    ▼                                     │
    │  ③ 이벤트 라우팅 (event-router)                                          │
    │     XREADGROUP chat:events:* → Lua Script (atomic)                      │
    │     ├─ SETEX chat:state:{job_id}       # 상태 업데이트                  │
    │     ├─ PUBLISH sse:events:{job_id}     # SSE 전달                       │
    │     └─ SETEX chat:published:{msg_id}   # idempotency                    │
    │                                    │                                     │
    │                                    ▼                                     │
    │  ④ SSE 스트리밍 (sse-gateway)                                            │
    │     SUBSCRIBE sse:events:{job_id} → Browser SSE                         │
    │     │                                                                    │
    │     └─ 복구 지원 (재연결 시)                                             │
    │         ├─ GET chat:state:{job_id}         # 현재 상태                  │
    │         └─ XRANGE chat:tokens:{job_id}     # 토큰 catch-up              │
    │                                    │                                     │
    │                                    ▼                                     │
    │  ⑤ 완료 후 영구 저장 (Eventual Consistency)                              │
    │     chat-api Consumer (chat:events → done 이벤트 구독)                   │
    │     ├─ Message UPDATE (answer, metadata) → PostgreSQL                   │
    │     └─ Conversation UPDATE (preview, last_message_at)                   │
    │                                                                          │
    └─────────────────────────────────────────────────────────────────────────┘

    4. 메모리 계층 설계 (Memory Hierarchy)

    4.1 3-Tier 아키텍처

    ┌─────────────────────────────────────────────────────────────────────────┐
    │                         Memory Hierarchy (3-Tier)                        │
    ├─────────────────────────────────────────────────────────────────────────┤
    │                                                                          │
    │  ┌───────────────────────────────────────────────────────────────────┐  │
    │  │ Tier 1: Redis (실시간, TTL 기반)                                   │  │
    │  ├───────────────────────────────────────────────────────────────────┤  │
    │  │                                                                    │  │
    │  │  Token Stream (chat:tokens:{job_id})                              │  │
    │  │  ├─ 모든 토큰 저장 (Redis Streams)                                │  │
    │  │  ├─ TTL: 1시간                                                    │  │
    │  │  └─ 용도: 재연결 시 catch-up                                      │  │
    │  │                                                                    │  │
    │  │  Token State (chat:token_state:{job_id})                          │  │
    │  │  ├─ 10 토큰마다 누적 텍스트 스냅샷                                │  │
    │  │  ├─ TTL: 1시간                                                    │  │
    │  │  └─ 용도: 즉시 복구 (XRANGE 없이)                                 │  │
    │  │                                                                    │  │
    │  │  State KV (chat:state:{job_id})                                   │  │
    │  │  ├─ 최신 파이프라인 상태                                          │  │
    │  │  ├─ TTL: 1시간                                                    │  │
    │  │  └─ 용도: SSE 재연결 시 현재 상태 조회                            │  │
    │  │                                                                    │  │
    │  │  Checkpoint Cache (chat:checkpoint:cache:{thread_id})             │  │
    │  │  ├─ LangGraph 체크포인트 L1 캐시                                  │  │
    │  │  ├─ TTL: 24시간                                                   │  │
    │  │  └─ 용도: Hot session ~1ms 응답                                   │  │
    │  │                                                                    │  │
    │  └───────────────────────────────────────────────────────────────────┘  │
    │                                    │                                     │
    │                                    ▼ Cache Miss                          │
    │  ┌───────────────────────────────────────────────────────────────────┐  │
    │  │ Tier 2: PostgreSQL (영구 저장)                                     │  │
    │  ├───────────────────────────────────────────────────────────────────┤  │
    │  │                                                                    │  │
    │  │  chat.conversations (세션 메타데이터)                              │  │
    │  │  ├─ user_id, title, preview, message_count                        │  │
    │  │  └─ 사이드바 채팅 목록용                                          │  │
    │  │                                                                    │  │
    │  │  chat.messages (메시지 히스토리)                                   │  │
    │  │  ├─ chat_id, role, content, intent, job_id                        │  │
    │  │  └─ 대화 내역 영구 보관                                           │  │
    │  │                                                                    │  │
    │  │  langgraph_checkpoints (LangGraph 상태)                           │  │
    │  │  ├─ thread_id = session_id = chat_id                              │  │
    │  │  └─ 멀티턴 대화 컨텍스트                                          │  │
    │  │                                                                    │  │
    │  └───────────────────────────────────────────────────────────────────┘  │
    │                                                                          │
    │  ┌───────────────────────────────────────────────────────────────────┐  │
    │  │ Tier 3: Context Compression (OpenCode 스타일)                      │  │
    │  ├───────────────────────────────────────────────────────────────────┤  │
    │  │                                                                    │  │
    │  │  ModelContextConfig (동적 설정)                                    │  │
    │  │  ├─ GPT-5.2:    context=400K, output=128K, trigger=272K          │  │
    │  │  ├─ Gemini-3:   context=1M,   output=64K,  trigger=936K          │  │
    │  │  └─ Summary:    15% 할당 (min 20K, max 65K)                       │  │
    │  │                                                                    │  │
    │  │  압축 트리거 (OpenCode isOverflow)                                │  │
    │  │  ├─ 조건: tokens > (context_window - max_output)                  │  │
    │  │  └─ PRUNE_PROTECT: 40K 토큰 보호 (최근 작업)                      │  │
    │  │                                                                    │  │
    │  │  요약 구조 (5개 섹션)                                              │  │
    │  │  ├─ 사용자 요청 원문                                              │  │
    │  │  ├─ 목표 및 기대 결과                                             │  │
    │  │  ├─ 완료된 작업                                                   │  │
    │  │  ├─ 진행 중/남은 작업                                             │  │
    │  │  └─ 중요 컨텍스트                                                 │  │
    │  │                                                                    │  │
    │  └───────────────────────────────────────────────────────────────────┘  │
    │                                                                          │
    └─────────────────────────────────────────────────────────────────────────┘

    4.2 v1.0 vs v2.0 비교

    항목 v1.0 (고정) v2.0 (동적) 개선율
    Trigger 임계값 3,072 tokens 272,000 tokens (GPT-5.2) 88x
    Summary 토큰 512 tokens 60,000 tokens 117x
    최근 보호 4 messages (~2K) 40,000 tokens (~80 msgs) 20x

    5. 오케스트레이션 (Orchestration)

    5.1 LangGraph Send API 기반 동적 라우팅

    ┌─────────────────────────────────────────────────────────────────────────┐
    │                    Dynamic Routing Architecture                          │
    ├─────────────────────────────────────────────────────────────────────────┤
    │                                                                          │
    │  사용자 메시지                                                            │
    │       │                                                                  │
    │       ▼                                                                  │
    │  ┌─────────────────────────────────────────────────────────────────────┐│
    │  │                 IntentClassifierService                              ││
    │  │  ┌───────────────────────────────────────────────────────────────┐  ││
    │  │  │  키워드 맵 기반 신뢰도 보정                                    │  ││
    │  │  │  ├─ WASTE:            버려, 버리, 분리, 재활용                │  ││
    │  │  │  ├─ CHARACTER:        캐릭터, 얻, 모아                        │  ││
    │  │  │  ├─ LOCATION:         어디, 근처, 위치                        │  ││
    │  │  │  ├─ BULK_WASTE:       대형폐기물, 소파, 냉장고               │  ││
    │  │  │  ├─ RECYCLABLE_PRICE: 시세, 가격, 고철, 폐지                 │  ││
    │  │  │  ├─ COLLECTION_POINT: 수거함, 의류수거, 폐건전지             │  ││
    │  │  │  ├─ WEB_SEARCH:       최신, 뉴스, 정책                       │  ││
    │  │  │  ├─ IMAGE_GENERATION: 이미지, 그림, 보여줘                   │  ││
    │  │  │  └─ GENERAL:          안녕, 뭐야                              │  ││
    │  │  └───────────────────────────────────────────────────────────────┘  ││
    │  │                              │                                       ││
    │  │                              ▼                                       ││
    │  │  Multi-Intent Detection: "종이 버리고 수거함도 알려줘"              ││
    │  │  ├─ primary_intent: waste                                           ││
    │  │  └─ additional_intents: [collection_point]                          ││
    │  └─────────────────────────────────────────────────────────────────────┘│
    │                              │                                           │
    │                              ▼                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐│
    │  │                    Dynamic Router (Send API)                         ││
    │  │                                                                      ││
    │  │  1. 주 Intent 노드                                                   ││
    │  │     └─ Send("waste_rag", state)                                     ││
    │  │                                                                      ││
    │  │  2. Multi-Intent Fanout                                              ││
    │  │     └─ Send("collection_point", state)                              ││
    │  │                                                                      ││
    │  │  3. Enrichment Rules (자동 보조 노드)                                ││
    │  │     ├─ waste → +weather (날씨 기반 분리배출 팁)                     ││
    │  │     └─ bulk_waste → +weather                                        ││
    │  │                                                                      ││
    │  │  4. Conditional Enrichment                                           ││
    │  │     └─ user_location 있음 → +weather                                ││
    │  └─────────────────────────────────────────────────────────────────────┘│
    │                              │                                           │
    │           ┌──────────────────┼──────────────────┐                        │
    │           │                  │                  │                        │
    │           ▼                  ▼                  ▼                        │
    │   ┌─────────────┐    ┌─────────────┐    ┌─────────────┐                 │
    │   │  waste_rag  │    │ collection  │    │   weather   │                 │
    │   │ (Local JSON)│    │   _point    │    │ (기상청 API)│                 │
    │   └──────┬──────┘    └──────┬──────┘    └──────┬──────┘                 │
    │          │                  │                  │                        │
    │          └──────────────────┼──────────────────┘                        │
    │                             │ 병렬 실행                                  │
    │                             ▼                                            │
    │                    ┌─────────────────┐                                   │
    │                    │ Aggregator Node │  결과 수집 + 검증                 │
    │                    └────────┬────────┘                                   │
    │                             │                                            │
    │                             ▼                                            │
    │                     ┌──────────────┐                                     │
    │                     │ Answer Node  │  최종 답변 생성 (스트리밍)          │
    │                     └──────────────┘                                     │
    │                                                                          │
    └─────────────────────────────────────────────────────────────────────────┘

    5.2 파이프라인 노드 (12개)

    Pre-Intent 노드 (Intent 분류 전)

    Node 트리거 외부 API 역할
    vision image_url 존재 시 OpenAI Vision API 이미지 분류 (major/sub category)

    Intent → Node 매핑 (9개 Intent → 9개 Node)

    Intent Node 외부 API Timeout
    waste waste_rag Local JSON Asset 1s
    character character gRPC (Character Service) 3s
    location location Kakao Local API 5s
    bulk_waste bulk_waste 행정안전부 API 10s
    recyclable_price recyclable_price 한국환경공단 API 5s
    collection_point collection_point KECO API 5s
    web_search web_search DuckDuckGo / Tavily 8s
    image_generation image_generation OpenAI Responses API 60s
    weather weather 기상청 API (Enrichment) 5s
    general general LLM Only -
    feedback feedback 품질 평가 -

    Note:

    • location 노드: 내부적으로 kakao_place_node 사용 (gRPC LocationClient는 deprecated)
    • weather 노드: Intent 라우팅 + Enrichment 규칙으로 자동 추가

    5.3 DynamicProgressTracker

    PHASE_PROGRESS = {
        "queued":     (0, 0),
        "intent":     (5, 15),
        "vision":     (15, 20),
        "subagents":  (20, 55),    # 동적 계산
        "aggregator": (55, 65),
        "summarize":  (65, 75),
        "answer":     (75, 95),
        "done":       (100, 100),
    }
    
    # Subagent Progress 공식
    # progress = base + (completed / total) * range
    # 예: 3개 활성화, 1개 완료 → 20 + (1/3) * 35 = 32%

    5.4 NodePolicy + FailMode

    class FailMode(Enum):
        FAIL_OPEN = "fail_open"       # 실패해도 진행 (보조 정보)
        FAIL_CLOSE = "fail_close"     # 실패 시 중단 (필수)
        FAIL_FALLBACK = "fail_fallback"  # 대체 노드 실행
    
    NODE_POLICIES = {
        "waste_rag":         FailMode.FAIL_FALLBACK,  # → web_search
        "bulk_waste":        FailMode.FAIL_FALLBACK,  # → web_search
        "weather":           FailMode.FAIL_OPEN,      # 보조 정보
        "character":         FailMode.FAIL_OPEN,      # 보조 정보
        "image_generation":  FailMode.FAIL_OPEN,      # 보조 정보
        "general":           FailMode.FAIL_CLOSE,     # 최후의 보루
    }

    6. Production Resilience

    6.1 Timeout 설정

    Component Timeout 설정 위치
    Character gRPC 3s grpc_client.py:96
    Location gRPC 3s grpc_client.py:110
    Image Generator connect=5s, read=60s openai_responses.py:78
    Task 전체 120s process_task.py (Human-in-the-Loop 대기 포함)

    6.2 Circuit Breaker

    # InMemoryCircuitBreaker
    circuit_breaker_threshold = 5  # 5회 실패 시 차단
    reset_timeout = 60  # 60초 후 재시도

    6.3 IntentSignals

    @dataclass(frozen=True)
    class IntentSignals:
        previous_intents: list[str]     # Chain-of-Intent
        has_image: bool                 # 이미지 첨부 여부
        user_location: dict | None      # 위치 정보

    7. Observability (LangSmith + Prometheus)

    7.1 LangSmith (Feature-level Observability)

    ┌─────────────────────────────────────────────────────────────────────────┐
    │                     LangSmith 자동 수집 메트릭                           │
    ├─────────────────────────────────────────────────────────────────────────┤
    │                                                                          │
    │  환경변수 설정만으로 자동 활성화:                                         │
    │    LANGCHAIN_TRACING_V2=true                                            │
    │    LANGCHAIN_API_KEY=lsv2_pt_xxxx                                       │
    │    LANGCHAIN_PROJECT=eco2-chat-worker                                   │
    │                                                                          │
    │  자동 수집:                                                              │
    │  ├─ Per-Node Latency    → intent, vision, waste_rag 등 노드별 소요시간   │
    │  ├─ Token Usage         → 노드별 input/output 토큰 수, 비용 추정         │
    │  ├─ Run Timeline        → 병렬 실행 (Send API) 시각화                    │
    │  ├─ Error Tracking      → 노드별 에러율, 스택 트레이스                   │
    │  └─ Feedback Loop       → RAG 품질 평가, Fallback 체인 추적              │
    │                                                                          │
    │  Jaeger OTEL 연동:                                                       │
    │    LANGSMITH_OTEL_ENABLED=true                                          │
    │    → End-to-End Trace (chat-api → chat-worker → subagents)              │
    │                                                                          │
    └─────────────────────────────────────────────────────────────────────────┘

    7.2 Prometheus (부하테스트 메트릭)

    메트릭 설명
    chat_stream_requests_total 총 스트리밍 요청 수
    chat_stream_active 현재 활성 스트림 수
    chat_stream_duration_seconds 스트리밍 소요 시간
    chat_stream_token_count 발행된 토큰 수
    chat_redis_latency_seconds Redis 작업 지연시간

    7.3 Native Streaming (astream_events)

    # 기존 방식 (ainvoke + 수동 토큰 발행)
    result = await graph.ainvoke(state)
    for token in result["answer"]:
        await notify_token(token)  # 메타데이터 없음
    
    # 신규 방식 (astream_events + 자동 메타데이터)
    async for event in graph.astream_events(state, version="v2"):
        if event["event"] == "on_llm_stream":
            chunk = event["data"]["chunk"]
            node = event["metadata"]["langgraph_node"]  # 발생 노드 자동 추적
            await notify_token_v2(chunk.content, node=node, step=event["metadata"]["langgraph_step"])

    개선점:

    • ainvokeastream_events 전환
    • metadata.langgraph_node로 토큰 발생 노드 자동 추적
    • Token Stream + State로 재연결 시 복구 가능

    8. Outdated 포인트 상세

    7.1 Recycling Agent 카드 (Line 2716-2783)

    항목 현재 변경
    상태 배지 "진행 중" "배포 완료, E2E 테스트 중"
    Intent 분류 4분류 9분류
    Subagent 2개 (Character, Location) 11개 노드
    라우팅 순차 Send API 병렬
    메모리 언급 RedisSaver만 3-Tier + Token v2

    7.2 Chat Agentic Workflow 다이어그램 (Line 2954-2999)

    현재:
      Intent → Retriever → Eval → Fallback → Answer
    
    변경:
                          ┌─ waste_rag ───────┐
      Intent ─▶ Router ──▶├─ weather (Enrich)─┼──▶ Aggregator ─▶ Answer
      (9분류)             └─ collection_point─┘
                              (병렬 Send)

    7.3 Chat Infrastructure Modal (Line 6784-6943)

    항목 현재 변경
    파이프라인 순차 병렬 (Send API)
    토큰 스트리밍 기본 notify_token Token v2 (복구 가능)
    Progress 고정 맵 DynamicProgressTracker
    컨텍스트 압축 미언급 OpenCode 스타일 동적

    7.4 개발 진행 상황 (Line 6925-6942)

    현재:
    ✅ LangGraph 파이프라인 구현 완료
    ✅ Intent Router + TagBasedRetriever 적용
    ✅ Eval Agent + Fallback Chain 구현
    🔄 부하 테스트 및 SLA 측정 예정
    🔄 Human-in-the-Loop 시나리오 검증 예정
    
    변경:
    ✅ LangGraph Send API 동적 라우팅 (Multi-Intent 병렬)
    ✅ 9분류 Intent + Enrichment Rules
    ✅ Token v2 복구 가능한 토큰 스트리밍
    ✅ Dynamic Context Compression (OpenCode 스타일)
    ✅ Production Resilience (Timeout, Circuit Breaker, NodePolicy)
    ✅ Observability (LangSmith + Prometheus 통합)
    ✅ 배포 완료 (ArgoCD)
    🔄 Queue 정합성 검증 및 E2E 테스트 진행 중

    9. 권장 수정 사항

    8.1 HTML 수정 대상

    라인 섹션 수정 내용
    2725-2728 상태 배지 "진행 중" → "배포 완료"
    2736-2737 설명 4분류 → 9분류, 순차 → 병렬
    2750-2751 Intent Router 4분류 → 9분류 + Multi-Intent
    2954-2999 파이프라인 다이어그램 순차 → 병렬 Send API
    6788-6789 Modal 상태 "진행 중" → "배포 완료"
    6805-6809 파이프라인 설명 병렬 실행, Aggregator 추가
    6925-6942 개발 상황 완료 항목 업데이트

    8.2 신규 추가 권장 섹션

    1. 메모리 계층 설계: 3-Tier 구조 다이어그램
    2. Token v2 스트리밍: 복구 가능한 토큰 스트리밍 설명
    3. 동적 컨텍스트 압축: OpenCode 스타일 설정
    4. Production Resilience: NodePolicy, Timeout, Circuit Breaker

    10. 참고 자료

    블로그 (개별 포스트)

    코드베이스

    • apps/chat_worker/infrastructure/orchestration/langgraph/
    • apps/chat_worker/infrastructure/events/redis_progress_notifier.py
    • apps/event_router/
    • apps/sse_gateway/

    문서 (Internal)

    • docs/reports/chat-queuing-strategy-analysis.md
    • docs/reports/chat-worker-production-architecture-impl.md
    • docs/blogs/applied/30-langgraph-dynamic-routing-send-api.md
    • docs/blogs/applied/31-chat-dynamic-context-compression.md
    • docs/blogs/applied/32-langgraph-native-streaming.md

    GitHub

     

    GitHub - eco2-team/backend: 🌱 이코에코(Eco²) BE

    🌱 이코에코(Eco²) BE. Contribute to eco2-team/backend development by creating an account on GitHub.

    github.com

    Service

     

    이코에코

     

    frontend.dev.growbin.app

    댓글

ABOUT ME

🎓 부산대학교 정보컴퓨터공학과 학사: 2017.03 - 2023.08
☁️ Rakuten Symphony Jr. Cloud Engineer: 2024.12.09 - 2025.08.31
🏆 2025 AI 새싹톤 우수상 수상: 2025.10.30 - 2025.12.02
🌏 이코에코(Eco²) 백엔드/인프라 고도화 중: 2025.12 - Present

Designed by Mango