ZhiWu 0.1.0 的部署事实只有一句话:平台整体编排不在本仓,仓库交付的是四个可手工启动的进程 +
一套沙箱 Provisioner 的 Compose 清单 + 一份逐行核对过的环境变量表。
本页把这三件事写清楚,并把每个结论对到仓内文件或 docs/08–docs/13 的核对记录上。
没有"一键起全栈"。四个进程分别启动,五个数据服务自备。
docs/09-deployment.md 第 2 节。backend/.env:该文件未被 git 跟踪,仓内也没有 .env.example 模板,变量名与默认值以本页环境变量总表为准。JWT_SECRET_KEY、API_KEY_DERIVATION_SECRET、SANDBOX_PROVISIONER_TOKEN(lifespan 硬校验,backend/server/src/server/utils/auth_utils.py)。ZHIWU_USER_DATA_DIR、ZHIWU_SKILL_DATA_DIR、ZHIWU_SKILL_PROJECTION_DIR,否则启动直接 RuntimeError。uv(后端与 CLI)、Node + pnpm(前端)、Docker(沙箱 Provisioner)。cd backend
uv sync --all-groups
uv run python server/src/server/main.py
cd backend
uv run python server/src/server/worker_main.py
# 并发度:ARQ_MAX_JOBS=10(默认)
# 启动时先 reconcile 收敛遗留任务
cd docker/sandbox_provisioner
docker compose up -d # 或 ./script/startup.sh
curl -s localhost:8002/health
cd web
pnpm install
VITE_API_URL=http://127.0.0.1:5050 \
VITE_MINIO_URL=http://127.0.0.1:9000 \
pnpm dev
web/vite.config.js 用 loadEnv(mode, process.cwd(), '') 读环境,
代理 target 是 env.VITE_API_URL || 'http://api:5050' 与 env.VITE_MINIO_URL || 'http://minio:9000'——
两个默认值都是 Compose 网络内的服务名,宿主机直跑后端时解析不到。仓内 web/.env
只预设了 VITE_API_URL=http://127.0.0.1:5050,没有设 VITE_MINIO_URL,
所以 /minio/public/* 仍会去连 http://minio:9000 而失败,表现为头像与公开资源加载不出来;
宿主机直跑需自行补 VITE_MINIO_URL=http://127.0.0.1:9000。来源:docs/09-deployment.md 第 1 节、web/vite.config.js。全仓唯一的完整编排是 docker/sandbox_provisioner/ 下的两套 compose(docker backend 与 kubernetes backend)。
api / worker / web / storage-migrator 没有 Dockerfile、没有编排清单,因此不存在"一条命令起全栈"。
web/AGENTS.md 里的 docker compose exec web … 是失效指令,本地直接 cd web && pnpm …。
来源:docs/09-deployment.md「已确认结论」。
backend/scripts/seed_initial_users.py 不存在(该目录只有 gen_invoice_images.py)。
初始 superadmin 走公开端点 POST /api/auth/initialize(首次运行时创建管理员账号),
见 docs/06-api.md 鉴权清单与 docs/00-overview.md 的核对修正。
迁移代码在仓内:backend/zhiwu/src/zhiwu/storage_migration.py,入口
cd backend && uv run python -m zhiwu.storage_migration。
但没为它包镜像,部署方需自行写成一次性 Job(先于 api / worker 执行)。
应用进程本身只校验 schema 版本兼容、不建表不改表(pg_manager.require_current_schema(),
server/utils/lifespan.py)。来源:docs/09-deployment.md 第 2 节。
默认镜像 enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
属第三方,本仓只通过 SANDBOX_RUNTIME_PROFILE(默认 core)注入 DISABLE_* 开关裁剪能力;
沙箱内 HTTP 端口固定 8080。镜像内预装运行时清单无法从本仓确认。
来源:docker/sandbox_provisioner/app.py、docs/09-deployment.md「已确认结论」。
这是仓库内唯一一份完整编排。docker-compose.yml 用 docker backend,
docker-compose.k8s.yml 用 kubernetes backend(集成测试用),两者并存但争用宿主 8002。
| 配置项 | docker-compose.yml(docker backend) | docker-compose.k8s.yml(kubernetes backend) |
|---|---|---|
| 项目名 / 服务名 | zhiwu-sandbox-provisioner · 服务 provisioner |
zhiwu-sandbox-provisioner-k8s · 服务 provisioner |
| container_name | zhiwu-sandbox-provisioner |
zhiwu-sandbox-provisioner-k8s |
| build args | BASE_IMAGE ← PROVISIONER_BASE_IMAGE,默认 docker.m.daocloud.io/library/python:3.13-slim;PIP_INDEX_URL 默认阿里云源 |
同上两个 build args,默认值一致 |
| image | zhiwu-sandbox-provisioner:local |
zhiwu-sandbox-provisioner:local(同一镜像) |
| command | 镜像默认 uvicorn app:app --host 0.0.0.0 --port 8002 --env-file sandbox.env |
显式覆盖为不带 --env-file 的 uvicorn,配置全走 environment,避免 sandbox.env 里 PROVISIONER_BACKEND=docker 干扰 |
| 端口 | ${PORT:-8002}:8002 |
8002:8002(写死) |
| 公共环境变量 | SANDBOX_PROVISIONER_TOKEN(compose 内含默认值,生产必须覆盖,≥32 字符且与 backend/.env 一致)、PROVISIONER_PUBLIC_URL(返回给 backend 的代理基址,默认 http://127.0.0.1:8002) |
同样两项,PROVISIONER_PUBLIC_URL 写死 http://127.0.0.1:8002 |
| backend 专用变量 | 由镜像内 sandbox.env 提供(PROVISIONER_BACKEND=docker) |
PROVISIONER_BACKEND=kubernetes、KUBECONFIG_PATH=/app/kubeconfig/kubeconfig.k8s.yaml、K8S_NAMESPACE=zhiwu-know、NODE_HOST=k8s.orb.local、SANDBOX_IMAGE=all-in-one-sandbox:zhiwu-local、SANDBOX_CONTAINER_PORT=8080、SANDBOX_HEALTH_TIMEOUT_SECONDS=300 |
| 挂载 | /var/run/docker.sock:/var/run/docker.sock(高权限)、../../user-data:/app/user-data、../../skill-projections:/app/skill-projections、./sandbox.env:/app/sandbox.env:ro |
./kubeconfig.k8s.yaml:/app/kubeconfig/kubeconfig.k8s.yaml:ro、./sandbox.env:/app/sandbox.env:ro(不挂 docker.sock) |
| extra_hosts | host.docker.internal:host-gateway(保证 Linux 部署同样可用) |
同左 |
| restart | unless-stopped |
unless-stopped |
来源:docker/sandbox_provisioner/docker-compose.yml、docker/sandbox_provisioner/docker-compose.k8s.yml、docker/sandbox_provisioner/Dockerfile。
docker compose -f docker-compose.yml down 再起 k8s 版,反之亦然。
backend/.env 的 SANDBOX_PROVISIONER_URL 始终指向宿主 8002,因此"起的是哪一套"决定了 backend 走 docker 还是 kubernetes。
k8s 版还需要本地 kubeconfig.k8s.yaml——它被 docker/sandbox_provisioner/.gitignore 显式排除(含客户端证书/私钥),新环境要自行生成;
基础设施模板在 docker/sandbox_provisioner/test/k8s-infra.yaml(Namespace + 两个静态绑定 PV/PVC)。/app/user-data 与 /app/skill-projections 不是给 provisioner 自己写的:它的 _resolve_host_paths 会按这两个容器内路径反查宿主 bind 源,再把宿主真实路径转挂给沙箱容器的 /home/gem/user-data、/home/gem/skills。反查失败就会表现为"沙箱与工作区文件不同步"。./sandbox.env 以只读挂载,既是 uvicorn --env-file 的配置来源,也是 provisioner load_sandbox_env 注入沙箱容器环境变量的来源——所以即使 k8s 版去掉了 --env-file,仍保留这份只读挂载。/var/run/docker.sock 等价于宿主 root 权限,仅限受控主机;需要更强隔离时选 kubernetes backend(见 docs/11-security.md 第 7 节)。封装 docker compose up -d --build,随后做 60 × 0.5s 的健康探测,并给宿主目录 chmod。日常起停用这个。
对应的停止入口(封装 docker compose down),避免遗留 provisioner 容器占住 8002。
在宿主机直接跑 provisioner(不经容器),用于调试;script/run/ 是运行产物目录(当前只有 provisioner.log,未被 git 跟踪),不是镜像构建输入。
应用侧可以水平扩,数据侧一件都不在仓库里。
| 数据服务 | 用途 | 仓库内是否含编排 | 关键变量 |
|---|---|---|---|
| PostgreSQL | 业务表 + LangGraph checkpoint(同一 DSN 共用) | 否 | POSTGRES_URL(无默认) |
| Redis | ARQ 队列、Run 事件 Stream、配置缓存、登录限速、Worker 健康 key | 否 | REDIS_URL,默认 redis://redis:6379/0 |
| MinIO | 文档 / 图片 / 公开附件(桶 knowledgebases、kb-images、public) | 否 | MINIO_URI / MINIO_ACCESS_KEY / MINIO_SECRET_KEY / MINIO_PUBLIC_URL |
| Milvus | 每 KB 一个 collection:向量 + BM25 稀疏,支持 hybrid 与图向量库 | 否 | MILVUS_URI / MILVUS_TOKEN / MILVUS_DB |
| Neo4j | 知识图谱抽取与召回 | 否 | NEO4J_URI / NEO4J_USERNAME / NEO4J_PASSWORD |
session 不存服务端,JWT 自包含(HS256,claims 只有 sub/exp/iss/aud),任意副本可服务任意请求。
来源:docs/09-deployment.md 第 4 节、utils/auth_utils.py。
租约 CAS 抢占 + 部分唯一索引 uq_agent_runs_one_active_per_thread 让同一
(uid, agent_slug, thread) 只能有一个非终态 Run;单并发度看 ARQ_MAX_JOBS(默认 10),
实例启动先跑 reconcile 收敛遗留任务。来源:services/run_worker.py、models_business.py。
provisioner 本身是单点 HTTP 服务(:8002),三种 backend 分支在 app.py;
沙箱容器按 runtime scope 一个,扩缩由 backend 决定而不是应用决定。
没有资源配额、没有 HPA/Ingress/PDB、没有应用侧 Deployment(k8s/ 是空目录)。
官方推荐规格与备份策略需外部确认——这一点在 docs/09-deployment.md 里明确列为待确认项。
knowledgebases / kb-images、宿主目录 user-data/、skill-sources/、skill-projections/、Milvus 与 Neo4j 数据。skill-projections/ 与内置 Skill 可凭 DB + 源目录重建(投影 fail-closed 设计支持重同步);Milvus 索引可凭 PG 表 knowledge_chunks 走 re-index 端点重建。script/ 是空目录且未被 git 跟踪,全仓无 *backup* 文件、无 cron 定义、
无快照或对象生命周期配置。备份与恢复属运维 P0 补齐项,需外部确认频率与保留期。来源:docs/09-deployment.md「已确认结论」。五个对象、四种机制。就绪探针与存活探针是分开的两个端点。
| 对象 | 端点 / 机制 | 默认参数 | 来源 |
|---|---|---|---|
| API 进程 | GET /api/system/health(存活)、GET /api/system/ready(就绪,聚合依赖探测) |
就绪探测超时 READINESS_PROBE_TIMEOUT_SECONDS=2、缓存 READINESS_CACHE_TTL_SECONDS=1 |
routers/system_router.py、services/readiness_service.py |
| Sandbox Provisioner | GET :8002/health |
—(script/startup.sh 起后按 60 × 0.5s 探测) |
docker/sandbox_provisioner/app.py |
| 沙箱容器 | provisioner 创建容器后轮询沙箱 :8080 健康接口 |
SANDBOX_HEALTH_TIMEOUT_SECONDS 默认 60(k8s 版 compose 覆盖为 300) |
docker/sandbox_provisioner/app.py、docker-compose.k8s.yml |
| ARQ Worker | 周期性向 Redis 写 health check key,外部读 key 判活 | WORKER_HEALTH_INTERVAL_SECONDS=5(health_check_interval / health_check_key) |
services/run_worker.py、services/run_queue_service.py |
| OCR 引擎 | GET /api/system/ocr/health |
权限 get_required_user(登录即可,非 admin) |
routers/system_router.py |
补充:MCP 连通性用 POST /api/system/mcp-servers/{slug}/test 主动验证,不属于常态探活。
来源:docs/09-deployment.md 第 3 节、docs/10-observability.md 第 3 节。
按域拆开。是否必填以代码校验逻辑为准,默认值逐个取自
os.getenv(NAME, default) 第二参数 / get_int_env / or 兜底分支,
来源 docs/08-configuration.md 第 2 节(已逐行核对)。
注意:环境变量名与 system_options 字段完全解耦——SILICONFLOW_API_KEY、MILVUS_* 不会被系统配置项回退读取。
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
ZHIWU_ENV | 运行环境(development / production);影响 CORS 默认值、reload 行为与密钥兜底策略。仅 {prod, production} 视为生产 | 否 | development |
ZHIWU_INSTANCE_ID | 实例标识,构成 JWT issuer zhiwu-know:{instance_id};变更后旧 token 因 issuer 不匹配全部失效 | 生产必填 | 开发缺失时自动生成 instance-{hex16} 并只写回进程环境(不落盘);生产缺失直接启动失败 |
ZHIWU_CODE_REVISION | 代码版本标识,用于 /api/system/info 与 Run manifest 展示 | 否 | 空串 |
ZHIWU_CORS_ORIGINS | CORS 白名单,逗号分隔;含 * 时关闭 credentials | 生产必填 | production 默认空;development 默认 http://localhost:5173、http://127.0.0.1:5173 |
HOST_IP / RUNNING_IN_DOCKER | 容器 / 宿主网络感知:设置了 RUNNING_IN_DOCKER 时,把 MinIO endpoint 的主机名改写为 HOST_IP | 否 | HOST_IP 缺省按 localhost 处理 |
ZHIWU_BRAND_FILE_PATH | 品牌定制文件路径;相对路径会锚定包内 config/static/ | 否 | 未配置时依次回退 info.local.yaml → info.template.yaml |
| 变量 | 用途 | 必填 | 默认值 / 校验 |
|---|---|---|---|
ZHIWU_USER_DATA_DIR | 用户工作区持久根(宿主卷,同时被 provisioner 反查转挂给沙箱) | 是 | 兜底相对路径 user-data,但 require_absolute_storage_roots() 对空值/相对值直接抛 RuntimeError |
ZHIWU_SKILL_DATA_DIR | Skill 共享源根(内置 Skill 同步落点) | 是 | 兜底 skill-sources,同样受绝对路径校验 |
ZHIWU_SKILL_PROJECTION_DIR | 按 uid 的 Skill 只读投影根 | 是 | 兜底 skill-projections,同样受绝对路径校验 |
ZHIWU_RUNTIME_DIR | 运行期临时目录(日志文件写在这里的 logs/ 下) | 否 | {tempdir}/zhiwu-runtime-{pid}——不配就每进程一个目录、重启即换,多实例不共享 |
ZHIWU_RUNTIME_DIR 没配时 /api/system/logs 基本读不到有意义的日志。
来源:zhiwu/config/__init__.py、server/utils/lifespan.py。| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
POSTGRES_URL | PG DSN,业务库与 LangGraph checkpoint 共用(环境变量名由 KB_DATABASE_URL_ENV 指定) | 是 | 无默认,缺失即报错(sandbox provider 侧读取时兜底空串,但不改变"必须配"的事实) |
POSTGRES_POOL_SIZE / POSTGRES_MAX_OVERFLOW / POSTGRES_POOL_TIMEOUT_SECONDS | 业务库 SQLAlchemy 异步引擎连接池 | 否 | 10 / 20 / 30s(非法值由 get_int_env 直接 raise) |
LANGGRAPH_POSTGRES_POOL_SIZE / LANGGRAPH_POSTGRES_POOL_TIMEOUT_SECONDS | checkpoint 库独立连接池 | 否 | 10 / 30s |
REDIS_URL / REDIS_MAX_CONNECTIONS / REDIS_SOCKET_TIMEOUT / REDIS_CONNECT_TIMEOUT | Redis:队列、事件 Stream、缓存、限速、健康 key | 是 | redis://redis:6379/0;连接上限 32;两个 timeout 未配置时传 None(走 redis-py 内置值) |
MILVUS_URI / MILVUS_TOKEN / MILVUS_DB | Milvus(KB collection 与图向量库同值) | KB 功能必填 | http://localhost:19530 / "" / zhiwu |
MINIO_URI / MINIO_ACCESS_KEY / MINIO_SECRET_KEY / MINIO_PUBLIC_URL | MinIO 对象存储与公开访问前缀 | 是 | http://minio:9000 / minioadmin / minioadmin / /minio——后两项是硬编码弱默认凭证 |
NEO4J_URI / NEO4J_USERNAME / NEO4J_PASSWORD | Neo4j 图谱 | 图谱功能必填 | bolt://localhost:7687 / neo4j / 0123456789——密码是硬编码弱默认 |
| 变量 | 用途 | 必填 | 默认值 / 校验 |
|---|---|---|---|
JWT_SECRET_KEY | JWT HS256 签名密钥 | 生产必填 | 生产缺失或等于公开默认值 zhiwu_know_secure_key → 启动失败;开发缺失自动生成临时 token_hex(32)(重启即变,旧 token 全失效) |
API_KEY_DERIVATION_SECRET | API Key 的 HMAC 派生密钥(支持可重放派生同 key) | 是(所有环境) | 无兜底:缺失时 _validate_configured_security_secrets 抛 ValueError |
SANDBOX_PROVISIONER_TOKEN | 与 Provisioner 通信的 Bearer token;compose 侧与 backend/.env 两侧必须一致 | 是(所有环境) | lifespan 与 provider.py 各校验一次:≥ 32 字符、无首尾空白、三项彼此不相等(hmac.compare_digest 交叉检查) |
OIDC_ENABLED | SSO 总开关;为 false 时直接返回 enabled=False,不读其余任何 OIDC 变量 | 否 | false |
OIDC_ISSUER_URL / OIDC_CLIENT_ID / OIDC_CLIENT_SECRET / OIDC_AUTHORIZATION_ENDPOINT / OIDC_TOKEN_ENDPOINT / OIDC_USERINFO_ENDPOINT / OIDC_END_SESSION_ENDPOINT / OIDC_REDIRECT_URI | authorization code 流程的 issuer / 客户端凭据 / 各端点(开关之外 20 个变量的发现与凭据子集) | 启用 SSO 时必填 | 默认空串 |
OIDC_PROVIDER_NAME / OIDC_SCOPES / OIDC_DEFAULT_ROLE / OIDC_DEFAULT_DEPARTMENT | 登录按钮显示名、请求 scope、新用户默认角色与默认部门 | 否 | OIDC登录 / openid profile email / user / OIDC用户 |
OIDC_EMAIL_CLAIM / OIDC_NAME_CLAIM / OIDC_USERNAME_CLAIM / OIDC_DEPARTMENT_CLAIM | 从 userinfo 取字段的 claim 名 | 否 | email / name / preferred_username / department |
OIDC_AUTO_CREATE_USER / OIDC_USE_RAW_USERNAME / OIDC_FETCH_DEPARTMENT_INFO / OIDC_FORCE_PROMPT_LOGIN | 布尔开关:自动建号 / 用原始用户名 / 拉部门信息 / 每次强制跳登录页 | 否 | true / false / false / true |
| 变量 | 用途 | 必填 | 默认值 / 行为 |
|---|---|---|---|
SILICONFLOW_API_KEY | SiliconFlow(内置默认供应商)的对话 / embed / rerank,同时是 DeepSeek-OCR 解析器的默认密钥源 | 用内置供应商时必填 | 无默认 |
WEB_SEARCH_PROVIDER | 联网搜索 provider 选择 | 否 | 只接受 doubao / tavily,非白名单值记 warning 后忽略;未设置时按字典序自动探测第一个已配置密钥的 provider |
TAVILY_API_KEY / DOUBAO_SEARCH_API_KEY | 对应 provider 的密钥 | 用搜索时必填 | 无默认;无 provider 且无密钥 → 不注册搜索工具 |
NOTION_API_KEY / NOTION_TOKEN | Notion 知识库连接器凭证 | 用 Notion KB 时必填 | NOTION_TOKEN 优先,回退 NOTION_API_KEY;两者都缺且未传凭证 → 拒连 |
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
RAPIDOCR_MODEL_DIR | RapidOCR 模型目录(默认 OCR 引擎) | 否 | 未设置时由 RapidOCR 使用包内模型 |
PADDLEX_URI | PP-StructureV3 自托管服务地址 | 用该引擎时必填 | http://localhost:8080(注意与沙箱 8080 端口概念无关) |
PADDLEOCR_API_URL / PADDLEOCR_API_TOKEN | PaddleOCR 云服务(PaddleOCR-VL 与 PP-OCRv6 共用) | 用该引擎时必填 | URL 回退模块常量 DEFAULT_PADDLEOCR_API_URL;token 无默认 |
MINERU_API_URI / MINERU_API_KEY / MINERU_TIMEOUT | MinerU 解析服务(自托管 / 官方云)与超时 | 用该引擎时必填 | http://localhost:30001;无默认 key;超时 1800s;官方云缺 MINERU_API_KEY 直接抛 DocumentParserException |
OFFICE_PREVIEW_TIMEOUT_SECONDS | Office 文档预览转换超时 | 否 | 60 |
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
SANDBOX_PROVIDER | 沙箱提供者选择 | 否 | provisioner——只支持这一个值,其余值报错 |
SANDBOX_PROVISIONER_URL | Provisioner 基址 | 是(宿主直跑需覆盖) | http://sandbox-provisioner:8002(容器服务名;本地 compose 部署可用 http://127.0.0.1:8002) |
SANDBOX_PROVISIONER_CREATE_TIMEOUT_SECONDS / SANDBOX_PROVISIONER_CREATE_ATTEMPTS / SANDBOX_PROVISIONER_DELETE_TIMEOUT_SECONDS / SANDBOX_PROVISIONER_DELETE_ATTEMPTS | 创建 / 删除沙箱的容错窗口 | 否 | 20 / 4 / 20 / 3 |
SANDBOX_KEEPALIVE_INTERVAL_SECONDS | 向 provisioner touch 保活周期 | 否 | 30 |
SANDBOX_VIRTUAL_PATH_PREFIX | 沙箱虚拟路径前缀(会被规范化为绝对路径);工作区根 | 否 | /home/gem/user-data;技能根 /home/gem/skills 与个人技能 {prefix}/agents/skills 为常量 |
SANDBOX_WORKDIR_PROBE | bind mount 工作目录探测强度 | 否 | required(可选 optional / disabled) |
SANDBOX_EXEC_TIMEOUT_SECONDS / SANDBOX_MAX_OUTPUT_BYTES | 单条命令超时 / 输出上限 | 否 | 180 / 262144 |
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
ARQ_MAX_JOBS | 单 Worker 进程并发任务数 | 否 | 10 |
ZHIWU_JOB_TIMEOUT_SECONDS | 单个 ARQ 任务超时 | 否 | 3600(长任务如图谱构建被 cancelled 时首先调这个) |
TASKER_DEFAULT_TIMEOUT_SECONDS | 通用任务器默认超时 | 否 | 21600(6h) |
RUN_SSE_POLL_INTERVAL_SECONDS / RUN_SSE_HEARTBEAT_SECONDS / RUN_SSE_MAX_CONNECTION_MINUTES | SSE 读事件轮询间隔 / 心跳 / 最长连接时长 | 否 | 1.0 / 15 / 30 |
RUN_CANCEL_KEY_TTL_SECONDS / RUN_EVENTS_STREAM_TTL_SECONDS / RUN_EVENTS_STREAM_MAXLEN | 取消标位 TTL / 事件 Stream 存活 / Stream 裁剪上限 | 否 | 1800 / 7200 / 0(0 = 不裁剪) |
WORKER_HEALTH_INTERVAL_SECONDS | Worker 健康上报周期(Redis health key) | 否 | 5 |
READINESS_PROBE_TIMEOUT_SECONDS / READINESS_CACHE_TTL_SECONDS | 依赖就绪探测超时 / 结果缓存 | 否 | 2 / 1 |
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
LANGFUSE_ENABLED | Langfuse 追踪总开关 | 否 | 默认 true(只有显式设为其他值才关闭) |
LANGFUSE_BASE_URL | 追踪上报的基座地址 | 否 | https://litefuse.cloud(外部 SaaS) |
LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY | 追踪项目凭据 | 上报时必填 | 无默认;不配则无副作用,配了就会向外部上报 |
| 变量 | 用途 | 必填 | 默认值 |
|---|---|---|---|
ZHIWU_DATASET_PERSIST_BATCH_SIZE | 评估数据集批量落库大小 | 否 | 1(带 max(1, …) 下界) |
MYSQL_HOST / MYSQL_USER / MYSQL_PASSWORD / MYSQL_DATABASE / MYSQL_PORT / MYSQL_DATABASE_DESCRIPTION | 内置 mysql-reporter Skill 脚本的连接信息。它们不来自任何 env 文件:真值存在 DB 表 agent_envs(用户级沙箱环境变量),建沙箱时由 load_user_agent_env(uid) 注入,仅对新建沙箱生效 | 用该 Skill 时四项必填 | 端口 3306;缺失抛 MySQLConnectionError |
MINIO_BUCKET 是失效变量:只出现在 backend/.env 里,全仓(backend/zhiwu、backend/server、docker/、zhiwu-cli)无任何读取点;桶名由代码常量 KB_BUCKETS(knowledgebases / kb-images)与 PUBLIC_READ_BUCKETS(public)决定,改它得改代码。MAX_LOGIN_FAILED_ATTEMPTS、LOGIN_LOCK_DURATION_SECONDS,storage/postgres/models_business.py),全仓没有 getenv("MAX_LOGIN_FAILED_ATTEMPTS"),也没有管理端解锁端点——想调只能改代码,且锁定期内 JWT 鉴权的所有接口都返 423。neo4j/0123456789、minioadmin/minioadmin),代码不会因为你没配就报错,只会安静地用默认值连上去;docker-compose.yml 里的 SANDBOX_PROVISIONER_TOKEN 同样带内置默认值,生产必须全部覆盖。环境变量管"连接与密钥",config_options 表管"模型与引擎"。两件事不要混,也不要指望环境变量能覆盖后者。
解析顺序是 DB 存储值 > 字段 environment 指向的环境变量 > 代码默认值(backend/zhiwu/src/zhiwu/config/options.py)。
只有声明了 environment 映射的 option 才吃环境变量(OCR host opts 一类);system_options 的 5 个字段没有 environment 映射,只能由 DB 覆盖。
入库由启动时 ensure_options_in_db() 幂等同步(advisory lock pg_advisory_xact_lock(94721801));params 由代码定义,管理员只改 value。
解析结果进 Redis,OPTION_CACHE_TTL_SECONDS = 300,key 前缀 zhiwu:config_option: + 版本号;含 sensitive 字段的 option 标记为 cacheable=False 不进缓存,且 option.get(db) 显式传会话时直接走 DB、绕过缓存。
system_options,params.internal=True)| 字段 | 用途 | 代码默认值 | 消费方 / 注意 |
|---|---|---|---|
default_model | 默认对话模型 | siliconflow-cn:deepseek-ai/DeepSeek-V4-Flash | Agent 上下文兜底、Run 创建、思维导图 / 示例问题生成、POST /api/chat/call 未传 model_spec 时 |
fast_model | 快速响应模型 | 与 default_model 同值 | ⚠ 后端无任何消费方(全仓 fast_model 只出现在定义处)。唯一链路在前端:BasicSettings 配置它,会话组件把它作为 meta.model_spec 传给 POST /api/chat/call 生成自动标题;不影响摘要 / 评估等内部任务 |
embed_model | 默认 Embedding 模型 | siliconflow-cn:BAAI/bge-m3 | Milvus KB 创建时 chunk_parser_config.setdefault("embed_model_id", …);换模型会让老 collection 维度不符 |
reranker | 默认 Re-Ranker | siliconflow-cn:BAAI/bge-reranker-v2-m3 | ⚠ key 名是 reranker 而不是 reranker_model;它与 Milvus KB 自己的配置字段 reranker_model 不打通——检索精排不会回退到本项 |
default_ocr_engine | 默认 OCR 引擎 | rapid_ocr | OCR 服务、附件解析、ocr_parse_file 工具 |
/api/system/config/options 管理)| option key | 字段 | 环境变量回退 | 备注 |
|---|---|---|---|
mineru_ocr_host_opts | server_url | MINERU_API_URI | 自托管 MinerU 服务地址 |
mineru_official_api_opts | api_key(sensitive) | MINERU_API_KEY | 敏感项 → cacheable=False,不走 Redis 缓存;接口只返回来源 + 脱敏预览(serialize_option) |
pp_structure_v3_ocr_host_opts | server_url | PADDLEX_URI | PP-StructureV3 自托管服务 |
paddleocr_api_opts | api_url / api_token(sensitive) | PADDLEOCR_API_URL / PADDLEOCR_API_TOKEN | PaddleOCR-VL 与 PP-OCRv6 共用 |
remote_skill_source_policy | allowed_hosts(list[str]) | 无 | 默认 github.com、modelscope.cn,精确匹配域名;空列表 = 关闭远程安装 |
/api/system/configGET 登录用户可读,POST 与 POST /update 需 admin。
这一组读写的是 system_options 单条记录里的 5 个字段(上表)。
来源:docs/08-configuration.md 第 3 节、routers/system_router.py。
/api/system/config/options/{key}GET /options 与 PUT /options/{key} 均为 admin,读写 OPTION_DEFINITIONS 里的其它选项;
list_options() 显式排除 system_options,所以这组接口不会返回上面那 5 个字段。两组入口互不覆盖。
下面是当前代码事实,不是目标状态。标"待收敛"的是已知项。
| 方式 | 机制 | 生命周期 / 注意 |
|---|---|---|
| 密码登录 | argon2 哈希($argon2 前缀校验)+ JWT HS256,claims 只有 sub/exp/iss/aud(aud=zhiwu-know-api,iss=zhiwu-know:{ZHIWU_INSTANCE_ID}) | ⚠ 有效期硬编码 7 天,无 refresh、无黑名单 / 会话表;吊销只能等过期或换 JWT_SECRET_KEY(代价是全体重登);ZHIWU_INSTANCE_ID 变更后旧 token 因 issuer 不匹配全部失效 |
| API Key | 生成 zhiwukey_<random>,DB 只存 sha256(key) + 12 字符前缀;中间件按前缀分流 | 有完整 is_enabled / revoked_at / expires_at / last_used_at;明文只在创建时返回一次;派生型 Key 可由 API_KEY_DERIVATION_SECRET 重放 |
| OIDC SSO | authorization code 流程(login-url / callback / exchange-code),由 OIDC_* 变量组驱动 | 取决于 IdP;开关关闭时不读其余变量 |
| CLI 浏览器设备码 | cli_auth_sessions:CLI 取 user_code → Web 端 approve → CLI 换 token | 会话一次性;能力由 /api/system/discovery 的 browser_login 声明 |
| Impersonate | POST /api/auth/impersonate/{user_id},仅 superadmin,禁止模拟 superadmin 账号 | ⚠ 写 operation_logs 并 logger.warning,但签发的 token 不带任何"模拟会话"claim,下游无法区分;无二次确认 |
角色三档:superadmin / admin / user(users.role)。
守卫实现是逐路由 FastAPI 依赖注入,没有全局中间件:get_required_user(登录且已绑部门,否则 400)→ get_admin_user → get_superadmin_user。
资源权限由 share_config(JSON)定义 read / manage scope,可授 global / department / user 级,角色上限 role_ceiling 约束可达范围;部门是 departments 树。
来源:zhiwu/permissions/resource_permission.py、docs/11-security.md「已确认结论」。
敏感工具集合固定 SENSITIVE_BACKEND_TOOLS = {write_file, edit_file, execute};
按用户 tool_approval_mode(default / always / never)启用 HumanInTheLoopMiddleware。
命中时 Run 进入 interrupted,前端 approve / edit / reject 后以 run_type=resume 从 checkpoint 续跑。
当前 Project workdir 内的写入豁免审批;子智能体默认禁用敏感工具与 install_skill。
/home/gem/*,越界直接拒agents/backends/composite.py)npx skills add + 域名白名单 + draft 两段确认,且 inherit_env=False(不继承宿主环境变量)sse / streamable_http,stdio 强制仅内置(启动时自动禁用历史用户的 stdio),杜绝任意命令执行面API_KEY_DERIVATION_SECRET 与 SANDBOX_PROVISIONER_TOKEN 在所有环境缺失都是启动硬失败backend/.env 打进镜像(该文件未被 git 跟踪,仓内亦无 .env.example)env / headers 与模型供应商 api_key 在管理端接口明文返回,只靠 get_admin_user 角色守卫,无字段级打码;普通用户走字段白名单/docs 与 /openapi.json 未按 ZHIWU_ENV 关闭X-Lock-Remaining)。客户端 IP 优先取 X-Forwarded-For 首段,生产必须由反向代理覆盖 / 剥离 XFF,否则可伪造绕过限速docker/sandbox_provisioner/docker-compose.yml 把 /var/run/docker.sock 挂进 provisioner 容器,
等价于授予宿主 root,只能跑在受控主机上;需要更强隔离就切 kubernetes backend(docs/11-security.md 第 7 节)。
其二,核对时发现开发机本地 .git/config 的 remote.origin.url 把仓库用户名与密码内嵌在 HTTPS URL 里——
这不是仓库内容,但任何能读 .git/config 或执行 git remote -v 的人都拿到拉取权限,且该 URL 可能已被写进日志与 IDE 输出;
建议改用 SSH 或 credential helper,并视该密码已失效、在服务端轮换。
backend/.env 与 docker/sandbox_provisioner/kubeconfig.k8s.yaml(含客户端私钥)都已在 .gitignore 内,别让流水线把它们打进产物。没有 Prometheus / OTel 指标端点,也没有告警规则——观测靠 trace_id 日志、Langfuse 追踪和 DB 聚合三件事。
| 信号 | 机制 / 来源 | 运维要点 |
|---|---|---|
日志与 trace_id |
loguru + contextvars 的 8 位短 ID(run_id / task_id / uuid 前 8 位归一化,占位 -);TraceIdMiddleware 在 ASGI 最外层注入并支持从入站提取 |
一次 HTTP 请求或一次 ARQ 任务内所有业务日志共享同一 trace_id;API 与 Worker 两侧各自独立生成、不要求一致。格式含 [T:trace_id]、file:line,业务字段另有 run_id / thread_id / uid / agent_slug |
| 日志输出目标 | 两路:运行态文件 {ZHIWU_RUNTIME_DIR}/logs/zhiwu-{上海当日日期}.log + sys.stderr(带色) |
容器里被日志采集器接住的只有 stderr 这一路;文件名在模块导入时按当天日期固化,进程跨天不会切到新日期名(同一路径上仍会 rotation 出历史文件) |
| 日志级别与保留 | utils/logging_config.py:level="DEBUG"、rotation="10 MB"、retention="30 days"、compression="zip"、enqueue=True |
⚠ 级别硬编码 DEBUG、不可配置:全仓无 LOG_LEVEL / ZHIWU_LOG_* 环境变量,生产与开发同样 DEBUG 落盘;只有 httpx / openai / neo4j / urllib3 被桥接为 WARNING 降噪。长期留存需外挂采集 |
| 在线日志查询 | GET /api/system/logs?levels=INFO,ERROR(admin) |
读文件尾部 1000 行(硬编码,无分页);只有 levels 一个参数,按 " - LEVEL - " 子串匹配,loguru 追加的多行 traceback 会被丢弃;响应里 scope:"api" 是硬编码标签,不是按进程过滤——两侧共享 ZHIWU_RUNTIME_DIR 时会混写进同一文件 |
| 追踪(Langfuse) | LangfuseTracingSession 在 Run 开始时建根 span、写 trace 级属性并抓 trace_id,langfuse CallbackHandler 以 trace_context 挂入模型调用;trace_id 持久化在 agent_runs.langfuse_trace_id |
API 侧 GET /api/agent/runs/{run_id}/langfuse 返回追踪链接;默认 enabled=true 且基座默认外部 SaaS litefuse.cloud,自托管需改 LANGFUSE_BASE_URL |
| 审计 | 模型 / 工具生命周期写成 messages 行,message_type = model_audit / tool_audit(带 run_id / request_id / operation_id / sequence / extra_metadata);工具审计同时向 tool_calls 写兼容投影行 |
⚠ 没有独立审计表(36 表清单已确认);写入按 lifecycle 事件串行开短事务、重复 start/finish 幂等收敛。读取端 GET /api/chat/thread/{tid}/audits 权限是 superadmin(不是 admin)。管理操作另写 operation_logs |
| Run 计时 | agent_runs.prepared_at / first_model_request_at / first_output_at + 终态时间 |
由此算排队时延、首字延迟、总耗时;"首字慢"先看这几个差值,再判 worker 并发与供应商限流 |
| Token 用量 | TokenUsageMiddleware 每次主模型调用写 token_usage JSON,按模型分桶 |
成本核算靠它 + /api/dashboard/stats;工具调用统计在 tool_calls 表,会话统计在 conversation_stats |
| 告警 | 仓库内未发现告警规则 / 通知器实现 | 需自建阈值:worker health key 心跳缺失、agent_runs 的 failed / lease_expired 比率、首字延迟 P95、模型 429 计数 |
来源:docs/10-observability.md 第 1–5 节与「已确认结论」。未引入 Prometheus / OTel 属事实而非 TODO。
后端是 uv workspace(成员 server 与 zhiwu:前者只做进程壳,后者是核心库);前端是 Vue 3 + pnpm。测试不需要任何外部数据服务。
cd backend && uv sync --all-groups # workspace: server, zhiwu
# 填 backend/.env(git 未跟踪,仓内无 .env.example)
uv run python server/src/server/main.py # API :5050
uv run python server/src/server/worker_main.py # Worker(另终端)
cd web && pnpm install && pnpm dev # :5173
cd docker/sandbox_provisioner && ./script/startup.sh
cd zhiwu-cli && uv tool install --editable . # 全局命令 zhiwu
# 后端:pytest 用例都在 backend/test/unit/**
cd backend && uv run pytest test/unit -x -q
# 前端单测(注意 runner)
cd web && pnpm run test:unit # node --test,不是 vitest
cd web && pnpm run lint:check # 只读 gate;pnpm lint 可自动修复
cd web && pnpm run build
# 后端静态检查:ruff(dev 依赖 ruff>=0.16.8)
backend/test/conftest.py 只做两件事:把 backend/ 加进 sys.path、注册仅 unit 一个 marker
(auth / integration / e2e / slow 四行全被注释掉);backend/pyproject.toml 没有 [tool.pytest.ini_options],也没有 testcontainers。
实测 uv run pytest test/unit -q 零外部服务可跑完(一处陈旧用例失败属测试与代码不同步)。
集成 / e2e 体系尚未建立,需真实 Milvus / Neo4j 的验证只能手工连本地服务。
前端 test:unit 的实体是 node --test --test-concurrency=1 "test/**/*.test.js" "test/**/*.spec.js",package.json 里没有 vitest 依赖。
来源:docs/12-development-guide.md「已确认结论」。| 要做的事 | 入口文件 / 位置 | 要点 |
|---|---|---|
| 新增 Agent 后端图 | backend/zhiwu/src/zhiwu/agents/buildin/<name>/graph.py(参考 chatbot/) |
context / state / prompt / graph 四件套,继承 BaseAgent,实现中间件栈与 _build_graph;注册 backend_id 后 /api/agent/backends 可见,agents.backend_id 指向它即可被选用 |
| 新增 Skill | 内置:backend/zhiwu/src/zhiwu/agents/skills/buildin/<slug>/SKILL.md(+ 可选 scripts/) |
frontmatter 写 name / slug / description;启动 lifespan 自动同步入库(bump version)。改的是入 git 的真源,不是 skill-sources/(后者是运行期拷贝目录、已被 .gitignore 排除)。上传 / 远程安装走 /api/skills/import/prepare 或 /remote/prepare → /install-drafts/{id}/confirm;脚本在沙箱内以 /home/gem/skills/<slug> 路径执行 |
| 新增知识库实现 | backend/zhiwu/src/zhiwu/knowledge/implementations/<type>.py + knowledge/runtime.py |
实现 knowledge/base.py 的 KnowledgeBase 抽象(parse / index / query 等),再 KnowledgeBaseFactory.register(...)(现例 MilvusKB / DifyKB / NotionKB);GET /api/knowledge/types 自动可选 |
| 接入 MCP | 页面 / API:POST /api/system/mcp-servers;内置 stdio:agents/mcp/service.py 的 _DEFAULT_MCP_SERVERS |
用户配置只允许 sse / streamable_http,配 headers 鉴权;POST /{slug}/test 验连通、/{slug}/tools 按需 toggle、/{slug}/tools/refresh 清进程内 slug:config_hash 缓存。新增内置 stdio 需谨慎——它在 Worker 进程内执行命令 |
[T:trace_id];在线查 GET /api/system/logsLANGFUSE_ENABLED=true 后按 Run 看完整 trace(/api/agent/runs/{id}/langfuse)GET /api/chat/thread/{tid}/state 直查 checkpoint/api/docs(开发环境)URL 无版本号,兼容性靠响应字段增量演进。GET /api/system/discovery(公开)返回
name / version / api_prefix、capabilities.features.knowledge 与 12 个 CLI 能力字段
(min_cli_version + browser_login、api_key_auth、remote_config、agent_list、kb_* 等)。
这些值是硬编码字面量,不随配置或运行状态变化;CLI 执行前读它校验版本(要求 ≥ 0.1.0)与能力标志,
不满足直接报 ServerCompatibilityError 退出。加端点时记得同步 routers/__init__.py 与 docs/06-api.md。
从 docs/13-faq-troubleshooting.md 提炼的十条高频项。先定位是哪一层:启动 → 鉴权 → Run → 沙箱 → RAG → 日志。
症状:进程秒退,日志提示密钥校验失败。
原因:JWT_SECRET_KEY、API_KEY_DERIVATION_SECRET、SANDBOX_PROVISIONER_TOKEN 任一为空、短于 32 字符、带首尾空白或彼此相等;开发环境只对 JWT_SECRET_KEY 自动生成临时值,另两项任何环境缺失都是硬失败。
处置:配三个互不相同的 ≥ 32 字符强随机值(openssl rand -hex 32)。
症状:lifespan 阶段 RuntimeError。
原因:ZHIWU_USER_DATA_DIR 等三个目录是相对路径或空值。
处置:改成绝对路径;顺带把 ZHIWU_RUNTIME_DIR 也固定下来,否则日志散落在带 pid 的临时目录里。
症状:应用进程启动时校验 schema 失败。
原因:应用只校验兼容、不建表不改表,迁移归 storage-migrator。
处置:先跑一次性迁移(仓内 uv run python -m zhiwu.storage_migration,镜像由部署方自打),再起 api / worker。
症状:登录返 423 + X-Lock-Remaining,其他鉴权接口也 423。
原因:连续 5 次密码错 → 锁 300s;且锁定检查在 get_current_user 的 JWT 分支,作用于所有鉴权请求。
处置只能等自然过期——仓内没有任何管理端解锁 / 重置端点,阈值也没有环境变量入口。紧急旁路:API Key 分支提前 return,不经过锁定检查。
症状:登录接口返 429。
原因:两层 IP 滑窗——内存层 60s / 每 IP 10 次,Redis 层 600s(IP+账号 10 次、单 IP 全局 30 次)。
处置:等窗口过期;无清除端点,必要时直接删 zhiwu:login-failure:ip:* / …:ipacct:* key。生产要确认反代已覆盖 XFF,否则限速按代理 IP 计数。
症状:提交后无输出。
原因:Worker 没跑或队列堆积;或同线程已有活跃 Run(部分唯一索引);Run 卡住多为 Worker 崩溃后租约未过期。
处置:查 Redis health key 是否每 5s 刷新、GET /api/agent/thread/{tid}/active_run、agent_run_attempts.heartbeat_at / lease_expires_at 是否停更;等租约过期会被其他实例接管。等 Run 时用 Last-Event-ID 重连 SSE 续传。
症状:execute 或文件工具失败,或沙箱里看不到刚写的文件。
原因:provisioner 不可达(两套 compose 抢了同一个 8002)、/var/run/docker.sock 权限、镜像缺失、两端 token 不一致;不同步多为宿主 bind 源反查失败。
处置:GET :8002/health → 看 provisioner 日志 → 核对 SANDBOX_PROVISIONER_URL 与 token → 确认 compose 里 user-data / skill-projections 相对挂载路径与真实宿主目录一致 → 用 SANDBOX_WORKDIR_PROBE 打开探测;重试次数看 SANDBOX_PROVISIONER_CREATE_ATTEMPTS。
症状:KB 不索引,或查询空返回。
原因:OCR / 解析引擎不可用(MinerU、PaddleOCR 服务地址或密钥未配、超时);检索侧是状态未到 indexed、相似度阈值过滤或 search_mode 选择不当。
处置:GET /api/system/ocr/health,把 default_ocr_engine 先换回本地 rapid_ocr;用 /databases/{kb_id}/query-test 调试、调 query-params;换 embed 模型导致维度不符时新建 KB 重索引。
症状:模型说没有某技能,或前端图片 404。
原因:技能需模型先 read_file SKILL.md 才激活,未激活就不放依赖工具;投影 fail-closed 会在同步异常时清空 skill-projections/<uid>/。头像 404 多为宿主机直跑没设 VITE_MINIO_URL,代理仍去打 http://minio:9000。
处置:检查 prompt 摘要是否含该技能、重新触发同步、补 VITE_MINIO_URL=http://127.0.0.1:9000。
症状:/api/system/logs 返回空或只有旧内容;历史用户配置的 stdio MCP 不见了。
原因:该端点只读当前 API 进程自己 的日志文件(文件名按当天固化),Worker 未共享 ZHIWU_RUNTIME_DIR 时写在另一个带 pid 的目录;stdio 是启动策略强制禁用的安全迁移。
处置:进容器 tail 文件,或让两侧共用同一 ZHIWU_RUNTIME_DIR(会互相混写);MCP 改用 sse / streamable_http 形态。
提示:docs/13-faq-troubleshooting.md 明确标注各条"原因"为代码逆向推导,尚无真实运维案例印证。
说明站本身是 site/ 下的原生静态 HTML / CSS / JS——零构建步骤、无外部 CDN 依赖、无 bundler。
同一份产物可以走 Pages(纯静态托管)或 Workers(Static Assets 绑定 + 少量边缘路由)。
cd site
npx wrangler pages deploy . --project-name=zhiwu-site
在 site/ 目录执行,产出目录就是当前目录,没有 build command。
Dashboard 路径等价:连 Git 仓库 → Root directory 设 site → Build command 留空 →
Output directory 填 site(或连仓库根时填 .,取决于 Root 设置)。
site/_headers 与 site/_redirects 只对 Pages 生效:它们必须放在被部署目录的根(也就是 site/ 下),
Pages 用它配缓存与安全头;走 Workers 时这两个文件不会被读取,同类规则要搬到 Worker 脚本或 Cloudflare 的 Rules 里。
当前 _headers 已给全站加 CSP / nosniff / frame-ancestors,并给 /assets/* 打一年不可变缓存、
给 /*.html 打 max-age=0, must-revalidate;_redirects 只保留两条历史别名
(/docs/* → /deploy、/api-docs → /api)。
不要再往 _redirects 里写 /capabilities → /capabilities.html 这类规则。
Pages 本身就会把 /x.html 规范化成 /x(308)并在无扩展名路径上找回同名 .html:
自己再补一条正向 301,两个方向就互相指回去,浏览器 ERR_TOO_MANY_REDIRECTS、页面打不开。
clean URL 是平台白送的,不需要脚本或规则去实现。
cd site
npm install
npx wrangler deploy
# 本地预览(两种方式都不需要构建)
python3 -m http.server 8788
# Workers 路径的本地联调用:
npx wrangler dev
Workers 路线靠 site/wrangler.jsonc 的静态资源声明:
assets.directory = "./"(即 site/ 自身)、assets.binding = "ASSETS"、
html_handling = "auto-trailing-slash"、not_found_handling = "404-page"(未命中时回 site/404.html)、
run_worker_first = true。因为开了 run_worker_first,每个请求先进
site/worker/index.js:只有 /api/*(/api/health、/api/site)与
/docs/*、/api-docs 这两条别名(Workers 不读 _redirects,所以在脚本里补齐以保持两条路线一致)
由脚本处理,其余一律 env.ASSETS.fetch(request) 交回静态资源——
脚本里同样不能对无扩展名路径手动改写 .html,否则会和 auto-trailing-slash 的规范化形成同一个重定向环。
部署命令:cd site && npm install && npx wrangler deploy;本地联调 npx wrangler dev
(npm run preview 是同一条)。账号信息不写在配置里(wrangler.jsonc 里没有 account_id 字段;留空字符串不是"自动探测",会让部署直接失败):
由 npx wrangler login 写入本地凭据,或用环境变量 CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN 提供。若不需要边缘路由,把 run_worker_first 改为 false
可让请求完全绕过 Worker,但那样 /api/health 会返回 404.html 而不是 JSON。
| 维度 | Pages | Workers + Static Assets |
|---|---|---|
| 适用 | 纯内容站(本站默认选择) | 需要在边缘加路由、重定向逻辑或鉴权时 |
| 构建 | 无;直接上传目录 | 无;wrangler deploy 打包 assets + 脚本 |
_headers / _redirects | 生效 | 不生效,需在 Worker 或 Rules 里实现 |
| 成本模型 | 静态请求额度为主 | 每次请求都执行 JS,计入 Worker 请求数 |
| 预览 | 每次部署产生 <hash>.zhiwu-site.pages.dev 预览域名;连 Git 时每个 PR 一个预览 | wrangler versions upload / 版本部署产生预览 |
Settings → Custom domains 添加域名,Cloudflare 会写 CNAME 并在托管 zone 内自动开代理;*.pages.dev 子域默认存在,可用 Branch settings 控制生产分支。Triggers → Custom domains 绑定,或在 wrangler.jsonc 里声明 routes 后随部署生效。pages.dev / workers.dev 域名,预览部署不会覆盖生产。wrangler.jsonc 的 name 与 Pages 的 --project-name 必须与实际 Cloudflare 账号下的项目名一致;不一致时 pages deploy 会新建项目、wrangler deploy 会新建 Worker,容易留下孤儿。npx wrangler login(或提供 CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID)。本站是公开内容,不需要 secrets;若后续给 Worker 加鉴权,密钥走 wrangler secret put,不要写进 wrangler.jsonc。