# npp_rag **Repository Path**: huxin889/npp_rag ## Basic Information - **Project Name**: npp_rag - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-04 - **Last Updated**: 2026-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # npp-rag 耐普新能源 RAG 知识库与问答系统。文档解析 → 拆分 → 向量化 → 入库 → 检索 → 问答, 后端 FastAPI + Chroma,前端 Vue3 + Element Plus。 ## 环境要求 - Python ≥ 3.12(`uv` 管理依赖) - Node ≥ 22.18(前端) ## 快速开始 ```bash # 1. 安装依赖 uv sync cd web && npm install && cd .. # 2. 配置环境变量 cp .env.example .env # 然后填入 LLM / Embedding 服务地址与密钥 # 3. 启动后端(启动时会自动增量同步 docs/ 与 uploads/) uv run uvicorn api.main:app --reload --port 8000 --workers 1 # 4. 另开一个终端,启动前端 cd web && npm run dev # 打开 http://localhost:5173 ``` > ⚠️ 后端**必须单进程**,不要加 `--workers N`。Chroma 客户端是进程内单例, > 多个 worker 抢同一个持久化目录会导致索引损坏。 ## 常用命令 ```bash # 单独跑一次同步(命令行自测,会打印统计) uv run python -m core.index_service uv run python -m core.index_service --force # 忽略内容比对,全量重建 # 前端 cd web npm run dev # 开发(Vite dev server,/api 代理到 8000) npm run build # 类型检查 + 构建到 web/dist npm run type-check # 只做类型检查 ``` ## 生产部署 `npm run build` 产出 `web/dist` 后,直接由 FastAPI 单端口托管: ```bash uv run uvicorn api.main:app --host 0.0.0.0 --port 8000 --workers 1 ``` 若前面挂 nginx,反代 `/api` 时**必须关闭缓冲**(`proxy_buffering off;`), 否则 SSE 流式输出会退化成一次性返回。 ## 功能 - **知识库**:拖拽上传(`.md` / `.txt` / `.pdf`)、文档列表、删除、手动同步、全量重建 - **智能问答**:向量召回 + 重排序的标准 RAG,SSE 流式输出,带多轮会话历史与引用来源 ## 检索流程 ``` 用户提问 └─ 向量检索召回 SEARCH_K 条候选(默认 20) └─ rerank 模型逐条打分(RERANK_MODEL) └─ 最高分 < RERANK_MIN_SCORE ? ├─ 是 → 直接回答"根据现有资料无法回答该问题",不调用 LLM └─ 否 → 取分数最高的 RERANK_TOP_N 条拼进 prompt └─ LLM 流式生成,前端展示每条来源的相关度 ``` 候选池 `SEARCH_K` 应大于最终条数 `RERANK_TOP_N`,否则重排序没有选择空间 (配置不满足时会被自动抬高)。重排序服务异常时会降级为「按向量距离取前 N 条」并记录 WARNING,不影响问答可用性。 相关性阈值 `RERANK_MIN_SCORE` 的作用是:既省下一次 LLM 调用,也避免把无关文档 当作"引用来源"展示给用户。 ## 长对话 会话上下文由 LangGraph 的 checkpointer 持有(`data/checkpoints.db`),**不再按轮数截断**—— 长对话会保留完整上下文。当上下文达到窗口的 `COMPRESS_TRIGGER_RATIO`(默认 70%)时, 较早的对话会被自动压成摘要保留,而不是丢弃。 界面上展示的始终是**完整原文**(存在 `data/app.db`,与 agent 上下文分开), 压缩只影响模型看到的内容,用户无感知。 `LLM_MAX_TOKENS` **必须配置正确**(默认 1000000):模型名不在 LangChain 的内置档案表里, 配错或漏配会导致压缩功能直接报错。 ## 评估 ```bash uv run python scripts/eval_rag.py # Hit@1 / Hit@N / MRR uv run python scripts/eval_rag.py --sweep # 相关性阈值的取舍曲线 ``` 只评估检索侧、不调用 LLM,所以跑得快也不烧 token。当前基线(20 条用例 / 6 篇文档): - Hit@1 = 100%,MRR = 1.000 - 有答案用例最高分 `[0.817, 1.000]`,无答案用例 `[0.001, 0.344]` **改动切分参数、prompt 或模型后请重跑**,否则无法判断是变好还是变坏。 新增文档时也要补充用例,不然指标只覆盖旧语料,会给出虚假的安心感。 > 注意:rerank 走的是 DashScope **原生**接口,不在 OpenAI 兼容模式下。 > `RERANK_BASE_URL` 填**主机名**即可,代码会自动拼接 > `/api/v1/services/rerank/text-rerank/text-rerank`;误填 `.../compatible-mode/v1` > 也会被自动纠正(兼容模式下该路径返回 404)。 ## 文档同步机制 启动时自动扫描 `docs/`(随仓库维护的语料)与 `uploads/`(界面上传)并做增量同步: - 以文件内容 sha256 为变更判据,**内容未变则直接跳过**,不重复调用 embedding - 内容变化则**先按来源删除旧 chunk,再重新入库**(避免 chunk 数变少时残留孤儿) - 磁盘上已删除的文件,其 chunk 会被清理 界面上传的文档落盘到 `uploads/`,与 `docs/` 分开存放,互不污染。 ## 常见问题 **上传或同步报「无法连接向量化服务」(WinError 10061 / 10054)** httpx 默认会读取**系统代理**(不只是环境变量,还包括 Windows 注册表里的设置)。 代理客户端退出后设置可能残留,导致请求被发往一个没人监听的端口并立刻失败。 本项目默认不读系统代理(`HTTP_TRUST_ENV=false`),正常情况下不会遇到。 如果你显式开了它、或者改用需要代理的境外端点,请确认代理客户端正在运行。 直接连不上时,可先确认主机是否可达: ```bash uv run python -c "import socket; socket.create_connection(('你的端点域名', 443), timeout=5); print('可达')" ``` **上传返回 503** 文件已保存到 `uploads/`,但向量化失败(通常是网络或鉴权问题)。 响应体里会说明原因,该文件会在下次「同步文档」时自动重试,无需重新上传。 ## 项目结构 ``` config/settings.py 配置(pydantic-settings,读取 .env) core/ document_loader.py 按扩展名分派 loader text_splitter.py 文本拆分 embedding.py 嵌入模型 vector_store.py Chroma 封装(按来源删除 / 枚举 / 检索) index_service.py 入库编排与增量同步(核心) reranker.py rerank 接口封装(重试 + 端点规范化) chat_store.py SQLite 会话历史 agent.py RAG 检索 + 重排序 + 流式生成 api/ main.py FastAPI 入口 + 启动同步 + 静态资源托管 routes/documents.py 上传 / 列表 / 删除 / 同步 routes/chat.py SSE 问答 + 会话管理 utils/logging.py 日志配置 docs/ RAG 语料(不是开发文档) uploads/ 上传文件落盘 data/ Chroma 持久化 + 会话 SQLite web/ Vue3 前端 ```