diff --git a/deploy/ollama_restart.sh b/deploy/ollama_restart.sh new file mode 100755 index 0000000..911f783 --- /dev/null +++ b/deploy/ollama_restart.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# ollama_restart.sh —— 向量化本地模型服务(Ollama + bge-m3 嵌入)幂等拉起 +# 用途:部署树与安装包共用;start_all.sh 在启动各服务前调用。 +# 零硬编码:Ollama 二进制多候选探测(PATH → /usr/local/bin/ollama → ~/ollama/bin/ollama → $OLLAMA_BIN)。 +# 行为:已监听 11434 则直接校验模型并返回;未起才拉起(setsid 脱离,日志 /tmp/plantai-ollama.log)。 +# 用法: bash ollama_restart.sh [embed-model] +set -uo pipefail + +MODEL="${1:-${VSTORE_EMBED_MODEL:-bge-m3:567m}}" +PORT="${OLLAMA_PORT:-11434}" +OLLAMA_BIN="${OLLAMA_BIN:-}" + +find_ollama() { + for cand in "$OLLAMA_BIN" /usr/local/bin/ollama "$HOME/ollama/bin/ollama"; do + [ -n "$cand" ] && [ -x "$cand" ] && { echo "$cand"; return 0; } + done + command -v ollama 2>/dev/null || true +} + +OLLAMA_BIN="$(find_ollama)" +if [ -z "$OLLAMA_BIN" ]; then + echo "[ollama] 未找到 Ollama 二进制(/usr/local/bin/ollama 或 ~/ollama/bin/ollama),向量化将不可用" >&2 + echo "[ollama] 安装:https://ollama.com/download/linux,然后 ollama pull $MODEL" >&2 + exit 2 +fi + +if curl -s --max-time 2 "http://127.0.0.1:$PORT/api/tags" >/dev/null 2>&1; then + echo "[ollama] 已在运行($OLLAMA_BIN,端口 $PORT),跳过拉起" +else + echo "[ollama] 拉起本地模型服务($OLLAMA_BIN)" + setsid "$OLLAMA_BIN" serve > /tmp/plantai-ollama.log 2>&1 < /dev/null & + disown + for _ in $(seq 1 30); do + sleep 1 + curl -s --max-time 2 "http://127.0.0.1:$PORT/api/tags" >/dev/null 2>&1 && break + done +fi + +# 嵌入模型校验(向量化必需:bge-m3;缺失给出拉取命令,不阻断其他服务启动) +if curl -s --max-time 5 "http://127.0.0.1:$PORT/api/tags" | grep -q "\"name\":\"$MODEL\""; then + echo "[ollama] 嵌入模型 $MODEL 就绪" +else + echo "[ollama] 警告:嵌入模型 $MODEL 未安装,向量化/私有资料检索将降级" >&2 + echo "[ollama] 安装命令:$OLLAMA_BIN pull $MODEL" >&2 +fi diff --git a/tooling/build_all.sh b/tooling/build_all.sh index 304ee18..1fad21e 100755 --- a/tooling/build_all.sh +++ b/tooling/build_all.sh @@ -88,6 +88,15 @@ cp -r "$PROJECT_ROOT/ui/dist" "$RELEASE_ROOT/ui/dist" cp "$PROJECT_ROOT/agent/qa/scripts/"*.sh "$RELEASE_ROOT/qa/scripts/" 2>/dev/null || true cp "$PROJECT_ROOT/agent/methodology/scripts/"*.sh "$RELEASE_ROOT/methodology/scripts/" 2>/dev/null || true cp "$PROJECT_ROOT/agent/legacy-rag/scripts/"*.sh "$RELEASE_ROOT/legacy-rag/scripts/" 2>/dev/null || true + +# 向量化环节(本地模型):vstore 明文源码 + 启动脚本 + Ollama 拉起脚本 +cp -r "$PROJECT_ROOT/vstore" "$RELEASE_ROOT/vstore" +rm -rf "$RELEASE_ROOT/vstore/tests" "$RELEASE_ROOT/vstore/__pycache__" +cp "$PROJECT_ROOT/vstore/vstore_restart.sh" "$RELEASE_ROOT/vstore_restart.sh" +cp "$PROJECT_ROOT/deploy/ollama_restart.sh" "$RELEASE_ROOT/ollama_restart.sh" +# env 模板(VSTORE_ROOT/KEY/EMBED 等;真实值不入包) +cp "$PROJECT_ROOT/vstore/.env.example" "$RELEASE_ROOT/vstore/.env.example" 2>/dev/null || true + # env 模板 for t in qa methodology legacy-rag; do cp "$PROJECT_ROOT/agent/$t/env/prod.env.example" "$RELEASE_ROOT/$t/env/prod.env.example" 2>/dev/null || true @@ -104,6 +113,8 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BIND="${1:-0.0.0.0}" echo "== 启动 PlantAI-rag-new 四服务 ==" +# 向量化本地模型(bge-m3 嵌入):先拉起 Ollama(已运行则跳过),模型缺失只告警不阻断 +bash "$ROOT/ollama_restart.sh" 2>/dev/null || echo "[ollama] 拉起失败,向量化降级(不阻断其余服务)" bash "$ROOT/qa/scripts/start.sh" 8091 || true bash "$ROOT/methodology/scripts/start.sh" 8092 || true bash "$ROOT/legacy-rag/scripts/start.sh" 8093 app || true diff --git a/vstore/vstore_restart.sh b/vstore/vstore_restart.sh new file mode 100755 index 0000000..89fb630 --- /dev/null +++ b/vstore/vstore_restart.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# vstore_restart.sh —— 向量库服务(8094)幂等重启(部署树与安装包共用,路径从脚本位置推导) +# 用法: bash vstore_restart.sh [python] [port] +# 说明: vstore 是包(部署根/vstore 目录),uvicorn 必须从父目录 -m vstore.api:app, +# cd 进 vstore/ 会导致 ModuleNotFoundError(先例注释保留)。 +# 零硬编码:venv/python/数据根全部环境变量或脚本位置推导。 +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # .../vstore +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # 部署根 +PYTHON="${1:-${PLANTAI_PYTHON:-$ROOT/venv/bin/python}}" +PORT="${2:-${VSTORE_PORT:-8094}}" +HOST="${VSTORE_BIND_HOST:-127.0.0.1}" + +[ -x "$PYTHON" ] || PYTHON="$(command -v python3 2>/dev/null || true)" +[ -n "$PYTHON" ] && [ -x "$PYTHON" ] || { echo "[vstore] Python 不可执行,请指定: vstore_restart.sh " >&2; exit 2; } + +# 杀旧进程([v]store 正则技巧防 pkill 自匹配;端口优先精确) +if command -v lsof >/dev/null 2>&1; then + OLD_PID="$(lsof -ti tcp:"$PORT" 2>/dev/null || true)" + [ -n "$OLD_PID" ] && kill "$OLD_PID" 2>/dev/null || true +else + pkill -f "[v]store.api:app" 2>/dev/null || true +fi +sleep 2 + +cd "$ROOT" +set -a +[ -f "$SCRIPT_DIR/.env" ] && source "$SCRIPT_DIR/.env" +set +a +setsid "$PYTHON" -m uvicorn vstore.api:app --host "$HOST" --port "$PORT" \ + > /tmp/plantai-vstore.log 2>&1 < /dev/null & +disown + +sleep 8 +if curl -s --max-time 5 "http://127.0.0.1:$PORT/api/vstore/health" | grep -q '"ok"'; then + echo "VSTORE UP ($HOST:$PORT)" + curl -s --max-time 5 "http://127.0.0.1:$PORT/api/vstore/health" + echo +else + echo "VSTORE FAIL —— 日志尾部:" >&2 + tail -20 /tmp/plantai-vstore.log >&2 || true + exit 1 +fi