โ† ใ‚นใ‚ญใƒซไธ€่ฆงใซๆˆปใ‚‹
eco2-team

chat-agent-flow

by eco2-team

๐ŸŒฑ ์ด์ฝ”์—์ฝ”(Ecoยฒ) BE

โญ 0๐Ÿด 0๐Ÿ“… 2026ๅนด1ๆœˆ25ๆ—ฅ

SKILL.md


name: chat-agent-flow description: Ecoยฒ Chat ์„œ๋น„์Šค E2E ํ…Œ์ŠคํŠธ ๋ฐ ๋””๋ฒ„๊น… ๊ฐ€์ด๋“œ. API ์—”๋“œํฌ์ธํŠธ, SSE ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ, Redis Streams ํŒŒ์ดํ”„๋ผ์ธ, Intent ๋ถ„๋ฅ˜ ์‹œ์Šคํ…œ. Use when: (1) Chat API E2E ํ…Œ์ŠคํŠธ ์‹œ, (2) SSE ์ด๋ฒคํŠธ ์ˆ˜์‹  ๋ฌธ์ œ ํ•ด๊ฒฐ ์‹œ, (3) Intent ๋ถ„๋ฅ˜/๋ผ์šฐํŒ… ํ™•์ธ ์‹œ, (4) Redis Streams ์ด๋ฒคํŠธ ํŒŒ์ดํ”„๋ผ์ธ ๋””๋ฒ„๊น… ์‹œ.

Chat Agent Flow Guide

Chat ์„œ๋น„์Šค E2E ํ…Œ์ŠคํŠธ ๋ฐ ์ด๋ฒคํŠธ ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ€์ด๋“œ

Quick Reference

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                           Chat Agent E2E Flow                                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                              โ”‚
โ”‚  [1] POST /api/v1/chat              โ†’  Create session (chat_id)             โ”‚
โ”‚  [2] POST /api/v1/chat/{id}/messages โ†’  Send message (job_id)               โ”‚
โ”‚  [3] GET  /api/v1/chat/{job_id}/events โ†’  Subscribe SSE (via chat-vs)       โ”‚
โ”‚                                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Endpoints

StepEndpointServiceResponse
1POST /api/v1/chatchat-api{id, title, created_at}
2POST /api/v1/chat/{chat_id}/messageschat-api{job_id, stream_url, status}
3GET /api/v1/chat/{job_id}/eventssse-gateway (via chat-vs)SSE stream

API Endpoint ์—ญํ•  ์ƒ์„ธ

Endpoint์—ญํ• DB ์˜ํ–ฅWorker ์ œ์ถœ
POST /chat์„ธ์…˜ ์ƒ์„ฑchat.conversations INSERTNO
POST /chat/{id}/messages๋ฉ”์‹œ์ง€ ์ „์†ก์—†์Œ (Worker ์™„๋ฃŒ ํ›„ ๋ฐฐ์น˜)YES (RabbitMQ)
GET /chat/{job_id}/eventsSSE ๊ตฌ๋…์—†์Œ-

โš ๏ธ ์ค‘์š”: POST /chat๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ์„ธ์…˜๋งŒ ์ƒ์„ฑ๋˜๊ณ  ๋ฉ”์‹œ์ง€๋Š” ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€๋ฅผ Worker๋กœ ์ „์†กํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ POST /chat/{chat_id}/messages๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Istio VirtualService Routing

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Istio Routing Priority (chat-vs)                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                              โ”‚
โ”‚  ์ˆœ์„œ๊ฐ€ ์ค‘์š”! Regex๋Š” Prefix๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„ ๋‚ฎ์Œ โ†’ ๊ฐ™์€ VS ๋‚ด์—์„œ ์ˆœ์„œ๋กœ ์ œ์–ด   โ”‚
โ”‚                                                                              โ”‚
โ”‚  1. [regex]  /api/v1/chat/[^/]+/events  โ†’  sse-gateway (SSE)                โ”‚
โ”‚  2. [prefix] /api/v1/chat               โ†’  chat-api (REST)                  โ”‚
โ”‚                                                                              โ”‚
โ”‚  โš ๏ธ ์ˆœ์„œ ๋ฐ”๋€Œ๋ฉด SSE ์š”์ฒญ์ด chat-api๋กœ ๋ผ์šฐํŒ…๋จ!                               โ”‚
โ”‚                                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ํŒŒ์ผ: workloads/routing/chat/base/virtual-service.yaml

E2E Test Commands

TOKEN="<JWT_TOKEN>"

# 1. Create chat session
CHAT_RESPONSE=$(curl -s -X POST "https://api.dev.growbin.app/api/v1/chat" \
  -H "Content-Type: application/json" \
  -H "Cookie: s_access=$TOKEN" \
  -d '{"title": "E2E Test"}')
CHAT_ID=$(echo $CHAT_RESPONSE | jq -r '.id')

# 2. Send message
MSG_RESPONSE=$(curl -s -X POST "https://api.dev.growbin.app/api/v1/chat/${CHAT_ID}/messages" \
  -H "Content-Type: application/json" \
  -H "Cookie: s_access=$TOKEN" \
  -d '{"message": "ํ”Œ๋ผ์Šคํ‹ฑ ๋ถ„๋ฆฌ๋ฐฐ์ถœ ๋ฐฉ๋ฒ• ์•Œ๋ ค์ค˜"}')
JOB_ID=$(echo $MSG_RESPONSE | jq -r '.job_id')

# 3. Subscribe SSE
curl -sN --max-time 60 \
  "https://api.dev.growbin.app/api/v1/chat/${JOB_ID}/events" \
  -H "Accept: text/event-stream" \
  -H "Cookie: s_access=$TOKEN"

Intent Classification (10 types)

IntentNodeDescription
WASTEwaste_rag๋ถ„๋ฆฌ๋ฐฐ์ถœ ์งˆ๋ฌธ
CHARACTERcharacter์บ๋ฆญํ„ฐ ์ •๋ณด (gRPC)
LOCATIONlocation์žฅ์†Œ ๊ฒ€์ƒ‰ (Kakao API)
BULK_WASTEbulk_waste๋Œ€ํ˜•ํ๊ธฐ๋ฌผ
RECYCLABLE_PRICErecyclable_price์žฌํ™œ์šฉ ์‹œ์„ธ
COLLECTION_POINTcollection_point์ˆ˜๊ฑฐํ•จ ์œ„์น˜ (KECO API)
WEB_SEARCHweb_search์›น ๊ฒ€์ƒ‰
IMAGE_GENERATIONimage_generation์ด๋ฏธ์ง€ ์ƒ์„ฑ
GENERALgeneral์ผ๋ฐ˜ ๋Œ€ํ™”
(weather)weather๋‚ ์”จ enrichment (์ž๋™ ์ถ”๊ฐ€)

Dynamic Routing (Send API)

# Multi-Intent: "์ข…์ด ๋ฒ„๋ฆฌ๋Š” ๋ฒ•์ด๋ž‘ ์ˆ˜๊ฑฐํ•จ๋„ ์•Œ๋ ค์ค˜"
โ†’ intent = "waste"
โ†’ additional_intents = ["collection_point"]
โ†’ Sends:
   - Send("waste_rag", state)         # ์ฃผ intent
   - Send("collection_point", state)  # multi-intent
   - Send("weather", state)           # enrichment rule

# 3๊ฐœ ๋…ธ๋“œ ๋ณ‘๋ ฌ ์‹คํ–‰!

Redis Streams Event Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Redis Streams Event Pipeline                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                              โ”‚
โ”‚  [1] chat-worker                                                             โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ XADD chat:events:{shard} (Redis Streams)                            โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [2] rfr-streams-redis (Redis Sentinel)                                     โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ XREADGROUP (Consumer Group)                                         โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [3] event-router                                                            โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ PUBLISH sse:events:{job_id} (Redis Pub/Sub)                         โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [4] rfr-pubsub-redis (Redis Sentinel)                                      โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ SUBSCRIBE (SSE Gateway)                                              โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [5] sse-gateway โ†’ SSE Stream โ†’ Client                                      โ”‚
โ”‚                                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Redis Instances

InstanceNamespacePurposeProtocol
rfr-streams-redisredisEvent Streams (XADD/XREADGROUP)Redis Streams
rfr-pubsub-redisredisSSE Broadcast (PUBLISH/SUBSCRIBE)Redis Pub/Sub

Message Persistence Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Message Persistence Pipeline                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                              โ”‚
โ”‚  [1] chat-worker                                                             โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ XADD chat:events:{shard} (done event ํฌํ•จ)                           โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [2] Redis Streams                                                           โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”œโ”€โ”€โ–บ Consumer Group: eventrouter โ†’ event-router โ†’ SSE                 โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ””โ”€โ”€โ–บ Consumer Group: chat-persistence โ†’ chat-persistence-consumer                 โ”‚
โ”‚                   โ”‚                                                          โ”‚
โ”‚                   โ–ผ                                                          โ”‚
โ”‚  [3] chat-persistence-consumer (ChatPersistenceConsumer)                                โ”‚
โ”‚       โ”‚                                                                      โ”‚
โ”‚       โ”‚ INSERT INTO chat.messages                                            โ”‚
โ”‚       โ–ผ                                                                      โ”‚
โ”‚  [4] PostgreSQL (chat.messages)                                             โ”‚
โ”‚                                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Persistence Components

ComponentNamespaceImageEntry Point
chat-persistence-consumerchatchat-api-dev-latestpython -m chat.persistence_consumer

Consumer Group ํ™•์ธ

# Consumer Group ๋ชฉ๋ก ์กฐํšŒ
kubectl exec -n redis rfr-streams-redis-0 -c redis -- \
  redis-cli XINFO GROUPS chat:events:0

# Expected:
# 1) name: eventrouter
# 2) name: chat-persistence

# ๋ฉ”์‹œ์ง€ ์ˆ˜ ํ™•์ธ
kubectl exec -n postgres deploy/postgresql -- \
  psql -U sesacthon -d ecoeco -c "SELECT COUNT(*) FROM chat.messages;"

Persistence ๊ด€๋ จ ํ…Œ์ด๋ธ”

TableSchemaPurpose
chat.conversationschat๋Œ€ํ™” ์„ธ์…˜
chat.messageschat๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
checkpointspublicLangGraph ์ฒดํฌํฌ์ธํŠธ
checkpoint_blobspublic์ฒดํฌํฌ์ธํŠธ Blob
checkpoint_writespublic์ฒดํฌํฌ์ธํŠธ Write Log

Hop-by-Hop Address Reference

HopComponentKube DNSPod DNS (Master)ProtocolPort
1โ†’2chat-worker โ†’ Redis Streamsrfr-streams-redis.redis.svc.cluster.localrfr-streams-redis-0.rfr-streams-redis.redis.svc.cluster.localXADD6379
2โ†’3Redis Streams โ†’ event-router(Consumer Group)-XREADGROUP6379
3โ†’4event-router โ†’ Redis Pub/Subrfr-pubsub-redis.redis.svc.cluster.localrfr-pubsub-redis-0.rfr-pubsub-redis.redis.svc.cluster.localPUBLISH6379
4โ†’5Redis Pub/Sub โ†’ sse-gateway(Subscriber)-SUBSCRIBE6379

Stream Keys & Patterns

Key PatternExamplePurpose
chat:events:{shard}chat:events:0 ~ chat:events:3Sharded event streams (4 shards)
chat:published:{job_id}:{stage}:{seq}chat:published:abc123:intent:10Idempotency marker (TTL 2h)
chat:tokens:{job_id}chat:tokens:abc123Token stream for recovery
chat:token_state:{job_id}chat:token_state:abc123Accumulated text snapshot
sse:events:{job_id}sse:events:abc123Pub/Sub channel for SSE

Environment Variables

ComponentVariableValue (Dev)
chat-workerCHAT_WORKER_REDIS_STREAMS_URLredis://rfr-streams-redis.redis.svc.cluster.local:6379/0
chat-workerCHAT_WORKER_REDIS_URLredis://rfr-pubsub-redis-0.rfr-pubsub-redis.redis.svc.cluster.local:6379/0
event-routerREDIS_STREAMS_URLredis://rfr-streams-redis.redis.svc.cluster.local:6379/0
event-routerREDIS_PUBSUB_URLredis://rfr-pubsub-redis.redis.svc.cluster.local:6379/0

Debug Commands (Redis)

# Redis Streams ํ™•์ธ (rfr-streams-redis)
kubectl exec -n redis rfr-streams-redis-0 -- redis-cli XLEN chat:events:0
kubectl exec -n redis rfr-streams-redis-0 -- redis-cli XRANGE chat:events:0 - + COUNT 5

# Pub/Sub ๋ชจ๋‹ˆํ„ฐ๋ง (rfr-pubsub-redis)
kubectl exec -n redis rfr-pubsub-redis-0 -- redis-cli PSUBSCRIBE "sse:events:*"

# Consumer Group ์ƒํƒœ ํ™•์ธ
kubectl exec -n redis rfr-streams-redis-0 -- redis-cli XINFO GROUPS chat:events:0

Troubleshooting

Issue 1: SSE ์š”์ฒญ์ด 404

์ฆ์ƒ: GET /api/v1/chat/{job_id}/events โ†’ 404

์›์ธ: VirtualService์—์„œ SSE ๊ทœ์น™์ด prefix ๋’ค์— ๋ฐฐ์น˜

ํ•ด๊ฒฐ: chat-vs์—์„œ SSE events ๊ทœ์น™์„ prefix ๊ทœ์น™ ์•ž์— ๋ฐฐ์น˜

Issue 2: TaskIQ ๋ฉ”์‹œ์ง€ ํŒŒ์‹ฑ ์˜ค๋ฅ˜

์ฆ์ƒ: Cannot parse message: b'{"args": [], "kwargs": {...}}'

์›์ธ: Worker๊ฐ€ TaskiqMessage ์ „์ฒด ํ˜•์‹์„ ๊ธฐ๋Œ€

ํ•ด๊ฒฐ: task_id, task_name, labels ํ•„๋“œ ํฌํ•จ

message = {
    "task_id": job_id,
    "task_name": "chat.process",
    "labels": {},
    "args": [],
    "kwargs": {...},
}

Issue 3: Redis READONLY ์˜ค๋ฅ˜

์ฆ์ƒ: -READONLY You can't write against a read only replica

์›์ธ: Headless service๊ฐ€ replica๋กœ ์—ฐ๊ฒฐ

ํ•ด๊ฒฐ: Master pod DNS ์ง์ ‘ ์‚ฌ์šฉ

rfr-pubsub-redis-0.rfr-pubsub-redis.redis.svc.cluster.local:6379

Issue 4: LangGraph State Access ์˜ค๋ฅ˜

์ฆ์ƒ: KeyError: 'job_id'

์›์ธ: astream_events์—์„œ ์ผ๋ถ€ ์ด๋ฒคํŠธ์— state ๋ˆ„๋ฝ

ํ•ด๊ฒฐ: .get() ๋ฉ”์„œ๋“œ๋กœ ์•ˆ์ „ํ•œ ์ ‘๊ทผ

# Bad
job_id = state["job_id"]

# Good
job_id = state.get("job_id", "")

Issue 5: Send API ๋ณ‘๋ ฌ ์‹คํ–‰ ์ถฉ๋Œ

์ฆ์ƒ: InvalidUpdateError: Can receive only one value per step

์›์ธ: StateGraph(dict) + Send API ๋ณ‘๋ ฌ ์‹คํ–‰

ํ•ด๊ฒฐ: Typed State + Annotated Reducer (์ง„ํ–‰ ์ค‘)

# ์ž„์‹œ: dynamic_routing ๋น„ํ™œ์„ฑํ™”
enable_dynamic_routing=False

# ๊ทผ๋ณธ: StateGraph(ChatState) + Reducer

Issue 6: Message Persistence ์‹คํŒจ (chat-persistence-consumer)

์ฆ์ƒ: ๋Œ€ํ™”๋Š” ์ •์ƒ ์ง„ํ–‰๋˜์ง€๋งŒ chat.messages ํ…Œ์ด๋ธ”์— ๋ฉ”์‹œ์ง€๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š์Œ

์ง„๋‹จ:

# 1. chat.messages ๋ ˆ์ฝ”๋“œ ์ˆ˜ ํ™•์ธ
kubectl exec -n postgres deploy/postgresql -- \
  psql -U sesacthon -d ecoeco -c "SELECT COUNT(*) FROM chat.messages;"

# 2. chat-persistence Consumer Group ํ™•์ธ
kubectl exec -n redis rfr-streams-redis-0 -c redis -- \
  redis-cli XINFO GROUPS chat:events:0 | grep -A5 chat-persistence

# 3. chat-persistence-consumer Pod ์ƒํƒœ ํ™•์ธ
kubectl get pods -n chat -l app=chat-persistence-consumer

๊ฐ€๋Šฅํ•œ ์›์ธ:

์›์ธ์ฆ์ƒํ•ด๊ฒฐ
chat-persistence-consumer ๋ฏธ๋ฐฐํฌPod ์—†์Œworkloads/domains/chat/base/deployment-persistence-consumer.yaml ๋ฐฐํฌ
livenessProbe ์‹คํŒจCrashLoopBackOff/proc/1/cmdline ํŒจํ„ด ์‚ฌ์šฉ
RabbitMQ ํ ๋ถˆ์ผ์น˜taskiq ํ์— ๋ฉ”์‹œ์ง€ ์Œ“์ž„CHAT_RABBITMQ_QUEUE=chat.process ์„ค์ •

์ฐธ์กฐ: docs/troubleshooting/chat-messages-not-persisted.md

Issue 7: SSE ์ด๋ฒคํŠธ ์ˆ˜์‹  ๋ถˆ๊ฐ€ (Redis ๋ถˆ์ผ์น˜)

SSE Event Types (๊ฒ€์ฆ๋จ - 2026-01-19):

Eventseq ๋ฒ”์œ„Description
intent1-999Intent ๋ถ„๋ฅ˜ ์™„๋ฃŒ
router1-999๋ผ์šฐํŒ… ์™„๋ฃŒ
answer1-999๋‹ต๋ณ€ ์ƒ์„ฑ ์‹œ์ž‘/์™„๋ฃŒ
token1001+์‹ค์‹œ๊ฐ„ ํ† ํฐ ์ŠคํŠธ๋ฆฌ๋ฐ (ํ•ต์‹ฌ!)
token_recovery-๋Šฆ์€ ๊ตฌ๋…์ž์šฉ ์Šค๋ƒ…์ƒท
done1-999์ฒ˜๋ฆฌ ์™„๋ฃŒ + ์ตœ์ข… ๊ฒฐ๊ณผ
error-์˜ค๋ฅ˜ ๋ฐœ์ƒ
keepalive-์—ฐ๊ฒฐ ์œ ์ง€

์ƒ์„ธ ํ˜•์‹: references/sse-event-format.md ์ฐธ์กฐ

Redis Streams Pipeline

chat-worker โ†’ XADD chat:events:{0-3} โ†’ event-router โ†’ PUBLISH sse:events:{job_id} โ†’ sse-gateway
InstancePurpose
rfr-streams-redisEvent Streams (XADD/XREADGROUP)
rfr-pubsub-redisSSE Broadcast (PUBLISH/SUBSCRIBE)

Debug Commands

# Worker logs
kubectl logs -n chat -l app=chat-worker -f --tail=100

# Event Router logs
kubectl logs -n event-router -l app=event-router -f --tail=50

# Redis Streams check
kubectl exec -n redis rfr-streams-redis-0 -c redis -- redis-cli XLEN chat:events:0
kubectl exec -n redis rfr-streams-redis-0 -c redis -- redis-cli XINFO GROUPS chat:events:0

# Pub/Sub monitoring
kubectl exec -n redis rfr-pubsub-redis-0 -c redis -- redis-cli PSUBSCRIBE "sse:events:*"

Reference Files

  • SSE Event Format: references/sse-event-format.md โ† Token streaming ์ด๋ฒคํŠธ ํ˜•์‹ ์ƒ์„ธ
  • E2E Test Plan: docs/reports/e2e-intent-test-plan.md
  • Troubleshooting (E2E): docs/troubleshooting/chat-worker-e2e-infra-fixes.md
  • Troubleshooting (Persistence): docs/troubleshooting/chat-messages-not-persisted.md
  • VirtualService (Chat): workloads/routing/chat/base/virtual-service.yaml
  • VirtualService (SSE): workloads/domains/sse-gateway/base/virtualservice.yaml
  • Frontend Spec: docs/specs/chat-agent-frontend-spec.md
  • Dynamic Routing: docs/blogs/applied/30-langgraph-dynamic-routing-send-api.md
  • Native Streaming: docs/blogs/applied/32-langgraph-native-streaming.md
PRTitleIssue
#400fix(routing): chat SSE ์ด๋ฒคํŠธ๋ฅผ sse-gateway๋กœ ๋ผ์šฐํŒ…SSE ๋ผ์šฐํŒ… ์˜ค๋ฅ˜
#401fix(chat): TaskIQ ๋ฉ”์‹œ์ง€ ํ˜•์‹ ์ˆ˜์ •๋ฉ”์‹œ์ง€ ํŒŒ์‹ฑ ์˜ค๋ฅ˜
#408-410fix(chat_worker): BaseCheckpointSaver ์ƒ์†Checkpointer ์˜ค๋ฅ˜
#411fix(chat-worker): Redis URL ์ˆ˜์ •DNS ํ•ด์„ ์‹คํŒจ
#412fix(chat-worker): Master pod DNS ์‚ฌ์šฉREADONLY ์˜ค๋ฅ˜
#413fix(chat_worker): max_completion_tokens ์‚ฌ์šฉOpenAI API ์˜ค๋ฅ˜
#415feat(chat_worker): Channel Separation + PrioritySend API ๋ณ‘๋ ฌ ์ถฉ๋Œ
#422-424feat(chat): ChatPersistenceConsumer ๋ฐฐํฌ๋ฉ”์‹œ์ง€ ์˜์†ํ™”
#425fix(chat): CachedPostgresSaver ํŠธ๋žœ์žญ์…˜CREATE INDEX CONCURRENTLY ์˜ค๋ฅ˜
#440fix(chat_worker): LangChain LLM ์ง์ ‘ ํ˜ธ์ถœ๋กœ ํ† ํฐ ์ŠคํŠธ๋ฆฌ๋ฐstream_mode="messages" ์บก์ฒ˜ ์•ˆ๋จ

ใ‚นใ‚ณใ‚ข

็ทๅˆใ‚นใ‚ณใ‚ข

60/100

ใƒชใƒใ‚ธใƒˆใƒชใฎๅ“่ณชๆŒ‡ๆจ™ใซๅŸบใฅใ่ฉ•ไพก

โœ“SKILL.md

SKILL.mdใƒ•ใ‚กใ‚คใƒซใŒๅซใพใ‚Œใฆใ„ใ‚‹

+20
โœ“LICENSE

ใƒฉใ‚คใ‚ปใƒณใ‚นใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

+10
โ—‹่ชฌๆ˜Žๆ–‡

100ๆ–‡ๅญ—ไปฅไธŠใฎ่ชฌๆ˜ŽใŒใ‚ใ‚‹

0/10
โ—‹ไบบๆฐ—

GitHub Stars 100ไปฅไธŠ

0/15
โ—‹ๆœ€่ฟ‘ใฎๆดปๅ‹•

3ใƒถๆœˆไปฅๅ†…ใซๆ›ดๆ–ฐใŒใ‚ใ‚‹

0/10
โ—‹ใƒ•ใ‚ฉใƒผใ‚ฏ

10ๅ›žไปฅไธŠใƒ•ใ‚ฉใƒผใ‚ฏใ•ใ‚Œใฆใ„ใ‚‹

0/5
โœ“Issue็ฎก็†

ใ‚ชใƒผใƒ—ใƒณIssueใŒ50ๆœชๆบ€

+5
โœ“่จ€่ชž

ใƒ—ใƒญใ‚ฐใƒฉใƒŸใƒณใ‚ฐ่จ€่ชžใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

+5
โ—‹ใ‚ฟใ‚ฐ

1ใคไปฅไธŠใฎใ‚ฟใ‚ฐใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

0/5

ใƒฌใƒ“ใƒฅใƒผ

๐Ÿ’ฌ

ใƒฌใƒ“ใƒฅใƒผๆฉŸ่ƒฝใฏ่ฟ‘ๆ—ฅๅ…ฌ้–‹ไบˆๅฎšใงใ™