services: mysql: image: mysql:8.4 restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?必须配置 MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ai_knowledge_base_v2 MYSQL_USER: ai_kb MYSQL_PASSWORD: ${MYSQL_PASSWORD:?必须配置 MYSQL_PASSWORD} TZ: Asia/Shanghai command: ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"] volumes: - ai_kb_v2_mysql_data:/var/lib/mysql healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD --silent"] interval: 10s timeout: 5s retries: 20 redis: image: redis:7-alpine restart: unless-stopped environment: REDIS_PASSWORD: ${REDIS_PASSWORD:?必须配置 REDIS_PASSWORD} command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD:?必须配置 REDIS_PASSWORD}"] volumes: - ai_kb_v2_redis_data:/data healthcheck: test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD ping | grep PONG"] interval: 10s timeout: 5s retries: 20 backend: build: context: ./apps/backend restart: unless-stopped environment: APP_ENV: production DEBUG: "false" DATABASE_URL: ${DATABASE_URL:?必须配置经过 URL 编码的 DATABASE_URL} REDIS_URL: ${REDIS_URL:?必须配置经过 URL 编码的 REDIS_URL} READINESS_REQUIRES_REDIS: "true" LOG_LEVEL: INFO BACKEND_WORKERS: ${BACKEND_WORKERS:-4} DB_POOL_SIZE: ${DB_POOL_SIZE:-20} DB_MAX_OVERFLOW: ${DB_MAX_OVERFLOW:-20} PERIODIC_REPORT_WORKER_ENABLED: ${PERIODIC_REPORT_WORKER_ENABLED:-true} PERIODIC_REPORT_POLL_SECONDS: ${PERIODIC_REPORT_POLL_SECONDS:-5} PERIODIC_REPORT_STALE_MINUTES: ${PERIODIC_REPORT_STALE_MINUTES:-30} PERIODIC_REPORT_MAX_ATTEMPTS: ${PERIODIC_REPORT_MAX_ATTEMPTS:-3} PERIODIC_REPORT_WEEKLY_ENABLED: ${PERIODIC_REPORT_WEEKLY_ENABLED:-true} PERIODIC_REPORT_MONTHLY_ENABLED: ${PERIODIC_REPORT_MONTHLY_ENABLED:-true} PERIODIC_REPORT_TIMEZONE: ${PERIODIC_REPORT_TIMEZONE:-Asia/Shanghai} PERIODIC_REPORT_GLOBAL_SCHEDULE_ENABLED: ${PERIODIC_REPORT_GLOBAL_SCHEDULE_ENABLED:-false} PERIODIC_REPORT_LAZY_CHECK_ENABLED: ${PERIODIC_REPORT_LAZY_CHECK_ENABLED:-true} PERIODIC_REPORT_LAZY_CHECK_LOCK_SECONDS: ${PERIODIC_REPORT_LAZY_CHECK_LOCK_SECONDS:-60} PERIODIC_REPORT_WEEKLY_BACKFILL_LIMIT: ${PERIODIC_REPORT_WEEKLY_BACKFILL_LIMIT:-26} PERIODIC_REPORT_MONTHLY_BACKFILL_LIMIT: ${PERIODIC_REPORT_MONTHLY_BACKFILL_LIMIT:-6} PERIODIC_REPORT_FEATURE_START: "${PERIODIC_REPORT_FEATURE_START:-2026-07-31T00:00:00+08:00}" PERIODIC_REPORT_SOURCE_CHUNK_CHARS: ${PERIODIC_REPORT_SOURCE_CHUNK_CHARS:-18000} USER_BEHAVIOR_RETENTION_DAYS: ${USER_BEHAVIOR_RETENTION_DAYS:-30} TOPIC_SETTLEMENT_WORKER_ENABLED: ${TOPIC_SETTLEMENT_WORKER_ENABLED:-true} TOPIC_SETTLEMENT_POLL_SECONDS: ${TOPIC_SETTLEMENT_POLL_SECONDS:-2} TOPIC_SETTLEMENT_STALE_MINUTES: ${TOPIC_SETTLEMENT_STALE_MINUTES:-30} TOPIC_SETTLEMENT_MAX_ATTEMPTS: ${TOPIC_SETTLEMENT_MAX_ATTEMPTS:-3} AGENT_BATCH_WORKER_ENABLED: ${AGENT_BATCH_WORKER_ENABLED:-true} AGENT_BATCH_POLL_SECONDS: ${AGENT_BATCH_POLL_SECONDS:-2} AGENT_BATCH_STALE_MINUTES: ${AGENT_BATCH_STALE_MINUTES:-30} AGENT_BATCH_WORKER_CONCURRENCY: ${AGENT_BATCH_WORKER_CONCURRENCY:-10} JWT_SECRET_KEY: ${JWT_SECRET_KEY:?必须配置 JWT_SECRET_KEY} CONFIG_ENCRYPTION_KEY: ${CONFIG_ENCRYPTION_KEY:?必须配置 CONFIG_ENCRYPTION_KEY} MOCK_SMS_ENABLED: "false" MOCK_RAG_ENABLED: "false" MOCK_MODEL_ENABLED: "false" FEISHU_MOCK_ENABLED: "false" BOOTSTRAP_ADMIN_USERNAME: ${BOOTSTRAP_ADMIN_USERNAME:?必须配置 BOOTSTRAP_ADMIN_USERNAME} BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD:?必须配置 BOOTSTRAP_ADMIN_PASSWORD} BOOTSTRAP_ADMIN_NAME: ${BOOTSTRAP_ADMIN_NAME:-系统管理员} AUTO_CREATE_TABLES: "false" depends_on: mysql: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8100/api/ready', timeout=3)"] interval: 15s timeout: 5s retries: 10 start_period: 30s user-client: build: context: ./apps/user-client dockerfile: Dockerfile.prod restart: unless-stopped admin-web: build: context: ./apps/admin-web dockerfile: Dockerfile.prod restart: unless-stopped gateway: image: nginx:1.27-alpine restart: unless-stopped ports: - "${GATEWAY_PORT:-8080}:80" volumes: - ./deploy/gateway.prod.conf:/etc/nginx/conf.d/default.conf:ro depends_on: backend: condition: service_healthy user-client: condition: service_started admin-web: condition: service_started volumes: ai_kb_v2_mysql_data: ai_kb_v2_redis_data: