ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Java+Vue+Milvus:语义检索与相似文档查重系统实现指南

Java+Vue+Milvus:语义检索与相似文档查重系统实现指南 简介一套基于Java与Vue的向量数据库语义检索与相似文档查重系统项目详解面向具备Java和Vue基础的软件工程师、系统架构师以及从事NLP、知识管理、内容安全的技术人员解决语义层面改写、同义替换造成的查重漏判问题。项目集成BERT等模型完成文本向量化基于Milvus实现近似最近邻检索覆盖文档上传、向量化处理、查重分析到结果可视化全流程并给出需求分析、架构设计、数据库建模、API规范、前后端代码及部署运维说明。资源包为1个docx文件大小81KB内容含目录式章节结构、核心模块代码示例、模块说明与关键实现思路适合结合代码实践也可直接参考其中的系统设计方案进行二次开发与功能扩展。目前已有153人学习下载适合作为智能语义检索与查重平台的落地参考。1. 这个系统到底解决什么问题从关键词检索到语义检索如果你手头有一个「基于 javavue 的向量数据库的语义检索与相似文档查重系统」这样的课程设计或毕设题目第一反应通常是这不就是做一个搜索框吗但真正动手你会发现传统的关键词 LIKE 查询在文档检索场景下非常脆弱——用户搜「怎么退款」系统匹配不到写的是「退货流程」的那篇文档两篇表述不同但内容高度相似的文档肉眼一看就知道是抄的程序却判断成「毫无关系」。这套系统的核心价值就是把「字面匹配」升级成「语义匹配」先用 embedding 模型把每篇文档转成高维向量再用向量数据库做相似度检索这样语义相近、写法不同的内容也能被召回。整套方案适合两类人一是拿它当课程设计/毕设题目、需要完整跑通前后端并交源码和文档的学生二是刚入行 RAG 或知识库方向、想熟悉 Java 技术栈下向量检索落地路径的开发者。下面这套实现我按「检索 查重」两条线来讲代码和设计都是能直接复现的。2. 选型与架构为什么是 Java Vue 向量数据库而不是 ES 硬扛2.1 技术栈选型Spring Boot 做后端、Vue 做前端的合理性课程设计场景下技术栈的选择首先要考虑「能不能顺利跑通」和「答辩时能不能讲清楚」。Java 后端选 Spring Boot 几乎是最稳的——生态成熟、资料多、部署简单一个mvn spring-boot:run就能起来。前端选 Vue 3 Element Plus组件现成表格、表单、上传弹窗这些检索系统要用的界面元素不需要从零写。见过不少同学为了炫技用微服务拆了五六个模块最后联调阶段直接翻车其实这种规模的系统单体应用 清晰的分层就足够了。这套组合放在向量检索场景里的意义是Spring Boot 负责业务编排接收请求、调用 embedding 服务、读写数据库Vue 负责交互上传文档、展示检索结果、展示查重报告向量数据库独立部署通过 SDK 或 HTTP API 接入。分层清晰每一层的职责单一答辩时从「前端 → Controller → Service → 向量库」这条链路讲下来评委很容易跟上。2.2 向量数据库选型对比Milvus / Chroma / Qdrant 怎么挑向量数据库是整个系统的核心依赖选型不能拍脑袋。课程设计最常见的选择是 Milvus、Chroma、Qdrant 这三个它们各有侧重维度MilvusChromaQdrant部署方式Docker 独立部署偏重嵌入式或独立服务轻量Docker 独立部署中等Java SDK官方 milvus-sdk-java功能全HTTP API 为主Java 需自己封装官方 qdrant-java-client适合场景百万级向量、生产级检索原型验证、小规模数据、学习入门中小规模、对过滤条件要求高学习成本较高概念多collection、partition、index低几分钟能跑起来中等概念和 Milvus 接近我一般会建议课程设计选 Milvus——不是因为它最轻量而是因为它是知识库、RAG 项目里最常出现的向量数据库面试和答辩时被问到的概率最高。用docker-compose起一个 standalone 实例也就几分钟Java 端调用官方 SDK类型安全、文档齐全。如果你的机器配置比较紧张Chroma 也是可行的替代方案它的 HTTP API 用 RestTemplate 就能调只是要把下面的代码改成 JSON 请求。2.3 系统架构与模块划分一条请求是怎么走完的整个系统我拆成四个模块每个模块只干一件事文档管理模块上传、解析、分块把原始文档内容落库向量化模块调用 embedding 接口把分块后的文本转成向量写入向量库语义检索模块接收用户查询向量化 query在向量库做相似度检索返回排序后的文档列表查重模块将库内文档两两比较相似度超过阈值判定为重复输出查重报告。模块之间通过 Service 接口交互不直接互相操作对方的表。这样做的好处是如果后续想把 embedding 模型从本地接口换成云端服务只需要替换向量化模块的实现类想把 Milvus 换成 Qdrant只需要替换向量访问层的实现。检索请求的完整路径是Vue 页面把用户输入的 query 发给 Spring Boot 的/api/search接口Controller 调SearchServiceSearchService先调EmbeddingClient把 query 转成向量再调VectorStoreClient查相似向量最后从 MySQL 里查出文档元信息组装结果返回给前端。3. 数据建模与向量化入库三张表和一条流水线3.1 核心数据模型文档表、分块表、查重任务表向量检索系统里数据建模的难点不是「怎么建表」而是「文档和向量的对应关系怎么设计」。实践中不能把整篇文档当成一个向量——文档一长embedding 会被稀释检索精度直线下降。常见做法是把文档切成块chunk每块对应一个向量向量库里保存 chunk_id 作为关联键。先看建表 SQL我拆成三张核心表-- 文档信息表存文档的元信息不存正文内容本身正文存在分块表里 CREATE TABLE document_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, doc_name VARCHAR(255) NOT NULL COMMENT 文档名称, doc_type VARCHAR(50) DEFAULT txt COMMENT 文档类型txt/pdf/docx, chunk_size INT DEFAULT 512 COMMENT 切块大小字符数, chunk_overlap INT DEFAULT 64 COMMENT 切块重叠长度, total_chunks INT DEFAULT 0 COMMENT 分块总数, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_doc_type (doc_type) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 文档元信息表; -- 文档分块表存切块后的文本是检索结果展示和查重判定的依据 CREATE TABLE document_chunk ( id BIGINT AUTO_INCREMENT PRIMARY KEY, doc_id BIGINT NOT NULL COMMENT 所属文档ID关联 document_info.id, chunk_seq INT NOT NULL COMMENT 块序号从0开始, chunk_text TEXT NOT NULL COMMENT 切块文本内容, token_size INT DEFAULT 0 COMMENT 块内token数用于调试和调参, INDEX idx_doc_id (doc_id), UNIQUE KEY uk_doc_seq (doc_id, chunk_seq) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 文档分块表; -- 查重任务表记录每次查重任务的执行状态和结果摘要 CREATE TABLE duplicate_task ( id BIGINT AUTO_INCREMENT PRIMARY KEY, task_name VARCHAR(255) NOT NULL COMMENT 任务名称, status TINYINT DEFAULT 0 COMMENT 0待执行 1执行中 2完成 3失败, total_docs INT DEFAULT 0 COMMENT 参与查重的文档数, duplicate_num INT DEFAULT 0 COMMENT 判定为重复的文档数, threshold DOUBLE DEFAULT 0.85 COMMENT 本次查重的相似度阈值, result_json JSON COMMENT 查重结果摘要冗余存储便于前端展示, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 查重任务表;这三张表的设计里有几个值得注意的点document_chunk的doc_id chunk_seq联合唯一键能防止重复入库重复跑向量化任务时不用手动清理数据chunk_text用TEXT而不是VARCHAR因为 512 字的中文文本加上标点可能超过 255 个字符result_json字段冗余存储查重摘要是为了前端展示时不需要实时重算这在数据量大时能省下不少响应时间。向量本身不落 MySQL而是存在 Milvus 里。Milvus 的 collection 里保存三个字段chunk_id对应 MySQL 的 document_chunk.id、doc_id、embeddingFloatVector。只存 id 和向量文本内容不回写向量库避免双份存储导致的数据不一致。3.2 向量化流水线文档切块、embedding、批量入库向量化入库是一条完整的流水线任何一个环节出问题都会导致「文档传上去了但搜不到」。完整步骤我拆成四步第一步解析文档提取纯文本。从上传的 txt 或 pdf 中读取文本内容。txt 直接用Files.readStringpdf 用 PDFBoxdocx 用 POI。这一步的产出是「一篇文档的完整字符串」。第二步文本切块。切块策略直接决定检索质量。常见做法有两种按固定字符数滑动窗口切适合通用场景按段落切适合结构化文档。课程设计阶段用固定窗口够用参数我一般设chunk_size512、chunk_overlap64。重叠部分是为了避免句子被拦腰截断导致语义不完整。注意这里不能用「前 512 字、下一个 512 字」这种硬切要尽量切在标点或换行处。public ListString splitText(String text, int chunkSize, int chunkOverlap) { ListString chunks new ArrayList(); // 先按换行符粗分把文本切成段落数组保留段落作为最小单位 String[] paragraphs text.split(\\n); StringBuilder current new StringBuilder(); for (String para : paragraphs) { // 如果加进来超过 chunkSize先把当前内容落块 if (current.length() para.length() chunkSize current.length() 0) { chunks.add(current.toString()); // 保留尾部 overlap 长度的内容作为下一块的起始保证跨块语义连续 int keep Math.min(current.length(), chunkOverlap); current new StringBuilder(current.substring(current.length() - keep)); } current.append(para).append(\n); } if (current.length() 0) { chunks.add(current.toString()); } return chunks; }这段切块逻辑说明两个关键点一是keep变量取的是「上一块末尾 64 个字符」而不是随便从中间截一段这样下一块的开始部分和上一块的结尾有语义衔接二是以换行符为粗切分单位避免直接把一句话劈成两半。切块完成后把每个 chunk 的文本和所属 doc_id 写入document_chunk表。第三步调用 embedding 接口把每个 chunk 转成向量。课程设计场景下常见做法是本地部署一个开源的 embedding 模型服务比如 bge-small-zh输出 512 维向量或者调用现成的 HTTP embedding API。Java 端封装一个EmbeddingClient接口屏蔽底层实现差异public interface EmbeddingClient { // 把单段文本转成向量 ListFloat embed(String text); // 批量转向量实现类内部做并发控制避免把模型服务打挂 ListListFloat embedBatch(ListString texts); }第四步批量写入 Milvus。这里最容易踩的坑是一条一条 insert几千个 chunk 能跑十几分钟。正确做法是攒够一批比如 100 条再批量写入public void batchInsertVectors(ListDocumentChunk chunks) { int batchSize 100; for (int i 0; i chunks.size(); i batchSize) { ListDocumentChunk batch chunks.subList(i, Math.min(i batchSize, chunks.size())); // 构建 Milvus 的 InsertParam字段顺序要和 collection schema 完全一致 ListInsertParam.Field fields new ArrayList(); ListLong chunkIds new ArrayList(); ListLong docIds new ArrayList(); ListListFloat vectors new ArrayList(); for (DocumentChunk c : batch) { chunkIds.add(c.getId()); docIds.add(c.getDocId()); vectors.add(embeddingClient.embed(c.getChunkText())); } fields.add(new InsertParam.Field(chunk_id, chunkIds)); fields.add(new InsertParam.Field(doc_id, docIds)); fields.add(new InsertParam.Field(embedding, vectors)); InsertParam param InsertParam.newBuilder() .withCollectionName(document_vectors) .withFields(fields) .build(); milvusClient.insert(param); } }这段代码的逻辑说明batchSize是吞吐和内存的折中100 条一批在大部分机器上不会 OOM速度也能接受InsertParam.Field的三个字段必须和建 collection 时定义的 schema 字段类型一致embedding字段传的是ListListFloatMilvus SDK 会把它序列化成 FloatVector 数组。3.3 数据库与向量库的一致性策略先落 MySQL 再写 Milvus数据一致性是向量检索系统里最容易被忽略的问题。「文档上传成功了但搜不到」十有八九是 MySQL 和 Milvus 的数据没对齐。我的处理原则是MySQL 是唯一数据源Milvus 是衍生数据。上传时先把文档和分块写入 MySQL成功后再逐个写入 Milvus如果 Milvus 写入失败把任务标为失败允许用户重试向量化而不是让用户重新上传文档。重试的机制不复杂给document_info表加一个vector_status字段0未向量化、1已向量化、2失败重试时只捞状态不为 1 的文档重新执行向量化流水线。这个设计比事务性保证更实用因为 Milvus 和 MySQL 跨系统无法用本地事务靠状态字段做补偿是最常见的做法。4. 语义检索与相似查重的核心实现Java 侧代码拆解4.1 语义检索核心代码从 query 到相似度排序检索接口是系统的主路径核心逻辑只有三步query 向量化、向量库相似度查询、结果组装。Service public class SearchService { private final EmbeddingClient embeddingClient; private final VectorStoreClient vectorStoreClient; private final DocumentChunkMapper chunkMapper; public SearchResult search(String query, int topK, double scoreThreshold) { // 第一步把用户输入的 query 转成向量 ListFloat queryVector embeddingClient.embed(query); // 第二步在 Milvus 里做 ANN 检索只取 topK 个候选 SearchParam param SearchParam.newBuilder() .withCollectionName(document_vectors) .withVectors(Collections.singletonList(queryVector)) .withTopK(topK) .withParams({\nprobe\: 16, \metric_type\: \COSINE\}) .build(); ListListSearchResultData results vectorStoreClient.search(param); // 第三步过滤低分结果反查 MySQL 组装返回 ListSearchHit hits new ArrayList(); for (SearchResultData data : results.get(0)) { if (data.getScore() scoreThreshold) { continue; } Long chunkId (Long) data.getFieldValues().get(chunk_id); DocumentChunk chunk chunkMapper.selectById(chunkId); if (chunk ! null) { hits.add(new SearchHit(chunk.getDocId(), chunk.getChunkText(), data.getScore())); } } hits.sort((a, b) - Double.compare(b.getScore(), a.getScore())); return new SearchResult(hits); } }这段代码里有两个参数需要重点说明topK控制召回数量课程设计场景一般取 10-20取太大会把大量低分噪音带进来取太小又可能漏掉相关文档scoreThreshold是相似度下限Milvus 的 COSINE 相似度范围是 [-1, 1]实践中 0.5 以下的结果基本可以断定是语义无关的。nprobe是 IVF 索引的搜索探测桶数量值越大越精确但越慢100 万向量以内的数据集设 16 足够。4.2 相似文档查重阈值判定与分组去重查重的逻辑和检索很像但有一个关键区别检索是「一个 query 找最相似的文档」查重是「库内所有文档两两比较」。直接两两比较的复杂度是 O(n²)文档一多就扛不住。我用的常见做法是以块为粒度召回 文档级聚合先每个 chunk 去向量库查最相似的 topK 个 chunk再把结果按文档聚合计算「两篇文档有多少对相似 chunk」来判定重复。阈值和判定逻辑是查重的核心public void runDuplicateCheck(Long taskId) { // 1. 取出库内所有 chunk分批查询 ListDocumentChunk allChunks chunkMapper.selectAll(); // 用 Map 记录“文档对 - 相似块对数”key 为 docA_docB 且保证 docA docB MapString, Integer similarPairCount new HashMap(); for (DocumentChunk chunk : allChunks) { // 拿当前 chunk 去向量库查最相似的几个 chunk ListSearchResultData topMatches vectorStoreClient.searchTopK( chunk.getEmbedding(), 5, 0.85); for (SearchResultData match : topMatches) { Long matchedChunkId (Long) match.getFieldValues().get(chunk_id); DocumentChunk matched chunkMapper.selectById(matchedChunkId); if (matched null || matched.getDocId().equals(chunk.getDocId())) { continue; // 跳过查到自己 } String key buildPairKey(chunk.getDocId(), matched.getDocId()); similarPairCount.merge(key, 1, Integer::sum); } } // 2. 聚合判定相似块对数超过阈值则认为文档重复 for (Map.EntryString, Integer entry : similarPairCount.entrySet()) { // 相似块对数 / 两篇文档的较小块数得到“覆盖度” double coverage computeCoverage(entry.getKey(), entry.getValue()); if (coverage 0.3) { // 记录到 resultJson标记这对文档为疑似重复 duplicateTaskMapper.appendResult(taskId, entry.getKey(), coverage); } } }这段代码的关键在判定条件match.getScore() 0.85是「块级相似」的阈值coverage 0.3是「文档级重复」的阈值。两个阈值含义不同新手很容易只设一个就调。我的经验是块级阈值定 0.85 以上能过滤掉大量主题相关但表述不同的正常文档文档级覆盖度定 0.3 左右比较合理——如果两篇文档有 30% 的内容是逐字逐句相似的基本可以判定为复制粘贴后改写。这里的buildPairKey保证无论先遇到谁key 都是小 docId 在前避免重复计数。4.3 必调参数三个参数决定检索效果的上限这套系统跑通容易跑好很难核心就在参数上。我最常调的是以下三个分块大小chunk_size512 是通用安全值。做长文档的知识库问答时建议调到 800-1000做查重时建议调小到 256因为查重需要更细粒度地捕捉局部重复块太大了一小段抄袭会被稀释到阈值以下。检索阈值score_threshold取决于 embedding 模型的分布特征。bge 系列模型在短文本匹配上分数普遍偏高0.6 以下基本可以抛弃如果你用的是通用英文模型阈值要适当放宽。建议上线前拿 20 组「相关/不相关」样例跑一遍看分数分布再定。查重覆盖度阈值这个参数调节的是「容忍度」。毕业论文查重语境下应该设严一点0.2-0.25因为要抓轻度抄袭如果是团队内部查技术文档重复0.4 都不迟因为大家的文档本来就会引用公共模板。5. 避坑记录向量检索项目最常见的 5 个坑5.1 全量文档一次性向量化JVM 直接 OOM现象上传 50 篇 PDF点击「一键向量化」程序跑了一半直接报java.lang.OutOfMemoryError: Java heap space重启后还要从头再来。原因一次性把所有文档的文本全部读进内存加上切块后的对象开销和 embedding 模型的中间结果几十篇文档就能吃满默认的 512MB 堆内存。向量化是 CPU 密集 内存密集操作不能把整个流程放在一个请求线程里跑。解决分批处理 限制并发。每次只读一篇文档切块后逐批batch size 100调用 embedding 接口并写入 Milvus处理完一篇就释放引用。启动参数加上-Xmx2g留足余量。如果文档量大把向量化任务丢进线程池异步执行前端轮询状态而不是同步等待。5.2 中文文档按字符硬切语义被切碎现象检索「退款政策」返回的文档片段内容驴唇不对马嘴看起来是相关内容但定位到的段落根本不在讲退款。原因切块时用了text.substring(0, 512)这种硬切方式把完整句子从中间劈开。中文不像英文有空格天然分词硬切会把「用户可以在 7 日内申请无条件退款」切成「用户可以在 7 日内申请无条件」和「退款」两段语义全丢。解决切块前先按换行符、句号、问号、感叹号做句子级切分然后按句子聚合成块。遇到超过 512 字的长句再按逗号做次级切分。宁可一个块短一点300-400 字也不要割裂语义。这个坑在查重场景更致命——块质量差查重结果基本是噪音。5.3 阈值凭感觉设查重报告被质疑「不准」现象自测时觉得查重结果挺准拿去和同学手工核对发现漏报了一篇整段复制的文档还误报了两篇只是主题相关的文章。原因阈值设置没有数据支撑。不同模型产出的余弦相似度分布差异很大同一个模型的阈值在不同领域文档上的表现也不同。之前有人问我推荐阈值我给了一个 0.85结果他用在简历查重场景差点出事——简历的句式高度模板化「精通 Java」「熟悉 MySQL」这种短语相似度天然偏高。解决跑完一批数据后把相似度分数按降序排列人工看前 50 对结果的文本内容找到「最像但不算重复」和「最不像但算重复」的边界然后在这个边界附近选阈值。这个过程用一台本地开发机就能做。5.4 前后端联调被跨域和上传大小限制卡住现象后端接口用 Postman 测得好好的前端 axios 一调就报CORS error文件上传稍微大一点直接返回 413 或 500。原因Spring Boot 默认不允许跨域访问Vue 开发服务器跑在 5173 端口后端跑在 8080属于跨域请求同时 Spring 默认的单次请求 body 大小是 1MB大 PDF 一传就超限。解决后端加一个 CORS 配置类允许本地开发地址的跨域请求application.yml里调大spring.servlet.multipart.max-file-size和max-request-size一般设 50MB 足够课程设计使用。注意 CORS 配置不要在生产环境放开所有来源不然会被安全评审扣分。5.5 Milvus 集合的索引类型没建检索慢到怀疑人生现象几百个向量的时候检索飞快导入几千个向量后一次查询要等好几秒。原因Milvus 默认不建索引插入的数据是暴力扫描。数据量小的时候暴力扫描和索引扫描差距不大数据量上来立刻暴露。解决插入数据后显式创建索引。小型数据集用 HNSW 或 IVF_FLAT 都行HNSW 查询更快但内存占用高IVF_FLAT 省内存。课程设计几百兆的数据量直接用 IVF_FLAT、nlist 设为 128 即可。注意索引创建是异步的创建完要等 Milvus 返回 ready 状态再执行查询。6. 前端落地与验证技巧把检索和查重界面做成立得住的 GUI6.1 Vue 3 页面设计与路由参数传递前端用 Vue 3 Element Plus 是最省事的组合界面结构我分成三个视图文档上传页、检索页、查重报告页。上传页用el-upload组件action指向后端的/api/document/upload检索页是核心搜索框 结果列表。列表点击跳转详情时涉及一个路由参数的细节从检索结果列表跳转到文档详情页需要把 docId 传给目标页面。正确做法是用vue-router的 query 参数而不是把 docId 放在组件内部变量里——刷新页面后组件状态丢失但 URL 中的 query 参数不会丢这个设计从用户体验上来说是更稳的// 检索结果列表项点击事件 const handleDetail (docId) { router.push({ name: DocumentDetail, query: { docId: docId } }) } // 详情页读取参数 const route useRoute() const docId route.query.docIdfetch 后端接口时把docId拼在请求 URL 上详情页就能拿到文档的分块列表和相似向量得分。这里有一个容易被忽略的点分块列表展示的是document_chunk里的原文不是向量库里的数值后端在返回给前端前要把 chunk_text 按 docId 聚合好前端只负责渲染。6.2 相似度矩阵的验证方法不靠肉眼靠数据查重功能做完后怎么验证它的结果是可信的我常用的方法是做一个相似度矩阵页面前端拿到后端返回的duplicateTask.resultJson把涉及重复的文档两两组合渲染成一个矩阵行和列都是文档名交叉点是它们的覆盖度分数。用 Element Plus 的el-table就能实现不需要引入额外图表库。验证时先准备一组「人工标注过」的测试集拿 3 篇完全相同的、2 篇各自改写了 20% 的、3 篇主题相关但内容独立的文档混在一起跑查重。理想结果是完全相同的覆盖度约等于 1改写 20% 的覆盖度在 0.5-0.7 之间主题相关的覆盖度低于 0.2。如果结果偏离这个预期优先调整块级相似阈值而不是覆盖度阈值——大部分误判都出在「块本身太粗或太细」。6.3 查重报告之外还能怎么进阶检索和查重跑通只是第一步你可以在交付文档里加上几项进阶说明这将显著提升项目的技术档次。第一项是增量更新目前做的是全量向量化进阶可以改成监听文档表的变更记录只对新插入的文档做向量化改动量不大收益却很明显。第二项是混合检索把 BM25 关键词检索结果和向量检索结果用 RRFReciprocal Rank Fusion做融合排序能同时兼顾「专有名词精确匹配」和「语义扩展召回」两种场景这是知识库类项目里最常见的生产级方案。第三项是分类过滤在 Milvus 的 collection 里给每个向量加一个category字段检索时用expr指定只搜某个分类下的向量适合文档库分成「合同」「手册」「论文」等多个目录的场景。说句实话调向量检索的参数有点像玄学同样的代码和数据换了一个 embedding 模型阈值就得全盘重调。我的习惯是把调好的参数组合写进一个配置类里每次换模型或换数据集时先跑一遍内置的测试集不达标就不上线。这套习惯帮我避过不少翻车的场合希望帮到你。本文还有配套的精品资源点击获取
返回列表