springboot+langchain4j实战:Day 19------Query 改写 意图识别,让模糊查询变精准,让检索策略变智能
Day 19 — Query 改写 意图识别主题让模糊查询变精准让检索策略变智能端口8091技术栈Spring Boot 3.4.3 LangChain4j 1.13.1 Java 171. Day 19 比 Day 18 多了什么维度Day 18Day 19Query 处理无直接用原始 queryLLM 改写指代消解 省略补全意图理解无固定三路LLM 四分类 意图感知路由检索策略固定三路全开动态策略按意图选择路径API 端点3 个7 个新增 4 个Pipeline召回 → 融合改写 → 分类 → 策略 → 召回 → 融合降级处理无LLM 失败降级返回原始 query2. 核心概念速览Query 改写指代消解“昨天那个产品怎么样” → “码哥AI中台产品功能和评价”省略补全“SDK初始化” → “SDK初始化步骤方法教程”降级LLM 失败时返回原始 query意图识别四分类knowledge知识库/ order订单/ chat闲聊/ complaint投诉策略路由knowledge → 三路全开order → 关键词加重权重×2 ESchat → 仅向量检索complaint → 仅 ES BM25全链路 Pipeline原始Query → LLM改写 → 意图识别 → 策略选择 → 并行召回 → RRF融合 → Top-K结果3. 快速启动# 1. 进入项目目录cdday19-query-rewrite# 2. 确保 PostgreSQL Elasticsearch 已启动# 3. 编译运行首次启动自动初始化知识库mvn clean spring-boot:run# 4. 访问教学前端openhttp://localhost:8091前置依赖PostgreSQL 14已安装 PGVector 扩展Elasticsearch 8.xDocker 快速启动 ESdockerrun-d--namees-p9200:9200\-ediscovery.typesingle-node\-expack.security.enabledfalse\elasticsearch:8.15.04. 项目结构day19-query-rewrite/ ├── pom.xml ├── docs/ │ ├── day19-ai-concepts-teaching.md # 教学文档5个核心概念 │ └── day19-reference.md # 代码参考速查 ├── README.md └── src/main/ ├── java/com/day19/demo/ │ ├── Day19Application.java # 启动入口 │ ├── config/ │ │ ├── ChatModelConfig.java # LLM Embedding Bean │ │ ├── ElasticsearchConfig.java # ES 客户端配置 │ │ └── DataInitializer.java # 知识库自动初始化 │ ├── core/ │ │ ├── HybridSearchResult.java # 统一检索结果 DTO │ │ └── RrfExperimentResult.java # RRF 实验 DTO │ ├── dto/ │ │ └── ApiResult.java # 统一 API 响应 │ ├── rag/ │ │ ├── VectorSearchService.java # PGVector 向量检索 │ │ ├── KeywordSearchService.java # N-gram 关键词检索 │ │ ├── ElasticsearchService.java # ES BM25 检索 │ │ ├── QueryRewriteService.java # ⭐ Query 改写 │ │ ├── IntentClassifierService.java # ⭐ 意图分类 │ │ └── MultiRecallService.java # ⭐ 意图感知多路召回 │ └── controller/ │ └── SearchController.java # REST API7个端点 └── resources/ ├── application.yml # 配置端口 8091 ├── schema.sql # PGVector 建表 ├── data.sql # 种子数据空 ├── static/ │ └── index.html # 教学前端3个Tab └── docs/ ├── 码哥科技.txt ├── SDK集成指南.txt ├── API文档.txt └── 部署指南.txt5. 核心实现5.1 Query 改写服务ServicepublicclassQueryRewriteService{publicStringrewrite(StringoriginalQuery){// 1. 构建 System Prompt指代消解 省略补全规则// 2. 调用 LLM 改写// 3. 降级LLM 失败返回原始 query}publicStringrewriteWithHistory(Stringquery,ListStringrecentMessages){// 带对话历史的上下文感知改写}}System Prompt 核心指代消解替换那个、“它”、“这个”省略补全补充缺失的动作和场景关键词扩展适度添加同义词只输出改写文本不要解释5.2 意图识别服务ServicepublicclassIntentClassifierService{publicIntentResultclassify(Stringquery){// 1. 构建 System Prompt四分类规则// 2. 调用 LLM 返回 JSON// 3. 解析 JSON → IntentResult}publicRetrievalStrategygetRetrievalStrategy(IntentResultintent){// knowledge → 三路全开// order → 关键词×2 ES// chat → 仅向量// complaint → 仅 ES}}5.3 意图感知多路召回ServicepublicclassMultiRecallService{publicMapString,ObjectsearchIntentAware(Stringquery,StringtableName){// 步骤1意图分类// 步骤2Query 改写knowledge 意图// 步骤3策略选择按意图选路径// 步骤4并行召回只执行选中的路径// 步骤5RRF 融合}}6. 配置说明server:port:8091# Day 19 端口# Query 改写 意图识别配置Day 19 新增query:rewrite:enabled:true# 是否启用改写use-history:true# 是否使用对话历史intent:enabled:true# 是否启用意图识别default-strategy:knowledge# 降级时的默认意图# 多路召回配置沿用 Day 18multi-recall:rrf:default-k:60# RRF 默认 k 值experiment-ks:10,30,60,100# k 值实验列表recall-top:20# 每条路径取 Top-Nfinal-top:5# 最终返回条数7. API 端点详解#端点用途测试命令1GET /search三路召回RRF标准curl localhost:8091/search?query产品2GET /search/intent-aware意图感知检索curl localhost:8091/search/intent-aware?querySDK3GET /query/rewriteQuery改写curl localhost:8091/query/rewrite?query那个产品4GET /query/classify意图分类curl localhost:8091/query/classify?query订单5GET /experiment/k-valuesk值实验curl localhost:8091/experiment/k-values?query部署6GET /search/raw三路原始数据curl localhost:8091/search/raw?queryAPI7GET /demo/intent-pipeline全链路演示curl localhost:8091/demo/intent-pipeline?query产品8. 测试指南测试 Query 改写# 模糊查询 → 改写curlhttp://localhost:8091/query/rewrite?query昨天那个产品怎么样# 预期rewritten 码哥AI中台产品功能特点和评价curlhttp://localhost:8091/query/rewrite?query怎么部署# 预期rewritten 生产环境部署步骤和方法测试意图识别curlhttp://localhost:8091/query/classify?querySDK初始化失败# 预期intentknowledge, confidence0.8curlhttp://localhost:8091/query/classify?query我的订单到哪了# 预期intentorder, keywords包含订单curlhttp://localhost:8091/query/classify?query为什么这么慢# 预期intentcomplaint, keywords包含投诉或反馈测试意图感知检索# knowledge 意图 → 三路全开curlhttp://localhost:8091/search/intent-aware?querySDK集成方法# chat 意图 → 仅向量路curlhttp://localhost:8091/search/intent-aware?query你好# 对比两种方式的结果差异测试全链路 Pipelinecurlhttp://localhost:8091/demo/intent-pipeline?query码哥科技的产品# 查看 6 个步骤的详细结果和每步耗时9. 踩坑记录9.1 LLM 改写输出格式不稳定现象LLM 有时输出 “改写后XXX” 而不是纯文本解决在代码中 strip 前缀/引号并设置长度上限超过原始 3 倍 → 降级9.2 意图分类 JSON 解析失败现象LLM 返回的 JSON 被 Markdown 代码块包裹解决用正则去掉 json … 包装再解析9.3 改写分类串行太慢现象两次 LLM 调用总耗时 2-3 秒原因改写和分类都依赖 deepseek-v3API 延迟约 1-1.5s/次优化如果改写结果对分类影响不大可以并行调用9.4 chat 意图只开向量路可能不够现象闲聊时向量检索返回的结果可能不相关原因闲聊 query如今天怎么样与知识库内容语义距离远策略chat 意图可以完全不检索直接由 LLM 生成回复9.5 order 意图模拟数据不足现象学习项目的知识库中没有订单数据说明Day 19 的 order/complaint 意图主要用于展示意图路由机制实际效果依赖于业务数据的质量10. 面试怎么说“用户问题很模糊怎么办”“我们在检索前加了 Query 改写管道。用 LLM 做指代消解和省略补全比如 ‘那个产品怎么样’ 会改写为 ‘码哥AI中台产品功能和评价’。改写是异步的、带降级的——LLM 挂了不影响检索直接用原 query。”“怎么提高检索命中率”“我们引入了意图感知路由先分类用户意图知识库/订单/闲聊/投诉不同意图走不同的检索策略。比如订单意图加重关键词路、闲聊意图只走向量路。从固定三路升级到意图驱动的动态路径减少了噪音召回。”“意图识别怎么做的”“基于 Prompt 的分类方法。用 System Prompt 告诉 LLM 四种意图的定义和判断规则让 LLM 输出 JSON。优点是零样本不需要训练数据缺点是依赖 LLM 调用延迟。生产环境可以用 BERT 分类器替代延迟降低到 10ms。”“Pipeline 的瓶颈在哪”瓶颈在 LLM 调用——改写分类两次调用占 90% 的耗时。优化方向缓存常见 query 的改写结果2) 改用更快的本地模型3) 改写和分类可以并行。