diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..1aa4684 --- /dev/null +++ b/.env.production.example @@ -0,0 +1,81 @@ +# B2B Shopping AI Assistant - Production Environment Variables +# 生产环境配置示例 - 请复制为 .env.production 并填写真实值 + +# ============ AI Model ============ +# 智谱 AI API Key +ZHIPU_API_KEY=your_zhipu_api_key_here +# 模型名称 +ZHIPU_MODEL=GLM-4-Flash-250414 +# 推理模式(开启会消耗更多 token,但更智能) +ENABLE_REASONING_MODE=false +# 复杂查询启用推理模式 +REASONING_MODE_FOR_COMPLEX=true + +# ============ Redis ============ +# Redis 密码(必须设置强密码) +REDIS_PASSWORD=your_secure_redis_password_here +# Redis 主机 +REDIS_HOST=redis +# Redis 端口 +REDIS_PORT=6379 +# Redis 数据库编号 +REDIS_DB=0 + +# ============ Chatwoot (生产环境) ============ +# Chatwoot API URL(生产环境地址) +CHATWOOT_API_URL=https://chatwoot.yourdomain.com +# Chatwoot API Token(从 Chatwoot 后台生成) +CHATWOOT_API_TOKEN=your_chatwoot_api_token_here +# Chatwoot Webhook Secret(配置在 Chatwoot webhook 设置中) +CHATWOOT_WEBHOOK_SECRET=your_webhook_secret_here +# Chatwoot Account ID +CHATWOOT_ACCOUNT_ID=1 + +# ============ Strapi CMS (FAQ/Knowledge Base) ============ +# Strapi API URL +STRAPI_API_URL=https://cms.yourdomain.com +# Strapi API Token +STRAPI_API_TOKEN=your_strapi_api_token_here + +# ============ Hyperf PHP API ============ +# Hyperf API URL(生产环境) +HYPERF_API_URL=https://api.gaia888.com +# Hyperf API Token +HYPERF_API_TOKEN=your_hyperf_api_token_here + +# ============ Mall API ============ +# Mall API URL(生产环境) +MALL_API_URL=https://apicn.gaia888.com +# 租户 ID +MALL_TENANT_ID=2 +# 货币代码 +MALL_CURRENCY_CODE=EUR +# 语言 ID +MALL_LANGUAGE_ID=1 +# 来源域名 +MALL_SOURCE=www.gaia888.com + +# ============ Frontend URLs ============ +# 前端 URL(用于生成订单详情链接) +FRONTEND_URL=https://www.gaia888.com + +# ============ Monitoring ============ +# Sentry DSN(错误监控,可选) +SENTRY_DSN= +# Grafana 管理员密码 +GRAFANA_ADMIN_PASSWORD=your_grafana_password_here + +# ============ Application Config ============ +# 日志级别(WARNING 或 ERROR) +LOG_LEVEL=WARNING +# 最大对话步数 +MAX_CONVERSATION_STEPS=10 +# 对话超时时间(秒) +CONVERSATION_TIMEOUT=3600 + +# ============ Security Notes ============ +# 1. 所有密钥和密码都必须使用强密码 +# 2. 生产环境不要使用默认密码 +# 3. 定期轮换 API Token 和密钥 +# 4. 使用环境变量管理工具(如 AWS Secrets Manager、Vault 等) +# 5. 不要将 .env.production 文件提交到 Git 仓库 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..4831775 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,381 @@ +# B2B Shopping AI Assistant - 生产环境部署指南 + +## 📋 目录 + +- [部署前准备](#部署前准备) +- [快速部署](#快速部署) +- [详细配置](#详细配置) +- [监控与维护](#监控与维护) +- [故障排查](#故障排查) +- [升级策略](#升级策略) + +--- + +## 🚀 部署前准备 + +### 系统要求 + +- **操作系统**: Linux (推荐 Ubuntu 20.04+) +- **Docker**: 20.10+ +- **Docker Compose**: 2.0+ +- **CPU**: 4 核心以上 +- **内存**: 8GB 以上 +- **磁盘**: 50GB 以上 + +### 网络要求 + +- **开放端口**: + - `8000`: Agent 服务 + - `8001-8004`: MCP 服务 + - `3000-3001`: Chatwoot (如果部署在同一服务器) + - `9090`: Prometheus (可选) + - `3001`: Grafana (可选) + +### 外部依赖 + +1. **Chatwoot**: 需要提前部署并配置好 +2. **Strapi CMS**: 用于 FAQ 和知识库管理 +3. **Hyperf API**: 后端业务 API +4. **Redis**: 使用 Docker Compose 内置,或外部 Redis 实例 + +--- + +## ⚡ 快速部署 + +### 1. 准备配置文件 + +```bash +# 复制环境变量模板 +cp .env.production.example .env.production + +# 编辑配置文件 +vim .env.production +``` + +**必须配置的关键参数**: + +```env +# AI 模型 +ZHIPU_API_KEY=your_actual_api_key + +# Redis 密码(必须修改) +REDIS_PASSWORD=your_strong_password_here + +# Chatwoot +CHATWOOT_API_URL=https://your-chatwoot.com +CHATWOOT_API_TOKEN=your_chatwoot_token +CHATWOOT_WEBHOOK_SECRET=your_webhook_secret + +# API 地址 +HYPERF_API_URL=https://api.yourdomain.com +MALL_API_URL=https://apicn.yourdomain.com +STRAPI_API_URL=https://cms.yourdomain.com +``` + +### 2. 执行部署 + +```bash +# 使用部署脚本(推荐) +./scripts/deploy-production.sh + +# 或手动部署 +docker-compose -f docker-compose.prod.yml up -d +``` + +### 3. 验证部署 + +```bash +# 检查服务状态 +docker-compose -f docker-compose.prod.yml ps + +# 健康检查 +curl http://localhost:8000/health + +# 查看日志 +docker-compose -f docker-compose.prod.yml logs -f agent +``` + +--- + +## 🔧 详细配置 + +### 生产环境与开发环境差异 + +| 配置项 | 开发环境 | 生产环境 | +|--------|---------|---------| +| 日志级别 | INFO | WARNING | +| 代码挂载 | 是(支持热更新) | 否(使用镜像) | +| 资源限制 | 无 | 有限制 | +| 健康检查 | 基础 | 完整 | +| 日志轮转 | 无 | 有(10MB x 3) | +| 重启策略 | unless-stopped | always | +| Redis 密码 | 无 | 必须 | + +### 资源配置 + +生产环境默认资源配置(可在 `docker-compose.prod.yml` 中调整): + +| 服务 | CPU 限制 | 内存限制 | CPU 预留 | 内存预留 | +|------|---------|---------|---------|---------| +| Agent | 2 核 | 2GB | 0.5 核 | 512MB | +| MCP 服务 | 0.5 核 | 512MB | 0.25 核 | 256MB | + +### 健康检查 + +所有服务都配置了健康检查: + +- **Agent**: 每 30 秒检查 `/health` 端点 +- **MCP 服务**: 每 30 秒检查 `/health` 端点 +- **Redis**: 每 10 秒 ping 检查 + +--- + +## 📊 监控与维护 + +### 启用监控(可选) + +```bash +# 启动 Prometheus + Grafana +docker-compose -f docker-compose.prod.yml --profile monitoring up -d +``` + +监控服务地址: +- **Prometheus**: http://localhost:9090 +- **Grafana**: http://localhost:3001 (默认用户名/密码: admin/admin) + +### 日志管理 + +```bash +# 查看实时日志 +docker-compose -f docker-compose.prod.yml logs -f + +# 查看特定服务日志 +docker-compose -f docker-compose.prod.yml logs -f agent + +# 查看最近 100 行日志 +docker-compose -f docker-compose.prod.yml logs --tail=100 agent + +# 导出日志 +docker-compose -f docker-compose.prod.yml logs agent > agent.log +``` + +日志文件位置: +- **Agent 日志**: `agent_logs_prod` volume +- **日志轮转**: 每个日志文件最大 10MB,保留 3 个文件 + +### 数据备份 + +```bash +# 备份 Redis 数据 +docker run --rm \ + -v ai_redis_data_prod:/data \ + -v $(pwd)/backups:/backup \ + alpine tar czf /backup/redis-$(date +%Y%m%d).tar.gz /data + +# 备份 Grafana 配置 +docker run --rm \ + -v ai_grafana_data:/data \ + -v $(pwd)/backups:/backup \ + alpine tar czf /backup/grafana-$(date +%Y%m%d).tar.gz /data +``` + +### 性能监控 + +使用 Docker 命令监控资源使用: + +```bash +# 查看容器资源使用情况 +docker stats + +# 查看特定容器 +docker stats ai_agent_prod +``` + +--- + +## 🔍 故障排查 + +### 常见问题 + +#### 1. 服务无法启动 + +**检查日志**: +```bash +docker-compose -f docker-compose.prod.yml logs agent +``` + +**常见原因**: +- 环境变量未配置或配置错误 +- 端口被占用 +- 依赖服务未启动 + +#### 2. API 调用失败 + +**检查步骤**: +1. 验证 API Token 是否正确 +2. 检查网络连接 +3. 查看 API 服务状态 + +```bash +# 测试 API 连接 +docker exec ai_agent_prod curl https://api.yourdomain.com/health +``` + +#### 3. Redis 连接失败 + +**检查 Redis 容器**: +```bash +docker exec ai_redis_prod redis-cli -a YOUR_PASSWORD ping +``` + +#### 4. 内存不足 + +**解决方案**: +- 增加 Docker 内存限制 +- 减少并发数 +- 优化模型调用 + +### 服务重启 + +```bash +# 重启所有服务 +docker-compose -f docker-compose.prod.yml restart + +# 重启特定服务 +docker-compose -f docker-compose.prod.yml restart agent + +# 强制重新创建(更新环境变量后) +docker-compose -f docker-compose.prod.yml up -d --force-recreate agent +``` + +### 完全重置 + +⚠️ **警告**: 此操作会删除所有容器和数据卷 + +```bash +# 停止并删除所有容器 +docker-compose -f docker-compose.prod.yml down -v + +# 重新部署 +./scripts/deploy-production.sh +``` + +--- + +## 🔄 升级策略 + +### 滚动升级(零停机) + +```bash +# 1. 拉取最新代码 +git pull origin main + +# 2. 构建新镜像 +docker-compose -f docker-compose.prod.yml build + +# 3. 逐个重启服务 +docker-compose -f docker-compose.prod.yml up -d --no-deps agent + +# 4. 验证新版本 +curl http://localhost:8000/health +``` + +### 蓝绿部署 + +```bash +# 1. 部署新版本到不同端口 +docker-compose -f docker-compose.prod.yml up -d + +# 2. 切换流量(修改 Nginx 配置) + +# 3. 停止旧版本 +docker-compose -f docker-compose.prod.yml down +``` + +### 回滚 + +```bash +# 回滚到上一个版本 +git checkout PREVIOUS_VERSION_TAG + +# 重新构建 +docker-compose -f docker-compose.prod.yml build + +# 重启服务 +docker-compose -f docker-compose.prod.yml up -d +``` + +--- + +## 🔐 安全建议 + +### 环境变量管理 + +1. **使用密钥管理服务**: + - AWS Secrets Manager + - HashiCorp Vault + - Azure Key Vault + +2. **文件权限**: + ```bash + chmod 600 .env.production + ``` + +3. **不要提交敏感信息**: + ```bash + # .gitignore + .env.production + .env + ``` + +### 网络安全 + +1. **使用防火墙限制端口访问** +2. **配置 HTTPS/TLS** +3. **使用 VPN 或专线连接服务** +4. **定期更新 Docker 镜像** + +### API 安全 + +1. **定期轮换 API Token** +2. **限制 API Token 权限** +3. **监控异常调用** +4. **设置速率限制** + +--- + +## 📝 部署检查清单 + +部署前确认: + +- [ ] 所有环境变量已配置 +- [ ] Redis 密码已设置 +- [ ] API Token 已验证 +- [ ] 端口未被占用 +- [ ] DNS 解析已配置 +- [ ] SSL 证书已安装 +- [ ] 监控已配置 +- [ ] 备份策略已制定 +- [ ] 回滚方案已准备 + +部署后验证: + +- [ ] 所有容器正常运行 +- [ ] 健康检查通过 +- [ ] API 调用成功 +- [ ] 日志正常输出 +- [ ] 资源使用正常 +- [ ] 监控数据正常 + +--- + +## 🆘 获取帮助 + +- **文档**: `/docs` 目录 +- **Issues**: GitHub Issues +- **日志**: `docker-compose logs` +- **监控**: Grafana Dashboard (如果启用) + +--- + +**最后更新**: 2026-01-26 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..f215d9c --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,336 @@ +version: '3.8' + +services: + # ============ Infrastructure ============ + + # Redis (Cache & Queue) - 生产环境配置 + redis: + image: redis:7-alpine + container_name: ai_redis_prod + command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-prod_redis_password_2024} + volumes: + - redis_data_prod:/data + networks: + - ai_network_prod + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-prod_redis_password_2024}", "ping"] + interval: 10s + timeout: 3s + retries: 5 + restart: always + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # ============ AI Agent Layer ============ + + # LangGraph Agent Main Service - 生产环境 + agent: + build: + context: ./agent + dockerfile: Dockerfile + args: + - ENVIRONMENT=production + image: ai-agent:latest + container_name: ai_agent_prod + environment: + # AI Model + ZHIPU_API_KEY: ${ZHIPU_API_KEY} + ZHIPU_MODEL: ${ZHIPU_MODEL:-GLM-4-Flash-250414} + ENABLE_REASONING_MODE: ${ENABLE_REASONING_MODE:-false} + REASONING_MODE_FOR_COMPLEX: ${REASONING_MODE_FOR_COMPLEX:-true} + + # Redis + REDIS_HOST: redis + REDIS_PORT: 6379 + REDIS_PASSWORD: ${REDIS_PASSWORD:-prod_redis_password_2024} + REDIS_DB: 0 + + # Chatwoot (生产环境) + CHATWOOT_API_URL: ${CHATWOOT_API_URL} + CHATWOOT_API_TOKEN: ${CHATWOOT_API_TOKEN} + CHATWOOT_WEBHOOK_SECRET: ${CHATWOOT_WEBHOOK_SECRET} + CHATWOOT_ACCOUNT_ID: ${CHATWOOT_ACCOUNT_ID:-1} + + # External APIs + STRAPI_API_URL: ${STRAPI_API_URL} + STRAPI_API_TOKEN: ${STRAPI_API_TOKEN} + HYPERF_API_URL: ${HYPERF_API_URL} + HYPERF_API_TOKEN: ${HYPERF_API_TOKEN} + + # Mall API + MALL_API_URL: ${MALL_API_URL} + MALL_TENANT_ID: ${MALL_TENANT_ID:-2} + MALL_CURRENCY_CODE: ${MALL_CURRENCY_CODE:-EUR} + MALL_LANGUAGE_ID: ${MALL_LANGUAGE_ID:-1} + MALL_SOURCE: ${MALL_SOURCE:-www.gaia888.com} + + # Frontend URLs + FRONTEND_URL: ${FRONTEND_URL:-https://www.gaia888.com} + + # MCP Servers + STRAPI_MCP_URL: http://strapi_mcp:8001 + ORDER_MCP_URL: http://order_mcp:8002 + AFTERSALE_MCP_URL: http://aftersale_mcp:8003 + PRODUCT_MCP_URL: http://product_mcp:8004 + + # Config + LOG_LEVEL: ${LOG_LEVEL:-WARNING} + MAX_CONVERSATION_STEPS: ${MAX_CONVERSATION_STEPS:-10} + CONVERSATION_TIMEOUT: ${CONVERSATION_TIMEOUT:-3600} + + # Production specific + ENVIRONMENT: production + SENTRY_DSN: ${SENTRY_DSN} + ports: + - "8000:8000" + volumes: + - agent_logs_prod:/app/logs + depends_on: + redis: + condition: service_healthy + strapi_mcp: + condition: service_started + order_mcp: + condition: service_started + aftersale_mcp: + condition: service_started + product_mcp: + condition: service_started + networks: + - ai_network_prod + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + + # ============ MCP Servers ============ + + # Strapi MCP (FAQ/Knowledge Base) - 生产环境 + strapi_mcp: + build: + context: ./mcp_servers/strapi_mcp + dockerfile: Dockerfile + image: ai-strapi-mcp:latest + container_name: ai_strapi_mcp_prod + environment: + STRAPI_API_URL: ${STRAPI_API_URL} + STRAPI_API_TOKEN: ${STRAPI_API_TOKEN} + LOG_LEVEL: ${LOG_LEVEL:-WARNING} + ENVIRONMENT: production + ports: + - "8001:8001" + volumes: + - ./mcp_servers/shared:/app/shared:ro + networks: + - ai_network_prod + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8001/health"] + interval: 30s + timeout: 10s + retries: 3 + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M + + # Order MCP - 生产环境 + order_mcp: + build: + context: ./mcp_servers/order_mcp + dockerfile: Dockerfile + image: ai-order-mcp:latest + container_name: ai_order_mcp_prod + environment: + HYPERF_API_URL: ${HYPERF_API_URL} + HYPERF_API_TOKEN: ${HYPERF_API_TOKEN} + MALL_API_URL: ${MALL_API_URL} + MALL_TENANT_ID: ${MALL_TENANT_ID:-2} + MALL_CURRENCY_CODE: ${MALL_CURRENCY_CODE:-EUR} + MALL_LANGUAGE_ID: ${MALL_LANGUAGE_ID:-1} + MALL_SOURCE: ${MALL_SOURCE:-www.gaia888.com} + LOG_LEVEL: ${LOG_LEVEL:-WARNING} + ENVIRONMENT: production + ports: + - "8002:8002" + volumes: + - ./mcp_servers/shared:/app/shared:ro + networks: + - ai_network_prod + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8002/health"] + interval: 30s + timeout: 10s + retries: 3 + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M + + # Aftersale MCP - 生产环境 + aftersale_mcp: + build: + context: ./mcp_servers/aftersale_mcp + dockerfile: Dockerfile + image: ai-aftersale-mcp:latest + container_name: ai_aftersale_mcp_prod + environment: + HYPERF_API_URL: ${HYPERF_API_URL} + HYPERF_API_TOKEN: ${HYPERF_API_TOKEN} + LOG_LEVEL: ${LOG_LEVEL:-WARNING} + ENVIRONMENT: production + ports: + - "8003:8003" + volumes: + - ./mcp_servers/shared:/app/shared:ro + networks: + - ai_network_prod + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8003/health"] + interval: 30s + timeout: 10s + retries: 3 + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M + + # Product MCP - 生产环境 + product_mcp: + build: + context: ./mcp_servers/product_mcp + dockerfile: Dockerfile + image: ai-product-mcp:latest + container_name: ai_product_mcp_prod + environment: + HYPERF_API_URL: ${HYPERF_API_URL} + HYPERF_API_TOKEN: ${HYPERF_API_TOKEN} + LOG_LEVEL: ${LOG_LEVEL:-WARNING} + ENVIRONMENT: production + ports: + - "8004:8004" + volumes: + - ./mcp_servers/shared:/app/shared:ro + networks: + - ai_network_prod + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8004/health"] + interval: 30s + timeout: 10s + retries: 3 + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M + + # ============ Monitoring (Optional) ============ + + # Prometheus - 指标收集 + prometheus: + image: prom/prometheus:latest + container_name: ai_prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + networks: + - ai_network_prod + restart: always + profiles: + - monitoring + + # Grafana - 可视化监控 + grafana: + image: grafana/grafana:latest + container_name: ai_grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin} + - GF_USERS_ALLOW_SIGN_UP=false + ports: + - "3001:3000" + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro + - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro + networks: + - ai_network_prod + restart: always + profiles: + - monitoring + +networks: + ai_network_prod: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + +volumes: + redis_data_prod: + agent_logs_prod: + prometheus_data: + grafana_data: diff --git a/docker-compose.yml b/docker-compose.yml index e933e0a..22413c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -149,9 +149,16 @@ services: context: ./mcp_servers/product_mcp dockerfile: Dockerfile container_name: ai_product_mcp + env_file: + - .env environment: HYPERF_API_URL: ${HYPERF_API_URL} HYPERF_API_TOKEN: ${HYPERF_API_TOKEN} + MALL_API_URL: ${MALL_API_URL} + MALL_TENANT_ID: ${MALL_TENANT_ID:-2} + MALL_CURRENCY_CODE: ${MALL_CURRENCY_CODE:-EUR} + MALL_LANGUAGE_ID: ${MALL_LANGUAGE_ID:-1} + MALL_SOURCE: ${MALL_SOURCE:-us.qa1.gaia888.com} LOG_LEVEL: ${LOG_LEVEL:-INFO} ports: - "8004:8004"