☰
Java团队如何用LangChain4j与LangGraph4j构建RAG知识库系统
2026/9/28 13:12:20 网站建设 项目流程

1. 为什么Java团队需要自己的RAG知识库系统

1.1 从一次内部技术分享的尴尬说起

上个月我在团队内部做了一次技术分享,主题是“如何让大模型回答我们自己的业务问题”。演示环节,我随手问了一句“咱们订单系统的超时补偿逻辑是怎么设计的”,模型洋洋洒洒回答了一大段,听起来头头是道,但在座的几个老同事面面相觑——它说的补偿阈值、重试次数、幂等键设计,跟我们系统里实际跑的完全对不上。那一刻我意识到,通用大模型的“知识”再广,也覆盖不到我们内部沉淀了五六年的业务细节。

这就是RAG(Retrieval-Augmented Generation,检索增强生成)要解决的核心问题。简单说,RAG就是给大模型配一个“外挂知识库”:用户提问时,系统先从知识库里检索出最相关的几段资料,再把这些资料连同问题一起塞给大模型,让它基于真实资料来回答,而不是凭记忆瞎编。这个思路听起来不复杂,但真正落地到Java技术栈里,坑比想象中多得多。

我选择用LangChain4j + LangGraph4j这套组合来构建,原因很直接:团队是清一色的Java后端,Spring Boot、Maven、MyBatis这套东西闭着眼睛都能写,你让我为了做个RAG去学Python生态的LangChain,学习成本和维护成本都太高。LangChain4j是LangChain的Java移植版,API设计思路一脉相承,但完全贴合Java开发者的习惯;LangGraph4j则把“多步骤编排”这件事用图的方式表达出来,特别适合RAG这种“检索→重排→生成→校验”的多阶段流程。

这篇文章适合谁看?如果你是有一定Java基础、想在自己项目里落地RAG的后端开发,或者正在技术选型阶段纠结用Spring AI还是LangChain4j,再或者你已经用Python跑通过Demo但想迁移到Java生产环境,那接下来的内容应该能帮你少走不少弯路。我会从整体架构讲到每个环节的具体实现,包括我踩过的坑和实测有效的参数配置。

1.2 RAG到底解决了大模型的哪些硬伤

在动手之前,得先把RAG的价值边界想清楚,不然很容易做成一个“看起来能用但实际不好用”的玩具。大模型有三个绕不过去的硬伤,RAG分别对应解决:

知识时效性问题。模型的训练数据有截止日期,你问它上个月刚发布的内部规范,它不可能知道。RAG把最新文档实时检索进来,模型就能回答。

私有知识缺失问题。公司内部的接口文档、数据库表结构、业务规则,这些永远不会出现在公开训练语料里。RAG把这些私有资料作为检索源,模型就能“读懂”你的业务。

幻觉问题。模型在不确定的时候会编造看似合理的答案,这在业务场景里是致命的。RAG通过“基于检索到的原文回答”这个约束,大幅降低幻觉概率——注意是降低而不是消除,后面我会讲怎么进一步校验。

但RAG不是银弹。它解决不了模型本身的推理能力上限,也解决不了检索质量差导致的“垃圾进垃圾出”。我见过太多团队兴冲冲搭了个RAG Demo,结果检索命中率惨不忍睹,最后归咎于“RAG不行”。其实问题往往出在文档切分、向量化策略、检索重排这些细节上,这些恰恰是本文的重点。

1.3 技术选型:LangChain4j、LangGraph4j与Spring AI的取舍

热词里有个问题被反复提到:“现在到底用Spring AI还是LangGraph4j?”我的答案是:看你的流程复杂度。

如果你的RAG流程是线性的——用户提问、检索、拼Prompt、调模型、返回,那Spring AI足够用,它的API更简洁,和Spring Boot集成更丝滑,适合快速验证。但一旦你的流程出现分支、循环、条件判断,比如“检索结果置信度低就换个查询重试”“生成后要过一遍事实校验,不通过就重新生成”,Spring AI的链式调用就会变得很别扭,这时候LangGraph4j的图编排优势就出来了。

LangGraph4j的核心概念是“状态图”:你定义一个共享的State对象,然后定义若干Node(节点)和Edge(边),每个节点读取State、处理、写回State,边决定下一步走哪个节点。这跟工作流引擎的思路很像,但更轻量,专门为LLM应用设计。我实测下来,用LangGraph4j把RAG流程拆成“查询改写→向量检索→关键词检索→结果融合→重排→生成→校验”七个节点,每个节点独立可测,出问题能精确定位,比一坨链式调用好维护太多。

至于LangChain4j和LangChain的关系,可以理解为“同源不同实现”。LangChain4j不是简单翻译,它针对Java生态做了不少适配,比如用Maven管理依赖、用Builder模式构建组件、和Spring Boot的自动配置集成。它的开发文档质量不错,但更新速度偶尔跟不上LangChain,遇到文档没覆盖的API,直接看源码比翻文档快。

2. 环境搭建与核心依赖配置

2.1 Maven依赖清单与版本选择逻辑

先把依赖理清楚。LangChain4j的模块化做得比较细,你需要什么功能就引什么包,不要一股脑全引进来,不然依赖冲突会让你怀疑人生。下面是我实测稳定的一套组合:

<properties> <langchain4j.version>0.35.0</langchain4j.version> <langgraph4j.version>0.0.3</langgraph4j.version> <java.version>17</java.version> </properties> <dependencies> <!-- LangChain4j核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- 对接OpenAI兼容接口的模型 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- 向量存储:这里用内存版做演示,生产换PGVector --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- LangGraph4j图编排 --> <dependency> <groupId>org.bsc.langgraph4j</groupId> <artifactId>langgraph4j-core</artifactId> <version>${langgraph4j.version}</version> </dependency> <!-- 文档解析:PDF和Word --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-document-parser-apache-pdfbox</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> </dependency> </dependencies>

版本选择上有个经验:LangChain4j的0.35.0是我目前用下来最稳的版本,0.36.x开始有些API做了不兼容调整,如果你不是追新党,建议锁在0.35.0。LangGraph4j还比较年轻,0.0.3版本基本够用,但要注意它的API可能在小版本间有变动,生产环境务必锁死版本号。

Java版本我强烈建议17起步。LangChain4j大量使用了Record、Sealed Class、Pattern Matching这些新特性,用Java 8会各种报错。而且17是LTS,虚拟线程在21才正式,但17的生态兼容性最好。

2.2 模型接入:OpenAI兼容接口的通用配置

不管你用的是哪家的模型服务,只要它提供OpenAI兼容的接口,LangChain4j都能对接。配置方式如下:

ChatLanguageModel chatModel = OpenAiChatModel.builder() .baseUrl("https://你的模型服务地址/v1") .apiKey(System.getenv("MODEL_API_KEY")) .modelName("你的模型名称") .temperature(0.1) .timeout(Duration.ofSeconds(60)) .maxRetries(2) .build(); EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("https://你的模型服务地址/v1") .apiKey(System.getenv("MODEL_API_KEY")) .modelName("你的向量模型名称") .build();

这里有几个参数值得说道。temperature设0.1,因为RAG场景要的是忠实于检索内容,不是发挥创造力,温度越低输出越稳定。timeout设60秒,RAG的Prompt通常比较长(塞了好几段检索结果),生成时间会比普通对话长,设太短容易超时。maxRetries设2,网络抖动时自动重试,但别设太多,不然一个请求卡住会拖垮整个线程池。

API Key千万别硬编码在代码里,用环境变量或者配置中心。我见过有人把Key提交到Git仓库,第二天就被刷爆了额度。用System.getenv是最低要求,生产环境建议走配置中心加密存储。

2.3 向量存储选型:从内存到PGVector的平滑迁移

开发阶段用内存向量存储最方便,重启数据就没了,适合快速迭代。但生产环境必须用持久化的向量数据库。我的建议是PostgreSQL + pgvector扩展,理由很实在:你们团队大概率已经有PG了,不用额外维护一套数据库;pgvector的检索性能对于百万级向量完全够用;而且它支持SQL过滤和向量检索混合查询,这在RAG里非常实用。

LangChain4j对PGVector的支持很完善:

EmbeddingStore<TextSegment> embeddingStore = PgVectorEmbeddingStore.builder() .host("localhost") .port(5432) .database("ragdb") .user("raguser") .password("yourpassword") .table("knowledge_vectors") .dimension(384) // 必须和embedding模型维度一致 .build();

dimension这个参数是重灾区。它必须和你用的向量模型输出维度严格一致,all-minilm-l6-v2是384维,OpenAI的text-embedding-3-small是1536维。维度对不上,插入数据时直接报错。更坑的是,如果你中途换了向量模型,维度变了,之前存的所有向量都得重新生成,所以选模型要慎重,别频繁换。

3. 知识库构建:从原始文档到可检索向量

3.1 文档加载与解析的实战细节

知识库的原料可能是PDF、Word、Markdown、HTML,甚至数据库里的工单记录。LangChain4j提供了DocumentLoader体系,但实际用起来,解析质量参差不齐。我的经验是:PDF解析是最容易出问题的环节。

Apache PDFBox解析出来的文本,经常出现段落错乱、表格内容混在一起、页眉页脚重复出现的问题。我处理过一份产品需求文档,解析后每页的“机密”水印都混进了正文,检索时经常把水印当内容返回。解决办法是在解析后加一层清洗:

Document document = FileSystemDocumentLoader.loadDocument( Paths.get("/data/docs/需求文档.pdf"), new ApachePdfBoxDocumentParser() ); // 清洗:去掉页眉页脚、多余空白、特殊字符 String cleanedText = document.text() .replaceAll("机密|内部资料|第\\d+页", "") .replaceAll("\\s{3,}", "\n") .replaceAll("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]", ""); Document cleaned = Document.from(cleanedText, document.metadata());

Word文档用POI解析相对靠谱,但要注意.doc和.docx是两套API,.doc用HWPF,.docx用XWPF。热词里有人问“java poi word能生成图表吗”,答案是能,但那是生成方向,解析方向我们只关心文本和表格。表格内容建议单独提取出来,转成Markdown表格格式再拼回正文,这样检索时表格的语义更完整。

3.2 文档切分策略:为什么固定长度切分是个坑

文档切分(Chunking)是RAG里最容易被低估的环节。很多人直接用固定长度切,比如每500字符一刀,结果把一句话从中间切断,检索出来的片段语义不完整,模型看了也懵。

我的策略是递归字符切分 + 语义边界优先。LangChain4j的DocumentSplitters.recursive方法支持按优先级依次尝试分隔符:

DocumentSplitter splitter = DocumentSplitters.recursive( 500, // 目标块大小 50, // 块间重叠 new RecursiveCharacterTextSplitter( List.of("\n\n", "\n", "。", ";", ",", " ", "") ) );

逻辑是这样的:先按空行(段落)切,如果某段还是超过500字符,再按换行切,再超就按句号切,以此类推。重叠50字符是为了防止关键信息刚好落在切分边界上,前后两块都各带一点,检索时至少有一块能命中。

但递归切分也不是万能的。对于结构化文档(比如API文档、数据库表说明),更好的做法是按语义单元切分:一个接口的说明作为一个块,一张表的字段说明作为一个块。这样检索出来的片段自包含,不需要前后文就能理解。我处理接口文档时,会先用正则把每个接口的段落提取出来,再单独向量化。

3.3 向量化与入库:批量处理的性能优化

向量化是调用Embedding模型把文本转成向量,这一步是IO密集型操作,批量处理能显著提升效率。LangChain4j的EmbeddingStoreIngestor封装了完整流程:

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(splitter) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); ingestor.ingest(document);

但默认配置下,它是逐条调用Embedding接口的,文档多了会很慢。优化方法是批量向量化:把切分后的文本按每批20-50条打包,一次性调接口。大多数Embedding服务都支持批量输入,能减少网络往返次数。我实测下来,批量处理比逐条处理快5-8倍。

入库时还有个细节:metadata的设计。每个向量块除了文本内容,还要存来源文件名、章节标题、页码、更新时间等信息。这些metadata在检索时可以用来过滤,比如“只在最近半年的文档里搜”“只搜产品部的文档”。LangChain4j的TextSegment支持附加metadata,检索时通过Filter条件筛选。

4. 用LangGraph4j编排RAG流程

4.1 为什么线性链式调用不够用

先说说我为什么从LangChain4j的链式API迁移到LangGraph4j。最初的版本很简单:用户提问→向量检索→拼Prompt→调模型→返回。跑通没问题,但上线后问题来了:

用户问“订单超时补偿的重试次数是多少”,向量检索返回了三个片段,其中两个讲的是支付超时,一个讲的是订单超时。模型把三个片段混在一起回答,给出了错误的数字。如果我在检索后加一个“重排”步骤,把最相关的片段排前面,再让模型只基于Top1回答,准确率会高很多。但链式API里加这个步骤,代码会变得很臃肿。

更复杂的是条件分支:如果检索结果的相似度分数都低于阈值,说明知识库里可能没有相关内容,这时候应该走“兜底回复”而不是硬让模型编。链式API处理这种分支很别扭,而LangGraph4j的图结构天然支持条件边。

4.2 定义State:RAG流程的共享上下文

LangGraph4j的核心是State,它是一个贯穿整个流程的共享对象。我定义的RAG State包含这些字段:

public class RagState extends AgentState { // 用户原始问题 private String question; // 改写后的查询(用于检索) private String rewrittenQuery; // 向量检索结果 private List<TextSegment> vectorResults; // 关键词检索结果 private List<TextSegment> keywordResults; // 融合重排后的结果 private List<TextSegment> rerankedResults; // 最终生成的答案 private String answer; // 校验是否通过 private boolean validated; // 重试次数 private int retryCount; }

State用AgentState作为基类,它内部维护了一个Map,支持动态存取。每个节点从State里读自己需要的字段,处理完写回。这种设计的好处是节点之间完全解耦,我可以单独测试“重排节点”的逻辑,不用把整个流程跑一遍。

4.3 节点设计:检索、重排、生成、校验四步走

我把RAG流程拆成四个核心节点,每个节点职责单一:

检索节点负责双路召回。一路走向量检索,用Embedding相似度找语义相近的片段;一路走关键词检索(BM25或全文索引),找字面匹配的片段。为什么要双路?因为向量检索对语义理解好,但可能漏掉包含精确术语的片段;关键词检索对术语敏感,但理解不了同义表达。两路结果融合,召回率明显提升。

重排节点负责精排。双路召回的结果可能有几十条,直接塞给模型会超长且引入噪声。重排用Cross-Encoder模型(或者简单的相似度打分)对每条结果重新打分,取Top3-5条。LangChain4j内置了ReRankingModel接口,可以对接Cohere等重排服务,也可以用本地的轻量模型。

生成节点负责拼Prompt和调模型。Prompt模板很关键,我用的模板是这样的:

你是一个严谨的技术助手。请仅基于以下参考资料回答问题。 如果参考资料中没有相关信息,请明确说“根据现有资料无法回答”,不要编造。 参考资料: {{context}} 问题:{{question}} 回答要求: 1. 引用具体资料时标注来源 2. 不确定的内容要说明 3. 保持简洁,不要展开无关内容

校验节点负责事实核查。生成完答案后,再调一次模型(或者用规则),检查答案里的关键数字、术语是否都能在检索结果里找到出处。找不到出处的部分标记为“待确认”。这个步骤能进一步降低幻觉,但会增加一次模型调用,延迟会上升。我的做法是:对准确性要求高的场景开启校验,对响应速度要求高的场景关闭。

4.4 条件边:让流程学会“知难而退”

LangGraph4j的条件边让流程能根据State动态决定下一步。我定义了两个关键分支:

第一个分支在检索之后:如果重排后的最高相似度分数低于0.6,说明知识库里大概率没有相关内容,直接跳到“兜底回复”节点,告诉用户“这个问题我暂时没有找到相关资料,建议联系XX”。这比让模型硬编一个答案要诚实得多。

第二个分支在校验之后:如果校验不通过且重试次数小于2,回到生成节点重新生成(可以调整Prompt或换模型);如果重试两次还不通过,返回当前答案并附上“此回答未经完全验证”的提示。

graph.addConditionalEdges( "rerank", state -> { double topScore = state.getTopScore(); return topScore < 0.6 ? "fallback" : "generate"; }, Map.of("fallback", "fallbackNode", "generate", "generateNode") );

这种“知难而退”的设计,在实际使用中比“强行回答”体验好很多。用户宁可听到“我不知道”,也不愿意被一个自信的错误答案误导。

5. 检索质量优化:从能用到好用

5.1 查询改写:让用户的问题更适合检索

用户提问往往很口语化:“那个订单超时了怎么办”。直接拿这句话去检索,向量模型可能抓不住重点。查询改写(Query Rewriting)就是用模型把口语化问题转成更适合检索的查询。

我用的改写Prompt:

将以下用户问题改写为3个适合知识库检索的查询,每个查询聚焦一个关键概念。 用户问题:{{question}} 输出格式:每行一个查询,不要编号。

比如“订单超时了怎么办”会被改写成“订单超时处理流程”“订单超时补偿机制”“订单超时告警配置”。三个查询分别检索,结果合并去重,召回率比单查询高不少。

但改写会增加一次模型调用,延迟上升。我的折中方案是:简单问题不改写,复杂问题才改写。判断标准是问题长度和是否包含多个实体。这个判断逻辑本身也可以用规则实现,不一定非要调模型。

5.2 混合检索:向量与关键词的互补

前面提了双路召回,这里展开说融合策略。向量检索和关键词检索的结果怎么合并?最简单的是加权融合:给两路结果分别打分,归一化后按权重相加。我实测下来,向量检索权重0.7、关键词权重0.3是个不错的起点,具体要根据你的文档特点调。

更精细的做法是RRF(Reciprocal Rank Fusion)。热词里有人提到“langchain和langchain4j的默认rrf实现去重逻辑存在缺陷”,这个我确实遇到过。RRF的思路是:不看具体分数,只看排名,每条结果的得分是1/(k+rank),k通常取60。两路结果按这个公式算分再相加,排名靠前的自然得分高。

但默认实现有个问题:去重逻辑不完善。同一个文档片段可能被两路都召回,如果不去重,它会占两个名额。更隐蔽的是,不同切分块可能内容高度重叠(因为切分时有重叠区),这些也应该去重。我的做法是在RRF之前,先用文本相似度(比如Jaccard相似度)做一轮去重,相似度超过0.9的只保留一个。

5.3 重排模型的选择与本地化部署

重排是提升检索质量最有效的手段之一。Cross-Encoder重排模型会把“查询+候选片段”一起输入,输出一个相关性分数,比向量相似度准确得多。但Cross-Encoder计算量大,不适合对全量文档做,只适合对召回后的几十条做精排。

商用重排服务(如Cohere Rerank)效果好但按量收费,长期用成本不低。本地化部署可以用BGE-Reranker系列,LangChain4j可以通过ONNX Runtime加载。我实测BGE-Reranker-Base在中文技术文档上的重排效果,比纯向量相似度提升明显,Top1命中率从62%提升到81%。

如果不想引入重排模型,也有轻量替代方案:用LLM做重排。把查询和候选片段列表给模型,让它输出相关性排序。效果也不错,但延迟更高,适合对延迟不敏感的场景。

6. 常见问题与排查实录

6.1 检索命中率低的排查思路

检索命中率低是最常见的问题。排查顺序应该是:先看切分,再看向量模型,最后看检索策略。

切分问题的典型表现是:检索出来的片段语义不完整,或者关键信息被切到了相邻块。排查方法是把切分后的块打印出来人工看,如果发现大量块以逗号或半个句子结尾,说明切分粒度太细或分隔符优先级不对。

向量模型问题的典型表现是:语义相近但用词不同的查询检索不到。比如搜“如何配置超时”找不到“超时参数设置指南”。这说明向量模型对同义表达的理解不够。解决办法是换更强的向量模型,或者在检索前做查询扩展(把同义词加进查询)。

检索策略问题的典型表现是:明明知识库里有相关内容,但就是排不到前面。这时候要检查是不是只用了单路检索,加上关键词检索和重排通常能解决。

6.2 模型回答“胡编乱造”的抑制手段

即使有RAG,模型还是可能编造。抑制手段分三层:

Prompt层:明确要求“仅基于参考资料回答”“没有相关信息就说不知道”。这个约束能挡掉大部分幻觉。

检索层:提高检索质量,确保塞给模型的资料确实相关。如果检索结果本身就不相关,模型只能靠编。

校验层:生成后做事实核查,把答案里的关键实体和数字提取出来,逐个在检索结果里找出处。找不到的标记出来。这个步骤可以用规则实现(字符串匹配),也可以用模型实现(让模型判断每句话是否有依据)。

我实测下来,三层都开启的情况下,幻觉率能从15%左右降到3%以下。代价是延迟增加约40%,需要根据场景权衡。

6.3 性能瓶颈定位与优化

RAG系统的延迟主要花在三个地方:检索、重排、生成。定位方法是在每个节点前后打时间戳,看哪一步最慢。

检索慢通常是向量数据库的问题。检查索引是否建好(pgvector要建IVFFlat或HNSW索引),检查是不是全表扫描。百万级向量用HNSW索引,检索能在毫秒级完成。

重排慢是Cross-Encoder的通病。优化方法是减少候选数量(召回阶段别取太多),或者用更小的重排模型。

生成慢取决于模型服务。优化手段包括:减少Prompt长度(少塞几条检索结果)、用流式输出(用户感知延迟降低)、换更快的模型。

下面这张表是我在实际项目中总结的常见问题速查:

现象可能原因排查方法解决方向
检索结果不相关切分粒度过粗/细打印切分块人工检查调整切分参数或改语义切分
同义查询搜不到向量模型能力不足用同义句测试检索换模型或加查询扩展
答案与资料矛盾Prompt约束不够检查Prompt模板强化“仅基于资料”约束
响应时间过长某节点耗时异常节点前后打时间戳定位瓶颈针对性优化
重复内容占名额去重逻辑缺失检查召回结果加文本相似度去重
表格内容检索不到表格解析丢失结构检查解析后文本表格转Markdown保留结构

6.4 数据一致性:知识库更新的坑

热词里有人问“java怎么保证数据一致性”,在RAG场景里,这个问题具体化为:知识库更新时,怎么保证向量库和原始文档一致。

我踩过的坑是:文档更新了,但向量库里还是旧版本,检索出来的是过时信息。解决办法是给每个文档块打上docId和version,更新时先按docId删除旧向量,再插入新向量。LangChain4j的EmbeddingStore支持removeAll按条件删除。

但删除和插入不是原子的,如果中间失败,会出现部分旧向量残留。更稳妥的做法是双写+切换:新版本写入新表,写完后原子切换表名。或者用软删除标记,检索时过滤掉已删除的。

还有一个隐蔽问题:Embedding模型升级。如果你换了向量模型,所有历史向量都得重新生成,因为新旧模型的向量空间不兼容。这个操作很重,建议在低峰期做,并且做好回滚预案。

7. 一些实测有效的经验参数

最后分享几个我调了很久才找到感觉的参数,供参考:

切分块大小500字符、重叠50字符。这个组合在中文技术文档上表现最均衡。块太小语义不完整,块太大检索精度下降。500是我试了300、500、800、1000之后的选择。

向量检索TopK=10,关键词检索TopK=10,融合后取Top5做重排,重排后取Top3给模型。这个漏斗式设计兼顾了召回率和精度。直接给模型太多片段,它会抓不住重点。

相似度阈值0.6。低于这个分数就触发兜底回复。这个阈值需要根据你的向量模型和文档特点微调,方法是拿一批已知相关和不相关的问题测试,看分数分布。

重试次数上限2。校验不通过时最多重新生成两次,再多就是浪费资源,而且往往说明检索结果本身有问题,重试也解决不了。

温度0.1,TopP 0.9。低温度保证输出稳定,TopP 0.9保留一点多样性避免死板。

这套参数在我的项目里跑了三个月,覆盖了大约两万次查询,人工抽检准确率在85%左右。剩下的15%主要是知识库本身缺失或问题超出知识范围,属于预期内的失败。

如果你刚开始搭,建议先用内存向量库和小规模文档跑通全流程,把每个节点的输入输出都打印出来看,确认逻辑对了再换生产级存储。RAG的调试很依赖对中间结果的观察,黑盒调参效率极低。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询