1. 知识库是什么:先破除三个误解

1.1 定义

企业知识库,是对企业内部分散的知识进行采集、治理、索引,并以搜索、问答、API 等形式提供给员工和系统消费的软件系统。

它的成熟度通常经历三个阶段:

阶段 形态 用户价值 典型技术
L1 文档存储 能找到文件 网盘、Wiki、DMS
L2 全文检索 能搜到片段 Elasticsearch、BM25
L3 知识服务 能直接回答问题 RAG、Agent、图谱

企业级知识库的目标,是从 L1/L2 走到 L3,并且在权限、治理、可运营性上达到企业标准。

1.2 三个常见误解

误解一:知识库 = 文档管理系统。
不是。文档管理系统解决“存和管”,知识库解决“找和用”。一个装满 PDF 的网盘不是知识库。

误解二:知识库 = 向量数据库。
不是。向量库只是索引层的一种存储。知识库还包含接入、解析、治理、权限、服务、评估等大量组件。把向量库当知识库,等于把 MySQL 当业务系统。

误解三:接了大模型就有知识库。
不是。大模型解决“生成”,不解决“知识从哪来、是否准确、能否追溯、权限是否合规”。没有检索和治理,大模型只会更流畅地编造。

1.3 知识库在整个系统中的位置

flowchart LR U["员工 / 业务系统 / Agent"] --> KB["企业知识库"] KB --> S["搜索"] KB --> QA["问答 / RAG"] KB --> API["知识 API"] S --> U QA --> U API --> U subgraph SRC["知识来源"] D1["Wiki / 文档库"] D2["工单 / CRM"] D3["业务数据库"] D4["代码仓库"] D5["网页 / 共享盘"] end SRC --> KB

知识库是知识的统一出口,而不是又一个数据孤岛。它的价值在于把分散在各系统里的知识,收敛成一致的、可追溯的、有权限的访问入口。


2. 核心概念速通

在进入架构之前,先统一几个术语。后面的所有设计都建立在这些概念之上。

概念 含义 为什么重要
文档(Document) 知识的最小来源单位,如一份 PDF、一个网页 权限、版本、删除都以文档为单位
切块(Chunk) 从文档切出的检索单元 切分质量直接决定召回质量
嵌入(Embedding) 把文本映射为高维向量 让“语义相似”可计算
索引(Index) 为快速检索建立的数据结构 全文索引、向量索引、结构化索引
召回(Recall) 从索引中取出候选片段 召回不到,后面全白搭
重排(Rerank) 对候选做精细排序 提升 Top-N 的准确率
上下文(Context) 送入大模型的证据片段 决定答案是否“有据可依”
引用(Citation) 答案对应的原始出处 可信度和可审计性的基础

一个关键区分:

  • 写路径(离线构建):数据源 → 解析 → 切分 → 索引。关注正确性、时效性、可重建。
  • 读路径(在线服务):提问 → 召回 → 排序 → 生成。关注延迟、准确率、可解释。

两条路径必须共用同一套处理逻辑,否则评估结果和线上表现会严重脱节。


3. 整体架构:六层 + 横切能力

企业级知识库推荐采用分层架构,层与层之间通过稳定接口解耦。这样换模型、加数据源、换索引时,不会牵一发动全身。

flowchart TB subgraph L1["应用层"] direction LR A1["企业搜索"] A2["智能问答 / RAG"] A3["Copilot / Agent"] A4["开放 API"] end subgraph L2["服务层"] direction LR S1["查询理解"] S2["检索路由"] S3["召回融合"] S4["Rerank"] S5["上下文组装"] S6["生成与引用"] end subgraph L3["索引层"] direction LR I1["全文索引 BM25"] I2["向量索引"] I3["结构化索引"] I4["图谱(可选)"] end subgraph L4["处理层"] direction LR P1["解析"] P2["清洗"] P3["切分"] P4["富化 / 权限标注"] end subgraph L5["接入层"] direction LR D1["文档库 / Wiki"] D2["工单 / CRM"] D3["数据库"] D4["代码仓库"] D5["网页 / 共享盘"] end subgraph X["横切能力"] direction LR X1["身份与权限"] X2["元数据标准"] X3["审计"] X4["监控与评测"] end L5 --> L4 L4 --> L3 L2 --> L3 L1 --> L2 X1 -.-> L2 X1 -.-> L3 X2 -.-> L4 X3 -.-> L2 X4 -.-> L2

各层职责

层 职责 关键设计
应用层 面向用户/系统的产品形态 搜索、问答、API、Copilot
服务层 查询理解、召回、排序、生成 可配置、可观测、可降级
索引层 多种索引并存 支持混合检索与索引重建
处理层 解析、清洗、切分、富化 离线在线同源
接入层 连接各数据源 增量同步、幂等、删除同步
横切 权限、元数据、审计、监控 从第一天就设计进每一层

架构建议:横切的三件事(身份权限、元数据标准、审计)是事后最难补的。不要等到“后面再加”。


4. 工作流程:写路径、读路径、运维闭环

知识库本质上是三条流水线。绝大多数“不好用”的问题,出在第一和第三条,而不是检索算法。

4.1 离线构建(写路径)

flowchart LR A["数据源"] --> B["增量采集"] B --> C["文档解析"] C --> D["清洗去噪"] D --> E["结构化切分"] E --> F["富化
元数据 · 权限 · 实体"] F --> G["Embedding"] G --> H["写入索引"] H --> I["蓝绿切换 / 发布"]

各阶段要点:

  1. 采集:支持全量与增量。增量以 updated_at 游标或 CDC 为准,必须幂等。
  2. 解析:PDF、Word、HTML、表格、扫描件分别处理。版面结构(标题层级、表格)要尽量保留。
  3. 清洗:去页眉页脚、导航、重复模板、乱码。洗太狠会丢语义,洗太轻会污染召回。
  4. 切分:优先按标题层级切,其次按段落,最后才用定长滑窗。这是影响效果最大的一步。
  5. 富化:为每个 chunk 打上来源、部门、密级、有效期、权限标识。元数据决定了后续能否过滤。
  6. 建索引:同步写入全文索引、向量索引、结构化字段。可选图谱。
  7. 发布:通过别名/版本做原子切换,避免用户查到“更新到一半”的数据。

4.2 在线服务(读路径)

flowchart TD Q["用户提问"] --> U["查询理解
改写 · 指代消解 · 多轮补全"] U --> F["ACL 与时效条件下推"] F --> R{"检索路由"} R -->|"精确术语 / 编号"| K["全文 + 结构化检索"] R -->|"语义描述"| V["向量召回"] R -->|"实体关系"| G["图谱查询"] K --> M["融合去重
RRF"] V --> M G --> M M --> RR["Rerank 精排"] RR --> CUT["截断到上下文预算"] CUT --> CTX["上下文组装
去冗余 · 附来源"] CTX --> LLM["生成答案"] CTX --> RAW["直接返回片段"] LLM --> OUT["答案 + 引用"] RAW --> OUT

请求时序如下:

sequenceDiagram autonumber participant U as 用户 participant Q as 查询服务 participant RT as 路由 participant IDX as 索引层 participant RR as Rerank participant LLM as 生成模型 U->>Q: 提问 Q->>Q: 改写 / 补全 / 意图识别 Q->>RT: 规范化查询 + 用户身份 RT->>IDX: 并行召回(带 ACL 过滤) IDX-->>RT: 多路候选集 RT->>RR: 融合去重后精排 RR-->>Q: Top-N 证据 Q->>LLM: 问题 + 证据 + 引用要求 LLM-->>U: 答案 + 出处

读路径的三个原则:

  1. 权限在召回阶段生效,不能事后再筛。
  2. 召回宁多勿少,排序宁精勿滥。召回和排序是两个目标,分开优化。
  3. 没有证据就不回答。拒答比编造更有价值。

4.3 运维闭环

flowchart LR A["线上查询日志"] --> B["bad case 归集"] B --> C{"归因"} C -->|"没召回"| D["补数据 / 调切分"] C -->|"召回错"| E["调权重 / 换 Embedding"] C -->|"排序差"| F["调 Rerank"] C -->|"答非所问"| G["调 Prompt / 拒答策略"] C -->|"内容过期"| H["刷新数据源 / 下线"] D --> I["更新索引"] E --> I F --> I G --> I H --> I I --> J["黄金评测集回归"] J --> A

运营闭环是整个系统能否长期存活的关键。没有评测集的团队,所有优化都只能靠“感觉好像好一点”。


5. 技术选型与技术对比

5.1 第一个问题:一定要用向量库吗?

不必须。 向量检索只解决“语义相似”这一类问题。知识库的真实查询是混合的:

检索方式 擅长 不擅长 典型实现
全文 / BM25 精确术语、代码、编号、法规条款 同义表达、口语化提问 Elasticsearch、OpenSearch、SQLite FTS5
结构化查询 元数据过滤(时间/作者/部门/状态) 非结构化长文本 SQL、图查询
知识图谱 实体关系推理 模糊长文本召回 Neo4j、RDF
向量检索 语义模糊匹配、同义改写、跨语言 精确 ID、否定词、数字比较 FAISS、pgvector、Milvus
混合检索 真实企业场景的绝大多数情况 增加复杂度 BM25 + 向量 + Rerank

结论:企业级场景默认走混合检索。纯向量在精确匹配上会输给 BM25,纯关键词在同义表达上会输给向量,两者互补。

5.2 检索方式决策树

flowchart TD A["这条查询主要想解决什么?"] --> B{"是否包含
精确编号 / 代码 / 条款号?"} B -->|"是"| C["全文检索 / 结构化查询"] B -->|"否"| D{"用户会用
不同措辞表达吗?"} D -->|"是"| E["向量检索"] D -->|"否"| F["全文检索"] C --> G["与向量结果融合 + Rerank"] E --> G F --> G G --> H["统一返回 Top-N + 引用"]

5.3 向量存储:库 vs 数据库

一个常见误区:做语义检索 ≠ 必须部署向量数据库。

方案 适合规模 运维成本 过滤能力 适用场景
numpy 暴力检索 < 10 万 极低 强(自己写) 原型、单机小知识库
FAISS 10 万 – 千万 低(进程内库) 弱 离线批处理、单机服务
sqlite-vec < 百万 极低 强 桌面应用、边缘部署
pgvector 百万 – 千万 低(复用 PG) 强 已有 PostgreSQL 的企业
Qdrant 千万级 中 强 需要独立服务、过滤复杂
Milvus 亿级 高 强 大规模、多租户
Elasticsearch kNN 千万级 中高 强 已有 ES,想统一检索
Pinecone / 云服务 按需 极低 中 无运维团队、数据可出域

选型建议:

  • 已有 PostgreSQL 且规模 < 1000 万 → 直接用 pgvector,少一个组件就是少一份运维。
  • 已有 Elasticsearch → 优先用 ES 的 BM25 + kNN,统一检索栈。
  • 需要独立、大规模、多租户 → Qdrant 或 Milvus。
  • 数据不能出域 → 全私有化部署,排除 SaaS 向量库。

5.4 Embedding 模型选型

企业中文场景的常见候选:

模型 语言 部署 特点 适用
BGE-M3 多语言(中英强) 私有化 稠密 + 稀疏 + 多向量,长文本 通用首选
BGE-large-zh-v1.5 中文 私有化 成熟稳定,生态好 纯中文、资源有限
GTE 系列 中英 私有化 阿里系,中文表现好 国内企业
Jina Embeddings v3 多语言 私有化/API 长上下文、任务适配 多语言文档
OpenAI text-embedding-3 多语言 API 效果好、免运维 数据可出域

选型维度:

  1. 语言:中文为主优先 BGE / GTE。
  2. 部署合规:金融、政务、医疗通常要求私有化。
  3. 维度与成本:维度越高,存储和计算成本越高。1024 维是常见平衡点。
  4. 长文本能力:BGE-M3 支持 8192 token,适合长文档。
  5. 可替换性:必须假设未来会换模型,索引要能全量重建。

硬性要求:换 Embedding 模型意味着所有向量都要重算。设计时必须保留原始文档和切分结果,支持“从零重建索引”。

5.5 Rerank 模型

召回负责“别漏”,Rerank 负责“排对”。常见选择:

方案 部署 特点
BGE-Reranker-v2-m3 私有化 中英强,性价比高,首选
Cohere Rerank API 效果好,数据需出域
Jina Reranker 私有化/API 多语言
Cross-Encoder 自训 私有化 需标注数据,适合垂直领域

经验值:Rerank 通常能把 Top-5 的准确率提升一个可观的比例,是 ROI 最高的组件之一。代价是增加几十到几百毫秒延迟,需要做超时降级。

5.6 文档解析

工具 能力 适合
Apache Tika 格式覆盖最广 Office、邮件、通用格式
Unstructured 版面感知、元素划分 混合格式文档
MinerU PDF 版面/公式/表格强 学术、报告类 PDF
Docling 版面分析 + 表格 企业 PDF
PyMuPDF / pdfplumber 轻量、可控 结构化 PDF
商业文档智能 OCR、表格、手写 扫描件、票据

建议组合:Tika 处理 Office,MinerU/Docling 处理 PDF,OCR 兜底扫描件。解析质量差,后面所有环节都救不回来。

5.7 编排框架

方案 定位 优点 风险
自研服务层 生产系统 完全可控、易治理 工作量大
LangChain 开发库 生态丰富、灵活 抽象重、版本变动快
LlamaIndex 数据框架 索引/检索能力强 黑盒较多
Dify 应用平台 低代码、快速验证 深度定制受限
RAGFlow RAG 引擎 解析和切分较强 与现有系统集成需评估
FastGPT 知识库应用 国内生态、开箱即用 大规模治理需扩展

企业级建议:用开源框架做原型验证,用自研服务层承载生产。框架适合“验证可行性”,不适合“长期承载权限、审计、多租户”。

5.8 默认推荐组合(典型私有化企业)

组件 推荐 理由
解析 Tika + MinerU 格式覆盖 + PDF 质量
切分 自研(按标题层级) 效果最好,可控
全文检索 Elasticsearch / OpenSearch 成熟、生态好
向量存储 pgvector(中小)/ Qdrant、Milvus(大) 按规模选择
Embedding BGE-M3 中英、长文本、可私有化
Rerank BGE-Reranker-v2-m3 性价比高
编排 自研服务层 权限、审计、多租户
生成模型 私有化 Qwen / DeepSeek 或合规 API 按合规要求

6. 为什么这么选:决策依据

技术选型不是选“最强的”,而是选“约束下最合适的”。推荐用 约束 → 候选 → 判定标准 → 结论 的方式记录决策(类似 ADR)。

6.1 为什么是混合检索,而不是纯向量

维度 纯向量 纯 BM25 混合
同义表达 强 弱 强
精确编号 弱 强 强
否定词 / 数字 弱 强 强
可解释性 弱 强 中
冷启动 需 Embedding 直接可用 需 Embedding
成本 高 低 中高

企业查询天然混合:既有“怎么申请报销”这种语义问题,也有“ERR-2041 是什么错误”这种精确问题。只做一种,必然有一类查询体验很差。

6.2 为什么中小规模优先 pgvector

  • 复用现有 PostgreSQL,少一个运维组件;
  • 支持事务、备份、监控,和业务数据同栈;
  • HNSW 索引在千万级以内性能足够;
  • 迁移成本低,规模上来再换 Milvus/Qdrant 也不难。

什么时候不该用:需要亿级向量、强多租户隔离、超高 QPS 时,再上专用向量库。

6.3 为什么一开始不上 GraphRAG

图谱能解决多跳推理(“A 的负责人所在部门的制度有哪些”),但代价是:

  • 实体抽取和关系构建成本高;
  • 更新维护复杂;
  • 对大多数“查文档、答问题”的场景,收益不明显。

建议:先用“混合检索 + Rerank”覆盖 80% 场景,确认存在明确的多跳关系需求后,再引入图谱作为补充索引,而不是替代。

6.4 为什么保留原始文档和切分结果

这是为了三个能力:

  1. 索引可重建:换 Embedding、换向量库时可以全量重算。
  2. 可追溯:答案能回到原文。
  3. 可调试:切分效果不好时能复现和调整。

如果只存向量不存原文,系统就成了一次性的黑盒。


7. 实现逻辑:关键模块设计

以下用 Python 风格伪代码说明核心逻辑,重点在设计意图,不追求可直接运行。

7.1 数据接入:增量与幂等

def sync_source(source, cursor):
    """按更新时间增量拉取,失败可重试,重复执行结果一致。"""
    docs = source.fetch(since=cursor.updated_at, limit=500)

    for doc in docs:
        content_hash = sha256(doc.content)

        # 内容未变则跳过,避免无谓的重新索引
        if store.get_hash(doc.id) == content_hash:
            continue

        pipeline.enqueue({
            "doc_id": doc.id,
            "source": source.name,
            "content": doc.content,
            "metadata": doc.metadata,     # 部门、密级、作者、有效期
            "acl": doc.acl,               # 权限标识,必须随文档一起流转
            "version": doc.version,
            "deleted": doc.deleted,       # 删除标记,必须同步
        })
        store.save_hash(doc.id, content_hash)

    return docs[-1].updated_at if docs else cursor.updated_at

三个必须做到的点:

  1. 幂等:同一文档重复处理,结果一致。
  2. 删除同步:源系统删了,索引必须删(合规要求)。
  3. 权限随行:ACL 必须从源头带到每个 chunk。

7.2 切分:结构优先,父子关联

def chunk_document(doc, max_tokens=800, overlap=100):
    """
    优先级:标题层级 > 段落 > 定长滑窗。
    每个 chunk 保留父文档路径,便于回溯和上下文扩展。
    """
    sections = split_by_heading(doc)          # 按 H1/H2/H3 切
    chunks = []

    for sec in sections:
        if estimate_tokens(sec.text) <= max_tokens:
            chunks.append(make_chunk(sec, parent=doc))
        else:
            # 过长段落再按句子边界切,保留 overlap
            for piece in sliding_window(sec.text, max_tokens, overlap):
                chunks.append(make_chunk(piece, parent=doc, heading=sec.heading))

    return chunks

为什么结构优先: 企业文档(制度、手册、API 文档)本身有清晰的标题层级,按结构切出的 chunk 语义完整,召回精度显著更高。定长切分会把一句话拦腰截断。

父子块(small-to-big)思路:用小块做检索(精度高),命中后返回其父段落做上下文(信息全)。这是工业界常用技巧。

7.3 富化与权限标注

def enrich(chunk, doc):
    return {
        **chunk,
        "doc_id": doc.id,
        "source": doc.source,
        "title": doc.title,
        "heading_path": chunk.heading_path,     # 例:员工手册 > 报销 > 差旅
        "department": doc.metadata.department,
        "classification": doc.metadata.classification,  # 密级
        "effective_at": doc.metadata.effective_at,
        "expires_at": doc.metadata.expires_at,
        "acl": doc.acl,                          # 关键:权限随 chunk 走
        "version": doc.version,
    }

元数据不是可选项。 它决定了:

  • 能不能按部门/密级过滤;
  • 能不能按有效期剔除过期内容;
  • 答案能不能给出准确的出处。

7.4 索引构建:多索引 + 蓝绿发布

flowchart LR A["原始文档 + 切分结果"] --> B["处理管线"] B --> C["索引 vN(蓝)"] B --> D["索引 vN+1(绿)"] E["别名 kb-live"] -.-> C E -.-> D F["切换"] --> E
def rebuild_index(docs):
    new_index = create_index(f"kb-{next_version()}")

    for chunk in process(docs):
        new_index.add_text(chunk.id, chunk.text)
        new_index.add_vector(chunk.id, embed(chunk.text))
        new_index.add_metadata(chunk.id, chunk.metadata)

    # 校验通过后原子切换别名,读流量无感迁移
    new_index.verify(docs_count=len(docs))
    alias_swap("kb-live", from_=current, to=new_index)

    # 保留上一版本一段时间,便于回滚
    schedule_cleanup(old_index, after="24h")

蓝绿发布解决三个问题:更新不中断、可回滚、可校验。没有它,任何一次全量重建都可能造成线上事故。

7.5 查询理解与路由

def understand(query, history=None, user=None):
    q = normalize(query)

    if history:
        q = resolve_pronouns(q, history)      # “它的有效期是多久?”

    intent = classify_intent(q)               # 精确查询 / 语义问答 / 关系查询
    rewritten = expand_synonyms(q)            # 可选:同义词扩展

    return Query(
        text=rewritten,
        intent=intent,
        acl_filter=build_acl_filter(user),    # 权限条件随查询下推
        time_filter=build_time_filter(user),  # 只查有权且有效的内容
    )

路由策略:

  • 命中错误码、订单号、条款号等模式 → 走全文/结构化;
  • 自然语言描述 → 走向量;
  • 显式关系问题 → 走图谱(如有);
  • 不确定时 → 全部并行召回,交给融合层。

7.6 混合召回与 RRF 融合

def hybrid_recall(query, top_k=50):
    # 权限与时效条件在检索阶段就下推,而不是召回后再筛
    filters = query.acl_filter & query.time_filter

    bm25_hits = fulltext.search(query.text, top_k=top_k, filters=filters)
    vec_hits  = vector.search(embed(query.text), top_k=top_k, filters=filters)

    return rrf_fuse([bm25_hits, vec_hits], k=60, top_n=30)


def rrf_fuse(result_lists, k=60, top_n=20):
    """Reciprocal Rank Fusion:只用名次,不用分数,天然规避量纲不一致。"""
    scores = defaultdict(float)
    for results in result_lists:
        for rank, hit in enumerate(results, start=1):
            scores[hit.id] += 1.0 / (k + rank)
    return sorted(scores.items(), key=lambda x: -x[1])[:top_n]

为什么用 RRF: BM25 分数和向量相似度量纲完全不同,直接加权很难调。RRF 只看排名,简单、稳健、无需调参,是混合检索的默认融合方式。

7.7 Rerank 与上下文组装

def rerank_and_pack(query, candidates, token_budget=6000):
    ranked = reranker.rank(query.text, candidates)        # 精排

    packed, used = [], 0
    seen = set()

    for hit in ranked:
        if hit.id in seen:
            continue
        if hit.doc_id in seen_docs(packed):                # 同一文档限制条数
            continue
        if used + hit.tokens > token_budget:
            break
        packed.append(hit)
        used += hit.tokens
        seen.add(hit.id)

    return packed

组装原则:

  • 去重,避免同一段内容重复喂给模型;
  • 每份证据都带 source / title / heading_path,供引用;
  • 控制 token 预算,优先保留高分证据;
  • 证据不足时明确拒答,而不是让模型硬编。

7.8 生成与引用

def answer(query, evidence):
    if not evidence or max(e.score for e in evidence) < REFUSE_THRESHOLD:
        return "未在知识库中找到足够依据,建议补充资料或转人工。"

    ctx = "\n\n".join(
        f"[{i+1}] {e.title} / {e.heading_path}\n{e.text}"
        for i, e in enumerate(evidence)
    )

    result = llm.generate(
        system="只能依据给定材料回答;每个结论必须标注引用编号;材料不足时明确说明。",
        user=f"问题:{query.text}\n\n材料:\n{ctx}",
    )

    validate_citations(result)   # 校验引用编号是否真实存在
    return result

两个硬约束:

  1. 无证据不回答(拒答阈值可配置)。
  2. 引用必须真实存在,不能是模型编造的编号。

8. 核心关键点:决定成败的八件事

8.1 权限必须在检索阶段生效(P0)

这不是“最佳实践”,是安全底线。

flowchart LR U["用户身份"] --> A["构建 ACL 条件"] A --> B["全文检索
(带过滤)"] A --> C["向量检索
(带过滤)"] B --> D["候选集"] C --> D D --> E["Rerank / 生成"]

错误做法:先召回全部,再在应用层筛掉无权文档。问题在于:

  • 无权内容的元信息可能已经进入日志或缓存;
  • 候选集被无关内容挤占,导致有效结果变少;
  • 模型可能从上下文里“推断”出不该知道的信息。

正确做法:把 ACL 作为过滤条件,下推到每一种索引的查询里。

8.2 切分质量 > 模型选择

同一套检索,切分方式不同,效果可能相差一倍。优先级:

  1. 按标题层级切(保留 heading_path)
  2. 按段落 / 语义边界切
  3. 定长滑窗 + overlap(兜底)

8.3 元数据是隐形主角

  • 权限、部门、密级 → 决定可见性;
  • 有效期、版本 → 决定时效性;
  • 来源、标题、路径 → 决定可追溯性。

事后补元数据极其痛苦,必须在接入和切分阶段就打全。

8.4 没有评测集就没有优化

建 20–50 条真实问题的黄金集,每条包含:问题、期望答案、期望来源。

指标建议:

指标 含义
Recall@K 相关片段是否进了候选
MRR / nDCG 排序质量
Faithfulness 答案是否忠于材料
Context Precision / Recall 上下文是否精准且完整
拒答率 不知道时是否老实说
引用准确率 引用是否指向真实来源
P95 延迟 用户体验

每次改切分、换模型、调权重,都跑一遍回归。

8.5 离线与在线必须同源

清洗、切分、Embedding 的逻辑必须共用一套代码。否则:

  • 离线评测结果无法代表线上;
  • 调试时无法复现问题;
  • 版本升级时容易不一致。

8.6 拒答比编造更重要

企业场景里,“不知道”是可接受的,“一本正经地胡说”是不可接受的。做法:

  • 设置相似度/重排分数阈值;
  • Prompt 明确要求“材料不足时说明”;
  • 输出阶段校验引用是否真实存在。

8.7 可观测性

记录每次查询的完整链路:

query_id | 原始问题 | 改写后 | 路由 | 各路召回 | 融合结果 | 重排结果 | 最终证据 | 答案 | 延迟 | 用户反馈

没有这条链路,bad case 只能靠猜。

8.8 内容治理是长期工程

  • 每个知识域有 owner;
  • 权威源唯一,避免多版本并存;
  • 有效期 + 复审周期;
  • 删除同步;
  • 内容质量指标(重复率、过期率、矛盾率)。

9. 注意事项与常见坑

# 坑 后果 对策
1 权限没做或事后过滤 安全事故、被迫回滚 ACL 下推到检索
2 数据源一锅端 召回全是噪音 建立准入标准,先治理再入库
3 没有评测集 无法判断优化是否有效 先建黄金集再优化
4 只上线不运营 内容过期,使用率归零 运营闭环 + owner 机制
5 过度工程 维护成本压垮团队 按规模选型,能用 pgvector 不上 Milvus
6 只做向量 精确查询体验差 混合检索
7 切分一刀切 语义断裂或噪声过多 结构优先 + 父子块
8 忽略删除同步 答出已作废内容,合规风险 删除事件 + tombstone
9 一次全量重建 线上抖动、无法回滚 蓝绿索引 + 别名切换
10 只看准确率不看拒答 模型过度自信 阈值 + 引用校验
11 忽略解析质量 表格、公式、扫描件全部丢失 按格式分而治之
12 权限只做文档级 文档内敏感段落泄露 段落级权限 / 脱敏

合规相关

  • 数据不出域:金融、政务、医疗通常要求私有化部署。
  • 审计留痕:谁查了什么、返回了什么,需可追溯。
  • 删除权:用户/业务要求删除时,索引、缓存、日志都要处理。
  • 最小权限:默认不可见,按需授权。

10. 分阶段落地路线

不要一上来就做“全公司知识平台”。企业级项目的典型死法是“大而全地启动,然后没人用”。

阶段 目标 关键交付 成功标准
0. 选场景 找高频、高价值、内容相对干净的场景 场景定义、成功指标、数据源清单 有明确的使用方和痛点
1. 最小闭环 单数据源、全文检索、返回片段带引用 可用的搜索/问答 能被真实用户使用
2. 质量提升 混合检索 + Rerank + 评测集 + 权限 效果可度量 黄金集指标达标
3. 规模扩展 多数据源、增量更新、多租户、审计 可推广的平台能力 能接入第二个场景
4. 运营体系 贡献机制、复审流程、bad case 闭环 长期运营机制 使用率与满意度稳定
flowchart LR A["阶段0
选场景"] --> B["阶段1
最小闭环"] B --> C["阶段2
质量与权限"] C --> D["阶段3
规模与治理"] D --> E["阶段4
持续运营"] C -.->|"指标不达标"| B E -.->|"bad case 回流"| C

推荐的首选场景:客服问答、运维手册、研发文档、HR 制度。它们的共同点是:查询高频、答案相对确定、内容有明确 owner。


11. 自建还是采购

层次 建议 理由
基础设施(解析、索引、向量库、Rerank) 采购或开源 通用能力,自研不划算
领域层(接入、切分、权限模型、评测集、Prompt) 自建 这是差异化所在
产品层(搜索/问答界面) 视团队而定 无专职团队时用成熟平台起步

一句话:通用能力买,领域能力建。


12. 附录

12.1 上线检查清单

12.2 术语表

术语 说明
RAG Retrieval-Augmented Generation,检索增强生成
BM25 经典全文检索排序算法
Embedding 文本的向量表示
Chunk 文档切分后的检索单元
RRF Reciprocal Rank Fusion,多路结果融合算法
Rerank 对候选结果做精细重排
ACL Access Control List,访问控制列表
CDC Change Data Capture,变更数据捕获
蓝绿索引 新旧索引并存、原子切换的发布方式

12.3 参考方向

  • 检索:BM25、稠密/稀疏混合检索、RRF
  • 模型:BGE 系列、GTE 系列、Rerank 模型
  • 评估:RAGAS 等 RAG 评测框架
  • 解析:Apache Tika、Unstructured、MinerU、Docling
  • 存储:pgvector、Qdrant、Milvus、Elasticsearch

结语

企业级知识库的本质,不是“把文档变成向量”,而是把分散的知识变成可信、可控、可运营的服务。

如果只能记住三句话:

  1. 权限和治理是 P0,检索算法是 P2。
  2. 混合检索 + 好的切分 + Rerank,能覆盖绝大多数场景。
  3. 知识库是产品,不是项目——上线只是运营的开始。

先把一个场景做深、做透、做出可度量的效果,再横向复制。这比一开始就搭一个“大而全的平台”更容易成功。


原文地址: https://www.cveoy.top/t/topic/qHR3 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录