Datawhale Easy Data × AI:构建知识与记忆驱动的 Agent笔记3

Easy Data × AI:构建知识与记忆驱动的 Agent笔记3

Task 3 · P2 + D2 + I2 · 打卡笔记

🔧 D2 作业

核心演示:d2_4 精确编号场景并排对比

复制代码
查询:「错误码 E-4012 的解决方案」

  纯向量搜索                               混合搜索(向量+全文+RRF)
  ------------------------------------   ------------------------------------
  [1] E-4012... (0.444)                   [1] E-4012... (0.016)
  [2] E-4011... (0.167)  ← 语义相近排第二  [2] E-4011... (0.016)
  [3] E-4013... (0.146)  ← 差一位数字!    [3] E-4013... (0.016)

📌 纯向量:三个错误码在语义空间几乎等距,排名带随机性
   混合搜索:全文精确命中 E-4012 + RRF 融合,信号清晰
   → 差一位数字 = 完全不同的错误,这是"对和错"的问题,不是"好和差"

这完美印证了 P2 课程稿的核心观点:混合搜索不是可选优化,是生产级 RAG 的基本要求。


📦 vecdb.py --- D1/D2 共享向量检索层

为什么要做?

方案 平台兼容 首次启动 代价
pyseekdb embedded ❌ 仅 Linux + macOS arm64 秒开 Windows 不可用(原生库 pylibseekdb 仅 Linux)
ChromaDB ✅ 全平台 ~10min 首次需下载 80MB embedding 模型,15KB/s 要 2 小时
TF-IDF + scipy(vecdb.py) ✅ 全平台 秒开 只需要 numpy + scipy(已装),生产级用 ChromaDB/pyseekdb

实现的 API(对齐 pyseekdb)

python 复制代码
from vecdb import Client

db = Client(db_path="./d2_kb")
db.create_collection("d2_knowledge_base")
c = db.get_collection("d2_knowledge_base")

# 写入
c.add(ids=[...], documents=[...], metadatas=[...])

# 纯向量检索
c.query(query_texts="怎么设计用户权限", n_results=3)

# 混合检索(全文 + 向量 + RRF + 元数据过滤,一条查询搞定)
c.hybrid_search(
    query={"where_document": {"$contains": "E-4012"}, "n_results": 5},
    knn={"query_texts": ["错误码 E-4012"], "where": {"version": "4.2"}, "n_results": 5},
    rank={"rrf": {}},
    n_results=3,
)

# JSON 持久化 ------ 下次 Client() 自动加载

RAG 数据层第一步:chunk_document 切分函数

为什么值得记:RAG 的第一步不是"向量化",而是"切分"------知识库的 chunk 大小直接决定检索质量。切太大,检索时携带无关信息;切太小,丢失上下文。

python 复制代码
# 来自 d2_1_ingest.py
def chunk_document(text: str, chunk_size: int = 200, overlap: int = 30) -> list[str]:
    """
    固定窗口切分 + overlap 重叠区。
    - chunk_size=200:每片不超过 200 字(经验值:让 embedding 模型一次能完整理解)
    - overlap=30:相邻片之间留 30 字重叠,避免一句话被切断导致语义断裂
    """
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunks.append(text[start:end].strip())
        start += chunk_size - overlap   # ← 关键:步长 = chunk_size - overlap
    return [c for c in chunks if c]

切分完整流程(d2_1 的核心逻辑):

python 复制代码
raw_docs = [
    {"text": "错误码 E-4012 表示数据库连接超时...", "category": "error_codes", "version": "4.2"},
    {"text": "数据库查询性能优化指南...",          "category": "best_practices", "version": "4.2"},
    # ... 更多文档
]

collection = db.create_collection("d2_knowledge_base")
for doc_idx, doc in enumerate(raw_docs):
    for chunk_idx, chunk in enumerate(chunk_document(doc["text"])):
        collection.add(
            ids=[f"doc_{doc_idx}_chunk_{chunk_idx}"],
            documents=[chunk],
            metadatas=[{"category": doc["category"], "version": doc["version"]}],   # ← 保留元数据
        )

混合检索完整调用(三路参数)

为什么值得记:hybrid_search 的三路参数是 pyseekdb 混合搜索的完整签名------也是 P2 讲的"广义 Retrieval 五路子通道"在代码里的集中体现:

python 复制代码
results = collection.hybrid_search(
    # ── 第 1 路:全文关键词检索 ──────────────────────────────────────
    query={
        "where_document": {"$contains": "E-4012"},   # 子串精确匹配
        "n_results": 5,
    },

    # ── 第 2 路:向量语义检索(支持元数据过滤) ──────────────────────
    knn={
        "query_texts": ["错误码 E-4012 的解决方案"],   # 自然语言 query
        "where": {"version": "4.2"},                 # ← 结构化过滤!只搜 4.2 版本
        "n_results": 5,
    },

    # ── 第 3 路:融合算法 ────────────────────────────────────────────
    rank={"rrf": {}},     # RRF = Reciprocal Rank Fusion,见下面实现

    n_results=3,          # 最终返回 TopK
)

三路参数对应 P2 的 RAG 六步链路:

参数 对应检索通道 作用
query.where_document.$contains 全文/关键词 精确匹配专有名词、错误码、版本号
knn.query_texts 向量/语义 理解口语化提问、同义表达
knn.where 标量/过滤 按版本、类目、时间精确筛选
rank.rrf 融合重排 三路结果合流,排对序

RRF 融合核心实现

为什么值得记:RRF 是混合搜索的"胶水"------没有它,得自己写向量分数和全文分数怎么归一化、怎么加权、怎么去重。RRF 绕开了所有这些麻烦,直接按排名给分:

python 复制代码
# 来自 vecdb.py 的 hybrid_search() 方法核心片段
k = 60          # RRF 平滑常数(经验值 60 是业界常用,用来压制排名靠后的影响)
scores = {}     # doc_index → 累积分数

# --- 全文分支 ---
ft_scores = backend.fulltext_scores(["E-4012"])     # 每个 doc 里 "E-4012" 出现次数
top_ft = np.argsort(-ft_scores)[:10]
for rank_pos, doc_idx in enumerate(top_ft):
    if ft_scores[doc_idx] <= 0: break
    # 排名第 0 的 doc 得 1/(60+1),第 1 的得 1/(60+2),以此类推
    scores[int(doc_idx)] = scores.get(int(doc_idx), 0.0) + 1.0 / (k + rank_pos + 1)

# --- 向量分支(同理) ---
vt_res = backend.query(["错误码 E-4012"], n_results=10, where={"version": "4.2"})
top_vt = np.argsort(-all_sims)[:10]
for rank_pos, doc_idx in enumerate(top_vt):
    if all_sims[doc_idx] <= 0: break
    scores[int(doc_idx)] = scores.get(int(doc_idx), 0.0) + 1.0 / (k + rank_pos + 1)

# --- 合流:按累积分数排序,取 TopK ---
sorted_idx = sorted(scores.items(), key=lambda x: -x[1])[:n_results]

RRF 的精妙之处:

  • 不需要分数归一化 :全文分支的 1/(60+0)=0.0164 和向量分支的分数天然在同一量级
  • 同一 doc 两路都命中 → 分数叠加 :比如 "E-4012 文档" 全文排第 0 + 向量排第 1,得分 = 1/61 + 1/62 ≈ 0.0328,自动压过只在一路排前面的 doc
  • k=60 压制长尾 :第 61 名的分数 = 1/121 ≈ 0.008,排名 60 之后的 doc 基本被忽略

TF-IDF 分词策略(让短文本效果到位)

  • 英文:[a-z][a-z0-9_\-]+ 完整单词
  • 中文:2-gram
  • 精确编号:E-4012 / 1.2.0 作为整体 token(让向量检索对版本号也有加分)

RRF(Reciprocal Rank Fusion)融合算法

python 复制代码
# 两路检索结果按排名给分,相加排序
score(doc) = Σ 1/(k + rank_of_doc_in_path)   # k=60 平滑常数
  • 全文命中:1/(60+0) + 1/(60+1) + ...
  • 向量命中:同上
  • 同一 doc 两路都命中 → 分数累加 → 自动排到前面

📚 产品篇 P2 · Agentic RAG 产品设计 · 学习笔记

核心金句

"用户说'AI 答得不好',PM 的第一反应不应该是'换模型',而应该先问:R 做对了吗?数据准备对了吗?检索路径对了吗?"

"60-80% 的 RAG 问题,根因在数据层(没搜全 / 搜错了),不在模型层(幻觉)。"

一、RAG ≠ 向量检索(最核心的认知纠正)

RAG 全称是 Retrieval-Augmented Generation ,重点是第一个词:Retrieval,检索。

广义 Retrieval 包含五路子通道:

检索方式 解决什么 例子
全文 / 关键词 精确匹配专有名词、产品型号、错误码 E-4012、OceanBase 4.4.1
向量 / 语义 口语化提问、同义表达、模糊需求 "怎么设计权限" → "访问控制架构"
标量 / 过滤 按版本、产品线、时间、权限筛选 只搜 version=4.2 的文档
图谱 / 关系跳转 多跳问题 "这个客户 → 哪个行业方案 → 哪些成功案例"
API / 工具调用 实时数据 查今天的库存、股价、物流

只做向量检索 = 只给用户一把锤子。 真实知识库充满了版本号、错误码、产品名------这些在语义空间都很"相近",但精确匹配至关重要。

二、纯向量检索的结构性软肋(被 D2 代码完美复现)

查询 纯向量结果 问题
"错误码 E-4012 的解决方案" E-4012 / E-4011 / E-4013 都排前面 三个错误码语义几乎等距,排第二的 E-4011 和 E-4012 差一位数字=完全不同的错误
"seekdb 1.2.0 版本兼容性" seekdb 1.2.0 / 1.0.0 / 1.3.0 都排前面 版本号在语义空间"都是 seekdb 版本",但兼容性可能完全不同

结论:纯向量检索能解决语义问题,但解决不了"差一点就是另一件事"的精确问题。

三、传统 RAG vs Agentic RAG

维度 传统 RAG Agentic RAG
流程结构 无环 DAG,预先设计好的固定流水线 有闭环,系统可以自主重试、补搜、改路由
典型路径 query → 改写 → 检索一次 → TopK → 拼 Prompt → 回答 query → 检索 → 判断够不够 → 不够换关键词再搜 → 还是不够再查另一路 → 生成
搜索方式 通常单一路径(向量搜索) 多路可选(全文/向量/过滤/工具),动态选择
"更聪明"的来源 换更强的模型 / 加更多节点 流程能自我调整

传统 RAG:像"按固定流程查一遍资料"

Agentic RAG:像"主动在替你反复找、反复核对、反复修正"

四、混合搜索 ------ 不是高级玩法,是基础要求

混合搜索在 RAG 流程里有三层价值:

第一层:同时服务传统 RAG 和 Agentic RAG

  • 一套底层支持向量 / 全文 / 过滤 → Agent 动态选搜索方式才可能实现

第二层:把复杂度内置,降低开发者负担

  • 不用自己拼向量库 + 搜索引擎 + 关系库 + 应用层合并去重重排
  • seekdb 这种 AI 原生数据库在引擎内部就做好了

第三层:支撑 Agentic RAG 的闭环编排

  • Agent 频繁做改写 query 再搜、先全文再向量、先过滤再匹配------底层原生支持才跑得动

五、完整 RAG 六步链路

复制代码
① 数据准备     ② 问题理解     ③ 搜索召回     ④ 融合重排     ⑤ 生成     ⑥ 评估反馈
(最容易低估)  query改写/路由  广撒网别漏掉   RRF/去重/重排  引用+约束  RAGAS
   ↓
load → parse → clean → split → enrich → embed → refresh

六步对应 RAGAS 评估指标:

阶段 RAGAS 指标 低了说明什么
③ 搜索召回 Context Recall 应该召回的证据漏掉了
④ 融合重排 Context Precision 混入了噪音,上下文不干净
⑤ 生成 Faithfulness 模型幻觉,编造了资料里没有的内容
用户感受 Answer Relevancy 答非所问,没围绕用户意图

六、三层归因框架(P2 最重要的工具)

当用户说"AI 答得不好"时:

复制代码
① 数据层 · 内容覆盖   → 知识库里有没有正确答案?没有 → 补数据
② 数据层 · 检索策略   → 有答案但 AI 没检索到?检索偏了/排错了 → 优化检索(混合搜索!)
③ 模型层 · 幻觉       → 检索到了,答的时候加了编造信息?→ 引用设计 + 约束生成
④ 业务层 · 规则       → 事实对但不合规/风格不对?→ Prompt + 业务规则

关键比例:60-80% 是数据层问题,10-20% 是模型层幻觉,剩下少量是业务层问题。

PM 最常见的误诊:把 ①② 诊断成 ③,花大量精力换模型 → 效果没变。

💡 对比:

之前以为 现在理解
RAG = 向量数据库 + LLM RAG = Retrieval(多路)+ Augmented + Generation,向量只是其中一路
混合搜索是"高级优化" 混合搜索是底线,差一位数字就是对和错的区别
AI 答不好 → 换模型 先查数据层:知识库里有没有?检索对了吗?排对了吗?再谈模型
Agentic RAG = 换更聪明的模型 Agentic RAG = 流程能自我调整(闭环 + 自主选择搜索方式 + 补搜)
RAG 做一次就完 RAG 是六步可调优的产品链路 + 评估反馈闭环(RAGAS)

相关推荐
u1301301 小时前
GitHub 热榜项目:日榜(2026-10-08)
人工智能·github
逸模1 小时前
BIM模型做一次要多少钱?值不值?
大数据·人工智能·公装·连锁公装·交底
野生码农AI实战1 小时前
AI 看不了网页?我用 CDP 重建浏览器采集通道,30 行脚本 2.7 秒读完全文
数据库·人工智能·ai编程
硅谷秋水1 小时前
将物理先验嵌入机器人学习:综述
人工智能·深度学习·机器学习·语言模型·机器人
怒放de生命20101 小时前
2026年新加坡TOKEN2049大会:传统金融与加密世界的深度融合与现实叩问
人工智能·web3·去中心化·区块链·以太坊·分布式账本
科创致远1 小时前
岳阳esop电子作业指导书:化工企业生产经理——化工生产最怕员工凭经验操作
大数据·人工智能·精益工程
Omics Pro1 小时前
反式感知虚拟细胞!超越线性序列
数据库·人工智能·算法·机器学习·自然语言处理
Summer-Bright1 小时前
深度 | OpenAI 甩出 722 篇数学论文,但黎曼猜想并没有被证明
人工智能·数学·openai