From 81ac0ca5c2bea3bc3b5643189df78203357dcc54 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 30 Sep 2026 11:56:41 +0000 Subject: [PATCH] Seed model and search settings on service start Why: a freshly deployed environment boots with an empty settings store, so the first run dies with "main model unconfigured" and the user has to open the settings UI before anything works - the opposite of one-command bring-up. Evidence: live matrix - full seed (chat+lightweight+tavily) lands in the settings store (API view shows baseUrl/model with the key masked), restart without flags does not re-seed and the settings persist, .svc.env sourcing seeds a fresh environment from the one-time config file, and a no-flag start stays silent. Also wrapped a /proc read race in pid_alive that printed tr noise during stop. Impact: --model-base/--model-key/--model-name, --light-base/-key/ -name, --tavily-keys on start/deploy (idempotent, only provided fields); SVC_MODEL_*/SVC_LIGHT_*/SVC_TAVILY_KEYS env vars; / .svc.env one-time config file (gitignored) makes every future deploy self-configuring; auth-protected instances fall back to a clear hint. Refs: feature/svc-auto-seed task card. --- .gitignore | 1 + scripts/svc.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 09762bc5..0404acc5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ /data-test/ .zcode/plans/ runtime/ +.svc.env diff --git a/scripts/svc.sh b/scripts/svc.sh index ccad2e05..862a08cd 100755 --- a/scripts/svc.sh +++ b/scripts/svc.sh @@ -17,16 +17,24 @@ # --auth on|off|auto API key 认证(默认 auto:PG 档开、嵌入式关) # --mock 演示模式(dev 常用) # --plugins dir 插件目录(默认 /plugins) +# --model-base URL 启动后自动播种主模型配置(settings.models.chat.baseUrl) +# --model-key KEY 主模型 API Key(播种用;勿写入脚本/仓库) +# --model-name NAME 主模型名称 +# --light-base/--light-key/--light-name 轻量模型(标题生成等;可选) +# --tavily-keys K1,K2 网络搜索密钥池(播种 tools.tavily.apiKeys) # -- <剩余参数> 原样透传给 agent-system serve(escape hatch) # # 环境变量默认值: # dev: 127.0.0.1:8788 数据 runtime/dev/data;SVC_DEV_ADDR/DATA/MOCK=1 可覆盖 # prod: 127.0.0.1:8787 数据 runtime/prod/data;SVC_PROD_ADDR/DATA/DSN/AUTH 可覆盖 # +# 一次性配置:把 SVC_MODEL_BASE/SVC_MODEL_KEY/SVC_MODEL_NAME 等写入 /.svc.env +#(已 gitignore),此后每次 start/deploy 自动播种,服务起来即可用。 +# # 示例: # scripts/svc.sh dev --port 9000 --mock # scripts/svc.sh prod --host 0.0.0.0 --dsn postgres://u:p@127.0.0.1:5432/as -# scripts/svc.sh dev deploy --addr 0.0.0.0:8788 +# scripts/svc.sh dev --model-base https://api.xxx.com/v1 --model-key sk-xx --model-name glm-4.7 # make svc ENV=dev ARGS="--port 9000" # # 安全约束: @@ -38,6 +46,10 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" RUNTIME="$ROOT/runtime" BIN="$ROOT/bin/agent-system" +# 一次性配置文件(含密钥,已 gitignore):SVC_MODEL_* 等在此设好后每次启动自动生效。 +# shellcheck disable=SC1090 +[ -f "$ROOT/.svc.env" ] && . "$ROOT/.svc.env" + c_info() { printf '\033[1;34m[svc]\033[0m %s\n' "$*"; } c_ok() { printf '\033[1;32m[svc]\033[0m %s\n' "$*"; } c_warn() { printf '\033[1;33m[svc]\033[0m %s\n' "$*"; } @@ -50,6 +62,8 @@ usage() { # ---- 命令行参数(覆盖层)---- CLI_ADDR=""; CLI_HOST=""; CLI_PORT=""; CLI_DATA=""; CLI_DSN="" CLI_AUTH=""; CLI_MOCK=0; CLI_PLUGINS=""; CLI_EXTRA=""; CLI_LOGS_N="" +CLI_MBASE=""; CLI_MKEY=""; CLI_MNAME="" +CLI_LBASE=""; CLI_LKEY=""; CLI_LNAME=""; CLI_TAVILY="" need_value() { [ $# -ge 2 ] || { c_err "参数 $1 需要值"; exit 2; }; } @@ -64,6 +78,13 @@ parse_args() { --auth) need_value "$@"; CLI_AUTH="$2"; shift 2 ;; --plugins) need_value "$@"; CLI_PLUGINS="$2"; shift 2 ;; --mock) CLI_MOCK=1; shift ;; + --model-base) need_value "$@"; CLI_MBASE="$2"; shift 2 ;; + --model-key) need_value "$@"; CLI_MKEY="$2"; shift 2 ;; + --model-name) need_value "$@"; CLI_MNAME="$2"; shift 2 ;; + --light-base) need_value "$@"; CLI_LBASE="$2"; shift 2 ;; + --light-key) need_value "$@"; CLI_LKEY="$2"; shift 2 ;; + --light-name) need_value "$@"; CLI_LNAME="$2"; shift 2 ;; + --tavily-keys) need_value "$@"; CLI_TAVILY="$2"; shift 2 ;; --) shift; CLI_EXTRA="$*"; break ;; -h|--help) usage; exit 0 ;; *) c_err "未知参数:$1"; usage; exit 2 ;; @@ -117,7 +138,7 @@ pid_alive() { local pid="$1" [ -n "$pid" ] && [ -r "/proc/$pid/cmdline" ] || return 1 local cmd - cmd="$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)" || return 1 + cmd="$( { tr '\0' ' ' < "/proc/$pid/cmdline"; } 2>/dev/null )" || return 1 case "$cmd" in *agent-system*serve*) return 0 ;; *) return 1 ;; @@ -164,6 +185,48 @@ do_build() { fi } +# seed_settings:把 CLI/环境变量给出的模型与搜索配置写入 settings(幂等; +# 只写明确给出的字段;设置持久化在环境数据目录——播种一次后续启动无需再传)。 +seed_settings() { + local host="$1" port="$2" + local cb="${CLI_MBASE:-${SVC_MODEL_BASE:-}}" ck="${CLI_MKEY:-${SVC_MODEL_KEY:-}}" cn="${CLI_MNAME:-${SVC_MODEL_NAME:-}}" + local lb="${CLI_LBASE:-${SVC_LIGHT_BASE:-}}" lk="${CLI_LKEY:-${SVC_LIGHT_KEY:-}}" ln="${CLI_LNAME:-${SVC_LIGHT_NAME:-}}" + local tk="${CLI_TAVILY:-${SVC_TAVILY_KEYS:-}}" + [ -z "$cb$ck$cn$lb$lk$ln$tk" ] && return 0 + command -v python3 >/dev/null 2>&1 || { c_warn "[$ENV_NAME] 跳过配置播种:需要 python3 构造请求"; return 0; } + local payload + payload="$(python3 - "$cb" "$ck" "$cn" "$lb" "$lk" "$ln" "$tk" <<'PY' +import json, sys +cb, ck, cn, lb, lk, ln, tk = sys.argv[1:8] +out = {} +def sub(base, key, name): + d = {} + if base: d["baseUrl"] = base + if key: d["apiKey"] = key + if name: d["model"] = name + return d +chat, light = sub(cb, ck, cn), sub(lb, lk, ln) +models = {} +if chat: models["chat"] = chat +if light: models["lightweight"] = light +if models: out["models"] = models +if tk: out["tools"] = {"tavily": {"apiKeys": tk}} +print(json.dumps(out)) +PY +)" + local resp + resp="$(curl -s -m 5 -X PUT "http://$host:$port/api/settings/user" -H 'Content-Type: application/json' -d "$payload")" + if echo "$resp" | grep -q '"ok": *true'; then + local did="" + [ -n "$cb$ck$cn" ] && did="主模型" + [ -n "$lb$lk$ln" ] && did="${did:+$did、}轻量模型" + [ -n "$tk" ] && did="${did:+$did、}Tavily" + c_ok "[$ENV_NAME] 已播种设置:$did(持久化于 $ENV_DATA,后续启动无需再传)" + else + c_warn "[$ENV_NAME] 设置播种未成功:${resp:-<无响应>}(若开启了 API key 认证,需在设置页手动配置)" + fi +} + do_start() { if pid_alive "$(read_pid)"; then c_warn "[$ENV_NAME] 已在运行 pid=$(read_pid)(如需换参数/新版本请用 deploy/restart)" @@ -190,6 +253,7 @@ do_start() { for _ in $(seq 1 100); do if curl -s -o /dev/null -m 2 "http://$host:$port/" 2>/dev/null; then c_ok "[$ENV_NAME] 已启动 pid=$(cat "$ENV_PID") → http://$host:$port(日志:$ENV_LOG)" + seed_settings "$host" "$port" return 0 fi pid_alive "$(read_pid)" || { c_err "[$ENV_NAME] 进程启动后即退出,查看日志:tail -40 $ENV_LOG"; exit 1; }