第3周 Day 4:RAG 实战——用 Embedding 实现语义搜索

5 阅读4分钟

昨天的 RAG 入门用的是关键词匹配,有个问题:用户搜"猫咪"找不到"猫",因为字不一样。

今天用 Embedding API 实现真正的语义搜索——让"猫咪"能找到"猫",让"小狗"能找到"狗"。


一、Embedding 是什么?

简单理解:把文字变成一串数字,意思相近的数字也相近

"猫" → [0.23, 0.45, 0.12, -0.08, 0.33, ...]
"猫咪" → [0.24, 0.44, 0.13, -0.07, 0.32, ...]  (和"猫"的数字很像!)
"小狗" → [0.25, 0.43, 0.14, -0.09, 0.31, ...]  (和"狗"的数字很像!)
"汽车" → [0.01, 0.02, 0.88, 0.55, 0.12, ...]   (和"猫""狗"差别很大)

用处:用户问"猫咪怎么养",能找到关于"猫"的文档,即使文档里没有"猫咪"这个词。


二、Embedding API 怎么用?

调用 Embedding API

import requests

# Embedding API(阿里云/智谱等都有提供)
EMBEDDING_URL = "https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding/text-embedding"
HEADERS = {
    "Authorization": "Bearer your-api-key",
    "Content-Type": "application/json"
}

def get_embedding(text):
    """把文字转成向量"""
    data = {
        "model": "text-embedding-v2",
        "input": {"texts": [text]}
    }

    response = requests.post(EMBEDDING_URL, headers=HEADERS, json=data)
    result = response.json()

    # 返回向量(一串数字)
    return result["output"]["embeddings"][0]["embedding"]

# 测试
vec_cat = get_embedding("猫")
vec_kitty = get_embedding("猫咪")

print(f"猫的向量长度:{len(vec_cat)}")
print(f"猫咪的向量长度:{len(vec_kitty)}")

计算向量相似度

def cosine_similarity(vec1, vec2):
    """计算两个向量的相似度(余弦相似度)"""
    import numpy as np

    # 转成 numpy 数组
    a = np.array(vec1)
    b = np.array(vec2)

    # 计算余弦相似度
    similarity = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
    return similarity

# 测试
sim_cat_kitty = cosine_similarity(vec_cat, vec_kitty)
sim_cat_car = cosine_similarity(vec_cat, get_embedding("汽车"))

print(f"猫 vs 猫咪 相似度:{sim_cat_kitty:.3f}")  # 0.95(很像)
print(f"猫 vs 汽车 相似度:{sim_cat_car:.3f}")    # 0.12(不像)

三、实战:用 Embedding 实现 RAG

第一步:把文档转成向量

# 学习记录文档
documents = [
    {"id": 1, "content": "2026-04-15 学习了 cat,意思是猫"},
    {"id": 2, "content": "2026-04-16 学习了 dog,意思是狗"},
    {"id": 3, "content": "2026-04-17 学习了 bird,意思是鸟"},
]

# 给每个文档生成向量
def build_vector_index(documents):
    """把所有文档转成向量,存起来"""
    for doc in documents:
        doc["embedding"] = get_embedding(doc["content"])
    return documents

# 构建向量索引
indexed_docs = build_vector_index(documents)
print(f"已为 {len(indexed_docs)} 条文档生成向量")

第二步:用向量相似度检索

def retrieve_by_embedding(query, documents, top_k=3):
    """用向量相似度检索相关文档"""

    # 把用户问题转成向量
    query_embedding = get_embedding(query)

    # 计算每个文档的相似度
    results = []
    for doc in documents:
        similarity = cosine_similarity(query_embedding, doc["embedding"])
        results.append({
            "content": doc["content"],
            "score": similarity
        })

    # 按相似度排序,取前 top_k 个
    results.sort(key=lambda x: x["score"], reverse=True)
    return results[:top_k]

# 测试语义搜索
query = "我学了什么猫咪相关的单词"
docs = retrieve_by_embedding(query, indexed_docs)

for doc in docs:
    print(f"相似度 {doc['score']:.3f}: {doc['content']}")

运行效果

用户问:"我学了什么猫咪相关的单词"

关键词匹配:找不到(文档里没有"猫咪")

向量匹配:
  相似度 0.87: "2026-04-15 学习了 cat,意思是猫"
  相似度 0.42: "2026-04-16 学习了 dog,意思是狗"
  相似度 0.31: "2026-04-17 学习了 bird,意思是鸟"

成功!用户说"猫咪",找到了关于"猫"的记录。


四、关键词匹配 vs 向量匹配对比

关键词匹配(Day 2)向量匹配(Day 3)
搜"猫咪"❌ 找不到"猫"✅ 能找到"猫"
搜"小狗"❌ 找不到"狗"✅ 能找到"狗"
搜"宠物"❌ 找不到"猫/狗"✅ 能找到"猫/狗"
实现难度简单需要 Embedding API
速度需要计算向量
准确度字面匹配语义匹配

五、完整代码流程

def rag_with_embedding(query, documents):
    """用 Embedding 实现完整 RAG 流程"""

    # 1. 检索:用向量相似度找相关文档
    print("【第一步:向量检索】")
    docs = retrieve_by_embedding(query, documents)
    print(f"找到 {len(docs)} 条相关文档")

    for doc in docs:
        print(f"  相似度 {doc['score']:.3f}: {doc['content'][:50]}...")

    # 2. 增强:把检索结果加到问题里
    print("\n【第二步:增强 Prompt】")
    context = "\n".join([doc["content"] for doc in docs])
    augmented_prompt = f"""
根据以下学习记录回答:
{context}

问题:{query}
"""
    print(f"增强后的 prompt(前100字):{augmented_prompt[:100]}...")

    # 3. 生成:调用 AI 回答
    print("\n【第三步:生成回答】")
    answer = call_ai(augmented_prompt)
    print(f"AI 回答:{answer}")

    return answer

# 测试
rag_with_embedding("我学了什么宠物相关的单词?", indexed_docs)

六、代码结构

week3/02_rag/
├── app.py                    # Day 2:关键词匹配版
├── app_embedding.py          # Day 3:向量匹配版(新增)
└── documents/
    └── learning_records.json # 学习记录数据

七、今日总结

今天干了啥:

  • ✅ 理解 Embedding API 怎么用(把文字转成向量)
  • ✅ 实现向量相似度计算(余弦相似度)
  • ✅ 用 Embedding 替换关键词匹配,实现真正的语义搜索
  • ✅ 测试效果:用户说"猫咪"能找到"猫"

对比效果

用户问题Day 2 关键词匹配Day 3 向量匹配
"猫咪相关单词"❌ 找不到✅ 找到 cat
"小狗相关单词"❌ 找不到✅ 找到 dog
"宠物相关单词"❌ 找不到✅ 找到 cat/dog

写于 2026-04-19,RAG 实战——用 Embedding 实现语义搜索