Skip to content

RAG

为什么不直接将知识库作为提示词的一部分?

这样会使得上下文变得很长,长上下文窗口会带来以下问题:

  • 成本急剧飙升:token告急
  • “中间迷失”效应:模型会忽略文档中间的内容
  • 信息过载与精度下降:模型可能“抓不住重点
  • 可追溯性与可信度问题:你无法知道答案的来源
  • 技术实现的硬约束:上下文长度上限

Indexing 索引

这个步骤需要将知识库以某种格式保存起来,方便后续匹配

Split 分割

  • 字符分割:按固定字符数切割
  • 递归分割:按分隔符优先级递归切割
  • Token分割:按LLM token数切割
  • 语义分割:基于嵌入向量相似度
  • LLM智能分割:使用LLM理解内容后分割
  • 混合分割:组合多种策略

Embedding 嵌入

嵌入的工作,是将一段文本变成一个多维向量的数学表达,为后续的检索提供便利。

python
# 将城市公园这个概念拆解成空间相关的语义维度
city_park_spatial_vector = {
"维度1 - 开放空间占比": 0.95,       # 正数:以开阔绿地/广场为主,封闭建筑少
"维度2 - 可达性(城市中心)": 0.85,  # 正数:靠近居民区/市中心,交通便利
"维度3 - 自然植被覆盖度": 0.90,     # 正数:树木草坪多,人工硬质景观少
"维度4 - 地形起伏程度": -0.30,     # 弱负数:地势平缓,几乎无陡坡高山
"维度5 - 建筑密度": -0.80,         # 强负数:建筑极少,以露天空间为主
"维度6 - 噪音污染程度": -0.70,     # 强负数:环境安静,远离主干道
"维度7 - 服务半径覆盖": 0.75,      # 正数:能覆盖周边1-3km居民
"维度8 - 夜间照明覆盖率": -0.20,   # 弱负数:夜间照明有限,偏自然
"维度9 - 水体占比": 0.30,          # 弱正数:常见有小湖/溪流
"维度10 - 边界开放性": 0.90        # 正数:多为开放式入口,无封闭围墙
...
}

可见,向量维度越高,对语言的理解就越准确越细腻。

要实现对一个完整语句的嵌入,有多种方法,比如传统统计、静态词嵌入、上下文感知嵌入等等。目前,绝对主流的嵌入方法是基于Transformer的上下文感知嵌入。

空间向量维度

维度大小代表模型 / 技术典型应用场景
128 维轻量版 Sentence-BERT、MiniLM资源受限设备、简单检索、快速原型
256 维部分轻量 BERT、DistilBERT 变种移动端 / 边缘设备、低延迟场景
512 维GPT-2 基础版、部分早期嵌入模型中等规模文本任务、平衡性能与精度
768 维BERT-base、Sentence-BERT-base、LLaMA-2 7B/13B 词嵌入通用 NLP 任务、主流语义检索
1024 维BERT-large、GPT-2 large、LLaMA-1 7B/13B复杂语义理解、高精度检索 / 生成
1536 维OpenAI text-embedding-ada-002通用商业级语义检索、RAG 系统
2048 维GPT-3 基础版、部分大模型中间层大规模语言建模、复杂上下文理解
3072 维LLaMA-2 70B、Mistral 8x7B超大参数量模型、长文本理解
4096 维GPT-3.5/4 部分版本、PaLM 2 基础版高端语义理解、复杂生成与检索任务
8192 维GPT-4 高级版、PaLM 2 大参数量版超大规模上下文、复杂推理任务

nomic-embed-text

模型安装:nomic-embed-text

bash
ollama pull nomic-embed-text

直接封装方法进行测试:传入一个字符串,通过ollama调用,返回一个向量

ts
import ollama from 'ollama';

const embeddings = async (text: string) => {
  const response = await ollama.embed({
    model: 'nomic-embed-text',
    input: text
  });
  console.log(response.embeddings);
};

await embeddings('hello world');

Storage 存储

存储嵌入结果通常使用向量数据库

  • 小型项目(<10万向量):ChromapgvectorSQLite
  • 中型应用(10万-1000万):QdrantWeaviateElasticSearch
  • 大型系统(>1000万):Milvus、专用云服务

Retrieval 检索

这个步骤需要将用户的问题与之前建立的索引进行匹配,得到相关度较高的结果

余弦相似度

cosa=ABABcos a = \frac{A \cdot B}{||A|| \cdot ||B||}

余弦相似度仅与方向有关,和向量长度无关。 它的取值范围是-1~1

  • -1:两个向量方向完全相反,属于反义
  • 0:两个向量垂直,含义完全无关
  • 1:两个向量方向完全相同,属于同义

为什么余弦相似度只看方向,不看长度?

长度无关性:因为长度仅表示意思的程度,不会改变含义本身

Generation 生成

这个步骤是将匹配的知识和用户问题混合投递给LLM,从而得到准确的结果

By Modify.