跳到正文
Agent 工程实践RAG 与知识库
返回课程/Agent 工程实践
第 8 / 15 章 约 8 分钟

RAG 与知识库

理解检索、引用、知识库与信息隔离。

这个模块在 Agent 里的位置

这个模块决定 Agent 从哪里获得可信事实。它影响:

  • 模型是否引用真实来源。
  • 知识是否过期。
  • 用户偏好是否被错误当成事实。
  • 多租户数据是否隔离。
  • 检索结果是否会撑爆上下文。

很多人把 RAG 理解成“向量库 + topK”。工程上远远不够。

一个生产级检索链路至少要处理:

  1. 租户过滤。
  2. 结构化过滤。
  3. FTS / 向量混合召回。
  4. rerank。
  5. 引用校验。
  6. 知识版本和新鲜度。
  7. 记忆删除。

先记住几个概念

概念含义为什么重要
Working context当前 turn 可见消息短期记忆
Session history当前任务历史任务期记忆
Long-term memory用户偏好、历史结论跨任务记忆
Knowledge base外部事实、文档、指标独立事实源
sourceId稳定来源 ID引用可校验
version文档版本支持审计和过期判断
tenant scope租户隔离边界防止跨租户泄漏

从一个真实问题开始:模型引用了一篇过期文档

用户问:

根据最新供应商资料,判断这家供应商是否有交付风险。

模型回答:

供应商 A 已具备稳定交付能力。

用户反馈这是错的。排查后发现:

  1. 知识库里有两份同名文档。
  2. 旧文档是 2024 年版本。
  3. 新文档是 2026 年版本。
  4. 向量检索按相似度召回了旧文档。
  5. 系统没有把 updatedAt 和 version 传给模型。
  6. 输出校验只检查引用存在,不检查是否最新。

这不是“模型幻觉”,而是知识契约缺失。

记忆和知识必须分开

很多人会把“记忆”和“知识库”混在一个表里。这会带来两个问题:

  1. 用户偏好被当成事实。
  2. 事实更新后,用户记忆还保留旧结论。

更合理的边界是:

类型内容生命周期举例
Long-term memory用户偏好、历史结论跨任务、可删除“报告偏好中文”
Knowledge base外部事实、文档、指标独立事实源、有版本“供应商 2026 年资质文件”

记忆可以是:

{
    "memoryType": "output_preference",
    "content": "报告偏好中文",
    "confidence": 0.92,
    "sourceRunId": "run-88",
    "expiresAt": "2026-12-31T00:00:00Z"
}

知识应该是:

{
    "sourceId": "doc-9182",
    "version": 12,
    "updatedAt": "2026-09-23T08:00:00Z",
    "confidence": 0.87
}

这两者不能互相替代。

检索不是只做向量 topK

在一个 5000 万文档、2000 租户的知识库里,正确路径通常是:

结构化过滤
  -> FTS / 向量召回
  -> rerank
  -> topK 10
  -> 返回 sourceId / version / updatedAt

结构化过滤必须先做:

  • tenantId
  • sourceType
  • updatedAt
  • entityRef
  • 权限范围

如果跳过结构化过滤,直接全库向量检索,会出现:

  1. 跨租户数据进入候选。
  2. 过期文档排在前面。
  3. 无权限文档被召回。
  4. 相似但无关的文档干扰判断。

多租户隔离不能交给模型

tenant scope 必须由服务端注入。

错误请求:

{
    "query": "供应商 A 的风险",
    "tenantId": 7
}

如果 tenantId 来自模型输出,就有被改写风险。

正确做法:

{
    "query": "供应商 A 的风险",
    "tenantScope": {
        "tenantId": 7,
        "source": "server"
    }
}

并在查询层强制:

where tenant_id = $1

模型永远只能提出“查什么”,不能决定“查哪个租户”。

引用必须可校验

RAG 输出不能只是“根据资料,供应商 A 有风险”。

应该返回:

{
    "answer": "供应商 A 存在交付风险",
    "sourceRefs": [
        {
            "sourceId": "doc-9182",
            "version": 12,
            "updatedAt": "2026-09-23T08:00:00Z",
            "quote": "交付延迟记录"
        }
    ]
}

Runtime validator 至少检查:

  1. sourceId 是否存在。
  2. version 是否是当前版本或允许版本。
  3. tenantId 是否匹配。
  4. 引用是否真的支持结论。

最后一点可以用引用一致性校验或人工抽检兜底。

知识更新和过期处理

知识库需要显式版本:

create table knowledge_documents (
  id bigint primary key,
  tenant_id bigint not null,
  source_type text not null,
  title text not null,
  content_ref text not null,
  version int not null,
  updated_at timestamptz not null,
  unique (tenant_id, id, version)
);

create table knowledge_chunks (
  id bigint primary key,
  tenant_id bigint not null,
  document_id bigint not null,
  document_version int not null,
  chunk_index int not null,
  chunk_hash text not null,
  content text not null,
  unique (tenant_id, document_id, document_version, chunk_index)
);

设计要点:

  1. 文档和 chunk 都有版本。
  2. 相同 hash 不重复入库。
  3. 检索时可以按版本过滤。
  4. 旧版本可归档,不建议直接删除。

记忆要可删除、可降权

用户记忆表:

create table user_memories (
  id bigint primary key,
  tenant_id bigint not null,
  user_id bigint not null,
  memory_type text not null,
  content text not null,
  confidence numeric not null,
  source_run_id bigint,
  created_at timestamptz not null,
  expires_at timestamptz
);

create index user_memories_lookup_idx
  on user_memories (tenant_id, user_id, memory_type, updated_at desc);

必须支持:

  1. 用户删除。
  2. 过期降权。
  3. 按 run 追溯来源。
  4. 记忆污染清理。

否则一次错误总结会长期影响后续任务。

RAG 结果如何进入上下文

检索结果也应该瘦身:

{
    "query": "供应商 A 风险",
    "results": [
        {
            "sourceId": "doc-9182",
            "version": 12,
            "snippet": "2026 年二季度出现两次交付延迟",
            "score": 0.91
        }
    ],
    "totalCandidates": 1240,
    "truncated": true
}

不要把整篇文档直接塞给模型。

如果需要全文,再通过 sourceId + version 精查。

和传统后端存储设计的关系

记忆和知识库的选型,和传统后端的存储设计是同一套思路:

Agent 里的需求传统后端里的对应物
短期记忆Redis / 本地缓存
长期记忆Postgres / MySQL 用户表
知识库搜索引擎 + 对象存储
向量检索向量数据库 / ANN 索引
版本和审计不可变版本表
租户隔离行级权限 / 分库分表

选型时仍然要看:

  1. 读写比例。
  2. 一致性要求。
  3. 查询模式。
  4. 数据生命周期。
  5. 成本和运维复杂度。

Agent 只是把“检索结果进入模型上下文”这一步加进了传统存储链路。

常见工程错误

错误实际后果正确做法
记忆当事实旧结论误导当前任务当前事实查知识库
无租户过滤跨租户数据泄漏服务端注入 tenant scope
只做向量召回过期或无关文档进入上下文结构化过滤 + FTS + rerank
引用只检查存在伪造或过期引用通过校验版本、租户和一致性
知识无版本无法审计document version
记忆不可删除合规和污染风险user delete API
检索返回全文上下文膨胀snippet + sourceId

上线前检查

  1. 记忆和知识分表。
  2. 记忆可删除、可过期。
  3. 知识有版本和更新时间。
  4. 检索先做租户和结构化过滤。
  5. FTS / 向量 / rerank 组合使用。
  6. 引用包含 sourceId、version、updatedAt。
  7. 输出校验引用真实存在。
  8. 检索结果进入上下文前先瘦身。