一、单靠大模型,专业场景根本不够用

大模型的参数化记忆天然有两个无法绕过的缺陷:

  • 领域标准知识过时

    :GB、DB、JT/T 系列标准每隔几年就修订一版,模型训练数据追不上

  • 幻觉无法消除

    :涉及具体数值、限值、条款编号时,模型"合理编造"的概率极高

解法不是换更大的模型,而是给模型一个可信赖的知识底座

二、RAG 层:混合检索 + 三阶段精排

我们的检索层基于 ES(Elasticsearch) 构建,采用向量检索(KNN)+ BM25 全文检索的混合召回策略。

2.1 两路召回,各取所长

 

用户问题
  ├── 向量检索(KNN)─── 语义相似,找"说同一件事"的 chunk
  └── BM25 全文检索 ─── 关键词命中,找"用了同样词"的 chunk
         ↓
   结果合并 → Rerank 重排序(bge-reranker-large)
         ↓
   Top-K 高质量 chunk → 注入 LLM Prompt

向量检索擅长捕捉语义,BM25 擅长精确命中专业术语(如 GB/T30685JT/T617)。两路互补,缺一不可。

2.2 Rerank:不是所有召回都值得用

召回是粗筛,Rerank 才是精选。我们使用 bge-reranker-large 对所有召回结果重新打分,真正做到"分数高的才给模型看",而不是把一堆无关 chunk 全塞进 Prompt 里稀释答案质量。

2.3 动态参数调控:意图不同,参数不同

我们在 Agent 层实现了查询意图识别,根据问题类型动态调整检索参数:

问题类型

topK

threshold

BM25权重

rewrite

默认

≥10

≤0.3

默认

on

含数值/限值

≥10

降至0.3

默认

on

标准内容查询(含编号)

≥10

≤0.3

提升至0.8

off

标准引用查询(问哪个标准)

≥20

≤0.3

降至0.3

on

💡 为什么"含标准编号"要关闭 rewrite?因为 query rewrite(同义词替换)会破坏 DB11/415-2016 这类精确编号,让 BM25 无法精确命中。但追问句("确定是20%吗?")虽然也触发了数值关键词,却不含标准编号——这种情况必须保持 rewrite 开启,否则语义丢失更严重。

二、RAG 层:混合检索 + 三阶段精排

我们的检索层基于 ES(Elasticsearch) 构建,采用向量检索(KNN)+ BM25 全文检索的混合召回策略。

2.1 两路召回,各取所长

 

用户问题
  ├── 向量检索(KNN)─── 语义相似,找"说同一件事"的 chunk
  └── BM25 全文检索 ─── 关键词命中,找"用了同样词"的 chunk
         ↓
   结果合并 → Rerank 重排序(bge-reranker-large)
         ↓
   Top-K 高质量 chunk → 注入 LLM Prompt

向量检索擅长捕捉语义,BM25 擅长精确命中专业术语(如 GB/T30685JT/T617)。两路互补,缺一不可。

2.2 Rerank:不是所有召回都值得用

召回是粗筛,Rerank 才是精选。我们使用 bge-reranker-large 对所有召回结果重新打分,真正做到"分数高的才给模型看",而不是把一堆无关 chunk 全塞进 Prompt 里稀释答案质量。

2.3 动态参数调控:意图不同,参数不同

我们在 Agent 层实现了查询意图识别,根据问题类型动态调整检索参数:

问题类型

topK

threshold

BM25权重

rewrite

默认

≥10

≤0.3

默认

on

含数值/限值

≥10

降至0.3

默认

on

标准内容查询(含编号)

≥10

≤0.3

提升至0.8

off

标准引用查询(问哪个标准)

≥20

≤0.3

降至0.3

on

💡 为什么"含标准编号"要关闭 rewrite?因为 query rewrite(同义词替换)会破坏 DB11/415-2016 这类精确编号,让 BM25 无法精确命中。但追问句("确定是20%吗?")虽然也触发了数值关键词,却不含标准编号——这种情况必须保持 rewrite 开启,否则语义丢失更严重。

3.3 标准引用查询增强

问题:"运输气瓶时应遵循哪个标准?"这类问题,答案不是一段描述,而是一个标准编号(如 GB/T30685)。

挑战:包含这个编号的 chunk 内容很长(22个条款),标准号被稀释在大量文字里,向量得分偏低。

解法:检测到"问答案是哪个标准"这种模式时,追加标准编号格式特征词,同时将 topK 扩大到20,降低 BM25 权重至0.3,让向量检索主导,把正确 chunk 捞出来。

 

# 检测:问的是某个操作"应符合/遵循哪个标准"
_is_asking_for_standard = bool(re.search(
    r'(遵循|遵守|符合|参照|依据|执行|按照|依照).{0,5}(哪个|什么|何种|哪些).{0,3}(标准|规范|规定|要求)',
    question
))
if _is_asking_for_standard:
    question = question + " 应严格遵守 应符合 GB/T JT/T JGJ CJJ 标准 规范"
    topk = 20
    term_weight_coefficient = 0.3

四、知识图谱层:LightRAG 风格的并行多路召回

向量+BM25 解决了"找到相关文本"的问题,但遇到跨文档关联问题时就力不从心了。知识图谱补上了这个短板。

4.1 图谱构建:实体+关系自动抽取

文档入库时,系统自动通过 LLM 从每个 chunk 中抽取实体(标准名称、技术指标、适用对象……)和关系(引用、包含、适用于、排除……),写入 Neo4j 图数据库,同时将实体向量化存入 Milvus

词表缓存到 Redis,检索时快速匹配问题中出现的实体词,精准定位图谱查询起点。

4.2 并行多路召回(LightRAG mix 风格)

 

# 并行执行 4 路召回,最后融合
_search_neo4j()          # Neo4j 图谱三元组检索
_search_entities()       # 实体向量检索(Milvus)
_search_relations()      # 关系向量检索(Milvus)
_search_community_reports()  # 社区报告检索(文档级摘要)
  • Neo4j 三元组

    :找到实体的直接邻居,回答"A 和 B 是什么关系"

  • 实体/关系向量

    :找到语义相关的节点,补充 BM25 和向量检索的盲区

  • 社区报告

    :LLM 自动生成的文档级摘要,回答宏观性问题

4.3 得分融合

四路结果按 score * 权重 + relation_weight * 因子 加权融合,避免某一路噪声过大,最终和 RAG 检索结果一起注入 Prompt。

五、对话历史管理:两层分工

机制

存储

用途

Agent 层

前端传入 history

内存(最近10轮)

追问句检测、query 补全、LLM 多轮上下文

后端会话层

conversationId

ES(按月分表)

持久化历史、跨会话恢复

Agent 用 history 做检索增强(补全追问句语义),后端用 conversationId 做对话持续性(跨会话记忆),两层职责清晰不重叠。


六、实际效果:从"召回失败"到"精准命中"

问题场景

修复前

修复后

"确定是20%吗?"

2条无关结果

8条相关,第一条即正确答案

"明确的不适用场景有哪些"

0条命中

直接命中"本标准不适用于"条款

"运输气瓶应遵循哪个标准"

错误章节排第一

含正确标准编号的条文命中

含%的追问句

rewrite 被误关闭

精准识别标准编号,动态开关

七、架构总结

 

用户提问
  │
  ▼  [Agent 层]
  ├── 意图识别(追问句/范围查询/标准引用/数值查询)
  ├── Query 增强(补全 + 扩写 + 参数动态调整)
  │
  ▼  [RAG 层]
  ├── 向量检索 KNN(Milvus/ES)
  ├── BM25 全文检索(ES)
  └── Rerank 精排(bge-reranker-large)
  │
  ▼  [Graph 层]
  ├── Neo4j 图谱三元组检索
  ├── 实体/关系向量检索(Milvus)
  │
  ▼  [LLM 生成]
  答案 + 来源引用

三层不是简单堆叠,而是分工明确:

  • RAG 解决"找到相关文本"

  • Graph 解决"找到关联关系"

  • Agent 解决"问题本身不够清晰"

最难的不是选择哪个技术,而是把问题拆清楚,让每一层只做它最擅长的事


这些优化都来自真实的问答失败日志——每一条"召回0条"的日志背后,都是一次值得深挖的工程问题。


关注我们,持续分享 AI 工程化落地实践。

技术栈总览


🖥️ 编程语言

语言

用途

Go 1.24

全部 Go 微服务

Python 3.12+

RAG 服务、Agent 服务、知识图谱

JavaScript / Vue

前端


🏗️ 后端框架

Go 侧: Gin、gRPC、GORM、Casbin、Viper、Zap、Swag

Python 侧: Flask(Agent 服务)、FastAPI + Uvicorn(RAG 服务)、Gunicorn


🎨 前端

Vue 2 + Vuex + Vue Router + Element UI、qiankun(微前端)、ECharts、AntV G6/X6(知识图谱/工作流可视化)、Monaco Editor、fetch-event-source(SSE 流式输出)


🗄️ 数据库 / 存储

组件

用途

MySQL

主关系型数据库

Elasticsearch 8

BM25 全文检索 + 历史会话存储

Milvus 2.x

向量数据库(语义检索)

Neo4j 5.23

知识图谱(GraphRAG)

Redis

缓存、图谱词表、限流

MinIO

对象存储(文档文件)

MongoDB

RAG 部分数据存储


📨 消息队列

Kafka(KRaft 模式)— 文档异步入库管道


🤖 AI / ML

LLM 接入: OpenAI SDK、DashScope/Qwen、DeepSeek、LangChain 0.3、LangGraph 0.6

向量 / 检索模型: BGE-M3(Embedding)、BGE-Reranker-Large(Rerank)、FlagEmbedding、ONNX Runtime、PyTorch + Transformers

RAG: rank-bm25、jieba 中文分词、tiktoken、pdfplumber / PyMuPDF / python-docx(文档解析)

知识图谱: NetworkX、graspologic(Leiden 社区发现)、Neo4j Python Driver

工具协议: MCP(Model Context Protocol)— Python mcp 1.12 + Go go-mcp


⚙️ 基础设施

Docker Compose、Nginx、Protobuf + gRPC、OpenTelemetry(链路追踪)、JWT + RSA、OAuth2


🏢 微服务拆分

 

Go 侧(8个服务)
  ├── bff-service      BFF 网关
  ├── iam-service      身份认证
  ├── model-service    模型管理
  ├── knowledge-service 知识库管理
  ├── assistant-service 对话会话
  ├── rag-service      RAG 调度
  ├── mcp-service      工具调用
  └── workflow         工作流引擎

Python 侧(2个服务)
  ├── agent      Agent 执行(Flask)
  └── rag        RAG + GraphRAG(FastAPI)

一句话概括:Go 微服务做业务编排,Python 做 AI 推理;ES 做文本检索,Milvus 做向量检索,Neo4j 做关系推理,三库协同构成知识底座;Kafka 打通异步数据管道;前端 qiankun 微前端保证各模块独立演进。

 

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐