
Eco²
Backend · Cloud infrastructure
Le tri des déchets, à partir d’une photo et d’une conversation.
Un assistant IA tout-en-un qui comprend le fonctionnement du tri des déchets en Corée.
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
Comment les 24 nœuds se relient réellement, un flux à la fois.
Kubernetes autogéré, construit avec kubeadm. La carte place les 24 nœuds physiques et leurs workloads dans les cinq couches du README et les relie par leurs connexions réelles. Choisir un flux ne met en évidence que ce chemin, avec des étapes numérotées.
Il identifie un déchet sur une photo et explique comment s’en défaire, et en conversation il appelle des outils de lieux, de météo, de prix du recyclage et de recherche web, et génère des images. Dans ce projet d’équipe, j’ai commencé par le cluster et le socle de déploiement, puis élargi mon périmètre au backend métier, au traitement asynchrone, à l’agent conversationnel et à la vérification.
Du développement pour la compétition au renforcement du backend et de l’exploitation
Le service a cessé de fonctionner ; une démo enregistrée et le rapport technique le montrent à l'œuvre.
Requête Scan → livraison en direct
Une demande d’analyse de photo devient un travail en file, et ses événements de progression reviennent au même navigateur.
Navigateurrésolution DNS: Résolution DNSRoute 53
Seul le nom est résolu à partir des enregistrements Route 53. Les requêtes ne passent pas par le DNS.
Navigateurflux de données: HTTPS 443AWS ALB
Le TLS se termine à l’ALB.
AWS ALBflux de données: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayTransmis à l’Envoy d’ingress Istio via le NodePort de la cible d’instance EC2.
Istio ingress Envoy
k8s-ingress-gatewayflux de données: VirtualServiceScan APIk8s-api-scanRouté vers l’API Scan, qui met le travail en file et renvoie aussitôt un job_id ; l’interrogation du résultat répond 202 tant que le job n’est pas terminé.
Scan API
k8s-api-scanflux de données: Tâche CeleryRabbitMQk8s-rabbitmqLa tâche Scan part sur RabbitMQ.
RabbitMQ
k8s-rabbitmqflux de données: consumescan-workerk8s-worker-ai · k8s-worker-ai-2scan-worker (Celery · gevent), dans le pool IA, la prend et exécute Vision → Rule → Answer → Reward. Les quatre étapes sont des tâches Celery chaînées, pas des nœuds.
Pool IA
k8s-worker-ai · k8s-worker-ai-2flux de données: HTTPSLLM APILes workers scan et chat appellent les API OpenAI et Google.
scan-worker
k8s-worker-ai · k8s-worker-ai-2flux de données: XADD scan:eventsStreamsk8s-redis-streamsLes événements de progression sont écrits dans l’un des quatre shards Streams.
Streams
k8s-redis-streamsflux de données: XREADGROUPEvent Routerk8s-event-routerL’Event Router les lit en tant que consumer group et fait un XACK après traitement ; pending et reclaim maintiennent une livraison at-least-once.
Event Router
k8s-event-routerflux de données: SETEX stateState KVk8s-redis-streamsIl écrit le dernier état et un marqueur de traitement en double dans State KV, sur le même nœud Redis.
Event Router
k8s-event-routerflux de données: PUBLISHPub/Subk8s-redis-pubsubLivré en temps réel sur le canal du job, dans le Pub/Sub de shard.
Pub/Sub
k8s-redis-pubsubflux de données: SUBSCRIBESSE Gatewayk8s-sse-gatewayLe SSE Gateway s’abonne via un seul listener partagé par processus et diffuse vers chaque navigateur connecté.
SSE Gateway
k8s-sse-gatewayflux de données: Flux SSENavigateurUne connexion ouverte par le même chemin ALB et ingress. La livraison est at-least-once ; les doublons sont traités de manière idempotente.
State KV
k8s-redis-streamsflux de données: Reprise à la reconnexionSSE Gatewayk8s-sse-gatewayÀ la reconnexion avec Last-Event-ID, State KV et les Streams conservés comblent les événements manqués.
Chat · tokens et stockage
Les tokens de Chat suivent le même chemin d’événements que Scan ; checkpoints et messages sont stockés par deux workloads différents.
Navigateurflux de données: HTTPS 443AWS ALB
Le TLS se termine à l’ALB.
AWS ALBflux de données: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayTransmis à l’Envoy d’ingress Istio via le NodePort de la cible d’instance EC2.
Istio ingress Envoy
k8s-ingress-gatewayflux de données: VirtualServiceChat APIk8s-api-chatRouté vers l’API Chat.
Chat API
k8s-api-chatflux de données: Tâche TaskiqRabbitMQk8s-rabbitmqLa tâche Chat part sur RabbitMQ.
RabbitMQ
k8s-rabbitmqflux de données: consumechat-workerk8s-worker-ai · k8s-worker-ai-2chat-worker (Taskiq · asyncio) classe l’intention avec LangGraph, puis exécute la vision si nécessaire, les outils sélectionnés, l’agrégateur et un résumé si nécessaire, et répond. Chaque requête n’exécute que les branches dont elle a besoin.
Pool IA
k8s-worker-ai · k8s-worker-ai-2flux de données: HTTPSLLM APILes workers scan et chat appellent les API OpenAI et Google.
chat-worker
k8s-worker-ai · k8s-worker-ai-2flux de données: XADD chat:eventsStreamsk8s-redis-streamsTokens et événements d’étape sont écrits dans quatre shards Streams ; de là, le chemin de livraison est le même que pour Scan.
Streams
k8s-redis-streamsflux de données: XREADGROUPEvent Routerk8s-event-routerL’Event Router les lit en tant que consumer group et fait un XACK après traitement ; pending et reclaim maintiennent une livraison at-least-once.
Event Router
k8s-event-routerflux de données: PUBLISHPub/Subk8s-redis-pubsubLivré en temps réel sur le canal du job, dans le Pub/Sub de shard.
Pub/Sub
k8s-redis-pubsubflux de données: SUBSCRIBESSE Gatewayk8s-sse-gatewayLe SSE Gateway s’abonne via un seul listener partagé par processus et diffuse vers chaque navigateur connecté.
SSE Gateway
k8s-sse-gatewayflux de données: Flux SSENavigateurUne connexion ouverte par le même chemin ALB et ingress. La livraison est at-least-once ; les doublons sont traités de manière idempotente.
chat-worker
k8s-worker-ai · k8s-worker-ai-2flux de données: checkpointCachek8s-redis-cacheLe checkpoint LangGraph est conservé dans Redis Cache.
Cache
k8s-redis-cacheflux de données: lire le checkpointcheckpoint-syncerk8s-worker-ai · k8s-worker-ai-2checkpoint-syncer lit le checkpoint depuis Redis.
checkpoint-syncer
k8s-worker-ai · k8s-worker-ai-2flux de données: synchroniser vers PostgreSQLPostgreSQLk8s-postgresqlAsyncPostgresSaver le stocke dans PostgreSQL de façon asynchrone.
Streams
k8s-redis-streamsflux de données: chat-persistence grouppersistence consumerk8s-api-chatLe persistence consumer du nœud Chat lit les événements done en tant que consumer group.
persistence consumer
k8s-api-chatflux de données: enregistrer les messagesPostgreSQLk8s-postgresqlLes messages de Chat sont enregistrés dans PostgreSQL. C’est un workload différent de checkpoint-syncer.
Entrée · authentification
Le chemin synchrone de l’entrée du cluster jusqu’à une API de domaine. Seuls les chemins protégés par une politique contrôlent d’abord l’authentification ; le trafic des pods entre nœuds passe par Calico VXLAN.
Navigateurrésolution DNS: Résolution DNSRoute 53
Seul le nom est résolu à partir des enregistrements Route 53. Les requêtes ne passent pas par le DNS.
Navigateurflux de données: HTTPS 443AWS ALB
Le TLS se termine à l’ALB.
AWS ALBflux de données: HTTP · NodePortIstio ingress Envoy
k8s-ingress-gatewayTransmis à l’Envoy d’ingress Istio via le NodePort de la cible d’instance EC2.
Istio ingress Envoy
k8s-ingress-gatewayflux de données: ext-authz · gRPC 50051ext-authzk8s-api-authSeuls les chemins soumis à une politique CUSTOM interrogent d’abord l’ext-authz Go (délai de 2 s, failOpen false). Les chemins comme login, health et docs sont exclus.
ext-authz
k8s-api-authflux de données: consultation de la blacklistblacklistk8s-redis-authLe token est vérifié contre la blacklist Redis.
Istio ingress Envoy
k8s-ingress-gatewayflux de données: VirtualServiceChat APIk8s-api-chatRouté vers l’API Chat.
Chat API
k8s-api-chatflux de données: gRPCimages APIk8s-api-imageL’appel synchrone chat → images cité dans le README.
auth API
k8s-api-authflux de données: gRPCusers APIk8s-api-usersL’appel synchrone auth → users cité dans le README.
Pool de stockage · écritures asynchrones
La blacklist d’authentification et la propriété des personnages sont stockées par les workers du pool de stockage, qui reçoivent leur travail de RabbitMQ.
blacklist
k8s-redis-authflux de données: outbox:blacklistauth-relayk8s-worker-storage · k8s-worker-storage-2auth-relay (l’auth-Bus du README) lit l’outbox Redis.
auth-relay
k8s-worker-storage · k8s-worker-storage-2flux de données: blacklist.eventsRabbitMQk8s-rabbitmqIl republie sur RabbitMQ, avec nouvelles tentatives et DLQ.
RabbitMQ
k8s-rabbitmqflux de données: blacklist.eventsauth-workerk8s-worker-storage · k8s-worker-storage-2auth-worker reçoit les événements de blacklist.
auth-worker
k8s-worker-storage · k8s-worker-storage-2flux de données: stocker la blacklistblacklistk8s-redis-authIls sont stockés dans la blacklist Redis.
RabbitMQ
k8s-rabbitmqflux de données: Celery batchusers-workerk8s-worker-storage · k8s-worker-storage-2users-worker reçoit par lots les travaux de propriété des personnages.
users-worker
k8s-worker-storage · k8s-worker-storage-2flux de données: UPSERTPostgreSQLk8s-postgresqlIls sont appliqués à PostgreSQL.
Configurer · mettre à l’échelle · observer
La Platform Layer n’est pas une étape traversée par les requêtes ; elle déploie, configure, met à l’échelle et observe les autres couches. Les nœuds sont des instances EC2 créées avec Terraform, sur lesquelles Ansible a installé kubeadm et Calico ; la CI ne fait que valider le Terraform et ne l’applique jamais automatiquement.
GitHub Actions · Docker Hubconfiguration · mise à l’échelle: image · révision des manifestsArgo CD image updater
k8s-masterArgo CD aligne la configuration du cluster via App-of-Apps et Sync Waves. Seul le placement sur le control plane de l’image updater a été confirmé.
ALB Controller · ExternalDNS
k8s-masterconfiguration · mise à l’échelle: Ingress → ALB · enregistrements DNSGéré par AWSL’ALB Controller et ExternalDNS configurent les ressources côté AWS. Ce n’est pas un chemin emprunté par les requêtes.
Istiod
k8s-masterconfiguration · mise à l’échelle: Configuration xDSIstio ingress Envoyk8s-ingress-gatewayIstiod pousse la configuration Gateway, VirtualService et EnvoyFilter via xDS vers l’Envoy d’ingress et le sidecar Envoy de chaque API.
KEDA
k8s-monitoringconfiguration · mise à l’échelle: nombre de podsPool IAk8s-worker-ai · k8s-worker-ai-2KEDA ajuste le nombre de pods des workers. Le nombre de nœuds ne change pas.
KEDA
k8s-monitoringconfiguration · mise à l’échelle: nombre de podspool de stockagek8s-worker-storage · k8s-worker-storage-2Les pods des workers du pool de stockage sont mis à l’échelle de la même façon.
Fluent Bitobservabilité: journauxElasticsearch
k8s-loggingFluent Bit est un DaemonSet présent sur chaque nœud et envoie les journaux à Elasticsearch.
Pool IA
k8s-worker-ai · k8s-worker-ai-2observabilité: traçage des exécutions LLMLangSmithLes traces des appels LLM vont vers LangSmith, un SaaS externe.
Toutes les connexions avec la même épaisseur. Choisissez un flux pour ne mettre en évidence que son chemin, avec des étapes numérotées.
- Trait plein · sens dans lequel circulent requêtes, travail, événements et écritures
- Pointillés · résolution DNS ; les requêtes n’y transitent pas
- Tirets · configuration ou mise à l’échelle ; pas un chemin de données
- Trait-point long · journaux et traçage des exécutions
- Trait noir épais · le flux sélectionné ; les numéros donnent l’ordre
- Cadre · un nœud physique, nom d’hôte dans l’en-tête
- Pastille intérieure · une charge de travail qui tourne sur ce nœud
- Cadre en tirets · un pool de planification à deux nœuds
- Cadre en tirets · hors du cluster, non compté comme nœud
- Contour orange · là où un modèle décide
- Control plane1 nœud
- Domain API8 nœuds
- Pool IA2 nœuds
- pool de stockage2 nœuds
- Data5 nœuds
- Platform6 nœuds
Les cinq couches du README (les bandes) et les groupes physiques (la teinte de l’en-tête de chaque nœud) regroupent différemment les mêmes 24 nœuds. State KV partage le nœud Streams et n’est pas compté à part ; Route 53, l’ALB et les services hors cluster ne sont pas des nœuds. Un pool est une plage de scheduling, pas l’affirmation que chaque pod tourne sur les deux nœuds.
24nœuds
24 = 1 control plane + 8 API + 4 worker + 5 data + 6 platform
1Control plane
role: control-planekube-apiserver · scheduler · controller-manager · etcd, plus les contrôleurs de la plateforme.
- k8s-master
8Domain API
domain: <service>Un nœud par domaine. Les appels internes utilisent 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
Deux pools de scheduling de deux nœuds chacun. KEDA modifie le nombre de pods ; le nombre de nœuds reste le même.
pool de stockage
domain: worker-storage- k8s-worker-storage
- k8s-worker-storage-2
Pool IA
domain: worker-ai- k8s-worker-ai
- k8s-worker-ai-2
5Data
Redis est réparti sur quatre nœuds dédiés par usage. Le stockage de PostgreSQL est en EBS CSI · gp3.
- k8s-postgresql
- k8s-redis-auth
- k8s-redis-streams
- k8s-redis-cache
- k8s-redis-pubsub
6Platform
Messagerie, observabilité, ingress et livraison temps réel ont chacun leur propre nœud.
- k8s-rabbitmq
- k8s-monitoring
- k8s-logging
- k8s-ingress-gateway
- k8s-sse-gateway
- k8s-event-router
- Chaque nœud
- Fluent Bit · DaemonSet
- Calico
Modèles configurés · par fournisseur
Le service a été fermé : il s’agit des modèles configurés dans le dépôt, pas de choix disponibles aujourd’hui. Chaque rôle est listé séparément ; ce n’est pas une liste d’exécutions vérifiées.
OpenAI
Fournisseur par défaut de Chat
Chat
openai/gpt-5.2par défaut
Le modèle par défaut est gpt-5.2 ; un modèle indiqué dans la requête est utilisé à la place.
Scan
gpt-5.2par défautgpt-5.2-progpt-5.1gpt-5gpt-5-progpt-5-mini
Les modèles hors de la correspondance de 13 modèles sont rejetés.
Correspondance d’images
gpt-image-1.5
Distinct de gpt-5.2, le modèle d’orchestration Responses. La génération d’images est désactivée par défaut.
Évaluation
gpt-4o-minipar défaut
Le correcteur LLM de l’évaluation des conversations, distinct du modèle Chat.
Google
Quand Chat choisit Google
Chat
google/gemini-3-pro-previewgoogle/gemini-3-flash-preview
Avec Google sélectionné, gemini-3-flash-preview est le modèle par défaut.
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
Correspondance d’images
gemini-3-pro-image-preview
Sourcebackend main d8840c7Commit public du September 29, 2026 · vérifié le October 8, 2026