# MindTrip **Repository Path**: rainyjensen/mind-trip ## Basic Information - **Project Name**: MindTrip - **Description**: No description available - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-26 - **Last Updated**: 2026-09-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MindTrip (AIzhilv) — 可验证的结构化旅行规划 Agent > 基于 FastAPI、LangGraph、Pydantic 与独立 LlamaIndex RAG 服务的旅行规划系统,支持来源引用、行程可行性校验、异步任务和可选多模态解析。 MindTrip 将在线生成链路拆分为“检索—规划—验证”三个阶段。LangGraph 负责编排,Pydantic 定义稳定的行程契约,确定性校验器负责引用、时间、交通、营业时间、费用和预算检查;RAG 服务独立部署并通过版本化 HTTP 契约提供证据。 ## 核心亮点 - **结构化行程**:输出日程、活动、费用、引用和警告,不以自由格式 Markdown 作为主要数据契约。 - **证据可追踪**:模型只能引用 RAG 返回的 `source_id`;未知引用会被移除,缺少证据的活动标记为 `needs-confirmation`。 - **确定性可行性检查**:校验时间排序、交通间隔、营业时间、活动密度、多人费用和总额/人均预算。 - **独立 RAG 服务**:LlamaIndex + BGE-M3,索引按语料哈希、嵌入模型和 schema 版本标识,并支持锁、原子切换、预热和 TTL 缓存。 - **可观察异步任务**:长耗时请求支持提交、轮询和取消,每个响应携带 `X-Request-Id`。 - **可选多模态边界**:图片能力默认关闭,只接受经过类型与大小校验的 JPEG、PNG 和 WebP。 - **自动化质量门禁**:当前测试套件包含 72 项测试,Gitee Go 执行 pytest 与限定范围 Ruff 检查。 ## 在线架构 ```text UniApp / API Client │ ▼ FastAPI 主服务 ───────────────► MySQL │ TripRequest ▼ LangGraph: retrieve → plan → validate │ │ │ HTTP └── Pydantic + 确定性行程校验 ▼ 独立 RAG 服务 │ ├── LlamaIndex + BGE-M3 ├── 版本化持久索引 + TTL 缓存 └── source_id / score / index_version LLM:LangChain OpenAI-compatible adapter → vLLM / DeepSeek / 其他兼容服务 ``` 详细设计见 [类型化行程工作流](docs/features/typed-trip-workflow.md)、[独立 RAG 服务](docs/features/independent-rag-service.md) 和 [部署文档](docs/DEPLOYMENT.md)。 ## 项目结构 ```text MindTrip/ ├── AIzhilv/ │ ├── backend/ # FastAPI 后端 │ │ ├── app/ │ │ │ ├── agent/ # LangGraph 工作流与确定性校验 │ │ │ ├── api/ # 行程、异步任务、多模态及兼容路由 │ │ │ ├── db/ # 数据库连接(sql_con.py) │ │ │ ├── schemas/ # Pydantic 数据模型 │ │ │ ├── services/ # RAG 客户端与任务服务 │ │ │ └── utils/ # 工具函数 │ │ └── tests/ # 72 项 pytest 回归测试 │ ├── rag_service/ # 独立 LlamaIndex 检索服务 │ ├── frontend/ # UniApp 前端(Vue 3) │ │ ├── pages/ # 页面组件 │ │ └── static/ # 静态资源 │ ├── faiss_store/ # FAISS 兼容语料库 │ ├── training/ # 训练 & RAG 构建脚本 │ ├── ingest_faiss.py # 旧 FAISS 索引构建脚本 │ ├── prepare_rag_from_citydata.py │ └── rag_query.py # 旧 RAG 查询兼容脚本 ├── docs/ # 需求、功能设计、进度与部署说明 ├── scripts/init_project.py # 初始化辅助脚本 ├── docker-compose.yml # MySQL + API + RAG 服务 ├── requirements-api*.txt # API 依赖与锁定版本 ├── requirements-rag-service*.txt └── README.md ``` ## 快速开始 ### Windows 一键演示 直接双击项目根目录的: ```text open-mindtrip-as-admin.cmd ``` 启动器会请求管理员权限,检测或通过 Docker Compose 启动 MySQL 8(3306),并启动本地 OpenAI 兼容推理服务(8080)、主 API(8000)、RAG(8010)和 UniApp Web 前端(4173),全部就绪后打开 `http://127.0.0.1:4173`。首次加载约 6.2 GB 的合并模型通常需要 1–3 分钟;首次运行前端时还会自动安装锁定依赖。运行日志和 PID 保存在不提交 Git 的 `.mindtrip-runtime` 目录。 默认 `/chat` 是轻量演示路由:调用独立 RAG 和本地模型,不依赖 MySQL,也不加载旧 FAISS、FlagEmbedding 与数据库会话栈;登录、注册、用户资料等完整业务功能仍需要 MySQL。需要旧聊天实现时才设置 `ENABLE_LEGACY_CHAT=true`。只演示 AI 对话时可执行 `scripts/start-project.ps1 -SkipDatabase`;只演示静态页面时可再加 `-SkipInference -SkipRag`。 ### 手动启动本地推理服务 不通过一键启动器时,推理服务(OpenAI 兼容,默认 8080 端口)可单独启动: ```powershell # 在项目根目录(Windows) .venv\Scripts\python.exe scripts\inference\serve-openai-compatible.py ` --model models\merged\ZhiluAI-3B-SFT-merged --port 8080 ``` - 需要 `torch` + `transformers` + `accelerate`(见 `requirements-inference.txt`);首次加载约 6.2 GB 合并模型需 1–3 分钟 - **无 GPU 的 CPU 机器务必加 `--dtype float32`**:默认 `bfloat16` 在没有 AVX512-BF16 指令集的 CPU 上会被软件模拟,速度慢 10 倍以上(实测 <0.2 token/s vs float32 约 2.5 token/s);float32 约占 12 GB 内存 - 验证:`curl http://127.0.0.1:8080/v1/models` 返回 `ZhiluAI` 即就绪 - 云端替代:有 DeepSeek Key 时在 `.env` 设 `LLM_PROVIDER=deepseek` + `DEEPSEEK_API_KEY=<真实key>`,无需本地推理 **后端跑在 Docker(WSL2)时的接线**:容器内 `127.0.0.1` 指容器自身,访问不到宿主机推理服务;且 WSL2 mirrored 网络模式下 WSL 与 Windows 共享端口空间,宿主机服务需换到别的端口(如 8088),再在 WSL 里做一条转发: ```bash # WSL(root):把容器网关 172.17.0.1:8080 转发到 Windows 的 8088 socat TCP-LISTEN:8080,bind=172.17.0.1,fork,reuseaddr TCP:127.0.0.1:8088 & # .env(栈内副本)相应改为: # VLLM_ENDPOINT=http://172.17.0.1:8080/v1/chat/completions # VLLM_BASE_URL=http://172.17.0.1:8080/v1 # CPU 推理较慢,建议同时设 LLM_TIMEOUT_SECONDS=1500 ``` ### 0. 一键初始化(推荐) ```bash python scripts/init_project.py ``` 自动完成:复制 `.env` 模板 → 导入 `aizhilv.sql` → 下载城市数据 → 构建 RAG 语料 → 构建 FAISS 索引。 可用 `--skip-db` / `--skip-rag` / `--with-tests` 控制步骤。也可以按下面 1–5 步手动执行。 > 初始化会连接 MySQL、下载数据并写入本地索引,执行前请确认数据库配置、网络、磁盘空间和数据许可。只想先检查 API 环境时可使用 `--skip-db --skip-rag --with-tests`。 ### 1. 环境要求 - Python 3.10+ - MySQL 8.0+ - Node.js 16+(前端开发) - HBuilderX 或 CLI(UniApp 编译) ### 2. 安装后端依赖 环境管理统一使用 [uv](https://docs.astral.sh/uv/)(比 pip 快 10 倍以上): ```bash # 安装 uv(已装可跳过):powershell -c "irm https://astral.sh/uv/install.ps1 | iex" uv venv .venv --python 3.10 # Windows PowerShell .\.venv\Scripts\Activate.ps1 uv pip install -r requirements-dev.txt ``` 完整 RAG 服务另外安装: ```bash uv pip install -r requirements-rag-service.txt ``` 生产复现可分别使用 `requirements-api-lock.txt`、`requirements-rag-service-lock.txt`;旧的完整单进程环境使用 `requirements-lock.txt`。 ## 可复现实验流水线 实验配置、模型来源、HyperAI 数据角色、QLoRA 参数、RAG 参数和基准协议统一保存在 [`configs/experiments/qwen25-3b-lora.json`](configs/experiments/qwen25-3b-lora.json)。Windows PowerShell 通过一个入口调用 WSL2 Ubuntu: ```powershell # 只读预检并创建实验账本 powershell -ExecutionPolicy Bypass -File scripts/run-experiment.ps1 -Stage preflight # 预览安装、下载或运行命令,不产生对应副作用 powershell -ExecutionPolicy Bypass -File scripts/run-experiment.ps1 -Stage bootstrap -DryRun ``` 预检会生成本地且不入 Git 的目录: ```text experiments// ├── config.snapshot.json ├── environment.json ├── experiment-ledger.jsonl ├── stage-state.json ├── chat-sync-summary.md └── logs/ ``` 环境快照记录 Git commit、GPU、驱动、Python、工具状态和配置 SHA-256;日志会递归隐藏 token、password、secret、authorization 和 API key。`chat-sync-summary.md` 是与当前对话同步实验进度的增量摘要,用户要求检查实验状态时由助手读取并汇报。 首次安装 WSL 工具和实验虚拟环境需要本人输入 Linux sudo 密码: ```powershell powershell -ExecutionPolicy Bypass -File scripts/run-experiment.ps1 -Stage bootstrap ``` 模型下载阶段优先使用 ModelScope,失败后切换 Hugging Face,并把实际来源写入账本: ```powershell powershell -ExecutionPolicy Bypass -File scripts/run-experiment.ps1 -Stage download-models ``` HyperAI 下载如需 `.torrent`,先把种子放到配置指定的 `data/manifests/torrents/`;随后运行: ```powershell powershell -ExecutionPolicy Bypass -File scripts/run-experiment.ps1 -Stage acquire-data ``` 完整需求和阶段边界见 [实验流水线需求](docs/requirements/002-reproducible-ml-experiments.md) 与 [设计说明](docs/features/reproducible-ml-experiments.md)。模型、数据、TensorBoard、TensorRT 引擎和原始实验日志默认不提交到代码仓库。 ### 3. 配置环境变量 ```powershell Copy-Item AIzhilv/backend/app/api/.env.example AIzhilv/backend/app/api/.env ``` 编辑 `app/api/.env`,填入实际的 API Key、数据库密码等: | 变量 | 说明 | |------|------| | `SENIVERSE_KEY` | 心知天气 API Key | | `DEEPSEEK_API_KEY` | DeepSeek LLM API Key | | `VLLM_BASE_URL` | 新行程工作流使用的 OpenAI 兼容地址(如 `http://127.0.0.1:8080/v1`) | | `VLLM_ENDPOINT` | 旧聊天兼容层使用的完整推理端点 | | `VLLM_MODEL_NAME` | vLLM 加载的模型名 | | `DB_HOST` / `DB_PORT` | MySQL 地址 | | `DB_USER` / `DB_PASSWORD` | MySQL 账号密码 | | `DB_NAME` | 数据库名 | | `CORS_ORIGINS` | 允许的前端来源(逗号分隔) | | `API_KEY` | 后端 API 鉴权密钥(留空或 `your_api_key_here` 则关闭鉴权) | ### 4. 启动独立 RAG 服务 建议先预热索引,避免第一位用户等待模型下载和整库编码: ```powershell cd AIzhilv python -m rag_service.prewarm ``` 预热可重复执行;已有持久化索引时会直接加载。 ```powershell cd AIzhilv python -m uvicorn rag_service.main:app --host 0.0.0.0 --port 8010 ``` 如果跳过预热,首次检索会从 `faiss_store/corpus.jsonl` 构建并持久化 LlamaIndex 索引。`GET /health` 的 `index_ready` 字段表示当前进程是否已加载索引。 索引按“语料哈希 + 嵌入模型”版本化并原子切换,进程/线程锁避免并发重复构建。检索回归评测: ```powershell cd AIzhilv python -m rag_service.evaluate --cases rag_service/eval-cases.example.jsonl --minimum-hit-rate 0.5 ``` ### 5. 启动主后端 另开一个终端: ```powershell cd AIzhilv python -m uvicorn backend.app.api.main:app --host 0.0.0.0 --port 8000 --reload ``` 创建一个结构化行程: ```powershell $body = @{ destination = "苏州"; days = 2; preferences = @("园林", "美食") } | ConvertTo-Json Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8000/api/v1/trip-plans -ContentType application/json -Body $body ``` 响应是可供前端、数据库或评测脚本直接消费的 Pydantic 数据结构。下面是精简字段示意,不代表实际模型结果: ```json { "destination": "苏州", "summary": "两日园林与美食行程", "days": [ { "day": 1, "activities": [ { "start_time": "09:00", "end_time": "11:00", "name": "示例景点", "category": "attraction", "estimated_cost": 80, "source_ids": ["source-example"], "verification_status": "verified" } ], "warnings": [] } ], "total_estimated_cost": 80, "sources": [], "warnings": [] } ``` 长耗时请求可改用异步任务接口:先 `POST /api/v1/trip-plan-jobs`,再从响应的 `Location` 轮询;`DELETE` 同一路径可请求取消。每个响应带 `X-Request-Id`,便于串联日志。 多模态菜单、票据和行程截图解析是可选能力,默认关闭。确认模型支持图片后设置 `ENABLE_MULTIMODAL=true`;接口只接收 JPEG、PNG、WebP,限制为 `MULTIMODAL_MAX_BYTES`。 ### 6. 启动前端 Web 演示无需 HBuilderX: ```powershell cd AIzhilv/frontend npm install npm run dev:h5 ``` 浏览器访问 `http://127.0.0.1:4173`。如需小程序、App 或 HBuilderX 内置浏览器调试,再使用 HBuilderX 打开 `AIzhilv/frontend`。 ## RAG 索引构建 ```bash # 1. 下载城市数据 CSV(如果尚未获取) python AIzhilv/training/download_citydata.py # 2. 从 CSV 构建 RAG 语料 python AIzhilv/prepare_rag_from_citydata.py # 3. 构建 FAISS 索引 python AIzhilv/ingest_faiss.py ``` ## 一步一步运行测试 以下命令从仓库根目录执行。 ### 第一步:创建并激活测试环境 ```powershell uv venv .venv --python 3.10 .\.venv\Scripts\Activate.ps1 uv pip install -r requirements-dev.txt ``` 这套轻量环境用于 API、LangGraph 工作流、数据契约和服务契约测试,不会下载 BGE-M3。 ### 第二步:运行全部后端测试 ```powershell python -m pytest AIzhilv/backend/tests -q ``` 当前基线应显示 `72 passed`。如果失败,用 `-vv -x` 显示第一个失败的完整上下文: ```powershell python -m pytest AIzhilv/backend/tests -vv -x ``` ### 第三步:按模块定位问题 ```powershell # LangGraph 工作流和结构化输出 python -m pytest AIzhilv/backend/tests/test_trip_workflow.py -vv # 行程时间、引用、费用和预算校验 python -m pytest AIzhilv/backend/tests/test_trip_validation.py -vv # 独立 RAG 服务契约与检索器 python -m pytest AIzhilv/backend/tests/test_rag_service.py AIzhilv/backend/tests/test_rag_retriever.py -vv # FastAPI 行程、异步任务与多模态接口 python -m pytest AIzhilv/backend/tests/test_trip_api.py AIzhilv/backend/tests/test_trip_jobs.py AIzhilv/backend/tests/test_trip_artifacts.py -vv ``` ### 第四步:运行重构链路的 Ruff 检查 ```powershell python -m ruff check AIzhilv/backend/app/agent AIzhilv/backend/app/schemas AIzhilv/backend/app/services AIzhilv/rag_service AIzhilv/backend/app/settings.py AIzhilv/backend/app/api/trip_plans.py AIzhilv/backend/app/api/trip_jobs.py AIzhilv/backend/app/api/trip_artifacts.py AIzhilv/backend/app/utils/auth_token.py ``` 命令应显示 `All checks passed!`。Gitee Go 使用的完整限定文件列表保存在 [.workflow/master-pipeline.yml](.workflow/master-pipeline.yml)。旧兼容模块仍有历史 lint 债务,因此当前不把整个 `backend/app` 作为 Ruff 质量门禁;这项限制应在后续逐步消除。 ### 第五步:运行需要真实 RAG 服务的离线检索评测 先安装重依赖并启动服务: ```powershell uv pip install -r requirements-rag-service.txt Set-Location AIzhilv python -m rag_service.prewarm python -m uvicorn rag_service.main:app --host 127.0.0.1 --port 8010 ``` 保留该终端运行,在另一个已激活环境的 PowerShell 中执行: ```powershell Set-Location AIzhilv python -m rag_service.evaluate --cases rag_service/eval-cases.example.jsonl --minimum-hit-rate 0.5 ``` 输出包含 `case_count`、`hit_rate`、`mean_reciprocal_rank` 和 `index_versions`。示例用例只用于验证评测链路;写入简历前应扩展成人工标注的真实评测集。 ## 已验证结果与待补实验 | 项目 | 当前状态 | |---|---| | 自动化测试 | 72 passed | | LangGraph、Pydantic、API 与 RAG 契约 | 自动化测试覆盖 | | Ruff | Gitee Go 限定范围检查 | | RAG Hit Rate / MRR | 已提供评测脚本,正式数据集待补 | | Recall@5 / Recall@10 / nDCG@10 | 待扩展评测器与人工标注集 | | 行程引用有效率、冲突率、预算违规率 | 待提交端到端评测集 | | 并发 1/5/10/20 的 P50、P95 与错误率 | 待提交压测脚本及硬件说明 | README 不发布无法由数据集、模型版本、硬件信息和脚本复现的性能数字。 ## API 端点 | 方法 | 路径 | 说明 | 鉴权 | |------|------|------|------| | GET | `/` | 健康检查 | 否 | | GET | `/docs` | Swagger 文档 | 否 | | GET | `/health` | 进程存活检查(无需鉴权) | 否 | | GET | `/ready` | 就绪检查:MySQL + RAG 依赖可达(无需鉴权) | 否 | | GET | `/chat/stream/events` | SSE 流式对话(进度事件 + token + done) | 是 | | GET | `/api/v1/trip-plan-jobs/{job_id}/events` | SSE 任务进度事件流 | 是 | | POST | `/chat/stream` | 流式 AI 对话(legacy 纯文本契约,保留兼容) | 是 | | POST | `/chat/rag_chat` | 非流式 RAG 对话 | 是 | | POST | `/api/v1/trip-plans` | 创建结构化旅行计划 | 是 | | POST | `/api/v1/trip-plan-jobs` | 提交异步旅行计划任务 | 是 | | GET/DELETE | `/api/v1/trip-plan-jobs/{job_id}` | 查询/取消任务 | 是 | | POST | `/api/v1/trip-artifact-analyses` | 可选图片信息提取 | 是 | | GET | `/chat/get_subject` | 获取对话主题列表 | 是 | | POST | `/user/login` | 用户登录(返回 JWT) | 是 | | POST | `/user/sign` | 用户注册 | 是 | | GET | `/weather/query` | 天气查询 | 是 | | GET | `/food/recommend` | 美食推荐 | 是 | | GET | `/hotel/search` | 酒店搜索 | 是 | > 鉴权方式:在请求头中添加 `X-API-Key: `。 > 当 `API_KEY` 环境变量未设置或为 `your_api_key_here` 时,鉴权自动关闭(开发模式)。 > 此外,`/user/user_select` 和 `/user/user_update` 还需要登录返回的 JWT: > 请求头添加 `Authorization: Bearer `(token 有效期默认 72 小时,可用 `JWT_TTL_HOURS` 调整)。 旧 `/chat/*` 路由已作为迁移兼容层,只有设置 `ENABLE_LEGACY_CHAT=true` 才加载;新行程规划使用 `/api/v1/trip-plans` 和独立 RAG 服务。 每项活动都返回 `verification_status`:只有引用了有效 RAG `source_id` 才是 `verified`。未知引用会被移除;缺少证据、时间冲突、活动过密、费用重算和超预算都会进入行程警告。 ## Agent 进度反馈与 SSE 协议 Agent 执行过程通过统一结构化 **ProgressEvent** 对用户透明(只展示"系统正在做什么",不暴露模型内部推理): ```json { "type": "progress", "stage": "retrieving", "status": "running", "message": "正在检索相关旅行资料…", "tool": "rag", "request_id": "req_xxx", "job_id": "trip_xxx", "metadata": {}, "ts": "2026-08-31T02:00:00Z" } ``` - **Stage 集合(稳定)**:`idle / understanding / retrieving / tool_calling / planning / validating / repairing / streaming / completed / failed / cancelled` - **Status 集合**:`running / completed / failed / retrying / degraded / cancelled`,明确区分重试、降级与失败 - **Tool 字段**:`rag / weather / hotel / map / search / llm` - 默认中文文案集中在 `backend/app/progress.py` 的 `STAGE_MESSAGES`,前端按 stage/status 映射 UI,节点代码不拼接展示文案 两条 SSE 通道: 1. `GET /chat/stream/events` — 对话场景:`progress` 帧(理解需求 → 检索资料 → 生成回答)+ `token` 帧(最终回答逐段输出)+ 终端 `done`/`error` 帧。旧 `GET /chat/stream` 纯文本契约保持不变。 2. `GET /api/v1/trip-plan-jobs/{job_id}/events` — 异步行程任务:按节点推进的 `progress` 帧 + 终端 `done`(含完整 TripPlan)/`error` 帧;晚连接的客户端会先收到完整事件回放。 前端聊天页在每条助手消息内嵌固定"执行过程"区域:进行中实时展开,完成后默认折叠为"已完成 N 个步骤",可点击展开;发送中显示"停止"按钮(AbortController 本地中断并标记 cancelled)。 取消语义:`DELETE /api/v1/trip-plan-jobs/{job_id}` 设置取消标记,节点边界协作式停止,进度流发送 `cancelled` 事件;服务关闭时未完成 Job 标记为 `INTERRUPTED` 而非永远停在 running。 ## 日志与可观测性 统一日志配置在 `backend/app/logging_config.py`:stdout(`docker compose logs -f` 可见)+ 轮转文件(10 MB × 5): ```text logs/ ├── app.log # 全部应用日志(含 request_id) ├── agent.log # LangGraph 节点 start/success/failed + duration_ms、LLM 调用 ├── rag.log # 检索服务 └── error.log # ERROR+ 完整 traceback ``` - 每条日志携带 `request_id`(FastAPI 中间件从 `X-Request-ID` 复用或自动生成,贯穿到 Job 工作线程) - LLM 日志只记录 provider/model/duration_ms/status/attempt,**不记录** Prompt、响应、API Key - 天气/酒店等 Tool 日志记录 tool/status/duration_ms/attempt,不记录第三方大段响应 ## 统一错误响应 所有 API 错误响应为稳定结构(`backend/app/errors.py`): ```json {"error": {"code": "WEATHER_UNAVAILABLE", "message": "天气服务暂时不可用", "request_id": "req_xxx", "details": {}}} ``` 内部 traceback 只进入 `error.log`,不返回给客户端。异常体系:`ConfigurationError / PlanValidationError / RAGError / LLMError / ToolError(Weather/Hotel/Map) / ExternalServiceError / DatabaseError / JobError(JobCancelled)`。 ## 统一命令入口 ```bash make dev # 本地启动后端(8000,热重载) make dev-rag # 本地启动 RAG 服务(8010) make test # 后端测试套件 make lint # Ruff 检查 make up # docker compose up -d --build make down # 停止 make logs # 跟随容器日志 make health # 探测 /health /ready make smoke # 端到端冒烟(见下) ``` ## 冒烟测试 栈启动后运行: ```bash python scripts/smoke.py # 完整冒烟(含 Agent 进度流验证) python scripts/smoke.py --skip-agent # 仅验证 health/ready ``` 覆盖:`/health`、`/ready`、RAG `/health`、异步任务全生命周期(POST → SSE 进度流 → 终端帧)、对话 SSE(progress + token + done/error)。 ## 常见故障排查 | 症状 | 排查 | |------|------| | 启动即报错配置缺失 | 查看启动日志 `startup check:` 行;对照 `.env.example` 补齐 `.env` | | `/ready` 返回 503 | 响应体 `checks` 指出是 `database` 还是 `rag_service` 不可用 | | MySQL 连不上 | `docker compose ps` 确认 db healthy;容器内用 `DB_HOST=db`,本地开发用 `localhost` | | 检索降级为 lexical-fallback | RAG 日志查看 `mindtrip-rag-index-builder` 错误;确认 `RAG_CORPUS_PATH` 存在、模型缓存可写 | | 索引不存在/损坏 | 删除 `llamaindex_store/versions/` 后重启,会原子化重建 | | 模型下载慢/失败 | 检查 `huggingface_cache` volume;设 `HF_ENDPOINT` 镜像;模型不进镜像 | | LLM 不可用 | 对话返回 `LLM_UNAVAILABLE` 错误帧;确认 `LLM_PROVIDER/LLM_BASE_URL` 指向可达服务(本地 vLLM 或 DeepSeek) | | 天气/酒店 503 | `SENIVERSE_KEY`/`AMAP_KEY` 未配置或第三方不可用;错误响应 `code` 区分工具 | | SSE 不更新/中断 | 确认响应头 `X-Accel-Buffering: no`;反向代理需关闭 buffering;前端用 `/chat/stream/events` | | 端口占用 | 8000/8010/3306 被占用时改 compose 端口映射 | | 日志查看 | `docker compose logs -f` 或宿主机 `logs/backend/`、`logs/rag/` | | Docker Volume 权限 | 容器以非 root 用户 `mindtrip`(uid 10001) 运行;bind mount 目录需可写 | ## 安全注意事项 - 用户密码使用 **bcrypt** 加盐哈希存储(`app/utils/security.py`);旧明文数据在首次成功登录时自动升级 - 登录接口带限流:每账号 10 分钟内最多 5 次尝试 - 用户查询接口**不返回密码字段** - `.env` 文件已被 `.gitignore` 排除,不会提交到仓库 - 数据库密码、API Key 等敏感信息统一通过环境变量管理 - CORS 来源可通过 `CORS_ORIGINS` 环境变量配置 - 生产环境务必设置 `API_KEY` 并移除 `CORS_ORIGINS` 中的通配符 - 生产环境设置 `MINDTRIP_ENV=production`;此时缺失或少于 32 字节的 `JWT_SECRET` 会阻止签发令牌 ## Docker 部署 > 生产环境(HTTPS、强密钥、上线检查清单)见 [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)。 ```bash # 1. 准备 .env(见上方"配置环境变量") # 2. 启动 MySQL 8 + FastAPI 后端(首次启动自动导入 aizhilv.sql) docker compose up -d --build ``` - MySQL 数据持久化在 `mysql_data` volume;后端在容器内通过 `DB_HOST=db` 访问数据库 - `rag-service` 会在首次检索时从现有 JSONL 语料构建 LlamaIndex 索引,并持久化到 `llamaindex_data` volume - Hugging Face 模型缓存持久化到 `huggingface_cache`;可用 `RAG_PREWARM_ON_START=true` 在服务启动时预热 - BGE-M3 首次下载和 CPU 建索引可能需要数分钟;后续启动会直接加载持久化索引 - 三个服务都带 healthcheck;`docker compose ps` 全为 `healthy` 即就绪。后端日志 bind mount 到宿主机 `logs/backend/`、`logs/rag/` - 镜像基于 `python:3.10-slim-bookworm`(锁定 Python 3.10 系列 + Debian bookworm),依赖安装使用 `requirements-*-lock.txt` 锁文件,容器内以非 root 用户 `mindtrip` 运行;`.dockerignore` 排除 .env/日志/模型/缓存,不会打进镜像 **实测注意事项**(WSL2 Ubuntu 24.04 + Docker Engine 29 全栈验证,2026-08-31): - **栈内 `.env` 的数据库配置必须指向 compose 服务**:`DB_HOST=db`、`DB_PORT=3306`、`DB_USER`/`DB_PASSWORD` 需与 compose 中 db 服务的账号一致(默认 `aizhilv` / `aizhilv123`)。若 `.env` 里仍是本地开发值(`DB_HOST=localhost`)或占位符(`your_db_user`),后端会被 MySQL 以 `1045 Access denied` 拒绝,`/ready` 报 `database: false` - **日志目录属主**:`logs/` bind mount 进容器后由非 root 的 `mindtrip`(uid 10001)写入。若宿主机 `logs/` 属主是 root(如以 root 执行过构建/同步),需先 `chown -R 10001:10001 logs/`,否则后端启动即 `PermissionError: /app/AIzhilv/logs/app.log` 并不断重启 - **宿主 3306 被占用**(本机已有 MySQL 等服务)时,把 compose 中 db 的端口映射改为 `"3307:3306"` 即可,容器间通信不受影响 ## 技术栈 | 层 | 技术 | |----|------| | 后端框架 | FastAPI + Uvicorn | | 工作流编排 | LangGraph | | 行程数据契约 | Pydantic | | 数据库 | MySQL 8.0 + PyMySQL | | 独立 RAG 服务 | LlamaIndex + BGE-M3(兼容现有 JSONL 语料) | | LLM 推理 | LangChain OpenAI-compatible adapter;vLLM / DeepSeek / OpenAI-compatible | | 可选多模态 | OpenAI-compatible vision model,默认关闭 | | 前端框架 | UniApp (Vue 3,选项式 API) | | 部署 | Docker Compose(MySQL 8 + FastAPI + 独立 RAG 服务) | ## 持续集成 `.workflow/master-pipeline.yml` 会在 Gitee Go 的 `master` 推送流水线中安装 API 开发依赖、运行后端测试,并检查本次重构运行时的 Ruff 规则。