From e9c56f86354d27c1db1f5151956d416421948352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Mon, 1 Jun 2026 17:33:40 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- offline-backend-20260512/.dockerignore | 34 +++++ offline-backend-20260512/.gitattributes | 3 + .../DEPLOY_LINUX_DOCKER_ZH.md | 142 +++++++++++++++++- offline-backend-20260512/backend/Dockerfile | 3 +- offline-backend-20260512/deploy.sh | 80 +++++++++- .../scripts/export-offline-image.sh | 25 +++ 6 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 offline-backend-20260512/.dockerignore create mode 100644 offline-backend-20260512/.gitattributes create mode 100644 offline-backend-20260512/scripts/export-offline-image.sh diff --git a/offline-backend-20260512/.dockerignore b/offline-backend-20260512/.dockerignore new file mode 100644 index 00000000..ae7e815a --- /dev/null +++ b/offline-backend-20260512/.dockerignore @@ -0,0 +1,34 @@ +# Docker build context exclusions (context = offline-backend-20260512/) +.env +.env.* +!.env.example + +# Runtime / local data — must not enter the image +data/ +backend/.deer-flow/ +backend/.venv/ +backend/venv/ + +# Python caches +backend/**/__pycache__/ +backend/**/*.py[cod] +backend/.pytest_cache/ +backend/.ruff_cache/ +backend/.coverage +backend/htmlcov/ + +# IDE / OS +.git/ +.idea/ +.vscode/ +.DS_Store +Thumbs.db + +# Docs and tests (not needed in runtime image) +backend/tests/ +backend/docs/ +*.md +!backend/README.md + +# Large optional trees (uncomment if build context is too slow) +# backend/knowledge/ diff --git a/offline-backend-20260512/.gitattributes b/offline-backend-20260512/.gitattributes new file mode 100644 index 00000000..001b9d83 --- /dev/null +++ b/offline-backend-20260512/.gitattributes @@ -0,0 +1,3 @@ +# Shell scripts must use LF so Linux/bash can run them directly. +*.sh text eol=lf +scripts/*.sh text eol=lf diff --git a/offline-backend-20260512/DEPLOY_LINUX_DOCKER_ZH.md b/offline-backend-20260512/DEPLOY_LINUX_DOCKER_ZH.md index 359eaac3..e3aadf61 100644 --- a/offline-backend-20260512/DEPLOY_LINUX_DOCKER_ZH.md +++ b/offline-backend-20260512/DEPLOY_LINUX_DOCKER_ZH.md @@ -184,17 +184,36 @@ curl -s http://127.0.0.1:8001/openapi.json | head HOST_PORT=9001 bash deploy.sh up ``` -### 5.2 离线镜像模式(无法构建时) +### 5.2 离线镜像模式(内网/无法访问 docker.io 时必用) -若已提供预构建镜像 tar: +**典型失败日志:** + +```text +failed to fetch anonymous token: Get "https://auth.docker.io/token...": i/o timeout +``` + +说明服务器访问不了 Docker Hub,**不能在线 build**,必须用离线 tar。 + +**步骤 1:在有公网的机器上导出镜像** ```bash +bash scripts/export-offline-image.sh +# 产出: deerflow-backend-offline-image-20260505.tar +``` + +**步骤 2:上传到内网服务器部署目录** + +**步骤 3:内网服务器加载并启动** + +```bash +DEPLOY_MODE=offline \ IMAGE_TAR=./deerflow-backend-offline-image-20260505.tar \ IMAGE_TAG=deerflow-backend-offline:20260505 \ -DEPLOY_MODE=offline \ bash deploy.sh up ``` +若 tar 已在目录中,`deploy.sh` 会**自动检测**并切换为离线模式。 + 或分步: ```bash @@ -202,6 +221,25 @@ bash deploy.sh load IMAGE_TAG=deerflow-backend-offline:20260505 bash deploy.sh up ``` +### 5.3 配置 Docker 镜像加速(可选,有内网镜像源时) + +编辑 `/etc/docker/daemon.json`: + +```json +{ + "registry-mirrors": [ + "https://你的内网或国内镜像地址" + ], + "dns": ["114.114.114.114", "223.5.5.5"] +} +``` + +```bash +sudo systemctl daemon-reload +sudo systemctl restart docker +bash deploy.sh build +``` + --- ## 6. 前端对接 @@ -268,6 +306,73 @@ bash deploy.sh restart ## 10. 故障排查 +### 10.0 Docker 未运行或无权限 + +报错示例: + +```text +[FAIL] Docker 未运行或无权限... +``` + +**第一步:看具体原因** + +```bash +docker info +``` + +**情况 A:permission denied(最常见)** + +说明当前用户不在 `docker` 组: + +```bash +# 把当前用户加入 docker 组 +sudo usermod -aG docker "$USER" + +# 让组权限立即生效(或重新 SSH 登录) +newgrp docker + +# 验证 +docker info +docker ps + +# 再部署 +bash deploy.sh +``` + +若仍不行,确认 socket 权限: + +```bash +ls -l /var/run/docker.sock +# 期望类似: srw-rw---- root docker ... +``` + +**情况 B:Cannot connect to the Docker daemon** + +说明 Docker 服务没起来: + +```bash +sudo systemctl status docker +sudo systemctl start docker +sudo systemctl enable docker +docker info +bash deploy.sh +``` + +**情况 C:未安装 Docker** + +```bash +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker "$USER" +newgrp docker +bash deploy.sh +``` + +**临时绕过(仅测试用,不推荐生产)** + +```bash +sudo bash deploy.sh +``` + ### 10.1 容器启动后立即退出 ```bash @@ -281,6 +386,37 @@ bash deploy.sh logs - MySQL 连接失败(检查 `MYSQL_DATABASE_URL` 是否用了容器可达的 IP) - 缺少 `asyncmy`(MySQL 模式):确认 `backend/wheelhouse` 中有对应 wheel,或配置 Python 私服 +### 10.1.1 脚本报 `$'\r': command not found` 或 `pipefail: invalid option` + +这是 **Windows 换行符(CRLF)** 导致的。在 Linux 上执行: + +```bash +sed -i 's/\r$//' deploy.sh start_backend.sh stop_backend.sh scripts/container-entrypoint.sh switch_database_to_mysql.sh +chmod +x deploy.sh start_backend.sh stop_backend.sh scripts/container-entrypoint.sh +bash deploy.sh +``` + +或在 Windows 上传前,用 VS Code 右下角把文件编码改为 **LF**。 + +### 10.1.2 构建时报 `.venv cannot be used because it is not a valid Python environment` + +原因:开发机(尤其 Windows)上的 `backend/.venv` 被打包进 Docker 构建上下文,Linux 容器内无法使用。 + +解决:仓库已添加 `.dockerignore` 排除 `.venv`。更新代码后重新构建: + +```bash +bash deploy.sh build +# 或 +bash deploy.sh up +``` + +若仍报错,手动删除后再构建: + +```bash +rm -rf backend/.venv +bash deploy.sh build +``` + ### 10.2 健康检查失败 ```bash diff --git a/offline-backend-20260512/backend/Dockerfile b/offline-backend-20260512/backend/Dockerfile index 94cfbe5f..506b9d62 100644 --- a/offline-backend-20260512/backend/Dockerfile +++ b/offline-backend-20260512/backend/Dockerfile @@ -47,8 +47,9 @@ COPY backend ./backend # Install dependencies with cache mount # When UV_EXTRAS is set (e.g. "postgres"), installs optional dependencies. +# Remove host .venv if present — Windows/macOS venvs break Linux container builds. RUN --mount=type=cache,target=/root/.cache/uv \ - sh -c "cd backend && UV_INDEX_URL=${UV_INDEX_URL:-https://pypi.org/simple} uv sync ${UV_EXTRAS:+--extra $UV_EXTRAS}" + sh -c "cd backend && rm -rf .venv && UV_INDEX_URL=${UV_INDEX_URL:-https://pypi.org/simple} uv sync ${UV_EXTRAS:+--extra $UV_EXTRAS}" # ── Stage 2: Dev ────────────────────────────────────────────────────────────── # Retains compiler toolchain from builder so startup-time `uv sync` can build diff --git a/offline-backend-20260512/deploy.sh b/offline-backend-20260512/deploy.sh index 49b2d470..be0dd967 100644 --- a/offline-backend-20260512/deploy.sh +++ b/offline-backend-20260512/deploy.sh @@ -37,6 +37,32 @@ IMAGE_TAR="${IMAGE_TAR:-${SCRIPT_DIR}/deerflow-backend-offline-image.tar}" DEPLOY_MODE="${DEPLOY_MODE:-build}" SKIP_BUILD="${SKIP_BUILD:-0}" +resolve_offline_tar() { + if [ -f "${IMAGE_TAR}" ]; then + return 0 + fi + local candidate + for candidate in \ + "${SCRIPT_DIR}/deerflow-backend-offline-image-20260505.tar" \ + "${SCRIPT_DIR}/deerflow-backend-offline-image.tar"; do + if [ -f "${candidate}" ]; then + IMAGE_TAR="${candidate}" + return 0 + fi + done + return 1 +} + +# 内网服务器通常无法访问 docker.io,检测到离线 tar 时自动切换模式 +resolve_offline_tar || true +if [ -f "${IMAGE_TAR}" ] && [ "${DEPLOY_MODE}" = "build" ]; then + DEPLOY_MODE="offline" + if [ "${IMAGE_TAG}" = "deerflow-backend:latest" ]; then + IMAGE_TAG="deerflow-backend-offline:20260505" + fi + warn "检测到离线镜像 ${IMAGE_TAR},已自动切换为 DEPLOY_MODE=offline" +fi + GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' @@ -92,7 +118,28 @@ ensure_env() { check_docker() { require_cmd docker - docker info >/dev/null 2>&1 || fail "Docker 未运行或无权限,请确认 docker 服务已启动且当前用户在 docker 组" + + local docker_err + docker_err="$(docker info 2>&1)" || { + echo "${docker_err}" >&2 + echo >&2 + if echo "${docker_err}" | grep -qiE 'permission denied|connect: permission'; then + fail "当前用户无权访问 Docker。请执行: + sudo usermod -aG docker \$USER + newgrp docker +然后重新运行: bash deploy.sh + +临时方案(不推荐长期使用): sudo bash deploy.sh" + fi + if echo "${docker_err}" | grep -qiE 'cannot connect|Is the docker daemon running|connection refused'; then + fail "Docker 服务未运行。请先启动: + sudo systemctl start docker + sudo systemctl enable docker +然后重新运行: bash deploy.sh" + fi + fail "Docker 不可用。请先在服务器上执行 docker info 查看详细错误" + } + docker compose version >/dev/null 2>&1 || fail "未找到 docker compose,请安装 Docker Compose V2" } @@ -122,16 +169,33 @@ load_offline_image() { ok "镜像已存在: ${IMAGE_TAG}" return 0 fi - [ -f "${IMAGE_TAR}" ] || fail "离线镜像不存在: ${IMAGE_TAR},请设置 IMAGE_TAR 或先执行 bash deploy.sh build" + [ -f "${IMAGE_TAR}" ] || fail "离线镜像不存在: ${IMAGE_TAR} + +内网服务器无法访问 docker.io 时,请: + 1. 在有公网的机器上执行 bash scripts/export-offline-image.sh 导出 tar + 2. 上传到本目录后执行: DEPLOY_MODE=offline bash deploy.sh up" info "加载离线镜像: ${IMAGE_TAR}" - docker load -i "${IMAGE_TAR}" + local load_out loaded_tag + load_out="$(docker load -i "${IMAGE_TAR}")" + echo "${load_out}" + loaded_tag="$(echo "${load_out}" | sed -n 's/^Loaded image: //p' | tail -n1)" + if [ -n "${loaded_tag}" ] && ! docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1; then + info "将 ${loaded_tag} 标记为 ${IMAGE_TAG}" + docker tag "${loaded_tag}" "${IMAGE_TAG}" + fi + docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1 || fail "镜像加载后未找到 ${IMAGE_TAG},请检查 IMAGE_TAG 是否与 tar 内标签一致" ok "镜像加载完成: ${IMAGE_TAG}" } build_image() { - info "构建 Docker 镜像: ${IMAGE_TAG}" + info "构建 Docker 镜像: ${IMAGE_TAG}(需要能访问 docker.io / ghcr.io)" export IMAGE_TAG - "${COMPOSE[@]}" build gateway + if ! "${COMPOSE[@]}" build gateway; then + fail "在线构建失败。内网服务器请改用离线镜像: + DEPLOY_MODE=offline IMAGE_TAR=./deerflow-backend-offline-image-20260505.tar bash deploy.sh up + +或在 /etc/docker/daemon.json 配置 registry-mirrors 后重试。" + fi ok "镜像构建完成" } @@ -184,7 +248,11 @@ cmd_up() { fi info "启动容器..." - "${COMPOSE[@]}" up -d --remove-orphans + if [ "${DEPLOY_MODE}" = "offline" ] || docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1; then + "${COMPOSE[@]}" up -d --no-build --remove-orphans + else + "${COMPOSE[@]}" up -d --remove-orphans + fi ok "容器已启动: ${CONTAINER_NAME}" echo echo " API 地址: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 127.0.0.1):${HOST_PORT}" diff --git a/offline-backend-20260512/scripts/export-offline-image.sh b/offline-backend-20260512/scripts/export-offline-image.sh new file mode 100644 index 00000000..d9af23d1 --- /dev/null +++ b/offline-backend-20260512/scripts/export-offline-image.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# 在有公网的环境构建并导出离线镜像 tar,供内网服务器 docker load 使用。 +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "${SCRIPT_DIR}" + +IMAGE_TAG="${IMAGE_TAG:-deerflow-backend-offline:20260505}" +OUTPUT="${OUTPUT:-${SCRIPT_DIR}/deerflow-backend-offline-image-20260505.tar}" + +echo "构建镜像: ${IMAGE_TAG}" +IMAGE_TAG="${IMAGE_TAG}" docker compose -p deerflow-backend -f docker-compose.yaml build gateway + +loaded="$(docker images --format '{{.Repository}}:{{.Tag}}' | grep -F "${IMAGE_TAG}" | head -n1 || true)" +if [ -z "${loaded}" ]; then + # compose build 可能产出 deerflow-backend:latest,再打离线标签 + docker tag deerflow-backend:latest "${IMAGE_TAG}" 2>/dev/null || true +fi + +echo "导出到: ${OUTPUT}" +docker save -o "${OUTPUT}" "${IMAGE_TAG}" +ls -lh "${OUTPUT}" +echo +echo "上传到内网服务器后执行:" +echo " DEPLOY_MODE=offline IMAGE_TAR=${OUTPUT##*/} IMAGE_TAG=${IMAGE_TAG} bash deploy.sh up" From 0c5938a5a396996e3586a0def515e782c47f54f7 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Mon, 1 Jun 2026 19:54:45 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-web/src/App.tsx | 2 + frontend-web/src/components/font-scale.tsx | 39 ++ .../settings/appearance-settings-page.tsx | 80 ++- frontend-web/src/core/settings/local.ts | 86 +-- .../backend/app/gateway/app.py | 16 + .../app/gateway/routers/_roundtable_seed.py | 623 ++---------------- .../roundtable-coordinator/SOUL.md | 42 ++ .../roundtable-coordinator/config.yaml | 5 + .../roundtable-intent/SOUL.md | 312 +++++++++ .../roundtable-intent/config.yaml | 3 + .../roundtable-recommender/SOUL.md | 191 ++++++ .../roundtable-recommender/config.yaml | 3 + 12 files changed, 784 insertions(+), 618 deletions(-) create mode 100644 frontend-web/src/components/font-scale.tsx create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-coordinator/SOUL.md create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-coordinator/config.yaml create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-intent/SOUL.md create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-intent/config.yaml create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-recommender/SOUL.md create mode 100644 offline-backend-20260512/backend/app/gateway/routers/_roundtable_seed_assets/roundtable-recommender/config.yaml diff --git a/frontend-web/src/App.tsx b/frontend-web/src/App.tsx index ea03c986..5649c323 100644 --- a/frontend-web/src/App.tsx +++ b/frontend-web/src/App.tsx @@ -2,6 +2,7 @@ import { useMemo } from "react"; import { Navigate, Route, Routes } from "react-router-dom"; import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { FontScaleApplier } from "@/components/font-scale"; import { QueryClientProvider } from "@/components/query-client-provider"; import { AuthSyncWatcher } from "@/core/auth/sync"; import { I18nProvider } from "@/core/i18n/context"; @@ -39,6 +40,7 @@ function AppProviders({ children }: { children: React.ReactNode }) { + {children} diff --git a/frontend-web/src/components/font-scale.tsx b/frontend-web/src/components/font-scale.tsx new file mode 100644 index 00000000..7c051700 --- /dev/null +++ b/frontend-web/src/components/font-scale.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { useEffect } from "react"; + +import { useLocalSettings } from "@/core/settings"; + +/** + * 基准字号 = 16px,对应《深蓝色 UI 规范》§2.1.2 后台系统正文字号(16px Regular)。 + * 用户在「外观 → 字号」里选择的倍数作用于 根元素的 font-size;由于 Tailwind + * 的字号 / 间距基于 rem,整套界面会随之按比例放大 / 缩小。`fontScale === 1` 即默认 16px。 + */ +export const BASE_FONT_PX = 16; + +export const FONT_SCALE_OPTIONS: { value: number; label: string; px: number }[] = + [ + { value: 0.875, label: "小", px: 14 }, + { value: 1, label: "标准", px: 16 }, + { value: 1.125, label: "大", px: 18 }, + { value: 1.25, label: "特大", px: 20 }, + ]; + +/** + * 将用户选择的字号倍数应用到 根元素。挂在应用根部,保证刷新后即时生效、全局统一。 + * 不渲染任何 DOM。 + */ +export function FontScaleApplier() { + const [settings] = useLocalSettings(); + const { fontScale } = settings.display; + + useEffect(() => { + const scale = + typeof fontScale === "number" && Number.isFinite(fontScale) && fontScale > 0 + ? fontScale + : 1; + document.documentElement.style.fontSize = `${BASE_FONT_PX * scale}px`; + }, [fontScale]); + + return null; +} diff --git a/frontend-web/src/components/workspace/settings/appearance-settings-page.tsx b/frontend-web/src/components/workspace/settings/appearance-settings-page.tsx index 5970d17e..5720df7c 100644 --- a/frontend-web/src/components/workspace/settings/appearance-settings-page.tsx +++ b/frontend-web/src/components/workspace/settings/appearance-settings-page.tsx @@ -6,6 +6,7 @@ import { toast } from "sonner"; import { useTheme } from "@/contexts/ThemeContext"; +import { FONT_SCALE_OPTIONS } from "@/components/font-scale"; import { Button } from "@/components/ui/button"; import { Select, @@ -21,12 +22,12 @@ import { getStoredAuth } from "@/core/auth"; import { enUS, isLocale, zhCN, type Locale } from "@/core/i18n"; import { useI18n } from "@/core/i18n/hooks"; import { useLocalSettings } from "@/core/settings"; -import { - useAppearanceSettings, - useCitationDisplaySettings, - useUpdateAppearanceSettings, - useUpdateCitationDisplaySettings, -} from "@/core/system-settings/hooks"; +import { + useAppearanceSettings, + useCitationDisplaySettings, + useUpdateAppearanceSettings, + useUpdateCitationDisplaySettings, +} from "@/core/system-settings/hooks"; import { cn } from "@/lib/utils"; import { SettingsSection } from "./settings-section"; @@ -42,13 +43,13 @@ export function AppearanceSettingsPage() { const currentTheme = theme; const [settings, setSettings] = useLocalSettings(); const isAdmin = getStoredAuth()?.system_role === "admin"; - const { data: appearance } = useAppearanceSettings(); - const updateAppearance = useUpdateAppearanceSettings(); - const { data: citationDisplay } = useCitationDisplaySettings(); - const updateCitationDisplay = useUpdateCitationDisplaySettings(); - const globalDefault = appearance?.default_theme ?? "light"; - const cleanupWords = - citationDisplay?.cleanup_words ?? settings.display.citationCleanupWords ?? []; + const { data: appearance } = useAppearanceSettings(); + const updateAppearance = useUpdateAppearanceSettings(); + const { data: citationDisplay } = useCitationDisplaySettings(); + const updateCitationDisplay = useUpdateCitationDisplaySettings(); + const globalDefault = appearance?.default_theme ?? "light"; + const cleanupWords = + citationDisplay?.cleanup_words ?? settings.display.citationCleanupWords ?? []; const handleSetGlobalDefault = async (value: "light" | "dark") => { if (value === globalDefault) return; @@ -147,6 +148,39 @@ export function AppearanceSettingsPage() { + +
+ +
+ 预览:多智能体圆桌研讨 · The quick brown fox · 0123456789 +
+
+
+ + + -