
Eco²
Backend · Cloud infrastructure
Hướng dẫn phân loại rác từ một bức ảnh và một cuộc trò chuyện.
Trợ lý AI tất cả trong một, hiểu cách phân loại rác tái chế ở Hàn Quốc.
Eco² vocabulary
- edge
- auth offload
- domain api
- task queue
- scan pipeline
- chat agent
- event bus
- cluster
- gitops
- autoscaling
- observability
- load test
- evaluation
Edge: ROUTE 53 · ALB · HTTPS 443 · internet-facing · external-dns · istio-ingressgateway · NodePort · eco2-gateway · VirtualService · x-canary · AuthorizationPolicy · ENVOY SIDECAR · kiali-serverAuth Offload: AuthorizationPolicy · action: CUSTOM · notPaths · envoyExtAuthzGrpc · failOpen: false · CheckRequest · x-user-id · BlacklistCache · BootstrapFromRedis · outbox:blacklist · blacklist.events · UsersServiceDomain API: VirtualService · x-user-id · /api/v1/scan · X-Idempotency-Key · job_id · stream_url · 202 ACCEPTED · Retry-After · ScanTask · UsersService · ImageService · ENVOY SIDECARTask Queue: outbox:blacklist · blacklist.events · RabbitmqCluster · scan.direct · reward.events · dlx · x-dead-letter-exchange · chat.process · AioPikaBroker · task_acks_late · apply_async · gevent · scan.vision · scan.rule · scan.answer · scan.reward · CELERY CHAIN · QueueLength · inspect ping · QUEUE SATURATIONScan Pipeline: ScanTask · scan.direct · reward.events · apply_async · gevent · scan.vision · scan.rule · scan.answer · scan.reward · CELERY CHAIN · RewardStep · item_class_list.yaml · situation_tags.yaml · LITE RAG · JsonRegulationRetriever · PipelineStage · waste_rag · XADD · domain: worker-ai · pipeline_duration · vision_stage_durationChat Agent: ImageService · chat.process · AioPikaBroker · create_chat_graph · route_by_intent · MULTI-INTENT FANOUT · waste_rag · recyclable_price · collection_point · image_generation · aggregator · thread_id · SyncableRedisSaver · AsyncPostgresSaver · LANGSMITH_TRACING · regenerateEvent Bus: job_id · stream_url · PipelineStage · XADD · scan:events:{shard} · XREADGROUP · XACK · XAUTOCLAIM · PendingReclaimer · STATE KV · scan:state:{job_id} · sse:events:{shard} · SSEBroadcastManager · Last-Event-ID · scan_sse_connections_active · ServiceMonitor · traceparentCluster: NodePort · kubeadm init · kubeadm join · vxlanMode · BGPConfiguration · module "vpc" · worker_ai · nodeSelector · domain: worker-ai · NoSchedule · 24-NODE CLUSTERGitOps: external-dns · x-canary · dev-root · ApplicationSet · sync-wave · selfHeal · ignoreDifferences · argocd-image-updater · update-strategy: digest · write-back-method · targetRevision · ci-services.ymlAutoscaling: ignoreDifferences · ScaledObject · QueueLength · cooldownPeriod · pollingInterval · fallback · maxReplicaCount · stabilizationWindowSeconds · HorizontalPodAutoscaler · scan_sse_connections_active · prometheus-adapterObservability: 24-NODE CLUSTER · prometheus-adapter · FLUENT BIT · Elasticsearch · Kibana · kube-prometheus-stack · AlertmanagerConfig · ServiceMonitor · JAEGER · OPENTELEMETRY · traceparent · kiali-server · LANGSMITH_TRACINGLoad Test: k6 · ramping-vus · thresholds · pipeline_duration · vision_stage_duration · poll_timeout · inspect ping · WORKER RESTART · POLLING TIMEOUT · QUEUE SATURATIONEvaluation: EvalConfig · eval_mode · eval_sample_rate · eval_cost_budget_daily_usd · EvalResult · EvalGrade · code_grader · llm_grader · CUSUM · regenerate · OpenAIBARSEvaluator
24 node thực sự kết nối với nhau ra sao, từng luồng một.
Kubernetes tự quản lý, dựng bằng kubeadm. Bản đồ đặt 24 node vật lý và các workload trên chúng vào năm lớp của README, rồi nối chúng bằng các kết nối thực tế. Chọn một luồng thì chỉ đường đi đó được làm nổi bật, với các bước đánh số.
Nó nhận diện rác thải từ ảnh và giải thích cách bỏ, và trong hội thoại thì gọi các công cụ về địa điểm, thời tiết, giá thu mua đồ tái chế và tìm kiếm web, đồng thời tạo hình ảnh. Trong dự án nhóm này, tôi bắt đầu từ nền tảng cụm và triển khai rồi mở rộng phạm vi sang backend miền, xử lý bất đồng bộ, tác nhân hội thoại và kiểm chứng.
Từ phát triển trong cuộc thi đến củng cố backend và vận hành
Dịch vụ đã kết thúc vận hành; video demo đã ghi và báo cáo kỹ thuật cho thấy nó hoạt động.
Yêu cầu Scan → gửi trực tiếp
Một yêu cầu phân tích ảnh trở thành công việc trong hàng đợi, và các sự kiện tiến độ của nó quay về cùng trình duyệt.
Trình duyệttra cứu DNS: Tra cứu DNSRoute 53
Chỉ tên được phân giải từ bản ghi Route 53. Yêu cầu không đi qua DNS.
Trình duyệtluồng dữ liệu: HTTPS 443AWS ALB
TLS kết thúc tại ALB.
AWS ALBluồng dữ liệu: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayĐược chuyển đến Istio ingress Envoy qua NodePort của EC2 instance target.
Istio ingress Envoy
k8s-ingress-gatewayluồng dữ liệu: VirtualServiceScan APIk8s-api-scanĐược định tuyến đến Scan API, nơi đưa công việc vào hàng đợi và trả về job_id ngay; truy vấn kết quả nhận 202 cho đến khi công việc xong.
Scan API
k8s-api-scanluồng dữ liệu: Tác vụ CeleryRabbitMQk8s-rabbitmqTác vụ Scan được đưa vào RabbitMQ.
RabbitMQ
k8s-rabbitmqluồng dữ liệu: consumescan-workerk8s-worker-ai · k8s-worker-ai-2scan-worker (Celery · gevent) trong pool AI nhận và chạy Vision → Rule → Answer → Reward. Bốn giai đoạn này là các Celery task nối thành chuỗi, không phải node.
Pool AI
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: HTTPSLLM APIWorker scan và chat gọi API của OpenAI và Google.
scan-worker
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: XADD scan:eventsStreamsk8s-redis-streamsSự kiện tiến độ được ghi vào một trong bốn shard của Streams.
Streams
k8s-redis-streamsluồng dữ liệu: XREADGROUPEvent Routerk8s-event-routerEvent Router đọc chúng theo consumer group và XACK sau khi xử lý; pending và reclaim giữ việc gửi ở mức at-least-once.
Event Router
k8s-event-routerluồng dữ liệu: SETEX stateState KVk8s-redis-streamsNó ghi trạng thái mới nhất và một dấu chống xử lý trùng vào State KV trên cùng node Redis.
Event Router
k8s-event-routerluồng dữ liệu: PUBLISHPub/Subk8s-redis-pubsubĐược gửi theo thời gian thực trên kênh của công việc trong shard Pub/Sub.
Pub/Sub
k8s-redis-pubsubluồng dữ liệu: SUBSCRIBESSE Gatewayk8s-sse-gatewaySSE Gateway đăng ký qua một listener dùng chung cho mỗi tiến trình và phân phát đến từng trình duyệt đang kết nối.
SSE Gateway
k8s-sse-gatewayluồng dữ liệu: Luồng SSETrình duyệtMột kết nối được mở qua cùng đường ALB và ingress. Việc gửi là at-least-once; các bản trùng được xử lý idempotent.
State KV
k8s-redis-streamsluồng dữ liệu: Khôi phục khi kết nối lạiSSE Gatewayk8s-sse-gatewayKhi kết nối lại với Last-Event-ID, State KV và Streams được lưu giữ sẽ bù các sự kiện bị lỡ.
Chat · token và lưu trữ
Token của Chat đi cùng đường sự kiện với Scan; checkpoint và tin nhắn được lưu bởi hai workload khác nhau.
Trình duyệtluồng dữ liệu: HTTPS 443AWS ALB
TLS kết thúc tại ALB.
AWS ALBluồng dữ liệu: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayĐược chuyển đến Istio ingress Envoy qua NodePort của EC2 instance target.
Istio ingress Envoy
k8s-ingress-gatewayluồng dữ liệu: VirtualServiceChat APIk8s-api-chatĐược định tuyến đến Chat API.
Chat API
k8s-api-chatluồng dữ liệu: Tác vụ TaskiqRabbitMQk8s-rabbitmqTác vụ Chat được đưa vào RabbitMQ.
RabbitMQ
k8s-rabbitmqluồng dữ liệu: consumechat-workerk8s-worker-ai · k8s-worker-ai-2chat-worker (Taskiq · asyncio) phân loại ý định bằng LangGraph, rồi chạy vision nếu cần, các công cụ được chọn, aggregator và bản tóm tắt nếu cần, rồi trả lời. Mỗi yêu cầu chỉ chạy các nhánh nó cần.
Pool AI
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: HTTPSLLM APIWorker scan và chat gọi API của OpenAI và Google.
chat-worker
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: XADD chat:eventsStreamsk8s-redis-streamsToken và sự kiện giai đoạn được ghi vào bốn shard của Streams; từ đó đường gửi giống như của Scan.
Streams
k8s-redis-streamsluồng dữ liệu: XREADGROUPEvent Routerk8s-event-routerEvent Router đọc chúng theo consumer group và XACK sau khi xử lý; pending và reclaim giữ việc gửi ở mức at-least-once.
Event Router
k8s-event-routerluồng dữ liệu: PUBLISHPub/Subk8s-redis-pubsubĐược gửi theo thời gian thực trên kênh của công việc trong shard Pub/Sub.
Pub/Sub
k8s-redis-pubsubluồng dữ liệu: SUBSCRIBESSE Gatewayk8s-sse-gatewaySSE Gateway đăng ký qua một listener dùng chung cho mỗi tiến trình và phân phát đến từng trình duyệt đang kết nối.
SSE Gateway
k8s-sse-gatewayluồng dữ liệu: Luồng SSETrình duyệtMột kết nối được mở qua cùng đường ALB và ingress. Việc gửi là at-least-once; các bản trùng được xử lý idempotent.
chat-worker
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: checkpointCachek8s-redis-cacheCheckpoint của LangGraph được giữ trong Redis Cache.
Cache
k8s-redis-cacheluồng dữ liệu: đọc checkpointcheckpoint-syncerk8s-worker-ai · k8s-worker-ai-2checkpoint-syncer đọc checkpoint từ Redis.
checkpoint-syncer
k8s-worker-ai · k8s-worker-ai-2luồng dữ liệu: đồng bộ sang PostgreSQLPostgreSQLk8s-postgresqlAsyncPostgresSaver lưu nó vào PostgreSQL một cách bất đồng bộ.
Streams
k8s-redis-streamsluồng dữ liệu: chat-persistence grouppersistence consumerk8s-api-chatPersistence consumer trên node Chat đọc các sự kiện done theo consumer group.
persistence consumer
k8s-api-chatluồng dữ liệu: lưu tin nhắnPostgreSQLk8s-postgresqlTin nhắn Chat được lưu vào PostgreSQL. Đây là workload khác với checkpoint-syncer.
Đầu vào · xác thực
Đường đi đồng bộ từ điểm vào của cụm đến một domain API. Chỉ các đường dẫn có chính sách bảo vệ mới kiểm tra xác thực trước; lưu lượng pod giữa các node chạy trên Calico VXLAN.
Trình duyệttra cứu DNS: Tra cứu DNSRoute 53
Chỉ tên được phân giải từ bản ghi Route 53. Yêu cầu không đi qua DNS.
Trình duyệtluồng dữ liệu: HTTPS 443AWS ALB
TLS kết thúc tại ALB.
AWS ALBluồng dữ liệu: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayĐược chuyển đến Istio ingress Envoy qua NodePort của EC2 instance target.
Istio ingress Envoy
k8s-ingress-gatewayluồng dữ liệu: ext-authz · gRPC 50051ext-authzk8s-api-authChỉ các đường dẫn thuộc chính sách CUSTOM mới hỏi Go ext-authz trước (timeout 2 s, failOpen false). Các đường dẫn như login, health và docs được loại trừ.
ext-authz
k8s-api-authluồng dữ liệu: tra blacklistblacklistk8s-redis-authToken được kiểm tra với blacklist trong Redis.
Istio ingress Envoy
k8s-ingress-gatewayluồng dữ liệu: VirtualServiceChat APIk8s-api-chatĐược định tuyến đến Chat API.
Chat API
k8s-api-chatluồng dữ liệu: gRPCimages APIk8s-api-imageLệnh gọi đồng bộ chat → images được nêu trong README.
auth API
k8s-api-authluồng dữ liệu: gRPCusers APIk8s-api-usersLệnh gọi đồng bộ auth → users được nêu trong README.
Pool storage · ghi bất đồng bộ
Blacklist xác thực và quyền sở hữu nhân vật được lưu bởi các worker của pool storage, nhận công việc từ RabbitMQ.
blacklist
k8s-redis-authluồng dữ liệu: outbox:blacklistauth-relayk8s-worker-storage · k8s-worker-storage-2auth-relay (auth-Bus trong README) đọc outbox của Redis.
auth-relay
k8s-worker-storage · k8s-worker-storage-2luồng dữ liệu: blacklist.eventsRabbitMQk8s-rabbitmqNó phát lại lên RabbitMQ, có thử lại và DLQ.
RabbitMQ
k8s-rabbitmqluồng dữ liệu: blacklist.eventsauth-workerk8s-worker-storage · k8s-worker-storage-2auth-worker nhận các sự kiện blacklist.
auth-worker
k8s-worker-storage · k8s-worker-storage-2luồng dữ liệu: lưu blacklistblacklistk8s-redis-authChúng được lưu vào blacklist trong Redis.
RabbitMQ
k8s-rabbitmqluồng dữ liệu: Celery batchusers-workerk8s-worker-storage · k8s-worker-storage-2users-worker nhận công việc về quyền sở hữu nhân vật theo lô.
users-worker
k8s-worker-storage · k8s-worker-storage-2luồng dữ liệu: UPSERTPostgreSQLk8s-postgresqlĐược áp dụng vào PostgreSQL.
Cấu hình · mở rộng · quan sát
Platform Layer không phải một bước yêu cầu đi qua; nó triển khai, cấu hình, mở rộng và quan sát các lớp khác. Các node là instance EC2 tạo bằng Terraform, với kubeadm và Calico được cài bằng Ansible; CI chỉ kiểm tra Terraform và không bao giờ tự động apply.
GitHub Actions · Docker Hubcấu hình · mở rộng: image · revision của manifestArgo CD image updater
k8s-masterArgo CD đưa cấu hình cụm về khớp qua App-of-Apps và Sync Waves. Chỉ vị trí control-plane của image updater được xác nhận.
ALB Controller · ExternalDNS
k8s-mastercấu hình · mở rộng: Ingress → ALB · bản ghi DNSAWS quản lýALB Controller và ExternalDNS cấu hình tài nguyên phía AWS. Đây không phải đường yêu cầu đi qua.
Istiod
k8s-mastercấu hình · mở rộng: cấu hình xDSIstio ingress Envoyk8s-ingress-gatewayIstiod đẩy cấu hình Gateway, VirtualService và EnvoyFilter qua xDS đến ingress Envoy và Envoy sidecar của từng API.
KEDA
k8s-monitoringcấu hình · mở rộng: số podPool AIk8s-worker-ai · k8s-worker-ai-2KEDA điều chỉnh số pod của worker. Số node không thay đổi.
KEDA
k8s-monitoringcấu hình · mở rộng: số podPool storagek8s-worker-storage · k8s-worker-storage-2Các pod worker của pool storage được mở rộng theo cùng cách.
Fluent Bitkhả năng quan sát: logElasticsearch
k8s-loggingFluent Bit là một DaemonSet trên mọi node và gửi log đến Elasticsearch.
Pool AI
k8s-worker-ai · k8s-worker-ai-2khả năng quan sát: Truy vết lượt chạy LLMLangSmithTrace của các lệnh gọi LLM được gửi đến LangSmith, một SaaS bên ngoài.
Mọi kết nối có cùng độ đậm. Chọn một luồng để chỉ làm nổi bật đường đi của nó, kèm các bước được đánh số.
- Nét liền · hướng đi của yêu cầu, công việc, sự kiện và thao tác ghi
- Nét chấm · tra cứu DNS; yêu cầu không đi qua đây
- Nét đứt · cấu hình hoặc mở rộng; không phải đường dữ liệu
- Nét gạch dài xen chấm · log và truy vết lượt chạy
- Nét đen đậm · luồng được chọn; số chỉ thứ tự
- Hộp · một node vật lý, tên host ở phần đầu
- Chip bên trong · một workload chạy trên node đó
- Khung nét đứt · một pool lập lịch hai node
- Hộp nét đứt · bên ngoài cụm, không tính là node
- Viền cam · nơi một mô hình quyết định
- Control plane1 node
- Domain API8 node
- Pool AI2 node
- Pool storage2 node
- Data5 node
- Platform6 node
Năm lớp của README (các dải) và các nhóm vật lý (màu phần đầu mỗi node) nhóm cùng 24 node theo cách khác nhau. State KV dùng chung node với Streams và không được tính riêng; Route 53, ALB và các dịch vụ ngoài cụm không phải node. Pool là phạm vi lập lịch, không có nghĩa mọi pod đều chạy trên cả hai node.
24node
24 = 1 control plane + 8 API + 4 worker + 5 data + 6 platform
1Control plane
role: control-planekube-apiserver · scheduler · controller-manager · etcd, cùng các controller của nền tảng.
- k8s-master
8Domain API
domain: <dịch vụ>Mỗi miền một node. Lệnh gọi nội bộ dùng gRPC.
- k8s-api-auth
- k8s-api-users
- k8s-api-scan
- k8s-api-character
- k8s-api-location
- k8s-api-image
- k8s-api-chat
- k8s-api-info
4Execution worker
Hai pool lập lịch, mỗi pool hai node. KEDA thay đổi số pod; số node giữ nguyên.
Pool storage
domain: worker-storage- k8s-worker-storage
- k8s-worker-storage-2
Pool AI
domain: worker-ai- k8s-worker-ai
- k8s-worker-ai-2
5Data
Redis được tách ra bốn node theo mục đích. Lưu trữ PostgreSQL là EBS CSI · gp3.
- k8s-postgresql
- k8s-redis-auth
- k8s-redis-streams
- k8s-redis-cache
- k8s-redis-pubsub
6Platform
Nhắn tin, khả năng quan sát, đầu vào và gửi thời gian thực mỗi thứ có node riêng.
- k8s-rabbitmq
- k8s-monitoring
- k8s-logging
- k8s-ingress-gateway
- k8s-sse-gateway
- k8s-event-router
- Mọi node
- Fluent Bit · DaemonSet
- Calico
Mô hình đã cấu hình · theo nhà cung cấp
Dịch vụ đã ngừng hoạt động, nên đây là các mô hình được cấu hình trong kho mã, không phải lựa chọn có sẵn hôm nay. Mỗi vai trò được liệt kê riêng; đây không phải danh sách các lượt chạy đã kiểm chứng.
OpenAI
Nhà cung cấp mặc định của Chat
Chat
openai/gpt-5.2mặc định
Mô hình mặc định là gpt-5.2; nếu yêu cầu chỉ định mô hình thì dùng mô hình đó.
Scan
gpt-5.2mặc địnhgpt-5.2-progpt-5.1gpt-5gpt-5-progpt-5-mini
Các mô hình nằm ngoài ánh xạ 13 mô hình bị từ chối.
Ánh xạ hình ảnh
gpt-image-1.5
Tách biệt với gpt-5.2, mô hình điều phối của Responses. Tạo hình ảnh mặc định bị tắt.
Đánh giá
gpt-4o-minimặc định
LLM grader cho đánh giá hội thoại, tách biệt với mô hình Chat.
Google
Khi Chat chọn Google
Chat
google/gemini-3-pro-previewgoogle/gemini-3-flash-preview
Khi chọn Google, gemini-3-flash-preview là mặc định.
Scan
gemini-3-pro-previewgemini-3-flash-previewgemini-2.5-progemini-2.5-flashgemini-2.5-flash-litegemini-2.0-flashgemini-2.0-flash-lite
Ánh xạ hình ảnh
gemini-3-pro-image-preview
Nguồnbackend main d8840c7Commit công khai September 29, 2026 · kiểm tra October 8, 2026