Day 012|Embedding 和向量数据库:知识库检索到底在检什么

系列:100 天系统学习 AI Agent 开发

当前阶段:RAG、知识库与工具边界

今日目标:Embedding 把文本映射到向量空间,向量检索用相似度找出最相关的片段。

搜到"意思差不多",离答对还很远

关键词搜索擅长找相同字词。用户问"Agent 断线后怎么接着跑",资料里写的是"checkpoint 支持恢复执行",两边没有完全相同的表达,关键词可能错过。Embedding 的用处,是把文本编码成一组数字,让语义接近的文本在向量空间里更靠近。

但"靠近"只是统计意义上的相似,不代表事实正确,更不代表片段足以回答问题。部署文档和卸载文档会共享大量词;旧版本与新版本也可能非常相似。向量检索解决的是候选召回,不是最终判断。

从文本到候选片段

#mermaid-svg-zf5176q1k2Fu2KMx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zf5176q1k2Fu2KMx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zf5176q1k2Fu2KMx .error-icon{fill:#552222;}#mermaid-svg-zf5176q1k2Fu2KMx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zf5176q1k2Fu2KMx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zf5176q1k2Fu2KMx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zf5176q1k2Fu2KMx .marker.cross{stroke:#333333;}#mermaid-svg-zf5176q1k2Fu2KMx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zf5176q1k2Fu2KMx p{margin:0;}#mermaid-svg-zf5176q1k2Fu2KMx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zf5176q1k2Fu2KMx .cluster-label text{fill:#333;}#mermaid-svg-zf5176q1k2Fu2KMx .cluster-label span{color:#333;}#mermaid-svg-zf5176q1k2Fu2KMx .cluster-label span p{background-color:transparent;}#mermaid-svg-zf5176q1k2Fu2KMx .label text,#mermaid-svg-zf5176q1k2Fu2KMx span{fill:#333;color:#333;}#mermaid-svg-zf5176q1k2Fu2KMx .node rect,#mermaid-svg-zf5176q1k2Fu2KMx .node circle,#mermaid-svg-zf5176q1k2Fu2KMx .node ellipse,#mermaid-svg-zf5176q1k2Fu2KMx .node polygon,#mermaid-svg-zf5176q1k2Fu2KMx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zf5176q1k2Fu2KMx .rough-node .label text,#mermaid-svg-zf5176q1k2Fu2KMx .node .label text,#mermaid-svg-zf5176q1k2Fu2KMx .image-shape .label,#mermaid-svg-zf5176q1k2Fu2KMx .icon-shape .label{text-anchor:middle;}#mermaid-svg-zf5176q1k2Fu2KMx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zf5176q1k2Fu2KMx .rough-node .label,#mermaid-svg-zf5176q1k2Fu2KMx .node .label,#mermaid-svg-zf5176q1k2Fu2KMx .image-shape .label,#mermaid-svg-zf5176q1k2Fu2KMx .icon-shape .label{text-align:center;}#mermaid-svg-zf5176q1k2Fu2KMx .node.clickable{cursor:pointer;}#mermaid-svg-zf5176q1k2Fu2KMx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zf5176q1k2Fu2KMx .arrowheadPath{fill:#333333;}#mermaid-svg-zf5176q1k2Fu2KMx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zf5176q1k2Fu2KMx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zf5176q1k2Fu2KMx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zf5176q1k2Fu2KMx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zf5176q1k2Fu2KMx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zf5176q1k2Fu2KMx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zf5176q1k2Fu2KMx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zf5176q1k2Fu2KMx .cluster text{fill:#333;}#mermaid-svg-zf5176q1k2Fu2KMx .cluster span{color:#333;}#mermaid-svg-zf5176q1k2Fu2KMx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zf5176q1k2Fu2KMx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zf5176q1k2Fu2KMx rect.text{fill:none;stroke-width:0;}#mermaid-svg-zf5176q1k2Fu2KMx .icon-shape,#mermaid-svg-zf5176q1k2Fu2KMx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zf5176q1k2Fu2KMx .icon-shape p,#mermaid-svg-zf5176q1k2Fu2KMx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zf5176q1k2Fu2KMx .icon-shape .label rect,#mermaid-svg-zf5176q1k2Fu2KMx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zf5176q1k2Fu2KMx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zf5176q1k2Fu2KMx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zf5176q1k2Fu2KMx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文档片段
Embedding 模型
向量 + 原文 + metadata
用户问题
同一 Embedding 模型
查询向量
相似度搜索
过滤/重排后的 Top K
交给模型基于证据回答

向量数据库实际保存的不应只有向量。至少还要有原始文本、chunk_id、来源、版本、权限和更新时间。否则即使搜到了,也无法引用、过滤或删除。

常见相似度可以直观理解为"方向是否接近"。下面只是数学演示,不负责生成真实 Embedding:

python 复制代码
from math import sqrt

def cosine_similarity(a: list[float], b: list[float]) -> float:
    if len(a) != len(b) or not a:
        raise ValueError("向量维度必须相同且不能为空")
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = sqrt(sum(x * x for x in a))
    norm_b = sqrt(sum(y * y for y in b))
    if norm_a == 0 or norm_b == 0:
        raise ValueError("零向量没有可用方向")
    return dot / (norm_a * norm_b)

真实系统必须使用同一套、同一版本的 Embedding 生成文档向量和查询向量。更换模型后直接混用旧索引,维度可能不同;即便维度碰巧相同,空间含义也不保证一致,通常需要重建索引或维护明确的索引版本。

五段学习笔记怎么准备

chunk_id 笔记主题 关键词 是否独立成块 理由
d006-loop Agent Loop 与最大轮数 loop、停止条件、observation 是 能独立回答循环控制
d007-retry 可重试错误与幂等 timeout、retry、idempotency 是 与参数缺失策略不同
d007-input 缺参数时如何追问 missing field、ask user 是 不应和自动重试混为一谈
d008-memory 短期/长期记忆 scope、TTL、source 是 有完整的概念边界
d009-cli CLI 与安全计算器 router、allowlist、trace 可按标题再拆 代码和设计理由检索意图不同

这里我会保留标题路径,例如"Day 007 > 错误分类 > 需要补充",而不是只存正文。很多短片段脱离标题后会失去"它在讨论什么"的信息。

向量、关键词还是混合检索

查询特点 更合适的起点
精确错误码、函数名、订单号 关键词/倒排检索
同义表达、自然语言问题 向量检索
既有专有名词又有语义描述 混合检索
强版本或权限约束 先做 metadata 过滤,再检索

只用向量检索的一个典型反例是错误码。"E_CONN_1042"几乎不需要语义联想,精确匹配更可靠。工程上经常把关键词和向量候选合并,再用规则或重排模型排序。

我会怎样比较,而不是凭感觉

准备五个问题,为每题预先标注应命中的 chunk_id,再记录 Top 1、Top 3 是否包含目标:

json 复制代码
{
  "query": "网络超时可以自动重试吗?",
  "expected_chunk_ids": ["d007-retry"],
  "retrieved_chunk_ids": [],
  "hit_at_1": null,
  "hit_at_3": null,
  "notes": "真实运行后填写"
}

列表保留空值,是为了明确本文没有运行向量模型。之后改变 chunk 大小、Embedding 模型或过滤条件时,用同一批问题复跑,才知道提升发生在哪里。

今天留下的判断

Embedding 像一张语义地图,向量数据库像管理这张地图的检索系统;它们都不是事实裁判。真正可靠的 RAG 还需要切分、元数据、过滤、重排、引用和拒答。下一篇研究 Chunking,正是因为"地图上的一个点代表多大一段文本"会直接影响能否找对。

面试官会追问:换一个 Embedding 模型,怎么证明更好?

不能只看某个问题"搜得更像"。同一批查询要固定语料、切分和 top-k,比较 Recall@k、MRR、索引体积、构建时间与单次检索延迟;中文、英文、代码和缩写查询还要分桶统计。向量维度更大可能提升表达能力,也会增加存储、内存和 ANN 搜索成本。

我会准备三类难例:同义改写、精确编号、跨语言表达。若向量检索擅长同义改写但漏掉产品编号,就用 BM25 与向量检索做混合召回,而不是逼一个 Embedding 模型同时解决全部问题。

面试时最重要的一句是:Embedding 选型是检索系统实验,不是模型排行榜抄答案;判断标准来自自己的语料和查询分布。

今日检查清单

  • 文档向量和查询向量来自同一模型版本
  • 每个向量能追溯到原文、来源、版本和权限
  • 精确标识符没有强行只走语义检索
  • 用预标注问题计算命中,不用一条漂亮回答下结论
  • 更换 Embedding 模型时有索引迁移或重建计划
相关推荐
comedate9 小时前
SMLib 的 Examples 导读:PyAPI / SM_API / SMLib 三组示例怎么读
python·cad·smlib·examples·sm_api
云和数据.ChenGuang9 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
摇滚侠9 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
hpoenixf9 小时前
AI 说“今天”,到底是哪一天?
agent
hpoenixf10 小时前
用户点了踩,Agent 就该学习吗?
agent
川石课堂软件测试10 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
万化冥合10 小时前
PyBox屏幕键盘如何辅助演示键盘操作?
python·计算机外设
车软学堂10 小时前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
天空依然蔚蓝10 小时前
分享一个自动检测网站在线状态的python程序
python·运维开发·1024程序员节
2501_9377671610 小时前
自建 SQL 数据库定时备份踩坑复盘与落地思路
数据库·sql