# 智服中台 · zhifu-platform 智服中台面向多商家电商客服与售后场景,将消费者咨询、订单查询、知识检索、换货申请、人工审批和 OMS 履约连接为完整业务流程。项目包含消费者 H5、商家后台、平台后台、Agent 服务、独立 OMS 服务,以及模型用量、费用和运行监控能力。 ## 本机演示访问与账户 以下为当前本机演示环境的访问地址与账号密码,地址均通过 `127.0.0.1` 在运行项目的电脑上访问。 | 服务/角色 | 访问地址 | 演示账号 | 密码 | | --- | --- | --- | --- | | 消费者 H5 | [进入商城](http://127.0.0.1:18762/#/customer/home) | `customer01` | `JJJkewsWJfHeNcmVLmuzgwmx` | | 商家管理员 | [商家后台](http://127.0.0.1:18762/#/merchant/login) | `admin01` | `JJJkewsWJfHeNcmVLmuzgwmx` | | 客服 | [商家后台](http://127.0.0.1:18762/#/merchant/login) | `staff01` | `JJJkewsWJfHeNcmVLmuzgwmx` | | 审批主管 | [商家后台](http://127.0.0.1:18762/#/merchant/login) | `supervisor01` | `JJJkewsWJfHeNcmVLmuzgwmx` | | 费用管理 | [商家后台](http://127.0.0.1:18762/#/merchant/login) | `finance02` | `JJJkewsWJfHeNcmVLmuzgwmx` | | 平台管理员 | [平台后台](http://127.0.0.1:18762/#/platform/login) | `platform-admin` | `3TnCh2YxrsBsoRyrV0MRiL4q16NWKggO` | | Grafana | [监控面板](http://127.0.0.1:13000/d/zhifu-operations) | `zhifu` | `zhifu_local_only` | | Prometheus | [指标查询](http://127.0.0.1:19090) | 无需登录 | 无需密码 | | Alertmanager | [告警管理](http://127.0.0.1:19093) | 无需登录 | 无需密码 | | RabbitMQ | [消息管理](http://127.0.0.1:15673) | `zhifu` | `zhifu_local_only` | 商家后台登录时,**商家标识**统一填写 `ac700000-2026-4000-8000-000000000001`。客服、审批主管、商家管理员和费用管理账户共用该入口,登录后按账户权限展示功能。 Grafana 支持匿名只读访问,管理操作使用表中的账号登录。消费者与商家凭据保存在 `.work/frontend-accounts.json`,平台凭据保存在 `.work/platform-accounts.json`;首次在新环境准备账户时,脚本生成该环境的密码,以对应账户文件为准。 ## 产品与业务 | 使用者 | 入口与职责 | | --- | --- | | 消费者 | 浏览商品与分类、查询本人订单、咨询 AI、补充申请信息、确认换货方案、查询工单、请求人工协助、管理沟通习惯 | | 商家管理员 | 管理店铺、员工权限、渠道、服务资料、售后规则、Agent 发布配置和模型费用 | | 客服 | 查看授权店铺会话、接管与回复、查询关联订单和工单、创建或修订售后方案 | | 审批主管 | 查看授权范围内的待办并批准或拒绝售后申请 | | 平台管理员 | 管理商家接入、启停状态、资源额度、渠道投递、运行概览和操作审计 | 商家管理员、客服和主管共用商家后台,菜单、数据与操作由账户授权决定。平台后台使用独立账户和会话。消费者、商家、平台分别保存登录状态,浏览器令牌保存在内存,刷新页面需要重新登录。 主要业务能力: - **身份与权限**:租户、店铺、客户、员工和 SERVICE 账户隔离;请求执行时检查账户状态、店铺权限及资源归属。 - **会话与 Agent**:LangGraph 编排、工具授权、多轮咨询、补充信息、业务等待、运行取消、人工接管及恢复;通过 SSE 传递持久事件并支持断线续传。 - **售后与履约**:可信订单同步、版本化换货规则、方案创建与修订、客户确认、主管审批、数量预占、OMS 结果查询和幂等处理。 - **知识与记忆**:资料草稿、发布、修订与撤回;BGE-M3 向量与中文 BM25 混合检索,答复附资料引用;经客户授权保存、修改和清空沟通习惯。 - **渠道与通知**:HMAC 和 Standard Webhooks 验签、入站去重、渠道身份关联、会话结果投递、员工通知、未读状态与业务跳转。 - **额度与费用**:全局及租户并发限制、交互与后台任务预留、每日 token 预算、人民币费用预占与结算、价格审批、账单导入和双人费用核对。 - **运行管理**:独立迁移、发布检查、工作进程心跳、Prometheus 指标、告警、数据库备份、消息核对和知识索引恢复。 一次换货申请依次经过订单与规则查询、方案创建、客户确认、主管审批、异步履约和结果查询。Agent 使用授权工具查询或创建方案;客户确认和主管审批由对应身份通过业务接口完成。网络结果未知时,系统保留原操作标识并查询外部结果。 商城中的购物车、交易支付、营销、地址、收藏等栏目按页面说明展示业务范围。项目中的 OMS 使用独立数据库和 HTTP 接口;接入商家已有 OMS 时,需要适配订单、商品明细、客户身份、凭据和履约状态。 ## 系统组成 ![智服中台架构](docs/architecture/zhifu-platform-current-shuhe.png) | 部分 | 使用的技术与职责 | | --- | --- | | 前端 | Vue 3、Vue Router、Vite;后台使用 Element Plus,H5 使用 Vant,图标使用 `@lucide/vue` | | 平台 API | Python 3.12、FastAPI、Pydantic、SQLAlchemy、asyncmy;处理身份、业务、权限和事务 | | Agent | LangChain、LangGraph、OpenAI 兼容模型接口;工作进程从持久任务中领取并执行运行 | | 平台与 OMS 数据库 | 两套 MySQL 8.4,分别维护业务数据和独立 Alembic 迁移 | | 缓存 | 独立 Redis 8.2,使用 `allkeys-lru` 管理可重建缓存 | | 持久状态 | 独立 Redis 8.2,启用 AOF 和 `noeviction`;提供协调状态、RedisJSON、Search 和 LangMem 记忆存储 | | 可靠消息 | RabbitMQ 4.2、MySQL Outbox、消费收据和外部操作记录 | | 知识检索 | Milvus 3.0.1、etcd、MinIO;BGE-M3 生成 1024 维向量,中文 BM25 与 RRF 合并检索结果 | | 监控与入口 | Prometheus、Alertmanager、Grafana;HAProxy 为多个 API 容器提供入口 | 五类常驻工作进程分别为 `agent`、`memory`、`fulfillment`、`knowledge`、`channel-delivery`,负责模型执行、记忆提取、售后履约、旧知识版本清理和渠道结果发送。 ## 目录结构 | 路径 | 内容 | | --- | --- | | `frontend/` | 三个产品入口、公共组件、API 调用和浏览器功能测试 | | `src/zhifu/core/` | 配置、数据库连接、错误响应、接口定义和指标 | | `src/zhifu/modules/` | 身份、售后、Agent、会话、知识、记忆、渠道、商家、平台、商品与费用模块 | | `src/zhifu/oms/` | 独立 OMS 服务及凭据登记命令 | | `src/zhifu/operations/` | 发布检查、工作进程入口、额度管理与恢复命令 | | `src/zhifu/monitoring/` | 任务、心跳与模型用量 exporter | | `migrations/`、`oms_migrations/` | 平台和 OMS 的数据库迁移 | | `ops/` | 容器环境示例、HAProxy、Prometheus、Alertmanager 与 Grafana 配置 | | `scripts/` | 开发数据准备、OpenAPI 导出、备份与恢复验证 | | `tests/`、`evaluations/` | 功能、集成、容量、恢复和模型评测资料 | | `docs/` | 设计依据、接口说明和专项操作说明 | | `.work/` | 被版本控制忽略的模型缓存、开发凭据、备份和中间文件 | ## 环境要求 - Python **3.12**,版本范围由 `pyproject.toml` 限定为 `>=3.12,<3.13`。 - uv;Dockerfile 使用 **0.11.30**,依赖安装使用仓库中的 `uv.lock`。 - Docker Engine 或 Docker Desktop,运行 Linux 容器;Docker Compose 需要支持 profiles、`--wait`,多副本配置还使用 `!reset`。 - Node.js **22.12.0 及以上**和 npm,前端依赖由 `frontend/package-lock.json` 固定。 - 可访问镜像仓库、Python/npm 包源和模型下载源;离线环境需提前准备对应镜像、依赖与模型缓存。 - 一个支持 `tools`、`tool_calls`、tool 消息和 `usage` 的 OpenAI 兼容模型服务,以及有效 API Key。 默认推理配置使用 CPU。运行配置为 BGE 服务设置 6 GiB 内存上限,为 API、OMS、exporter 和每类工作进程分别设置 1 GiB 上限;数据库、Milvus 和监控还需要额外资源。部署前根据服务数量、知识规模和并发目标分配内存、CPU 与持久存储。 以下本机命令使用 **PowerShell**,从项目根目录执行;前端命令所在目录会单独说明。逐条执行命令,出现非零退出状态时处理具体错误后继续。示例配置中的本机密码仅用于开发环境。 ## 本机完整部署 ### 1. 准备配置与依赖 首次部署复制配置文件,已有文件继续使用原内容: ```powershell if (-not (Test-Path .env)) { Copy-Item .env.example .env } if (-not (Test-Path .env.oms)) { Copy-Item .env.oms.example .env.oms } if (-not (Test-Path .env.agent)) { Copy-Item .env.agent.example .env.agent } New-Item -ItemType Directory -Force .work | Out-Null if (-not (Test-Path .work/runtime.env)) { Copy-Item ops/runtime.local.env.example .work/runtime.env } uv sync --locked ``` 宿主机命令分别读取 `.env`、`.env.oms` 和 `.env.agent`。容器通过 `.work/runtime.env` 注入环境变量,服务地址使用 Docker 服务名。两种配置需要指向同一组业务数据和同一模型配置。 | 配置 | 宿主机开发值 | 容器配置值 | | --- | --- | --- | | `ZHIFU_DATABASE_URL` | 平台 MySQL:`127.0.0.1:13306/zhifu` | `mysql:3306/zhifu`,完整连接串见示例文件 | | `OMS_DATABASE_URL` | OMS MySQL:`127.0.0.1:13307/oms` | `mysql-oms:3306/oms` | | `ZHIFU_CACHE_URL` | `redis://127.0.0.1:16379/0` | `redis://redis-cache:6379/0` | | `ZHIFU_STATE_URL` | `redis://127.0.0.1:16380/0` | `redis://redis-state:6379/0` | | `ZHIFU_BROKER_URL` | RabbitMQ:`127.0.0.1:15672` | `rabbitmq:5672`,使用 `amqp://` 连接串 | | `KNOWLEDGE_MILVUS_URL` | `http://127.0.0.1:19530` | `http://milvus:19530` | | `KNOWLEDGE_INFERENCE_URL` | `http://127.0.0.1:8200` | `http://inference:8200` | | `AGENT_MODEL_NAME`、`AGENT_MODEL_URL` | 在 `.env.agent` 中配置 | 在 `.work/runtime.env` 中配置 | | `AGENT_CREDENTIAL_ENV` | 保存模型密钥的环境变量名称 | API、Agent、记忆和发布检查使用的环境变量名称 | MySQL 连接串必须使用 `mysql+asyncmy://用户名:密码@主机:端口/数据库`。缓存和持久状态必须使用两个独立 Redis 服务。宿主机的知识配置通过进程环境变量设置,`KnowledgeSettings` 不读取上述 `.env` 文件;各配置文件只填写其对应设置类支持的字段。 模型示例配置使用 `deepseek-flash` 和 `DEEPSEEK_API_KEY`。将自己的密钥注入当前进程;已经配置 Windows 用户环境变量时可以使用: ```powershell if (-not $env:DEEPSEEK_API_KEY) { $env:DEEPSEEK_API_KEY = [Environment]::GetEnvironmentVariable('DEEPSEEK_API_KEY', 'User') } if (-not $env:DEEPSEEK_API_KEY) { throw '请配置 DEEPSEEK_API_KEY 后继续' } ``` 使用其他供应商时,同步调整模型名称、地址、`AGENT_CREDENTIAL_ENV` 和 `AGENT_MODEL_EXTRA_BODY`。后者为 JSON 对象,默认可设为 `{}`。容器中需要存在 `AGENT_CREDENTIAL_ENV` 指定的实际密钥变量;自定义变量可以放入受保护的运行环境文件。现有 Compose 对 `DEEPSEEK_API_KEY` 的显式注入读取启动命令所在终端,因此该变量需要在当前终端中有效。 ### 2. 启动基础服务 ```powershell docker compose config --quiet docker compose up -d --wait docker compose ps ``` 默认启动平台 MySQL、OMS MySQL、两套 Redis、RabbitMQ、etcd、MinIO 和 Milvus,创建 `zhifu-platform_default` 网络及持久数据卷。`monitoring`、`fault-tests`、`replica-tests` profile 按需启用。 ### 3. 准备 BGE-M3 模型 本机下载固定版本模型到 `.work/models`: ```powershell uv run python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-m3', revision='5617a9f61b028005a4858fdac845db406aefb181', cache_folder='.work/models', device='cpu')" ``` 容器使用只读模型目录,并设置 `HF_HUB_OFFLINE=1`。需要保留完整缓存目录,包括模型快照及其引用文件;容器用户 UID `10001` 必须具有读取权限。下载完成后设置 Compose 参数: ```powershell $env:ZHIFU_RUNTIME_ENV = (Resolve-Path .work/runtime.env).Path $env:ZHIFU_MODEL_PATH = (Resolve-Path .work/models).Path $env:ZHIFU_DEPENDENCY_NETWORK = 'zhifu-platform_default' ``` 这些变量在后续迁移、启动、发布检查和停止命令所在终端中都需要保留。`ZHIFU_RUNTIME_ENV` 和 `ZHIFU_MODEL_PATH` 在 Compose 解析时即为必填项。 ### 4. 构建镜像并初始化数据库与知识集合 ```powershell docker build -t zhifu-platform:local . docker build --build-arg DEPENDENCY_GROUP=inference -t zhifu-inference:local . docker compose -f compose.runtime.yaml config --quiet docker compose -f compose.runtime.yaml run --rm migrate docker compose -f compose.runtime.yaml run --rm migrate-oms docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.modules.knowledge.vector ``` 平台和 OMS 分别迁移至镜像中的 Alembic head。API 启动不执行数据库结构变更。知识初始化创建 `KNOWLEDGE_COLLECTION` 指定的集合,默认名称为 `zhifu_knowledge_v1`;集合已存在时加载该集合。 ### 5. 启动应用并执行发布检查 ```powershell docker compose -f compose.runtime.yaml --profile inference up -d --wait api oms operations inference docker compose -f compose.runtime.yaml run --rm release-check docker compose -f compose.runtime.yaml ps ``` `release-check` 在 60 秒期限内检查两套数据库版本、Redis、RabbitMQ、Milvus 集合与向量维度、真实 BGE 请求、API readiness、OMS 健康状态及模型配置。任意检查失败返回非零退出状态。该命令会计算本地文本向量,生成模型只检查配置与凭据是否存在。 本机访问端口如下,均绑定 `127.0.0.1`: | 服务 | 端口或路径 | | --- | --- | | 平台 API | `28000`;`/health/live`、`/health/ready` | | OMS | `28100`;`/health` | | BGE-M3 | `28200`;`/health/ready` | | 运行 exporter | `28300`;`/metrics` | | 平台 / OMS MySQL | `13306` / `13307` | | 缓存 / 持久 Redis | `16379` / `16380` | | RabbitMQ AMQP / 管理界面 | `15672` / `15673` | | Milvus / Milvus 健康检查 | `19530` / `19091` | 平台 `/health/ready` 检查 MySQL 和两套 Redis。完整依赖可用性还需要结合发布检查、向量服务状态和工作进程指标判断。 ### 6. 准备本机登录账户 在本机 development/test 数据库中执行: ```powershell uv run python scripts/prepare_frontend_accounts.py uv run python scripts/prepare_storefront_data.py uv run python scripts/prepare_platform_data.py ``` 脚本创建独立开发商家的账户、商品和示例订单,使用实际数据库保存数据。商家与消费者凭据保存在 `.work/frontend-accounts.json`,平台凭据保存在 `.work/platform-accounts.json`,渠道 SERVICE 凭据保存在 `.work/storefront-channel.json`。账户文件需要限制访问权限。 开发账户包括 `customer01`、`staff01`、`supervisor01`、`admin01`、`finance02`,平台账户为 `platform-admin`。这些脚本只允许 development/test 环境;示例订单不包含独立 OMS 的履约登记。完整换货流程还需要按照后文配置 OMS,并持续同步可信订单。 ### 7. 启动后台工作进程 ```powershell docker compose -f compose.runtime.yaml --profile workers up -d agent memory fulfillment knowledge channel-delivery docker compose -f compose.runtime.yaml ps docker compose -f compose.runtime.yaml logs --tail 100 agent memory fulfillment knowledge channel-delivery ``` 有待处理咨询或已授权记忆任务时,`agent` 和 `memory` 会调用配置的生成模型。启动前确认租户额度、凭据及预算。OMS 履约密钥需要注入使用连接器的 API、Agent 和 `fulfillment`;渠道签名密钥需要注入 API 与 `channel-delivery`,变量名称与对应业务配置中的 `credential_env` 一致。 ### 8. 启动前端 进入 `frontend` 目录: ```powershell Set-Location frontend if (-not (Test-Path .env.local)) { Copy-Item .env.example .env.local } npm ci ``` 编辑 `frontend/.env.local`,将租户和店铺 UUID 替换为账户文件中的实际值: ```dotenv API_TARGET=http://127.0.0.1:28000 VITE_TENANT_ID=替换为实际租户UUID VITE_STORE_ID=替换为实际店铺UUID ``` ```powershell npm run dev ``` 开发服务器端口为 `18762`,浏览器打开 `http://127.0.0.1:18762`。页面入口分别为 `/#/customer/home`、`/#/merchant/login`、`/#/platform/login`。修改环境文件后重新启动 Vite。 `API_TARGET` 只控制 Vite 开发与预览代理;前端业务请求使用同域相对路径。`VITE_TENANT_ID` 和 `VITE_STORE_ID` 会进入前端构建产物,只用于默认业务范围,服务端仍独立验证身份与权限。密钥不得使用 `VITE_` 前缀。 ### 9. 启用监控 完整容器部署时,在 `ops/prometheus/prometheus.yml` 中将平台、向量和 operations 的采集目标分别设置为 `api:8000`、`inference:8200`、`operations:8300`;这些服务通过基础 Compose 网络互相访问。检查 `ops/prometheus/replicas/` 中的发现文件,只保留实际运行的采集目标。 从项目根目录执行: ```powershell docker compose --profile monitoring up -d prometheus alertmanager grafana ``` Prometheus、Alertmanager、Grafana 的本机端口分别为 `19090`、`19093`、`13000`。已有 Prometheus 进程在配置修改后需要重启。检查采集目标正常,且 `zhifu_workers_alive` 包含五类有效工作进程。 开发 Grafana 管理账户为 `zhifu`,密码为 `zhifu_local_only`,允许匿名只读访问。生产环境需要独立密码、访问控制和 HTTPS。Alertmanager 默认接收器只在服务内保存告警,外部通知需要配置实际接收渠道。 ## 生产部署 ### 基础设施与运行配置 1. 准备独立的生产平台数据库、OMS 数据库、缓存 Redis、持久 Redis、RabbitMQ 和知识存储。数据库使用独立凭据,持久 Redis 保留 RedisJSON/Search 能力及 `noeviction`。基础 `compose.yaml` 提供本机开发配置;生产依赖由受控部署配置或托管服务提供。 2. 为应用容器准备能够访问依赖的 Docker 网络,将网络名称写入 `ZHIFU_DEPENDENCY_NETWORK`。该网络是 external 网络,启动 runtime Compose 前必须存在。 3. 准备受保护的运行环境文件,设置 `ZHIFU_ENVIRONMENT=production`。生产配置校验要求 Redis 使用带认证的 `rediss://`,RabbitMQ 使用带独立凭据的 `amqps://`,MySQL 使用独立密码。按部署环境安装受信 CA、配置证书与网络访问控制。 4. 固定应用镜像和推理镜像的 digest,分别设置 `ZHIFU_IMAGE` 与 `ZHIFU_INFERENCE_IMAGE`。将完整模型缓存挂载至 `ZHIFU_MODEL_PATH`,将环境文件绝对路径设置为 `ZHIFU_RUNTIME_ENV`。 5. 保证 API、全部工作进程与迁移任务使用一致的业务配置。根据连接预算设置 `ZHIFU_POOL_SIZE`、`ZHIFU_MAX_OVERFLOW`;根据供应商额度配置 `AGENT_GLOBAL_CONCURRENCY`、`AGENT_TENANT_CONCURRENCY`、两类预留和租户预算。交互与后台预留之和不得超过全局并发上限。 以下 PowerShell 参数需要替换为目标环境的实际值,运行环境文件由部署系统生成和保护: ```powershell $env:ZHIFU_IMAGE = 'registry.example.com/zhifu-platform@sha256:替换为镜像摘要' $env:ZHIFU_INFERENCE_IMAGE = 'registry.example.com/zhifu-inference@sha256:替换为镜像摘要' $env:ZHIFU_RUNTIME_ENV = (Resolve-Path .work/runtime.production.env).Path $env:ZHIFU_MODEL_PATH = (Resolve-Path .work/models).Path $env:ZHIFU_DEPENDENCY_NETWORK = 'zhifu-production' docker compose -f compose.runtime.yaml config --quiet docker compose -f compose.runtime.yaml --profile inference --profile workers pull ``` `ZHIFU_RUNTIME_ENV` 是 Compose 的注入文件,支持多组服务变量;宿主机配置继续按设置类分别管理。运行镜像只包含应用、依赖和迁移,前端静态文件、模型、开发脚本与密钥独立发布。 首次生产部署在开放入口前执行迁移、知识初始化和服务检查: ```powershell docker compose -f compose.runtime.yaml run --rm migrate docker compose -f compose.runtime.yaml run --rm migrate-oms docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.modules.knowledge.vector docker compose -f compose.runtime.yaml --profile inference up -d --wait api oms operations inference docker compose -f compose.runtime.yaml run --rm release-check ``` ### 初始化正式账户与授权 完成两套数据库迁移后,通过管理命令创建正式租户、店铺和业务账户。密码使用隐藏终端输入,至少 12 个字符: ```powershell docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.manage tenant --name '商家名称' $tenantId = Read-Host '输入上条命令返回的租户 UUID' docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.manage store --tenant $tenantId --name '店铺名称' $storeId = Read-Host '输入上条命令返回的店铺 UUID' docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.manage user --tenant $tenantId --username merchant-admin --kind STAFF $adminId = Read-Host '输入上条命令返回的员工 UUID' ``` 新增账户没有店铺权限;首位商家管理员由受控数据库管理流程配置 `store_grants`。使用上面得到的 UUID,在平台 MySQL 执行以下首次初始化 SQL: ```sql SET @tenant_id = UUID_TO_BIN('替换为租户UUID'); SET @store_id = UUID_TO_BIN('替换为店铺UUID'); SET @admin_id = UUID_TO_BIN('替换为员工UUID'); START TRANSACTION; SELECT p.username, p.kind, p.status, t.name, t.status, s.name, s.status FROM principals p JOIN tenants t ON t.id = p.tenant_id JOIN stores s ON s.tenant_id = t.id WHERE p.tenant_id = @tenant_id AND p.id = @admin_id AND s.id = @store_id FOR UPDATE; ``` 确认查询返回唯一目标员工和店铺,员工为启用的 `STAFF`,租户和店铺均为 `ACTIVE` 后,在同一事务中执行: ```sql INSERT INTO store_grants (tenant_id, principal_id, store_id, permission) VALUES (@tenant_id, @admin_id, @store_id, 'orders:read'), (@tenant_id, @admin_id, @store_id, 'tickets:read'), (@tenant_id, @admin_id, @store_id, 'stores:manage'), (@tenant_id, @admin_id, @store_id, 'staff:manage'), (@tenant_id, @admin_id, @store_id, 'channels:manage'), (@tenant_id, @admin_id, @store_id, 'rules:write'), (@tenant_id, @admin_id, @store_id, 'knowledge:write'), (@tenant_id, @admin_id, @store_id, 'agent:publish'); COMMIT; ``` SQL 适用于新账户首次初始化;已有授权按当前记录处理,保存操作人、审批依据和授权清单。`zhifu.manage grant` 可用于其 `--help` 列出的订单、售后和规则权限。客服、主管、客户和 SERVICE 账户按实际职责分别创建,员工邀请记录需要完成身份接入后才具备登录条件。 平台账户独立保存在 `platform_accounts`。由受控管理员在交互终端生成 Argon2 密码摘要: ```powershell docker compose -f compose.runtime.yaml run --rm migrate python -c "from getpass import getpass; from zhifu.modules.identity.auth import password_hasher; p=getpass('Password: '); assert 12<=len(p)<=256 and p==getpass('Confirm: '), 'Password validation failed'; print(password_hasher.hash(p))" ``` 将输出摘要直接填入 MySQL 客户端中的首次账户登记语句,登记结果保存至受控操作记录: ```sql INSERT INTO platform_accounts (id, username, password_hash, status, permissions, created_at) VALUES (UUID_TO_BIN(UUID()), 'platform-admin', '替换为完整Argon2摘要', 'ACTIVE', JSON_ARRAY('platform:manage'), UTC_TIMESTAMP(6)); ``` 平台页面登记的商家初始为 `PENDING`,需要在受控接入流程中完成店铺、账户、渠道和权限配置,更新 `tenant_registrations` 为 `COMPLETE` 后才能通过平台启用。首次直接使用管理 CLI 创建的租户默认启用,完成配置前保持外部入口关闭。 ### 配置 OMS、渠道和业务资料 使用项目自带 OMS 时,为每个租户与店铺准备 32–128 个字符的独立凭据,将对应变量注入运行环境。示例使用 `OMS_STORE_TOKEN`: ```powershell docker compose -f compose.runtime.yaml run --rm migrate-oms python -m zhifu.oms.manage --tenant $tenantId --store $storeId --credential-env OMS_STORE_TOKEN ``` 登记平台连接器时,地址必须能从 API 和工作进程访问。本机容器地址为 `http://oms:8100`;生产连接器要求 HTTPS,需配置 OMS TLS 入口或使用商家授权的 HTTPS OMS: ```powershell $omsBaseUrl = Read-Host '输入应用容器可访问的 OMS 地址' docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.modules.integrations.manage --tenant $tenantId --store $storeId --base-url $omsBaseUrl --credential-env OMS_STORE_TOKEN ``` 仓储登记订单与确认履约完成使用单独凭据,通过 `zhifu.oms.manage --fulfillment` 授予对应能力。平台接入的 SERVICE 账户需要 `orders:sync`,通过 `/internal/v1/orders/{order_id}` 同步订单;来源、客户、商品明细和订单标识必须与 OMS 一致。确认与审批要求订单观察时间在最近五分钟内,接入任务应持续更新可信订单。 商家保存渠道名称与公网 HTTPS 回调地址,并确认业务授权范围。新建渠道使用 `ZHIFU_CHANNEL_SIGNING_SECRET` 作为出站签名凭据变量,需要注入 API 和 `channel-delivery`。入站签名另行登记密钥标识、协议、凭据变量与有效期;例如将独立密钥注入 API 和管理容器的 `CHANNEL_INBOUND_SECRET` 后执行: ```powershell $channelKeyId = Read-Host '输入入站密钥标识' $validFrom = Read-Host '输入有效期开始时间,包含时区' $validUntil = Read-Host '输入有效期结束时间,包含时区' docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.operations.channel_keys --tenant $tenantId --store $storeId --key-id $channelKeyId --credential-env CHANNEL_INBOUND_SECRET --protocol zhifu-hmac --valid-from $validFrom --valid-until $validUntil ``` HMAC 密钥至少 32 个字符;时间使用带时区的 ISO 8601 格式,结束时间晚于开始时间。Standard Webhooks 使用 `--protocol standard-webhooks`,密钥为 `whsec_` 加标准 Base64,解码后为 24–64 字节,每个绑定和密钥标识使用独立密钥。 配置完成后启用渠道,由客户与具有店铺 `identities:verify` 权限的 SERVICE 账户完成身份关联,再验证签名消息入站和结果回调。该 SERVICE 权限通过受控 `store_grants` 授权流程登记。接收端验证签名、时间戳和事件标识,按持久事件编号去重;渠道工作进程每组最多尝试三次,失败后通过平台查询并按需重试。 商家管理员发布换货规则、知识资料和 Agent 配置。知识发布依赖可用的 BGE 与 Milvus;Agent 发布检查模型配置、凭据和店铺配置。商品目录、订单来源及售后规则需要使用正式业务数据。 通过平台配置租户并发及 token 额度。启用金额预算时,使用 `zhifu.operations.billing_access grant` 为不同员工分配 `billing:read`、`billing:request`、`billing:approve` 中所需权限,命令要求 `--tenant`、`--principal`、`--permission`、`--operator`、`--reason`。费用页面入口另外需要店铺 `store_grants` 中的 `billing:read`。随后依次批准精确模型价格和每日预算,申请人不能审批自己的申请。价格使用核实过的供应商依据,金额以整数微元保存,`1000000` 微元为 1 元。 ### 构建并发布前端 保留完整仓库目录,前端构建会引用 `docs/design/wireframes` 中的已确认样式。在 `frontend` 目录中为目标店铺设置参数并执行: ```powershell $env:VITE_TENANT_ID = $tenantId $env:VITE_STORE_ID = $storeId npm ci npm run build ``` 将 `frontend/dist/` 内容部署到静态文件服务器,配置 HTTPS。不同店铺使用对应构建参数;构建后修改服务器环境变量不会改变已生成的静态文件。`npm run preview` 用于本机检查,默认端口为 `18763`。 入口代理需要完成以下配置: - 页面使用 Hash 路由,三个入口共用同一套静态文件,默认静态资源路径为站点根目录。 - 将 `/openapi/` 和 `/admin/` 原样转发到平台 API。代理位于同一宿主机时,上游使用 `127.0.0.1:28000`;代理位于容器内时,加入应用网络并使用 `api:8000`。多副本使用 HAProxy 入口。 - 启用渠道入站时,原样转发 `/channel/`,保留完整路径、全部签名请求头和原始请求正文。 - 保留 `Authorization`、`Idempotency-Key`、`Last-Event-ID` 和请求正文,保留服务端状态码及 `Retry-After`。关闭 API 响应缓存,关闭 SSE 响应缓冲,连接读取超时设置为至少 65 秒。 - SSE 每 15 秒发送心跳,约 50 秒结束一次连接,前端携带最后处理序号续传。事件游标过期返回 410,前端读取历史和当前状态后恢复订阅。 - 按实际代理地址配置 Uvicorn 的可信代理范围和 `X-Forwarded-For`,使登录限流读取正确客户端地址;只信任已授权代理。 - `/internal/` 仅向受信任的业务同步系统开放;数据库、Redis、RabbitMQ、Milvus、推理服务和 `/metrics` 保持内部访问。 使用同一 Linux 宿主机上的 Nginx 时,将构建产物放入 `/srv/zhifu/frontend`,证书和私钥放入受保护目录。在 Nginx 的 `http` 配置范围内加入以下配置,替换域名、证书路径及上游端口: ```nginx server { listen 80; server_name service.example.com; return 308 https://service.example.com$request_uri; } server { listen 443 ssl; server_name service.example.com; ssl_certificate /etc/nginx/certs/zhifu-fullchain.pem; ssl_certificate_key /etc/nginx/certs/zhifu-key.pem; root /srv/zhifu/frontend; index index.html; location ~ ^/(openapi|admin|channel)/ { proxy_pass http://127.0.0.1:28000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Connection ""; proxy_buffering off; proxy_cache off; proxy_read_timeout 75s; proxy_send_timeout 75s; } location / { try_files $uri $uri/ =404; } } ``` 此示例的 Nginx 直接接收浏览器请求;存在其他代理时,按受信网络配置客户端地址传递。渠道发送方直接使用 HTTPS 地址。通过 `FORWARDED_ALLOW_IPS` 向 API 容器配置受信代理的实际来源地址。证书、静态目录与配置准备完成后,在目标 Linux 主机检查并加载配置: ```bash sudo nginx -t sudo systemctl reload nginx ``` ### 发布顺序与验收 1. 固定发布镜像、前端产物、模型版本和运行配置,验证备份及恢复资料,审查迁移与已有应用的兼容性。 2. 更新部署前关闭业务写入入口,等待已有请求结束,停止五类工作进程。首次部署从数据库迁移开始。 3. 执行两套迁移,初始化或加载知识集合,启动 API、OMS、exporter 与 BGE,执行 `release-check`。 4. 完成首次账户、业务数据、凭据、规则、模型及预算配置;启动五类工作进程并检查有效心跳。 5. 发布前端和 HTTPS 代理,在目标环境验证三个入口登录、权限范围、订单读取、知识检索、会话 SSE、客户确认、主管审批、OMS 履约与渠道通知。 6. 模型验收使用已授权的小样本,设置请求次数、输出 token 和执行时间上限,记录全部尝试、usage 与费用。发布检查通过后仍需独立确认生成模型和外部业务调用结果。 7. 开放流量,检查错误率、429、任务等待、Outbox 积压、未知结果及费用预占,确认运行状态稳定。 更新应用时可复用本机部署中的迁移、启动和检查命令。恢复旧应用镜像前,确认其支持当前数据库版本;数据库恢复使用已验证备份和对应恢复流程。 ### 多个 API 副本 `compose.scale.yaml` 取消单个 API 的宿主机端口,通过 Docker DNS 将副本加入 HAProxy,入口端口为 `28080`,指标端口为 `28404`: ```powershell docker compose -f compose.runtime.yaml run --rm migrate python -m zhifu.operations.scale_capacity --replicas 2 --pool-size 8 --max-overflow 2 docker compose -f compose.runtime.yaml -f compose.scale.yaml config --quiet docker compose -f compose.runtime.yaml -f compose.scale.yaml up -d --wait --scale api=2 api gateway ``` 连接预算检查通过后执行扩容,检查参数与部署中的连接池配置保持一致。前端代理随后使用 `127.0.0.1:28080`。后续管理这一部署时继续同时传入两个 Compose 文件。每个 API 容器使用一个 Uvicorn 进程,网关最多发现 16 个 API 副本。连接预算要计算全部 API、工作进程、OMS、exporter 与部署任务;增加 API 副本不会自动增加供应商模型额度或 BGE 处理能力。 全部副本保持一致的 SSE 配置。多副本示例的全局 SSE 上限为 3000、每账户上限为 3,每个 API 的基础连接池为 8、额外连接上限为 2,均可通过 Compose 变量调整。Prometheus 应分别采集各个副本,不能只采集负载均衡入口。 ## 宿主机开发 需要调试 Python 代码时,基础服务继续使用 `compose.yaml`,应用进程由宿主机 uv 启动。完成 `.env`、`.env.oms`、`.env.agent`、模型缓存与凭据配置后执行: ```powershell uv sync --locked docker compose up -d --wait uv run alembic upgrade head uv run alembic -c oms-alembic.ini upgrade head uv run python -m zhifu.modules.knowledge.vector ``` 下列常驻命令分别在独立终端执行: ```powershell uv run uvicorn zhifu.modules.knowledge.inference:app --host 127.0.0.1 --port 8200 uv run uvicorn zhifu.oms.api:create_app --factory --host 127.0.0.1 --port 8100 uv run uvicorn zhifu.app:create_app --factory --host 127.0.0.1 --port 18000 uv run uvicorn zhifu.monitoring.api:app --host 127.0.0.1 --port 8300 ``` 工作进程也分别使用独立终端,API、Agent 和记忆进程需要读取有效模型密钥: ```powershell uv run python -m zhifu.modules.agent_runtime.worker uv run python -m zhifu.modules.memory.worker uv run python -m zhifu.modules.integrations.worker uv run python -m zhifu.modules.knowledge.maintenance uv run python -m zhifu.modules.platform.delivery ``` Windows 开发使用上述模块入口;容器使用 `zhifu.operations.worker` 统一处理停止信号。前端 `API_TARGET` 设置为 `http://127.0.0.1:18000`。同一数据库由选定的宿主机或容器工作进程负责处理,调试时确认实际运行的进程数量。 宿主机完整发布检查可以指定实际地址: ```powershell uv run python -m zhifu.operations.release_check --api-url http://127.0.0.1:18000 --oms-url http://127.0.0.1:8100 ``` ## 检查与测试 代码、依赖和接口定义检查: ```powershell uv lock --check uv run ruff check . uv run ruff format --check . uv run python scripts/export_openapi.py --check uv run alembic check uv run alembic -c oms-alembic.ini check ``` 后端默认测试连接实际服务并创建业务数据,使用独立验收环境执行。停止宿主机与容器中的五类常驻工作进程,完成两套迁移,并保持平台 API `18000`、OMS `8100`、BGE `8200` 和基础依赖运行: ```powershell docker compose --profile fault-tests up -d toxiproxy uv run pytest -q ``` 前端使用开发账户和真实服务执行浏览器功能测试。在 `frontend` 目录中安装浏览器并运行: ```powershell npx playwright install chromium $env:PLAYWRIGHT_CHROMIUM_EXECUTABLE = node --input-type=module -e "import { chromium } from 'playwright'; console.log(chromium.executablePath())" $env:FRONTEND_URL = 'http://127.0.0.1:18762' npm run test:functional npm run test:responsive ``` 测试前准备开发账户,并更新示例订单观察时间;涉及同一账户或会话的测试顺序执行。生成模型专项、容量测试和会改变进程状态的恢复演练按对应脚本设置数量与时间上限,在独立环境执行。测试结束后恢复工作进程。 FastAPI 在 `development` 和 `test` 环境通过平台 `/docs` 和 `/openapi.json` 提供接口定义,`production` 环境关闭这两个入口;业务路径 `/openapi/v1` 继续可用。仓库同时保存四份 OpenAPI,修改接口后执行 `uv run python scripts/export_openapi.py`,再运行 `--check` 验证同步。 ## 日常运行与恢复 ### 查看状态和停止服务 ```powershell docker compose -f compose.runtime.yaml ps docker compose -f compose.runtime.yaml logs --tail 100 api oms operations docker compose -f compose.runtime.yaml stop agent memory fulfillment knowledge channel-delivery docker compose -f compose.runtime.yaml stop api oms operations inference docker compose stop ``` 容器设置 75 秒停止等待,工作进程正常退出时释放连接、事务资源并删除心跳。业务任务通过租约、版本和原操作标识恢复处理。停止服务保留数据卷,日常停机使用 `stop`。 ### 备份与恢复验证 平台 MySQL 保存业务事实、权限、费用、会话和 Outbox;OMS MySQL 保存独立订单与履约;持久 Redis 保存记忆与协调状态;Milvus、etcd 和 MinIO 保存知识索引。镜像、模型版本、配置与凭据也需要纳入恢复资料。 仓库提供的 MySQL 与 Redis 备份工具面向本机 `compose.yaml`,使用宿主机开发环境执行: ```powershell uv run python scripts/mysql_recovery.py backup uv run python scripts/redis_recovery.py backup $mysqlBackup = Read-Host '输入 MySQL 备份命令返回的目录' $redisBackup = Read-Host '输入 Redis 备份命令返回的目录' uv run python scripts/mysql_recovery.py verify --directory $mysqlBackup uv run python scripts/redis_recovery.py verify --directory $redisBackup ``` 备份分别保存在 `.work/backups/` 和 `.work/redis-backups/`,包含 SHA256 清单。验证使用独立恢复容器,要求保留源镜像;验证完成后删除验证容器。生产数据库使用对应环境的备份、binlog 与恢复工具,并设置加密、独立存储、访问审计和保留期限。 完整业务恢复需要关闭写入和外部调用、停止五类工作进程,选择相容的 MySQL 与 Redis 数据时间,核对授权、记忆引用、Outbox、消费收据和未完成外部操作。每个组件的独立快照只保证其自身范围的一致性。 消息核对使用 `zhifu.operations.message_recovery inspect`,按租户和事件 UUID 查询收据与发送状态;确认需要重放时使用 `replay`,保留原事件标识并登记操作人和原因。知识索引使用 `zhifu.modules.knowledge.recovery`,从当前发布资料重建新的 `zhifu_recovery_` 集合,设置文档数、分段数和总运行时间上限,验证成功后配置目标集合并重新启动相关进程。 ### 常见启动问题 | 现象 | 检查内容 | | --- | --- | | Compose 提示缺少参数或 external 网络 | 检查 `ZHIFU_RUNTIME_ENV`、`ZHIFU_MODEL_PATH` 的绝对路径,以及基础网络是否已创建 | | 运行容器访问数据库失败 | 检查容器环境文件中的服务地址、凭据和数据库名称;本机 `127.0.0.1` 地址只适用于宿主机进程 | | BGE 容器启动失败 | 检查固定 revision 的模型缓存完整性、UID `10001` 读取权限和可用内存 | | 发布检查失败 | 按失败项检查两套迁移、Redis、RabbitMQ、Milvus 集合、BGE、API、OMS 与模型凭据 | | 登录后没有菜单或查询返回 403 | 检查账户类型、租户/店铺范围和具体权限,平台使用独立账户 | | 会话持续等待 | 检查 Agent 心跳、队列、并发额度、token/金额预算、模型配置,以及运行是否等待客户或主管操作 | | 换货审批后无法履约 | 检查 OMS 连接器地址与凭据、订单同步时间、`fulfillment` 心跳和外部操作状态 | | 页面请求失败或 SSE 消息延迟 | 检查 API 转发、状态码、SSE 缓冲与超时,以及 `Authorization` 和 `Last-Event-ID` 是否被保留 | | Prometheus 采集失败 | 检查采集地址是否对应容器或宿主机部署方式,并清理已停用服务的发现配置 |