RAG 架构:分块、向量检索与 Go 落地¶
30 秒版(开场)¶
RAG(检索增强生成) = 用户问题 → 检索相关资料 → 把受控上下文交给模型生成。检索可组合 dense embedding、BM25、metadata filter 和 rerank。RAG 能提高可追溯性与时效性,但不自动保证答案正确,也不能替代权限控制。
3 分钟版(精讲深度)¶
- 是什么:用外部知识库补充模型上下文,减少仅凭参数记忆回答;检索层与生成层解耦。
- 为什么:企业文档私有、需可审计答案;纯微调成本高、更新慢。
- 怎么做:Ingest(解析→分块→向量化→入库)+ Query(改写→召回→Rerank→组装 Prompt);Go 常负责 API 编排、任务队列、权限过滤。
10 分钟版(原理 + 图示)¶
flowchart LR
Doc[PDF/Markdown] --> Chunk[分块 + 元数据]
Chunk --> Embed[Embedding API]
Embed --> VDB[(向量库 ES/Milvus/pgvector)]
Q[用户问题] --> QEmbed[Query Embedding]
QEmbed --> VDB
VDB --> TopK[Top-K 片段]
TopK --> Prompt[Prompt 组装]
Prompt --> LLM[大模型]
LLM --> Ans[带引用的回答]
分块策略对比
| 策略 | 优点 | 缺点 |
|---|---|---|
| 固定长度 + overlap | 实现简单 | 切断语义 |
| 按标题/段落 | 结构清晰 | 长短不一 |
| 语义分块 | 质量高 | 成本高 |
Go 编排示例(伪代码)
func (s *RAGService) Answer(ctx context.Context, q string, userID string) (string, error) {
// 1. ACL/tenant filter 必须进入检索查询,不能先全局 Top-K 再事后过滤。
filter, err := s.acl.SearchFilter(ctx, userID)
if err != nil {
return "", err
}
// 2. 向量检索(可叠加 BM25 hybrid)
chunks, err := s.vector.Search(ctx, q, filter, 20)
if err != nil { return "", err }
// 3. Rerank(可选小模型或 cross-encoder)
chunks = s.reranker.TopN(ctx, q, chunks, 5)
// 4. 组装 prompt + 调 LLM
prompt := buildRAGPrompt(q, chunks)
return s.llm.Complete(ctx, prompt)
}
生产场景¶
- 内部知识库问答:Confluence/Notion 同步 → 夜间 ingest
- 客服:商品详情 + 政策文档 RAG;答案必须带
source_id链接 - 代码助手:repo 分块 + 路径元数据;与 IDE 索引类似
排查与工具¶
- 评估:Hit Rate@K、答案 groundedness、人工抽检
- Bad case:检索为空 → 检查 embedding 模型一致性、分块是否过碎
- ES:
knn+boolfilter;Milvus:collection schema 与索引类型(HNSW)
架构取舍¶
| 向量库 | 适用 |
|---|---|
| Elasticsearch | 已有 ES、要 Hybrid 全文+向量 |
| pgvector | 希望与 PostgreSQL 数据、事务和运维体系共存;容量取决于索引与集群设计 |
| Milvus/Qdrant | 独立向量检索系统,适合需要专门扩展与索引能力的场景 |
何时不用 RAG:强实时数据(用 API 查库);极小知识集(直接塞进 system prompt)。
深挖问答¶
- Embedding 模型换了怎么办? → 全量 re-embed;双写过渡期。
- 怎么降低无依据回答? → 检索质量评估、无足够证据时拒答、引用可核验片段、结构化事实校验;一句“仅根据上下文”不是安全保证。
- 长文档表格怎么处理? → 结构化抽取或 HTML 表转 Markdown 再分块。
- 和微调怎么选? → 风格/格式用微调;事实知识用 RAG。
反模式与事故¶
- chunk 无元数据 → 无法做权限过滤,数据泄露
- 机械固定 Top-K → 不同问题、chunk 大小和模型预算需要不同候选数;用离线评估与 token budget 调参
- query 不改写 → 口语化问题检索差;可加 HyDE 或 query expansion
- ingest 与线上 embedding 模型不一致 → 检索几乎随机
代码示例¶
与 S-ES-01 倒排索引 结合:生产可用 ES dense_vector;本仓库教学示例:
go test ./examples/senior/rag/...
examples/senior/rag/ 演示分块、哈希向量、Top-K 检索与 llmclient.MockClient 编排。