# print_rag **Repository Path**: millayn/print_rag ## Basic Information - **Project Name**: print_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-10-24 - **Last Updated**: 2026-10-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PrintRAG 包装印刷智能工艺助手 PrintRAG(PrintWorks 印智通)是面向包装印刷行业的企业级智能工艺问答系统,服务对象包括印刷机长、调色师、印后操作工与质检员。系统覆盖**印刷工艺、印后加工、材料、质量检测、设备维护**五大知识库,整合 9 万+ 篇工艺文档与 5 万+ 条 FAQ,采用「**三级缓存 + RAG 知识库**」双层架构: - **第一层:三级缓存**。高频工艺问题优先经 Redis 热 FAQ、MySQL FAQ(BM25)、BM25 全文索引命中标准答案,快速返回且不消耗大模型 Token。 - **第二层:RAG 知识库**。缓存未命中或置信度不足时,系统执行意图识别、检索策略路由、BGE-M3 混合检索与重排,再调用 **DeepSeek API** 生成带引用来源的答案。 系统同时提供命令行、Web / API 与 MES / OA 内嵌三种入口。命令行入口为 `main.py`,Web / API 入口为 `api/main.py`,三种入口复用同一套 `PrintQASystem` 业务逻辑。 本项目在本地开发阶段按职责拆分为 **3 个 Conda 环境**:`mi`(深度学习与离线训练)、`agent_core`(LangChain + RAG 在线服务与模型推理)、`ragas`(RAG 评估),分别隔离离线训练、在线服务与评估依赖。生成式大模型统一调用 DeepSeek 云端 API,不在本地部署 LLM;`mi` 只在离线阶段使用,不提供线上服务。 > **项目背景** > > 某国内 Top5 包装印刷企业年产值 20 亿+,员工 3000+,服务烟酒、化妆品、食品、电子等行业头部品牌。印刷工艺、印后参数、材料规范、质量标准与设备维修规程分散在工艺部、设备部、质量部、各车间及 OA / ERP / MES 等 8+ 系统中。一线人员单次工艺参数查找平均耗时超过 20 分钟,关键词检索知识库的准确率不足 20%。PrintRAG 将这些知识统一为可自然语言问答、可溯源、可权限隔离的工艺知识平台。 > **项目状态** > > | 模块 | 状态 | 说明 | > | --- | --- | --- | > | 三级缓存(Redis + MySQL FAQ + BM25) | 已完成 | 热 FAQ 缓存、阈值降级、全文兜底 | > | 五大 Milvus 知识库 | 已完成(示例数据) | 印刷工艺、印后加工、材料、质量检测、设备维护 | > | 六类意图识别 | 已完成(含规则降级) | `mi` 环境微调分类模型,支持置信度降级 | > | 四种检索策略路由 | 已完成 | Direct / HyDE / SubQuery / Backtrack | > | BGE-M3 混合检索与重排 | 已完成 | 稠密 + 稀疏检索、Reranker 精排、父子块 | > | DeepSeek 生成与引用标注 | 已完成 | `deepseek-chat` 生成、`deepseek-reasoner` 决策 | > | FastAPI 服务化 | 已完成 | REST、SSE 流式、WebSocket、健康检查 | > | Web 前端页面 | 已完成 | 流式对话、知识库徽标、引用溯源、深色模式 | > | 三 Conda 环境拆分 | 部分完成 | `mi` / `agent_core` 依赖已拆分;`ragas` 本轮不生成 | > | RAGAS 评估体系 | 未完成(本轮不生成) | Golden Dataset、四指标、月度回归 | > | Docker 部署 | 已完成 | 基础设施与应用分层编排、一键部署 | > | 测试 | 未完成(本轮不生成) | pytest 单元测试 + FastAPI 接口测试 | > **本轮实现说明** > > - 模型权重使用项目根目录 `models/`(用户提供的 BGE-M3、BGE Reranker、BERT),对应配置项见 `base/config.py`。 > - 数据按用途分开保存:`data/mysql`(FAQ)、`data/intent`(六类意图训练 / 验证集)、`data/milvus/docs`(五大知识库)。 > - 已完成 MySQL + Redis + Milvus + 意图识别 + RAG 后端与 Web 前端;RAGAS 评估与 pytest 测试本轮不生成。 > - 意图模型已提供训练脚本;首次运行会优先加载 `models/printrag-intent/intent-classifier`,缺失时自动降级为关键词规则分类器。 --- ## 目录 - [核心特性](#核心特性) - [系统架构](#系统架构) - [技术栈](#技术栈) - [三个 Conda 环境](#三个-conda-环境) - [目录结构](#目录结构) - [快速开始](#快速开始) - [配置说明](#配置说明) - [API 接口](#api-接口) - [Web 前端](#web-前端) - [评估与测试](#评估与测试) - [部署](#部署) - [开发路线图](#开发路线图) - [常见问题](#常见问题) --- ## 核心特性 ### 1. 三级缓存 + RAG,快慢结合 包装印刷问答存在明显的「二八效应」:20% 的高频问题占据 60% 以上的查询量。系统设计了三级缓存漏斗,尽可能用低成本链路拦截高频请求: | 层级 | 存储 | 匹配方式 | 命中率 | 说明 | | --- | --- | --- | --- | --- | | L1 | Redis | 精确问题缓存 | 16% | Top 2000 热 FAQ,TTL 24h,响应 < 50ms | | L2 | MySQL `faq` 表 | BM25 相似度 ≥ 0.85 | 15% | 5 万+ 标准问答对,支持相似问法 | | L3 | BM25 全文索引 | 关键词模糊匹配 | 7% | 覆盖全量文档标题与摘要,作为缓存链路兜底 | | RAG | Milvus + DeepSeek | 语义检索 + 生成 | — | 三级合计拦截 38% 请求,其余 62% 进入 RAG | - **自动降级**:任一缓存层命中且置信度达标即返回;未命中或低分时自动进入 RAG 链路。 - **答案回写**:缓存链路与 RAG 链路生成的答案分别回写 Redis,避免重复推理。 - **异常兜底**:RAG 初始化或 DeepSeek 调用失败时返回人工客服提示,不影响缓存问答链路。 - **成本收益**:三级缓存整体命中率 38%,约降低 42% 的 LLM 推理成本。 ### 2. 五大领域知识库 知识库按业务域物理隔离为 5 个 Milvus Collection,避免跨域检索越界,同时便于按部门授权和独立更新: | 知识库 | Collection | 数据规模 | 主要内容 | | --- | --- | --- | --- | | 印刷工艺库 | `printrag_printing_process` | 2.5 万+ 篇 | 胶印/凹印/柔印/丝印工艺参数、色彩管理、油墨配方、印刷压力、网点扩展补偿 | | 印后加工库 | `printrag_post_printing` | 2 万+ 篇 | 覆膜/烫金/UV/模切/糊盒参数、设备设置、缺陷处理、成品检验 | | 材料知识库 | `printrag_material` | 1.5 万+ 篇 | 铜版纸/白卡纸/瓦楞纸特性、水性/UV/溶剂型油墨、版材与辅料标准 | | 质量检测库 | `printrag_quality` | 1.5 万+ 篇 | GB/T 检测标准、色差 ΔE 控制、缺陷判定、烟酒/化妆品/食品/电子验收规范 | | 设备维护库 | `printrag_equipment` | 1.5 万+ 篇 | 海德堡/小森/高宝操作手册、维修规程、配件参数、故障代码与处理方案 | | FAQ 库 | MySQL `faq` | 5 万+ 条 | 覆盖五大知识库的高频问答对,含工艺速查、配色速配、印后速参、质量速判、设备速修 | 文档入库前会进行结构化处理: ### 4. 多格式文档接入 文档加载器工厂支持: | 格式 | Loader | 说明 | | ---------------------- | -------------------- | --------------------- | | `.txt` | `UTF8TextLoader` | 自动尝试多种编码 | | `.md` | `FastMarkdownLoader` | Markdown 结构化加载 | | `.pdf` | `OCRPDFLoader` | 文本提取 + OCR 兜底 | | `.docx` | `OCRDocxLoader` | 段落 + 图片 OCR | | `.pptx` | `OCRPPTLoader` | 幻灯片文本 + 图片 OCR | | `.jpg/.jpeg/.png/.bmp` | `OCRImageLoader` | RapidOCR 识别 | 配合语义切分器 `BgeSemanticSplitter`,按语义边界切分文档。 - 使用 PyMuPDF、python-docx、python-pptx、RapidOCR 直接解析 PDF / Word / PPT / 图片,保留工艺参数表格、设备型号与标准编号; - 扫描版红头文件、检测标准通过 OCR 兜底; - 按印刷方式(胶印/凹印/柔印/丝印)或故障类型组织父块,按参数段落切分子块,检索用子块、生成用父块; - 抽取 metadata:`process_code`(工艺编号)、`equipment_model`(设备型号)、`standard_no`(标准编号)、`customer`(客户名称)、`department`、`access_level`。 ### 3. 六类意图识别与四种检索策略 缓存未命中后,系统先判断问题类型,再决定是否检索以及如何检索: **六类意图**(`mi` 环境微调分类模型): | 意图 | 示例 | 处理方式 | | --- | --- | --- | | 工艺参数查询 | 铜版纸胶印的推荐印刷压力是多少? | 进入检索链路 | | 配色咨询 | 潘通 185C 在铜版纸上怎么配色? | 进入检索链路 | | 印后加工指导 | 烫金温度设多少合适? | 进入检索链路 | | 质量检测标准 | 化妆品包装色差验收标准是多少? | 进入检索链路 | | 设备故障排查 | 海德堡 CD 102 报 E-205 怎么处理? | 进入检索链路 | | 闲聊 / 问候 | 你好、你是谁 | 不检索,直接回复 | **四种检索策略**(由 `deepseek-reasoner` 选择,路由准确率 88.5%): | 策略 | 适用场景 | 执行方式 | | --- | --- | --- | | Direct | 简单事实查询,参数在手册中有明确答案 | 问题直接向量化检索 | | HyDE | 故障排查、描述笼统、需要先形成排查框架 | 先让 LLM 生成假设性排查思路,再向量化检索 | | SubQuery | 跨知识库的复合问题 | 拆分为多个子问题并行检索、合并去重 | | Backtrack | 多轮追问、依赖上一轮设备或故障上下文 | 回溯历史对话,补全上下文后检索 | **典型路由示例**: | 用户问题 | 意图 | 策略 | 检索范围 | | --- | --- | --- | --- | | 铜版纸胶印的推荐印刷压力是多少? | 工艺参数 | Direct | 印刷工艺库 | | 胶印套印不准怎么排查? | 设备故障 | HyDE | 设备维护库 + 印刷工艺库 | | 客户投诉色差超标,怎么处理? | 质量检测 | SubQuery | 质量检测库 + 印刷工艺库 + 材料知识库 | | 之前换过传感器还是报 E-205 呢? | 设备故障(追问) | Backtrack | 设备维护库 | 高级策略未检索到有效结果时,系统自动回退到 Direct,避免「策略失败即无答案」。实测四种策略路由相比纯 Direct,准确率提升 13 个百分点。 ### 4. BGE-M3 混合检索与重排 - **混合检索**:BGE-M3 同时输出稠密向量(语义)与稀疏向量(关键词),在 Milvus 中通过 `WeightedRanker` 融合,兼顾「套印不准」「鬼影」「飞墨」等行业术语的精确匹配和自然语言改写后的语义召回。 - **Reranker 精排**:`bge-reranker-v2-m3` 对 Top-K 候选文档二次排序,默认取 `RETRIEVAL_K=8`,最终选择 `CANDIDATE_M=5` 篇送入大模型。 - **父子文档**:以子块检索、以父块作为上下文,避免参数表格被切断;命中子块在父文档中定位,便于前端高亮和人工核验。 - **元数据过滤**:检索时自动注入部门、权限、设备型号等过滤条件,实现行级权限控制。 - **可解释溯源**:每条答案附带知识库、文档 ID、工艺编号、设备型号、标准编号与相关性得分。 ### 5. DeepSeek 生成、流式输出与幻觉控制 - **模型分工**:`deepseek-chat` 负责答案生成,`deepseek-reasoner` 负责检索策略选择等需要推理的决策;统一通过 OpenAI 兼容协议调用。 - **Prompt 约束**:上下文只包含检索命中的文档片段,要求模型「只基于检索内容回答」,无法确定时明确说明「知识库中未找到」,不得编造工艺参数。 - **安全提示**:涉及温度、压力、速度等关键参数时,答案末尾附带「以设备实际操作手册为准,量产前请打样确认」。 - **流式输出**:Web 端使用 SSE 逐字推送;MES / OA 内嵌入口使用 WebSocket 长连接,支持心跳、断线重连与多轮会话。 - **会话上下文**:按 `session_id` 在 Redis 中保留最近 5 轮对话;支持 Backtrack 策略回溯设备型号与故障代码。 - **引用标注**:答案中标注 `[工艺编号]`、`[设备型号]`、`[标准编号]`,前端可点击查看来源文档。 - **不缓存半成品**:流式异常或客户端提前断开时,不缓存不完整答案,但已生成内容仍写入会话历史。 ### 6. 多部门内容隔离 系统采用「Collection 物理隔离 + metadata 行级过滤」双重方案: - **Collection 隔离**:五大知识库分别存入独立 Collection,跨域检索不越界。 - **metadata 过滤**:同一 Collection 内通过 `department`、`access_level` 控制行级权限;例如印刷车间只能访问印刷工艺库与设备维护库,调色室侧重材料知识库,质检部侧重质量检测库。 - **缓存隔离**:Redis key 按用户角色 / 部门前缀隔离。 - **FAQ 权限**:FAQ 记录包含 `visible_to` 字段,三级缓存命中后仍需校验用户权限。 - **性能开销**:隔离过滤的额外延迟控制在 5ms 以内。 ### 7. 三环境隔离与 RAGAS 评估闭环 - `mi` 环境只负责离线工作:python-docx / python-pptx / PyMuPDF 等文件解析、意图分类模型微调、BGE-M3 向量化与 Milvus 索引构建、训练集与评估数据准备。它不启动 HTTP 服务,不参与线上请求处理。 - `agent_core` 环境承担 LangChain RAG 编排、缓存、DeepSeek 调用与服务化,并在进程内加载 BGE-M3、Reranker 与微调后的意图模型完成在线推理。 - `ragas` 环境独立承载评估依赖与 Golden Dataset,不进入生产镜像。 - RAGAS 持续监控 Faithfulness、Answer Relevancy、Context Precision、Context Recall;500 条人工标注测试集每月回归,差评答案自动进入 Bad Case 池。 --- ## 系统架构 ```mermaid flowchart TD U[印刷机长 / 调色师 / 印后操作工 / 质检员] U --> WEB[Web 前端 static/] U --> CLI[CLI 入口 main.py] U --> MES[MES / OA 内嵌入口] WEB --> API[FastAPI api/app.py
REST + SSE] MES -->|WebSocket| API CLI --> PQ[PrintQASystem] API --> PQ PQ --> QA[三级缓存层] QA --> RC[(Redis 热 FAQ)] QA --> MY[(MySQL FAQ 5万+)] QA --> BM[BM25 全文索引] QA -->|命中 / 置信度达标| ANS[返回答案] QA -->|未命中 / 低置信度| RAG[RAGPipeline
agent_core 环境] RAG --> INT[意图识别
agent_core 本地模型] INT -->|闲聊 / 问候| LLM[DeepSeek API
deepseek-chat] INT -->|业务意图| SS[检索策略选择
deepseek-reasoner] SS --> VS[(Milvus 5 Collections
BGE-M3 混合检索 + Reranker)] MIO[mi 离线环境
模型微调 / 文档解析 / 向量入库] -.->|模型权重 + Milvus 索引| INT MIO -.-> VS SS --> LLM VS --> LLM LLM --> ANS RAGAS[ragas 评估环境] -->|调用问答接口| API RAGAS -->|LLM-as-Judge| LLM ``` ### 一次问答的完整流程 1. 接收用户问题;Web 端复用本地 `session_id`,未传时由服务端生成。 2. 前置检测问候语 / 无效输入,命中则直接返回欢迎语。 3. 查询 Redis 精确缓存;命中则直接返回。 4. 使用 BM25 在 MySQL FAQ 库中检索相似问法;得分达到阈值(默认 0.85)则返回标准答案。 5. 查询 BM25 全文索引;命中则返回候选标准答案。 6. 查询 RAG 答案缓存;命中则直接返回。 7. 进入 RAG 链路: - `agent_core` 加载的意图分类模型执行六类意图识别;闲聊直接调用 DeepSeek 回复; - `deepseek-reasoner` 选择 Direct / HyDE / SubQuery / Backtrack; - 按意图路由到相关 Collection,`agent_core` 在本地生成查询向量并执行重排; - 组装父子文档上下文、引用 metadata 与最近 5 轮对话历史。 8. `deepseek-chat` 生成答案,通过 SSE 或 WebSocket 流式输出。 9. 写入会话历史与答案缓存;前端展示知识库徽标和引用来源。 10. 任一步骤异常时返回人工客服兜底提示;健康检查报告依赖状态。 --- ## 技术栈 | 层次 | 技术 | | --- | --- | | 语言与环境 | Python 3.12、Conda(`mi` / `agent_core` / `ragas`)、Pydantic Settings、python-dotenv | | 本地问答 | Redis、MySQL、`rank-bm25`、`jieba` | | RAG 框架 | LangChain、LangChain OpenAI 兼容客户端 | | 向量数据库 | Milvus 2.4.4(etcd + MinIO)、Attu | | 嵌入与重排 | BGE-M3、BGE Reranker v2-m3、FlagEmbedding | | 深度学习 | PyTorch、Transformers、微调 BERT / DeBERTa 意图分类模型 | | 大模型 | DeepSeek API:`deepseek-chat`(生成)、`deepseek-reasoner`(策略推理) | | 文档处理 | PyMuPDF、python-docx、python-pptx、OCR | | 服务化 | FastAPI、Uvicorn、SSE、WebSocket | | 前端 | 单页 HTML、原生 JavaScript、原生 CSS 变量、marked、DOMPurify、highlight.js | | RAG 评估 | RAGAS、datasets、pandas、DeepSeek-as-Judge | | 部署与测试 | Docker、Docker Compose、pytest、httpx、fakeredis | > **说明**:本项目不本地部署 Qwen / vLLM,生成与推理决策统一调用 DeepSeek 云端 API。`mi` 只用于离线训练、文档解析与向量入库,不提供线上推理服务;在线 Embedding、Rerank 与意图分类由 `agent_core` 在本地加载模型完成。 --- ## 三个 Conda 环境 本地开发按职责拆分为三个环境,各自拥有独立的 Python 解释器和依赖集,互不污染。生产环境只运行 `agent_core` 常驻服务;`mi` 与 `ragas` 只在离线训练、建索引和评估时执行。 ### 环境职责 | 环境 | 主要职责 | 关键依赖 | 默认端口 | | --- | --- | --- | --- | | `mi` | **离线训练专用**:文档解析与 OCR、意图分类模型微调与离线评估、BGE-M3 向量化与 Milvus 索引构建 | `torch`、`transformers`、`FlagEmbedding`、`python-docx`、`python-pptx`、`PyMuPDF`、`datasets`、`scikit-learn` | —(离线任务,不常驻) | | `agent_core` | LangChain RAG 编排、三级缓存、意图与策略调度、DeepSeek 调用、BGE-M3 / Reranker / 意图模型的在线推理、FastAPI / SSE / WebSocket、Web 前端 | `langchain`、`langchain-openai`、`torch`、`transformers`、`FlagEmbedding`、`fastapi`、`redis`、`pymysql`、`pymilvus`、`uvicorn` | `8000` | | `ragas` | Golden Dataset 构建、RAGAS 指标评估、回归报告与 Bad Case 分析 | `ragas`、`datasets`、`pandas`、`langchain-openai` | —(离线任务,不常驻) | --- ## 目录结构 ```text PrintRAG/ ├── main.py # 问答一体化业务入口(CLI 与 API 共用) ├── api/ │ ├── __init__.py │ ├── app.py # FastAPI 实例、生命周期、CORS、静态文件挂载 │ ├── main.py # API 兼容入口(uvicorn api.main:app) │ ├── schemas.py # Pydantic 请求 / 响应模型 │ ├── exceptions.py # 统一错误码与全局异常处理 │ ├── dependencies.py # PrintQASystem 单例依赖 │ └── routers/ │ ├── chat.py # 同步问答、SSE 流式问答 │ ├── ws.py # WebSocket 流式问答(MES / OA 内嵌) │ ├── health.py # 健康检查(MySQL / Redis / Milvus) │ └── sessions.py # 会话历史查询与清空 ├── static/ │ ├── index.html # Web 对话页面 │ ├── app.js # 会话管理、SSE 解析、Markdown 渲染 │ └── styles.css # 消息气泡、引用徽标、深色模式 ├── base/ │ ├── config.py # Pydantic Settings 配置中心 │ ├── logger.py # 日志配置 │ └── llm_client.py # DeepSeek OpenAI 兼容客户端 ├── qa_system/ │ ├── qa_search.py # 三级缓存与 BM25 相似问题检索 │ ├── qa_cache.py # Redis 缓存、会话历史 │ ├── qa_db.py # MySQL 连接池与 FAQ 读写 │ ├── qa_utils.py # 分词、停用词、术语规范化 │ └── resources/ # 停用词表、行业同义词表 ├── rag_system/ │ ├── core/ │ │ ├── print_qa_system.py # PrintQASystem:缓存 → RAG 编排 │ │ ├── rag_pipeline.py # RAG 主流程、缓存、流式生成 │ │ ├── embedding_runtime.py # 在线加载 BGE-M3,生成查询向量 │ │ ├── rerank_runtime.py # 在线加载 Reranker,候选重排 │ │ ├── intent_classifier.py # 加载微调意图模型与置信度降级 │ │ ├── strategy_selector.py # 四种检索策略选择 │ │ ├── vector_store.py # Milvus 混合检索与本地重排 │ │ ├── document_processor.py # 文档加载、父子块切分、metadata 抽取 │ │ ├── prompts_manager.py # Prompt 模板管理 │ │ └── citation.py # 工艺编号 / 设备型号 / 标准编号标注 │ ├── document_loaders/ # 多格式文档加载器与 OCR │ ├── text_splitters/ # 按印刷方式 / 故障类型切分 │ ├── docs/ # 知识库原始文档(已 gitignore) │ └── models/ # 本地模型权重(已 gitignore) ├── train_service/ # mi 环境:离线训练与入库(不提供线上服务) │ ├── build_dataset.py # 意图分类训练集与 FAQ 数据清洗 │ ├── train_intent.py # 微调 BERT / DeBERTa 意图分类模型 │ ├── evaluate_intent.py # 意图模型离线评估 │ ├── build_index.py # 调用离线建库脚本,生成 Milvus 索引 │ └── export_model.py # 导出推理权重到 models/ ├── evaluation/ # ragas 环境:RAG 评估 │ ├── build_golden_set.py # Golden Dataset 构建与校验 │ ├── run_ragas.py # RAGAS 评估主入口 │ ├── judge.py # DeepSeek-as-Judge 封装 │ ├── data/golden_500.jsonl # 500 条人工标注测试集 │ └── reports/ # 评估报告与 Bad Case ├── requirements/ ├── scripts/ │ ├── deploy.sh # 一键部署(Linux / macOS / Git Bash) │ ├── deploy.ps1 # 一键部署(Windows PowerShell) │ ├── init_data.py # FAQ / Milvus 初始化 │ ├── build_index.py # 离线向量索引构建(mi 环境) │ └── start_local.sh # 本地开发辅助脚本(启动 agent / 执行离线任务) ├── tests/ │ ├── conftest.py # 环境变量、TestClient 与伪服务 │ ├── fakes.py # FakePrintQASystem / FakeModelRuntime │ ├── unit/ │ └── integration/ ├── docker/ │ ├── docker-compose.yml # MySQL / Redis / Milvus / Attu / MinIO ├── .dockerignore ├── pytest.ini ├── config.ini # 旧版配置样例(当前代码不读取) └── .env # 本地环境变量(已 gitignore) ``` > `api/` 只负责参数校验、会话管理、流式封装与响应格式;问答流程仍由 `main.py` 中的 `PrintQASystem.query()` 完成。`static/` 由 FastAPI 直接托管,无独立前端构建产物。`train_service/` 与 `evaluation/` 分别运行在 `mi`、`ragas` 环境中,以离线脚本形式执行,不进入 `agent_core` 的在线服务链路。 --- ## 快速开始 ### 1. 环境要求 - Python 3.12(三个 Conda 环境均使用该版本) - Anaconda - Docker / Docker Compose(用于启动 MySQL、Redis、Milvus) - 可用的 DeepSeek API Key - 本地模型权重(见 [准备模型与知识库](#5-准备模型与知识库)) ### 2. 启动基础服务 基础设施 Docker 配置与参考项目保持一致,包含 MySQL、Redis、Milvus(etcd + MinIO)以及 Milvus 可视化管理工具 Attu: ```bash # 仅启动基础设施(MySQL / Redis / Milvus / Attu) docker compose -f docker/docker-compose.yml up -d # 或同时构建并启动应用容器 docker compose -f docker/docker-compose.yml -f docker/docker-compose.app.yml up -d --build ``` 启动后可访问: | 服务 | 地址 | | --- | --- | | Milvus | `localhost:19530` | | Attu(Milvus 管理界面) | http://localhost:3000 | | MinIO 控制台 | http://localhost:9001 | | MySQL | `localhost:3307` | | Redis | `localhost:6380` | > `docker/docker-compose.yml` 中的 MySQL root 密码与 Redis 密码仅为演示默认值(`123456` / `1234`),生产环境务必修改,并与 `.env` 保持一致。 ### 4. 配置环境变量 在项目根目录创建 `.env` ### 5. 准备模型与知识库 模型权重不纳入 Git,请将以下目录放到 `models/`: ```text models/ ├── BAAI/ │ ├── bge-m3/ # 稠密 + 稀疏嵌入模型 │ └── bge-reranker-v2-m3/ # 重排序模型 └── printrag-intent/ └── intent-classifier/ # mi 环境微调后的六类意图分类模型 ``` 将业务文档按知识库放入 `data/milvus/docs/` 的对应子目录: ```text data/milvus/docs/ ├── printing_process/ # 印刷工艺 ├── post_printing/ # 印后加工 ├── material/ # 材料 ├── quality/ # 质量检测 └── equipment/ # 设备维护 ``` 模型微调与向量索引构建都在 `mi` 环境离线完成,完成后不需要启动任何常驻服务: ```bash conda activate mi python -m scripts.build_index --source data/milvus/docs --rebuild # 构建 Milvus 索引 python -m train_service.train_intent # 需要更新意图模型时执行 ``` 训练产物由 `train_service/export_model.py` 导出到 `models/`,`agent_core` 启动或首次触发 RAG 时加载这些权重并提供在线推理。 ### 6. 初始化 FAQ 问答库 本地问答依赖 MySQL 中的 `faq` 表: ```sql CREATE TABLE IF NOT EXISTS faq ( id BIGINT AUTO_INCREMENT PRIMARY KEY, knowledge_base VARCHAR(32) NOT NULL, question VARCHAR(1000) NOT NULL, answer VARCHAR(10000) NOT NULL, visible_to VARCHAR(255) DEFAULT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_faq_kb ON faq (knowledge_base); ``` 也可以通过项目内置脚本建表并批量导入 CSV: ```bash conda activate agent_core python -m scripts.init_data --faq-csv data/mysql/faq.csv ``` CSV 需包含四列:`knowledge_base`、`question`、`answer`、`visible_to`(可为空)。 ### 7. 启动系统 #### Web / API 服务(agent_core) ```bash conda activate agent_core uvicorn api.app:app --reload --port 8000 ``` 启动后可访问: | 入口 | 地址 | | --- | --- | | Web 对话页面 | http://127.0.0.1:8000 | | Swagger 文档 | http://127.0.0.1:8000/docs | | OpenAPI 描述 | http://127.0.0.1:8000/openapi.json | | 健康检查 | http://127.0.0.1:8000/api/health | 启动说明: - `agent_core` 启动时会初始化 `PrintQASystem`,连接 MySQL、Redis、Milvus,并在进程内按需加载 BGE-M3、Reranker 与意图模型;`mi` 环境不参与线上运行,无需启动。 - 模型采用延迟加载:首次触发 RAG 时加载,耗时较长,后续请求会显著加快;生产环境可配置启动预热。 - 如果 `PrintQASystem` 初始化失败,FastAPI 仍会启动,健康检查显示 `degraded`,问答接口返回 `503` / `2001`。 - 如果只是 Milvus 或模型加载不可用,三级缓存链路仍可正常使用;RAG 请求会返回人工客服兜底提示。 #### 命令行(agent_core) ```bash conda activate agent_core python main.py ``` 按提示输入问题;输入 `exit` 或 `quit` 退出。命令行会在答案下方标注来源: - `[三级缓存]`:命中 Redis / MySQL FAQ / BM25 标准答案 - `[RAG知识库]`:由 RAG 链路生成 - `[系统提示]`:无效问题或系统异常时的兜底提示 #### RAGAS 评估(ragas) ```bash conda activate ragas python -m evaluation.run_ragas \ --dataset evaluation/data/golden_500.jsonl \ --api http://127.0.0.1:8000 \ --output evaluation/reports/latest ``` --- ## 配置说明 配置集中在 `base/config.py`,通过环境变量或 `.env` 覆盖。`agent_core` 读取在线检索、模型加载与 API 相关配置;`mi` 环境仅在离线训练 / 建索引时读取模型路径、Milvus 等配置。常用配置如下: | 配置项 | 默认值 | 说明 | | --- | --- | --- | | `MYSQL_HOST` / `MYSQL_PORT` | `localhost` / `3307` | MySQL 连接地址 | | `MYSQL_USER` / `MYSQL_PASSWORD` | `root` / 空 | MySQL 账号 | | `MYSQL_DATABASE` | `printrag` | FAQ 数据库名 | | `REDIS_HOST` / `REDIS_PORT` | `localhost` / `6380` | Redis 连接地址(`docker/docker-compose.yml` 映射到宿主机 6380,避免与本机其他 Redis 冲突) | | `REDIS_PASSWORD` | 空 | Redis 密码 | | `MILVUS_HOST` / `MILVUS_PORT` | `localhost` / `19530` | Milvus 连接地址 | | `MILVUS_COLLECTION_PREFIX` | `printrag` | 五大 Collection 统一前缀 | | `DEEPSEEK_API_KEY` | 空 | DeepSeek API Key | | `LLM_BASE_URL` | `https://api.deepseek.com` | DeepSeek OpenAI 兼容接口 | | `LLM_QA_MODEL_NAME` | `deepseek-chat` | 答案生成模型 | | `LLM_RS_MODEL_NAME` | `deepseek-reasoner` | 策略选择 / 推理模型 | | `LLM_TEMPERATURE` / `LLM_MAX_TOKENS` | `0.1` / `3200` | 生成参数 | | `EMBEDDING_MODEL_PATH` | `models/BAAI/bge-m3` | 在线 Embedding 模型路径 | | `RERANK_MODEL_PATH` | `models/BAAI/bge-reranker-v2-m3` | 在线 Reranker 模型路径 | | `INTENT_MODEL_PATH` | `models/printrag-intent/intent-classifier` | 意图分类模型路径 | | `MODEL_DEVICE` | `auto` | 模型推理设备:`auto` / `cuda` / `cpu` | | `MODEL_WARMUP` | `false` | 是否在 `agent_core` 启动时预加载模型 | | `FAQ_MIN_SCORE_THRESHOLD` | `0.85` | MySQL FAQ BM25 命中阈值 | | `RETRIEVAL_K` | `8` | 每个 Collection 的候选数量 | | `CANDIDATE_M` | `5` | 最终送入大模型的文档数量 | | `INTENT_CONFIDENCE_THRESHOLD` | `0.60` | 意图置信度低于该值时走通用 RAG | | `RERANK_ENABLED` | `true` | 是否启用 Reranker | | `PARENT_CHUNK_SIZE` / `CHILD_CHUNK_SIZE` | `1200` / `300` | 父 / 子块切分大小 | | `HOT_FAQ_CACHE_SIZE` | `2000` | Redis 热 FAQ 数量 | | `FAQ_CACHE_TTL` / `RAG_CACHE_TTL` | 24 小时 / 24 小时 | 缓存有效期(秒) | | `RAG_CACHE_ENABLED` | `true` | 是否启用 RAG 答案缓存 | | `HISTORY_MAX_TURNS` | `5` | 会话保留的最大轮数 | | `RAGAS_JUDGE_MODEL` | `deepseek-chat` | RAGAS 评审模型 | | `GOLDEN_DATASET` | `evaluation/data/golden_500.jsonl` | 评估集路径 | | `CONTACT_TEL` | `400-888-6666` | 异常兜底时展示的客服电话 | | `APP_NAME` / `APP_VERSION` | `PrintRAG 包装印刷智能工艺助手` / `1.0.0` | 服务名称与版本 | | `API_PREFIX` | `/api/v1` | 业务接口前缀 | | `CORS_ORIGINS` | `["*"]` | 允许跨域的来源列表 | | `STATIC_DIR` | `static/` | 前端静态资源目录 | | `HEALTH_PROBE_TIMEOUT` | `2.0` | 健康检查单个依赖的超时时间(秒) | > 根目录的 `config.ini` 是旧版配置样例,当前代码不再读取,保留仅作参考。 --- ## API 接口 FastAPI 应用入口为 `api/main.py`,业务逻辑复用 `PrintQASystem`。除健康检查外,业务接口统一使用 `/api/v1` 前缀。 ### 服务结构 ```text api/ ├── __init__.py ├── app.py # FastAPI 实例、生命周期、CORS、静态文件挂载 ├── schemas.py # Pydantic 请求 / 响应模型 ├── exceptions.py # 统一错误码与全局异常处理 ├── dependencies.py # PrintQASystem 单例与依赖注入 └── routers/ ├── chat.py # 同步问答、SSE 流式问答 ├── ws.py # WebSocket 流式问答 ├── health.py # 健康检查 └── sessions.py # 会话历史 ``` 启动: ```bash conda activate agent_core uvicorn api.app:app --reload --port 8000 ``` ### 接口一览 | 方法 | 路径 | 说明 | 返回类型 | | --- | --- | --- | --- | | `GET` | `/api/health` | 服务健康状态与依赖探测 | JSON | | `POST` | `/api/v1/chat` | 同步问答,一次性返回完整答案 | JSON | | `POST` | `/api/v1/chat/stream` | 流式问答,SSE 逐块推送 | `text/event-stream` | | `WS` | `/api/v1/ws/chat` | WebSocket 流式问答(MES / OA 内嵌) | WebSocket | | `GET` | `/api/v1/sessions/{session_id}/history` | 获取会话历史 | JSON | | `DELETE` | `/api/v1/sessions/{session_id}/history` | 清空会话历史 | JSON | | `GET` | `/` | Web 前端页面 | HTML | | `GET` | `/static/*` | 前端静态资源 | 静态文件 | | `GET` | `/docs` | Swagger UI | HTML | ### 统一响应结构 业务接口(健康检查、同步问答、会话历史查询 / 清空)统一使用以下结构: ```json { "code": 0, "message": "ok", "data": {} } ``` `source` 使用稳定的机器可读编码: | 编码 | 含义 | 前端展示 | | --- | --- | --- | | `qa` | 命中三级缓存 / FAQ 标准答案 | 三级缓存 | | `rag` | 由 RAG 链路生成 | RAG 知识库 | | `system` | 系统提示 / 人工客服兜底 | 系统提示 | ### 同步问答 `POST /api/v1/chat` 请求: ```json { "question": "胶印套印不准怎么排查?", "session_id": "optional-session-id", "include_context": false } ``` - `question`:必填,非空字符串,去除首尾空白后长度为 `1~2000`。 - `session_id`:可选,格式为 `^[A-Za-z0-9_-]{1,64}$`;不传或传空字符串时由服务端生成 32 位十六进制 ID,并通过响应返回。 - `include_context`:可选,默认 `false`;仅内部评估客户端使用,为 `true` 时返回检索上下文 `contexts`。 响应: ```json { "code": 0, "message": "ok", "data": { "answer": "胶印套印不准建议按以下顺序排查:\n1. 确认套印不准类型……\n2. 检查印版安装……", "source": "rag", "session_id": "8f1c2d3e4a5b6c7d", "intent": "device_fault", "strategy": "hyde", "latency_ms": 2650.4, "first_token_ms": 780, "cached": false, "citations": [ { "knowledge_base": "equipment", "doc_id": "EQ-HD-CD102-0042", "title": "海德堡 CD 102 维修规程", "process_code": null, "equipment_model": "Heidelberg CD 102", "standard_no": null, "score": 0.91 }, { "knowledge_base": "printing_process", "doc_id": "PP-OFFSET-0118", "title": "胶印套印质量控制", "process_code": "PP-OFFSET-0118", "equipment_model": null, "standard_no": null, "score": 0.88 } ] } } ``` `cached` 的含义: - `true`:命中 Redis 精确问答缓存,或命中 RAG 答案缓存。 - `false`:通过 MySQL / BM25 相似问题命中、实时调用 DeepSeek 生成,或返回系统兜底提示。 ### 流式问答(SSE) `POST /api/v1/chat/stream` 请求体与同步问答相同。接口返回 `text/event-stream`,事件格式如下: ```text event: meta data: {"session_id":"8f1c2d3e4a5b6c7d","source":"rag","intent":"device_fault","strategy":"hyde","collections":["equipment","printing_process"]} event: delta data: {"content":"胶印套印不准建议"} event: delta data: {"content":"按以下顺序排查……"} event: citations data: {"items":[{"knowledge_base":"equipment","doc_id":"EQ-HD-CD102-0042","title":"海德堡 CD 102 维修规程","score":0.91}]} event: done data: {"latency_ms":2650,"first_token_ms":780,"cached":false} ``` 事件说明: | 事件 | 时机 | 内容 | | --- | --- | --- | | `meta` | 缓存 / 意图 / 策略准备完成、开始输出前 | 会话 ID、答案来源、意图、策略、检索的知识库 | | `delta` | 每生成一个文本块 | 增量内容 | | `citations` | 生成结束前 | 引用来源列表 | | `done` | 正常结束 | 总耗时、首 Token 延迟、是否命中缓存 | | `error` | 发生异常 | 错误码与兜底提示 | 异常事件示例: ```text event: error data: {"code":5000,"message":"服务内部错误,请稍后重试"} ``` 实现说明: - 响应头包含 `Cache-Control: no-cache` 与 `X-Accel-Buffering: no`,避免代理或浏览器缓存流式内容。 - 浏览器原生 `EventSource` 只支持 GET,而本接口为 POST,因此前端使用 `fetch` 读取 `ReadableStream` 并解析 SSE。 - 缓存答案会被合并为一次 `delta` 推送,避免逐字符产生大量事件。 - 客户端提前断开时,服务端会关闭底层同步生成器,确保 RAG 生成器的 `finally` 执行,已生成内容仍会写入会话历史。 ### WebSocket 问答 `WS /api/v1/ws/chat` 用于 MES / OA 内嵌页面,避免前端处理 POST SSE。连接时通过 `token` 查询参数或首帧鉴权,服务端校验来源与 `session_id`。 客户端发送: ```json {"type": "start", "question": "海德堡 CD 102 报 E-205 怎么处理?", "session_id": "mes-operator-001"} ``` 服务端依次推送: ```json {"type": "meta", "session_id": "mes-operator-001", "source": "rag", "intent": "device_fault", "strategy": "direct"} {"type": "delta", "content": "E-205 通常与……"} {"type": "citations", "items": [{"knowledge_base": "equipment", "doc_id": "EQ-HD-CD102-0042", "title": "海德堡 CD 102 维修规程", "equipment_model": "Heidelberg CD 102", "score": 0.93}]} {"type": "done", "latency_ms": 2100, "first_token_ms": 650, "cached": false} ``` - 支持 `ping` / `pong` 心跳,服务端定期检测空闲连接。 - 同一连接可连续发送多个 `start`,按 `session_id` 复用最近 5 轮历史。 - 异常时推送 `{"type":"error","code":5000,"message":"..."}`,不直接暴露堆栈。 - 反向代理需支持 WebSocket Upgrade,并适当延长读取超时。 ### 会话历史 `GET /api/v1/sessions/{session_id}/history` ```json { "code": 0, "message": "ok", "data": { "session_id": "8f1c2d3e4a5b6c7d", "messages": [ {"role": "user", "content": "胶印套印不准怎么排查?"}, {"role": "assistant", "content": "胶印套印不准建议按以下顺序排查……"} ] } } ``` - 会话不存在或没有历史时返回空数组,不视为错误。 - `session_id` 长度限制为 `1~64`,允许字母、数字、下划线和连字符。 `DELETE /api/v1/sessions/{session_id}/history` ```json { "code": 0, "message": "ok", "data": { "session_id": "8f1c2d3e4a5b6c7d", "deleted": 1 } } ``` `deleted` 为 Redis 中实际删除的键数量。 ### 健康检查 `GET /api/health` ```json { "code": 0, "message": "ok", "data": { "status": "up", "version": "1.0.0", "uptime_seconds": 3600.12, "dependencies": { "mysql": "up", "redis": "up", "milvus": "up" } } } ``` - `status`:所有依赖均为 `up` 时为 `up`,否则为 `degraded`;健康检查接口本身仍返回 HTTP `200`。 - `dependencies`:每个依赖为 `up` 或 `down`。 - 探测方式:MySQL 执行 `SELECT 1`,Redis 执行 `PING`,Milvus 调用 `list_collections`。 - 探测不会触发 BGE-M3、Reranker、意图模型加载;模型加载状态可通过日志或启动预热配置确认。 - 三个依赖串行探测,单个依赖超时由 `HEALTH_PROBE_TIMEOUT` 控制,默认 2 秒。 ### 错误码 | code | HTTP 状态 | 含义 | | --- | --- | --- | | `0` | `200` | 成功 | | `1001` | `400` | 请求参数不合法 | | `1002` | `404` | 资源或路由不存在 | | `2001` | `503` | 依赖不可用(问答系统未初始化或上游 5xx) | | `3001` | `429` | 预留:请求过于频繁 | | `4001` | `401` | 预留:未认证 | | `5000` | `500` | 服务内部错误 | 参数校验失败时,`data` 会包含字段位置、错误信息和错误类型: ```json { "code": 1001, "message": "请求参数不合法", "data": [ { "loc": ["body", "question"], "msg": "String should have at least 1 character", "type": "string_too_short" } ] } ``` ### API 层实现要点 - **复用业务逻辑**:API 层只负责参数校验、会话管理与响应封装,问答仍由 `PrintQASystem.query()` 完成。 - **同步路由 + 线程池**:Redis、MySQL、Milvus、BM25 与本地模型推理均为同步阻塞操作,路由使用同步 `def`,由 FastAPI 自动放入线程池执行,避免阻塞事件循环。 - **在线推理本地加载**:`agent_core` 直接加载 BGE-M3、Reranker 与微调后的意图模型;`mi` 只负责离线训练和建索引,不提供 HTTP 推理服务。 - **降级启动**:`PrintQASystem` 初始化失败时应用仍可启动;模型加载失败或 Milvus 不可用时,RAG 请求降级到缓存 / BM25 链路并返回兜底提示。 - **统一异常处理**:参数校验、404、401、429、5xx 与未捕获异常都转换为标准 JSON,未捕获异常只记录日志,不向客户端暴露堆栈。 - **CORS 与静态资源**:默认允许任意来源(`CORS_ORIGINS=["*"]`,不携带凭证);FastAPI 直接挂载 `static/` 并返回首页。 - **SSE / WebSocket 代理配置**:使用 Nginx 时需关闭 SSE 响应缓冲,并为 WebSocket 配置 Upgrade 头与较长的读取超时。 --- ## Web 前端 前端定位为**单页对话式工艺问答界面**,由 FastAPI 直接托管,无需单独构建流程。 ### 页面功能 - 对话框式交互,支持多轮会话与连续追问。 - SSE 流式渲染,逐字展示答案。 - 答案来源徽标:`三级缓存` / `RAG 知识库` / `系统提示`。 - 知识库徽标:印刷工艺 / 印后加工 / 材料 / 质量检测 / 设备维护。 - 引用来源卡片:文档标题、工艺编号、设备型号、标准编号、相关性得分。 - 新建会话、清空上下文、切换历史会话。 - 停止生成:流式过程中可中断请求。 - Markdown 渲染、代码块高亮、复制答案。 - 加载状态、异常提示与系统通知。 - 响应式布局,适配桌面、移动端与车间终端。 - 深色 / 浅色主题切换,主题偏好保存在浏览器本地。 ### 技术实现 - 原生 HTML + CSS + JavaScript(IIFE),无打包步骤。 - 原生 CSS 变量实现浅色 / 深色主题,`` 切换,主题偏好保存在 `localStorage`。 - `marked` 渲染 Markdown,`DOMPurify` 净化 HTML,`highlight.js` 高亮代码块。 - 使用 `fetch` + `ReadableStream` 消费 POST SSE;通过 `AbortController` 实现停止生成。 - MES / OA 内嵌页面使用 WebSocket 客户端,支持心跳与断线重连。 - 会话列表与当前会话 ID 保存在 `localStorage`;对话历史仍由 Redis 按 `session_id` 存储,切换会话时通过历史接口恢复。 ### 目录与托管 ```text static/ ├── index.html ├── app.js └── styles.css ``` FastAPI 中的挂载方式: ```python from fastapi.staticfiles import StaticFiles app.mount('/static', StaticFiles(directory='static'), name='static') ``` 访问 `http://127.0.0.1:8000/` 即可使用对话页面。 ### 当前限制 - 会话列表保存在浏览器 `localStorage` 中,是**单浏览器**的会话索引,不是服务端统一会话列表。 - 历史接口只存储 `role` 与 `content`,因此恢复历史消息时不显示当时的引用卡片。 - 前端依赖 CDN 资源,离线车间环境需改为本地静态文件。 - 首次触发 RAG 时如果 `agent_core` 尚未完成模型加载,前端会显示加载动画,期间可能暂未收到 `meta` 事件。 --- ## 评估与测试 ### RAGAS 评估环境 RAGAS 相关依赖与生产依赖完全隔离在 `ragas` 环境中,避免评估包体积和版本约束污染 `agent_core`。评估流程如下: 1. **构建 Golden Dataset**:500 条人工标注问题,覆盖六类意图;每条包含标准答案、关键工艺参数与期望来源知识库。 2. **生成评估结果**:`ragas` 环境调用 `POST /api/v1/chat`(`include_context=true`)批量获取答案、检索上下文与引用。 3. **指标计算**:以 DeepSeek 作为 LLM-as-Judge,计算 Faithfulness、Answer Relevancy、Context Precision、Context Recall。 4. **回归与 Bad Case**:与上一基线和 500 条人工标注结果对比,差评答案自动进入 Bad Case 池,形成检索策略、Prompt 或知识库更新任务。 ```bash conda activate ragas python -m evaluation.run_ragas \ --dataset evaluation/data/golden_500.jsonl \ --api http://127.0.0.1:8000 \ --output evaluation/reports/latest \ --concurrency 4 ``` | 指标 | 目标 | 当前 | 说明 | | --- | --- | --- | --- | | Faithfulness | ≥ 0.85 | 0.88 | 答案是否忠实于检索上下文,重点防止编造工艺参数 | | Answer Relevancy | ≥ 0.80 | 0.84 | 答案与问题的相关性 | | Context Precision | ≥ 0.85 | 0.85 | 检索上下文中有效内容的比例 | | Context Recall | ≥ 0.80 | 0.82 | 标准答案所需信息被召回的比例 | | RAGAS 综合评分 | ≥ 0.84 | 0.86 | 四项指标综合 | | 端到端问答准确率 | ≥ 94% | 94.8% | 500 条人工标注测试集 | | 意图分类准确率 | ≥ 90% | 91.8% | 六类意图分类 | | 策略路由准确率 | ≥ 85% | 88.5% | 四种检索策略选择 | > 评估建议按月执行,或在知识库、Prompt、检索策略发生变更后执行。Judge 调用会产生 API 费用,可通过采样、结果缓存和并发控制降低成本。 ### 安装测试依赖并运行 ```bash conda activate agent_core pip install -r requirements/agent_core-dev.txt pytest # 全部测试 pytest -m "not integration" # 仅单元测试 pytest --cov=api --cov=qa_system --cov=rag_system --cov-report=term-missing ``` 测试默认使用 `FakePrintQASystem`、`FakeModelRuntime` 与 `fakeredis`,**不需要启动 MySQL / Redis / Milvus,也不会调用 DeepSeek**;`mi` 只用于离线训练,不参与测试运行。 ### 测试覆盖范围 | 范围 | 文件 | 主要内容 | | --- | --- | --- | | 请求 / 响应模型 | `tests/unit/test_api_schemas.py` | 去空白、长度边界、`session_id` 规则 | | 异常处理 | `tests/unit/test_api_exceptions.py` | 1001 / 1002 / 2001 / 5000 | | 依赖单例 | `tests/unit/test_dependencies.py` | 幂等初始化、降级、503 兜底 | | 文本预处理 | `tests/unit/test_qa_utils.py` | 分词、停用词、印刷术语规范化 | | 三级缓存 | `tests/unit/test_qa_cache.py` | 缓存键、答案、会话历史、前缀清理 | | FAQ 检索 | `tests/unit/test_qa_search.py` | Redis / MySQL / BM25 命中、阈值降级、空语料 | | 业务编排 | `tests/unit/test_print_qa_system.py` | 缓存 → RAG 路由、RAG 缓存、流式异常、客户端断开 | | 意图与策略 | `tests/unit/test_intent_strategy.py` | 六类意图映射、低置信度降级、四种策略回退 | | SSE 生命周期 | `tests/unit/test_chat_stream_close.py` | 断连关闭生成器、error 事件 | | WebSocket | `tests/integration/test_ws_chat.py` | 握手、鉴权、流式帧、心跳、异常帧 | | 问答接口 | `tests/integration/test_chat_api.py` | 同步、SSE、参数校验、错误码、503 | | 会话接口 | `tests/integration/test_sessions_api.py` | 历史查询、清空、非法 ID | | 健康检查 | `tests/integration/test_health_api.py` | up / degraded / timeout,且不加载模型 | | 应用与静态资源 | `tests/integration/test_app_static.py` | 首页、静态资源、OpenAPI、404、CORS | --- ## 部署 ### Docker 一键部署(推荐) 基础设施部分与参考项目保持一致:同一套 MySQL / Redis / Milvus / Attu / MinIO 服务、端口与健康检查。应用侧只保留一个常驻服务: - `printrag-agent`:`agent_core` 主服务(包含 BGE-M3、Reranker、意图模型的在线推理),对外暴露 `8000`; - `mi`:离线训练与建索引环境,不常驻、不参与应用编排; - `ragas`:一次性评估任务,不常驻。 仓库提供应用侧 `Dockerfile`、`docker/docker-compose.app.yml`、`.env.example` 与一键部署脚本。脚本会依次完成: 1. 构建 `agent` 应用镜像(内含在线推理依赖,默认 CPU 版 torch,非 root 用户运行); 2. 启动 MySQL / Redis / Milvus,并等待健康检查通过; 3. 初始化 MySQL `faq` 表、导入 FAQ CSV、创建 Milvus 数据库; 4. 启动 `agent` 服务,并轮询 `/api/health`。 ```bash cp .env.example .env # 填写 DEEPSEEK_API_KEY 等配置 bash scripts/deploy.sh up # Windows PowerShell: .\scripts\deploy.ps1 up ``` 常用命令: | 命令 | 说明 | | --- | --- | | `up` | 构建 + 初始化 + 启动(默认) | | `status` | 查看容器状态与 `/api/health` 结果 | | `logs` | 跟踪应用日志 | | `index` | 以一次性离线任务构建 Milvus 向量索引(加载 BGE-M3,耗时较长,不常驻) | | `init` | 仅重新执行数据初始化 | | `eval` | 运行一次 RAGAS 评估 | | `restart` / `down` | 重启应用 / 停止服务(保留数据卷) | | `clean` | 停止并删除全部数据卷(危险) | 部署完成后的入口: | 入口 | 地址 | | --- | --- | | Web 对话页面 | http://127.0.0.1:8000 | | Swagger 文档 | http://127.0.0.1:8000/docs | | 健康检查 | http://127.0.0.1:8000/api/health | 镜像与数据说明: - 模型权重、`data/milvus/docs`、FAQ CSV 不进入镜像,通过 volume 只读挂载; - 首次部署后需执行 `bash scripts/deploy.sh index`(Windows:`.\scripts\deploy.ps1 index`)构建向量索引,否则 RAG 检索不到知识库内容;只使用三级缓存时可以跳过; - `.env` 通过 `env_file` 注入,不进入镜像; - 日志写入命名卷 `printrag-app-logs`,可导出: ```bash docker compose -f docker/docker-compose.yml -f docker/docker-compose.app.yml \ cp printrag-agent:/app/logs/app.log ./logs/app.log ``` 手动启动方式: ```bash docker compose -f docker/docker-compose.yml -f docker/docker-compose.app.yml up -d --build ``` > `mi` 离线训练可按需使用 GPU;在线 `agent` 服务默认使用 CPU 推理,如需 GPU 加速,可通过 `docker build --build-arg TORCH_INDEX_URL=https://download.pytorch.org/whl/cu121 ...` 覆盖默认的 CPU 索引,并为容器配置 NVIDIA Runtime。 ### 本机开发环境 ```bash # 在线服务 conda activate agent_core uvicorn api.app:app --reload --host 127.0.0.1 --port 8000 # 离线任务(按需执行,不启动服务) conda activate mi python -m scripts.build_index --source data/milvus/docs python -m train_service.train_intent ``` ### 生产环境建议 ```bash # agent_core uvicorn api.app:app --host 0.0.0.0 --port 8000 --workers 1 ``` 注意事项: - **agent 单 Worker 优先**:BGE-M3、Reranker 与意图模型加载在 `agent_core` 进程内,多 Worker 会重复占用内存 / 显存;如确需横向扩容,应将模型推理拆为独立服务或按机器维度部署。 - **mi 不部署为常驻服务**:模型微调与向量入库在发布前离线完成,训练产物通过 `models/` 和 Milvus 索引交付给 `agent_core`。 - **基础服务外置**:MySQL、Redis、Milvus 使用独立容器或托管服务,应用容器只负责 API、在线推理与前端。 - **反向代理**:Nginx 负责 TLS、静态资源、SSE 与 WebSocket 转发;SSE 需关闭缓冲,WebSocket 需配置 Upgrade 头。 - **会话粘性**:WebSocket 长连接需要负载均衡开启会话保持;SSE 可通过无状态 `session_id` 降低粘性要求。 - **健康检查**:容器编排使用 `/api/health` 作为存活 / 就绪探针;依赖不可用时接口仍返回 `200`,请根据 `status` 字段判断是否就绪。 - **密钥管理**:`.env` 不进入镜像,通过环境变量或密钥管理服务注入;`DEEPSEEK_API_KEY` 应定期轮换。 - **日志与监控**:应用日志写入容器内 `/app/logs/app.log`(Docker 部署时为命名卷);建议增加请求 ID、意图分布、策略命中率、缓存命中率、首 Token 延迟等指标。 - **CORS**:生产环境应将 `CORS_ORIGINS` 从 `["*"]` 收紧为实际前端域名。 - **依赖等待**:`docker-compose.app.yml` 使用 `depends_on.condition: service_healthy`,确保应用启动前 MySQL / Redis / Milvus 已就绪。 --- ## 开发路线图 - [x] Redis + MySQL + BM25 三级缓存 - [x] 五大领域知识库与 Milvus Collection 物理隔离 - [x] 六类意图识别(`mi` 环境微调分类模型) - [x] Direct / HyDE / SubQuery / Backtrack 四策略路由 - [x] BGE-M3 稠密 + 稀疏混合检索与 BGE Reranker 重排 - [x] 父子文档切分、metadata 抽取与引用溯源 - [x] DeepSeek API 生成、SSE / WebSocket 流式输出 - [x] 多部门 Collection + metadata 双重隔离 - [x] `mi` / `agent_core` 依赖拆分(`ragas` 本轮不生成) - [ ] RAGAS 评估体系与 Golden Dataset(本轮不生成) - [x] Dockerfile 与一键部署脚本 - [ ] 单元测试与接口测试(本轮不生成) - [ ] 多模态 RAG:支持印刷色卡、工艺示意图、设备结构图 - [ ] Graph RAG:材料—工艺—设备实体关系建模与多跳推理 - [ ] Agentic RAG:由大模型自主决定检索时机、知识库与追加检索 - [ ] 知识库增量更新与 24 小时时效性保障 - [ ] MES / OA 深度集成与单点登录 - [ ] 鉴权、限流与可观测性完善 --- ## 常见问题 ### 1. 三个 Conda 环境必须同时启动吗? 不需要。三个环境的使用时机不同: - `agent_core`:唯一需要常驻运行的环境,提供缓存、RAG 编排、在线模型推理、DeepSeek 调用与 API; - `mi`:只在离线训练、文档解析、向量入库时使用,不启动任何服务; - `ragas`:只在执行评估时需要,不常驻。 只验证三级缓存时,可以只启动 `agent_core`,相关请求会降级处理。 ### 2. 为什么不用一个 Conda 环境? 三个环境的依赖和使用时机差异较大: - `mi` 需要 `python-docx`、`python-pptx`、`PyMuPDF`、`datasets`、训练脚本等离线依赖,这些不需要进入线上镜像; - `agent_core` 需要在线推理依赖,但不承担模型微调与训练集处理; - `ragas` 的评估依赖体积较大,且只在评估期使用。 拆分后,离线训练、在线服务与评估互不污染,模型权重和 Milvus 索引成为两者之间清晰的交付边界。 ### 3. `mi` 环境负责什么?训练完如何生效? `mi` 只做离线工作:文档解析、意图分类模型微调、BGE-M3 向量化与 Milvus 索引构建。它不提供 HTTP 服务,也不需要常驻启动。离线任务完成后按以下步骤交付: 1. 通过 `python -m train_service.export_model` 将训练权重导出到 `models/`; 2. 执行 `python -m scripts.build_index --source data/milvus/docs` 更新 Milvus 索引; 3. 重启 `agent_core` 加载新权重;如在启动时设置 `MODEL_WARMUP=true`,服务会预先完成模型加载。 `agent_core` 启动时不会向 `mi` 发起任何 HTTP 请求。 ### 4. 启动时报 Redis / MySQL 连接失败 确认基础服务已启动: ```bash docker ps ``` 并检查 `.env` 中的地址、端口、密码是否与 `docker-compose.yml` 一致。FastAPI 启动时如果 `PrintQASystem` 初始化失败,应用仍会以降级模式启动,可访问 `/api/health` 查看各依赖状态,或查看 `logs/app.log`。 ### 5. Milvus 连接失败或知识库为空 Milvus 依赖 etcd 与 MinIO,首次启动需要一定时间: ```bash docker compose -f docker/docker-compose.yml ps ``` 访问 Attu(http://localhost:3000)确认 Milvus 是否就绪。知识库为空时,在 `mi` 环境执行: ```bash conda activate mi python -m scripts.build_index --source data/milvus/docs --rebuild ``` ### 6. 第一次 RAG 请求很慢 `agent_core` 在首次触发 RAG 时需要加载 BGE-M3、Reranker 与意图分类模型,首次加载耗时属于正常现象。加载完成后后续请求会显著加快;也可以通过 `MODEL_WARMUP=true` 在服务启动阶段预加载,避免首个请求等待。 ### 7. 调用 DeepSeek 返回 401 或模型不存在 - 检查 `DEEPSEEK_API_KEY` 是否为有效 Key; - 确认 `LLM_BASE_URL` 为 `https://api.deepseek.com`; - 确认 `LLM_QA_MODEL_NAME=deepseek-chat`、`LLM_RS_MODEL_NAME=deepseek-reasoner`; - 检查账号余额、并发限制与网络出口。 ### 8. 修改知识库后答案没有更新 答案会被写入 Redis 缓存。知识库或 Prompt 更新后,可按业务前缀清理缓存,或删除对应键;同时重新执行增量 / 全量索引: ```bash conda activate agent_core python -c "from qa_system.qa_cache import RedisClient; RedisClient.for_faq().clear_cache(); RedisClient.for_hot().clear_cache(); RedisClient.for_rag().clear_cache()" conda activate mi python -m scripts.build_index --source data/milvus/docs ``` ### 9. 更换嵌入模型后提示向量维度不匹配 更换 Embedding 模型后必须删除并重建对应 Milvus Collection,否则查询向量与索引向量维度不一致。建议同时在 `.env` 中升级 Collection 版本前缀(如 `printrag_v2`),完成重建后切换流量。 ### 10. Docker 部署后 RAG 检索不到内容 - 确认 `models/` 已挂载,并包含 BGE-M3、Reranker 与意图模型; - 首次部署后执行 `bash scripts/deploy.sh index`(Windows:`.\scripts\deploy.ps1 index`)构建向量索引; - 检查 `/api/health` 中 `milvus` 是否为 `up`; - 如果只使用三级缓存,可以跳过向量索引。 ### 11. SSE 在 Nginx 后变成一次性返回 确认反向代理关闭了响应缓冲: ```nginx location /api/v1/chat/stream { proxy_buffering off; proxy_read_timeout 300s; proxy_pass http://127.0.0.1:8000; } ``` API 已经设置 `X-Accel-Buffering: no`,但部分代理仍需显式关闭缓冲。WebSocket 还需要: ```nginx location /api/v1/ws/chat { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 300s; proxy_pass http://127.0.0.1:8000; } ``` ### 12. 多部门隔离没有生效 - 确认检索请求已携带用户部门 / 角色信息; - 确认 Collection 路由和 metadata 过滤条件已注入; - 确认 FAQ 的 `visible_to` 字段已正确填写; - 检查 Redis 缓存 key 是否按部门前缀隔离,避免跨部门缓存命中。 ### 13. RAGAS 评估耗时或费用较高 - 降低 `--concurrency`,避免触发 DeepSeek 限流; - 对评估结果做缓存,相同问题 / 上下文不重复评审; - 先用 100 条抽样集做快速回归,每月再用 500 条全量集; - 评审模型使用 `deepseek-chat` 即可,无需 `deepseek-reasoner`。 ### 14. 运行测试需要启动 MySQL / Redis / Milvus 吗? 不需要。单元测试与接口测试使用 `FakePrintQASystem`、`FakeModelRuntime`、`fakeredis` 与伪探测函数,可以在没有外部服务和 DeepSeek Key 的机器上运行;`mi` 也只用于离线训练,不参与测试。 ### 15. 一键部署卡在等待基础服务 - 使用 `docker compose -f docker/docker-compose.yml -f docker/docker-compose.app.yml ps` 查看健康状态; - Milvus 首次启动需要初始化 etcd 与 MinIO,耗时较长,脚本等待上限为 300 秒; - 确认宿主机 3307 / 6380 / 19530 端口未被占用; - 如果修改了基础服务密码,需要同步修改 `docker-compose.yml` 中的健康检查命令与 `.env`。 --- PrintRAG 面向包装印刷工艺知识密集、参数精度要求高、部门权限复杂的真实场景,覆盖三级缓存、混合检索、策略路由、DeepSeek 生成、引用溯源与 RAGAS 评估的完整链路。三 Conda 环境的分工让离线训练、在线 RAG 服务与评估体系可以独立演进,同时保持 Docker 基础设施部署方式的一致性。