x-api-worker-env: &api-worker-env HOST_IP: ${HOST_IP:-} RUNNING_IN_DOCKER: "true" YUXI_ENV: production PYTHONPATH: /app/package JWT_SECRET_KEY: ${JWT_SECRET_KEY:?Set JWT_SECRET_KEY in .env.prod} API_KEY_DERIVATION_SECRET: ${API_KEY_DERIVATION_SECRET:?Set API_KEY_DERIVATION_SECRET in .env.prod} RAPIDOCR_MODEL_DIR: /home/yuxi/.cache/rapidocr/models YUXI_INSTANCE_ID: ${YUXI_INSTANCE_ID:?Set YUXI_INSTANCE_ID in .env.prod} POSTGRES_URL: ${POSTGRES_URL:-postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env.prod}@postgres:5432/${POSTGRES_DB:-yuxi}} REDIS_URL: ${REDIS_URL:-redis://redis:6379/0} RUN_CANCEL_KEY_TTL_SECONDS: ${RUN_CANCEL_KEY_TTL_SECONDS:-1800} RUN_EVENTS_STREAM_TTL_SECONDS: ${RUN_EVENTS_STREAM_TTL_SECONDS:-7200} READINESS_PROBE_TIMEOUT_SECONDS: ${READINESS_PROBE_TIMEOUT_SECONDS:-2} READINESS_CACHE_TTL_SECONDS: ${READINESS_CACHE_TTL_SECONDS:-1} YUXI_CORS_ORIGINS: ${YUXI_CORS_ORIGINS:-} NEO4J_URI: ${NEO4J_URI:-bolt://graph:7687} NEO4J_USERNAME: ${NEO4J_USERNAME:-neo4j} NEO4J_PASSWORD: ${NEO4J_PASSWORD:?Set NEO4J_PASSWORD in .env.prod} MILVUS_URI: ${MILVUS_URI:-http://milvus:19530} MILVUS_TOKEN: ${MILVUS_TOKEN:-} MINERU_API_URI: ${MINERU_API_URI:-http://mineru-api:30001} PADDLEX_URI: ${PADDLEX_URI:-http://paddlex:8080} MINIO_URI: ${MINIO_URI:-http://minio:9000} SANDBOX_PROVIDER: ${SANDBOX_PROVIDER:-provisioner} SANDBOX_PROVISIONER_URL: ${SANDBOX_PROVISIONER_URL:-http://sandbox-provisioner:8002} SANDBOX_PROVISIONER_TOKEN: ${SANDBOX_PROVISIONER_TOKEN:?Set SANDBOX_PROVISIONER_TOKEN in .env.prod} SANDBOX_PROVISIONER_DELETE_TIMEOUT_SECONDS: ${SANDBOX_PROVISIONER_DELETE_TIMEOUT_SECONDS:-120} SANDBOX_VIRTUAL_PATH_PREFIX: ${SANDBOX_VIRTUAL_PATH_PREFIX:-/home/gem/user-data} SANDBOX_EXEC_TIMEOUT_SECONDS: ${SANDBOX_EXEC_TIMEOUT_SECONDS:-180} SANDBOX_MAX_OUTPUT_BYTES: ${SANDBOX_MAX_OUTPUT_BYTES:-262144} TASKER_DEFAULT_TIMEOUT_SECONDS: ${TASKER_DEFAULT_TIMEOUT_SECONDS:-21600} YUXI_DATASET_PERSIST_BATCH_SIZE: ${YUXI_DATASET_PERSIST_BATCH_SIZE:-1} YUXI_SKILL_DATA_DIR: /app/skill-sources YUXI_SKILL_PROJECTION_DIR: /app/skill-projections NO_PROXY: localhost,127.0.0.1,milvus,graph,minio,etcd,mineru,paddlex,sandbox-provisioner,api.siliconflow.cn no_proxy: localhost,127.0.0.1,milvus,graph,minio,etcd,mineru,paddlex,sandbox-provisioner,api.siliconflow.cn # 长驻服务的容器日志轮转上界,防止 docker json.log 无限增长占满磁盘。 x-logging: &default-logging driver: json-file options: max-size: "50m" max-file: "3" services: api: ports: - "${YUXI_API_PORT:-6050}:5050" build: context: . dockerfile: docker/api.Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-api:${YUXI_VERSION:-0.7.3} working_dir: /app user: "1000:1000" networks: - app-network volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/threads:/app/user-data - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-sources:/app/skill-sources - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-projections:/app/skill-projections extra_hosts: - "host.docker.internal:host-gateway" env_file: - ${YUXI_ENV_FILE:-.env.prod} # region api_envs environment: <<: *api-worker-env YUXI_RUNTIME_DIR: /app/runtime/api YUXI_USER_DATA_DIR: /app/user-data POSTGRES_POOL_SIZE: ${API_POSTGRES_POOL_SIZE:-120} POSTGRES_MAX_OVERFLOW: ${API_POSTGRES_MAX_OVERFLOW:-40} POSTGRES_POOL_TIMEOUT_SECONDS: ${API_POSTGRES_POOL_TIMEOUT_SECONDS:-30} LANGGRAPH_POSTGRES_POOL_SIZE: ${API_LANGGRAPH_POSTGRES_POOL_SIZE:-10} LANGGRAPH_POSTGRES_POOL_TIMEOUT_SECONDS: ${API_LANGGRAPH_POSTGRES_POOL_TIMEOUT_SECONDS:-30} REDIS_MAX_CONNECTIONS: ${API_REDIS_MAX_CONNECTIONS:-256} # endregion api_envs command: uv run --no-sync --no-dev uvicorn server.main:app --host 0.0.0.0 --port 5050 restart: unless-stopped healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:5050/api/system/ready || exit 1"] interval: 30s timeout: 15s retries: 8 start_period: 180s depends_on: postgres: condition: service_healthy redis: condition: service_healthy minio: condition: service_healthy sandbox-provisioner: condition: service_healthy storage-migrator: condition: service_completed_successfully worker: build: context: . dockerfile: docker/api.Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-api:${YUXI_VERSION:-0.7.3} working_dir: /app user: "1000:1000" networks: - app-network volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/threads:/app/user-data - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-sources:/app/skill-sources - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-projections:/app/skill-projections extra_hosts: - "host.docker.internal:host-gateway" env_file: - ${YUXI_ENV_FILE:-.env.prod} environment: <<: *api-worker-env YUXI_RUNTIME_DIR: /app/runtime/worker YUXI_USER_DATA_DIR: /app/user-data ARQ_MAX_JOBS: ${ARQ_MAX_JOBS:-140} REDIS_MAX_CONNECTIONS: ${WORKER_REDIS_MAX_CONNECTIONS:-256} POSTGRES_POOL_SIZE: ${WORKER_POSTGRES_POOL_SIZE:-120} POSTGRES_MAX_OVERFLOW: ${WORKER_POSTGRES_MAX_OVERFLOW:-40} POSTGRES_POOL_TIMEOUT_SECONDS: ${WORKER_POSTGRES_POOL_TIMEOUT_SECONDS:-30} LANGGRAPH_POSTGRES_POOL_SIZE: ${WORKER_LANGGRAPH_POSTGRES_POOL_SIZE:-120} LANGGRAPH_POSTGRES_POOL_TIMEOUT_SECONDS: ${WORKER_LANGGRAPH_POSTGRES_POOL_TIMEOUT_SECONDS:-30} command: uv run --no-sync --no-dev python -m server.worker_main restart: unless-stopped healthcheck: test: ["CMD", "python", "-m", "yuxi.services.worker_health"] interval: 10s timeout: 10s retries: 6 start_period: 180s depends_on: postgres: condition: service_healthy redis: condition: service_healthy minio: condition: service_healthy sandbox-provisioner: condition: service_healthy storage-migrator: condition: service_completed_successfully storage-migrator: build: context: . dockerfile: docker/api.Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-api:${YUXI_VERSION:-0.7.3} working_dir: /app user: "0:0" networks: - app-network volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi:/app/legacy-saves - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/threads:/app/user-data - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-sources:/app/skill-sources - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-projections:/app/skill-projections env_file: - ${YUXI_ENV_FILE:-.env.prod} environment: <<: *api-worker-env YUXI_RUNTIME_DIR: /app/runtime/storage-migrator YUXI_LEGACY_STORAGE_DIR: /app/legacy-saves YUXI_USER_DATA_DIR: /app/user-data command: uv run --no-sync --no-dev python -m yuxi.storage_migration restart: "no" depends_on: postgres: condition: service_healthy sandbox-provisioner: ports: - "127.0.0.1:${YUXI_SANDBOX_PORT:-9002}:8002" build: context: ./docker/sandbox_provisioner dockerfile: Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-sandbox-provisioner:${YUXI_VERSION:-0.7.3} volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/threads:/app/user-data - ${YUXI_STATE_DIR:-./docker/volumes}/yuxi/skill-projections:/app/skill-projections - ${SANDBOX_ENV_FILE:-./docker/sandbox_provisioner/sandbox.env}:/app/sandbox.env:ro - /var/run/docker.sock:/var/run/docker.sock networks: app-network: # 动态接入沙盒网络时保留管理端口的默认路由。 gw_priority: 1 extra_hosts: - "host.docker.internal:host-gateway" environment: # Common provisioner settings - PROVISIONER_BACKEND=${SANDBOX_PROVISIONER_BACKEND:-docker} - PROVISIONER_PUBLIC_URL=${SANDBOX_PROVISIONER_URL:-http://sandbox-provisioner:8002} - SANDBOX_PROVISIONER_TOKEN=${SANDBOX_PROVISIONER_TOKEN:?Set SANDBOX_PROVISIONER_TOKEN in .env.prod} - SANDBOX_IMAGE=${SANDBOX_IMAGE:-enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:1.11.0} - SANDBOX_RUNTIME_PROFILE=${SANDBOX_RUNTIME_PROFILE:-core} - SANDBOX_CONTAINER_PORT=${SANDBOX_CONTAINER_PORT:-8080} - MEMORY_SANDBOX_URL_TEMPLATE=${MEMORY_SANDBOX_URL_TEMPLATE:-http://agent-sandbox:8000} - SANDBOX_HEALTH_TIMEOUT_SECONDS=${SANDBOX_HEALTH_TIMEOUT_SECONDS:-300} - SANDBOX_IDLE_TIMEOUT_SECONDS=${SANDBOX_IDLE_TIMEOUT_SECONDS:-120} - SANDBOX_IDLE_CHECK_INTERVAL_SECONDS=${SANDBOX_IDLE_CHECK_INTERVAL_SECONDS:-10} - SANDBOX_EXEC_TIMEOUT_SECONDS=${SANDBOX_EXEC_TIMEOUT_SECONDS:-180} - SANDBOX_DELETE_CONCURRENCY=${SANDBOX_DELETE_CONCURRENCY:-32} - SANDBOX_CONTAINER_STOP_TIMEOUT_SECONDS=${SANDBOX_CONTAINER_STOP_TIMEOUT_SECONDS:-2} # 单沙盒执行的资源上界 - SANDBOX_MEM_LIMIT=${SANDBOX_MEM_LIMIT:-2g} - SANDBOX_CPUS=${SANDBOX_CPUS:-2} - SANDBOX_PIDS_LIMIT=${SANDBOX_PIDS_LIMIT:-512} # Optional proxy for sandbox-provisioner container - HTTP_PROXY=${SANDBOX_HTTP_PROXY:-} - HTTPS_PROXY=${SANDBOX_HTTPS_PROXY:-} - NO_PROXY=localhost,127.0.0.1,host.docker.internal # Docker backend only - DOCKER_NETWORK_PREFIX=${SANDBOX_DOCKER_NETWORK_PREFIX:-${COMPOSE_PROJECT_NAME:-yuxi}-sandbox} - DOCKER_ADDRESS_POOL=${SANDBOX_DOCKER_ADDRESS_POOL:-10.253.240.0/20} - DOCKER_SUBNET_PREFIX=${SANDBOX_DOCKER_SUBNET_PREFIX:-28} - DOCKER_USER_DATA_HOST_PATH=${SANDBOX_DOCKER_USER_DATA_HOST_PATH:-} - DOCKER_SKILL_PROJECTIONS_HOST_PATH=${SANDBOX_DOCKER_SKILL_PROJECTIONS_HOST_PATH:-} - DOCKER_SANDBOX_PREFIX=${SANDBOX_DOCKER_SANDBOX_PREFIX:-${COMPOSE_PROJECT_NAME:-yuxi}-sandbox} # Kubernetes backend only - K8S_NAMESPACE=${SANDBOX_K8S_NAMESPACE:-yuxi-know} - NODE_HOST=${SANDBOX_NODE_HOST:-host.docker.internal} - KUBECONFIG_PATH=${KUBECONFIG_PATH:-} - USER_DATA_PVC=${USER_DATA_PVC:-yuxi-user-data} - SKILLS_PVC=${SKILLS_PVC:-yuxi-skills} command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8002"] healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8002/health').read()"] interval: 10s timeout: 5s retries: 6 start_period: 10s restart: unless-stopped depends_on: storage-migrator: condition: service_completed_successfully web: build: context: . dockerfile: docker/web.Dockerfile target: production image: ${COMPOSE_PROJECT_NAME:-yuxi}-web:${YUXI_VERSION:-0.7.3} ports: - "${YUXI_WEB_PORT:-80}:80" depends_on: - api networks: - app-network environment: - NODE_ENV=production command: nginx -g "daemon off;" restart: unless-stopped graph: ports: - "127.0.0.1:${YUXI_NEO4J_HTTP_PORT:-8474}:7474" - "127.0.0.1:${YUXI_NEO4J_BOLT_PORT:-8687}:7687" image: neo4j:5.26.29 volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/neo4j/data:/data - ${YUXI_STATE_DIR:-./docker/volumes}/neo4j/logs:/logs healthcheck: test: ["CMD-SHELL", "cypher-shell -a bolt://localhost:7687 -u \"$${NEO4J_AUTH%%/*}\" -p \"$${NEO4J_AUTH#*/}\" 'RETURN 1;' >/dev/null || exit 1"] interval: 20s timeout: 10s retries: 5 start_period: 30s environment: - NEO4J_AUTH=${NEO4J_USERNAME:-neo4j}/${NEO4J_PASSWORD:?Set NEO4J_PASSWORD in .env.prod} - NEO4J_server_bolt_listen__address=0.0.0.0:7687 - NEO4J_server_http_listen__address=0.0.0.0:7474 - ENTITY_EMBEDDING=true networks: - app-network restart: unless-stopped etcd: image: quay.io/coreos/etcd:v3.5.5 logging: *default-logging environment: - ETCD_AUTO_COMPACTION_MODE=revision - ETCD_AUTO_COMPACTION_RETENTION=1000 - ETCD_QUOTA_BACKEND_BYTES=4294967296 - ETCD_SNAPSHOT_COUNT=50000 volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/milvus/etcd:/etcd command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd healthcheck: test: ["CMD", "etcdctl", "endpoint", "health"] interval: 60s timeout: 30s retries: 5 start_period: 30s networks: - app-network restart: unless-stopped minio: build: context: ./docker/minio dockerfile: Dockerfile ports: - "127.0.0.1:${YUXI_MINIO_API_PORT:-10000}:9000" - "127.0.0.1:${YUXI_MINIO_CONSOLE_PORT:-10001}:9001" image: ${COMPOSE_PROJECT_NAME:-yuxi}-minio:RELEASE.2023-03-20T20-16-18Z environment: MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?Set MINIO_ACCESS_KEY in .env.prod} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?Set MINIO_SECRET_KEY in .env.prod} volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/milvus/minio:/data - ${YUXI_STATE_DIR:-./docker/volumes}/milvus/minio_config:/root/.minio command: minio server /data --address 0.0.0.0:9000 --console-address 0.0.0.0:9001 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 60s timeout: 30s retries: 5 start_period: 30s networks: - app-network restart: unless-stopped milvus: ports: - "127.0.0.1:${YUXI_MILVUS_PORT:-20530}:19530" - "127.0.0.1:${YUXI_MILVUS_HEALTH_PORT:-10091}:9091" image: milvusdb/milvus:v2.5.6 command: ["milvus", "run", "standalone"] # v2.5.6 不读取 MILVUS_LOG_LEVEL;日志级别只能由镜像内 milvus.yaml 拥有, # 这里只对 stdout/stderr 轮转封顶。内存随知识库数据量线性增长,不设 mem_limit: # 硬上界终会被合法增长击穿,击穿模式是 OOM→重启→重加载→再 OOM 的循环。 # cpus 上限同时让内置 maxprocs 按 cgroup quota 收敛 GOMAXPROCS。 logging: *default-logging cpus: ${YUXI_MILVUS_CPUS:-2} security_opt: - seccomp:unconfined environment: ETCD_ENDPOINTS: etcd:2379 MINIO_ADDRESS: minio:9000 MINIO_ACCESS_KEY_ID: ${MINIO_ACCESS_KEY:?Set MINIO_ACCESS_KEY in .env.prod} MINIO_SECRET_ACCESS_KEY: ${MINIO_SECRET_KEY:?Set MINIO_SECRET_KEY in .env.prod} volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/milvus/milvus:/var/lib/milvus - ${YUXI_STATE_DIR:-./docker/volumes}/milvus/logs:/var/lib/milvus/logs healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] interval: 60s start_period: 120s timeout: 30s retries: 5 depends_on: - "etcd" - "minio" networks: - app-network restart: unless-stopped postgres: ports: - "127.0.0.1:${YUXI_POSTGRES_PORT:-6432}:5432" image: postgres:16 command: ["postgres", "-c", "max_connections=${POSTGRES_MAX_CONNECTIONS:-600}"] environment: - POSTGRES_USER=${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env.prod} - POSTGRES_DB=${POSTGRES_DB:-yuxi} - TZ=${TZ:-Asia/Shanghai} volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/postgresql:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-yuxi} || exit 1"] interval: 5s timeout: 3s retries: 30 networks: - app-network restart: unless-stopped redis: ports: - "127.0.0.1:${YUXI_REDIS_PORT:-7379}:6379" image: redis:7.4.10-alpine command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 10 volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/redis:/data networks: - app-network restart: unless-stopped mineru-api: ports: - "127.0.0.1:${YUXI_MINERU_PORT:-30001}:30001" build: context: . dockerfile: docker/mineru.Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-mineru:latest profiles: - all env_file: - ${YUXI_ENV_FILE:-.env.prod} environment: MINERU_MODEL_SOURCE: local entrypoint: mineru-api command: --host 0.0.0.0 --port 30001 # --allow-public-http-client # Disabled by default; when binding to 0.0.0.0 or ::, this re-enables *-http-client backends and server_url. Enable only if you accept the SSRF risk. # parameters for vllm-engine # --gpu-memory-utilization 0.5 # If encountering VRAM shortage, reduce the KV cache size by this parameter; if VRAM issues persist, try lowering it further to `0.4` or below. ulimits: memlock: -1 stack: 67108864 ipc: host healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:30001/health || exit 1"] deploy: resources: reservations: devices: - driver: nvidia device_ids: ["0"] # Modify for multiple GPUs: ["0", "1"] capabilities: [gpu] networks: - app-network restart: unless-stopped paddlex: ports: - "127.0.0.1:${YUXI_PADDLEX_PORT:-8080}:8080" build: context: . dockerfile: docker/paddlex.Dockerfile image: ${COMPOSE_PROJECT_NAME:-yuxi}-paddlex:latest profiles: - all volumes: - ${YUXI_STATE_DIR:-./docker/volumes}/paddlex:/paddlex healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"] interval: 20s timeout: 10s retries: 5 start_period: 60s deploy: resources: reservations: devices: - driver: nvidia device_ids: ['0'] capabilities: [gpu] networks: - app-network restart: unless-stopped networks: app-network: driver: bridge