FEATURED · 精选文章

基于RAG的企业知识库问答:文心千帆+Milvus+ApacheTika实战

发布时间 / 2026/9/2 3:00:11
来源 / 创域科博编辑部
栏目 / 资讯中心
基于RAG的企业知识库问答:文心千帆+Milvus+ApacheTika实战 简介这是一套基于知识库的智能对话助手系统的完整工程源码面向具备Python或Java基础、希望在大模型应用方向快速起步的开发者与学习者。系统整合了文心千帆大语言模型、Milvus向量数据库与ApacheTika文档解析工具实现了从多格式文档解析、向量化存储、语义检索到智能问答的完整业务闭环。压缩包内共50个文件以39个Java源码文件为主辅以YAML、XML、JSON等配置文件、HTML前端页面、README说明文档、测试图片与附赠资料包体约22.64MB项目结构清晰可导入IDE直接运行调试。已有93人学习浏览。除主工程外还提供说明文件和资源附件能帮助读者理解LangChain风格的知识库构建思路、Milvus相似度检索优化以及Tika多格式文本抽取的接入细节通过学习可掌握大模型API调用、向量索引设计与文档解析管线搭建等关键环节适合用于毕业设计、课程项目或企业知识库问答系统的二次开发基线。 我们直接进入正题。这篇不是我第一次做企业知识库问答但把文心千帆、Milvus、ApacheTika这三样串在一起做成一个能落地的文档智能问答助手遇到的问题和绕过的坑确实值得整理出来。如果你正在做“RAG知识库”“智能客服问答系统”或者想给团队内部的文档库加一个能对话的入口这篇文章应该能帮你少走不少弯路。下面所有内容都基于我这个项目的实际搭建过程。1. 项目整体设计一条从文档到答案的RAG流水线1.1 需求拆解与整体方案选型所以回到这类系统的本质问题企业内部的知识分散在Word、PDF、PPT、扫描件里用户想找某个制度、某个参数、某个历史结论的时候靠CtrlF全文搜索根本搜不到语义相近的内容。大语言模型虽然能听懂自然语言但它的训练数据里没有你企业内部的文档直接问等于让一个博学但不了解你家情况的人回答你。解决思路就是RAG检索增强生成先把文档向量化存起来用户提问时先从向量库里检索最相关的片段再把这些片段拼进提示词交给大模型生成回答。整体流程拆开看就是五道工序文档解析ApacheTika把各种格式转成纯文本文本切片把长文切成适合向量化的小块向量化Embedding模型把文本变向量存储检索Milvus存向量并在召回阶段做相似度检索生成回答把检索结果交给文心千帆生成答案这五道工序单独看都不复杂但把它们串成一个可用、可控、效果不跑偏的系统每一步都有不少细节。1.2 核心技术选型为什么是这三件套先说为什么选Milvus。做向量检索可选的东西挺多faiss、pgvector、elasticsearch、Milvus都可以。这个项目选Milvus主要是因为它的混合检索能力企业文档场景里一个用户问“2023年的财务报销制度”这不是单纯的关键词匹配也不是单纯的向量相似度而是同时涉及文本过滤条件时间范围、制度类型加语义理解Milvus的标量过滤加向量检索能一次性解决这个问题。另外它的Java SDK和LangChain4j集成都比较成熟对后端开发很友好。文心千帆这边当时核心考虑点是中文理解能力尤其是涉及中文长文档和偏口语化的提问时整体效果稳定。同时它兼顾企业级API的服务方式在私有化部署和调用链路审计方面做得比较完善这对企业内部系统是个加分项。如果你没有类似约束换成其他通用大模型API也能照搬这套架构RAG的核心并不绑定具体某个大模型。ApacheTika的价值在于它把几十种文档格式的解析能力收敛成一个统一入口。Word、PDF、PPT、老式DOC、甚至邮件eml它都能抽成纯文本省去了为每种格式单独接SDK的繁重工作。1.3 影响范围评估这套系统到底能干什么文档智能问答系统的最终效果取决于四层结构数据层能解析多少格式、索引层能存多少文档、检索层能召回多准的片段、生成层能组织出多自然的回答。我把这个系统搭好以后把公司内部的一份产品白皮书和工作流程制度文档传了进去同时跑通了以下场景问“报销流程需要哪几步”问“产品支持哪些部署方式”问“服务超时时间默认是多久”回答基本都能从文档片段里定位到出处并生成可读的中文回复。这意味着你完全可以把它直接嵌入到企业微信、钉钉的机器人入口变成内部员工的“文档问答助手”。2. 文心千帆、Milvus、ApacheTika 分工明细与核心原理2.1 ApacheTika多种格式文档解析的“万能开瓶器”Tika在外行人看起来就是个文件解析工具但真正把它放进生产环境你就会发现它最核心的价值不是“能解析多少格式”而是“解析出来的文本质量有多稳定”。我用它解析同一个PDF文件时普通文本抽取和带OCR的抽取结果差异很大扫描件如果不走OCR出来的就是空白文本。这一点后面会单独讲。Tika的调用方式有两种一种是用Tika Server起一个独立进程通过HTTP上传文件拿解析结果适合多语言、多服务复用的场景另一种是在Java项目里引入tika-core和tika-parsers-standard-package直接通过InputStream调用。我这次选的是第二种省掉一个进程运维成本但要注意它解析超大PDF时会比较吃内存。Tika解析的准入门槛很低几行代码就能跑通Tika tika new Tika(); try (InputStream stream new FileInputStream(sample.pdf)) { String content tika.parseToString(stream); System.out.println(content); }但真实场景不能这么粗暴我后面会在“实操过程”里给出完整的配置和超时参数。2.2 文心千帆大模型生成回答的“知识加工器”文心千帆在本项目里承担的是最后一步根据检索到的文档片段生成回答。它的核心接口调用方式和OpenAI风格一致走HTTP调用但有一个关键点需要在系统设计时就考虑清楚上下文长度限制。我现在用的版本单次请求能携带的上下文有限而RAG系统恰恰是“片段越多回答越准”。如果检索回来的片段有5个每个500字拼在一起就是2500字再加上系统提示词和用户问题稍微长一点的文档就会触及窗口上限这时候你的系统必须做取舍。我用的是“先大体量召回再截断置顶”的策略检索阶段返回最相关的topK个片段拼装提示词时按相关度排序超出上限的部分直接丢弃保证最重要的信息永远能进到上下文里。文心千帆的另一个特性是支持在API层做简单的安全审核和自定义参数设置比如temperature、top_p。我在这套系统里把temperature设到了0.1目的是让回答尽量忠于文档原意少一点自由发挥。做企业知识库问答时“准确”比“生动”重要得多。2.3 Milvus向量数据库文档语义的“记忆宫殿”Milvus在这套系统里是我个人认为最能提升系统整体效果的一环。它不只是在做向量相似度检索它更是一个完整的非结构化数据服务支持动态Schema、标量字段过滤、混合检索、分区和索引管理。我这次用的是Docker Compose安装的Milvus Standalone版本组件包含etcd元数据存储、MinIO对象存储和Milvus主服务。数据存储流是文档解析并向量化后Embedding向量和文档原文、元数据文档名、章节、页码一起存入Milvus的Collection里每个Collection的字段设计大致是id主键自增doc_name标量字段用于展示来源chunk_text原文切片用于拼提示词时引用chunk_vectorFloatVector用于向量检索seq_id标量字段控制同一文档内切片顺序Milvus对这个项目的最大价值在于当知识库文档量过万甚至更高时它的分段检索性能依然稳定而且可以根据业务需求划分多个Collection比如分部门、分权限建立隔离的知识库。这在没有Milvus的时候用普通数据库根本没法玩。3. 搭建过程从零开始跑通一个文档问答助手3.1 环境准备与Milvus安装先把基础环境列一下我用的版本组合是经过多轮验证的稳定搭配不一定是最新但踩坑最少组件版本/配置说明JDK17Tika和LangChain4j都要求较高版本Milvus2.3.xStandalone通过Docker Compose安装文心千帆API最新稳定版需要申请API KeyEmbedding模型bge-large-zh-v1.5本地或千帆API内置方案不同后面细说ApacheTika2.9.x通过Maven引入LangChain4j0.33可选用于简化调用链路Milvus的安装用Docker Compose最省事官方仓库里有对应版本的compose文件。这里需要特别注意一个点Docker安装Milvus时默认只会把Milvus的端口映射出来你还需要确认etcd和MinIO对应的端口在容器内部是否正常工作如果资源紧张导致etcd启动不了Milvus会一直处于“服务未就绪”状态。我遇到过一次最后通过增加内存限制参数解决。对于不想用Docker的同学Milvus也提供Windows下的非容器安装方式但需要自己编译或下载预编译包还要处理一堆依赖我这边没有采用还是推荐Docker最便捷。Docker Compose方式确认集群就绪后可以用官方提供的一个Python或Java客户端做连通性测试确保能正常创建Collection。3.2 文档解析与文本切片实现文档解析这层表面是Tika一把梭实际上你要处理的问题比想象中多。首先是格式兼容Tika针对不同文件会自动匹配不同的Parser但你要在代码里给一个超时和大小上限防止解析超大文件时把程序拖死。我的实现方案是在服务里做了一个异步解析任务接收上传文件后先落盘存储再异步调用Tika解析最后把返回的纯文本存到数据库里等待向量化。这个“异步”的设计非常关键因为Tika解析一份大PDF可能要几十秒如果同步处理前端请求早就超时了。文本切片是后续检索准确度最直接的变数。切太大单个片段语义过多检索不精准切太小片段之间的信息割裂生成回答时上下文不连贯。我这边试过固定字符数切分也试过按自然段落切分最终的结论是“固定窗口重叠”最稳窗口大小取500个字符、重叠50个字符对中文文档比较合适。对于有明确章节结构的Markdown或Word文档按标题层级切分更合理但通用性弱一些。我在系统里实现了一个简单的切分策略优先按标题切没有标题再用窗口切。3.3 向量化与Milvus写入文本向量化是整个系统的“承重墙”。最开始我用的是文心千帆提供的Embedding接口直接用官方SDK调用好处是免运维缺点是每个文本块都是一次远程调用文档量大的时候会产生费用和延迟。后来换成了本地部署的bge系列模型用JVector或LangChain4j的Embedding接口统一接入实现了一处改动、全链路生效的效果。为什么选择bge系列因为对中文的支持和领域泛化性都不错而且在检索任务上的表现完全能满足知识库问答场景。qwen embedding我也试用过整体效果也很好具体选择看你的部署环境和成本控制。如果只是演示和中小规模知识库用云API更省心如果是离线环境或者文档量极大本地Embedding是必选项。向量写入Milvus的代码和普通数据库操作很像我用的是LangChain4j的MilvusEmbeddingStore封装因为它直接帮我把Embedding和Milvus insert串成一条链路减少重复代码。但如果你的场景有复杂的过滤需求我建议直接用Milvus Java SDK写原生调用可控性更高。// LangChain4j方式示例 MilvusEmbeddingStore embeddingStore MilvusEmbeddingStore.builder() .host(localhost) .port(19530) .collectionName(doc_kb) .dimension(1024) .build(); embeddingStore.add(textSegments, embeddingModel.embedAll(textSegments));注意dimension必须和你选的Embedding模型输出维度一致否则插入数据会报错。本地bge模型一般输出1024维如果用别的模型记得先查一下对应维度。3.4 检索与问答生成的完整链路检索阶段我在Milvus里存完向量之后写了两个查询函数一个是纯向量检索输入用户问题转成向量通过query接口拿到最接近的topK片段另一个是带条件的混合检索根据doc_name等标量字段先过滤再算向量相似度。两个函数配合使用比如“只看2023年产品手册里的内容”这类带条件的提问就能很好满足。拿到检索片段后组装提示词这步我花了一些精力调优。最终模板大概是这样的你是一个企业知识库问答助手请仅根据下方提供的上下文内容回答用户的问题。 如果上下文中没有相关信息请明确回答“未在知识库中找到相关信息”不要编造。 上下文 {context} 用户问题{question}模板里特意加了“不要编造”的约束这是调了几次后加上的因为早期版本里大模型偶尔会自行“发挥”把知识库里没有的信息说得跟真的一样这在企业场景里是无法接受的。然后通过文心千帆的聊天补全接口把提示词和用户问题发过去拿到回答后返回给前端。这个链路从用户输入到收到回答在普通文档量下实测大约3到6秒其中检索和向量化占了大头大模型生成倒是很快。如果你觉得慢可以优化向量化那步把Embedding结果加一层本地缓存重复内容不重复向量化。4. 各路问题排查与调优实践4.1 检索不准确、回答牛头不对马嘴怎么办这是RAG知识库被问得最多的一个问题也是最容易出现挫败感的环节。我系统上线后第一批测试就遇到一个很典型的案例用户问“报销流程”检索出来的片段反而是产品介绍里的“报销模式”两个词表面相近语义却完全不同。问题出在切片粒度上——产品介绍的那段里“报销模式”和“流程”出现在同一个大片段里向量检索时被无关词汇干扰了。排查方法是打开Milvus的查询日志把召回的top10片段全部打印出来看。如果前几个片段跟问题明显不相关通常不是向量检索本身的问题而是切片或Embedding模型的问题。我可以给你一个排查顺序先检查切片是否过大大片段包含的噪声信息太多再检查Embedding模型是否适用于该领域通用模型对专业术语理解弱最后看是否有停用词干扰比如“的”“了”“流程”“模式”这类高频词拉高了无关片段的相似度。在我这个项目里把切片从800字符缩小到500字符、重叠从100缩小到50之后检索准确性提升非常明显。4.2 Tika解析出来的文本是空的或乱码这个问题我遇到过两次。一次是扫描版PDF文本提取出来就是空字符串一次是老式DOC文件用默认解析器解出来全是乱码。扫描版PDF的正解是加OCR能力Tika可以通过配置TesseractOCR来实现但这需要你额外安装Tesseract并下载中文语言包而且OCR速度和精度都有代价。我这边考虑到项目进度暂时只在代码里识别出了“空文本”情况提醒用户手动转成Word文件再上传。老式DOC乱码的问题多半是charset检测出错Tika本身提供了编码检测能力但遇到特定中文字符集可能失效。我的做法是解析后增加一个编码探测兜底检测到乱码就改用备选解析方式。具体细节不展开但你可以记住一句话Tika不是万能的预检比解析更重要。4.3 Milvus检索慢或结果排序混乱Milvus本身性能很好但如果你的Collection索引参数设置不对查询会明显变慢。我现在用的索引类型是IVF_FLATnlist设为1024查询时nprobe设为32这个配置在性能和数据量之间比较均衡。如果数据量更大可以切到HNSW速度更快但构建索引时间会长同时更吃内存。另一个高频问题是多个Collection之间的查询结果排序混乱。Milvus不会自动按相似度分数全局排序你在查询时必须指定limit参数而且多个分片返回的结果需要在应用层做merge。如果你用Java SDK直接用query接口传topK参数即可它会自动按相似度降序返回不需要自己merge。5. 落地扩展知识库问答系统的下一步演进系统跑通之后我觉得最值得继续深挖的方向有四个多租户隔离。当前Collection结构可以平滑扩展为“一个租户一个Collection”在初始Collection的Schema里加tenant_id字段查询时用过滤条件强制限定数据范围防止跨部门越权检索。重排Rerank阶段。目前是向量检索直接出topK如果你想追求更高精度可以在Milvus检完之后再接一个重排模型重新精排片段再把最精的结果交给大模型。这部分LangChain4j也有对应封装但需要额外引入大模型或专用重排模型。增量学习与文档更新。文档更新后切片、向量化和索引替换的全流程处理当前系统没有做全自动需要定时任务或监听文件变化触发。接入聊天前端。有了REST API之后只需要再套一个Web聊天界面或者企业IM机器人就可以让整个系统变成一个实时问答助手。我目前就是给内部做了个简单的管理后台上传文档、查看检索日志、测试问答效果。5.1 最后分享一个调优技巧在我实际使用这套系统的过程中最影响体验的一个配置是“检索置信度阈值”。Milvus返回的相似度分数对不同的Embedding模型取值范围差异很大直接拿0.7这种固定阈值裁掉低分片段往往不靠谱。我最后的做法是先不设阈值把topK片段全部拿到然后根据分数的相对差异第一名的分数与第K名的分数之间是否有断层动态决定保留几个片段。这个方法在大量测试里表现稳定值得一试。另外插一句我自己的感受RAG系统的上限不在于大模型选得多先进而在于数据进得干不干净。先把文档解析和切片做好比花时间调prompt有效得多。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻