代码部署说明

This commit is contained in:
董宏凯 committed 2026-07-11 18:58:16 +08:00
1 parent 221e947488
commit 55b5c2cc9f
7 files changed
+727

No files matched your search

@@ -0,0 +1,17 @@
.git
.pytest_cache
.ruff_cache
.venv
__pycache__
*.py[cod]
*.pyo
*.zip
# Runtime state is mounted from the host in Docker deployments.
.deer-flow
logs
# Local/offline artifacts should not be baked into the runtime image.
docker-data
dist
wheelhouse
@@ -0,0 +1,52 @@
# Backend Docker Offline Deployment
Build once on an online Linux server:
```bash
cd offline-backend-20260512/backend
chmod +x build-backend-image.sh save-backend-image.sh start-backend-docker.sh
./build-backend-image.sh
./save-backend-image.sh
```
The build script defaults to Aliyun for apt packages and Tsinghua for Python packages:
```bash
APT_MIRROR=mirrors.aliyun.com \
UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple/ \
./build-backend-image.sh
```
The Docker build installs the pinned `requirements-offline.txt` with pip from
the configured Python mirror, so the dependency image contains the backend
runtime dependencies and can be moved to an offline machine.
Move `dist/deerflow-backend-offline.tar` plus this backend directory to the
offline machine, then load and start:
```bash
docker load -i deerflow-backend-offline.tar
./start-backend-docker.sh 8001
```
Useful overrides:
```bash
BACKEND_PORT=9001 ./start-backend-docker.sh
IMAGE_NAME=your-image:tag ./start-backend-docker.sh 9001
DATA_DIR=/data/deerflow-backend ./start-backend-docker.sh
```
The startup script bind-mounts these host paths:
- backend code: current backend directory -> `/app/backend`
- config: `docker-data/config/config.yaml`, `extensions_config.json`, `.env`
- agents and SQLite/runtime data: `docker-data/.deer-flow`
- skills: `docker-data/skills`
- logs: `docker-data/logs`
Edit files under `docker-data/` on the host, then restart the container:
```bash
docker restart deerflow-backend
```
@@ -0,0 +1,108 @@
# Backend-only DeerFlow image for offline delivery.
#
# Build online once, then export with:
# docker save deerflow-backend-offline:latest -o deerflow-backend-offline.tar
#
# Runtime code/config/data are bind-mounted by start-backend-docker.sh so the
# customer can edit code, agents, skills, config.yaml, .env, and SQLite data
# without rebuilding this dependency image.
ARG PYTHON_IMAGE=python:3.12-slim-bookworm
FROM ${PYTHON_IMAGE} AS builder
ARG APT_MIRROR=mirrors.aliyun.com
ARG UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple/
ARG UV_VERSION=0.7.20
ARG UV_NO_CACHE=1
ARG UV_COMPILE_BYTECODE=0
ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
UV_INDEX_URL=${UV_INDEX_URL} \
UV_LINK_MODE=copy \
UV_NO_CACHE=${UV_NO_CACHE} \
UV_COMPILE_BYTECODE=${UV_COMPILE_BYTECODE} \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /build/backend
RUN if [ -n "${APT_MIRROR}" ]; then \
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; \
sed -i "s|security.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; \
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list 2>/dev/null || true; \
sed -i "s|security.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list 2>/dev/null || true; \
fi \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
git \
&& rm -rf /var/lib/apt/lists/* \
&& python -m pip install --no-cache-dir -i "${UV_INDEX_URL}" "uv==${UV_VERSION}"
ARG UV_HTTP_TIMEOUT=300
ARG UV_CONCURRENT_DOWNLOADS=4
ARG UV_CONCURRENT_INSTALLS=4
ARG UV_CONCURRENT_BUILDS=2
ENV UV_HTTP_TIMEOUT=${UV_HTTP_TIMEOUT} \
UV_CONCURRENT_DOWNLOADS=${UV_CONCURRENT_DOWNLOADS} \
UV_CONCURRENT_INSTALLS=${UV_CONCURRENT_INSTALLS} \
UV_CONCURRENT_BUILDS=${UV_CONCURRENT_BUILDS}
COPY pyproject.toml uv.lock README.md requirements-offline.txt ./
COPY packages ./packages
RUN python -m venv /opt/venv \
&& /opt/venv/bin/python -m pip install --no-cache-dir --upgrade -i "${UV_INDEX_URL}" pip setuptools wheel \
&& sed -E '/--hash=/d; s/[[:space:]]+\\$//; s|^-e[[:space:]]+./packages/harness$|./packages/harness|' requirements-offline.txt > /tmp/requirements-docker.txt \
&& /opt/venv/bin/pip install --no-cache-dir -i "${UV_INDEX_URL}" -r /tmp/requirements-docker.txt \
&& /opt/venv/bin/pip install --no-cache-dir -i "${UV_INDEX_URL}" "asyncmy>=0.2.11"
COPY . .
FROM ${PYTHON_IMAGE} AS runtime
ARG APT_MIRROR=mirrors.aliyun.com
ENV PATH=/opt/venv/bin:$PATH \
PYTHONPATH=/app/backend:/app/backend/packages/harness \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DEER_FLOW_PROJECT_ROOT=/app/backend \
DEER_FLOW_HOME=/app/backend/.deer-flow \
DEER_FLOW_CONFIG_PATH=/app/backend/config.yaml \
DEER_FLOW_EXTENSIONS_CONFIG_PATH=/app/backend/extensions_config.json \
DEER_FLOW_SKILLS_PATH=/app/backend/skills \
BACKEND_PORT=8001
WORKDIR /app/backend
RUN if [ -n "${APT_MIRROR}" ]; then \
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; \
sed -i "s|security.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; \
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list 2>/dev/null || true; \
sed -i "s|security.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list 2>/dev/null || true; \
fi \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
docker.io \
libmagic1 \
nodejs \
npm \
poppler-utils \
tini \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /opt/venv /opt/venv
COPY --from=builder /build/backend /app/backend
EXPOSE 8001
ENTRYPOINT ["tini", "--"]
CMD ["sh", "-c", "cd /app/backend && exec uvicorn app.gateway.app:app --host 0.0.0.0 --port ${BACKEND_PORT:-8001}"]
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_NAME="${IMAGE_NAME:-deerflow-backend-offline:latest}"
APT_MIRROR="${APT_MIRROR:-mirrors.aliyun.com}"
UV_INDEX_URL="${UV_INDEX_URL:-https://pypi.tuna.tsinghua.edu.cn/simple/}"
UV_HTTP_TIMEOUT="${UV_HTTP_TIMEOUT:-300}"
UV_NO_CACHE="${UV_NO_CACHE:-1}"
UV_COMPILE_BYTECODE="${UV_COMPILE_BYTECODE:-0}"
UV_CONCURRENT_DOWNLOADS="${UV_CONCURRENT_DOWNLOADS:-4}"
UV_CONCURRENT_INSTALLS="${UV_CONCURRENT_INSTALLS:-4}"
UV_CONCURRENT_BUILDS="${UV_CONCURRENT_BUILDS:-2}"
PYTHON_IMAGE="${PYTHON_IMAGE:-python:3.12-slim-bookworm}"
cd "${SCRIPT_DIR}"
docker build \
-f Dockerfile.backend-offline \
-t "${IMAGE_NAME}" \
--build-arg "PYTHON_IMAGE=${PYTHON_IMAGE}" \
--build-arg "APT_MIRROR=${APT_MIRROR}" \
--build-arg "UV_INDEX_URL=${UV_INDEX_URL}" \
--build-arg "UV_HTTP_TIMEOUT=${UV_HTTP_TIMEOUT}" \
--build-arg "UV_NO_CACHE=${UV_NO_CACHE}" \
--build-arg "UV_COMPILE_BYTECODE=${UV_COMPILE_BYTECODE}" \
--build-arg "UV_CONCURRENT_DOWNLOADS=${UV_CONCURRENT_DOWNLOADS}" \
--build-arg "UV_CONCURRENT_INSTALLS=${UV_CONCURRENT_INSTALLS}" \
--build-arg "UV_CONCURRENT_BUILDS=${UV_CONCURRENT_BUILDS}" \
.
echo "Built image: ${IMAGE_NAME}"
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_NAME="${IMAGE_NAME:-deerflow-backend-offline:latest}"
OUT_DIR="${OUT_DIR:-${SCRIPT_DIR}/dist}"
OUT_FILE="${OUT_FILE:-${OUT_DIR}/deerflow-backend-offline.tar}"
mkdir -p "${OUT_DIR}"
docker save "${IMAGE_NAME}" -o "${OUT_FILE}"
echo "Saved image: ${OUT_FILE}"
echo "Offline load command: docker load -i ${OUT_FILE}"
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_NAME="${IMAGE_NAME:-deerflow-backend-offline:latest}"
CONTAINER_NAME="${CONTAINER_NAME:-deerflow-backend}"
BACKEND_PORT="${1:-${BACKEND_PORT:-8001}}"
CODE_DIR="${CODE_DIR:-${SCRIPT_DIR}}"
DATA_DIR="${DATA_DIR:-${SCRIPT_DIR}/docker-data}"
CONFIG_DIR="${CONFIG_DIR:-${DATA_DIR}/config}"
RUNTIME_DIR="${RUNTIME_DIR:-${DATA_DIR}/.deer-flow}"
SKILLS_DIR="${SKILLS_DIR:-${DATA_DIR}/skills}"
KNOWLEDGE_DIR="${KNOWLEDGE_DIR:-${DATA_DIR}/knowledge}"
LOGS_DIR="${LOGS_DIR:-${DATA_DIR}/logs}"
CONFIG_FILE="${CONFIG_DIR}/config.yaml"
EXTENSIONS_FILE="${CONFIG_DIR}/extensions_config.json"
LANGGRAPH_FILE="${CONFIG_DIR}/langgraph.json"
ENV_FILE="${CONFIG_DIR}/.env"
copy_if_missing() {
local src="$1"
local dst="$2"
if [[ ! -e "${dst}" && -e "${src}" ]]; then
mkdir -p "$(dirname "${dst}")"
cp -a "${src}" "${dst}"
fi
}
copy_dir_if_missing() {
local src="$1"
local dst="$2"
if [[ ! -e "${dst}" && -d "${src}" ]]; then
mkdir -p "$(dirname "${dst}")"
cp -a "${src}" "${dst}"
fi
}
mkdir -p "${CONFIG_DIR}" "${RUNTIME_DIR}/data" "${LOGS_DIR}"
copy_if_missing "${CODE_DIR}/config.yaml" "${CONFIG_FILE}"
copy_if_missing "${CODE_DIR}/extensions_config.json" "${EXTENSIONS_FILE}"
copy_if_missing "${CODE_DIR}/langgraph.json" "${LANGGRAPH_FILE}"
copy_if_missing "${CODE_DIR}/.env" "${ENV_FILE}"
touch "${ENV_FILE}"
copy_dir_if_missing "${CODE_DIR}/skills" "${SKILLS_DIR}"
mkdir -p "${SKILLS_DIR}"
copy_dir_if_missing "${CODE_DIR}/knowledge" "${KNOWLEDGE_DIR}"
mkdir -p "${KNOWLEDGE_DIR}"
copy_dir_if_missing "${CODE_DIR}/.deer-flow/agents" "${RUNTIME_DIR}/agents"
copy_dir_if_missing "${CODE_DIR}/.deer-flow/channels" "${RUNTIME_DIR}/channels"
copy_dir_if_missing "${CODE_DIR}/.deer-flow/users" "${RUNTIME_DIR}/users"
copy_if_missing "${CODE_DIR}/.deer-flow/SOUL.md" "${RUNTIME_DIR}/SOUL.md"
copy_if_missing "${CODE_DIR}/.deer-flow/USER.md" "${RUNTIME_DIR}/USER.md"
copy_if_missing "${CODE_DIR}/.deer-flow/system_settings.json" "${RUNTIME_DIR}/system_settings.json"
if docker ps -a --format '{{.Names}}' | grep -Fxq "${CONTAINER_NAME}"; then
docker rm -f "${CONTAINER_NAME}" >/dev/null
fi
DOCKER_ARGS=(
run -d
--name "${CONTAINER_NAME}"
--restart unless-stopped
-p "${BACKEND_PORT}:${BACKEND_PORT}"
--add-host host.docker.internal:host-gateway
-e "BACKEND_PORT=${BACKEND_PORT}"
-e "DEER_FLOW_GATEWAY_PORT=${BACKEND_PORT}"
-e "DEER_FLOW_PROJECT_ROOT=/app/backend"
-e "DEER_FLOW_HOME=/app/backend/.deer-flow"
-e "DEER_FLOW_HOST_BASE_DIR=${RUNTIME_DIR}"
-e "DEER_FLOW_CONFIG_PATH=/app/backend/config.yaml"
-e "DEER_FLOW_EXTENSIONS_CONFIG_PATH=/app/backend/extensions_config.json"
-e "DEER_FLOW_SKILLS_PATH=/app/backend/skills"
-v "${CODE_DIR}:/app/backend"
-v "${CONFIG_FILE}:/app/backend/config.yaml"
-v "${EXTENSIONS_FILE}:/app/backend/extensions_config.json"
-v "${LANGGRAPH_FILE}:/app/backend/langgraph.json"
-v "${ENV_FILE}:/app/backend/.env"
-v "${RUNTIME_DIR}:/app/backend/.deer-flow"
-v "${SKILLS_DIR}:/app/backend/skills"
-v "${KNOWLEDGE_DIR}:/app/backend/knowledge"
-v "${LOGS_DIR}:/app/backend/logs"
)
if [[ -S /var/run/docker.sock ]]; then
DOCKER_ARGS+=(-v /var/run/docker.sock:/var/run/docker.sock)
fi
DOCKER_ARGS+=("${IMAGE_NAME}")
docker "${DOCKER_ARGS[@]}"
echo "Started ${CONTAINER_NAME}"
echo "Backend: http://127.0.0.1:${BACKEND_PORT}"
echo "Config: ${CONFIG_DIR}"
echo "Data: ${DATA_DIR}"
@@ -0,0 +1,403 @@
# DeerFlow 后台离线部署说明
本文档只说明“后台服务”的离线部署,不包含前端。
## 1. 当前服务器文件位置
当前已经在服务器上准备好的部署目录是:
```bash
/opt/deerflow-backend
```
主要文件和目录如下:
```text
/opt/deerflow-backend/
├── dist/deerflow-backend-offline.tar # 离线 Docker 镜像包
├── start-backend-docker.sh # 启动脚本
├── save-backend-image.sh # 镜像导出脚本
├── build-backend-image.sh # 在线构建脚本
├── config.yaml # 默认配置模板
├── extensions_config.json # 默认扩展配置模板
├── langgraph.json # LangGraph 配置
├── .env # 环境变量文件,没有则启动脚本会创建空文件
├── .deer-flow/agents/ # 默认智能体模板目录
├── skills/ # 默认技能模板目录
└── docker-data/ # 实际运行时挂载数据,启动后生成
```
当前服务器已验证:
```bash
cd /opt/deerflow-backend
./start-backend-docker.sh 8001
```
健康检查:
```bash
curl http://127.0.0.1:8001/health
curl http://127.0.0.1:8001/api/health
```
## 2. 客户现场更换部署目录
客户现场可以换任意部署目录,不需要改脚本。把整个 `deerflow-backend` 目录放到目标位置即可。
示例:客户现场部署到 `/data/deerflow-backend`
```bash
DEPLOY_DIR=/data/deerflow-backend
mkdir -p "$DEPLOY_DIR"
# 将交付的 deerflow-backend 目录内容放到 $DEPLOY_DIR
cd "$DEPLOY_DIR"
chmod +x *.sh
```
加载离线镜像:
```bash
docker load -i dist/deerflow-backend-offline.tar
```
启动后台,端口可改:
```bash
./start-backend-docker.sh 8001
```
如果客户现场要用 `9001` 端口:
```bash
./start-backend-docker.sh 9001
```
脚本会自动把当前目录作为代码目录,把数据放到:
```text
$DEPLOY_DIR/docker-data
```
## 3. 数据、配置、智能体、技能目录
启动后,实际可修改的文件都在宿主机目录,不在容器内部。
默认目录如下:
```text
$DEPLOY_DIR/docker-data/config # 配置目录
$DEPLOY_DIR/docker-data/.deer-flow # 运行时目录:智能体、用户数据、SQLite 数据
$DEPLOY_DIR/docker-data/skills # 技能目录
$DEPLOY_DIR/docker-data/knowledge # 知识库目录
$DEPLOY_DIR/docker-data/logs # 日志目录
```
配置文件位置:
```text
$DEPLOY_DIR/docker-data/config/config.yaml
$DEPLOY_DIR/docker-data/config/extensions_config.json
$DEPLOY_DIR/docker-data/config/langgraph.json
$DEPLOY_DIR/docker-data/config/.env
```
智能体目录:
```text
$DEPLOY_DIR/docker-data/.deer-flow/agents
```
技能目录:
```text
$DEPLOY_DIR/docker-data/skills/public
$DEPLOY_DIR/docker-data/skills/custom
```
SQLite 聊天/checkpointer 数据库目录:
```text
$DEPLOY_DIR/docker-data/.deer-flow/data
$DEPLOY_DIR/docker-data/.deer-flow/.deer-flow/data
```
注意:当前部署使用 **MySQL 业务库 + SQLite 聊天/checkpointer 库**,不使用 PostgreSQL。SQLite 运行时可能同时有以下文件,备份或替换时要一起处理:
```text
deerflow.db
deerflow.db-wal
deerflow.db-shm
```
## 4. 客户现场加载智能体和技能
推荐流程:
1. 先启动一次,让目录自动生成。
2. 停止容器。
3. 复制客户的智能体、技能、SQLite 聊天库文件,并配置 MySQL 地址。
4. 再启动容器。
命令示例:
```bash
cd "$DEPLOY_DIR"
./start-backend-docker.sh 8001
docker rm -f deerflow-backend
```
复制智能体:
```bash
mkdir -p "$DEPLOY_DIR/docker-data/.deer-flow/agents"
cp -a /path/to/customer-agents/. "$DEPLOY_DIR/docker-data/.deer-flow/agents/"
```
复制技能:
```bash
mkdir -p "$DEPLOY_DIR/docker-data/skills/public"
mkdir -p "$DEPLOY_DIR/docker-data/skills/custom"
cp -a /path/to/customer-skills/public/. "$DEPLOY_DIR/docker-data/skills/public/"
cp -a /path/to/customer-skills/custom/. "$DEPLOY_DIR/docker-data/skills/custom/"
```
重新启动:
```bash
cd "$DEPLOY_DIR"
./start-backend-docker.sh 8001
```
如果只更新技能或智能体,不换数据库,也可以复制后直接重启:
```bash
docker restart deerflow-backend
```
## 5. 修改或替换 SQLite 聊天/checkpointer 数据库
SQLite 只保存聊天运行时、LangGraph checkpointer、部分本地运行数据。业务表使用 MySQL,MySQL 地址见第 6 节。
最稳的方式是停容器后整体替换运行时目录:
```bash
cd "$DEPLOY_DIR"
docker rm -f deerflow-backend
mv docker-data/.deer-flow docker-data/.deer-flow.bak.$(date +%Y%m%d%H%M%S)
cp -a /path/to/customer-dot-deer-flow "$DEPLOY_DIR/docker-data/.deer-flow"
./start-backend-docker.sh 8001
```
如果只替换数据库文件,至少要处理这两个目录里的 `deerflow.db*`:
```text
$DEPLOY_DIR/docker-data/.deer-flow/data/deerflow.db*
$DEPLOY_DIR/docker-data/.deer-flow/.deer-flow/data/deerflow.db*
```
操作示例:
```bash
cd "$DEPLOY_DIR"
docker rm -f deerflow-backend
mkdir -p docker-data/.deer-flow/data
mkdir -p docker-data/.deer-flow/.deer-flow/data
cp -a /path/to/db-main/deerflow.db* docker-data/.deer-flow/data/
cp -a /path/to/db-runtime/deerflow.db* docker-data/.deer-flow/.deer-flow/data/
./start-backend-docker.sh 8001
```
不要在容器运行时直接覆盖 SQLite 文件,容易损坏 WAL 数据。
## 6. 修改配置和数据库配置
默认配置会在第一次启动时复制到:
```text
$DEPLOY_DIR/docker-data/config/config.yaml
```
要改配置,改这个文件,不要进容器改。
本项目现场部署按 **MySQL + SQLite** 使用:
- MySQL:业务表,例如用户、智能体、技能、任务、菜单、按钮、敏感词、轻应用等。
- SQLite:聊天运行时和 LangGraph checkpointer,继续放在挂载出来的 `.deer-flow` 目录。
- 不使用 PostgreSQL,不要配置 `database.backend: postgres`,也不要配置 `POSTGRES_URL`。
MySQL 地址主要改两个文件:
```text
$DEPLOY_DIR/docker-data/config/config.yaml
$DEPLOY_DIR/docker-data/config/.env
```
`config.yaml` 里的数据库配置建议改成:
```yaml
database:
backend: mysql
sqlite_dir: .deer-flow/data
mysql_url: $MYSQL_DATABASE_URL
pool_size: 20
max_overflow: 20
pool_timeout: 30
echo_sql: false
checkpointer:
type: sqlite
connection_string: .deer-flow/data/deerflow.db
```
然后在 `.env` 里写 MySQL 连接地址:
```bash
MYSQL_DATABASE_URL=mysql+asyncmy://deerflow:密码@192.168.1.10:3306/deerflow?charset=utf8mb4
```
把上面示例里的内容换成客户现场真实值:
```text
deerflow # MySQL 用户名
密码 # MySQL 密码
192.168.1.10 # MySQL 服务器 IP
3306 # MySQL 端口
deerflow # MySQL 数据库名
```
如果 MySQL 就装在 Docker 宿主机这台服务器上,容器里不要写 `127.0.0.1`,建议写:
```bash
MYSQL_DATABASE_URL=mysql+asyncmy://deerflow:密码@host.docker.internal:3306/deerflow?charset=utf8mb4
```
如果密码里有特殊字符,需要做 URL 编码,常见示例:
```text
@ 写成 %40
# 写成 %23
% 写成 %25
空格 写成 %20
```
MySQL 数据库建议提前建好:
```sql
CREATE DATABASE deerflow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
如果 MySQL 用户有建库权限,服务启动时也会尝试自动创建缺失的数据库。客户现场更稳的做法是先建库、导入 MySQL 备份,再启动后台。
如果客户现场已有 MySQL 备份,先在客户 MySQL 中导入:
```bash
mysql -h 192.168.1.10 -P 3306 -u deerflow -p deerflow < deerflow_mysql.sql
```
SQLite 聊天库仍按第 5 节复制到:
```text
$DEPLOY_DIR/docker-data/.deer-flow/data/deerflow.db*
$DEPLOY_DIR/docker-data/.deer-flow/.deer-flow/data/deerflow.db*
```
如果需要把数据目录单独放到别的位置,不需要改 `config.yaml`,启动时指定 `DATA_DIR` 更稳:
```bash
cd "$DEPLOY_DIR"
DATA_DIR=/data/deerflow-backend-data ./start-backend-docker.sh 8001
```
此时实际目录变成:
```text
/data/deerflow-backend-data/config
/data/deerflow-backend-data/.deer-flow
/data/deerflow-backend-data/skills
/data/deerflow-backend-data/knowledge
/data/deerflow-backend-data/logs
```
如果客户现场部署目录变化,只需要换 `DEPLOY_DIR` 或把整包放到新目录;MySQL 地址仍然只改 `$DEPLOY_DIR/docker-data/config/.env`,SQLite/智能体/技能仍然跟着 `DATA_DIR` 走。
如果要改 `.env`,位置是:
```text
$DEPLOY_DIR/docker-data/config/.env
```
修改配置后重启:
```bash
cd "$DEPLOY_DIR"
docker rm -f deerflow-backend
./start-backend-docker.sh 8001
```
## 7. 常用运维命令
查看容器:
```bash
docker ps -a --filter name=deerflow-backend
```
查看日志:
```bash
docker logs -f --tail 200 deerflow-backend
```
停止:
```bash
docker rm -f deerflow-backend
```
重启:
```bash
docker restart deerflow-backend
```
重新启动并换端口:
```bash
cd "$DEPLOY_DIR"
./start-backend-docker.sh 9001
```
检查接口:
```bash
curl http://127.0.0.1:8001/health
curl http://127.0.0.1:8001/api/health
```
## 8. 交付清单
客户离线部署至少需要:
```text
deerflow-backend/
├── dist/deerflow-backend-offline.tar
├── start-backend-docker.sh
├── config.yaml
├── extensions_config.json
├── langgraph.json
├── .env
├── .deer-flow/agents/
├── skills/
└── 其他后台代码文件
```
客户机器需要提前安装好 Docker。离线部署时不需要再下载 Python 依赖,依赖已经包含在 Docker 镜像里。