Spring AI 2.0企业级RAG实战:引用校验、无依据拒答与知识治理怎么做?

目录

[1. 企业级RAG真正要解决的是"可信"](#1. 企业级RAG真正要解决的是“可信”)

[2. 检索结果相关,不代表它可以使用](#2. 检索结果相关,不代表它可以使用)

[3. 生产级RAG不要只做向量检索](#3. 生产级RAG不要只做向量检索)

[4. 回答必须和Evidence绑定](#4. 回答必须和Evidence绑定)

[5. Citation不是在答案后面加个1](#5. Citation不是在答案后面加个[1])

[6. 企业RAG必须敢于拒答](#6. 企业RAG必须敢于拒答)

[7. 文档上传成功,也不等于知识可以使用](#7. 文档上传成功,也不等于知识可以使用)

[8. 企业知识源也不应该只有"上传文件"](#8. 企业知识源也不应该只有“上传文件”)

[9. 回答错误之后,还需要Quality Loop](#9. 回答错误之后,还需要Quality Loop)

[10. 我现在更推荐的生产级RAG架构](#10. 我现在更推荐的生产级RAG架构)

[11. Spring AI 2.0负责什么,业务系统又该负责什么?](#11. Spring AI 2.0负责什么,业务系统又该负责什么?)

[12. 开源项目:Spring AI Business Copilot](#12. 开源项目:Spring AI Business Copilot)

[13. 总结](#13. 总结)

本文所有架构思路、工程设计和核心代码,都来自我正在持续维护的开源项目 Spring AI Business Copilot

⭐ GitHub:

GitHub - qcodingdev/spring-ai-business-copilot: Production-style Java + Spring AI 2.0 enterprise AI workbench: cited RAG, Text-to-SQL, support, HR, guardrails, human-in-the-loop and audit. · GitHub

项目基于 Java 21 + Spring Boot 4.1 + Spring AI 2.0 + PostgreSQL + pgvector,目前已经包含 Knowledge、Data、Support、Report、HR 等多个企业 AI Copilot。

本文讲到的 混合检索、引用校验、无依据拒答、知识版本、权限过滤、质量复核 都可以直接在项目中找到实现并运行测试。

如果这套企业 AI 工程实践对你有帮助,建议先给项目点个 Star ⭐。

后续这个系列还会继续拆 Text-to-SQL、Guardrails、Human-in-the-loop、Agent Evaluation 等生产级实现。


前言

很多人第一次做 RAG,流程基本都是:

复制代码
文档上传
  ↓
文本解析
  ↓
Chunk
  ↓
Embedding
  ↓
Vector Store
  ↓
TopK
  ↓
Prompt
  ↓
LLM

这条链路当然没有问题。

我之前的文章里也已经详细写过企业 RAG 的整体设计,包括文档解析、Chunk、向量检索、权限过滤、Rerank 等内容。

所以这篇不再重新讲一遍 Chunk 怎么切、Embedding 怎么调、TopK 怎么选

这次继续往生产环境走一步。

因为真正把 RAG 接进企业系统以后,很快会遇到另一个问题:

检索到了,不代表这个答案就可以相信。

比如员工问:

北京出差住宿标准是多少?

知识库召回了一份《员工差旅制度》:

复制代码
一线城市住宿标准最高 500 元/晚。

模型回答:

根据公司差旅制度,北京住宿标准最高为 500 元/晚。

答案流畅、召回正常,甚至还能给你挂一个引用。

但问题是:

这是一份两年前的制度。

半年前,公司已经更新到 V3:

复制代码
一线城市住宿标准最高 600 元/晚。

这时候问题已经不再是:

RAG 能不能找到内容?

而是:

RAG 怎么保证找到的是当前有效、用户有权访问,而且真正能够支撑答案的内容?

这就是 Demo RAG 和生产级 RAG 之间非常关键的一道分界线。


1. 企业级RAG真正要解决的是"可信"

一个简单 RAG 主要解决:

复制代码
Question
   ↓
Retrieval
   ↓
Context
   ↓
LLM
   ↓
Answer

但企业真正关心的问题会多很多:

  • 这句话依据哪份文档?
  • 引用的 Chunk 真的存在吗?
  • 文档是不是当前版本?
  • 文档有没有过期?
  • 用户有没有权限查看?
  • 两份制度冲突怎么办?
  • 没有证据时模型会不会继续编?
  • AI 回答错了以后能不能追查?
  • 人工发现问题后怎么形成治理闭环?

所以我现在更倾向于把生产级 RAG 的可信能力拆成三层:

复制代码
检索可信
Retrieval Trust

      ↓

回答可信
Answer Trust

      ↓

知识可信
Knowledge Trust

第一层保证:

找到的知识可以使用。

第二层保证:

模型的回答能够被证据支撑。

第三层保证:

进入 RAG 的知识本身处于正确生命周期。

这时候 RAG 才不只是:

复制代码
Vector Search + LLM

而逐渐变成一套完整的:

复制代码
Knowledge Governance
        +
Retrieval
        +
Evidence
        +
Generation
        +
Guardrails
        +
Audit

2. 检索结果相关,不代表它可以使用

RAG 优化时我们经常关注:

  • TopK
  • Similarity
  • Hybrid Search
  • Rerank
  • Recall
  • Precision

这些都重要。

但生产环境还要加一个前提:

相关性只是知识进入上下文的条件之一。

例如知识库里存在三份文档:

复制代码
员工差旅制度 V1
状态:历史版本

员工差旅制度 V3
状态:当前版本

财务特殊报销政策
权限:Finance

员工询问:

北京出差住宿标准是多少?

从语义相关度来看,这三份文档都可能被命中。

甚至 V1 与 V3 的文本高度相似。

如果检索逻辑只有:

复制代码
ORDER BY similarity DESC
LIMIT 10

就可能把:

  • 历史版本
  • 已经过期文档
  • 当前用户无权访问的文档

一起交给 LLM。

这也是为什么我在 Knowledge Copilot 中没有单纯依赖向量相似度,而是把知识生命周期直接放进检索条件

项目中的文本检索核心 SQL,实际会先做这些过滤:

java 复制代码
private static final String TEXT_SEARCH_SQL = """
    SELECT c.id AS chunk_id,
           ts_rank_cd(
               to_tsvector('simple', c.content),
               websearch_to_tsquery('simple', ?)
           ) AS rank
    FROM knowledge_chunks c
    JOIN knowledge_documents d ON d.id = c.document_id
    WHERE d.enabled = TRUE
      AND d.current_version = TRUE
      AND d.index_status = 'INDEXED'
      AND (d.expires_at IS NULL OR d.expires_at > now())
      AND d.conflict_status = 'NONE'
      AND (
            d.visibility_scope = 'ALL'
            OR (d.visibility_scope = 'HR_REVIEWER' AND ?)
            OR (d.visibility_scope = 'ADMIN' AND ?)
          )
      AND (?::text IS NULL OR d.category = ?::text)
      AND to_tsvector('simple', c.content)
          @@ websearch_to_tsquery('simple', ?)
    ORDER BY rank DESC, c.id
    LIMIT ?
    """;

这里真正值得关注的不是全文检索 SQL 本身,而是:

复制代码
enabled = true

current_version = true

index_status = INDEXED

未过期

无冲突

ACL匹配

业务分类匹配

只有通过这些条件的知识,才有资格进入后续检索。

这意味着:

权限、版本和生命周期控制应该发生在 Retrieval 层,而不是 Prompt 层。

千万不要先把敏感文档塞进 Context,再告诉模型:

复制代码
如果用户没有权限,请不要回答。

因为这个时候数据已经进入模型上下文了。


3. 生产级RAG不要只做向量检索

另一个常见误区是:

RAG = Vector Search。

但实际企业知识里,大量查询并不完全适合纯向量检索。

例如:

复制代码
A10086退款规范

2026差旅管理制度

P1故障处理流程

采购审批三级权限

这种包含:

  • 编号
  • 专有名词
  • 制度名称
  • 业务关键词

的问题,Keyword / Full Text Search 往往也非常有价值。

所以 Knowledge Copilot 当前采用的是:

复制代码
PostgreSQL Text Search
        +
Keyword Search
        +
pgvector Vector Search
        ↓
      Fusion
        ↓
      TopK

核心逻辑类似这样:

java 复制代码
List<KnowledgeChunkRepository.TextSearchResult> textResults =
        chunkRepository.findByTextSearch(question, topK * 2);

List<KnowledgeChunkRepository.TextSearchResult> keywordResults =
        chunkRepository.findByKeywordSearch(
                KnowledgeQueryTerms.extract(question), topK * 2);

float[] questionVector =
        aiEmbeddingService.embed(
                "knowledge.question-retrieval", question);

List<KnowledgeEmbeddingRepository.SimilaritySearchResult> vectorResults =
        embeddingRepository.findSimilarChunks(
                questionVector,
                embeddingModel,
                topK * 2,
                minSimilarity);

然后把不同通道的排序结果进行融合。

这里需要强调一点:

Hybrid Retrieval 解决的是"怎么找到更好的候选"。

但是它依然不能替代:

复制代码
Document Version
ACL
Expiry
Conflict
Index Status

Rerank 也是同理。

Rerank 能告诉你谁更相关,却不能告诉你谁有资格被使用。


4. 回答必须和Evidence绑定

传统 RAG 通常会这样构建 Prompt:

java 复制代码
请根据以下知识回答问题。

Context:
{retrievedChunks}

Question:
{question}

相比直接让模型裸答,这已经安全很多。

但是:

LLM 拿到 Context,不代表它一定只使用 Context。

它仍然可能:

  • 使用自己的参数知识;
  • 补全缺失的信息;
  • 把两个 Chunk 拼出一个不存在的结论;
  • 生成证据中没有出现的数字;
  • 编造一个不存在的引用。

因此企业知识助手最好不要只返回:

java 复制代码
{
  "answer": "北京住宿标准为600元/晚"
}

更合理的是返回:

java 复制代码
{
  "status": "ANSWERED",
  "answer": "根据当前差旅制度,北京住宿标准最高为600元/晚。",
  "citations": [
    {
      "chunkId": 1028,
      "excerpt": "一线城市住宿标准最高600元/晚"
    }
  ]
}

此时答案和证据形成:

复制代码
Answer
  ↓
Claim
  ↓
Citation
  ↓
Evidence

也就是说:

重要结论必须能够回到本次真实检索到的证据。


5. Citation不是在答案后面加个1

现在很多 RAG 系统已经开始支持引用:

复制代码
北京住宿标准最高600元/晚。[1]

[1] 员工差旅制度.pdf

看起来已经能够溯源。

但如果这个 [1] 完全由 LLM 自己生成,它仍然可能出现幻觉。

比如模型返回:

java 复制代码
{
  "chunkId": 99999
}

99999 根本没有在这次检索结果里出现。

所以 Citation 不能直接信模型。

我在项目里单独做了一个 CitationGuardrailService

核心代码并不复杂:

java 复制代码
public CitationValidationResult validate(
        List<KnowledgeCitation> citations,
        List<RetrievedKnowledgeChunk> retrievedChunks) {

    List<String> violations = new ArrayList<>();

    if (citations == null || citations.isEmpty()) {
        violations.add("ANSWERED 状态至少需要一条引用,但模型未返回引用");
        return new CitationValidationResult(false, violations);
    }

    Set<Long> validChunkIds = retrievedChunks.stream()
            .map(r -> r.chunk().id())
            .collect(Collectors.toSet());

    for (KnowledgeCitation citation : citations) {
        if (citation.chunkId() == null) {
            violations.add("引用中的 chunkId 不能为空");
            continue;
        }

        if (!validChunkIds.contains(citation.chunkId())) {
            violations.add(
                    "引用的 chunkId=" + citation.chunkId()
                    + " 不在本次召回结果中");
        }
    }

    return new CitationValidationResult(
            violations.isEmpty(), violations);
}

思路其实很简单:

复制代码
LLM说自己引用了什么
        ↓
系统拿本次真实Retrieved Chunks
        ↓
做集合校验
        ↓
引用真实存在?
       ↙   ↘
     YES    NO
      ↓      ↓
   Continue Reject

在项目里我甚至没有直接相信模型返回的引用摘录。

Citation 校验完成以后,excerpt 会重新从服务端真实 Chunk 中生成。

因为模型完全可能把原文:

复制代码
原则上不超过600元

改写成:

复制代码
统一标准为600元

虽然看起来只改了一点点,但语义已经发生变化。

所以:

Citation ID 可以让模型选择,但 Citation Evidence 最好由系统重新绑定。


6. 企业RAG必须敢于拒答

企业 AI 最危险的一种能力其实是:

什么都敢回答。

比如员工问:

公司对海外长期驻场的住房补贴是多少?

系统检索后:

复制代码
没有找到任何当前有效知识。

这个时候最危险的处理是:

复制代码
LLM根据常识继续生成。

更加可靠的处理应该是:

复制代码
NO_EVIDENCE

直接告诉用户:

当前知识库没有找到能够支持该问题的有效资料。

在 Knowledge Copilot 里,这个逻辑不是单纯写一句 Prompt:

复制代码
不知道的时候请说不知道。

而是直接进入系统状态。

例如当前 KnowledgeAnswerService 的核心逻辑:

java 复制代码
if (retrievedChunks == null || retrievedChunks.isEmpty()) {

    log.info("未召回知识分片,返回无依据状态");

    return result(
        new KnowledgeAnswerResponse(
            KnowledgeAnswerStatus.NO_EVIDENCE,
            null,
            List.of(),
            List.of(),
            aiChatService.modelName()
        ),
        null,
        null,
        "NO_RETRIEVED_EVIDENCE"
    );
}

注意这里一个很重要的细节:

没有 Evidence 时,连 LLM 都不调用。

这样不仅避免模型幻觉,还能:

  • 减少 Token 消耗;
  • 降低模型调用延迟;
  • 让拒答状态可以统计;
  • 让监控系统区分"知识不足"和"模型失败"。

而即使模型返回了 ANSWERED,后面依然还会做 Citation Guardrail:

java 复制代码
CitationValidationResult validation =
        citationGuardrailService.validate(
                citations, retrievedChunks);

if (!validation.valid()) {

    return result(
        new KnowledgeAnswerResponse(
            KnowledgeAnswerStatus.REJECTED,
            null,
            List.of(),
            validation.violations(),
            modelName
        ),
        prompt.metadata(),
        aiMetadata,
        "CITATION_VALIDATION_FAILED"
    );
}

所以完整思路并不是:

复制代码
LLM说可以回答
      ↓
直接展示

而是:

复制代码
Retrieval
    ↓
Evidence Gate
    ↓
LLM
    ↓
Structured Output
    ↓
Citation Guardrail
    ↓
Final Answer

这里我认为有一句话特别重要:

有召回,不等于有依据。


7. 文档上传成功,也不等于知识可以使用

企业知识还有一个很容易被忽略的问题:

生命周期。

普通 Demo 里文档可能只有:

复制代码
上传
↓
切分
↓
入库

但生产环境需要考虑:

复制代码
旧版本怎么办?

Embedding失败怎么办?

索引做到一半应用重启怎么办?

外部知识已经删除怎么办?

文档已经过期怎么办?

两份制度发生冲突怎么办?

因此文档本身需要状态:

复制代码
Version 1
SUPERSEDED

Version 2
SUPERSEDED

Version 3
CURRENT

索引任务也需要状态:

复制代码
PENDING
   ↓
PROCESSING
   ↓
SUCCESS

或

FAILED

只有同时满足:

复制代码
enabled

current_version

INDEXED

not expired

no conflict

ACL passed

才允许进入检索。

这也是为什么我不建议企业知识库采用:

复制代码
上传文件 → 直接标记成功

而应该把:

文档状态

和:

索引任务状态

分开管理。

甚至还要处理一种比较隐蔽的情况:

复制代码
任务A开始索引V1
      ↓
任务A执行很慢
      ↓
用户已经上传V2
      ↓
任务B完成V2索引
      ↓
任务A此时才执行完成

如果没有版本和任务状态保护,旧任务就可能反过来覆盖新数据。


8. 企业知识源也不应该只有"上传文件"

企业知识往往并不只来自上传 PDF。

还可能来自:

复制代码
MinIO / S3

SharePoint

Confluence

Notion

内部知识平台

挂载目录

这时候新的问题又来了:

每次同步是不是重新全量向量化?

显然不应该。

更加合理的是:

复制代码
Source
  ↓
Cursor
  ↓
Incremental Sync
  ↓
Content Hash
  ↓
Changed?
 ↙     ↘
YES     NO
 ↓       ↓
Reindex Renew

除此之外,还必须处理:

  • 源端删除;
  • ACL发生变化;
  • 同步失败;
  • Cursor异常;
  • 长期没有成功同步;
  • 外部文档过期;
  • 内容冲突。

因此做到后面会发现:

企业 Knowledge Copilot 已经不只是一个 RAG 页面。

它逐渐变成一个:

知识接入 + 知识生命周期 + 检索 + AI问答 + 质量治理系统。

这也是生产级 RAG 真正复杂的地方。


9. 回答错误之后,还需要Quality Loop

做到前面这些,是不是就不会回答错了?

当然不是。

LLM 永远不可能保证 100% 正确。

所以系统还需要解决最后一个问题:

错了以后怎么办?

简单一点的产品通常只有:

复制代码
👍 / 👎

但这对于真正定位问题还不够。

一次错误回答可能来自完全不同的原因:

复制代码
Retrieval Error
检索错了

Evidence Error
证据不足

Citation Error
引用错误

Answer Error
模型理解错误

Knowledge Error
知识本身过期

Policy Error
规则设计问题

因此在 Knowledge Copilot 中,我把用户反馈继续进入 Quality Queue。

整体链路是:

复制代码
Answer
  ↓
Feedback
  ↓
Quality Queue
  ↓
Reviewer
  ↓
Evidence Assessment
+
Answer Assessment
+
Remediation
+
Disposition
  ↓
Closed

这样人工审核结果才能反过来指导系统优化。

例如:

复制代码
召回错误
→ 调整Retrieval

引用错误
→ 修复Citation Guardrail

知识过期
→ 修复Source / Lifecycle

模型过度推断
→ 调整Prompt / Evidence Gate

资料本身错误
→ 回到Knowledge Governance

这时候"用户点了一个差评"才真正形成工程闭环。


10. 我现在更推荐的生产级RAG架构

把前面的能力串起来以后,一套企业 Knowledge Copilot 大致会变成:

复制代码
                 Knowledge Sources
                       ↓
              Document Processing
                       ↓
              Version / Index Job
                       ↓
            Lifecycle + ACL Filter
                       ↓
        ┌──────────────┼──────────────┐
        ↓              ↓              ↓
   Text Search    Keyword Search   Vector Search
        └──────────────┼──────────────┘
                       ↓
                  Result Fusion
                       ↓
                  Evidence Set
                       ↓
                 Evidence Gate
                   ↙        ↘
            NO_EVIDENCE    SUPPORTED
                 ↓             ↓
              Refuse           LLM
                                 ↓
                       Structured Answer
                                 ↓
                       Citation Guardrail
                                 ↓
                         Final Response
                                 ↓
                     Audit / Feedback
                                 ↓
                        Quality Review

这时候企业 RAG 已经从:

复制代码
Retrieval + LLM

升级成:

复制代码
Knowledge Lifecycle
+
Hybrid Retrieval
+
Evidence
+
Generation
+
Citation Guardrail
+
Audit
+
Quality Loop

11. Spring AI 2.0负责什么,业务系统又该负责什么?

这里也很容易产生一个误区:

使用 Spring AI 做了 RAG,就等于企业级 RAG 已经完成。

实际上不是。

Spring AI 2.0 可以帮我们提供很多 AI 应用基础设施,例如:

复制代码
Chat Model

Embedding Model

Vector Store

Prompt

Structured Output

Tool Calling

Advisor

这些能力能大幅降低 Java 开发 AI 应用的成本。

但:

复制代码
Document Version

ACL

Knowledge Lifecycle

Index Job

Citation Validation

NO_EVIDENCE

Conflict Management

Quality Review

Audit

这些仍然需要你的业务系统负责。

这和我们以前做 Spring Boot 项目其实非常像。

Spring Boot 帮我们解决:

复制代码
IOC
MVC
Transaction
AOP

但是:

复制代码
订单状态

审批规则

业务权限

库存一致性

依然要自己设计。

AI Framework 也是同样的道理。

框架负责提供能力,企业系统负责守住业务边界。


12. 开源项目:Spring AI Business Copilot

本文上面讲到的这些能力,并不是我单独整理的一套概念。

目前都在持续落到我的开源项目:

Spring AI Business Copilot

⭐ GitHub:

GitHub - qcodingdev/spring-ai-business-copilot: Production-style Java + Spring AI 2.0 enterprise AI workbench: cited RAG, Text-to-SQL, support, HR, guardrails, human-in-the-loop and audit. · GitHubProduction-style Java + Spring AI 2.0 enterprise AI workbench: cited RAG, Text-to-SQL, support, HR, guardrails, human-in-the-loop and audit. - qcodingdev/spring-ai-business-copilothttps://github.com/qcodingdev/spring-ai-business-copilot

当前项目已经包含:

复制代码
Knowledge Copilot
企业知识助手

Data Copilot
Text-to-SQL / 数据分析

Support Copilot
客户服务

Report Copilot
企业报告

HR Copilot
招聘与员工服务

其中本文拆解的 Knowledge Copilot 已经覆盖:

  • 文档版本管理
  • 持久化索引任务
  • PostgreSQL Text Search
  • Keyword Search
  • pgvector Vector Search
  • 混合检索
  • Role / Category权限过滤
  • 无依据拒答
  • Citation Guardrail
  • 过期与冲突知识过滤
  • Answer Feedback
  • Quality Review Queue

项目可以直接 Clone 后运行。

如果你正在学习:

复制代码
Spring AI 2.0

RAG

Agent

企业AI架构

AI工程治理

也可以直接拿它做学习和二次开发参考。

如果看到这里还没有 Star,可以顺手支持一下。⭐

后续我也会继续基于这个项目拆:

Text-to-SQL 真正进入生产为什么不能直接执行 SQL?
Guardrails 和 Human-in-the-loop 到底应该放在哪一层?
Agent Evaluation 怎么做离线评测、Readiness 和上线门禁?


13. 总结

回到本文最开始的问题:

企业级 RAG 真正难在哪里?

现在答案应该比较清楚了。

难的并不是:

复制代码
把PDF转成向量。

而是:

复制代码
知识是不是最新的?

用户有没有权限?

索引是否真的完成?

检索结果能不能作为证据?

引用是不是真的?

没有证据时AI敢不敢拒答?

知识更新后旧版本怎么处理?

AI回答错了以后怎么追查?

所以我现在更愿意把企业 RAG 理解成:

一个围绕"可信知识"建立起来的 AI 应用系统,而不仅仅是向量检索。

Vector Search 只是其中一个组件。

真正决定它能不能进入企业生产环境的,是:

版本、权限、生命周期、证据、引用、拒答、审计和质量闭环。

最终我们真正需要完成的是:

复制代码
AI能回答

到:

复制代码
AI为什么这样回答,我能够证明。

的升级。


👨‍💻 关于作者|QCoding

专注 AI应用开发与Java技术实践

持续分享 Spring AI、RAG、 Agentic、Agent Evaluation、企业AI架构、工程治理与AI转型实践

相关推荐
新知图书1 小时前
第13章 从一句话需求到上线“续费管家”小程序
人工智能·小程序·智能体
吴声子夜歌1 小时前
ApacheCommons——commons-math3(科学计算与线性代数)(一)
java·线性代数·算法·apache
m0_547486661 小时前
《计算机视觉实践》全套PPT课件2026
人工智能·计算机视觉
tachibana21 小时前
Embedding 有哪几种算法?
人工智能·算法·ai·大模型·llm·embedding·agent
PFFstronger1 小时前
Code Diff 测试覆盖分析工具 — 开发提测后,自动发现遗漏的测试点
人工智能·功能测试·ai
小小工匠1 小时前
Open WebUI 深度解析:把大模型装进自己机房的那一层「AI 操作系统」
人工智能·openwebui
minhuan1 小时前
基于Playwright数据采集,大模型对接Flask+SQLite,平滑升级向量库与分布式微服务26.5
人工智能·flask·大模型应用·大模型业务集成·微服务架构演进
动物园猫1 小时前
驾驶员危险行为目标检测数据集:3类别、14,000张图像 | 目标检测
人工智能·目标检测·计算机视觉
AI的探索之旅1 小时前
97 个 OpenCV 实例(二十一):音频进阶,音视频同抽与麦克风采集
人工智能·opencv·音视频