# rag-assistant **Repository Path**: au2/rag-assistant ## Basic Information - **Project Name**: rag-assistant - **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-09-26 - **Last Updated**: 2026-09-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RAG 智能问答助手系统(rag-assistant) ## 一、项目介绍 面向内部用户的知识库问答后端服务。用户输入提问后,系统先通过智谱 GLM 大模型识别问题意图,再结合 Neo4j 图知识库做 RAG 检索增强,最后调用通义千问 Qwen 流式大模型生成答案,通过 SSE 实时分段返回。 **核心价值:** - 意图识别 + RAG 检索增强,回答更贴合企业私有知识 - SSE 流式输出,首字响应快,用户体验流畅 - 会话上下文 Redis 缓存,支持多轮对话 - 聊天记录 MySQL 持久化,支持会话管理与历史回看 **适用场景:** 企业内部知识库问答、智能客服、文档助手。 ## 二、架构设计 ### 整体流程 ``` 用户提问 │ ▼ POST /api/v1/chat/completions (SSE) │ ▼ ┌─────────────────────────────────────────────────────────┐ │ ChatService 编排 │ │ │ │ ① IntentService ──► 智谱 GLM 意图识别模型(glm-4-flash) │ │ │ (知识问答 / 闲聊 / 其他) │ │ ▼ │ │ ② RagService ─────► Neo4j 图知识库 CONTAINS 检索 │ │ │ (闲聊意图跳过, 异常降级为空上下文) │ │ ▼ │ │ ③ 组装 Prompt ◄──── Redis 会话上下文(最近≤5轮) │ │ │ │ │ ▼ │ │ ④ Qwen 流式模型 ──► SSE 分段推送 data:{"content":"..."} │ │ │ │ │ ▼ │ │ ⑤ 持久化 ─────────► MySQL user_chat_record │ │ + Redis 上下文写回(TTL 2h) │ └─────────────────────────────────────────────────────────┘ ``` ### 节点职责 | 模块 | 职责 | |------|------| | ChatController | 对话接口入口,参数校验,SSE/分页/删除 | | HealthController | 五组件健康探测(mysql/redis/neo4j/zhipu/qwen) | | ChatService | 问答主编排:意图→检索→提示词→流式生成→持久化 | | IntentService | 智谱 GLM 意图分类,失败降级 OTHER | | RagService | Neo4j 知识检索,异常降级空上下文 | | ChatRecordService | 聊天记录持久化、会话聚合查询、逻辑删除 | ### 数据流转 - **写路径:** 问答完成 → MySQL `user_chat_record`(del_flag=0) + Redis `rag:assistant:session:{sessionId}:context` - **删路径:** DELETE 会话 → MySQL 逻辑删除(del_flag=1) + Redis 上下文 key 清除 ## 三、核心概念 ### 1. 意图识别(Intent Recognition) 调用智谱 GLM 云端轻量模型(glm-4-flash,温度=0 保证确定性),用提示词约束模型只输出 `KNOWLEDGE_QA / CHITCHAT / OTHER` 三个标签。闲聊意图跳过知识库检索直接回答,节省检索开销。 ### 2. RAG 检索增强(Retrieval-Augmented Generation) **知识实体设计(本项目专属,与 Neo4j 中其他数据严格隔离):** - Label:`RagAssistantKnowledge` - 属性:`title`(标题)、`content`(正文)、`category`(分类)、`source`(来源)、`create_time`(创建时间) 检索时仅在该 Label 内对 `title / content / category` 做 CONTAINS 模糊匹配, 命中知识拼入 Prompt 作为上下文,让大模型基于私有知识作答,缓解幻觉。 初始化脚本见 `src/main/resources/cypher/knowledge-init.cypher`。 ### 3. 流式生成(Streaming) LangChain4j `OpenAiStreamingChatModel` + `StreamingResponseHandler` 回调: - `onNext(token)`:每生成一个片段立即通过 SSE 推送 - `onComplete(response)`:流结束后落库并推送 `[DONE]` - `onError(error)`:异常时推送 `{"code":50001,"msg":"对话处理失败"}` ### 4. SSE(Server-Sent Events) 基于 HTTP 的单向服务端推送协议。本系统事件协议: ``` data:{"content":"回答片段"} ← 内容事件(多次) data:[DONE] ← 结束事件 data:{"code":50001,"msg":"对话处理失败"} ← 异常事件 ``` ### 5. 逻辑删除 MyBatis-Plus `@TableLogic` 注解:查询自动附加 `del_flag=0` 条件,删除自动转为 `UPDATE del_flag=1`,数据物理保留可恢复。 ## 四、代码详解 ### ChatService 流式编排(核心) ```java qwenStreamingChatModel.generate(prompt, new StreamingResponseHandler() { @Override public void onNext(String token) { answerBuilder.append(token); // 累积完整回答用于落库 sendContentEvent(emitter, token); // 实时推送 SSE 片段 } @Override public void onComplete(Response response) { chatRecordService.saveRecord(...); // 完整回答写入 MySQL appendContext(sessionId, query, fullAnswer); // 更新 Redis 上下文 sendDoneEvent(emitter); // 推送 [DONE] emitter.complete(); } ... }); ``` ### 会话标题生成规则 ```java // 会话首条记录:取提问截断 20 字作为标题;后续记录复用已有标题 private String resolveSessionTitle(String sessionId, String userQuery) { UserChatRecord existing = chatRecordMapper.selectOne(...); if (existing != null && !existing.getTitle().isBlank()) { return existing.getTitle(); } return userQuery.length() <= 20 ? userQuery : userQuery.substring(0, 20); } ``` ### 配置属性校验(规范约束) ```java @Data @Validated @ConfigurationProperties(prefix = "langchain4j.open-ai.chat-model") public class Langchain4jQwenProperties { @NotBlank(message = "langchain4j.open-ai.chat-model.api-key 不能为空") private String apiKey; @Min(0) @Max(1) private double temperature; ... } ``` > 注:langchain4j 0.34.0 的 `OpenAiStreamingChatModel` 构建器暂不支持自定义请求体参数,`enable-thinking` 作为配置项保留,待框架升级后接入。 ## 五、API 使用 接口统一前缀:`/api/v1`,非流式接口统一 `Result{code,msg,data}` 包装。 ### 1. 流式智能问答(SSE) ```bash curl -N -X POST http://localhost:8573/api/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"sessionId":"s-001","userId":"user001","query":"什么是RAG?"}' ``` 响应(`text/event-stream`): ``` data:{"content":"RAG"} data:{"content":"(检索增强生成)是"} data:{"content":"一种结合检索与生成的技术..."} data:[DONE] ``` ### 2. 获取用户全部会话列表 ```bash curl "http://localhost:8573/api/v1/chat/sessions?userId=user001&pageNum=1&pageSize=10" ``` ```json { "code": 200, "msg": "success", "data": { "records": [{"sessionId":"s-001","title":"什么是RAG?","lastChatTime":"2026-09-26T10:00:00"}], "total": 1, "pageNum": 1, "pageSize": 10 } } ``` ### 3. 会话历史查询 ```bash curl "http://localhost:8573/api/v1/chat/sessions/s-001/records?pageNum=1&pageSize=20" ``` ```json { "code": 200, "msg": "success", "data": { "records": [{"id":1,"userQuery":"什么是RAG?","aiAnswer":"RAG是...","createTime":"2026-09-26T10:00:00"}], "total": 1, "pageNum": 1, "pageSize": 20 } } ``` ### 4. 会话逻辑删除 ```bash curl -X DELETE "http://localhost:8573/api/v1/chat/sessions/s-001" ``` ```json {"code":200,"msg":"success","data":3} ``` ### 5. 服务健康检查 ```bash curl "http://localhost:8573/api/v1/health" ``` ```json { "code": 200, "msg": "success", "data": { "status": "UP", "components": {"mysql":"UP","redis":"UP","neo4j":"UP","zhipu":"UP","qwen":"UP"} } } ``` ## 六、运行指南 ### 环境要求 - JDK 17+、Maven 3.6+ - MySQL 8.x、Redis 6+、Neo4j 5.x - 智谱开放平台 API Key(意图识别)、通义千问 API Key(流式回答) ### 初始化数据库 ```sql CREATE DATABASE IF NOT EXISTS rag_assistant DEFAULT CHARACTER SET utf8mb4; USE rag_assistant; CREATE TABLE IF NOT EXISTS user_chat_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', session_id VARCHAR(128) NOT NULL COMMENT '会话唯一标识', user_id VARCHAR(64) NOT NULL COMMENT '用户标识', title VARCHAR(255) DEFAULT NULL COMMENT '会话标题(首条提问截断)', user_query TEXT COMMENT '用户提问内容', ai_answer TEXT COMMENT 'AI回答内容', create_time DATETIME COMMENT '创建时间', del_flag TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记:0正常 1删除', PRIMARY KEY (id), KEY idx_session_id (session_id), KEY idx_user_id (user_id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='用户聊天记录表'; ``` ### 配置 复制 `.env.example` 为 `.env`,填写实际连接信息(密钥禁止硬编码,全部走环境变量): ```properties MYSQL_URL=jdbc:mysql://127.0.0.1:3306/rag_assistant?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai MYSQL_USERNAME=root MYSQL_PASSWORD=你的密码 REDIS_HOST=127.0.0.1 NEO4J_URI=bolt://127.0.0.1:7687 NEO4J_USERNAME=neo4j NEO4J_PASSWORD=你的密码 ZHIPU_API_KEY=你的智谱APIKey ZHIPU_BASE_URL=https://open.bigmodel.cn/api/paas/v4 ZHIPU_INTENT_MODEL=glm-4-flash ZHIPU_INTENT_TEMPERATURE=0 LANGCHAIN4J_QWEN_API_KEY=你的API Key LANGCHAIN4J_QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 LANGCHAIN4J_QWEN_MODEL_NAME=qwen-plus ``` > 配置加载优先级:系统环境变量/启动参数 `ENV_DIRECTORY` 指定目录下的 `.env` > JVM 参数 `env.directory` > 项目根目录 `.env`。 > 注意 `ZHIPU_BASE_URL` 即使配置为完整 v4 路径,框架层也会自动规整为主机根,兼容 langchain4j-zhipu-ai 0.34.0 的相对路径要求。 ### 启动 ```bash # 1. 编译 mvn clean compile # 2. 打包(跳过测试) mvn clean install -DskipTests # 3. 启动 mvn clean spring-boot:run -DskipTests # 4. 访问测试页面 # http://localhost:8573/index.html ``` 服务端口固定为 **8573**(初始化时随机选定,不再改动)。 ## 七、快速开始 1. 启动 MySQL/Redis/Neo4j 三个依赖服务 2. 执行上方建库建表 SQL 3. 在 Neo4j Browser 或 cypher-shell 执行 `src/main/resources/cypher/knowledge-init.cypher` 初始化知识数据 4. 配置 `.env` 文件(参考 `.env.example`) 5. `mvn clean spring-boot:run -DskipTests` 启动服务 6. 浏览器打开 `http://localhost:8573/index.html` 7. 输入用户ID → 点击"新会话" → 输入问题(如"什么是RAG?") → 实时查看流式回答 8. 调用 `GET /api/v1/health` 验证各组件连通状态 ## 八、学习思考 1. **为什么意图识别和答案生成要用不同模型?** 意图识别是简单分类任务,智谱 glm-4-flash 轻量模型温度设为 0 即可稳定完成,云端调用免本地部署且免费额度充足;答案生成需要强推理能力,使用通义千问大模型。分级调度兼顾成本与效果。 2. **Neo4j 检索异常为什么要降级而不是直接报错?** 知识库是增强手段而非必要依赖。检索失败时降级为纯大模型回答,保证对话主流程高可用;健康检查接口负责暴露组件异常供监控告警。 3. **会话上下文为什么放 Redis 而不是 MySQL?** 上下文是高频读写的临时数据,Redis 读写快、支持 TTL 自动过期(2小时),避免上下文无限膨胀;MySQL 只存最终问答记录,职责清晰。 4. **SSE 与 WebSocket 的取舍?** 问答场景是"请求-流式响应"单工模式,SSE 基于 HTTP 实现简单、天然支持重连、与 REST 风格统一;WebSocket 的双工能力在此场景属于过度设计。 5. **逻辑删除的代价?** 查询需始终带 `del_flag` 条件(MyBatis-Plus 自动处理),数据量持续增长需配合归档策略;换来的是误删可恢复、审计可追溯。