diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..b79114c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,55 @@
+# Dependencies
+node_modules/
+.pnpm-store/
+
+# Build output
+dist/
+build/
+target/
+
+# IDE files
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+
+# Git files
+.git/
+.gitignore
+
+# Docker files (避免递归,但保留需要的配置文件)
+docker/backend/
+docker/frontend/Dockerfile
+docker/.env
+docker/.env.example
+docker/docker-compose.yml
+docker/docker-*.sh
+docker/docker-*.bat
+
+# Test files
+coverage/
+.nyc_output/
+.playwright-cli/
+
+# Documentation
+*.md
+docs/
+
+# Root config files (不需要复制到镜像)
+.env
+.gitattributes
+.github/
+AGENTS.md
+LICENSE
+
+# SQL files (通过 volume 挂载,不需要复制)
+sql/
diff --git a/data-agent-backend/.dockerignore b/data-agent-backend/.dockerignore
new file mode 100644
index 0000000..559e119
--- /dev/null
+++ b/data-agent-backend/.dockerignore
@@ -0,0 +1,28 @@
+# Build artifacts
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+# IDE files
+.idea/
+*.iml
+*.iws
+*.ipr
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+
+# Git files
+.git/
+.gitignore
+
+# Docker files
+Dockerfile
+docker/
+
+# Documentation
+README.md
+docs/
diff --git a/data-agent-backend/pom.xml b/data-agent-backend/pom.xml
index e5a1e53..64e6b4c 100644
--- a/data-agent-backend/pom.xml
+++ b/data-agent-backend/pom.xml
@@ -84,6 +84,11 @@
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
org.springframework.boot
spring-boot-starter-validation
@@ -123,7 +128,6 @@
io.agentscope
agentscope-extensions-nacos-skill
${agentscope.version}
- true
diff --git a/data-agent-backend/src/main/resources/application.properties b/data-agent-backend/src/main/resources/application.properties
index e7038ac..4930a39 100644
--- a/data-agent-backend/src/main/resources/application.properties
+++ b/data-agent-backend/src/main/resources/application.properties
@@ -2,6 +2,8 @@ server.port=8080
spring.application.name=data-agent-management
+management.endpoints.web.exposure.include=health
+
# Database Configuration
spring.datasource.url=${DB_URL:jdbc:mysql://localhost:3306/data_agent?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}
spring.datasource.username=${DB_USERNAME:root}
diff --git a/data-agent-frontend/.dockerignore b/data-agent-frontend/.dockerignore
new file mode 100644
index 0000000..d227597
--- /dev/null
+++ b/data-agent-frontend/.dockerignore
@@ -0,0 +1,40 @@
+# Dependencies
+node_modules/
+.pnpm-store/
+
+# Build output
+dist/
+build/
+
+# IDE files
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+
+# Git files
+.git/
+.gitignore
+
+# Docker files
+Dockerfile
+docker/
+
+# Test files
+coverage/
+.nyc_output/
+.playwright-cli/
+
+# Documentation
+README.md
+docs/
+CODE_STYLE.md
diff --git a/docker/.env.example b/docker/.env.example
new file mode 100644
index 0000000..f3a436d
--- /dev/null
+++ b/docker/.env.example
@@ -0,0 +1,18 @@
+# Data Agent 环境变量配置
+# 此文件不应提交到 Git(已在 .gitignore 中)
+
+# AI 模型配置 (必填)
+IO_GITHUB_MALONETALK_MODEL_API_KEY=
+IO_GITHUB_MALONETALK_MODEL_PROVIDER=openai
+IO_GITHUB_MALONETALK_MODEL_NAME=deepseek-v4-flash
+IO_GITHUB_MALONETALK_MODEL_BASE_URL=https://api.deepseek.com
+IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED=true
+
+# 数据库配置
+DB_PASSWORD=root
+
+# JWT 配置(生产环境必须 >= 32 字节)
+JWT_SECRET=
+
+# 管理员配置(首次启动时使用)
+ADMIN_INIT_PASSWORD=admin
diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile
new file mode 100644
index 0000000..e842739
--- /dev/null
+++ b/docker/backend/Dockerfile
@@ -0,0 +1,50 @@
+# Stage 1: Build (使用 maven 镜像,自带 Maven)
+FROM maven:3.9-eclipse-temurin-17 AS builder
+
+WORKDIR /app
+
+# Maven 依赖默认直连 Maven Central。国内网络拉取易中断导致构建失败时,
+# 取消下面整段注释,改用阿里云镜像源:
+# RUN mkdir -p /root/.m2 && echo '\
+# \
+# \
+# aliyun\
+# https://maven.aliyun.com/repository/public\
+# *\
+# \
+# \
+# ' > /root/.m2/settings.xml
+
+# Copy pom.xml and download dependencies
+COPY data-agent-backend/pom.xml .
+RUN mvn dependency:go-offline -B
+
+# Copy source code and build
+COPY data-agent-backend/src ./src
+RUN mvn clean package -DskipTests -B
+
+# Stage 2: Runtime
+FROM eclipse-temurin:17-jre-alpine
+
+WORKDIR /app
+
+# Skills 目录(skill.properties 的 filesystem 源)
+COPY skills/ /app/skills/
+
+# Add non-root user
+RUN addgroup -g 1001 appgroup && \
+ adduser -u 1001 -G appgroup -D appuser
+
+# Copy the built jar
+COPY --from=builder /app/target/*.jar app.jar
+
+# Create log directory
+RUN mkdir -p /app/logs && chown -R appuser:appgroup /app
+
+USER appuser
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
+ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
+
+ENTRYPOINT ["java", "-jar", "app.jar"]
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..8b0e5ed
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,104 @@
+# Data Agent Docker Compose Configuration
+#
+# 所有配置项都有合理的默认值,可以直接启动。
+# 如需自定义配置:cp .env.example .env,然后编辑 .env。
+#
+
+services:
+ # MySQL Database
+ mysql:
+ image: mysql:8.0
+ container_name: data-agent-mysql
+ restart: unless-stopped
+ environment:
+ # 可通过 .env.example 覆盖:DB_PASSWORD=xxx
+ MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root}
+ MYSQL_DATABASE: data_agent
+ MYSQL_CHARACTER_SET_SERVER: utf8mb4
+ MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
+ TZ: Asia/Shanghai
+ ports:
+ # 可通过 .env.example 覆盖:MYSQL_PORT=3307
+ - "${MYSQL_PORT:-3306}:3306"
+ volumes:
+ - data-agent-mysql:/var/lib/mysql
+ # 自动挂载项目根目录的 sql/ 文件夹进行初始化
+ - ../sql:/docker-entrypoint-initdb.d:ro
+ command:
+ - --character-set-server=utf8mb4
+ - --collation-server=utf8mb4_unicode_ci
+ - --character-set-client-handshake=FALSE
+ - --init-connect='SET NAMES utf8mb4'
+ - --default-authentication-plugin=mysql_native_password
+ networks:
+ - data-agent-network
+ healthcheck:
+ # 使用 mysql 命令实际测试数据库连接和查询
+ test: ["CMD", "mysql", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-root}", "-e", "SELECT 1 FROM data_agent.sys_user LIMIT 1;"]
+ interval: 10s
+ timeout: 10s
+ retries: 10
+ start_period: 60s
+
+ # Backend Service (Spring Boot)
+ backend:
+ build:
+ context: ..
+ dockerfile: docker/backend/Dockerfile
+ container_name: data-agent-backend
+ restart: unless-stopped
+ environment:
+ # 数据库配置(通常不需要修改)
+ DB_URL: ${DB_URL:-jdbc:mysql://mysql:3306/data_agent?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}
+ DB_USERNAME: ${DB_USERNAME:-root}
+ DB_PASSWORD: ${DB_PASSWORD:-root}
+
+ # AI 模型配置(重要:需要配置真实的 API Key)
+ # 可通过 .env.example 覆盖:IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-xxx
+ IO_GITHUB_MALONETALK_MODEL_PROVIDER: ${IO_GITHUB_MALONETALK_MODEL_PROVIDER:-dashscope}
+ IO_GITHUB_MALONETALK_MODEL_NAME: ${IO_GITHUB_MALONETALK_MODEL_NAME:-qwen3-max}
+ IO_GITHUB_MALONETALK_MODEL_BASE_URL: ${IO_GITHUB_MALONETALK_MODEL_BASE_URL:-}
+ IO_GITHUB_MALONETALK_MODEL_API_KEY: ${IO_GITHUB_MALONETALK_MODEL_API_KEY:-}
+ IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED: ${IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED:-false}
+
+ # JWT 配置(可通过 .env.example 覆盖)
+ # JWT_SECRET 必须 >= 32 字节
+ JWT_SECRET: ${JWT_SECRET:-}
+ JWT_EXPIRATION_HOURS: ${JWT_EXPIRATION_HOURS:-24}
+
+ # 初始管理员密码(首次启动时使用)
+ ADMIN_INIT_PASSWORD: ${ADMIN_INIT_PASSWORD:-admin}
+
+ TZ: Asia/Shanghai
+ ports:
+ # 可通过 .env.example 覆盖:BACKEND_PORT=8081
+ - "${BACKEND_PORT:-8080}:8080"
+ depends_on:
+ mysql:
+ condition: service_healthy
+ networks:
+ - data-agent-network
+
+ # Frontend Service (Vue + Nginx)
+ frontend:
+ build:
+ context: ..
+ dockerfile: docker/frontend/Dockerfile
+ container_name: data-agent-frontend
+ restart: unless-stopped
+ ports:
+ # 可通过 .env.example 覆盖:FRONTEND_PORT=3001
+ - "${FRONTEND_PORT:-3000}:80"
+ depends_on:
+ backend:
+ condition: service_healthy
+ networks:
+ - data-agent-network
+
+volumes:
+ data-agent-mysql:
+ name: data-agent-mysql
+
+networks:
+ data-agent-network:
+ name: data-agent-network
diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile
new file mode 100644
index 0000000..ef48775
--- /dev/null
+++ b/docker/frontend/Dockerfile
@@ -0,0 +1,31 @@
+# Stage 1: Build
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# 直接安装 pnpm(不使用 corepack)
+RUN npm install -g pnpm
+
+# 先复制所有源代码(.dockerignore 会排除不需要的文件)
+COPY data-agent-frontend/ .
+
+# 再安装依赖
+RUN pnpm install --frozen-lockfile
+
+# Build the application (跳过类型检查,直接打包)
+RUN pnpm vite build
+
+# Stage 2: Production with Nginx
+FROM nginx:alpine
+
+# Copy custom nginx config
+COPY docker/frontend/nginx.conf /etc/nginx/nginx.conf
+
+# Copy built files
+COPY --from=builder /app/dist /usr/share/nginx/html
+
+# Add healthcheck
+HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
+ CMD wget --no-verbose --tries=1 --spider http://127.0.0.1/health || exit 1
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker/frontend/nginx.conf b/docker/frontend/nginx.conf
new file mode 100644
index 0000000..d3f4b91
--- /dev/null
+++ b/docker/frontend/nginx.conf
@@ -0,0 +1,76 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # Logging
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+ error_log /var/log/nginx/error.log warn;
+
+ # Gzip compression
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 1024;
+ gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss image/svg+xml;
+
+ # Docker DNS resolver
+ resolver 127.0.0.11 valid=10s;
+
+ server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+
+ # Vue Router history mode support
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ location = /index.html {
+ add_header Cache-Control "no-cache";
+ }
+
+ # API proxy to backend
+ location ^~ /api/ {
+ set $backend_upstream http://backend:8080;
+ proxy_pass $backend_upstream;
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_buffering off;
+ proxy_read_timeout 600s;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 60s;
+ }
+
+ # Cache static assets
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+
+ # Health check endpoint
+ location /health {
+ access_log off;
+ return 200 "healthy\n";
+ add_header Content-Type text/plain;
+ }
+ }
+}
diff --git a/docs/getting-started.md b/docs/getting-started.md
index a264e73..310aa96 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -2,7 +2,75 @@
本文带你从零把 Data Agent 跑起来,并在前端用自然语言提第一个问题。预计耗时 10–20 分钟。
-## 1. 前置依赖
+## 方式一:Docker 启动(推荐)
+
+最快的方式,自动搭建 MySQL、后端、前端。
+
+### 前置要求
+
+- Docker 20.10+ 与 Docker Compose v2.0+
+- 支持 Linux / Windows / macOS
+
+### 环境变量配置
+
+启动前需要配置环境变量, 将`docker/.env.example`重命名为`docker/.env`:
+
+```bash
+cp docker/.env.example docker/.env
+```
+
+然后编辑 `docker/.env`,参考以下说明:
+
+**必填项:**
+```bash
+# AI 模型配置(必须配置,否则 AI 功能无法使用)
+IO_GITHUB_MALONETALK_MODEL_API_KEY={你的密钥}
+IO_GITHUB_MALONETALK_MODEL_PROVIDER=openai
+IO_GITHUB_MALONETALK_MODEL_NAME=deepseek-v4-flash
+IO_GITHUB_MALONETALK_MODEL_BASE_URL=https://api.deepseek.com
+IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED=true
+```
+
+**可选项(有默认值,生产环境建议修改):**
+```bash
+# 数据库密码(默认:root)
+DB_PASSWORD=
+
+# JWT 密钥(默认:空,生产环境必须设置且 >= 32 字节)
+JWT_SECRET=
+
+# 管理员初始密码(默认:admin)
+ADMIN_INIT_PASSWORD=
+```
+
+### 启动
+
+```bash
+cd docker
+docker-compose up -d --build
+```
+
+首次启动会自动:
+- 构建后端/前端镜像
+- 下载 MySQL 镜像
+- 执行 `sql/` 目录初始化数据库
+- 启动所有服务
+
+修改配置后重启:`docker-compose restart`
+
+### 访问
+
+- **前端**: http://localhost:3000
+- **后端**: http://localhost:8080
+- **管理员账号**: `admin` / `admin`(或你设置的 `ADMIN_INIT_PASSWORD`)
+
+---
+
+## 方式二:手动搭建
+
+适合开发调试或不想用 Docker 的场景。
+
+#### 1. 前置依赖
| 依赖 | 版本要求 | 说明 |
| --- | --- | --- |
@@ -12,7 +80,7 @@
| pnpm | 8+ | 前端包管理 |
| MySQL | 8+ | 元数据库(存语义层/数据源/会话等) |
-## 2. 准备元数据库
+### 2. 准备元数据库
Data Agent 需要一张 MySQL 元数据库来存放语义层、数据源、会话等信息。
@@ -26,7 +94,7 @@ mysql -u root -p data_agent < sql/data_source.sql
> `sql/data_source.sql` 包含全部元数据库初始化表结构,导入这一份即可。
-## 3. 配置并启动后端
+### 3. 配置并启动后端
后端默认端口 `8080`,应用名 `data-agent-management`。启动前需要告诉它:元数据库在哪、用哪个 LLM。
@@ -83,7 +151,7 @@ mvn spring-boot:run
看到日志中嵌入式容器启动在 `8080` 即成功。
-## 4. 启动前端
+### 4. 启动前端
```bash
cd data-agent-frontend
@@ -93,7 +161,7 @@ pnpm dev
前端默认运行在 http://localhost:3000 ,开发代理已把 `/api` 转发到 `http://localhost:8080`(见 `vite.config.ts`)。
-## 5. 第一次提问
+## 第一次提问
> **先分清两类数据库**:「数据源管理」里配置的是 **Agent 要连接、执行 SQL 数据分析的目标数据库**(你的业务库),不是第 2 步准备的元数据库 `data_agent`(后端存放语义层/数据源配置/会话的库)。元数据库在后端启动时通过环境变量 `DB_URL` 指定,不在页面上配置。
@@ -106,7 +174,7 @@ pnpm dev
> 如果回答不准,多半是语义层/指标口径没配好,或数据源尚未接入。参见 [semantic-layer.md](semantic-layer.md) 与 [configuration.md](configuration.md)。
-## 6. 常见问题
+## 常见问题
- **新增数据源连接失败,提示「未找到数据库驱动」**:后端默认仅内置 MySQL 驱动。请在 `data-agent-backend/pom.xml` 中添加你所用数据库的 JDBC 驱动依赖(坐标见 [configuration.md](configuration.md#4-查询数据源)),重新构建并启动后端后再试。
- **启动后端报数据源连接失败**:检查 `DB_URL` 中的库名、账号密码,以及 MySQL 是否允许该连接方式。