企业搜索这件事,很多人第一反应是"不就是个搜索框"。但真把公司里散落在文档、聊天记录、工单、代码仓库、邮件、会议纪要里的信息全部接进来,做到"问一句话就能找到准确答案,还不会把不该看的内容推给不该看的人",难度完全不在搜索框本身。Glean 这个标题讲的正是这条路径:一个企业搜寻产品从第一个版本、第一批客户到第一次验证。把它放到 CSDN 的技术语境里,值得讨论的不是融资故事,而是企业级搜索与 RAG 知识助手到底由哪些工程模块拼起来、哪些环节最容易翻车、自建一套最小可用系统需要准备什么。
公开资料里,Glean 的定位是企业搜索与 AI 知识助手平台,核心思路是把企业内多个 SaaS 系统的内容统一接入、统一索引,再用语义检索加生成式问答的方式把结果呈现给用户。它最值得关注的不是"接了大模型",而是三件事:一是连接器体系,能把文档、聊天、工单、代码等异构数据源纳入同一套索引;二是权限继承,检索结果必须按用户原有权限过滤,这是企业搜索和个人搜索最大的分水岭;三是检索与生成的配合,先召回再重排再交给模型组织答案,而不是让模型凭空编。
这类产品的门槛也不在显卡上。个人玩 RAG,一台带 8G 显存的机器就能跑起来;企业级搜索真正的成本在数据接入的工程量、索引规模带来的内存与磁盘开销、embedding 与 LLM 的调用费用,以及权限和审计体系的改造。换句话说,第一个产品能不能立住,往往取决于"最先接的三个数据源是不是用户每天都在用的",而不是模型参数量。
下面这篇文章会沿着"能力拆解、场景边界、环境准备、部署启动、功能验证、接口与批量任务、资源观察、排错、最佳实践"这条线展开。前半段讲 Glean 这类企业搜索产品的技术构成与验证思路,后半段给出一套可以自己动手跑起来的最小企业搜索方案,包含可复制的配置与代码。适合正在做企业内部知识库、RAG 问答、搜索中台的技术同学,也适合想判断"要不要自建、自建到什么程度"的架构决策者。
1. 核心能力速览
企业搜索产品和普通的全文检索系统,差别集中在"能不能接住真实企业数据"和"能不能控住权限"这两件事上。先把能力项列清楚,后面所有的部署与测试都围绕这张表展开。需要说明的是,下表里涉及 Glean 的部分来自其公开定位的概括,具体功能、接入源数量、计费方式以其官方文档为准;涉及自建方案的部分是通用工程实践,参数需要按实际组件版本调整。
| 能力项 | 说明 |
|---|---|
| 产品类型 | 企业搜索 + AI 知识助手(SaaS 为主),同时存在大量开源自建替代方案 |
| 开源来源 | 标题所指为商业产品,非开源项目;自建可组合 OpenSearch、pgvector、开源 embedding 与 LLM |
| 主要功能 | 多源内容接入、统一索引、关键词与语义混合检索、RAG 问答、结果权限过滤、搜索 API |
| 数据接入 | 文档、聊天、工单、代码仓库、邮件、会议记录等,通常通过连接器与增量同步实现 |
| 检索方式 | BM25 全文检索 + 向量近邻检索 + 重排模型,多路结果融合 |
| 权限模型 | 继承源系统的 ACL,检索阶段做过滤,而非结果返回后再裁剪 |
| 推荐硬件 | 商业产品无需自备硬件;自建方案取决于索引规模,embedding 可 CPU 推理,LLM 视模型大小而定 |
| 显存占用 | 商业产品不涉及;自建时 embedding 模型通常几百 MB 到数 GB,LLM 需按模型版本实测 |
| 支持平台 | 商业产品以 Web 与浏览器插件为主;自建方案通常为 Linux 服务端 + Web 前端 |
| 启动方式 | 商业产品为注册开通;自建方案为 Docker Compose 或命令行启动 |
| 是否支持 API | 商业产品提供搜索与问答 API;自建方案可自行封装 REST 接口 |
| 是否支持批量任务 | 支持,批量导入与增量同步是必备能力 |
| 适合场景 | 企业内部知识检索、客服辅助、研发文档问答、HR 与制度查询 |
| 不适合场景 | 个人小规模文档管理、无权限体系的完全公开数据、强实时性要求极高的场景 |
这张表里最容易被低估的是"权限模型"这一行。很多团队做内部知识库,第一版直接把所有文档灌进向量库,检索时谁都能搜到全部内容,演示阶段没问题,一旦接入 HR 制度、薪酬说明、客户合同这类数据,立刻变成安全事故。企业搜索产品从第一天就要把 ACL 当作一等公民,检索请求必须携带用户身份,过滤条件在召回阶段就生效。这一点在后面的测试章节会单独拿出来验证。
另一个容易被低估的是增量同步。企业数据每天都在变,文档会改、权限会变、员工会离职。全量重建索引在几千篇文档时还能接受,到了几十万篇就是灾难。所以从第一版开始就要设计增量机制:记录每个数据源的同步位点,按更新时间拉取变更,删除与权限变更也要同步。这不是优化项,是能不能长期跑下去的分水岭。
2. 适用场景与使用边界
先说适合谁。第一类是中大型企业里"信息孤岛严重"的团队,文档在网盘、讨论在即时通讯、工单在项目管理工具、代码在代码托管平台,新人入职要问十个人才能找到一份规范。这类场景下企业搜索的收益最直接,因为它是把已有信息重新组织,不需要改变任何人的工作习惯。第二类是客服与售前团队,需要快速从产品文档、历史工单里找到答案,RAG 问答能把平均响应时间压下来。第三类是研发团队,代码注释、设计文档、故障复盘记录分散在多个系统,语义检索比目录查找有效得多。
再说能解决什么问题。企业搜索解决的核心问题是"召回",也就是把可能相关的内容找出来;RAG 问答解决的是"组织",把召回结果压缩成一段可读的答案。两者不能混为一谈。很多项目失败的原因是直接把问答当搜索用,用户问一个模糊的词,系统非要生成一段话,反而比直接给十条结果更低效。更稳妥的产品设计是搜索结果页和问答入口并存,让用户自己选。
不适合的场景也要讲清楚。第一,数据量极小的个人项目,几十篇文档用本地全文检索工具就够了,上向量库加 LLM 是过度设计,维护成本远大于收益。第二,完全没有权限体系的公开数据,做语义检索意义有限,关键词匹配往往更准更快。第三,对延迟极其敏感的场景,比如在线交易系统内的实时提示,RAG 的多段流水线延迟通常在几百毫秒到数秒,不适合硬塞进去。
合规边界必须单独强调。企业搜索天然要接触内部数据,接入前必须确认数据授权范围,明确哪些数据源允许被索引、哪些字段需要脱敏、日志里能不能留存查询原文。涉及员工个人信息、客户合同、财务数据的内容,要做字段级过滤和访问审计。如果产品要对外提供问答能力,还要确认生成内容不会泄露未授权信息,必要时在答案里附上引用来源,让用户能回溯到原始文档。自建方案同样适用这些要求,甚至更严格,因为责任完全在自己这边。
3. 环境准备与前置条件
自建一套最小可用的企业搜索系统,需要准备的组件可以分成五层:数据接入层、解析与切分层、索引层、检索与重排层、生成层。下面这张清单是通用的,具体版本按实际项目选择,不要照抄版本号。
| 层次 | 组件 | 说明 |
|---|---|---|
| 操作系统 | Linux(Ubuntu 22.04 或同类) | 生产环境不建议用桌面系统 |
| 运行时 | Python 3.10+ | 用于索引脚本与 API 服务 |
| 容器 | Docker 与 Docker Compose | 用于快速起索引服务 |
| 全文索引 | OpenSearch 或 Elasticsearch | 提供 BM25 检索与向量字段 |
| 向量存储 | OpenSearch kNN、pgvector 或 Milvus | 小规模用前两者即可 |
| Embedding | 开源 embedding 模型或 API | 中文场景优先选中文效果好的模型 |
| 重排 | Cross-encoder 重排模型 | 可选,但对准确率提升明显 |
| 生成 | 开源 LLM 或 API | 用于 RAG 问答,按显存与成本选择 |
| 磁盘 | 按索引规模预留 | 向量索引占空间明显,建议单独挂载 |
几个前置检查必须先做。第一,确认端口没有被占用,OpenSearch 默认 9200,自定义 API 服务建议避开常用端口,启动前用ss -lntp看一眼。第二,确认内存足够,OpenSearch 单节点建议至少给 2GB 堆内存,向量索引规模上去后要同步调大。第三,确认 embedding 服务的推理设备,CPU 推理在批量导入时吞吐会明显低于 GPU,如果文档量在十万级以上,要提前评估时间成本。第四,确认磁盘空间,向量索引加原始文本的占用通常是原始文档体积的数倍,留够余量。
还有一个经常被忽略的前置条件:数据源的访问凭据。企业搜索要接多个系统,每个系统都需要独立的鉴权方式,有的用 OAuth,有的用 API Token,有的只能走数据库只读账号。建议在项目一开始就建一张凭据清单,记录每个数据源的类型、权限范围、过期时间、负责人。凭据管理混乱是自建企业搜索最常见的隐性成本,等接了七八个源之后再回头整理,工作量会翻倍。
4. 安装部署与启动方式
最小可用方案用 Docker Compose 起一个单节点 OpenSearch,再写两个 Python 脚本,一个负责批量索引,一个负责检索与问答。生产环境当然要换成集群、加安全插件、配反向代理,但验证阶段单节点足够跑通全流程。
4.1 启动索引服务
下面的 Compose 文件是示例配置,镜像版本、堆内存、数据目录都需要按实际环境调整。关闭安全插件只是为了本地验证方便,任何对外暴露的环境都必须开启认证。
version: "3.8" services: opensearch: image: opensearchproject/opensearch:2.15.0 container_name: os-search environment: - discovery.type=single-node - DISABLE_SECURITY_PLUGIN=true - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g ports: - "9200:9200" volumes: - ./data/opensearch:/usr/share/opensearch/data restart: unless-stopped启动命令很直接,起完之后用健康检查确认服务可用:
# 启动服务 docker compose up -d # 查看容器状态 docker compose ps # 健康检查,返回 status 为 green 或 yellow 即可 curl -s "http://127.0.0.1:9200/_cluster/health?pretty"如果返回连接被拒绝,先看容器日志docker compose logs -f opensearch,再看端口是否被别的进程占用。单节点模式下集群健康状态是 yellow 属于正常现象,因为副本分片无法分配,不影响检索。
4.2 创建索引与字段映射
索引需要同时支持全文检索和向量检索,所以映射里要包含文本字段和 knn 向量字段。下面的请求创建了一个带中文分词、带向量字段、带权限字段的索引。分词器按实际语言选择,中文场景需要提前安装对应分词插件,否则默认分词效果很差。
curl -s -X PUT "http://127.0.0.1:9200/docs" \ -H "Content-Type: application/json" \ -d '{ "settings": { "index": { "knn": true } }, "mappings": { "properties": { "title": { "type": "text" }, "content": { "type": "text" }, "source": { "type": "keyword" }, "owner": { "type": "keyword" }, "acl": { "type": "keyword" }, "updated_at": { "type": "date" }, "embedding": { "type": "knn_vector", "dimension": 1024 } } } }'维度必须和 embedding 模型输出一致,写错的话批量导入会全部失败。建议把维度写进配置文件,索引脚本和建索引脚本共用一个常量,避免两边不一致。
4.3 批量索引脚本
索引脚本的逻辑是:读文档、切分、调 embedding 服务拿向量、拼 NDJSON、批量写入。批量写入必须用_bulk接口,并且 Content-Type 是application/x-ndjson,用普通 JSON 数组提交会直接报错。下面的脚本是一个可运行的最小版本,错误处理和重试做了简化,实际项目要补上失败重试和断点续传。
import json import requests EMBED_URL = "http://127.0.0.1:8080/embed" OS_URL = "http://127.0.0.1:9200" INDEX = "docs" BATCH_SIZE = 32 def embed_texts(texts): resp = requests.post(EMBED_URL, json={"texts": texts}, timeout=120) resp.raise_for_status() return resp.json()["vectors"] def bulk_write(pairs): lines = [] for doc, vec in pairs: lines.append(json.dumps({"index": {"_index": INDEX}})) record = dict(doc) record["embedding"] = vec lines.append(json.dumps(record, ensure_ascii=False)) payload = "\n".join(lines) + "\n" resp = requests.post( f"{OS_URL}/_bulk", data=payload.encode("utf-8"), headers={"Content-Type": "application/x-ndjson"}, timeout=120, ) resp.raise_for_status() result = resp.json() if result.get("errors"): print("部分文档写入失败,检查 failed 字段") return result def load_docs(path): with open(path, "r", encoding="utf-8") as f: return [json.loads(line) for line in f if line.strip()] def main(): docs = load_docs("./inputs/docs.jsonl") for i in range(0, len(docs), BATCH_SIZE): chunk = docs[i:i + BATCH_SIZE] vectors = embed_texts([d["content"] for d in chunk]) bulk_write(list(zip(chunk, vectors))) print(f"已写入 {i + len(chunk)} / {len(docs)}") if __name__ == "__main__": main()这里有一个工程细节值得单独说:切分粒度。切得太碎,单块信息不足,模型答不出来;切得太粗,召回精度下降,还会浪费上下文窗口。通用做法是按段落切分,再带一点重叠,同时把文档标题和来源作为元数据挂在每个块上。块大小没有通用最优值,要在自己的语料上做小规模对比测试。
5. 功能测试与效果验证
功能验证不能只测"能不能搜出来",要按维度拆开测。下面六个维度是企业搜索最值得优先跑的,每个都给出测试目的、操作方式、预期结果和失败原因。
5.1 文档接入与解析测试
测试目的是确认各类格式的文档能被正确解析成纯文本。准备一组样例,覆盖纯文本、Markdown、PDF、Word、表格文件,每种至少两份。操作方式是跑一遍索引脚本,然后在索引里查文档总数和抽样内容。预期结果是总数与源文件数一致,抽样内容里中文没有乱码、表格没有串行、PDF 的页眉页脚没有混进正文。失败最常见的原因是 PDF 解析库对扫描件无效,需要先做 OCR;其次是编码问题,源文件是 GBK 编码但按 UTF-8 读取,正文会变成乱码。判断成功的标准很朴素:随机抽十篇,人工看内容可读。
5.2 全文检索测试
目的确认 BM25 通路正常。输入一组精确关键词,比如某个产品型号、某个内部系统名,看能不能命中。这一步不需要 embedding,直接调 OpenSearch 的_search接口即可。预期结果是包含关键词的文档排在前列。如果一条都搜不到,先检查分词器,中文没有装分词插件时,默认分词会把整句当一个词,导致召回极差。判断标准是关键词命中的前三条里至少有一条是人工认为最相关的。
5.3 语义检索测试
目的确认向量通路有效。输入一段自然语言描述,不含精确关键词,比如"新员工入职第一周需要办理哪些手续",看能不能召回制度类文档。操作方式是把 query 也过一遍 embedding,然后用 knn 查询。预期结果是语义相关但字面不同的文档能被召回。失败原因通常是 embedding 模型与语料领域不匹配,通用模型在专业术语上表现一般,需要换领域模型或做微调。这一项要单独测,不能和全文检索混在一起,否则出了问题分不清是哪条路。
5.4 混合检索与重排测试
目的是验证多路召回融合的效果。做法是同时跑 BM25 和向量检索,用 RRF 这类融合算法合并排序,再交给重排模型打一次分。预期结果是相比单路检索,前五条的准确率有可见提升。这一步最容易出的问题是融合权重拍脑袋定,建议准备一个小型评估集,人工标注二十到五十个查询的正确文档,用命中率做对比。没有评估集的调参都是玄学。
5.5 RAG 问答测试
目的是验证生成环节。输入一个需要跨文档综合的问题,看答案是否准确、是否带引用。操作方式是先检索取前若干条,拼进提示词,调用 LLM 生成。预期结果是答案里的关键信息能在引用文档中找到出处。最常见的失败是模型编造,明明检索结果里没有相关信息,它仍然给出一段看似合理的答案。解决办法是在提示词里明确要求"资料不足时直接说明找不到",并且在返回结构里强制带上引用列表,前端展示时把引用做成可点击的。
5.6 权限过滤测试
这是最不能省的一项。准备两个测试账号,A 有权限看某份文档,B 没有。用 B 的身份发起检索和问答,预期结果是这份文档既不出现在结果列表里,也不能被问答引用。操作方式是在检索请求里带上用户标识,服务端在查询时把 ACL 过滤条件拼进查询体。失败原因通常是先召回再过滤,也就是取回一百条结果后在前端裁剪,这样既浪费算力,又存在日志泄露风险。正确做法是过滤条件下推到查询层。判断标准是 B 无论怎么换关键词都搜不到,且服务端日志里不记录文档正文。
6. 接口 API 与批量任务
企业搜索最终一定要以接口形式对外提供能力,前端、插件、机器人、第三方系统都通过接口调用。最小可用的接口设计包含三个:索引接口、检索接口、问答接口。下面的示例是通用模板,路径和参数需要按实际服务调整。
6.1 检索接口调用
curl -s -X POST "http://127.0.0.1:8000/api/search" \ -H "Content-Type: application/json" \ -d '{ "query": "报销流程", "top_k": 5, "user": "alice", "mode": "hybrid" }'返回结构建议固定为results数组加total和took_ms,每条结果包含标题、摘要、来源、链接、分数。固定结构的好处是前端和第三方接入时不用做兼容分支。mode字段用来切换纯全文、纯向量、混合三种模式,方便排查问题。
6.2 问答接口调用
问答接口在检索基础上多一层生成,返回里必须带引用。下面的 Python 示例演示了调用方式和结果处理,重点是引用字段不能省。
import requests def ask(question, user): payload = { "query": question, "user": user, "top_k": 6, "with_citations": True, } resp = requests.post( "http://127.0.0.1:8000/api/ask", json=payload, timeout=120, ) resp.raise_for_status() data = resp.json() print("答案:", data.get("answer")) for cite in data.get("citations", []): print("引用:", cite.get("title"), cite.get("url")) return data if __name__ == "__main__": ask("新员工第一周需要完成哪些入职手续", "alice")6.3 批量任务配置
批量任务要单独设计,不能靠循环调单条接口。通用做法是提供一个任务配置文件,指定输入目录、输出目录、批大小、失败日志位置,服务端按文件逐条处理并记录进度。下面的配置是示例结构。
{ "input_dir": "./inputs", "output_dir": "./outputs", "batch_size": 32, "embedding_url": "http://127.0.0.1:8080/embed", "index_name": "docs", "failed_log": "./outputs/failed.jsonl", "resume_from_checkpoint": true }批量任务有三个工程要点。第一,失败要单独落到failed.jsonl,不要中断整个任务,等跑完再统一重试。第二,要支持断点续传,记录已处理文件的偏移量,否则中途失败就得从头再跑一遍。第三,要有进度输出,至少能看到已处理数量和剩余预估时间。文档量大的时候,没有进度反馈的任务会让人怀疑是不是卡死了。
7. 资源占用与性能观察
这套系统的资源消耗主要集中在三个地方:embedding 推理、向量索引内存、LLM 生成。观察方式和优化方向不一样,要分开看。
Embedding 是批量导入时的瓶颈。CPU 推理下,吞吐通常只有每秒几十条到上百条短文本,十万级文档可能要跑几个小时。GPU 推理会快很多,但显存占用取决于模型大小,几百 MB 到数 GB 不等,具体以实际模型为准。观察方式是看导入脚本的进度输出,再配合nvidia-smi或top看设备利用率。如果 GPU 利用率长期很低,通常是数据加载或网络请求成了瓶颈,不是模型慢。
向量索引的内存占用和文档块数、向量维度直接相关。块数翻倍,内存基本翻倍。OpenSearch 的 knn 索引会把向量加载进内存,字段维度越高占用越大。观察方式是看容器的内存曲线,再对照索引的文档数。如果内存吃紧,可以先用更低维度的 embedding 模型做验证,或者在检索时限制候选集大小。
LLM 生成是问答环节的成本大头。影响因素是输入 token 数、输出长度和并发数。检索回来的文档块越多,提示词越长,延迟和成本越高。常见优化是把 top_k 从 10 降到 5 到 6,同时用重排保证精度;再就是把明显不相关的块在拼接前就过滤掉。观察方式是记录每次请求的耗时和 token 数,做成简单的统计表,跑一周就能看出瓶颈在哪。
还有两个容易踩的运维坑。第一是端口冲突,尤其是同时跑多个服务时,9200、8000、8080 这几个常用端口很容易撞,启动前统一规划一份端口表。第二是进程残留,Docker 容器没有正常停止时,端口会一直被占用,表现为"服务明明重启了但还是连不上旧配置",用docker ps -a和ss -lntp交叉确认即可。
8. 常见问题与排查方法
下面这张表覆盖了从部署到运行最常见的几类问题。排查顺序建议统一为:先看服务是否活着,再看日志报什么,最后才怀疑模型和参数。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 索引服务起不来 | 内存不足或端口被占用 | 看容器日志与端口监听 | 调低堆内存或更换端口 |
| 批量导入全部失败 | 向量维度与映射不一致 | 对比映射 dimension 与模型输出 | 重建索引并统一维度常量 |
| 中文搜不到结果 | 未安装中文分词插件 | 用单字查询验证分词效果 | 安装分词插件并重建索引 |
| 语义检索召回很差 | embedding 模型与领域不匹配 | 用同义句测试向量相似度 | 换领域模型或做微调 |
| 问答答案编造 | 提示词未约束无答案场景 | 人工核对引用是否支撑答案 | 强制引用并加兜底话术 |
| 越权搜到内容 | 过滤条件未下推到查询层 | 用低权限账号复测 | 改为查询阶段过滤 |
| 接口调用超时 | 生成耗时过长或并发过高 | 看单次请求耗时分布 | 降 top_k、加超时与限流 |
| 批量任务中途卡住 | 单条数据异常导致阻塞 | 看失败日志与最后处理位点 | 加异常捕获与断点续传 |
| 内存持续上涨 | 索引未合并或查询缓存膨胀 | 看容器内存曲线 | 调整合并策略与缓存上限 |
| 增量同步漏数据 | 位点记录或删除事件缺失 | 对比源系统与索引文档数 | 补齐删除同步与位点校验 |
这张表里最值得提前设计的是"越权搜到内容"和"问答答案编造"两项,因为它们属于安全性问题,不是性能问题,出一次就够麻烦。建议在项目初期就把权限测试和引用校验写进验收清单,而不是等上线前再补。
9. 最佳实践与使用建议
第一条,第一次先小规模跑通,别一上来就接十个数据源。选三个用户每天都在用、格式相对规整的源,把接入、解析、索引、检索、问答全链路跑通,再逐步扩。企业搜索的失败大多不是因为技术不行,而是因为一开始摊子铺太大,每个源都做到一半,最后没有一个能用。
第二条,保留一套最小可运行配置。Compose 文件、索引映射、批量脚本、评估集,这四个东西放进版本管理,任何人拉下来都能在半小时内起一套环境。这套配置的价值在排错时体现得最明显,出问题时可以快速判断是环境差异还是代码问题。
第三条,目录结构从一开始就分清楚。输入素材、解析中间结果、索引数据、输出结果、失败日志,各占一个目录,不要混在一起。批量任务尤其如此,混在一起之后想重跑某一段都没法定位。
第四条,批量任务必须带日志和失败重试。日志要记录每条数据的唯一标识、处理状态、耗时。失败重试要能按标识单独重跑,而不是整批重来。这两点在文档量过万之后会直接决定运维体验。
第五条,接口服务要限制访问范围。即使是内网服务,也要做基本的鉴权,至少确认调用方身份,避免任何人拿到地址就能查全量数据。对外暴露时加反向代理、限流和访问日志,日志里不要记录文档正文和完整查询语句。
第六条,涉及人脸、声音、版权素材、员工个人信息的内容,接入前必须确认授权范围。企业搜索处理的是内部数据,但内部数据不等于可以随便用,尤其是跨部门、跨主体的数据,权限继承要严格按源系统来,不要为了方便做"超级账号"。
第七条,发布或交付前做一次效果复核。准备一个包含二三十个真实查询的小评估集,人工标注正确答案,每次改动检索策略或换模型后跑一遍,看命中率和答案准确率有没有退化。没有评估集的迭代,本质上是在靠感觉调参。
10. 总结与下一步
回到标题里的"第一个产品、第一批客户、第一次验证",企业搜索这件事的验证路径其实很清晰:先证明能在真实数据上召回得准,再证明权限控得住,最后证明问答不乱编。这三件事任何一件没过,产品故事讲得再好也落不了地。技术侧最先该验证的不是大模型效果,而是权限过滤和增量同步这两个"脏活",因为它们决定了系统能不能长期跑在企业环境里。
如果要自己动手,建议第一步只做检索,不做生成。用几十篇真实文档建索引,跑通全文加向量的混合检索,把准确率调到可接受,再往上加问答。第二步补权限过滤,用两个不同权限的账号做对比测试。第三步才是接 LLM 做 RAG,并且强制要求带引用。这个顺序能让你在每一层都拿到确定的结论,而不是在一个黑盒里反复试。
最容易踩的坑有三个:中文分词没配好导致全文检索形同虚设;向量维度和映射不一致导致批量导入全军覆没;权限过滤放在返回结果之后而不是查询阶段。这三件事都不难,但都很容易被忽略,建议直接写进上线的检查清单。
后续可以扩展的方向包括:接入更多数据源的增量同步、加一层查询理解做意图分类、把评估集做成自动化回归、以及把检索能力封装成内部 SDK 供其他系统调用。做到最后,企业搜索的价值不在于那个搜索框,而在于它成了公司内部信息流动的基础设施,谁都能在上面接自己的场景。这套东西建议收藏备用,等真正要动手的时候,按上面的顺序一步步来就行。