# LangHuan **Repository Path**: armycm/LangHuan ## Basic Information - **Project Name**: LangHuan - **Description**: LangHuan 是一个基于 Kotlin + Spring AI 的智能对话与知识检索系统,集成了 RAG 技术,为个人和小团队提供知识管理和智能问答的一站式解决方案。 "琅嬛福地",藏天下典籍。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 5 - **Created**: 2026-05-25 - **Last Updated**: 2026-05-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # LangHuan(万卷琅嬛) LangHuan 是一个基于 Kotlin + Spring AI 的智能对话与知识检索系统,集成了 RAG 技术,为个人和小团队提供知识管理和智能问答的一站式解决方案。 `琅嬛福地,藏天下典籍。` ## 功能特色 ### 🤖 智能对话系统 - **个人知识空间**:为每个用户提供独立的知识库管理空间 - **对话记忆功能**:智能保存和管理对话上下文,提供连贯的交互体验 - **深度思考模式**:支持模型深度推理输出,提供更详细的分析过程 - **图片文档识别**:支持多模态模型,可识别图片和文档,提供更丰富的答案和体验 - **多专家模式对话**:支持多角色协作对话,模拟专家团队讨论 - **移动端适配**:响应式布局设计,自适应手机/平板等设备,移动端友好的界面交互 - **MCP支持**:支持stdio/streamable-http/sse协议MCP,更好的工具扩展性 ### 📚 RAG知识检索 - **智能文档处理**:自动提取文档图片并进行存储,提供图文并茂的答案 - **混合检索技术**:结合向量检索、BM25全文检索,RRF(Reciprocal Rank Fusion)自适应融合,无需手动调参 - **查询预处理管线**:CompressionQueryTransformer(多轮指代消解)→ RewriteQueryTransformer(口语化改写)→ HyDE(假设性答案嵌入检索),三级预处理提升召回率 - **MMR多样性去重**:Maximal Marginal Relevance算法,避免同一文档多个chunk占据所有TopK位置 - **重排序机制**:支持ReRank模型,优化检索结果排序 - **BM25增量索引**:Lucene索引与向量存储实时同步,消除最长2天的数据不一致 - **多格式文档支持**:支持Md、Word、HTML(保留结构)、纯文本等多种文档格式 - **批量高效入库**:向量写入批次优化,入库速度提升10-50倍 - **Redis缓存**:统一缓存后端替代内存缓存,支持多实例部署和缓存持久化 - **召回测试工具**:提供可视化的检索效果测试和优化工具 ### 🗂️ 文件管理系统 - **文件组织管理**:支持文件分组、标签化管理 - **权限控制**:细粒度的文件访问权限控制,支持公开/私有设置 - **文件共享**:支持用户间文件组共享,便于协作 - **向量化管理**:支持单条向量的编辑、删除和优化 - **批量导入导出**:支持RAG文档的批量导入导出功能 ### 👥 用户权限系统 - **角色权限管理**:接口级别,完整的RBAC权限控制体系 - **JWT认证**:安全的用户认证和授权机制 - **权限缓存优化**:高效的权限验证和缓存机制 - **多用户支持**:支持多用户并发使用,数据隔离 ### 🔧 系统管理功能 - **提示词管理**:可配置的提示词模板和优化工具 - **模型配置**:支持多种OpenAI兼容模型的配置和切换 - **消息通知系统**:完整的系统消息推送和通知机制 - **使用统计仪表盘**:详细的系统使用情况统计和分析 - **MinIO对象存储**:集成对象存储,知识文件定期备份等功能 ### 🐳 部署运维 - **轻量化部署**:适合个人和小团队快速部署使用 - **Docker容器化**:可选的完整的Docker部署方案 - **数据库备份**:自动化的数据备份和恢复机制 - **定时任务管理**:定时清理临时文件等维护任务 - **日志管理**:完善的系统日志记录和管理 ## 部署 ### 推荐使用 [Docker部署方案](./langhuan_docker_dist/README.md) ### 基于源码部署: [前端本地部署](./langhuan-web/README.md) | [后端本地部署](./langhuan-server/README.md) ### 默认登陆账号 - 账号: admin - 密码: asb#1234 `默认密码在 langhuan-server/src/main/kotlin/com/langhuan/common/Constant.kt 文件中调节后重新启动` ## 重点功能流程 ### 对话流程 ```mermaid flowchart TD Start([开始: ChatController.chat]) --> ParseOption[解析ChatRestOption
设置默认模型] ParseOption --> CallChat[ChatService.chat] %% 文件分类 CallChat --> FileCategorize[FileUtil.categorizeFiles
分类: images / documents] FileCategorize --> CommandCheck{userMessage以
/add_personal_knowledge_space
开头?} %% ====== 个人知识空间分支 ====== CommandCheck -->|是| ExtractContent[提取实际内容
移除命令前缀] ExtractContent --> ImageCheck{有图片文件?} ImageCheck -->|有| ImgToText[ImgService.chat_imageUnderstandingToText
多模态图片信息提取] ImageCheck -->|无| ImgSkip[跳过图片处理] ImgToText --> DocCheck{有文档文件?} ImgSkip --> DocCheck DocCheck -->|有| DocSplit["RagService.readAndSplitDocument
CompositeTextSplitter
(======分片 + 固定窗口overlap)"] DocCheck -->|无| DocSkip[跳过文档处理] DocSplit --> CollectAll[收集全部内容
用户文本 + 图片文本 + 文档文本] DocSkip --> CollectAll CollectAll --> FilterEmpty[过滤空内容] FilterEmpty --> HasContent{有有效内容?} HasContent -->|有| AddToMySpace["RagService.addDocumentToMySpace
创建/更新个人知识空间
写入向量库 + Lucene增量索引"] AddToMySpace --> AddResult{添加结果} AddResult -->|成功| ReturnSuccess[返回: 添加到个人知识空间成功] AddResult -->|失败| ReturnFail[返回: 添加到个人知识空间失败] HasContent -->|无| ReturnNoContent[返回: 未提取到知识信息] %% ====== toChat 主对话分支 ====== CommandCheck -->|否| ToChat[toChat: 主对话处理] ToChat --> RagCheck{ragGroupId
非空?} %% --- RAG启用分支 --- RagCheck -->|启用RAG| GetRAGPrompt[获取RAG提示词模板
优先缓存配置] GetRAGPrompt --> QueryRewrite["QueryRewriteService.rewriteQuery
查询改写: 口语化→检索友好"] QueryRewrite --> QueryCompress["QueryCompressionService.compressQuery
查询压缩: 多轮指代消解
(历史消息≥5条时触发)"] QueryCompress --> HyDE["HydeService.generateHypotheticalDocument
HyDE: 生成假设性答案
(15s超时,失败返回null)"] HyDE --> HyDECheck{hydeText
非null?} HyDECheck -->|是| RagSearchHyde["RagCallBackService.ragSearchWithHyde
直接检索 + HyDE检索 → RRF二次融合"] HyDECheck -->|否| RagSearch["RagCallBackService.ragSearch
向量 + BM25 → RRF融合 + 去重"] RagSearchHyde --> BuildRAGContext[拼接检索文档内容] RagSearch --> BuildRAGContext BuildRAGContext --> ReplaceTemplate[替换提示词模板变量
构建RAG上下文] ReplaceTemplate --> CallLLM %% --- 普通对话分支 --- RagCheck -->|未启用RAG| CallLLM[调用ChatClient] CallLLM --> BuildResponse["构建ChatModelResult
chat=AI响应
rag=检索文档列表"] BuildResponse --> End([结束]) %% 异常处理 AddToMySpace -.->|异常| CatchException CallLLM -.->|异常| CatchException CatchException[捕获异常] --> HandleException["返回友好错误消息
ChatModelResult(chat=错误信息)"] HandleException --> End %% 样式定义 classDef serviceClass fill:#e1f5fe,stroke:#01579b,stroke-width:2px classDef preProcessClass fill:#e8eaf6,stroke:#283593,stroke-width:2px classDef decisionClass fill:#fff3e0,stroke:#e65100,stroke-width:2px classDef ragClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef resultClass fill:#fce4ec,stroke:#880e4f,stroke-width:2px classDef errorClass fill:#ffebee,stroke:#c62828,stroke-width:2px class ToChat,CallLLM serviceClass class QueryRewrite,QueryCompress,HyDE preProcessClass class CommandCheck,RagCheck,HyDECheck,ImageCheck,DocCheck,HasContent,AddResult decisionClass class RagSearch,RagSearchHyde,BuildRAGContext,ReplaceTemplate,AddToMySpace ragClass class ReturnSuccess,ReturnFail,ReturnNoContent,BuildResponse resultClass class CatchException,HandleException errorClass ``` ### 检索流程 ```mermaid flowchart TD A([开始: ragSearch调用]) --> PARALLEL[并行执行双重检索] %% ====== 向量检索分支 ====== PARALLEL --> EMB[向量检索: embeddingCallBack] EMB --> FILTER_CHECK{groupId/fileId
过滤条件?} FILTER_CHECK -->|无过滤| GLOBAL_SEARCH["全局向量搜索
similaritySearch(query, topK, threshold)"] FILTER_CHECK -->|有过滤| BUILD_FILTER["FilterExpressionValidator
构建安全的过滤表达式"] BUILD_FILTER --> FILTERED_SEARCH["带过滤条件向量搜索
fileId == 'xxx'
或 groupId IN (...)"] GLOBAL_SEARCH --> EMB_RESULT[向量检索结果
doc.score = 向量分数] FILTERED_SEARCH --> EMB_RESULT %% ====== BM25检索分支 ====== PARALLEL --> BM25["BM25全文检索: bm25CallBack
Lucene增量索引"] BM25 --> LUCENE_SEARCH["LuceneIndexService.searchDocuments
全文索引搜索"] LUCENE_SEARCH --> BM25_IDS[获取搜索结果ID列表 + BM25分数] BM25_IDS --> DB_LOOKUP["vectorStoreRagDao.selectRagByIds
根据ID查询向量库获取内容"] DB_LOOKUP --> BM25_RESULT["BM25检索结果
metadata.bm25Score = BM25分数"] %% ====== 分数融合 ====== EMB_RESULT --> MERGE["mergeDocumentsWithScores
按ID合并两路结果"] BM25_RESULT --> MERGE MERGE --> MERGE_DETAIL["合并逻辑:
相同ID → 合并bm25Score到向量文档
仅BM25 → 补充向量分数=0
仅向量 → 补充bm25Score=0"] MERGE_DETAIL --> MERGED[融合后的文档列表] %% ====== 融合策略选择 ====== MERGED --> STRATEGY_CHECK{融合策略
rag.fusion.strategy} STRATEGY_CHECK -->|"rrf
(默认)"| RRF["rankRRF: Reciprocal Rank Fusion
score = 1/(k+embRank) + 1/(k+bm25Rank)
k=60, 无需调参"] STRATEGY_CHECK -->|"linear"| LINEAR["rankLinearWeighting: 线性加权
Min-Max归一化 → 加权求和
权重: 0.6, 0.4"] RRF --> FUSED LINEAR --> FUSED[Fused: 融合排序后文档列表] %% ====== HyDE增强路径(ragSearchWithHyde) ====== FUSED -.->|如果启用HyDE| HYDE_PATH HYDE_PATH["HyDE路径 (ragSearchWithHyde):
embeddingCallBack(hydeText, topK=hydeTopK)"] HYDE_PATH --> RRF_HYDE["rankRRFForHyde: 二次RRF融合
hydeRrfScore = 1/(k+directRank) + 1/(k+hydeRank)"] RRF_HYDE --> RERANK_CHECK %% ====== ReRank vs 去重 分支选择 ====== FUSED --> RERANK_CHECK{是否启用ReRank?} %% ReRank启用:跳过去重,全量候选交给cross-encoder RERANK_CHECK -->|启用| RERANK_TOPK["取Top rerankTopK文档
(跳过去重,保留完整候选集)"] RERANK_TOPK --> RERANK_CALL["ReRankProcessor.rerank
cross-encoder精排"] RERANK_CALL --> FINAL_TOPN %% ReRank未启用:用去重作为启发式兜底 RERANK_CHECK -->|未启用| DEDUP["DedupFactory.create().deduplicate()"] DEDUP --> DEDUP_STRATEGY{去重策略
rag.dedup.strategy} DEDUP_STRATEGY -->|none| NO_DEDUP[不去重: 直通] DEDUP_STRATEGY -->|fileId-dedup| FILE_DEDUP["基于fileId父文档去重
同一文档最多N个chunk"] DEDUP_STRATEGY -->|mmr| MMR_DEDUP["MMR语义多样性去重
λ=0.7 平衡相关性与多样性"] DEDUP_STRATEGY -->|"mmr+fileId-dedup"| COMBO_DEDUP["先fileId去重
再MMR语义去重"] NO_DEDUP --> FILTER_BRANCH FILE_DEDUP --> FILTER_BRANCH MMR_DEDUP --> FILTER_BRANCH COMBO_DEDUP --> FILTER_BRANCH FILTER_BRANCH{融合策略?} FILTER_BRANCH -->|RRF| DIRECT_TOPN["直接取Top llmRagTopN"] FILTER_BRANCH -->|Linear| THRESHOLD_FILTER["按weightedScore阈值过滤
≥ linearWeightingNum"] THRESHOLD_FILTER --> FILTERED_TOPN["取Top llmRagTopN"] DIRECT_TOPN --> FINAL_TOPN FILTERED_TOPN --> FINAL_TOPN %% ====== 最终结果 ====== FINAL_TOPN["限制最终结果数量
llmRagTopN (默认5)"] --> TT([结束: 返回搜索结果]) %% 样式定义 classDef searchMethod fill:#e3f2fd,stroke:#1976d2,stroke-width:2px classDef fusionMethod fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef dedupMethod fill:#fff8e1,stroke:#f57f17,stroke-width:2px classDef rerankMethod fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px classDef dataFlow fill:#fff3e0,stroke:#f57c00,stroke-width:2px classDef decisionClass fill:#fce4ec,stroke:#c2185b,stroke-width:2px classDef hydeClass fill:#ede7f6,stroke:#4527a0,stroke-width:2px class EMB,BM25,LUCENE_SEARCH,DB_LOOKUP searchMethod class MERGE,RRF,LINEAR fusionMethod class NO_DEDUP,FILE_DEDUP,MMR_DEDUP,COMBO_DEDUP dedupMethod class RERANK_TOPK,RERANK_CALL rerankMethod class EMB_RESULT,BM25_RESULT,MERGED,FUSED dataFlow class FILTER_CHECK,STRATEGY_CHECK,RERANK_CHECK,DEDUP_STRATEGY,FILTER_BRANCH decisionClass class HYDE_PATH,RRF_HYDE hydeClass ``` ## 功能发展历程 ### 核心功能建设期 (2024.12 - 2025.01) - [x] 基础对话功能和记忆机制 (2024.12) - [x] RAG检索问答系统 (2024.12) - [x] 函数调用功能实现 (2024.12) - [x] JWT用户认证授权 (2024.12) - [x] 前端工程页面开发 (2024.12) - [x] 用户角色权限体系 (2024.12) - [x] 聊天记录管理 (2024.12) - [x] OpenAI兼容模型接入 (2025.01) ### 功能扩展期 (2025.02 - 2025.03) - [x] Spring AI框架升级优化 (2025.03) - [x] 提示词配置管理 (2025.03) - [x] 文件组和文件管理 (2025.03) - [x] RAG召回测试工具 (2025.03) - [x] Docker部署方案 (2025.03) ### 体验优化期 (2025.04 - 2025.05) - [x] 提示词优化功能 (2025.03) - [x] 文字向量化管理 (2025.04) - [x] ReRank重排序机制 (2025.04) - [x] Markdown格式支持 (2025.04) - [x] 大文档处理优化 (2025.04) - [x] 文档引用和反馈 (2025.04-05) - [x] Spring AI 1.0正式版 (2025.05) - [x] 权限控制完善 (2025.05) - [x] RAG文件导出功能 (2025.05) ### 功能完善期 (2025.06 - 2025.08) - [x] ReRank模型集成 (2025.06) - [x] 深度思考模式 (2025.06) - [x] 消息通知系统 (2025.06) - [x] 权重计算优化 (2025.07) - [x] 仪表盘统计功能 (2025.07) - [x] 用户信息完善 (2025.07) - [x] 文件组权限共享 (2025.07) - [x] MinIO对象存储 (2025.08) - [x] 多专家模式对话 (2025.08) - [x] 个人知识空间 (2025.08) - [x] 新增图片和文档的多模态识别 (2025.08) - [x] 混合检索技术 (2025.10) - [x] 移动端适配,更多的使用细节处理 (2025.12) ### RAG深度优化期 (2026.04) - [x] Spring AI 1.1.4版本升级 (2026.04) - [x] 向量写入批次优化,入库速度提升10-50倍 (2026.04) - [x] BM25增量索引同步,消除数据不一致 (2026.04) - [x] Redis缓存基础设施,替代内存缓存 (2026.04) - [x] 查询压缩CompressionQueryTransformer,多轮指代消解 (2026.04) - [x] 查询改写RewriteQueryTransformer,口语化→检索友好 (2026.04) - [x] HyDE假设性答案嵌入检索 (2026.04) - [x] RRF(Reciprocal Rank Fusion)融合替换线性加权 (2026.04) - [x] MMR(Maximal Marginal Relevance)多样性去重 (2026.04) - [x] FixedWindow滑动窗口overlap分块 (2026.04) - [x] 网页解析保留Markdown结构 (2026.04) - [x] filterExpression参数校验安全修复 (2026.04) - [x] RAG参数全面外部化到application.yml (2026.04) - [x] Streaming流式响应 (2026.05) ### 规划中功能 - [ ] Contextual Retrieval:chunk前置上下文,降低检索失败率 - [ ] 语义分块 + SummaryMetadataEnricher - [ ] 多查询扩展MultiQueryExpander - [ ] Embedding缓存,避免重复计算 - [ ] 移除function tool代码,转为全面对MCP的支持,实现MCP热插拔 - [ ] 计划开发与之配合的MCP服务 - [ ] 提供针对模型等信息的配置页面,可自由更换模型api,rag配置,权重等 - [ ] 添加更多的拆分文档方式(在全文中划线拆分chunk) - [ ] 不满意的回答过滤文档后重新回答 - [ ] 在对话中标记文档,记录被调用次数多并且满意的文档,区分高低价值 便于维护 - [ ] 支持调用更多的服务解析文档 ## 开源协议 本项目采用 **Apache License 2.0** 许可协议,详情见 `LICENSE` 文件。使用前请务必遵守协议条款。 ## 特别声明 1. 作者不对因使用本项目代码引发的任何法律风险或技术问题承担责任。 2. 本项目与作者任职单位(如有)无关,非职务作品,未利用雇主资源、技术文档或商业信息。 ## 联系与支持 如有问题,请联系开发团队或提交 Issue。