# 使用指南 - saas-search(运行与运维) 本文档聚焦运行与运维:服务启动/停止、日志查看、多环境切换、故障排查、Suggestion 索引运维。 开发与配置(环境入口、配置体系、Provider 架构、模块扩展规范)请优先阅读 `docs/QUICKSTART.md`。 ## 目录 1. [环境准备](#环境准备) 2. [服务启动](#服务启动) 3. [服务管理总览](#全盘串讲服务管理方式) 4. [配置说明](#配置说明) 5. [查看日志](#查看日志) 6. [测试验证](#测试验证) 7. [常见问题](#常见问题) --- ## 环境准备 ### 系统要求 - **操作系统**: Linux (推荐 CentOS 7+ / Ubuntu 18.04+) - **Python**: 3.8+ - **内存**: 建议 8GB+ - **磁盘**: 10GB+ (包含模型文件) - **Elasticsearch**: 8.x (可通过Docker运行) ### 安装依赖 #### 1. 安装 Python 依赖与激活环境 **推荐**:使用项目根目录的 `activate.sh` 激活环境(会加载 `.env`)。当前仅支持 venv(`.venv`),不存在 Conda 回退。系统要求、Python 环境、生产凭证与 `.env` 模板见 [QUICKSTART.md](./QUICKSTART.md) §1.4–1.8。 ```bash cd /data/saas-search ./scripts/create_venv.sh # 首次创建 venv(只需执行一次) source activate.sh ``` 模型服务采用独立环境(推荐): ```bash cd /data/saas-search ./scripts/setup_embedding_venv.sh # .venv-embedding ./scripts/setup_reranker_venv.sh # .venv-reranker ./scripts/setup_cnclip_venv.sh # .venv-cnclip ``` TEI 文本向量服务使用 Docker 容器: ```bash # GPU(需 nvidia-container-toolkit) TEI_DEVICE=cuda ./scripts/start_tei_service.sh # CPU TEI_DEVICE=cpu ./scripts/start_tei_service.sh ``` 专项说明: - `docs/TEI_SERVICE说明文档.md` - `docs/CNCLIP_SERVICE说明文档.md` #### 2. 启动Elasticsearch **方式1: 使用Docker(推荐)** ```bash docker run -d \ --name elasticsearch \ -p 9200:9200 \ -e "discovery.type=single-node" \ -e "ES_JAVA_OPTS=-Xms2g -Xmx2g" \ elasticsearch:8.11.0 ``` **方式2: 本地安装** 参考 [Elasticsearch官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/8.11/install-elasticsearch.html) #### 3. 配置环境变量(支持多环境) 创建 `.env` 文件(以本地 / prod 环境为例): ```bash # MySQL配置 DB_HOST=120.79.247.228 DB_PORT=3316 DB_DATABASE=saas DB_USERNAME=saas DB_PASSWORD=your_password # Elasticsearch配置 ES_HOST=http://localhost:9200 ES_USERNAME=essa ES_PASSWORD=your_es_password # Redis配置(可选,用于缓存) REDIS_HOST=localhost REDIS_PORT=6479 REDIS_PASSWORD=your_redis_password # DeepL翻译API(可选) DEEPL_AUTH_KEY=your_deepl_auth_key # 运行环境(用于区分 prod / uat / test / dev) RUNTIME_ENV=prod # ES 索引命名空间前缀(用于按环境隔离索引,prod 通常留空) ES_INDEX_NAMESPACE= # API服务配置 API_HOST=0.0.0.0 API_PORT=6002 ``` --- ## 服务启动 ### 方式1: 一键启动(推荐) ```bash cd /data/saas-search ./run.sh all ``` 这个脚本会自动: 1. 创建日志目录 2. 按目标启动服务(`all`:`tei cnclip embedding embedding-image translator reranker reranker-fine backend indexer frontend eval-web`) 3. 写入 PID 到 `logs/*.pid` 4. 执行健康检查 5. 启动 monitor daemon(运行期连续失败自动重启) 启动完成后,访问: - **前端界面**: http://localhost:6003 - **搜索评估 UI**: http://localhost:6010(`eval-web`,依赖 backend 提供实时搜索) - **后端API**: http://localhost:6002 - **API文档**: http://localhost:6002/docs - **索引API**: http://localhost:6004/docs 仅启动部分服务示例:`./run.sh backend indexer frontend` ### 方式2: 统一控制脚本(推荐) ```bash # 查看状态 ./scripts/service_ctl.sh status # 推荐:一键启动 + 监控守护 ./scripts/service_ctl.sh up all # 启动指定服务 ./scripts/service_ctl.sh up backend indexer frontend # 启动指定服务(不自动拉起 monitor daemon) ./scripts/service_ctl.sh start backend indexer frontend translator reranker tei cnclip # 停止全部服务(含 monitor daemon) ./scripts/service_ctl.sh down all # 重启全部服务 ./scripts/service_ctl.sh restart all ``` 补充说明: - `./scripts/service_ctl.sh` 可以直接使用,不要求你手动先 `source ./activate.sh`;因为各个 `start_*.sh` 会自行加载项目虚拟环境。 - 但如果你要运行 `pytest`、评估脚本或临时 Python 脚本,建议先执行 `source ./activate.sh`。 ### 方式3: 分步启动(单环境) #### 启动后端服务 ```bash ./scripts/start_backend.sh ``` 后端API会在 http://localhost:6002 启动(使用当前 `.env` 中的 ES_HOST / DB_HOST / RUNTIME_ENV / ES_INDEX_NAMESPACE) 等价的手动命令是: ```bash cd /data/saas-search source ./activate.sh python main.py serve --host "${API_HOST:-0.0.0.0}" --port "${API_PORT:-6002}" --es-host "${ES_HOST:-http://localhost:9200}" ``` #### 启动前端服务 ```bash ./scripts/start_frontend.sh ``` 前端界面会在 http://localhost:6003 启动 ## 本地测试与检索效果评估 ```bash cd /data/saas-search source ./activate.sh python -m pytest -q tests/test_rerank_client.py tests/test_es_query_builder.py tests/test_search_rerank_window.py tests/test_query_parser_mixed_language.py ./scripts/service_ctl.sh restart backend sleep 3 ./scripts/service_ctl.sh status backend python ./scripts/eval_search_quality.py ``` 评估结果会输出到 `artifacts/search_eval/`,包含: - `search_eval_*.json`:便于脚本二次分析 - `search_eval_*.md`:便于人工浏览 top20 结果、分数与命中子句 ### 方式4: 多环境示例(prod / uat) 假设有两套环境: - **prod**:正式环境,RUNTIME_ENV=prod,ES_INDEX_NAMESPACE 为空 - **uat**:联调环境,RUNTIME_ENV=uat,ES_INDEX_NAMESPACE=uat_ 可以在项目根目录准备两个配置文件: - `.env.prod`: ```bash RUNTIME_ENV=prod ES_INDEX_NAMESPACE= ES_HOST=http://prod-es:9200 DB_HOST=prod-mysql ... ``` - `.env.uat`: ```bash RUNTIME_ENV=uat ES_INDEX_NAMESPACE=uat_ ES_HOST=http://uat-es:9200 DB_HOST=uat-mysql ... ``` 启动不同环境时: ```bash # 启动 UAT 后端 + 索引服务 cp .env.uat .env ./scripts/start_backend.sh ./scripts/start_indexer.sh # 启动 PROD 后端 + 索引服务 cp .env.prod .env ./scripts/start_backend.sh ./scripts/start_indexer.sh ``` ### 方式5: 手动启动 #### 启动后端API服务 ```bash python -m api.app \ --host 0.0.0.0 \ --port 6002 \ --es-host http://localhost:9200 \ --reload ``` #### 启动前端服务(可选) ```bash # 使用Python简单HTTP服务器 cd frontend python -m http.server 6003 ``` ## 服务管理总览 ### 1) 入口脚本职责 - `./run.sh [all|service...]`:薄封装,直接调用 `./scripts/service_ctl.sh up [all|service...]`。 - `./restart.sh [all|service...]`:薄封装,直接调用 `./scripts/service_ctl.sh restart [all|service...]`。 - `./scripts/stop.sh [all|service...]`:薄封装,直接调用 `./scripts/service_ctl.sh down [all|service...]`。 - `./status.sh`:薄封装,直接调用 `./scripts/service_ctl.sh status`(包含进程/端口 + HTTP 健康检查结果)。 - `./scripts/service_ctl.sh`:统一控制器,支持 `up/down/start/stop/restart/status/monitor*`(带参数行为完全由此脚本定义)。 ### 2) `service_ctl.sh` 的默认行为 - `up`:**必须显式指定** 服务或 `all`,并自动启动 monitor daemon。 - `down`:**必须显式指定** 服务或 `all`,并停止 monitor daemon。 - `start`:**必须显式指定** 服务或 `all`(不自动拉起 monitor daemon)。 - `stop`:**必须显式指定** 服务或 `all`;若 monitor daemon 运行会先停止它。 - `restart`:**必须显式指定** 服务或 `all`。 - `status`(不带服务名):显示全部已知服务状态 + monitor daemon 状态,并对支持的服务执行 HTTP/容器级健康检查。 ### 3) 全量服务一键拉起 ```bash ./scripts/service_ctl.sh up all ``` 说明: - `TEI_DEVICE` / `CNCLIP_DEVICE` 统一使用 `cuda|cpu`。 - `all` 内部已按依赖顺序处理(先 `tei/cnclip` 再 `embedding`)。 ### 4) 常用运维命令 ```bash # 一键拉起完整栈(推荐) ./scripts/service_ctl.sh up all # 或 ./run.sh all # 查看全量状态 + 健康检查(进程/端口 + HTTP) ./scripts/service_ctl.sh status # 仅重启某个服务 ./scripts/service_ctl.sh restart embedding # 停止全部 ./scripts/service_ctl.sh down all # 或 ./scripts/stop.sh all ``` ### 停止服务 ```bash # 推荐:统一停止(示例:全部) ./scripts/stop.sh all # 或使用统一控制脚本 ./scripts/service_ctl.sh down all ``` ### 服务端口 | 服务 | 端口 | URL | |------|------|-----| | Elasticsearch | 9200 | http://localhost:9200 | | Backend API | 6002 | http://localhost:6002 | | Indexer API | 6004 | http://localhost:6004 | | Frontend Web | 6003 | http://localhost:6003 | | Search eval web (`eval-web`) | 6010 | http://localhost:6010 | | Embedding Text (optional) | 6005 | http://localhost:6005 | | Embedding Image (optional) | 6008 | http://localhost:6008 | | TEI (optional) | 8080 | http://localhost:8080 | | Translation (optional) | 6006 | http://localhost:6006 | | Reranker (optional) | 6007 | http://localhost:6007 | | API Docs | 6002 / 6004 | http://localhost:6002/docs / http://localhost:6004/docs | --- ## 配置说明 ### 环境配置文件 (.env) 主要配置项说明: ```bash # Elasticsearch配置 ES_HOST=http://localhost:9200 ES_USERNAME=essa ES_PASSWORD=your_es_password # MySQL配置 DB_HOST=120.79.247.228 DB_PORT=3316 DB_DATABASE=saas DB_USERNAME=saas DB_PASSWORD=your_password # Redis配置(可选,用于缓存) REDIS_HOST=localhost REDIS_PORT=6479 REDIS_PASSWORD=your_redis_password # DeepL翻译API DEEPL_AUTH_KEY=your_deepl_auth_key # API服务配置 API_HOST=0.0.0.0 API_PORT=6002 # Indexer服务配置 INDEXER_HOST=0.0.0.0 INDEXER_PORT=6004 # Optional service ports FRONTEND_PORT=6003 EVAL_WEB_PORT=6010 EMBEDDING_TEXT_PORT=6005 EMBEDDING_IMAGE_PORT=6008 TEI_PORT=8080 CNCLIP_PORT=51000 TRANSLATION_PORT=6006 RERANKER_PORT=6007 ``` ### 修改配置 1. 编辑 `.env` 文件 2. 重启相关服务 --- ## 查看日志 ### 日志文件位置 日志文件存储在 `logs/` 目录下: - `logs/-YYYY-MM-DD.log` - `service_ctl.sh` 统一管理的按天日志文件(真实文件) - `logs/.log` - 指向当天日志文件的软链(兼容原有命令) - `logs/api.log` - backend 进程内日志(按天轮转) - `logs/backend_verbose.log` - backend 详细大对象日志(按天轮转,含 `Search response` / `ES query built` 完整内容) - `logs/indexer.log` - 索引结构化日志(JSON,按天轮转) ### 查看实时日志 ```bash # 查看后端日志(建议 -F,跨天自动跟随新文件) tail -F logs/backend.log # 查看前端日志 tail -F logs/frontend.log # 查看 backend 详细大对象日志 tail -F logs/backend_verbose.log ``` ### 日志级别 日志级别可以通过环境变量 `LOG_LEVEL` 设置: ```bash # 在 .env 文件中设置 LOG_LEVEL=DEBUG # DEBUG, INFO, WARNING, ERROR, CRITICAL ``` ### 日志轮转 日志文件按天自动轮转,默认保留 30 天历史日志(可通过 `LOG_RETENTION_DAYS` 调整 `service_ctl.sh` 管理日志的保留天数)。 --- ## 测试验证 ### 1. 健康检查 ```bash curl http://localhost:6002/admin/health ``` **预期响应**: ```json { "status": "healthy", "elasticsearch": "connected" } ``` ### 2. 索引统计 ```bash curl http://localhost:6002/admin/stats -H "X-Tenant-ID: 162" ``` ### 3. 简单搜索测试 ```bash curl -X POST http://localhost:6002/search/ \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: 162" \ -d '{ "query": "玩具", "size": 10 }' ``` 或者通过查询参数: ```bash curl -X POST "http://localhost:6002/search/?tenant_id=2" \ -H "Content-Type: application/json" \ -d '{ "query": "玩具", "size": 10 }' ``` ### 4. 带过滤器的搜索 ```bash curl -X POST http://localhost:6002/search/ \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: 162" \ -d '{ "query": "玩具", "size": 10, "filters": { "category.keyword": ["玩具", "益智玩具"] }, "range_filters": { "price": {"gte": 50, "lte": 200} } }' ``` ### 5. 分面搜索测试 ```bash curl -X POST http://localhost:6002/search/ \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: 162" \ -d '{ "query": "玩具", "size": 10, "facets": [ {"field": "category.keyword", "size": 15}, {"field": "vendor.keyword", "size": 15} ] }' ``` ### 6. 图片搜索测试 ```bash curl -X POST http://localhost:6002/search/image \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: 162" \ -d '{ "image_url": "https://oss.essa.cn/example.jpg", "size": 10 }' ``` ### 7. 前端界面测试 访问 http://localhost:6003 或 http://localhost:6002/ 进行可视化测试。 **注意**: 所有搜索接口都需要通过 `X-Tenant-ID` 请求头或 `tenant_id` 查询参数指定租户ID。 --- ## 8. Suggestion 索引与接口使用 ### 8.1 构建 Suggestion 索引(Phase 2:全量 + 增量) Suggestion 索引会从: - ES 商品索引:`title.{lang}`, `qanchors.{lang}` - MySQL 日志表:`shoplazza_search_log.query`(含 `language`、`request_params`) 聚合生成版本化索引并发布 alias: - 物理索引:`{ES_INDEX_NAMESPACE}search_suggestions_tenant_{tenant_id}_v` - 读别名:`{ES_INDEX_NAMESPACE}search_suggestions_tenant_{tenant_id}_current` 在项目根目录执行(以 tenant_id=162 为例): ```bash # 1) 全量重建(版本化 + alias 发布) ./scripts/build_suggestions.sh 162 \ --mode full \ --days 30 \ --publish-alias \ --keep-versions 2 # 2) 增量更新(watermark + overlap) ./scripts/build_suggestions.sh 162 \ --mode incremental \ --overlap-minutes 30 ``` 可选参数: - `--days`:回溯日志天数(默认 30) - `--batch-size`:扫描商品索引的批大小(默认 500) - `--min-query-len`:参与 suggestion 的最小查询长度(默认 1) - `--overlap-minutes`:增量窗口重叠分钟数(默认 30) - `--bootstrap-if-missing`:增量模式下若缺少 active index 则自动全量初始化(默认 true) > 建议在商品索引构建完成、日志正常写入一段时间后执行一次全量构建,然后按天/小时增加增量构建任务。 ### 8.2 调用 Suggestion 接口 全量构建完成后,可直接通过 `/search/suggestions` 获取自动补全结果: ```bash # UAT 环境(本地或 UAT 集群) curl "http://localhost:6002/search/suggestions?q=iph&size=5&language=en" \ -H "X-Tenant-ID: 162" # PROD 环境(域名 / 端口按实际部署调整) curl "https://api.yourdomain.com/search/suggestions?q=iph&size=5&language=en" \ -H "X-Tenant-ID: 162" ``` ### 8.3 商品索引构建(search_products,多环境例子) 以 tenant_id=162 为例,分别在 UAT / PROD 构建商品索引。 #### UAT 环境(索引前缀 uat_) ```bash cd /data/saas-search # 1. 切换到 UAT 环境配置 cp .env.uat .env # 2. 启动 indexer 服务(如尚未启动) ./scripts/start_indexer.sh # 3. 可选:重建索引结构(会删除旧索引) ./scripts/create_tenant_index.sh 162 # 实际创建的索引名为:uat_search_products_tenant_162 # 4. 导入商品数据(全量索引) curl -X POST "http://localhost:6004/indexer/reindex" \ -H "Content-Type: application/json" \ -d '{ "tenant_id": "162", "batch_size": 500 }' ``` #### PROD 环境(无前缀) ```bash cd /data/saas-search # 1. 切换到 PROD 环境配置 cp .env.prod .env # 2. 启动 indexer 服务 ./scripts/start_indexer.sh # 3. 可选:重建索引结构(search_products_tenant_162) ./scripts/create_tenant_index.sh 162 # 4. 导入商品数据 curl -X POST "http://localhost:6004/indexer/reindex" \ -H "Content-Type: application/json" \ -d '{ "tenant_id": "162", "batch_size": 500 }' ``` 完成后: - UAT 环境商品索引:`uat_search_products_tenant_162` - PROD 环境商品索引:`search_products_tenant_162` 两套环境的搜索 / suggestion API 调用完全一致,只是连接到各自的后端 / ES。 接口返回结构详见 `docs/搜索API对接指南.md` 的“3.7 搜索建议接口”章节。 --- ## 常见问题 ### Q1: MySQL连接失败 **症状**: `Failed to connect to MySQL` **解决方案**: ```bash # 检查MySQL服务状态 mysql -h 120.79.247.228 -P 3316 -u saas -p -e "SELECT 1" # 检查配置 cat .env | grep DB_ ``` ### Q2: Elasticsearch连接失败 **症状**: `Failed to connect to Elasticsearch` **解决方案**: ```bash # 检查ES服务状态 curl http://localhost:9200 # 检查ES版本 curl http://localhost:9200 | grep version # 确认配置 cat .env | grep ES_ ``` ### Q3: 服务启动失败 **症状**: `Address already in use` 或端口被占用 **解决方案**: ```bash # 查看占用端口的进程 lsof -i :6002 # 后端 lsof -i :6003 # 前端 lsof -i :6010 # 搜索评估 UI(eval-web) lsof -i :9200 # ES # 杀掉进程 kill -9 # 或修改端口配置 ``` ### Q4: 搜索无结果 **症状**: 搜索返回空结果 **解决方案**: ```bash # 检查ES中是否有数据 curl http://localhost:9200/search_products/_count # 检查tenant_id过滤是否正确 curl -X POST http://localhost:6002/search/ \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: 162" \ -d '{"query": "*", "size": 10, "debug": true}' ``` ### Q5: 前端无法连接后端 **症状**: CORS错误 **解决方案**: - 确保后端在 http://localhost:6002 运行 - 检查浏览器控制台错误信息 - 检查后端日志中的CORS配置 ### Q6: 翻译不工作 **症状**: 翻译返回原文 **解决方案**: - 检查DEEPL_AUTH_KEY是否正确 - 如果没有API key,系统会使用mock模式(返回原文) --- ## 相关文档 - **测试数据构造文档**: `TEST_DATA_GUIDE.md` - 如何构造和导入测试数据 - **API接口文档**: `API_INTEGRATION_GUIDE.md` - 完整的API对接指南 - **字段说明文档**: `INDEX_FIELDS_DOCUMENTATION.md` - 索引字段详细说明 - **设计文档**: `设计文档.md` - 系统架构和设计说明 - **README**: `README.md` - 项目概述和快速开始 --- **文档版本**: v2.0 **最后更新**: 2024-12